Repository: Azure/azure-functions-durable-js
Pull request #102 (2019-09-21 00:49:02)

Durable Entities work
This PR contributes the basic functionality of Durable Entities to the JS/TypeScript offering.

Note that while this PR covers much of the functionality of Durable Entities, it is missing advanced functionality like locks.

ConnorMcMahon: @sebastianburckhardt

Patch:
diff --git a/package-lock.json b/package-lock.json
index c578207..46188a0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -24,7 +24,7 @@
       "integrity": "sha512-ZAR2bPHOl4Xg6eklUGpsdiIJ4+J1SNag1DHHrG/73Uz/nVwXqjgUtRPLoS+aVyieN9cSbc0E4LsU984tWcDyNg==",
       "dev": true,
       "requires": {
-        "@sinonjs/samsam": "^2 || ^3"
+        "@sinonjs/samsam": "2.1.3"
       }
     },
     "@sinonjs/samsam": {
@@ -45,7 +45,7 @@
       "integrity": "sha512-MFiW54UOSt+f2bRw8J7LgQeIvE/9b4oGvwU7XW30S9QGAiHGnU/fmiOprsyMkdmH2rl8xSPc0/yrQw8juXU6bQ==",
       "dev": true,
       "requires": {
-        "@types/chai": "*"
+        "@types/chai": "4.1.7"
       }
     },
     "@types/commander": {
@@ -54,7 +54,7 @@
       "integrity": "sha1-xg5VFwkfniIApeIGOoMO3axKcDY=",
       "dev": true,
       "requires": {
-        "@types/node": "*"
+        "@types/node": "6.14.7"
       }
     },
     "@types/debug": {
@@ -80,13 +80,13 @@
       "integrity": "sha512-ZHf/X8rTQ5Tb1rHjxIJYqm55uO265agE3G7NoSXVa2ep+EcJXgB2fsme+zBvK7MhrxTwkC/xkB6THyv50u0MGw==",
       "dev": true,
       "requires": {
-        "@types/node": "*"
+        "@types/node": "6.14.7"
       }
     },
     "@types/node": {
-      "version": "6.14.2",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-6.14.2.tgz",
-      "integrity": "sha512-JWB3xaVfsfnFY8Ofc9rTB/op0fqqTSqy4vBcVk1LuRJvta7KTX+D//fCkiTMeLGhdr2EbFZzQjC97gvmPilk9Q=="
+      "version": "6.14.7",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-6.14.7.tgz",
+      "integrity": "sha512-YbPXbaynBTe0pVExPhL76TsWnxSPeFAvImIsmylpBWn/yfw+lHy+Q68aawvZHsgskT44ZAoeE67GM5f+Brekew=="
     },
     "@types/rimraf": {
       "version": "0.0.28",
@@ -105,7 +105,7 @@
       "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.4.tgz",
       "integrity": "sha512-tPIgT0GUmdJQNSHxp0X2jnpQfBSTfGxUMc/2CXBU2mnyTFVYVa2ojpoQ74w0U2yn2vw3jnC640+77lkFFpdVDw==",
       "requires": {
-        "@types/node": "*"
+        "@types/node": "6.14.7"
       }
     },
     "@types/validator": {
@@ -131,7 +131,7 @@
       "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
       "dev": true,
       "requires": {
-        "sprintf-js": "~1.0.2"
+        "sprintf-js": "1.0.3"
       }
     },
     "arrify": {
@@ -152,7 +152,7 @@
       "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==",
       "requires": {
         "follow-redirects": "1.5.10",
-        "is-buffer": "^2.0.2"
+        "is-buffer": "2.0.3"
       }
     },
     "babel-code-frame": {
@@ -161,9 +161,9 @@
       "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
       "dev": true,
       "requires": {
-        "chalk": "^1.1.3",
-        "esutils": "^2.0.2",
-        "js-tokens": "^3.0.2"
+        "chalk": "1.1.3",
+        "esutils": "2.0.2",
+        "js-tokens": "3.0.2"
       }
     },
     "balanced-match": {
@@ -176,7 +176,7 @@
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
       "requires": {
-        "balanced-match": "^1.0.0",
+        "balanced-match": "1.0.0",
         "concat-map": "0.0.1"
       }
     },
@@ -198,12 +198,12 @@
       "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==",
       "dev": true,
       "requires": {
-        "assertion-error": "^1.1.0",
-        "check-error": "^1.0.2",
-        "deep-eql": "^3.0.1",
-        "get-func-name": "^2.0.0",
-        "pathval": "^1.1.0",
-        "type-detect": "^4.0.5"
+        "assertion-error": "1.1.0",
+        "check-error": "1.0.2",
+        "deep-eql": "3.0.1",
+        "get-func-name": "2.0.0",
+        "pathval": "1.1.0",
+        "type-detect": "4.0.8"
       }
     },
     "chai-as-promised": {
@@ -212,7 +212,7 @@
       "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==",
       "dev": true,
       "requires": {
-        "check-error": "^1.0.2"
+        "check-error": "1.0.2"
       }
     },
     "chalk": {
@@ -221,11 +221,11 @@
       "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
       "dev": true,
       "requires": {
-        "ansi-styles": "^2.2.1",
-        "escape-string-regexp": "^1.0.2",
-        "has-ansi": "^2.0.0",
-        "strip-ansi": "^3.0.0",
-        "supports-color": "^2.0.0"
+        "ansi-styles": "2.2.1",
+        "escape-string-regexp": "1.0.5",
+        "has-ansi": "2.0.0",
+        "strip-ansi": "3.0.1",
+        "supports-color": "2.0.0"
       },
       "dependencies": {
         "supports-color": {
@@ -262,7 +262,7 @@
       "resolved": "http://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
       "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=",
       "requires": {
-        "graceful-readlink": ">= 1.0.0"
+        "graceful-readlink": "1.0.1"
       }
     },
     "concat-map": {
@@ -284,7 +284,7 @@
       "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
       "dev": true,
       "requires": {
-        "type-detect": "^4.0.0"
+        "type-detect": "4.0.8"
       }
     },
     "deep-equal": {
@@ -322,7 +322,7 @@
       "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
       "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
       "requires": {
-        "debug": "=3.1.0"
+        "debug": "3.1.0"
       },
       "dependencies": {
         "debug": {
@@ -351,12 +351,12 @@
       "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
       "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
       "requires": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^3.0.4",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
+        "fs.realpath": "1.0.0",
+        "inflight": "1.0.6",
+        "inherits": "2.0.3",
+        "minimatch": "3.0.4",
+        "once": "1.4.0",
+        "path-is-absolute": "1.0.1"
       }
     },
     "graceful-readlink": {
@@ -376,7 +376,7 @@
       "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
       "dev": true,
       "requires": {
-        "ansi-regex": "^2.0.0"
+        "ansi-regex": "2.1.1"
       }
     },
     "has-flag": {
@@ -396,8 +396,8 @@
       "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
       "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
       "requires": {
-        "once": "^1.3.0",
-        "wrappy": "1"
+        "once": "1.4.0",
+        "wrappy": "1.0.2"
       }
     },
     "inherits": {
@@ -428,8 +428,8 @@
       "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
       "dev": true,
       "requires": {
-        "argparse": "^1.0.7",
-        "esprima": "^4.0.0"
+        "argparse": "1.0.10",
+        "esprima": "4.0.1"
       }
     },
     "json-stringify-safe": {
@@ -472,7 +472,7 @@
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
       "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
       "requires": {
-        "brace-expansion": "^1.1.7"
+        "brace-expansion": "1.1.11"
       }
     },
     "minimist": {
@@ -530,12 +530,12 @@
           "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
           "dev": true,
           "requires": {
-            "fs.realpath": "^1.0.0",
-            "inflight": "^1.0.4",
-            "inherits": "2",
-            "minimatch": "^3.0.4",
-            "once": "^1.3.0",
-            "path-is-absolute": "^1.0.0"
+            "fs.realpath": "1.0.0",
+            "inflight": "1.0.6",
+            "inherits": "2.0.3",
+            "minimatch": "3.0.4",
+            "once": "1.4.0",
+            "path-is-absolute": "1.0.1"
           }
         }
       }
@@ -557,11 +557,11 @@
       "integrity": "sha512-kGASVhuL4tlAV0tvA34yJYZIVihrUt/5bDwpp4tTluigxUr2bBlJeDXmivb6NuEdFkqvdv/Ybb9dm16PSKUhtw==",
       "dev": true,
       "requires": {
-        "@sinonjs/formatio": "^3.1.0",
-        "just-extend": "^4.0.2",
-        "lolex": "^2.3.2",
-        "path-to-regexp": "^1.7.0",
-        "text-encoding": "^0.6.4"
+        "@sinonjs/formatio": "3.1.0",
+        "just-extend": "4.0.2",
+        "lolex": "2.7.5",
+        "path-to-regexp": "1.7.0",
+        "text-encoding": "0.6.4"
       },
       "dependencies": {
         "lolex": {
@@ -578,15 +578,15 @@
       "integrity": "sha512-b47OWj1qf/LqSQYnmokNWM8D88KvUl2y7jT0567NB3ZBAZFz2bWp2PC81Xn7u8F2/vJxzkzNZybnemeFa7AZ2w==",
       "dev": true,
       "requires": {
-        "chai": "^4.1.2",
-        "debug": "^4.1.0",
-        "deep-equal": "^1.0.0",
-        "json-stringify-safe": "^5.0.1",
-        "lodash": "^4.17.5",
-        "mkdirp": "^0.5.0",
-        "propagate": "^1.0.0",
-        "qs": "^6.5.1",
-        "semver": "^5.5.0"
+        "chai": "4.2.0",
+        "debug": "4.1.1",
+        "deep-equal": "1.0.1",
+        "json-stringify-safe": "5.0.1",
+        "lodash": "4.17.15",
+        "mkdirp": "0.5.1",
+        "propagate": "1.0.0",
+        "qs": "6.6.0",
+        "semver": "5.6.0"
       },
       "dependencies": {
         "debug": {
@@ -595,7 +595,7 @@
           "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
           "dev": true,
           "requires": {
-            "ms": "^2.1.1"
+            "ms": "2.1.1"
           }
         },
         "ms": {
@@ -606,202 +606,3251 @@
         }
       }
     },
-    "once": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
-      "requires": {
-        "wrappy": "1"
-      }
-    },
-    "path-is-absolute": {
-      "version": "1.0.1",
-      "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
-    },
-    "path-parse": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
-      "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
-      "dev": true
-    },
-    "path-to-regexp": {
-      "version": "1.7.0",
-      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
-      "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
-      "dev": true,
-      "requires": {
-        "isarray": "0.0.1"
-      }
-    },
-    "pathval": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz",
-      "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=",
-      "dev": true
-    },
-    "propagate": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/propagate/-/propagate-1.0.0.tgz",
-      "integrity": "sha1-AMLa7t2iDofjeCs0Stuhzd1q1wk=",
-      "dev": true
-    },
-    "qs": {
-      "version": "6.6.0",
-      "resolved": "https://registry.npmjs.org/qs/-/qs-6.6.0.tgz",
-      "integrity": "sha512-KIJqT9jQJDQx5h5uAVPimw6yVg2SekOKu959OCtktD3FjzbpvaPr8i4zzg07DOMz+igA4W/aNM7OV8H37pFYfA==",
-      "dev": true
-    },
-    "resolve": {
-      "version": "1.8.1",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz",
-      "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==",
-      "dev": true,
-      "requires": {
-        "path-parse": "^1.0.5"
-      }
-    },
-    "rimraf": {
-      "version": "2.5.4",
-      "resolved": "http://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz",
-      "integrity": "sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=",
-      "requires": {
-        "glob": "^7.0.5"
-      }
-    },
-    "semver": {
-      "version": "5.6.0",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
-      "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
-      "dev": true
-    },
-    "sinon": {
-      "version": "7.1.1",
-      "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.1.1.tgz",
-      "integrity": "sha512-iYagtjLVt1vN3zZY7D8oH7dkjNJEjLjyuzy8daX5+3bbQl8gaohrheB9VfH1O3L6LKuue5WTJvFluHiuZ9y3nQ==",
-      "dev": true,
+    "npm": {
+      "version": "6.11.3",
+      "resolved": "https://registry.npmjs.org/npm/-/npm-6.11.3.tgz",
+      "integrity": "sha512-K2h+MPzZiY39Xf6eHEdECe/LKoJXam4UCflz5kIxoskN3LQFeYs5fqBGT5i4TtM/aBk+86Mcf+jgXs/WuWAutQ==",
       "requires": {
-        "@sinonjs/commons": "^1.2.0",
-        "@sinonjs/formatio": "^3.0.0",
-        "@sinonjs/samsam": "^2.1.2",
-        "diff": "^3.5.0",
-        "lodash.get": "^4.4.2",
-        "lolex": "^3.0.0",
-        "nise": "^1.4.6",
-        "supports-color": "^5.5.0",
-        "type-detect": "^4.0.8"
+        "JSONStream": "1.3.5",
+        "abbrev": "1.1.1",
+        "ansicolors": "0.3.2",
+        "ansistyles": "0.1.3",
+        "aproba": "2.0.0",
+        "archy": "1.0.0",
+        "bin-links": "1.1.3",
+        "bluebird": "3.5.5",
+        "byte-size": "5.0.1",
+        "cacache": "12.0.3",
+        "call-limit": "1.1.1",
+        "chownr": "1.1.2",
+        "ci-info": "2.0.0",
+        "cli-columns": "3.1.2",
+        "cli-table3": "0.5.1",
+        "cmd-shim": "3.0.3",
+        "columnify": "1.5.4",
+        "config-chain": "1.1.12",
+        "debuglog": "1.0.1",
+        "detect-indent": "5.0.0",
+        "detect-newline": "2.1.0",
+        "dezalgo": "1.0.3",
+        "editor": "1.0.0",
+        "figgy-pudding": "3.5.1",
+        "find-npm-prefix": "1.0.2",
+        "fs-vacuum": "1.2.10",
+        "fs-write-stream-atomic": "1.0.10",
+        "gentle-fs": "2.2.1",
+        "glob": "7.1.4",
+        "graceful-fs": "4.2.2",
+        "has-unicode": "2.0.1",
+        "hosted-git-info": "2.8.2",
+        "iferr": "1.0.2",
+        "imurmurhash": "0.1.4",
+        "infer-owner": "1.0.4",
+        "inflight": "1.0.6",
+        "inherits": "2.0.4",
+        "ini": "1.3.5",
+        "init-package-json": "1.10.3",
+        "is-cidr": "3.0.0",
+        "json-parse-better-errors": "1.0.2",
+        "lazy-property": "1.0.0",
+        "libcipm": "4.0.3",
+        "libnpm": "3.0.1",
+        "libnpmaccess": "3.0.2",
+        "libnpmhook": "5.0.3",
+        "libnpmorg": "1.0.1",
+        "libnpmsearch": "2.0.2",
+        "libnpmteam": "1.0.2",
+        "libnpx": "10.2.0",
+        "lock-verify": "2.1.0",
+        "lockfile": "1.0.4",
+        "lodash._baseindexof": "3.1.0",
+        "lodash._baseuniq": "4.6.0",
+        "lodash._bindcallback": "3.0.1",
+        "lodash._cacheindexof": "3.0.2",
+        "lodash._createcache": "3.1.2",
+        "lodash._getnative": "3.9.1",
+        "lodash.clonedeep": "4.5.0",
+        "lodash.restparam": "3.6.1",
+        "lodash.union": "4.6.0",
+        "lodash.uniq": "4.5.0",
+        "lodash.without": "4.4.0",
+        "lru-cache": "5.1.1",
+        "meant": "1.0.1",
+        "mississippi": "3.0.0",
+        "mkdirp": "0.5.1",
+        "move-concurrently": "1.0.1",
+        "node-gyp": "5.0.3",
+        "nopt": "4.0.1",
+        "normalize-package-data": "2.5.0",
+        "npm-audit-report": "1.3.2",
+        "npm-cache-filename": "1.0.2",
+        "npm-install-checks": "3.0.0",
+        "npm-lifecycle": "3.1.3",
+        "npm-package-arg": "6.1.1",
+        "npm-packlist": "1.4.4",
+        "npm-pick-manifest": "3.0.2",
+        "npm-profile": "4.0.2",
+        "npm-registry-fetch": "4.0.0",
+        "npm-user-validate": "1.0.0",
+        "npmlog": "4.1.2",
+        "once": "1.4.0",
+        "opener": "1.5.1",
+        "osenv": "0.1.5",
+        "pacote": "9.5.8",
+        "path-is-inside": "1.0.2",
+        "promise-inflight": "1.0.1",
+        "qrcode-terminal": "0.12.0",
+        "query-string": "6.8.2",
+        "qw": "1.0.1",
+        "read": "1.0.7",
+        "read-cmd-shim": "1.0.4",
+        "read-installed": "4.0.3",
+        "read-package-json": "2.1.0",
+        "read-package-tree": "5.3.1",
+        "readable-stream": "3.4.0",
+        "readdir-scoped-modules": "1.1.0",
+        "request": "2.88.0",
+        "retry": "0.12.0",
+        "rimraf": "2.6.3",
+        "safe-buffer": "5.1.2",
+        "semver": "5.7.1",
+        "sha": "3.0.0",
+        "slide": "1.1.6",
+        "sorted-object": "2.0.1",
+        "sorted-union-stream": "2.1.3",
+        "ssri": "6.0.1",
+        "stringify-package": "1.0.0",
+        "tar": "4.4.10",
+        "text-table": "0.2.0",
+        "tiny-relative-date": "1.3.0",
+        "uid-number": "0.0.6",
+        "umask": "1.1.0",
+        "unique-filename": "1.1.1",
+        "unpipe": "1.0.0",
+        "update-notifier": "2.5.0",
+        "uuid": "3.3.2",
+        "validate-npm-package-license": "3.0.4",
+        "validate-npm-package-name": "3.0.0",
+        "which": "1.3.1",
+        "worker-farm": "1.7.0",
+        "write-file-atomic": "2.4.3"
       },
       "dependencies": {
-        "supports-color": {
-          "version": "5.5.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
-          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-          "dev": true,
+        "JSONStream": {
+          "version": "1.3.5",
+          "bundled": true,
           "requires": {
-            "has-flag": "^3.0.0"
+            "jsonparse": "1.3.1",
+            "through": "2.3.8"
           }
-        }
-      }
-    },
-    "source-map": {
-      "version": "0.5.7",
-      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-      "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-      "dev": true
-    },
-    "source-map-support": {
-      "version": "0.4.18",
-      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
-      "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
-      "dev": true,
-      "requires": {
-        "source-map": "^0.5.6"
-      }
-    },
-    "sprintf-js": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
-      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
-      "dev": true
-    },
-    "strip-ansi": {
-      "version": "3.0.1",
-      "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
-      "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
-      "dev": true,
-      "requires": {
-        "ansi-regex": "^2.0.0"
-      }
-    },
-    "supports-color": {
-      "version": "5.4.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
-      "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
-      "dev": true,
-      "requires": {
-        "has-flag": "^3.0.0"
-      }
-    },
-    "text-encoding": {
-      "version": "0.6.4",
-      "resolved": "http://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz",
-      "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=",
-      "dev": true
-    },
-    "ts-node": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-1.0.0.tgz",
-      "integrity": "sha1-ufGO9Zyrpn7JXD6eYHDDBF0FZms=",
-      "dev": true,
-      "requires": {
-        "arrify": "^1.0.0",
-        "chalk": "^1.1.1",
-        "diff": "^2.1.1",
-        "make-error": "^1.1.1",
-        "minimist": "^1.2.0",
-        "mkdirp": "^0.5.1",
-        "source-map-support": "^0.4.0",
-        "xtend": "^4.0.0"
-      },
-      "dependencies": {
-        "diff": {
-          "version": "2.2.3",
-          "resolved": "https://registry.npmjs.org/diff/-/diff-2.2.3.tgz",
-          "integrity": "sha1-YOr9DSjukG5Oj/ClLBIpUhAzv5k=",
-          "dev": true
         },
-        "minimist": {
-          "version": "1.2.0",
-          "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
-          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
-          "dev": true
-        }
-      }
-    },
-    "tslib": {
-      "version": "1.9.3",
-      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
-      "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==",
-      "dev": true
-    },
-    "tslint": {
-      "version": "5.11.0",
-      "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.11.0.tgz",
-      "integrity": "sha1-mPMMAurjzecAYgHkwzywi0hYHu0=",
-      "dev": true,
-      "requires": {
-        "babel-code-frame": "^6.22.0",
-        "builtin-modules": "^1.1.1",
-        "chalk": "^2.3.0",
-        "commander": "^2.12.1",
-        "diff": "^3.2.0",
-        "glob": "^7.1.1",
-        "js-yaml": "^3.7.0",
-        "minimatch": "^3.0.4",
-        "resolve": "^1.3.2",
-        "semver": "^5.3.0",
-        "tslib": "^1.8.0",
-        "tsutils": "^2.27.2"
+        "abbrev": {
+          "version": "1.1.1",
+          "bundled": true
+        },
+        "agent-base": {
+          "version": "4.3.0",
+          "bundled": true,
+          "requires": {
+            "es6-promisify": "5.0.0"
+          }
+        },
+        "agentkeepalive": {
+          "version": "3.5.2",
+          "bundled": true,
+          "requires": {
+            "humanize-ms": "1.2.1"
+          }
+        },
+        "ajv": {
+          "version": "5.5.2",
+          "bundled": true,
+          "requires": {
+            "co": "4.6.0",
+            "fast-deep-equal": "1.1.0",
+            "fast-json-stable-stringify": "2.0.0",
+            "json-schema-traverse": "0.3.1"
+          }
+        },
+        "ansi-align": {
+          "version": "2.0.0",
+          "bundled": true,
+          "requires": {
+            "string-width": "2.1.1"
+          }
+        },
+        "ansi-regex": {
+          "version": "2.1.1",
+          "bundled": true
+        },
+        "ansi-styles": {
+          "version": "3.2.1",
+          "bundled": true,
+          "requires": {
+            "color-convert": "1.9.1"
+          }
+        },
+        "ansicolors": {
+          "version": "0.3.2",
+          "bundled": true
+        },
+        "ansistyles": {
+          "version": "0.1.3",
+          "bundled": true
+        },
+        "aproba": {
+          "version": "2.0.0",
+          "bundled": true
+        },
+        "archy": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "are-we-there-yet": {
+          "version": "1.1.4",
+          "bundled": true,
+          "requires": {
+            "delegates": "1.0.0",
+            "readable-stream": "2.3.6"
+          },
+          "dependencies": {
+            "readable-stream": {
+              "version": "2.3.6",
+              "bundled": true,
+              "requires": {
+                "core-util-is": "1.0.2",
+                "inherits": "2.0.4",
+                "isarray": "1.0.0",
+                "process-nextick-args": "2.0.0",
+                "safe-buffer": "5.1.2",
+                "string_decoder": "1.1.1",
+                "util-deprecate": "1.0.2"
+              }
+            },
+            "string_decoder": {
+              "version": "1.1.1",
+              "bundled": true,
+              "requires": {
+                "safe-buffer": "5.1.2"
+              }
+            }
+          }
+        },
+        "asap": {
+          "version": "2.0.6",
+          "bundled": true
+        },
+        "asn1": {
+          "version": "0.2.4",
+          "bundled": true,
+          "requires": {
+            "safer-buffer": "2.1.2"
+          }
+        },
+        "assert-plus": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "asynckit": {
+          "version": "0.4.0",
+          "bundled": true
+        },
+        "aws-sign2": {
+          "version": "0.7.0",
+          "bundled": true
+        },
+        "aws4": {
+          "version": "1.8.0",
+          "bundled": true
+        },
+        "balanced-match": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "bcrypt-pbkdf": {
+          "version": "1.0.2",
+          "bundled": true,
+          "optional": true,
+          "requires": {
+            "tweetnacl": "0.14.5"
+          }
+        },
+        "bin-links": {
+          "version": "1.1.3",
+          "bundled": true,
+          "requires": {
+            "bluebird": "3.5.5",
+            "cmd-shim": "3.0.3",
+            "gentle-fs": "2.2.1",
+            "graceful-fs": "4.2.2",
+            "write-file-atomic": "2.4.3"
+          }
+        },
+        "bluebird": {
+          "version": "3.5.5",
+          "bundled": true
+        },
+        "boxen": {
+          "version": "1.3.0",
+          "bundled": true,
+          "requires": {
+            "ansi-align": "2.0.0",
+            "camelcase": "4.1.0",
+            "chalk": "2.4.1",
+            "cli-boxes": "1.0.0",
+            "string-width": "2.1.1",
+            "term-size": "1.2.0",
+            "widest-line": "2.0.0"
+          }
+        },
+        "brace-expansion": {
+          "version": "1.1.11",
+          "bundled": true,
+          "requires": {
+            "balanced-match": "1.0.0",
+            "concat-map": "0.0.1"
+          }
+        },
+        "buffer-from": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "builtins": {
+          "version": "1.0.3",
+          "bundled": true
+        },
+        "byline": {
+          "version": "5.0.0",
+          "bundled": true
+        },
+        "byte-size": {
+          "version": "5.0.1",
+          "bundled": true
+        },
+        "cacache": {
+          "version": "12.0.3",
+          "bundled": true,
+          "requires": {
+            "bluebird": "3.5.5",
+            "chownr": "1.1.2",
+            "figgy-pudding": "3.5.1",
+            "glob": "7.1.4",
+            "graceful-fs": "4.2.2",
+            "infer-owner": "1.0.4",
+            "lru-cache": "5.1.1",
+            "mississippi": "3.0.0",
+            "mkdirp": "0.5.1",
+            "move-concurrently": "1.0.1",
+            "promise-inflight": "1.0.1",
+            "rimraf": "2.6.3",
+            "ssri": "6.0.1",
+            "unique-filename": "1.1.1",
+            "y18n": "4.0.0"
+          }
+        },
+        "call-limit": {
+          "version": "1.1.1",
+          "bundled": true
+        },
+        "camelcase": {
+          "version": "4.1.0",
+          "bundled": true
+        },
+        "capture-stack-trace": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "caseless": {
+          "version": "0.12.0",
+          "bundled": true
+        },
+        "chalk": {
+          "version": "2.4.1",
+          "bundled": true,
+          "requires": {
+            "ansi-styles": "3.2.1",
+            "escape-string-regexp": "1.0.5",
+            "supports-color": "5.4.0"
+          }
+        },
+        "chownr": {
+          "version": "1.1.2",
+          "bundled": true
+        },
+        "ci-info": {
+          "version": "2.0.0",
+          "bundled": true
+        },
+        "cidr-regex": {
+          "version": "2.0.10",
+          "bundled": true,
+          "requires": {
+            "ip-regex": "2.1.0"
+          }
+        },
+        "cli-boxes": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "cli-columns": {
+          "version": "3.1.2",
+          "bundled": true,
+          "requires": {
+            "string-width": "2.1.1",
+            "strip-ansi": "3.0.1"
+          }
+        },
+        "cli-table3": {
+          "version": "0.5.1",
+          "bundled": true,
+          "requires": {
+            "colors": "1.3.3",
+            "object-assign": "4.1.1",
+            "string-width": "2.1.1"
+          }
+        },
+        "cliui": {
+          "version": "4.1.0",
+          "bundled": true,
+          "requires": {
+            "string-width": "2.1.1",
+            "strip-ansi": "4.0.0",
+            "wrap-ansi": "2.1.0"
+          },
+          "dependencies": {
+            "ansi-regex": {
+              "version": "3.0.0",
+              "bundled": true
+            },
+            "strip-ansi": {
+              "version": "4.0.0",
+              "bundled": true,
+              "requires": {
+                "ansi-regex": "3.0.0"
+              }
+            }
+          }
+        },
+        "clone": {
+          "version": "1.0.4",
+          "bundled": true
+        },
+        "cmd-shim": {
+          "version": "3.0.3",
+          "bundled": true,
+          "requires": {
+            "graceful-fs": "4.2.2",
+            "mkdirp": "0.5.1"
+          }
+        },
+        "co": {
+          "version": "4.6.0",
+          "bundled": true
+        },
+        "code-point-at": {
+          "version": "1.1.0",
+          "bundled": true
+        },
+        "color-convert": {
+          "version": "1.9.1",
+          "bundled": true,
+          "requires": {
+            "color-name": "1.1.3"
+          }
+        },
+        "color-name": {
+          "version": "1.1.3",
+          "bundled": true
+        },
+        "colors": {
+          "version": "1.3.3",
+          "bundled": true,
+          "optional": true
+        },
+        "columnify": {
+          "version": "1.5.4",
+          "bundled": true,
+          "requires": {
+            "strip-ansi": "3.0.1",
+            "wcwidth": "1.0.1"
+          }
+        },
+        "combined-stream": {
+          "version": "1.0.6",
+          "bundled": true,
+          "requires": {
+            "delayed-stream": "1.0.0"
+          }
+        },
+        "concat-map": {
+          "version": "0.0.1",
+          "bundled": true
+        },
+        "concat-stream": {
+          "version": "1.6.2",
+          "bundled": true,
+          "requires": {
+            "buffer-from": "1.0.0",
+            "inherits": "2.0.4",
+            "readable-stream": "2.3.6",
+            "typedarray": "0.0.6"
+          },
+          "dependencies": {
+            "readable-stream": {
+              "version": "2.3.6",
+              "bundled": true,
+              "requires": {
+                "core-util-is": "1.0.2",
+                "inherits": "2.0.4",
+                "isarray": "1.0.0",
+                "process-nextick-args": "2.0.0",
+                "safe-buffer": "5.1.2",
+                "string_decoder": "1.1.1",
+                "util-deprecate": "1.0.2"
+              }
+            },
+            "string_decoder": {
+              "version": "1.1.1",
+              "bundled": true,
+              "requires": {
+                "safe-buffer": "5.1.2"
+              }
+            }
+          }
+        },
+        "config-chain": {
+          "version": "1.1.12",
+          "bundled": true,
+          "requires": {
+            "ini": "1.3.5",
+            "proto-list": "1.2.4"
+          }
+        },
+        "configstore": {
+          "version": "3.1.2",
+          "bundled": true,
+          "requires": {
+            "dot-prop": "4.2.0",
+            "graceful-fs": "4.2.2",
+            "make-dir": "1.3.0",
+            "unique-string": "1.0.0",
+            "write-file-atomic": "2.4.3",
+            "xdg-basedir": "3.0.0"
+          }
+        },
+        "console-control-strings": {
+          "version": "1.1.0",
+          "bundled": true
+        },
+        "copy-concurrently": {
+          "version": "1.0.5",
+          "bundled": true,
+          "requires": {
+            "aproba": "1.2.0",
+            "fs-write-stream-atomic": "1.0.10",
+            "iferr": "0.1.5",
+            "mkdirp": "0.5.1",
+            "rimraf": "2.6.3",
+            "run-queue": "1.0.3"
+          },
+          "dependencies": {
+            "aproba": {
+              "version": "1.2.0",
+              "bundled": true
+            },
+            "iferr": {
+              "version": "0.1.5",
+              "bundled": true
+            }
+          }
+        },
+        "core-util-is": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "create-error-class": {
+          "version": "3.0.2",
+          "bundled": true,
+          "requires": {
+            "capture-stack-trace": "1.0.0"
+          }
+        },
+        "cross-spawn": {
+          "version": "5.1.0",
+          "bundled": true,
+          "requires": {
+            "lru-cache": "4.1.5",
+            "shebang-command": "1.2.0",
+            "which": "1.3.1"
+          },
+          "dependencies": {
+            "lru-cache": {
+              "version": "4.1.5",
+              "bundled": true,
+              "requires": {
+                "pseudomap": "1.0.2",
+                "yallist": "2.1.2"
+              }
+            },
+            "yallist": {
+              "version": "2.1.2",
+              "bundled": true
+            }
+          }
+        },
+        "crypto-random-string": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "cyclist": {
+          "version": "0.2.2",
+          "bundled": true
+        },
+        "dashdash": {
+          "version": "1.14.1",
+          "bundled": true,
+          "requires": {
+            "assert-plus": "1.0.0"
+          }
+        },
+        "debug": {
+          "version": "3.1.0",
+          "bundled": true,
+          "requires": {
+            "ms": "2.0.0"
+          },
+          "dependencies": {
+            "ms": {
+              "version": "2.0.0",
+              "bundled": true
+            }
+          }
+        },
+        "debuglog": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "decamelize": {
+          "version": "1.2.0",
+          "bundled": true
+        },
+        "decode-uri-component": {
+          "version": "0.2.0",
+          "bundled": true
+        },
+        "deep-extend": {
+          "version": "0.5.1",
+          "bundled": true
+        },
+        "defaults": {
+          "version": "1.0.3",
+          "bundled": true,
+          "requires": {
+            "clone": "1.0.4"
+          }
+        },
+        "define-properties": {
+          "version": "1.1.3",
+          "bundled": true,
+          "requires": {
+            "object-keys": "1.0.12"
+          }
+        },
+        "delayed-stream": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "delegates": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "detect-indent": {
+          "version": "5.0.0",
+          "bundled": true
+        },
+        "detect-newline": {
+          "version": "2.1.0",
+          "bundled": true
+        },
+        "dezalgo": {
+          "version": "1.0.3",
+          "bundled": true,
+          "requires": {
+            "asap": "2.0.6",
+            "wrappy": "1.0.2"
+          }
+        },
+        "dot-prop": {
+          "version": "4.2.0",
+          "bundled": true,
+          "requires": {
+            "is-obj": "1.0.1"
+          }
+        },
+        "dotenv": {
+          "version": "5.0.1",
+          "bundled": true
+        },
+        "duplexer3": {
+          "version": "0.1.4",
+          "bundled": true
+        },
+        "duplexify": {
+          "version": "3.6.0",
+          "bundled": true,
+          "requires": {
+            "end-of-stream": "1.4.1",
+            "inherits": "2.0.4",
+            "readable-stream": "2.3.6",
+            "stream-shift": "1.0.0"
+          },
+          "dependencies": {
+            "readable-stream": {
+              "version": "2.3.6",
+              "bundled": true,
+              "requires": {
+                "core-util-is": "1.0.2",
+                "inherits": "2.0.4",
+                "isarray": "1.0.0",
+                "process-nextick-args": "2.0.0",
+                "safe-buffer": "5.1.2",
+                "string_decoder": "1.1.1",
+                "util-deprecate": "1.0.2"
+              }
+            },
+            "string_decoder": {
+              "version": "1.1.1",
+              "bundled": true,
+              "requires": {
+                "safe-buffer": "5.1.2"
+              }
+            }
+          }
+        },
+        "ecc-jsbn": {
+          "version": "0.1.2",
+          "bundled": true,
+          "optional": true,
+          "requires": {
+            "jsbn": "0.1.1",
+            "safer-buffer": "2.1.2"
+          }
+        },
+        "editor": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "encoding": {
+          "version": "0.1.12",
+          "bundled": true,
+          "requires": {
+            "iconv-lite": "0.4.23"
+          }
+        },
+        "end-of-stream": {
+          "version": "1.4.1",
+          "bundled": true,
+          "requires": {
+            "once": "1.4.0"
+          }
+        },
+        "env-paths": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "err-code": {
+          "version": "1.1.2",
+          "bundled": true
+        },
+        "errno": {
+          "version": "0.1.7",
+          "bundled": true,
+          "requires": {
+            "prr": "1.0.1"
+          }
+        },
+        "es-abstract": {
+          "version": "1.12.0",
+          "bundled": true,
+          "requires": {
+            "es-to-primitive": "1.2.0",
+            "function-bind": "1.1.1",
+            "has": "1.0.3",
+            "is-callable": "1.1.4",
+            "is-regex": "1.0.4"
+          }
+        },
+        "es-to-primitive": {
+          "version": "1.2.0",
+          "bundled": true,
+          "requires": {
+            "is-callable": "1.1.4",
+            "is-date-object": "1.0.1",
+            "is-symbol": "1.0.2"
+          }
+        },
+        "es6-promise": {
+          "version": "4.2.8",
+          "bundled": true
+        },
+        "es6-promisify": {
+          "version": "5.0.0",
+          "bundled": true,
+          "requires": {
+            "es6-promise": "4.2.8"
+          }
+        },
+        "escape-string-regexp": {
+          "version": "1.0.5",
+          "bundled": true
+        },
+        "execa": {
+          "version": "0.7.0",
+          "bundled": true,
+          "requires": {
+            "cross-spawn": "5.1.0",
+            "get-stream": "3.0.0",
+            "is-stream": "1.1.0",
+            "npm-run-path": "2.0.2",
+            "p-finally": "1.0.0",
+            "signal-exit": "3.0.2",
+            "strip-eof": "1.0.0"
+          },
+          "dependencies": {
+            "get-stream": {
+              "version": "3.0.0",
+              "bundled": true
+            }
+          }
+        },
+        "extend": {
+          "version": "3.0.2",
+          "bundled": true
+        },
+        "extsprintf": {
+          "version": "1.3.0",
+          "bundled": true
+        },
+        "fast-deep-equal": {
+          "version": "1.1.0",
+          "bundled": true
+        },
+        "fast-json-stable-stringify": {
+          "version": "2.0.0",
+          "bundled": true
+        },
+        "figgy-pudding": {
+          "version": "3.5.1",
+          "bundled": true
+        },
+        "find-npm-prefix": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "find-up": {
+          "version": "2.1.0",
+          "bundled": true,
+          "requires": {
+            "locate-path": "2.0.0"
+          }
+        },
+        "flush-write-stream": {
+          "version": "1.0.3",
+          "bundled": true,
+          "requires": {
+            "inherits": "2.0.4",
+            "readable-stream": "2.3.6"
+          },
+          "dependencies": {
+            "readable-stream": {
+              "version": "2.3.6",
+              "bundled": true,
+              "requires": {
+                "core-util-is": "1.0.2",
+                "inherits": "2.0.4",
+                "isarray": "1.0.0",
+                "process-nextick-args": "2.0.0",
+                "safe-buffer": "5.1.2",
+                "string_decoder": "1.1.1",
+                "util-deprecate": "1.0.2"
+              }
+            },
+            "string_decoder": {
+              "version": "1.1.1",
+              "bundled": true,
+              "requires": {
+                "safe-buffer": "5.1.2"
+              }
+            }
+          }
+        },
+        "forever-agent": {
+          "version": "0.6.1",
+          "bundled": true
+        },
+        "form-data": {
+          "version": "2.3.2",
+          "bundled": true,
+          "requires": {
+            "asynckit": "0.4.0",
+            "combined-stream": "1.0.6",
+            "mime-types": "2.1.19"
+          }
+        },
+        "from2": {
+          "version": "2.3.0",
+          "bundled": true,
+          "requires": {
+            "inherits": "2.0.4",
+            "readable-stream": "2.3.6"
+          },
+          "dependencies": {
+            "readable-stream": {
+              "version": "2.3.6",
+              "bundled": true,
+              "requires": {
+                "core-util-is": "1.0.2",
+                "inherits": "2.0.4",
+                "isarray": "1.0.0",
+                "process-nextick-args": "2.0.0",
+                "safe-buffer": "5.1.2",
+                "string_decoder": "1.1.1",
+                "util-deprecate": "1.0.2"
+              }
+            },
+            "string_decoder": {
+              "version": "1.1.1",
+              "bundled": true,
+              "requires": {
+                "safe-buffer": "5.1.2"
+              }
+            }
+          }
+        },
+        "fs-minipass": {
+          "version": "1.2.6",
+          "bundled": true,
+          "requires": {
+            "minipass": "2.3.3"
+          }
+        },
+        "fs-vacuum": {
+          "version": "1.2.10",
+          "bundled": true,
+          "requires": {
+            "graceful-fs": "4.2.2",
+            "path-is-inside": "1.0.2",
+            "rimraf": "2.6.3"
+          }
+        },
+        "fs-write-stream-atomic": {
+          "version": "1.0.10",
+          "bundled": true,
+          "requires": {
+            "graceful-fs": "4.2.2",
+            "iferr": "0.1.5",
+            "imurmurhash": "0.1.4",
+            "readable-stream": "2.3.6"
+          },
+          "dependencies": {
+            "iferr": {
+              "version": "0.1.5",
+              "bundled": true
+            },
+            "readable-stream": {
+              "version": "2.3.6",
+              "bundled": true,
+              "requires": {
+                "core-util-is": "1.0.2",
+                "inherits": "2.0.4",
+                "isarray": "1.0.0",
+                "process-nextick-args": "2.0.0",
+                "safe-buffer": "5.1.2",
+                "string_decoder": "1.1.1",
+                "util-deprecate": "1.0.2"
+              }
+            },
+            "string_decoder": {
+              "version": "1.1.1",
+              "bundled": true,
+              "requires": {
+                "safe-buffer": "5.1.2"
+              }
+            }
+          }
+        },
+        "fs.realpath": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "function-bind": {
+          "version": "1.1.1",
+          "bundled": true
+        },
+        "gauge": {
+          "version": "2.7.4",
+          "bundled": true,
+          "requires": {
+            "aproba": "1.2.0",
+            "console-control-strings": "1.1.0",
+            "has-unicode": "2.0.1",
+            "object-assign": "4.1.1",
+            "signal-exit": "3.0.2",
+            "string-width": "1.0.2",
+            "strip-ansi": "3.0.1",
+            "wide-align": "1.1.2"
+          },
+          "dependencies": {
+            "aproba": {
+              "version": "1.2.0",
+              "bundled": true
+            },
+            "string-width": {
+              "version": "1.0.2",
+              "bundled": true,
+              "requires": {
+                "code-point-at": "1.1.0",
+                "is-fullwidth-code-point": "1.0.0",
+                "strip-ansi": "3.0.1"
+              }
+            }
+          }
+        },
+        "genfun": {
+          "version": "5.0.0",
+          "bundled": true
+        },
+        "gentle-fs": {
+          "version": "2.2.1",
+          "bundled": true,
+          "requires": {
+            "aproba": "1.2.0",
+            "chownr": "1.1.2",
+            "fs-vacuum": "1.2.10",
+            "graceful-fs": "4.2.2",
+            "iferr": "0.1.5",
+            "infer-owner": "1.0.4",
+            "mkdirp": "0.5.1",
+            "path-is-inside": "1.0.2",
+            "read-cmd-shim": "1.0.4",
+            "slide": "1.1.6"
+          },
+          "dependencies": {
+            "aproba": {
+              "version": "1.2.0",
+              "bundled": true
+            },
+            "iferr": {
+              "version": "0.1.5",
+              "bundled": true
+            }
+          }
+        },
+        "get-caller-file": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "get-stream": {
+          "version": "4.1.0",
+          "bundled": true,
+          "requires": {
+            "pump": "3.0.0"
+          }
+        },
+        "getpass": {
+          "version": "0.1.7",
+          "bundled": true,
+          "requires": {
+            "assert-plus": "1.0.0"
+          }
+        },
+        "glob": {
+          "version": "7.1.4",
+          "bundled": true,
+          "requires": {
+            "fs.realpath": "1.0.0",
+            "inflight": "1.0.6",
+            "inherits": "2.0.4",
+            "minimatch": "3.0.4",
+            "once": "1.4.0",
+            "path-is-absolute": "1.0.1"
+          }
+        },
+        "global-dirs": {
+          "version": "0.1.1",
+          "bundled": true,
+          "requires": {
+            "ini": "1.3.5"
+          }
+        },
+        "got": {
+          "version": "6.7.1",
+          "bundled": true,
+          "requires": {
+            "create-error-class": "3.0.2",
+            "duplexer3": "0.1.4",
+            "get-stream": "3.0.0",
+            "is-redirect": "1.0.0",
+            "is-retry-allowed": "1.1.0",
+            "is-stream": "1.1.0",
+            "lowercase-keys": "1.0.1",
+            "safe-buffer": "5.1.2",
+            "timed-out": "4.0.1",
+            "unzip-response": "2.0.1",
+            "url-parse-lax": "1.0.0"
+          },
+          "dependencies": {
+            "get-stream": {
+              "version": "3.0.0",
+              "bundled": true
+            }
+          }
+        },
+        "graceful-fs": {
+          "version": "4.2.2",
+          "bundled": true
+        },
+        "har-schema": {
+          "version": "2.0.0",
+          "bundled": true
+        },
+        "har-validator": {
+          "version": "5.1.0",
+          "bundled": true,
+          "requires": {
+            "ajv": "5.5.2",
+            "har-schema": "2.0.0"
+          }
+        },
+        "has": {
+          "version": "1.0.3",
+          "bundled": true,
+          "requires": {
+            "function-bind": "1.1.1"
+          }
+        },
+        "has-flag": {
+          "version": "3.0.0",
+          "bundled": true
+        },
+        "has-symbols": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "has-unicode": {
+          "version": "2.0.1",
+          "bundled": true
+        },
+        "hosted-git-info": {
+          "version": "2.8.2",
+          "bundled": true,
+          "requires": {
+            "lru-cache": "5.1.1"
+          }
+        },
+        "http-cache-semantics": {
+          "version": "3.8.1",
+          "bundled": true
+        },
+        "http-proxy-agent": {
+          "version": "2.1.0",
+          "bundled": true,
+          "requires": {
+            "agent-base": "4.3.0",
+            "debug": "3.1.0"
+          }
+        },
+        "http-signature": {
+          "version": "1.2.0",
+          "bundled": true,
+          "requires": {
+            "assert-plus": "1.0.0",
+            "jsprim": "1.4.1",
+            "sshpk": "1.14.2"
+          }
+        },
+        "https-proxy-agent": {
+          "version": "2.2.2",
+          "bundled": true,
+          "requires": {
+            "agent-base": "4.3.0",
+            "debug": "3.1.0"
+          }
+        },
+        "humanize-ms": {
+          "version": "1.2.1",
+          "bundled": true,
+          "requires": {
+            "ms": "2.1.1"
+          }
+        },
+        "iconv-lite": {
+          "version": "0.4.23",
+          "bundled": true,
+          "requires": {
+            "safer-buffer": "2.1.2"
+          }
+        },
+        "iferr": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "ignore-walk": {
+          "version": "3.0.1",
+          "bundled": true,
+          "requires": {
+            "minimatch": "3.0.4"
+          }
+        },
+        "import-lazy": {
+          "version": "2.1.0",
+          "bundled": true
+        },
+        "imurmurhash": {
+          "version": "0.1.4",
+          "bundled": true
+        },
+        "infer-owner": {
+          "version": "1.0.4",
+          "bundled": true
+        },
+        "inflight": {
+          "version": "1.0.6",
+          "bundled": true,
+          "requires": {
+            "once": "1.4.0",
+            "wrappy": "1.0.2"
+          }
+        },
+        "inherits": {
+          "version": "2.0.4",
+          "bundled": true
+        },
+        "ini": {
+          "version": "1.3.5",
+          "bundled": true
+        },
+        "init-package-json": {
+          "version": "1.10.3",
+          "bundled": true,
+          "requires": {
+            "glob": "7.1.4",
+            "npm-package-arg": "6.1.1",
+            "promzard": "0.3.0",
+            "read": "1.0.7",
+            "read-package-json": "2.1.0",
+            "semver": "5.7.1",
+            "validate-npm-package-license": "3.0.4",
+            "validate-npm-package-name": "3.0.0"
+          }
+        },
+        "invert-kv": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "ip": {
+          "version": "1.1.5",
+          "bundled": true
+        },
+        "ip-regex": {
+          "version": "2.1.0",
+          "bundled": true
+        },
+        "is-callable": {
+          "version": "1.1.4",
+          "bundled": true
+        },
+        "is-ci": {
+          "version": "1.1.0",
+          "bundled": true,
+          "requires": {
+            "ci-info": "1.6.0"
+          },
+          "dependencies": {
+            "ci-info": {
+              "version": "1.6.0",
+              "bundled": true
+            }
+          }
+        },
+        "is-cidr": {
+          "version": "3.0.0",
+          "bundled": true,
+          "requires": {
+            "cidr-regex": "2.0.10"
+          }
+        },
+        "is-date-object": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "is-fullwidth-code-point": {
+          "version": "1.0.0",
+          "bundled": true,
+          "requires": {
+            "number-is-nan": "1.0.1"
+          }
+        },
+        "is-installed-globally": {
+          "version": "0.1.0",
+          "bundled": true,
+          "requires": {
+            "global-dirs": "0.1.1",
+            "is-path-inside": "1.0.1"
+          }
+        },
+        "is-npm": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "is-obj": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "is-path-inside": {
+          "version": "1.0.1",
+          "bundled": true,
+          "requires": {
+            "path-is-inside": "1.0.2"
+          }
+        },
+        "is-redirect": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "is-regex": {
+          "version": "1.0.4",
+          "bundled": true,
+          "requires": {
+            "has": "1.0.3"
+          }
+        },
+        "is-retry-allowed": {
+          "version": "1.1.0",
+          "bundled": true
+        },
+        "is-stream": {
+          "version": "1.1.0",
+          "bundled": true
+        },
+        "is-symbol": {
+          "version": "1.0.2",
+          "bundled": true,
+          "requires": {
+            "has-symbols": "1.0.0"
+          }
+        },
+        "is-typedarray": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "isarray": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "isexe": {
+          "version": "2.0.0",
+          "bundled": true
+        },
+        "isstream": {
+          "version": "0.1.2",
+          "bundled": true
+        },
+        "jsbn": {
+          "version": "0.1.1",
+          "bundled": true,
+          "optional": true
+        },
+        "json-parse-better-errors": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "json-schema": {
+          "version": "0.2.3",
+          "bundled": true
+        },
+        "json-schema-traverse": {
+          "version": "0.3.1",
+          "bundled": true
+        },
+        "json-stringify-safe": {
+          "version": "5.0.1",
+          "bundled": true
+        },
+        "jsonparse": {
+          "version": "1.3.1",
+          "bundled": true
+        },
+        "jsprim": {
+          "version": "1.4.1",
+          "bundled": true,
+          "requires": {
+            "assert-plus": "1.0.0",
+            "extsprintf": "1.3.0",
+            "json-schema": "0.2.3",
+            "verror": "1.10.0"
+          }
+        },
+        "latest-version": {
+          "version": "3.1.0",
+          "bundled": true,
+          "requires": {
+            "package-json": "4.0.1"
+          }
+        },
+        "lazy-property": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "lcid": {
+          "version": "1.0.0",
+          "bundled": true,
+          "requires": {
+            "invert-kv": "1.0.0"
+          }
+        },
+        "libcipm": {
+          "version": "4.0.3",
+          "bundled": true,
+          "requires": {
+            "bin-links": "1.1.3",
+            "bluebird": "3.5.5",
+            "figgy-pudding": "3.5.1",
+            "find-npm-prefix": "1.0.2",
+            "graceful-fs": "4.2.2",
+            "ini": "1.3.5",
+            "lock-verify": "2.1.0",
+            "mkdirp": "0.5.1",
+            "npm-lifecycle": "3.1.3",
+            "npm-logical-tree": "1.2.1",
+            "npm-package-arg": "6.1.1",
+            "pacote": "9.5.8",
+            "read-package-json": "2.1.0",
+            "rimraf": "2.6.3",
+            "worker-farm": "1.7.0"
+          }
+        },
+        "libnpm": {
+          "version": "3.0.1",
+          "bundled": true,
+          "requires": {
+            "bin-links": "1.1.3",
+            "bluebird": "3.5.5",
+            "find-npm-prefix": "1.0.2",
+            "libnpmaccess": "3.0.2",
+            "libnpmconfig": "1.2.1",
+            "libnpmhook": "5.0.3",
+            "libnpmorg": "1.0.1",
+            "libnpmpublish": "1.1.2",
+            "libnpmsearch": "2.0.2",
+            "libnpmteam": "1.0.2",
+            "lock-verify": "2.1.0",
+            "npm-lifecycle": "3.1.3",
+            "npm-logical-tree": "1.2.1",
+            "npm-package-arg": "6.1.1",
+            "npm-profile": "4.0.2",
+            "npm-registry-fetch": "4.0.0",
+            "npmlog": "4.1.2",
+            "pacote": "9.5.8",
+            "read-package-json": "2.1.0",
+            "stringify-package": "1.0.0"
+          }
+        },
+        "libnpmaccess": {
+          "version": "3.0.2",
+          "bundled": true,
+          "requires": {
+            "aproba": "2.0.0",
+            "get-stream": "4.1.0",
+            "npm-package-arg": "6.1.1",
+            "npm-registry-fetch": "4.0.0"
+          }
+        },
+        "libnpmconfig": {
+          "version": "1.2.1",
+          "bundled": true,
+          "requires": {
+            "figgy-pudding": "3.5.1",
+            "find-up": "3.0.0",
+            "ini": "1.3.5"
+          },
+          "dependencies": {
+            "find-up": {
+              "version": "3.0.0",
+              "bundled": true,
+              "requires": {
+                "locate-path": "3.0.0"
+              }
+            },
+            "locate-path": {
+              "version": "3.0.0",
+              "bundled": true,
+              "requires": {
+                "p-locate": "3.0.0",
+                "path-exists": "3.0.0"
+              }
+            },
+            "p-limit": {
+              "version": "2.2.0",
+              "bundled": true,
+              "requires": {
+                "p-try": "2.2.0"
+              }
+            },
+            "p-locate": {
+              "version": "3.0.0",
+              "bundled": true,
+              "requires": {
+                "p-limit": "2.2.0"
+              }
+            },
+            "p-try": {
+              "version": "2.2.0",
+              "bundled": true
+            }
+          }
+        },
+        "libnpmhook": {
+          "version": "5.0.3",
+          "bundled": true,
+          "requires": {
+            "aproba": "2.0.0",
+            "figgy-pudding": "3.5.1",
+            "get-stream": "4.1.0",
+            "npm-registry-fetch": "4.0.0"
+          }
+        },
+        "libnpmorg": {
+          "version": "1.0.1",
+          "bundled": true,
+          "requires": {
+            "aproba": "2.0.0",
+            "figgy-pudding": "3.5.1",
+            "get-stream": "4.1.0",
+            "npm-registry-fetch": "4.0.0"
+          }
+        },
+        "libnpmpublish": {
+          "version": "1.1.2",
+          "bundled": true,
+          "requires": {
+            "aproba": "2.0.0",
+            "figgy-pudding": "3.5.1",
+            "get-stream": "4.1.0",
+            "lodash.clonedeep": "4.5.0",
+            "normalize-package-data": "2.5.0",
+            "npm-package-arg": "6.1.1",
+            "npm-registry-fetch": "4.0.0",
+            "semver": "5.7.1",
+            "ssri": "6.0.1"
+          }
+        },
+        "libnpmsearch": {
+          "version": "2.0.2",
+          "bundled": true,
+          "requires": {
+            "figgy-pudding": "3.5.1",
+            "get-stream": "4.1.0",
+            "npm-registry-fetch": "4.0.0"
+          }
+        },
+        "libnpmteam": {
+          "version": "1.0.2",
+          "bundled": true,
+          "requires": {
+            "aproba": "2.0.0",
+            "figgy-pudding": "3.5.1",
+            "get-stream": "4.1.0",
+            "npm-registry-fetch": "4.0.0"
+          }
+        },
+        "libnpx": {
+          "version": "10.2.0",
+          "bundled": true,
+          "requires": {
+            "dotenv": "5.0.1",
+            "npm-package-arg": "6.1.1",
+            "rimraf": "2.6.3",
+            "safe-buffer": "5.1.2",
+            "update-notifier": "2.5.0",
+            "which": "1.3.1",
+            "y18n": "4.0.0",
+            "yargs": "11.0.0"
+          }
+        },
+        "locate-path": {
+          "version": "2.0.0",
+          "bundled": true,
+          "requires": {
+            "p-locate": "2.0.0",
+            "path-exists": "3.0.0"
+          }
+        },
+        "lock-verify": {
+          "version": "2.1.0",
+          "bundled": true,
+          "requires": {
+            "npm-package-arg": "6.1.1",
+            "semver": "5.7.1"
+          }
+        },
+        "lockfile": {
+          "version": "1.0.4",
+          "bundled": true,
+          "requires": {
+            "signal-exit": "3.0.2"
+          }
+        },
+        "lodash._baseindexof": {
+          "version": "3.1.0",
+          "bundled": true
+        },
+        "lodash._baseuniq": {
+          "version": "4.6.0",
+          "bundled": true,
+          "requires": {
+            "lodash._createset": "4.0.3",
+            "lodash._root": "3.0.1"
+          }
+        },
+        "lodash._bindcallback": {
+          "version": "3.0.1",
+          "bundled": true
+        },
+        "lodash._cacheindexof": {
+          "version": "3.0.2",
+          "bundled": true
+        },
+        "lodash._createcache": {
+          "version": "3.1.2",
+          "bundled": true,
+          "requires": {
+            "lodash._getnative": "3.9.1"
+          }
+        },
+        "lodash._createset": {
+          "version": "4.0.3",
+          "bundled": true
+        },
+        "lodash._getnative": {
+          "version": "3.9.1",
+          "bundled": true
+        },
+        "lodash._root": {
+          "version": "3.0.1",
+          "bundled": true
+        },
+        "lodash.clonedeep": {
+          "version": "4.5.0",
+          "bundled": true
+        },
+        "lodash.restparam": {
+          "version": "3.6.1",
+          "bundled": true
+        },
+        "lodash.union": {
+          "version": "4.6.0",
+          "bundled": true
+        },
+        "lodash.uniq": {
+          "version": "4.5.0",
+          "bundled": true
+        },
+        "lodash.without": {
+          "version": "4.4.0",
+          "bundled": true
+        },
+        "lowercase-keys": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "lru-cache": {
+          "version": "5.1.1",
+          "bundled": true,
+          "requires": {
+            "yallist": "3.0.3"
+          }
+        },
+        "make-dir": {
+          "version": "1.3.0",
+          "bundled": true,
+          "requires": {
+            "pify": "3.0.0"
+          }
+        },
+        "make-fetch-happen": {
+          "version": "5.0.0",
+          "bundled": true,
+          "requires": {
+            "agentkeepalive": "3.5.2",
+            "cacache": "12.0.3",
+            "http-cache-semantics": "3.8.1",
+            "http-proxy-agent": "2.1.0",
+            "https-proxy-agent": "2.2.2",
+            "lru-cache": "5.1.1",
+            "mississippi": "3.0.0",
+            "node-fetch-npm": "2.0.2",
+            "promise-retry": "1.1.1",
+            "socks-proxy-agent": "4.0.2",
+            "ssri": "6.0.1"
+          }
+        },
+        "meant": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "mem": {
+          "version": "1.1.0",
+          "bundled": true,
+          "requires": {
+            "mimic-fn": "1.2.0"
+          }
+        },
+        "mime-db": {
+          "version": "1.35.0",
+          "bundled": true
+        },
+        "mime-types": {
+          "version": "2.1.19",
+          "bundled": true,
+          "requires": {
+            "mime-db": "1.35.0"
+          }
+        },
+        "mimic-fn": {
+          "version": "1.2.0",
+          "bundled": true
+        },
+        "minimatch": {
+          "version": "3.0.4",
+          "bundled": true,
+          "requires": {
+            "brace-expansion": "1.1.11"
+          }
+        },
+        "minimist": {
+          "version": "0.0.8",
+          "bundled": true
+        },
+        "minipass": {
+          "version": "2.3.3",
+          "bundled": true,
+          "requires": {
+            "safe-buffer": "5.1.2",
+            "yallist": "3.0.2"
+          },
+          "dependencies": {
+            "yallist": {
+              "version": "3.0.2",
+              "bundled": true
+            }
+          }
+        },
+        "minizlib": {
+          "version": "1.2.1",
+          "bundled": true,
+          "requires": {
+            "minipass": "2.3.3"
+          }
+        },
+        "mississippi": {
+          "version": "3.0.0",
+          "bundled": true,
+          "requires": {
+            "concat-stream": "1.6.2",
+            "duplexify": "3.6.0",
+            "end-of-stream": "1.4.1",
+            "flush-write-stream": "1.0.3",
+            "from2": "2.3.0",
+            "parallel-transform": "1.1.0",
+            "pump": "3.0.0",
+            "pumpify": "1.5.1",
+            "stream-each": "1.2.2",
+            "through2": "2.0.3"
+          }
+        },
+        "mkdirp": {
+          "version": "0.5.1",
+          "bundled": true,
+          "requires": {
+            "minimist": "0.0.8"
+          }
+        },
+        "move-concurrently": {
+          "version": "1.0.1",
+          "bundled": true,
+          "requires": {
+            "aproba": "1.2.0",
+            "copy-concurrently": "1.0.5",
+            "fs-write-stream-atomic": "1.0.10",
+            "mkdirp": "0.5.1",
+            "rimraf": "2.6.3",
+            "run-queue": "1.0.3"
+          },
+          "dependencies": {
+            "aproba": {
+              "version": "1.2.0",
+              "bundled": true
+            }
+          }
+        },
+        "ms": {
+          "version": "2.1.1",
+          "bundled": true
+        },
+        "mute-stream": {
+          "version": "0.0.7",
+          "bundled": true
+        },
+        "node-fetch-npm": {
+          "version": "2.0.2",
+          "bundled": true,
+          "requires": {
+            "encoding": "0.1.12",
+            "json-parse-better-errors": "1.0.2",
+            "safe-buffer": "5.1.2"
+          }
+        },
+        "node-gyp": {
+          "version": "5.0.3",
+          "bundled": true,
+          "requires": {
+            "env-paths": "1.0.0",
+            "glob": "7.1.4",
+            "graceful-fs": "4.2.2",
+            "mkdirp": "0.5.1",
+            "nopt": "3.0.6",
+            "npmlog": "4.1.2",
+            "request": "2.88.0",
+            "rimraf": "2.6.3",
+            "semver": "5.3.0",
+            "tar": "4.4.10",
+            "which": "1.3.1"
+          },
+          "dependencies": {
+            "nopt": {
+              "version": "3.0.6",
+              "bundled": true,
+              "requires": {
+                "abbrev": "1.1.1"
+              }
+            },
+            "semver": {
+              "version": "5.3.0",
+              "bundled": true
+            }
+          }
+        },
+        "nopt": {
+          "version": "4.0.1",
+          "bundled": true,
+          "requires": {
+            "abbrev": "1.1.1",
+            "osenv": "0.1.5"
+          }
+        },
+        "normalize-package-data": {
+          "version": "2.5.0",
+          "bundled": true,
+          "requires": {
+            "hosted-git-info": "2.8.2",
+            "resolve": "1.10.0",
+            "semver": "5.7.1",
+            "validate-npm-package-license": "3.0.4"
+          },
+          "dependencies": {
+            "resolve": {
+              "version": "1.10.0",
+              "bundled": true,
+              "requires": {
+                "path-parse": "1.0.6"
+              }
+            }
+          }
+        },
+        "npm-audit-report": {
+          "version": "1.3.2",
+          "bundled": true,
+          "requires": {
+            "cli-table3": "0.5.1",
+            "console-control-strings": "1.1.0"
+          }
+        },
+        "npm-bundled": {
+          "version": "1.0.6",
+          "bundled": true
+        },
+        "npm-cache-filename": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "npm-install-checks": {
+          "version": "3.0.0",
+          "bundled": true,
+          "requires": {
+            "semver": "5.7.1"
+          }
+        },
+        "npm-lifecycle": {
+          "version": "3.1.3",
+          "bundled": true,
+          "requires": {
+            "byline": "5.0.0",
+            "graceful-fs": "4.2.2",
+            "node-gyp": "5.0.3",
+            "resolve-from": "4.0.0",
+            "slide": "1.1.6",
+            "uid-number": "0.0.6",
+            "umask": "1.1.0",
+            "which": "1.3.1"
+          }
+        },
+        "npm-logical-tree": {
+          "version": "1.2.1",
+          "bundled": true
+        },
+        "npm-package-arg": {
+          "version": "6.1.1",
+          "bundled": true,
+          "requires": {
+            "hosted-git-info": "2.8.2",
+            "osenv": "0.1.5",
+            "semver": "5.7.1",
+            "validate-npm-package-name": "3.0.0"
+          }
+        },
+        "npm-packlist": {
+          "version": "1.4.4",
+          "bundled": true,
+          "requires": {
+            "ignore-walk": "3.0.1",
+            "npm-bundled": "1.0.6"
+          }
+        },
+        "npm-pick-manifest": {
+          "version": "3.0.2",
+          "bundled": true,
+          "requires": {
+            "figgy-pudding": "3.5.1",
+            "npm-package-arg": "6.1.1",
+            "semver": "5.7.1"
+          }
+        },
+        "npm-profile": {
+          "version": "4.0.2",
+          "bundled": true,
+          "requires": {
+            "aproba": "2.0.0",
+            "figgy-pudding": "3.5.1",
+            "npm-registry-fetch": "4.0.0"
+          }
+        },
+        "npm-registry-fetch": {
+          "version": "4.0.0",
+          "bundled": true,
+          "requires": {
+            "JSONStream": "1.3.5",
+            "bluebird": "3.5.5",
+            "figgy-pudding": "3.5.1",
+            "lru-cache": "5.1.1",
+            "make-fetch-happen": "5.0.0",
+            "npm-package-arg": "6.1.1"
+          }
+        },
+        "npm-run-path": {
+          "version": "2.0.2",
+          "bundled": true,
+          "requires": {
+            "path-key": "2.0.1"
+          }
+        },
+        "npm-user-validate": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "npmlog": {
+          "version": "4.1.2",
+          "bundled": true,
+          "requires": {
+            "are-we-there-yet": "1.1.4",
+            "console-control-strings": "1.1.0",
+            "gauge": "2.7.4",
+            "set-blocking": "2.0.0"
+          }
+        },
+        "number-is-nan": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "oauth-sign": {
+          "version": "0.9.0",
+          "bundled": true
+        },
+        "object-assign": {
+          "version": "4.1.1",
+          "bundled": true
+        },
+        "object-keys": {
+          "version": "1.0.12",
+          "bundled": true
+        },
+        "object.getownpropertydescriptors": {
+          "version": "2.0.3",
+          "bundled": true,
+          "requires": {
+            "define-properties": "1.1.3",
+            "es-abstract": "1.12.0"
+          }
+        },
+        "once": {
+          "version": "1.4.0",
+          "bundled": true,
+          "requires": {
+            "wrappy": "1.0.2"
+          }
+        },
+        "opener": {
+          "version": "1.5.1",
+          "bundled": true
+        },
+        "os-homedir": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "os-locale": {
+          "version": "2.1.0",
+          "bundled": true,
+          "requires": {
+            "execa": "0.7.0",
+            "lcid": "1.0.0",
+            "mem": "1.1.0"
+          }
+        },
+        "os-tmpdir": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "osenv": {
+          "version": "0.1.5",
+          "bundled": true,
+          "requires": {
+            "os-homedir": "1.0.2",
+            "os-tmpdir": "1.0.2"
+          }
+        },
+        "p-finally": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "p-limit": {
+          "version": "1.2.0",
+          "bundled": true,
+          "requires": {
+            "p-try": "1.0.0"
+          }
+        },
+        "p-locate": {
+          "version": "2.0.0",
+          "bundled": true,
+          "requires": {
+            "p-limit": "1.2.0"
+          }
+        },
+        "p-try": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "package-json": {
+          "version": "4.0.1",
+          "bundled": true,
+          "requires": {
+            "got": "6.7.1",
+            "registry-auth-token": "3.3.2",
+            "registry-url": "3.1.0",
+            "semver": "5.7.1"
+          }
+        },
+        "pacote": {
+          "version": "9.5.8",
+          "bundled": true,
+          "requires": {
+            "bluebird": "3.5.5",
+            "cacache": "12.0.3",
+            "chownr": "1.1.2",
+            "figgy-pudding": "3.5.1",
+            "get-stream": "4.1.0",
+            "glob": "7.1.4",
+            "infer-owner": "1.0.4",
+            "lru-cache": "5.1.1",
+            "make-fetch-happen": "5.0.0",
+            "minimatch": "3.0.4",
+            "minipass": "2.3.5",
+            "mississippi": "3.0.0",
+            "mkdirp": "0.5.1",
+            "normalize-package-data": "2.5.0",
+            "npm-package-arg": "6.1.1",
+            "npm-packlist": "1.4.4",
+            "npm-pick-manifest": "3.0.2",
+            "npm-registry-fetch": "4.0.0",
+            "osenv": "0.1.5",
+            "promise-inflight": "1.0.1",
+            "promise-retry": "1.1.1",
+            "protoduck": "5.0.1",
+            "rimraf": "2.6.3",
+            "safe-buffer": "5.1.2",
+            "semver": "5.7.1",
+            "ssri": "6.0.1",
+            "tar": "4.4.10",
+            "unique-filename": "1.1.1",
+            "which": "1.3.1"
+          },
+          "dependencies": {
+            "minipass": {
+              "version": "2.3.5",
+              "bundled": true,
+              "requires": {
+                "safe-buffer": "5.1.2",
+                "yallist": "3.0.3"
+              }
+            }
+          }
+        },
+        "parallel-transform": {
+          "version": "1.1.0",
+          "bundled": true,
+          "requires": {
+            "cyclist": "0.2.2",
+            "inherits": "2.0.4",
+            "readable-stream": "2.3.6"
+          },
+          "dependencies": {
+            "readable-stream": {
+              "version": "2.3.6",
+              "bundled": true,
+              "requires": {
+                "core-util-is": "1.0.2",
+                "inherits": "2.0.4",
+                "isarray": "1.0.0",
+                "process-nextick-args": "2.0.0",
+                "safe-buffer": "5.1.2",
+                "string_decoder": "1.1.1",
+                "util-deprecate": "1.0.2"
+              }
+            },
+            "string_decoder": {
+              "version": "1.1.1",
+              "bundled": true,
+              "requires": {
+                "safe-buffer": "5.1.2"
+              }
+            }
+          }
+        },
+        "path-exists": {
+          "version": "3.0.0",
+          "bundled": true
+        },
+        "path-is-absolute": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "path-is-inside": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "path-key": {
+          "version": "2.0.1",
+          "bundled": true
+        },
+        "path-parse": {
+          "version": "1.0.6",
+          "bundled": true
+        },
+        "performance-now": {
+          "version": "2.1.0",
+          "bundled": true
+        },
+        "pify": {
+          "version": "3.0.0",
+          "bundled": true
+        },
+        "prepend-http": {
+          "version": "1.0.4",
+          "bundled": true
+        },
+        "process-nextick-args": {
+          "version": "2.0.0",
+          "bundled": true
+        },
+        "promise-inflight": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "promise-retry": {
+          "version": "1.1.1",
+          "bundled": true,
+          "requires": {
+            "err-code": "1.1.2",
+            "retry": "0.10.1"
+          },
+          "dependencies": {
+            "retry": {
+              "version": "0.10.1",
+              "bundled": true
+            }
+          }
+        },
+        "promzard": {
+          "version": "0.3.0",
+          "bundled": true,
+          "requires": {
+            "read": "1.0.7"
+          }
+        },
+        "proto-list": {
+          "version": "1.2.4",
+          "bundled": true
+        },
+        "protoduck": {
+          "version": "5.0.1",
+          "bundled": true,
+          "requires": {
+            "genfun": "5.0.0"
+          }
+        },
+        "prr": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "pseudomap": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "psl": {
+          "version": "1.1.29",
+          "bundled": true
+        },
+        "pump": {
+          "version": "3.0.0",
+          "bundled": true,
+          "requires": {
+            "end-of-stream": "1.4.1",
+            "once": "1.4.0"
+          }
+        },
+        "pumpify": {
+          "version": "1.5.1",
+          "bundled": true,
+          "requires": {
+            "duplexify": "3.6.0",
+            "inherits": "2.0.4",
+            "pump": "2.0.1"
+          },
+          "dependencies": {
+            "pump": {
+              "version": "2.0.1",
+              "bundled": true,
+              "requires": {
+                "end-of-stream": "1.4.1",
+                "once": "1.4.0"
+              }
+            }
+          }
+        },
+        "punycode": {
+          "version": "1.4.1",
+          "bundled": true
+        },
+        "qrcode-terminal": {
+          "version": "0.12.0",
+          "bundled": true
+        },
+        "qs": {
+          "version": "6.5.2",
+          "bundled": true
+        },
+        "query-string": {
+          "version": "6.8.2",
+          "bundled": true,
+          "requires": {
+            "decode-uri-component": "0.2.0",
+            "split-on-first": "1.1.0",
+            "strict-uri-encode": "2.0.0"
+          }
+        },
+        "qw": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "rc": {
+          "version": "1.2.7",
+          "bundled": true,
+          "requires": {
+            "deep-extend": "0.5.1",
+            "ini": "1.3.5",
+            "minimist": "1.2.0",
+            "strip-json-comments": "2.0.1"
+          },
+          "dependencies": {
+            "minimist": {
+              "version": "1.2.0",
+              "bundled": true
+            }
+          }
+        },
+        "read": {
+          "version": "1.0.7",
+          "bundled": true,
+          "requires": {
+            "mute-stream": "0.0.7"
+          }
+        },
+        "read-cmd-shim": {
+          "version": "1.0.4",
+          "bundled": true,
+          "requires": {
+            "graceful-fs": "4.2.2"
+          }
+        },
+        "read-installed": {
+          "version": "4.0.3",
+          "bundled": true,
+          "requires": {
+            "debuglog": "1.0.1",
+            "graceful-fs": "4.2.2",
+            "read-package-json": "2.1.0",
+            "readdir-scoped-modules": "1.1.0",
+            "semver": "5.7.1",
+            "slide": "1.1.6",
+            "util-extend": "1.0.3"
+          }
+        },
+        "read-package-json": {
+          "version": "2.1.0",
+          "bundled": true,
+          "requires": {
+            "glob": "7.1.4",
+            "graceful-fs": "4.2.2",
+            "json-parse-better-errors": "1.0.2",
+            "normalize-package-data": "2.5.0",
+            "slash": "1.0.0"
+          }
+        },
+        "read-package-tree": {
+          "version": "5.3.1",
+          "bundled": true,
+          "requires": {
+            "read-package-json": "2.1.0",
+            "readdir-scoped-modules": "1.1.0",
+            "util-promisify": "2.1.0"
+          }
+        },
+        "readable-stream": {
+          "version": "3.4.0",
+          "bundled": true,
+          "requires": {
+            "inherits": "2.0.4",
+            "string_decoder": "1.2.0",
+            "util-deprecate": "1.0.2"
+          }
+        },
+        "readdir-scoped-modules": {
+          "version": "1.1.0",
+          "bundled": true,
+          "requires": {
+            "debuglog": "1.0.1",
+            "dezalgo": "1.0.3",
+            "graceful-fs": "4.2.2",
+            "once": "1.4.0"
+          }
+        },
+        "registry-auth-token": {
+          "version": "3.3.2",
+          "bundled": true,
+          "requires": {
+            "rc": "1.2.7",
+            "safe-buffer": "5.1.2"
+          }
+        },
+        "registry-url": {
+          "version": "3.1.0",
+          "bundled": true,
+          "requires": {
+            "rc": "1.2.7"
+          }
+        },
+        "request": {
+          "version": "2.88.0",
+          "bundled": true,
+          "requires": {
+            "aws-sign2": "0.7.0",
+            "aws4": "1.8.0",
+            "caseless": "0.12.0",
+            "combined-stream": "1.0.6",
+            "extend": "3.0.2",
+            "forever-agent": "0.6.1",
+            "form-data": "2.3.2",
+            "har-validator": "5.1.0",
+            "http-signature": "1.2.0",
+            "is-typedarray": "1.0.0",
+            "isstream": "0.1.2",
+            "json-stringify-safe": "5.0.1",
+            "mime-types": "2.1.19",
+            "oauth-sign": "0.9.0",
+            "performance-now": "2.1.0",
+            "qs": "6.5.2",
+            "safe-buffer": "5.1.2",
+            "tough-cookie": "2.4.3",
+            "tunnel-agent": "0.6.0",
+            "uuid": "3.3.2"
+          }
+        },
+        "require-directory": {
+          "version": "2.1.1",
+          "bundled": true
+        },
+        "require-main-filename": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "resolve-from": {
+          "version": "4.0.0",
+          "bundled": true
+        },
+        "retry": {
+          "version": "0.12.0",
+          "bundled": true
+        },
+        "rimraf": {
+          "version": "2.6.3",
+          "bundled": true,
+          "requires": {
+            "glob": "7.1.4"
+          }
+        },
+        "run-queue": {
+          "version": "1.0.3",
+          "bundled": true,
+          "requires": {
+            "aproba": "1.2.0"
+          },
+          "dependencies": {
+            "aproba": {
+              "version": "1.2.0",
+              "bundled": true
+            }
+          }
+        },
+        "safe-buffer": {
+          "version": "5.1.2",
+          "bundled": true
+        },
+        "safer-buffer": {
+          "version": "2.1.2",
+          "bundled": true
+        },
+        "semver": {
+          "version": "5.7.1",
+          "bundled": true
+        },
+        "semver-diff": {
+          "version": "2.1.0",
+          "bundled": true,
+          "requires": {
+            "semver": "5.7.1"
+          }
+        },
+        "set-blocking": {
+          "version": "2.0.0",
+          "bundled": true
+        },
+        "sha": {
+          "version": "3.0.0",
+          "bundled": true,
+          "requires": {
+            "graceful-fs": "4.2.2"
+          }
+        },
+        "shebang-command": {
+          "version": "1.2.0",
+          "bundled": true,
+          "requires": {
+            "shebang-regex": "1.0.0"
+          }
+        },
+        "shebang-regex": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "signal-exit": {
+          "version": "3.0.2",
+          "bundled": true
+        },
+        "slash": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "slide": {
+          "version": "1.1.6",
+          "bundled": true
+        },
+        "smart-buffer": {
+          "version": "4.0.2",
+          "bundled": true
+        },
+        "socks": {
+          "version": "2.3.2",
+          "bundled": true,
+          "requires": {
+            "ip": "1.1.5",
+            "smart-buffer": "4.0.2"
+          }
+        },
+        "socks-proxy-agent": {
+          "version": "4.0.2",
+          "bundled": true,
+          "requires": {
+            "agent-base": "4.2.1",
+            "socks": "2.3.2"
+          },
+          "dependencies": {
+            "agent-base": {
+              "version": "4.2.1",
+              "bundled": true,
+              "requires": {
+                "es6-promisify": "5.0.0"
+              }
+            }
+          }
+        },
+        "sorted-object": {
+          "version": "2.0.1",
+          "bundled": true
+        },
+        "sorted-union-stream": {
+          "version": "2.1.3",
+          "bundled": true,
+          "requires": {
+            "from2": "1.3.0",
+            "stream-iterate": "1.2.0"
+          },
+          "dependencies": {
+            "from2": {
+              "version": "1.3.0",
+              "bundled": true,
+              "requires": {
+                "inherits": "2.0.4",
+                "readable-stream": "1.1.14"
+              }
+            },
+            "isarray": {
+              "version": "0.0.1",
+              "bundled": true
+            },
+            "readable-stream": {
+              "version": "1.1.14",
+              "bundled": true,
+              "requires": {
+                "core-util-is": "1.0.2",
+                "inherits": "2.0.4",
+                "isarray": "0.0.1",
+                "string_decoder": "0.10.31"
+              }
+            },
+            "string_decoder": {
+              "version": "0.10.31",
+              "bundled": true
+            }
+          }
+        },
+        "spdx-correct": {
+          "version": "3.0.0",
+          "bundled": true,
+          "requires": {
+            "spdx-expression-parse": "3.0.0",
+            "spdx-license-ids": "3.0.3"
+          }
+        },
+        "spdx-exceptions": {
+          "version": "2.1.0",
+          "bundled": true
+        },
+        "spdx-expression-parse": {
+          "version": "3.0.0",
+          "bundled": true,
+          "requires": {
+            "spdx-exceptions": "2.1.0",
+            "spdx-license-ids": "3.0.3"
+          }
+        },
+        "spdx-license-ids": {
+          "version": "3.0.3",
+          "bundled": true
+        },
+        "split-on-first": {
+          "version": "1.1.0",
+          "bundled": true
+        },
+        "sshpk": {
+          "version": "1.14.2",
+          "bundled": true,
+          "requires": {
+            "asn1": "0.2.4",
+            "assert-plus": "1.0.0",
+            "bcrypt-pbkdf": "1.0.2",
+            "dashdash": "1.14.1",
+            "ecc-jsbn": "0.1.2",
+            "getpass": "0.1.7",
+            "jsbn": "0.1.1",
+            "safer-buffer": "2.1.2",
+            "tweetnacl": "0.14.5"
+          }
+        },
+        "ssri": {
+          "version": "6.0.1",
+          "bundled": true,
+          "requires": {
+            "figgy-pudding": "3.5.1"
+          }
+        },
+        "stream-each": {
+          "version": "1.2.2",
+          "bundled": true,
+          "requires": {
+            "end-of-stream": "1.4.1",
+            "stream-shift": "1.0.0"
+          }
+        },
+        "stream-iterate": {
+          "version": "1.2.0",
+          "bundled": true,
+          "requires": {
+            "readable-stream": "2.3.6",
+            "stream-shift": "1.0.0"
+          },
+          "dependencies": {
+            "readable-stream": {
+              "version": "2.3.6",
+              "bundled": true,
+              "requires": {
+                "core-util-is": "1.0.2",
+                "inherits": "2.0.4",
+                "isarray": "1.0.0",
+                "process-nextick-args": "2.0.0",
+                "safe-buffer": "5.1.2",
+                "string_decoder": "1.1.1",
+                "util-deprecate": "1.0.2"
+              }
+            },
+            "string_decoder": {
+              "version": "1.1.1",
+              "bundled": true,
+              "requires": {
+                "safe-buffer": "5.1.2"
+              }
+            }
+          }
+        },
+        "stream-shift": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "strict-uri-encode": {
+          "version": "2.0.0",
+          "bundled": true
+        },
+        "string-width": {
+          "version": "2.1.1",
+          "bundled": true,
+          "requires": {
+            "is-fullwidth-code-point": "2.0.0",
+            "strip-ansi": "4.0.0"
+          },
+          "dependencies": {
+            "ansi-regex": {
+              "version": "3.0.0",
+              "bundled": true
+            },
+            "is-fullwidth-code-point": {
+              "version": "2.0.0",
+              "bundled": true
+            },
+            "strip-ansi": {
+              "version": "4.0.0",
+              "bundled": true,
+              "requires": {
+                "ansi-regex": "3.0.0"
+              }
+            }
+          }
+        },
+        "string_decoder": {
+          "version": "1.2.0",
+          "bundled": true,
+          "requires": {
+            "safe-buffer": "5.1.2"
+          }
+        },
+        "stringify-package": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "strip-ansi": {
+          "version": "3.0.1",
+          "bundled": true,
+          "requires": {
+            "ansi-regex": "2.1.1"
+          }
+        },
+        "strip-eof": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "strip-json-comments": {
+          "version": "2.0.1",
+          "bundled": true
+        },
+        "supports-color": {
+          "version": "5.4.0",
+          "bundled": true,
+          "requires": {
+            "has-flag": "3.0.0"
+          }
+        },
+        "tar": {
+          "version": "4.4.10",
+          "bundled": true,
+          "requires": {
+            "chownr": "1.1.2",
+            "fs-minipass": "1.2.6",
+            "minipass": "2.3.5",
+            "minizlib": "1.2.1",
+            "mkdirp": "0.5.1",
+            "safe-buffer": "5.1.2",
+            "yallist": "3.0.3"
+          },
+          "dependencies": {
+            "minipass": {
+              "version": "2.3.5",
+              "bundled": true,
+              "requires": {
+                "safe-buffer": "5.1.2",
+                "yallist": "3.0.3"
+              }
+            },
+            "yallist": {
+              "version": "3.0.3",
+              "bundled": true
+            }
+          }
+        },
+        "term-size": {
+          "version": "1.2.0",
+          "bundled": true,
+          "requires": {
+            "execa": "0.7.0"
+          }
+        },
+        "text-table": {
+          "version": "0.2.0",
+          "bundled": true
+        },
+        "through": {
+          "version": "2.3.8",
+          "bundled": true
+        },
+        "through2": {
+          "version": "2.0.3",
+          "bundled": true,
+          "requires": {
+            "readable-stream": "2.3.6",
+            "xtend": "4.0.1"
+          },
+          "dependencies": {
+            "readable-stream": {
+              "version": "2.3.6",
+              "bundled": true,
+              "requires": {
+                "core-util-is": "1.0.2",
+                "inherits": "2.0.4",
+                "isarray": "1.0.0",
+                "process-nextick-args": "2.0.0",
+                "safe-buffer": "5.1.2",
+                "string_decoder": "1.1.1",
+                "util-deprecate": "1.0.2"
+              }
+            },
+            "string_decoder": {
+              "version": "1.1.1",
+              "bundled": true,
+              "requires": {
+                "safe-buffer": "5.1.2"
+              }
+            }
+          }
+        },
+        "timed-out": {
+          "version": "4.0.1",
+          "bundled": true
+        },
+        "tiny-relative-date": {
+          "version": "1.3.0",
+          "bundled": true
+        },
+        "tough-cookie": {
+          "version": "2.4.3",
+          "bundled": true,
+          "requires": {
+            "psl": "1.1.29",
+            "punycode": "1.4.1"
+          }
+        },
+        "tunnel-agent": {
+          "version": "0.6.0",
+          "bundled": true,
+          "requires": {
+            "safe-buffer": "5.1.2"
+          }
+        },
+        "tweetnacl": {
+          "version": "0.14.5",
+          "bundled": true,
+          "optional": true
+        },
+        "typedarray": {
+          "version": "0.0.6",
+          "bundled": true
+        },
+        "uid-number": {
+          "version": "0.0.6",
+          "bundled": true
+        },
+        "umask": {
+          "version": "1.1.0",
+          "bundled": true
+        },
+        "unique-filename": {
+          "version": "1.1.1",
+          "bundled": true,
+          "requires": {
+            "unique-slug": "2.0.0"
+          }
+        },
+        "unique-slug": {
+          "version": "2.0.0",
+          "bundled": true,
+          "requires": {
+            "imurmurhash": "0.1.4"
+          }
+        },
+        "unique-string": {
+          "version": "1.0.0",
+          "bundled": true,
+          "requires": {
+            "crypto-random-string": "1.0.0"
+          }
+        },
+        "unpipe": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "unzip-response": {
+          "version": "2.0.1",
+          "bundled": true
+        },
+        "update-notifier": {
+          "version": "2.5.0",
+          "bundled": true,
+          "requires": {
+            "boxen": "1.3.0",
+            "chalk": "2.4.1",
+            "configstore": "3.1.2",
+            "import-lazy": "2.1.0",
+            "is-ci": "1.1.0",
+            "is-installed-globally": "0.1.0",
+            "is-npm": "1.0.0",
+            "latest-version": "3.1.0",
+            "semver-diff": "2.1.0",
+            "xdg-basedir": "3.0.0"
+          }
+        },
+        "url-parse-lax": {
+          "version": "1.0.0",
+          "bundled": true,
+          "requires": {
+            "prepend-http": "1.0.4"
+          }
+        },
+        "util-deprecate": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "util-extend": {
+          "version": "1.0.3",
+          "bundled": true
+        },
+        "util-promisify": {
+          "version": "2.1.0",
+          "bundled": true,
+          "requires": {
+            "object.getownpropertydescriptors": "2.0.3"
+          }
+        },
+        "uuid": {
+          "version": "3.3.2",
+          "bundled": true
+        },
+        "validate-npm-package-license": {
+          "version": "3.0.4",
+          "bundled": true,
+          "requires": {
+            "spdx-correct": "3.0.0",
+            "spdx-expression-parse": "3.0.0"
+          }
+        },
+        "validate-npm-package-name": {
+          "version": "3.0.0",
+          "bundled": true,
+          "requires": {
+            "builtins": "1.0.3"
+          }
+        },
+        "verror": {
+          "version": "1.10.0",
+          "bundled": true,
+          "requires": {
+            "assert-plus": "1.0.0",
+            "core-util-is": "1.0.2",
+            "extsprintf": "1.3.0"
+          }
+        },
+        "wcwidth": {
+          "version": "1.0.1",
+          "bundled": true,
+          "requires": {
+            "defaults": "1.0.3"
+          }
+        },
+        "which": {
+          "version": "1.3.1",
+          "bundled": true,
+          "requires": {
+            "isexe": "2.0.0"
+          }
+        },
+        "which-module": {
+          "version": "2.0.0",
+          "bundled": true
+        },
+        "wide-align": {
+          "version": "1.1.2",
+          "bundled": true,
+          "requires": {
+            "string-width": "1.0.2"
+          },
+          "dependencies": {
+            "string-width": {
+              "version": "1.0.2",
+              "bundled": true,
+              "requires": {
+                "code-point-at": "1.1.0",
+                "is-fullwidth-code-point": "1.0.0",
+                "strip-ansi": "3.0.1"
+              }
+            }
+          }
+        },
+        "widest-line": {
+          "version": "2.0.0",
+          "bundled": true,
+          "requires": {
+            "string-width": "2.1.1"
+          }
+        },
+        "worker-farm": {
+          "version": "1.7.0",
+          "bundled": true,
+          "requires": {
+            "errno": "0.1.7"
+          }
+        },
+        "wrap-ansi": {
+          "version": "2.1.0",
+          "bundled": true,
+          "requires": {
+            "string-width": "1.0.2",
+            "strip-ansi": "3.0.1"
+          },
+          "dependencies": {
+            "string-width": {
+              "version": "1.0.2",
+              "bundled": true,
+              "requires": {
+                "code-point-at": "1.1.0",
+                "is-fullwidth-code-point": "1.0.0",
+                "strip-ansi": "3.0.1"
+              }
+            }
+          }
+        },
+        "wrappy": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "write-file-atomic": {
+          "version": "2.4.3",
+          "bundled": true,
+          "requires": {
+            "graceful-fs": "4.2.2",
+            "imurmurhash": "0.1.4",
+            "signal-exit": "3.0.2"
+          }
+        },
+        "xdg-basedir": {
+          "version": "3.0.0",
+          "bundled": true
+        },
+        "xtend": {
+          "version": "4.0.1",
+          "bundled": true
+        },
+        "y18n": {
+          "version": "4.0.0",
+          "bundled": true
+        },
+        "yallist": {
+          "version": "3.0.3",
+          "bundled": true
+        },
+        "yargs": {
+          "version": "11.0.0",
+          "bundled": true,
+          "requires": {
+            "cliui": "4.1.0",
+            "decamelize": "1.2.0",
+            "find-up": "2.1.0",
+            "get-caller-file": "1.0.2",
+            "os-locale": "2.1.0",
+            "require-directory": "2.1.1",
+            "require-main-filename": "1.0.1",
+            "set-blocking": "2.0.0",
+            "string-width": "2.1.1",
+            "which-module": "2.0.0",
+            "y18n": "3.2.1",
+            "yargs-parser": "9.0.2"
+          },
+          "dependencies": {
+            "y18n": {
+              "version": "3.2.1",
+              "bundled": true
+            }
+          }
+        },
+        "yargs-parser": {
+          "version": "9.0.2",
+          "bundled": true,
+          "requires": {
+            "camelcase": "4.1.0"
+          }
+        }
+      }
+    },
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "requires": {
+        "wrappy": "1.0.2"
+      }
+    },
+    "path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+    },
+    "path-parse": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+      "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+      "dev": true
+    },
+    "path-to-regexp": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
+      "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
+      "dev": true,
+      "requires": {
+        "isarray": "0.0.1"
+      }
+    },
+    "pathval": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz",
+      "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=",
+      "dev": true
+    },
+    "propagate": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/propagate/-/propagate-1.0.0.tgz",
+      "integrity": "sha1-AMLa7t2iDofjeCs0Stuhzd1q1wk=",
+      "dev": true
+    },
+    "qs": {
+      "version": "6.6.0",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.6.0.tgz",
+      "integrity": "sha512-KIJqT9jQJDQx5h5uAVPimw6yVg2SekOKu959OCtktD3FjzbpvaPr8i4zzg07DOMz+igA4W/aNM7OV8H37pFYfA==",
+      "dev": true
+    },
+    "resolve": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz",
+      "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==",
+      "dev": true,
+      "requires": {
+        "path-parse": "1.0.6"
+      }
+    },
+    "rimraf": {
+      "version": "2.5.4",
+      "resolved": "http://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz",
+      "integrity": "sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=",
+      "requires": {
+        "glob": "7.1.3"
+      }
+    },
+    "semver": {
+      "version": "5.6.0",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
+      "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
+      "dev": true
+    },
+    "sinon": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.1.1.tgz",
+      "integrity": "sha512-iYagtjLVt1vN3zZY7D8oH7dkjNJEjLjyuzy8daX5+3bbQl8gaohrheB9VfH1O3L6LKuue5WTJvFluHiuZ9y3nQ==",
+      "dev": true,
+      "requires": {
+        "@sinonjs/commons": "1.3.0",
+        "@sinonjs/formatio": "3.1.0",
+        "@sinonjs/samsam": "2.1.3",
+        "diff": "3.5.0",
+        "lodash.get": "4.4.2",
+        "lolex": "3.0.0",
+        "nise": "1.4.8",
+        "supports-color": "5.5.0",
+        "type-detect": "4.0.8"
+      },
+      "dependencies": {
+        "supports-color": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "dev": true,
+          "requires": {
+            "has-flag": "3.0.0"
+          }
+        }
+      }
+    },
+    "source-map": {
+      "version": "0.5.7",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+      "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+      "dev": true
+    },
+    "source-map-support": {
+      "version": "0.4.18",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
+      "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
+      "dev": true,
+      "requires": {
+        "source-map": "0.5.7"
+      }
+    },
+    "sprintf-js": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+      "dev": true
+    },
+    "strip-ansi": {
+      "version": "3.0.1",
+      "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+      "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+      "dev": true,
+      "requires": {
+        "ansi-regex": "2.1.1"
+      }
+    },
+    "supports-color": {
+      "version": "5.4.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
+      "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+      "dev": true,
+      "requires": {
+        "has-flag": "3.0.0"
+      }
+    },
+    "text-encoding": {
+      "version": "0.6.4",
+      "resolved": "http://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz",
+      "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=",
+      "dev": true
+    },
+    "ts-node": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-1.0.0.tgz",
+      "integrity": "sha1-ufGO9Zyrpn7JXD6eYHDDBF0FZms=",
+      "dev": true,
+      "requires": {
+        "arrify": "1.0.1",
+        "chalk": "1.1.3",
+        "diff": "2.2.3",
+        "make-error": "1.3.5",
+        "minimist": "1.2.0",
+        "mkdirp": "0.5.1",
+        "source-map-support": "0.4.18",
+        "xtend": "4.0.1"
+      },
+      "dependencies": {
+        "diff": {
+          "version": "2.2.3",
+          "resolved": "https://registry.npmjs.org/diff/-/diff-2.2.3.tgz",
+          "integrity": "sha1-YOr9DSjukG5Oj/ClLBIpUhAzv5k=",
+          "dev": true
+        },
+        "minimist": {
+          "version": "1.2.0",
+          "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+          "dev": true
+        }
+      }
+    },
+    "tslib": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
+      "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==",
+      "dev": true
+    },
+    "tslint": {
+      "version": "5.11.0",
+      "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.11.0.tgz",
+      "integrity": "sha1-mPMMAurjzecAYgHkwzywi0hYHu0=",
+      "dev": true,
+      "requires": {
+        "babel-code-frame": "6.26.0",
+        "builtin-modules": "1.1.1",
+        "chalk": "2.4.1",
+        "commander": "2.19.0",
+        "diff": "3.5.0",
+        "glob": "7.1.3",
+        "js-yaml": "3.13.1",
+        "minimatch": "3.0.4",
+        "resolve": "1.8.1",
+        "semver": "5.6.0",
+        "tslib": "1.9.3",
+        "tsutils": "2.29.0"
       },
       "dependencies": {
         "ansi-styles": {
@@ -810,7 +3859,7 @@
           "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
           "dev": true,
           "requires": {
-            "color-convert": "^1.9.0"
+            "color-convert": "1.9.3"
           }
         },
         "chalk": {
@@ -819,9 +3868,9 @@
           "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
           "dev": true,
           "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
+            "ansi-styles": "3.2.1",
+            "escape-string-regexp": "1.0.5",
+            "supports-color": "5.4.0"
           }
         },
         "commander": {
@@ -838,7 +3887,7 @@
       "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
       "dev": true,
       "requires": {
-        "tslib": "^1.8.1"
+        "tslib": "1.9.3"
       }
     },
     "type-detect": {
diff --git a/package.json b/package.json
index 61bcfb0..f70e2be 100644
--- a/package.json
+++ b/package.json
@@ -33,6 +33,7 @@
     "commander": "~2.9.0",
     "debug": "~2.6.9",
     "lodash": "^4.17.15",
+    "npm": "^6.11.3",
     "rimraf": "~2.5.4",
     "uuid": "~3.3.2",
     "validator": "~10.8.0"
@@ -44,7 +45,7 @@
     "@types/debug": "0.0.29",
     "@types/mocha": "~5.2.5",
     "@types/nock": "^9.3.0",
-    "@types/node": "~6.14.0",
+    "@types/node": "~6.14.7",
     "@types/rimraf": "0.0.28",
     "@types/sinon": "~5.0.5",
     "chai": "~4.2.0",
diff --git a/samples/Counter/function.json b/samples/ContinueAsNewCounter/function.json
similarity index 100%
rename from samples/Counter/function.json
rename to samples/ContinueAsNewCounter/function.json
diff --git a/samples/Counter/index.js b/samples/ContinueAsNewCounter/index.js
similarity index 100%
rename from samples/Counter/index.js
rename to samples/ContinueAsNewCounter/index.js
diff --git a/samples/CounterEntity/functions.json b/samples/CounterEntity/functions.json
new file mode 100644
index 0000000..b79bed5
--- /dev/null
+++ b/samples/CounterEntity/functions.json
@@ -0,0 +1,10 @@
+{
+    "bindings": [
+      {
+        "name": "context",
+        "type": "entityTrigger",
+        "direction": "in"
+      }
+    ],
+    "disabled": false
+  }
\ No newline at end of file
diff --git a/samples/CounterEntity/index.js b/samples/CounterEntity/index.js
new file mode 100644
index 0000000..f9bf7c8
--- /dev/null
+++ b/samples/CounterEntity/index.js
@@ -0,0 +1,20 @@
+const df = require("durable-functions");
+
+module.exports = df.entity(function(context) {
+    let currentValue = context.df.getState(() => 0);
+
+    switch (context.df.operationName) {
+        case "add":
+            const amount = context.df.getInput();
+            currentValue += amount;
+            break;
+        case "reset":
+            currentValue = 0;
+            break;
+        case "get":
+            context.df.return(currentValue);
+            break;
+    }
+
+    context.df.setState(currentValue);
+});
\ No newline at end of file
diff --git a/samples/CounterOrchestration/function.json b/samples/CounterOrchestration/function.json
new file mode 100644
index 0000000..acf1ac7
--- /dev/null
+++ b/samples/CounterOrchestration/function.json
@@ -0,0 +1,10 @@
+{
+  "bindings": [
+    {
+      "name": "context",
+      "type": "orchestrationTrigger",
+      "direction": "in"
+    }
+  ],
+  "disabled": false
+}
\ No newline at end of file
diff --git a/samples/CounterOrchestration/index.js b/samples/CounterOrchestration/index.js
new file mode 100644
index 0000000..c106a84
--- /dev/null
+++ b/samples/CounterOrchestration/index.js
@@ -0,0 +1,10 @@
+const df = require("durable-functions");
+
+module.exports = df.orchestrator(function*(context){
+    const entityId = new df.EntityId("CounterEntity", "myCounter");
+
+    currentValue = yield context.df.callEntity(entityId, "get");
+    if (currentValue < 10) {
+        yield context.df.callEntity(entityId, "add", 1);
+    }
+});
diff --git a/samples/extensions.csproj b/samples/extensions.csproj
index e4884fb..88c1c17 100644
--- a/samples/extensions.csproj
+++ b/samples/extensions.csproj
@@ -5,7 +5,7 @@
 	<DefaultItemExcludes>**</DefaultItemExcludes>
   </PropertyGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.DurableTask" Version="1.8.1" />
+    <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.DurableTask" Version="2.0.0-beta3" />
     <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage" Version="3.0.0" />
     <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Twilio" Version="3.0.0" />
     <PackageReference Include="Microsoft.Azure.WebJobs.Script.ExtensionsMetadataGenerator" Version="1.0.0" />
diff --git a/src/actions/callentityaction.ts b/src/actions/callentityaction.ts
new file mode 100644
index 0000000..202fe1d
--- /dev/null
+++ b/src/actions/callentityaction.ts
@@ -0,0 +1,19 @@
+import { ActionType, EntityId, IAction, Utils } from "../classes";
+
+/** @hidden */
+export class CallEntityAction implements IAction {
+    public readonly actionType: ActionType = ActionType.CallEntity;
+    public readonly instanceId: string;
+
+    constructor(
+        entityId: EntityId,
+        public readonly operation: string,
+        public readonly input?: unknown,
+    ) {
+        if (!entityId) {
+            throw new Error("Must provide EntityId to CallEntityAction constructor");
+        }
+        Utils.throwIfEmpty(operation, "operation");
+        this.instanceId = EntityId.getSchedulerIdFromEntityId(entityId);
+    }
+}
diff --git a/src/actions/externaleventtype.ts b/src/actions/externaleventtype.ts
new file mode 100644
index 0000000..3d4cb75
--- /dev/null
+++ b/src/actions/externaleventtype.ts
@@ -0,0 +1,10 @@
+/**
+ * @hidden
+ * Represents the options that can be provided for the "reason" field of events in
+ * Durable Functions 2.0.
+ */
+export enum ExternalEventType {
+    ExternalEvent = "ExternalEvent",
+    LockAcquisitionCompleted = "LockAcquisitionCompleted",
+    EntityResponse = "EntityResponse",
+}
diff --git a/src/actions/waitforexternaleventaction.ts b/src/actions/waitforexternaleventaction.ts
index 95aa5ca..67f0231 100644
--- a/src/actions/waitforexternaleventaction.ts
+++ b/src/actions/waitforexternaleventaction.ts
@@ -1,4 +1,4 @@
-import { ActionType, IAction, Utils } from "../classes";
+import { ActionType, ExternalEventType, IAction, Utils } from "../classes";
 
 /** @hidden */
 export class WaitForExternalEventAction implements IAction {
@@ -6,6 +6,7 @@ export class WaitForExternalEventAction implements IAction {
 
     constructor(
         public readonly externalEventName: string,
+        public readonly reason = ExternalEventType.ExternalEvent,
     ) {
         Utils.throwIfEmpty(externalEventName, "externalEventName");
     }
diff --git a/src/classes.ts b/src/classes.ts
index 9f6189c..d218feb 100644
--- a/src/classes.ts
+++ b/src/classes.ts
@@ -3,15 +3,21 @@ export { Constants } from "./constants";
 export { Utils } from "./utils";
 
 export { Orchestrator } from "./orchestrator";
+export { Entity } from "./entity";
 
-export { IFunctionContext } from "./ifunctioncontext";
+export { IEntityFunctionContext } from "./ientityfunctioncontext";
+export { IOrchestrationFunctionContext } from "./iorchestrationfunctioncontext";
+export { DurableEntityBindingInfo } from "./durableentitybindinginfo";
+export { DurableEntityContext } from "./durableentitycontext";
 export { DurableOrchestrationBindingInfo } from "./durableorchestrationbindinginfo";
 export { DurableOrchestrationContext } from "./durableorchestrationcontext";
 
 export { IAction } from "./actions/iaction";
 export { ActionType } from "./actions/actiontype";
+export { ExternalEventType } from "./actions/externaleventtype";
 export { CallActivityAction } from "./actions/callactivityaction";
 export { CallActivityWithRetryAction } from "./actions/callactivitywithretryaction";
+export { CallEntityAction } from "./actions/callentityaction";
 export { CallSubOrchestratorAction } from "./actions/callsuborchestratoraction";
 export { CallSubOrchestratorWithRetryAction } from "./actions/callsuborchestratorwithretryaction";
 export { CallHttpAction } from "./actions/callhttpaction";
@@ -22,6 +28,7 @@ export { WaitForExternalEventAction } from "./actions/waitforexternaleventaction
 export { HistoryEvent } from "./history/historyevent";
 export { HistoryEventOptions } from "./history/historyeventoptions";
 export { EventRaisedEvent } from "./history/eventraisedevent";
+export { EventSentEvent } from "./history/eventsentevent";
 export { ExecutionStartedEvent } from "./history/executionstartedevent";
 export { HistoryEventType } from "./history/historyeventtype";
 export { OrchestratorStartedEvent } from "./history/orchestratorstartedevent";
@@ -49,6 +56,7 @@ export { DurableOrchestrationClient } from "./durableorchestrationclient";
 export { OrchestrationClientInputData } from "./orchestrationclientinputdata";
 export { HttpCreationPayload } from "./httpcreationpayload";
 export { HttpManagementPayload } from "./httpmanagementpayload";
+export { GetStatusOptions } from "./getstatusoptions";
 
 export { IHttpRequest } from "./ihttprequest";
 export { IHttpResponse } from "./ihttpresponse";
@@ -61,3 +69,12 @@ export { GuidManager } from "./guidmanager";
 
 export { DurableHttpRequest } from "./durablehttprequest";
 export { DurableHttpResponse } from "./durablehttpresponse";
+export { DurableLock } from "./entities/durablelock";
+export { EntityId } from "./entities/entityid";
+export { EntityState } from "./entities/entitystate";
+export { EntityStateResponse } from "./entities/entitystateresponse";
+export { LockState } from "./entities/lockstate";
+export { OperationResult } from "./entities/operationresult";
+export { RequestMessage } from "./entities/requestmessage";
+export { ResponseMessage } from "./entities/responsemessage";
+export { Signal } from "./entities/signal";
diff --git a/src/durableentitybindinginfo.ts b/src/durableentitybindinginfo.ts
new file mode 100644
index 0000000..2850944
--- /dev/null
+++ b/src/durableentitybindinginfo.ts
@@ -0,0 +1,11 @@
+import { EntityId, RequestMessage } from "./classes";
+
+/** @hidden */
+export class DurableEntityBindingInfo {
+    constructor(
+        public readonly self: EntityId,
+        public readonly exists: boolean,
+        public readonly state: string,
+        public readonly batch: RequestMessage[],
+    ) { }
+}
diff --git a/src/durableentitycontext.ts b/src/durableentitycontext.ts
new file mode 100644
index 0000000..4cdb8c3
--- /dev/null
+++ b/src/durableentitycontext.ts
@@ -0,0 +1,96 @@
+import { EntityId } from "./classes";
+
+/**
+ * Provides functionality for application code implementing an entity
+ * operation.
+ */
+export class DurableEntityContext {
+    /**
+     * Gets the name of the currently executing entity.
+     */
+    public readonly entityName: string;
+
+    /**
+     * Gets the key of the currently executing entity.
+     */
+    public readonly entityKey: string;
+
+    /**
+     * Gets the id of the currently executing entity.
+     */
+    public readonly entityId: EntityId;
+
+    /**
+     * Gets the name of the operation that was called.
+     *
+     * An operation invocation on an entity includes an operation name, which
+     * states what operation to perform, and optionally an operation input.
+     */
+    public readonly operationName: string;
+
+    /**
+     * Whether this entity is freshly constructed, i.e. did not exist prior to
+     * this operation being called.
+     */
+    public readonly isNewlyConstructed: boolean;
+
+    /**
+     * Gets the current state of this entity, for reading and/or writing.
+     *
+     * @param initializer Provides an initial value to use for the state,
+     * instead of TState's default.
+     * @returns The current state of this entity, or undefined if none has been set yet.
+     */
+    public getState(initializer?: () => unknown): unknown | undefined {
+        throw new Error("This is a placeholder.");
+    }
+
+    /**
+     * Sets the current state of this entity.
+     *
+     * @param state The state of the entity.
+     */
+    public setState(state: unknown): void {
+        throw new Error("This is a placeholder.");
+    }
+
+    /**
+     * Gets the input for this operation.
+     *
+     * An operation invocation on an entity includes an operation name, which
+     * states what operation to perform, and optionally an operation input.
+     *
+     * @returns The operation input, or undefined if none.
+     */
+    public getInput(): unknown | undefined {
+        throw new Error("This is a placeholder.");
+    }
+
+    /**
+     * Returns the given result to the caller of this operation.
+     *
+     * @param result The result to return.
+     */
+    public return(result: unknown): void {
+        throw new Error("This is a placeholder.");
+    }
+
+    /**
+     * Deletes this entity after this operation completes.
+     */
+    public destructOnExit(): void {
+        throw new Error("This is a placeholder.");
+    }
+
+    /**
+     * Signals an entity to perform an operation, without waiting for a
+     * response. Any result or exception is ignored (fire and forget).
+     *
+     * @param entity The target entity.
+     * @param operationName The name of the operation.
+     * @param operationInput The operation input.
+     */
+    public signalEntity(entity: EntityId, operationName: string, operationInput?: unknown): void {
+        throw new Error("This is a placeholder.");
+    }
+}
diff --git a/src/durableorchestrationbindinginfo.ts b/src/durableorchestrationbindinginfo.ts
index 6ceccb8..2ca058d 100644
--- a/src/durableorchestrationbindinginfo.ts
+++ b/src/durableorchestrationbindinginfo.ts
@@ -1,4 +1,4 @@
-import { HistoryEvent } from "./classes";
+import { EntityId, HistoryEvent } from "./classes";
 
 /** @hidden */
 export class DurableOrchestrationBindingInfo {
@@ -8,5 +8,7 @@ export class DurableOrchestrationBindingInfo {
         public readonly instanceId?: string,
         public readonly isReplaying?: boolean,
         public readonly parentInstanceId?: string,
+        // TODO: Implement entity locking
+        // public readonly contextLocks?: EntityId[],
     ) { }
 }
diff --git a/src/durableorchestrationclient.ts b/src/durableorchestrationclient.ts
index 3ffb87a..4871653 100644
--- a/src/durableorchestrationclient.ts
+++ b/src/durableorchestrationclient.ts
@@ -1,13 +1,19 @@
+// tslint:disable:member-access
+
 import { HttpRequest } from "@azure/functions";
-import axios, { AxiosInstance } from "axios";
+import axios, { AxiosInstance, AxiosResponse } from "axios";
 import cloneDeep = require("lodash/cloneDeep");
 import process = require("process");
 import url = require("url");
+import uuid = require("uuid/v1");
 import { isURL } from "validator";
-import { Constants, DurableOrchestrationStatus, HttpCreationPayload, HttpManagementPayload, IFunctionContext,
-    IHttpRequest, IHttpResponse, OrchestrationClientInputData, OrchestrationRuntimeStatus,
-    PurgeHistoryResult, Utils,
+import { Constants, DurableOrchestrationStatus, EntityId, EntityStateResponse,
+    GetStatusOptions, HttpCreationPayload, HttpManagementPayload,
+    IHttpRequest, IHttpResponse, IOrchestrationFunctionContext, OrchestrationClientInputData,
+    OrchestrationRuntimeStatus, PurgeHistoryResult, Utils,
 } from "./classes";
+import { WebhookUtils } from "./webhookutils";
+
 /**
  * Returns an OrchestrationClient instance.
  * @param context The context object of the Azure function whose body
@@ -25,7 +31,7 @@ import { Constants, DurableOrchestrationStatus, HttpCreationPayload, HttpManagem
  * ```
  */
 export function getClient(context: unknown): DurableOrchestrationClient {
-    let clientData = getClientData(context as IFunctionContext);
+    let clientData = getClientData(context as IOrchestrationFunctionContext);
 
     if (!process.env.WEBSITE_HOSTNAME || process.env.WEBSITE_HOSTNAME.includes("0.0.0.0")) {
         clientData = correctClientData(clientData);
@@ -34,16 +40,18 @@ export function getClient(context: unknown): DurableOrchestrationClient {
     return new DurableOrchestrationClient(clientData);
 }
 
-function getClientData(context: IFunctionContext): OrchestrationClientInputData {
-    const matchingInstances = Utils.getInstancesOf<OrchestrationClientInputData>(
-        (context as IFunctionContext).bindings,
-        new OrchestrationClientInputData(undefined, undefined, undefined));
+function getClientData(context: IOrchestrationFunctionContext): OrchestrationClientInputData {
+    if (context.bindings) {
+        const matchingInstances = Object.keys(context.bindings)
+        .map((key) => context.bindings[key])
+        .filter((val) => OrchestrationClientInputData.isOrchestrationClientInputData(val));
 
-    if (!matchingInstances || matchingInstances.length === 0) {
-        throw new Error("An orchestration client function must have an orchestrationClient input binding. Check your function.json definition.");
+        if (matchingInstances && matchingInstances.length > 0) {
+            return matchingInstances[0] as OrchestrationClientInputData;
+        }
     }
 
-    return matchingInstances[0];
+    throw new Error("An orchestration client function must have an orchestrationClient input binding. Check your function.json definition.");
 }
 
 function correctClientData(clientData: OrchestrationClientInputData): OrchestrationClientInputData {
@@ -179,22 +187,15 @@ export class DurableOrchestrationClient {
         showHistoryOutput?: boolean,
         showInput?: boolean,
         ): Promise<DurableOrchestrationStatus> {
-        const template = this.clientData.managementUrls.statusQueryGetUri;
-        const idPlaceholder = this.clientData.managementUrls.id;
-
-        let webhookUrl = template.replace(idPlaceholder, instanceId);
-        if (showHistory) {
-            webhookUrl += `&${this.showHistoryQueryKey}=${showHistory}`;
-        }
-        if (showHistoryOutput) {
-            webhookUrl += `&${this.showHistoryOutputQueryKey}=${showHistoryOutput}`;
-        }
-        if (showInput === false) {
-            webhookUrl += `&${this.showInputQueryKey}=${showInput}`;
-        }
-
         try {
-            const response = await this.axiosInstance.get(webhookUrl);
+            const options: GetStatusOptions = {
+                instanceId,
+                showHistory,
+                showHistoryOutput,
+                showInput,
+            };
+            const response = await this.getStatusInternal(options);
+
             switch (response.status) {
                 case 200: // instance completed
                 case 202: // instance in progress
@@ -214,13 +215,8 @@ export class DurableOrchestrationClient {
      * Gets the status of all orchestration instances.
      */
     public async getStatusAll(): Promise<DurableOrchestrationStatus[]> {
-        // omit instanceId to get status for all instances
-        const idPlaceholder = this.clientData.managementUrls.id;
-        const requestUrl = this.clientData.managementUrls.statusQueryGetUri
-            .replace(idPlaceholder, "");
-
         try {
-            const response = await this.axiosInstance.get(requestUrl);
+            const response = await this.getStatusInternal({});
             return response.data as DurableOrchestrationStatus[];
         } catch (error) {   // error object is axios-specific, not a JavaScript Error; extract relevant bit
             throw error.message;
@@ -242,30 +238,14 @@ export class DurableOrchestrationClient {
         createdTimeTo: Date,
         runtimeStatus: OrchestrationRuntimeStatus[],
         ): Promise<DurableOrchestrationStatus[]> {
-        const idPlaceholder = this.clientData.managementUrls.id;
-        let requestUrl = this.clientData.managementUrls.statusQueryGetUri
-            .replace(idPlaceholder, "");
-
-        if (createdTimeFrom) {
-            requestUrl += `&${this.createdTimeFromQueryKey}=${createdTimeFrom.toISOString()}`;
-        }
-
-        if (createdTimeTo) {
-            requestUrl += `&${this.createdTimeToQueryKey}=${createdTimeTo.toISOString()}`;
-        }
-
-        if (runtimeStatus && runtimeStatus.length > 0) {
-            const statusesString = runtimeStatus
-                .map((value) => value.toString())
-                .reduce((acc, curr, i, arr) => {
-                    return acc + (i > 0 ? "," : "") + curr;
-            });
-
-            requestUrl += `&${this.runtimeStatusQueryKey}=${statusesString}`;
-        }
-
         try {
-            const response = await this.axiosInstance.get(requestUrl);
+            const options: GetStatusOptions = {
+                createdTimeFrom,
+                createdTimeTo,
+                runtimeStatus,
+            };
+            const response = await this.getStatusInternal(options);
+
             if (response.status > 202) {
                 return Promise.reject(new Error(`Webhook returned status code ${response.status}: ${response.data}`));
             } else {
@@ -418,6 +398,40 @@ export class DurableOrchestrationClient {
         }
     }
 
+    /**
+     * Tries to read the current state of an entity. Returnes undefined if the
+     * entity does not exist, or if the JSON-serialized state of the entity is
+     * larger than 16KB.
+     * @param T The JSON-serializable type of the entity.
+     * @param entityId The target entity.
+     * @param taskHubName The TaskHubName of the target entity.
+     * @param connectionName The name of the connection string associated with
+     * [taskHubName].
+     * @returns A response containing the current state of the entity.
+     */
+    public async readEntityState<T>(entityId: EntityId, taskHubName?: string, connectionName?: string): Promise<EntityStateResponse<T>> {
+        const requestUrl = WebhookUtils.getReadEntityUrl(this.clientData.baseUrl,
+            this.clientData.requiredQueryStringParameters,
+            entityId.name,
+            entityId.key,
+            taskHubName,
+            connectionName);
+
+        try {
+            const response = await this.axiosInstance.get(requestUrl);
+            switch (response.status) {
+                case 200:   // entity exists
+                    return new EntityStateResponse(true, response.data as T);
+                case 404:   // entity does not exist
+                    return new EntityStateResponse(false, undefined);
+                default:
+                    return Promise.reject(new Error(`Webhook returned unrecognized status code ${response.status}`));
+            }
+        } catch (error) {   // error object is axios-specific, not a JavaScript Error; extract relevant bit
+            throw error.message;
+        }
+    }
+
     /**
      * Rewinds the specified failed orchestration instance with a reason.
      * @param instanceId The ID of the orchestration instance to rewind.
@@ -449,6 +463,42 @@ export class DurableOrchestrationClient {
         }
     }
 
+    /**
+     * Signals an entity to perform an operation.
+     * @param entityId The target entity.
+     * @param operationName The name of the operation.
+     * @param operationContent The content for the operation.
+     * @param taskHubName The TaskHubName of the target entity.
+     * @param connectionName The name of the connection string associated with [taskHubName].
+     */
+    public async signalEntity(
+        entityId: EntityId,
+        operationName?: string,
+        operationContent?: unknown,
+        taskHubName?: string,
+        connectionName?: string,
+        ): Promise<void> {
+        const requestUrl = WebhookUtils.getSignalEntityUrl(this.clientData.baseUrl,
+            this.clientData.requiredQueryStringParameters,
+            entityId.name,
+            entityId.key,
+            operationName,
+            taskHubName,
+            connectionName);
+
+        try {
+            const response = await this.axiosInstance.post(requestUrl, JSON.stringify(operationContent));
+            switch (response.status) {
+                case 202: // signal accepted
+                    return;
+                default:
+                    return Promise.reject(new Error(`Webhook returned unrecognized status code ${response.status}`));
+            }
+        } catch (error) {   // error object is axios-specific, not a JavaScript Error; extract relevant bit
+            throw error.message;
+        }
+    }
+
     /**
      * Starts a new instance of the specified orchestrator function.
      *
@@ -636,4 +686,43 @@ export class DurableOrchestrationClient {
 
         return origins;
     }
+
+    private async getStatusInternal(options: GetStatusOptions): Promise<AxiosResponse> {
+        const template = this.clientData.managementUrls.statusQueryGetUri;
+        const idPlaceholder = this.clientData.managementUrls.id;
+
+        let requestUrl = template.replace(idPlaceholder, typeof(options.instanceId) === "string" ? options.instanceId : "");
+        if (options.taskHubName) {
+            requestUrl = requestUrl.replace(this.clientData.taskHubName, options.taskHubName);
+        }
+        if (options.connectionName) {
+            requestUrl = requestUrl.replace(/(connection=)([\w]+)/gi, "$1" + options.connectionName);
+        }
+        if (options.showHistory) {
+            requestUrl += `&${this.showHistoryQueryKey}=${options.showHistory}`;
+        }
+        if (options.showHistoryOutput) {
+            requestUrl += `&${this.showHistoryOutputQueryKey}=${options.showHistoryOutput}`;
+        }
+        if (options.createdTimeFrom) {
+            requestUrl += `&${this.createdTimeFromQueryKey}=${options.createdTimeFrom.toISOString()}`;
+        }
+        if (options.createdTimeTo) {
+            requestUrl += `&${this.createdTimeToQueryKey}=${options.createdTimeTo.toISOString()}`;
+        }
+        if (options.runtimeStatus && options.runtimeStatus.length > 0) {
+            const statusesString = options.runtimeStatus
+                .map((value) => value.toString())
+                .reduce((acc, curr, i, arr) => {
+                    return acc + (i > 0 ? "," : "") + curr;
+            });
+
+            requestUrl += `&${this.runtimeStatusQueryKey}=${statusesString}`;
+        }
+        if (typeof options.showInput === "boolean") {
+            requestUrl += `&${this.showInputQueryKey}=${options.showInput}`;
+        }
+
+        return this.axiosInstance.get(requestUrl);
+    }
 }
diff --git a/src/durableorchestrationcontext.ts b/src/durableorchestrationcontext.ts
index a87130c..da91f08 100644
--- a/src/durableorchestrationcontext.ts
+++ b/src/durableorchestrationcontext.ts
@@ -1,4 +1,5 @@
-import { DurableHttpRequest, ITaskMethods, RetryOptions, Task, TimerTask } from "./classes";
+import { DurableHttpRequest, DurableLock, EntityId, ITaskMethods, LockState,
+     RetryOptions, Task, TimerTask } from "./classes";
 
 /**
  * Parameter data for orchestration bindings that can be used to schedule
@@ -79,6 +80,18 @@ export class DurableOrchestrationContext {
         throw new Error("This is a placeholder.");
     }
 
+    /**
+     * Calls an operation on an entity, passing an argument, and waits for it
+     * to complete.
+     *
+     * @param entityId The target entity.
+     * @param operationName The name of the operation.
+     * @param operationInput The input for the operation.
+     */
+    public callEntity(entityId: EntityId, operationName: string, operationInput?: unknown): Task {
+        throw new Error("This is a placeholder.");
+    }
+
     /**
      * Schedules an orchestration function named `name` for execution.
      *
diff --git a/src/entities/durablelock.ts b/src/entities/durablelock.ts
new file mode 100644
index 0000000..fbcdd12
--- /dev/null
+++ b/src/entities/durablelock.ts
@@ -0,0 +1,7 @@
+/**
+ * For use with locking operations.
+ * TODO: improve this
+ */
+export class DurableLock {
+
+}
diff --git a/src/entities/entityid.ts b/src/entities/entityid.ts
new file mode 100644
index 0000000..e0cfe6d
--- /dev/null
+++ b/src/entities/entityid.ts
@@ -0,0 +1,39 @@
+// tslint:disable:member-access
+
+import { Utils } from "../utils";
+
+/**
+ * A unique identifier for an entity, consisting of entity class and entity key.
+ */
+export class EntityId {
+    /** @hidden */
+    static getEntityIdFromSchedulerId(schedulerId: string): EntityId {
+        const pos = schedulerId.indexOf("@", 1);
+        const entityName = schedulerId.substring(1, pos);
+        const entityKey = schedulerId.substring(pos + 1);
+        return new EntityId(entityName, entityKey);
+    }
+
+    /** @hidden */
+    static getSchedulerIdFromEntityId(entityId: EntityId): string {
+        return `@${entityId.name.toLowerCase()}@${entityId.key}`;
+    }
+
+    /**
+     * Create an entity id for an entity.
+     */
+    constructor(
+        // TODO: consider how to name these fields more accurately without interfering with JSON serialization
+        /** The name of the entity class. */
+        public readonly name: string,
+        /** The entity key. Uniquely identifies an entity among all instances of the same class. */
+        public readonly key: string,
+    ) {
+        Utils.throwIfEmpty(name, "name");
+        Utils.throwIfEmpty(key, "key");
+    }
+
+    public toString(): string {
+        return EntityId.getSchedulerIdFromEntityId(this);
+    }
+}
diff --git a/src/entities/entitystate.ts b/src/entities/entitystate.ts
new file mode 100644
index 0000000..0ddf7e0
--- /dev/null
+++ b/src/entities/entitystate.ts
@@ -0,0 +1,14 @@
+import { OperationResult, Signal } from "../classes";
+
+/** @hidden */
+export class EntityState {
+    public entityExists: boolean;
+    public entityState: string;
+    public readonly results: OperationResult[];
+    public readonly signals: Signal[];
+
+    constructor(results: OperationResult[], signals: Signal[]) {
+        this.results = results;
+        this.signals = signals;
+    }
+}
diff --git a/src/entities/entitystateresponse.ts b/src/entities/entitystateresponse.ts
new file mode 100644
index 0000000..0d47125
--- /dev/null
+++ b/src/entities/entitystateresponse.ts
@@ -0,0 +1,12 @@
+/**
+ * The response returned by [[DurableOrchestrationClient]].[[readEntityState]].
+ */
+export class EntityStateResponse<T> {
+    constructor(
+        /** Whether this entity exists or not. */
+        public entityExists: boolean,
+
+        /** The current state of the entity, if it exists, or default value otherwise. */
+        public entityState: T,
+    ) {}
+}
diff --git a/src/entities/lockstate.ts b/src/entities/lockstate.ts
new file mode 100644
index 0000000..e0c8400
--- /dev/null
+++ b/src/entities/lockstate.ts
@@ -0,0 +1,14 @@
+import { EntityId } from "../classes";
+
+/**
+ * Returned by [DurableOrchestrationContext].[isLocked] and
+ * [DurableEntityContext].[isLocked].
+ */
+export class LockState {
+    constructor(
+        /** Whether the context already holds some locks. */
+        public readonly isLocked: boolean,
+        /** The locks held by the context. */
+        public readonly ownedLocks: EntityId[],
+    ) { }
+}
diff --git a/src/entities/operationresult.ts b/src/entities/operationresult.ts
new file mode 100644
index 0000000..f761c60
--- /dev/null
+++ b/src/entities/operationresult.ts
@@ -0,0 +1,8 @@
+/** @hidden */
+export class OperationResult {
+    constructor(
+        readonly isError: boolean,
+        readonly duration: number,
+        readonly result?: string,
+    ) { }
+}
diff --git a/src/entities/requestmessage.ts b/src/entities/requestmessage.ts
new file mode 100644
index 0000000..cdd0fa2
--- /dev/null
+++ b/src/entities/requestmessage.ts
@@ -0,0 +1,34 @@
+import { EntityId } from "./entityid";
+
+/** @hidden */
+export class RequestMessage {
+    /** A unique identifier for this operation. */
+    public id: string;  // Id
+
+    /**
+     * The name of the operation being called (if this is an operation message)
+     * or undefined (if this is a lock request).
+     */
+    public name?: string;          // Operation
+
+    /** Whether or not this is a one-way message. */
+    public signal?: boolean;    // IsSignal
+
+    /** The operation input. */
+    public input?: string;      // Input
+
+    /** The content the operation was called with. */
+    public arg?: unknown;       // Content
+
+    /** The parent instance that called this operation. */
+    public parent?: string;     // ParentInstanceId
+
+    /**
+     * For lock requests, the set of locks being acquired. Is sorted,
+     * contains at least one element, and has no repetitions.
+     */
+    public lockset?: EntityId[];    // LockSet
+
+    /** For lock requests involving multiple locks, the message number. */
+    public pos?: number;            // Position
+}
diff --git a/src/entities/responsemessage.ts b/src/entities/responsemessage.ts
new file mode 100644
index 0000000..dacfac7
--- /dev/null
+++ b/src/entities/responsemessage.ts
@@ -0,0 +1,7 @@
+/** @hidden */
+export class ResponseMessage {
+    public result: string;              // Result
+    public exceptionType?: string;      // ExceptionType
+}
+
+// TODO: error deserialization
diff --git a/src/entities/signal.ts b/src/entities/signal.ts
new file mode 100644
index 0000000..61532c3
--- /dev/null
+++ b/src/entities/signal.ts
@@ -0,0 +1,9 @@
+import { EntityId } from "../classes";
+
+export class Signal {
+    constructor(
+        public readonly target: EntityId,
+        public readonly name: string,
+        public readonly input: string,
+    ) { }
+}
diff --git a/src/entity.ts b/src/entity.ts
new file mode 100644
index 0000000..e74ac71
--- /dev/null
+++ b/src/entity.ts
@@ -0,0 +1,103 @@
+import * as debug from "debug";
+import { DurableEntityBindingInfo, DurableEntityContext, EntityId, EntityState, IEntityFunctionContext,
+    OperationResult, RequestMessage, Signal, Utils } from "./classes";
+
+/** @hidden */
+const log = debug("orchestrator");
+
+/** @hidden */
+export class Entity {
+    constructor(public fn: (context: IEntityFunctionContext) => unknown) { }
+
+    public listen() {
+        return this.handle.bind(this);
+    }
+
+    private async handle(context: IEntityFunctionContext): Promise<void> {
+        const entityBinding = Utils.getInstancesOf<DurableEntityBindingInfo>(
+            context.bindings, new DurableEntityBindingInfo(null, null, null, []))[0];
+
+        if (entityBinding === undefined) {
+            throw new Error("Could not find an entityTrigger binding on context.");
+        }
+
+        // Setup
+        const returnState: EntityState = new EntityState([], []);
+        returnState.entityExists = entityBinding.exists;
+        returnState.entityState = entityBinding.state;
+        for (let i = 0; i < entityBinding.batch.length; i++) {
+            const startTime = new Date();
+            context.df = this.getCurrentDurableEntityContext(entityBinding, returnState, i, startTime);
+
+            try {
+                this.fn(context);
+                if (!returnState.results[i]) {
+                    const elapsedMs = this.computeElapsedMilliseconds(startTime);
+                    returnState.results[i] = new OperationResult(false, elapsedMs);
+                }
+            } catch (error) {
+                const elapsedMs = this.computeElapsedMilliseconds(startTime);
+                returnState.results[i] = new OperationResult(true, elapsedMs, JSON.stringify(error));
+            }
+        }
+
+        context.done(null, returnState);
+    }
+
+    private getCurrentDurableEntityContext(bindingInfo: DurableEntityBindingInfo, batchState: EntityState, requestIndex: number, startTime: Date): DurableEntityContext  {
+        const currentRequest = bindingInfo.batch[requestIndex];
+        return {
+            entityName: bindingInfo.self.name,
+            entityKey: bindingInfo.self.key,
+            entityId: bindingInfo.self,
+            operationName: currentRequest.name,
+            isNewlyConstructed: !batchState.entityExists,
+            getState: this.getState.bind(this, batchState),
+            setState: this.setState.bind(this, batchState),
+            getInput: this.getInput.bind(this, currentRequest),
+            return: this.return.bind(this, batchState, startTime),
+            destructOnExit: this.destructOnExit.bind(this, batchState),
+            signalEntity: this.signalEntity.bind(this, batchState),
+        };
+    }
+
+    private destructOnExit(batchState: EntityState): void {
+        batchState.entityExists = false;
+        batchState.entityState = undefined;
+    }
+
+    private getInput(currentRequest: RequestMessage): unknown | undefined {
+        if (currentRequest.input) {
+            return JSON.parse(currentRequest.input);
+        }
+        return undefined;
+    }
+
+    private getState(returnState: EntityState, initializer?: () => unknown): unknown | undefined {
+        if (returnState.entityState) {
+            return JSON.parse(returnState.entityState);
+        } else if (initializer != null) {
+            return initializer();
+        }
+        return undefined;
+    }
+
+    private return(returnState: EntityState, startTime: Date, result: unknown): void {
+        returnState.entityExists = true;
+        returnState.results.push(new OperationResult(false, this.computeElapsedMilliseconds(startTime), JSON.stringify(result)));
+    }
+
+    private setState(returnState: EntityState, state: unknown): void {
+        returnState.entityExists = true;
+        returnState.entityState = JSON.stringify(state);
+    }
+
+    private signalEntity(returnState: EntityState, entity: EntityId, operationName: string, operationInput?: unknown): void {
+        returnState.signals.push(new Signal(entity, operationName, operationInput ? JSON.stringify(operationInput) : ""));
+    }
+
+    private computeElapsedMilliseconds(startTime: Date): number {
+        const endTime = new Date();
+        return endTime.getTime() - startTime.getTime();
+    }
+}
diff --git a/src/getstatusoptions.ts b/src/getstatusoptions.ts
new file mode 100644
index 0000000..025e22c
--- /dev/null
+++ b/src/getstatusoptions.ts
@@ -0,0 +1,16 @@
+// tslint:disable:member-access
+
+import { OrchestrationRuntimeStatus } from "./classes";
+
+/** @hidden */
+export class GetStatusOptions {
+    instanceId?: string;
+    taskHubName?: string;
+    connectionName?: string;
+    showHistory?: boolean;
+    showHistoryOutput?: boolean;
+    createdTimeFrom?: Date;
+    createdTimeTo?: Date;
+    runtimeStatus?: OrchestrationRuntimeStatus[];
+    showInput?: boolean;
+}
diff --git a/src/history/eventsentevent.ts b/src/history/eventsentevent.ts
new file mode 100644
index 0000000..52e690d
--- /dev/null
+++ b/src/history/eventsentevent.ts
@@ -0,0 +1,23 @@
+import { HistoryEvent, HistoryEventOptions, HistoryEventType } from "../classes";
+
+/** @hidden */
+export class EventSentEvent extends HistoryEvent {
+    public Name: string;
+    public Input: string;
+    public InstanceId: string;
+
+    constructor(
+        options: HistoryEventOptions,
+    ) {
+        super(
+            HistoryEventType.EventSent,
+            options.eventId,
+            options.isPlayed,
+            options.timestamp,
+        );
+
+        this.Name = options.name;
+        this.Input = options.input;
+        this.InstanceId = options.instanceId;
+    }
+}
diff --git a/src/ientityfunctioncontext.ts b/src/ientityfunctioncontext.ts
new file mode 100644
index 0000000..ee77a2c
--- /dev/null
+++ b/src/ientityfunctioncontext.ts
@@ -0,0 +1,6 @@
+import { Context } from "@azure/functions";
+import { DurableEntityContext } from "./classes";
+
+export interface IEntityFunctionContext extends Context {
+    df: DurableEntityContext;
+}
diff --git a/src/index.ts b/src/index.ts
index fbfc07b..7ae07cd 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,11 +1,15 @@
-import { DurableHttpRequest, DurableHttpResponse, OrchestrationRuntimeStatus, RetryOptions } from "./classes";
+import { DurableHttpRequest, DurableHttpResponse, EntityId, EntityStateResponse,
+    OrchestrationRuntimeStatus, RetryOptions } from "./classes";
 import { getClient } from "./durableorchestrationclient";
-import { orchestrator } from "./shim";
+import { entity, orchestrator } from "./shim";
 import { ManagedIdentityTokenSource } from "./tokensource";
 
 export {
     DurableHttpRequest,
     DurableHttpResponse,
+    entity,
+    EntityId,
+    EntityStateResponse,
     getClient,
     ManagedIdentityTokenSource,
     orchestrator,
diff --git a/src/ifunctioncontext.ts b/src/iorchestrationfunctioncontext.ts
similarity index 68%
rename from src/ifunctioncontext.ts
rename to src/iorchestrationfunctioncontext.ts
index ceea220..28e7d65 100644
--- a/src/ifunctioncontext.ts
+++ b/src/iorchestrationfunctioncontext.ts
@@ -1,6 +1,6 @@
 import { Context } from "@azure/functions";
 import { DurableOrchestrationContext } from "./classes";
 
-export interface IFunctionContext extends Context {
+export interface IOrchestrationFunctionContext extends Context {
     df: DurableOrchestrationContext;
 }
diff --git a/src/orchestrationclientinputdata.ts b/src/orchestrationclientinputdata.ts
index b05ff65..e499de4 100644
--- a/src/orchestrationclientinputdata.ts
+++ b/src/orchestrationclientinputdata.ts
@@ -2,9 +2,25 @@ import { HttpCreationPayload, HttpManagementPayload } from "./classes";
 
 /** @hidden */
 export class OrchestrationClientInputData {
+    public static isOrchestrationClientInputData(obj: unknown): boolean {
+        const typedInstance = obj as { [index: string]: unknown };
+        if (typedInstance) {
+            // Only check for required fields.
+            if (typedInstance.taskHubName !== undefined
+                && typedInstance.creationUrls !== undefined
+                && typedInstance.managementUrls !== undefined) {
+                return true;
+            }
+            return false;
+        }
+        return false;
+    }
+
     constructor(
         public taskHubName: string,
         public creationUrls: HttpCreationPayload,
         public managementUrls: HttpManagementPayload,
+        public baseUrl?: string,
+        public requiredQueryStringParameters?: string,
     ) { }
 }
diff --git a/src/orchestrator.ts b/src/orchestrator.ts
index 19f8b5f..4dbfa59 100644
--- a/src/orchestrator.ts
+++ b/src/orchestrator.ts
@@ -1,9 +1,10 @@
 import * as debug from "debug";
-import { CallActivityAction, CallActivityWithRetryAction, CallHttpAction, CallSubOrchestratorAction,
-    CallSubOrchestratorWithRetryAction, ContinueAsNewAction, CreateTimerAction, DurableHttpRequest,
-    DurableOrchestrationBindingInfo, EventRaisedEvent, GuidManager, HistoryEvent,
-    HistoryEventType, IAction, IFunctionContext, OrchestratorState, RetryOptions,
-    SubOrchestrationInstanceCompletedEvent, SubOrchestrationInstanceCreatedEvent,
+import { CallActivityAction, CallActivityWithRetryAction, CallEntityAction, CallHttpAction,
+    CallSubOrchestratorAction, CallSubOrchestratorWithRetryAction, ContinueAsNewAction,
+    CreateTimerAction, DurableHttpRequest, DurableLock, DurableOrchestrationBindingInfo, EntityId,
+    EventRaisedEvent, EventSentEvent, ExternalEventType, GuidManager, HistoryEvent, HistoryEventType,
+    IAction, IOrchestrationFunctionContext, LockState, OrchestratorState, RequestMessage, ResponseMessage,
+    RetryOptions, SubOrchestrationInstanceCompletedEvent, SubOrchestrationInstanceCreatedEvent,
     SubOrchestrationInstanceFailedEvent, Task, TaskCompletedEvent, TaskFailedEvent,
     TaskScheduledEvent, TaskSet, TimerCreatedEvent, TimerFiredEvent, TimerTask,
     Utils, WaitForExternalEventAction,
@@ -19,13 +20,13 @@ export class Orchestrator {
     private customStatus: unknown;
     private newGuidCounter: number;
 
-    constructor(public fn: (context: IFunctionContext) => IterableIterator<unknown>) { }
+    constructor(public fn: (context: IOrchestrationFunctionContext) => IterableIterator<unknown>) { }
 
     public listen() {
         return this.handle.bind(this);
     }
 
-    private async handle(context: IFunctionContext): Promise<void> {
+    private async handle(context: IOrchestrationFunctionContext): Promise<void> {
         const orchestrationBinding = Utils.getInstancesOf<DurableOrchestrationBindingInfo>(
             context.bindings, new DurableOrchestrationBindingInfo())[0];
 
@@ -36,6 +37,7 @@ export class Orchestrator {
         const state: HistoryEvent[] = orchestrationBinding.history;
         const input: unknown = orchestrationBinding.input;
         const instanceId: string = orchestrationBinding.instanceId;
+        // const contextLocks: EntityId[] = orchestrationBinding.contextLocks;
 
         // Initialize currentUtcDateTime
         let decisionStartedEvent: HistoryEvent = state.find((e) =>
@@ -54,12 +56,15 @@ export class Orchestrator {
             parentInstanceId: orchestrationBinding.parentInstanceId,
             callActivity: this.callActivity.bind(this, state),
             callActivityWithRetry: this.callActivityWithRetry.bind(this, state),
+            callEntity: this.callEntity.bind(this, state),
             callSubOrchestrator: this.callSubOrchestrator.bind(this, state),
             callSubOrchestratorWithRetry: this.callSubOrchestratorWithRetry.bind(this, state),
             callHttp: this.callHttp.bind(this, state),
             continueAsNew: this.continueAsNew.bind(this, state),
             createTimer: this.createTimer.bind(this, state),
             getInput: this.getInput.bind(this, input),
+            // isLocked: this.isLocked.bind(this, contextLocks),
+            // lock: this.lock.bind(this, state, instanceId, contextLocks),
             newGuid: this.newGuid.bind(this, instanceId),
             setCustomStatus: this.setCustomStatus.bind(this),
             waitForExternalEvent: this.waitForExternalEvent.bind(this, state),
@@ -215,6 +220,33 @@ export class Orchestrator {
         );
     }
 
+    private callEntity(state: HistoryEvent[], entityId: EntityId, operationName: string, operationInput: unknown): Task {
+        const newAction = new CallEntityAction(entityId, operationName, operationInput);
+
+        const schedulerId = EntityId.getSchedulerIdFromEntityId(entityId);
+        const eventSent = this.findEventSent(state, schedulerId, "op");
+        let eventRaised;
+        if (eventSent) {
+            const eventSentInput = eventSent ? JSON.parse(eventSent.Input) as RequestMessage : undefined;
+            eventRaised = eventSentInput ? this.findEventRaised(state, eventSentInput.id) : undefined;
+        }
+        this.setProcessed([ eventSent, eventRaised ]);
+
+        if (eventRaised) {
+            const parsedResult = this.parseHistoryEvent(eventRaised) as ResponseMessage;
+
+            return new Task(true, false, newAction, JSON.parse(parsedResult.result), eventRaised.Timestamp, eventSent.EventId);
+        }
+
+        // TODO: error handling
+
+        return new Task(
+            false,
+            false,
+            newAction,
+        );
+    }
+
     private callSubOrchestrator(state: HistoryEvent[], name: string, input?: unknown, instanceId?: string): Task {
         const newAction = new CallSubOrchestratorAction(name, instanceId, input);
 
@@ -380,6 +412,59 @@ export class Orchestrator {
         return input;
     }
 
+    private isLocked(contextLocks: EntityId[]): LockState {
+        return new LockState(
+            contextLocks !== undefined && contextLocks !== null,
+            contextLocks,
+        );
+    }
+
+    private lock(state: HistoryEvent[], instanceId: string, contextLocks: EntityId[], entities: EntityId[]): DurableLock {
+        if (contextLocks) {
+            throw new Error("Cannot acquire more locks when already holding some locks.");
+        }
+
+        if (!entities || entities.length === 0) {
+            throw new Error("The list of entities to lock must not be null or empty.");
+        }
+
+        entities = this.cleanEntities(entities);
+
+        const lockRequestId = this.newGuid(instanceId);
+
+        // All the entities in entities[] need to be locked, but to avoid
+        // deadlock, the locks have to be acquired sequentially, in order. So,
+        // we send the lock request to the first entity; when the first lock is
+        // granted by the first entity, the first entity will forward the lock
+        // request to the second entity, and so on; after the last entity
+        // grants the last lock, a response is sent back here.
+
+        // send lock request to first entity in the lock set
+
+        return undefined;
+    }
+
+    private cleanEntities(entities: EntityId[]): EntityId[] {
+        // sort entities
+        return entities.sort((a, b) => {
+            if (a.key === b.key) {
+                if (a.name === b.name) {
+                    return 0;
+                } else if (a.name < b.name) {
+                    return -1;
+                } else {
+                    return 1;
+                }
+            } else if (a.key < b.key) {
+                return -1;
+            } else {
+                return 1;
+            }
+        });
+
+        // TODO: remove duplicates if necessary
+    }
+
     private newGuid(instanceId: string): string {
         const guidNameValue = `${instanceId}_${this.currentUtcDateTime.valueOf()}_${this.newGuidCounter}`;
         this.newGuidCounter++;
@@ -391,7 +476,7 @@ export class Orchestrator {
     }
 
     private waitForExternalEvent(state: HistoryEvent[], name: string): Task {
-        const newAction = new WaitForExternalEventAction(name);
+        const newAction = new WaitForExternalEventAction(name, ExternalEventType.ExternalEvent);
 
         const eventRaised = this.findEventRaised(state, name);
         this.setProcessed([ eventRaised ]);
@@ -480,6 +565,19 @@ export class Orchestrator {
         return returnValue as EventRaisedEvent;
     }
 
+    /* Returns undefined if not found. */
+    private findEventSent(state: HistoryEvent[], instanceId: string, eventName: string): EventSentEvent {
+        const returnValue = eventName
+            ? state.filter((val: HistoryEvent) => {
+                return val.EventType === HistoryEventType.EventSent
+                    && (val as EventSentEvent).InstanceId === instanceId
+                    && (val as EventSentEvent).Name === eventName
+                    && !val.IsProcessed;
+            })[0]
+            : undefined;
+        return returnValue as EventSentEvent;
+    }
+
     /* Returns undefined if not found. */
     private findRetryTimer(state: HistoryEvent[], failedTask: HistoryEvent): TimerCreatedEvent {
         const returnValue = failedTask
diff --git a/src/retryoptions.ts b/src/retryoptions.ts
index 4ec3dfa..ca3a4fc 100644
--- a/src/retryoptions.ts
+++ b/src/retryoptions.ts
@@ -1,4 +1,3 @@
-import { Constants } from "./classes";
 import { Utils } from "./utils";
 
 /**
diff --git a/src/shim.ts b/src/shim.ts
index a9cec82..ffdcdda 100644
--- a/src/shim.ts
+++ b/src/shim.ts
@@ -1,4 +1,4 @@
-import { IFunctionContext, Orchestrator } from "./classes";
+import { Entity, IEntityFunctionContext, IOrchestrationFunctionContext, Orchestrator } from "./classes";
 
 /**
  * Enables a generator function to act as an orchestrator function.
@@ -14,10 +14,18 @@ import { IFunctionContext, Orchestrator } from "./classes";
  * });
  * ```
  */
-export function orchestrator(fn: (context: IFunctionContext) => IterableIterator<unknown>)
-    : (context: IFunctionContext) => void {
+export function orchestrator(fn: (context: IOrchestrationFunctionContext) => IterableIterator<unknown>)
+    : (context: IOrchestrationFunctionContext) => void {
     const listener = new Orchestrator(fn).listen();
-    return (context: IFunctionContext) => {
+    return (context: IOrchestrationFunctionContext) => {
+        listener(context);
+    };
+}
+
+export function entity(fn: (context: IEntityFunctionContext) => unknown)
+    : (context: IEntityFunctionContext) => void {
+    const listener = new Entity(fn).listen();
+    return (context: IEntityFunctionContext) => {
         listener(context);
     };
 }
diff --git a/src/utils.ts b/src/utils.ts
index e5d6aac..68df532 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -32,7 +32,7 @@ export class Utils {
     }
 
     public static throwIfEmpty(value: unknown, name: string): void {
-        if (typeof value !== "string" || !(value.trim())) {
+        if (typeof value !== "string" || value.trim().length < 1) {
             throw new TypeError(`${name}: Expected non-empty, non-whitespace string but got ${typeof value}`);
         }
     }
diff --git a/src/webhookutils.ts b/src/webhookutils.ts
new file mode 100644
index 0000000..0500d8b
--- /dev/null
+++ b/src/webhookutils.ts
@@ -0,0 +1,44 @@
+/** @hidden */
+export class WebhookUtils {
+    public static getReadEntityUrl(baseUrl: string, requiredQueryStrings: string, entityName: string, entityKey: string, taskHubName?: string, connectionName?: string): string {
+        let requestUrl = baseUrl + "/entities/"
+        + entityName + "/"
+        + entityKey + "?";
+
+        const queryStrings: string[] = [];
+        if (taskHubName) {
+            queryStrings.push("taskHub=" + taskHubName);
+        }
+
+        if (connectionName) {
+            queryStrings.push("connection=" + connectionName);
+        }
+
+        queryStrings.push(requiredQueryStrings);
+        requestUrl += queryStrings.join("&");
+        return requestUrl;
+    }
+
+    public static getSignalEntityUrl(baseUrl: string, requiredQueryStrings: string, entityName: string, entityKey: string, operationName?: string, taskHubName?: string, connectionName?: string) {
+        let requestUrl = baseUrl + "/entities/"
+        + entityName + "/"
+        + entityKey + "?";
+
+        const queryStrings: string[] = [];
+        if (operationName) {
+            queryStrings.push("op=" + operationName);
+        }
+
+        if (taskHubName) {
+            queryStrings.push("taskHub=" + taskHubName);
+        }
+
+        if (connectionName) {
+            queryStrings.push("connection=" + connectionName);
+        }
+
+        queryStrings.push(requiredQueryStrings);
+        requestUrl += queryStrings.join("&");
+        return requestUrl;
+    }
+}
diff --git a/tsconfig.json b/tsconfig.json
index 0047ef4..ba55b42 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,5 +1,6 @@
 {
   "compilerOptions": {
+    "strictNullChecks": false,
     "declaration": true,
     "module": "commonjs",
     "moduleResolution": "node",
diff --git a/tslint.json b/tslint.json
index 0180fc5..2f4d5a6 100644
--- a/tslint.json
+++ b/tslint.json
@@ -5,6 +5,7 @@
     "max-line-length": false,
     "no-implicit-dependencies": [true, "dev"],
     "no-submodule-imports": false,
-    "object-literal-sort-keys": false
+    "object-literal-sort-keys": false,
+    "interface-name": false
   }
 }


Test patch:
diff --git a/test/integration/entity-spec.ts b/test/integration/entity-spec.ts
new file mode 100644
index 0000000..d42935c
--- /dev/null
+++ b/test/integration/entity-spec.ts
@@ -0,0 +1,68 @@
+import { expect } from "chai";
+import "mocha";
+import { DurableEntityContext, EntityState, OperationResult } from "../../src/classes";
+import { TestEntities } from "../testobjects/testentities";
+import { TestEntityBatches  } from "../testobjects/testentitybatches";
+import { StringStoreOperation  } from "../testobjects/testentityoperations";
+
+describe("Entity", () => {
+    it("StringStore entity with no initial state.", async () => {
+        const entity = TestEntities.StringStore;
+        const operations: StringStoreOperation[] = [];
+        operations.push({ kind: "set", value: "hello"});
+        operations.push({ kind: "get"});
+        operations.push({ kind: "set", value: "hello world"});
+        operations.push({ kind: "get"});
+
+        const testData = TestEntityBatches.GetStringStoreBatch(operations, null);
+        const mockContext = new MockContext({
+            context: testData.input,
+        });
+        entity(mockContext);
+
+        entityStateMatchesExpected(mockContext.doneValue, testData.output);
+    });
+
+    it("StringStore entity with initial state.", async () => {
+        const entity = TestEntities.StringStore;
+        const operations: StringStoreOperation[] = [];
+        operations.push({ kind: "get"});
+
+        const testData = TestEntityBatches.GetStringStoreBatch(operations, "Hello world");
+        const mockContext = new MockContext({
+            context: testData.input,
+        });
+        entity(mockContext);
+
+        entityStateMatchesExpected(mockContext.doneValue, testData.output);
+    });
+});
+
+// Have to compare on an element by element basis as elapsed time is not deterministic.
+function entityStateMatchesExpected(actual: EntityState, expected: EntityState) {
+    expect(actual.entityExists).to.be.equal(expected.entityExists);
+    expect(actual.entityState).to.be.deep.equal(expected.entityState);
+    expect(actual.signals).to.be.deep.equal(expected.signals);
+    for (let i = 0; i < actual.results.length; i++) {
+        expect(actual.results[i].isError).to.be.equal(expected.results[i].isError);
+        expect(actual.results[i].result).to.be.deep.equal(expected.results[i].result);
+    }
+}
+
+class MockContext {
+    constructor(
+        public bindings: IBindings,
+        public df?: DurableEntityContext,
+        public doneValue?: EntityState,
+        public err?: Error | string | null,
+    ) { }
+
+    public done(err?: Error | string | null, result?: EntityState) {
+        this.doneValue = result;
+        this.err = err;
+    }
+}
+
+interface IBindings {
+    [key: string]: unknown;
+}
diff --git a/test/integration/orchestrator-spec.ts b/test/integration/orchestrator-spec.ts
index 0d2201e..c97953d 100644
--- a/test/integration/orchestrator-spec.ts
+++ b/test/integration/orchestrator-spec.ts
@@ -2,12 +2,14 @@ import { expect } from "chai";
 import "mocha";
 import * as moment from "moment";
 import * as uuidv1 from "uuid/v1";
+import { isUUID } from "validator";
 import { ManagedIdentityTokenSource } from "../../src";
 import {
-    CallActivityAction, CallActivityWithRetryAction, CallHttpAction, CallSubOrchestratorAction,
-    CallSubOrchestratorWithRetryAction, Constants, ContinueAsNewAction, CreateTimerAction,
-    DurableHttpRequest, DurableHttpResponse, DurableOrchestrationBindingInfo, DurableOrchestrationContext,
-    IOrchestratorState, OrchestratorState, RetryOptions, WaitForExternalEventAction,
+    CallActivityAction, CallActivityWithRetryAction, CallEntityAction, CallHttpAction, CallSubOrchestratorAction,
+    CallSubOrchestratorWithRetryAction, ContinueAsNewAction, CreateTimerAction, DurableHttpRequest,
+    DurableHttpResponse, DurableOrchestrationBindingInfo, DurableOrchestrationContext, EntityId,
+    ExternalEventType, IOrchestratorState, LockState, OrchestratorState,
+    RetryOptions, WaitForExternalEventAction,
 } from "../../src/classes";
 import { TestHistories } from "../testobjects/testhistories";
 import { TestOrchestrations } from "../testobjects/TestOrchestrations";
@@ -705,6 +707,79 @@ describe("Orchestrator", () => {
         });
     });
 
+    describe("callEntity()", () => {
+        const testEntityId = new EntityId("StringStore2", "12345");
+
+        it("schedules an entity request", async () => {
+            const orchestrator = TestOrchestrations.CallEntitySet; // TODO: finish
+            const instanceId = uuidv1();
+            const expectedEntity = new EntityId("StringStore2", "12345");
+            const mockContext = new MockContext({
+                context: new DurableOrchestrationBindingInfo(
+                    TestHistories.GetOrchestratorStart(
+                        "CallEntityGet",
+                        moment.utc().toDate(),
+                    ),
+                    expectedEntity,
+                    instanceId,
+                ),
+            });
+
+            orchestrator(mockContext);
+
+            expect(mockContext.doneValue).to.be.deep.equal(
+                new OrchestratorState({
+                    isDone: false,
+                    output: undefined,
+                    actions:
+                    [
+                        [
+                            new CallEntityAction(
+                                expectedEntity,
+                                "set",
+                                "testString"),
+                        ],
+                    ],
+                }),
+            );
+        });
+
+        it("handles a completed entity request", async () => {
+            const orchestrator = TestOrchestrations.CallEntitySet;
+            const instanceId = uuidv1();
+            const expectedEntity = new EntityId("StringStore2", "12345");
+            const mockContext = new MockContext({
+                context: new DurableOrchestrationBindingInfo(
+                    TestHistories.GetCallEntitySet(
+                        moment.utc().toDate(),
+                        expectedEntity,
+                        ),
+                    expectedEntity,
+                    instanceId,
+                    true,
+                ),
+            });
+
+            orchestrator(mockContext);
+
+            expect(mockContext.doneValue).to.be.deep.equal(
+                new OrchestratorState({
+                    isDone: true,
+                    actions:
+                    [
+                        [
+                            new CallEntityAction(
+                                expectedEntity,
+                                "set",
+                                "testString"),
+                        ],
+                    ],
+                    output: "OK",
+                }),
+            );
+        });
+    });
+
     describe("callSubOrchestrator()", () => {
         it("schedules a suborchestrator function", async () => {
             const orchestrator = TestOrchestrations.SayHelloWithSubOrchestrator;
@@ -1179,6 +1254,68 @@ describe("Orchestrator", () => {
         });
     });
 
+    describe.skip("isLocked()", () => {
+        it("returns correct state when no locks are owned", () => {
+            const orchestrator = TestOrchestrations.CheckForLocksNone;
+            const mockContext = new MockContext({
+                context: new DurableOrchestrationBindingInfo(
+                    TestHistories.GetOrchestratorStart("CheckForLocksNone", moment.utc().toDate()),
+                ),
+            });
+
+            const expectedLockState = new LockState(false, undefined);
+
+            orchestrator(mockContext);
+
+            expect(mockContext.doneValue).to.deep.equal(
+                new OrchestratorState({
+                    isDone: true,
+                    actions: [],
+                    output: expectedLockState,
+                }),
+            );
+        });
+
+        it.skip("returns correct state when locks are owned", () => {
+            // TODO: fill in
+        });
+    });
+
+    describe("newGuid()", () => {
+        it("generates consistent GUIDs", () => {
+            const orchestrator = TestOrchestrations.GuidGenerator;
+            const currentUtcDateTime = moment.utc().toDate();
+            const instanceId = uuidv1();
+
+            const mockContext1 = new MockContext({
+                context: new DurableOrchestrationBindingInfo(
+                    TestHistories.GetOrchestratorStart(
+                        "GuidGenerator",
+                        currentUtcDateTime,
+                    ),
+                    undefined,
+                    instanceId,
+                ),
+            });
+            const mockContext2 = new MockContext({
+                context: new DurableOrchestrationBindingInfo(
+                    TestHistories.GetOrchestratorStart(
+                        "GuidGenerator",
+                        currentUtcDateTime,
+                    ),
+                    undefined,
+                    instanceId,
+                ),
+            });
+
+            orchestrator(mockContext1);
+            orchestrator(mockContext2);
+
+            expect(mockContext1.doneValue.isDone).to.equal(true);
+            expect(mockContext1.doneValue).to.deep.equal(mockContext2.doneValue);
+        });
+    });
+
     describe("setCustomStatus()", () => {
         it("sets a custom status", async () => {
             const orchestrator = TestOrchestrations.SayHelloWithCustomStatus;
@@ -1232,7 +1369,7 @@ describe("Orchestrator", () => {
                     output: undefined,
                     actions:
                     [
-                        [ new WaitForExternalEventAction("start") ],
+                        [ new WaitForExternalEventAction("start", ExternalEventType.ExternalEvent) ],
                     ],
                 }),
             );
@@ -1260,7 +1397,7 @@ describe("Orchestrator", () => {
                     output: undefined,
                     actions:
                     [
-                        [ new WaitForExternalEventAction("start") ],
+                        [ new WaitForExternalEventAction("start", ExternalEventType.ExternalEvent) ],
                         [ new CallActivityAction("Hello", name) ],
                     ],
                 }),
@@ -1289,7 +1426,7 @@ describe("Orchestrator", () => {
                     output: undefined,
                     actions:
                     [
-                        [ new WaitForExternalEventAction("start") ],
+                        [ new WaitForExternalEventAction("start", ExternalEventType.ExternalEvent) ],
                     ],
                 }),
             );
diff --git a/test/testobjects/TestOrchestrations.ts b/test/testobjects/TestOrchestrations.ts
index 7ccff18..1aa7661 100644
--- a/test/testobjects/TestOrchestrations.ts
+++ b/test/testobjects/TestOrchestrations.ts
@@ -17,6 +17,17 @@ export class TestOrchestrations {
        return returnValue;
     });
 
+    public static CallEntitySet: any = df.orchestrator(function*(context: any) {
+        const entity = context.df.getInput() as df.EntityId;
+        yield context.df.callEntity(entity, "set", "testString");
+
+        return "OK";
+    });
+
+    public static CheckForLocksNone: any = df.orchestrator(function*(context: any) {
+        return context.df.isLocked();
+    });
+
     public static ContinueAsNewCounter: any = df.orchestrator(function*(context: any) {
         const currentValueObject = context.df.getInput();
         let currentValue = currentValueObject
@@ -46,6 +57,22 @@ export class TestOrchestrations {
         return totalBytes;
     });
 
+    public static GetAndReleaseLock: any = df.orchestrator(function*(context: any) {
+        const entities = context.df.getInput();
+
+        const lock = yield context.df.lock(entities);
+
+        return "ok";
+    });
+
+    public static GetLockBadly: any = df.orchestrator(function*(context: any) {
+        const locksToGet = context.df.getInput();
+
+        const lock = yield context.df.lock(locksToGet);
+
+        return "ok";
+    });
+
     public static GuidGenerator: any = df.orchestrator(function*(context: any) {
         const outputs: string[] = [];
 
diff --git a/test/testobjects/testconstants.ts b/test/testobjects/testconstants.ts
index 088a828..2d0d7a2 100644
--- a/test/testobjects/testconstants.ts
+++ b/test/testobjects/testconstants.ts
@@ -5,22 +5,28 @@ export class TestConstants {
 
     public static readonly testCode: string = "code=base64string";
 
-    public static readonly eventNamePlaceholder = "{eventName}";
+    public static readonly entityNamePlaceholder: string = "{entityName}";
+    public static readonly entityKeyPlaceholder: string = "{entityKey?}";
+    public static readonly eventNamePlaceholder: string = "{eventName}";
     public static readonly functionPlaceholder: string = "{functionName}";
     public static readonly idPlaceholder: string = "[/{instanceId}]";
-    public static readonly intervalPlaceholder = "{intervalInSeconds}";
-    public static readonly reasonPlaceholder = "{text}";
+    public static readonly intervalPlaceholder: string = "{intervalInSeconds}";
+    public static readonly operationPlaceholder: string = "{operation}";
+    public static readonly reasonPlaceholder: string = "{text}";
     public static readonly timeoutPlaceholder: string = "{timeoutInSeconds}";
 
-    public static readonly uriSuffix = `taskHub=${TestConstants.taskHubPlaceholder}&connection=${TestConstants.connectionPlaceholder}&${TestConstants.testCode}`; // tslint:disable-line max-line-length
+    public static readonly uriSuffix: string = `taskHub=${TestConstants.taskHubPlaceholder}&connection=${TestConstants.connectionPlaceholder}&${TestConstants.testCode}`; // tslint:disable-line max-line-length
     public static readonly webhookPath: string = "/runtime/webhooks/durabletask/";
 
     public static readonly statusQueryGetUriTemplate: string = `${TestConstants.hostPlaceholder}${TestConstants.webhookPath}instances/${TestConstants.idPlaceholder}?${TestConstants.uriSuffix}`; // tslint:disable-line max-line-length
-    public static readonly sendEventPostUriTemplate: string = `${TestConstants.hostPlaceholder}${TestConstants.webhookPath}instances/${TestConstants.idPlaceholder}/raiseEvent/{eventName}?${TestConstants.uriSuffix}`; // tslint:disable-line max-line-length
+    public static readonly sendEventPostUriTemplate: string = `${TestConstants.hostPlaceholder}${TestConstants.webhookPath}instances/${TestConstants.idPlaceholder}/raiseEvent/${TestConstants.eventNamePlaceholder}?${TestConstants.uriSuffix}`; // tslint:disable-line max-line-length
     public static readonly terminatePostUriTemplate: string = `${TestConstants.hostPlaceholder}${TestConstants.webhookPath}instances/${TestConstants.idPlaceholder}/terminate?reason=${TestConstants.reasonPlaceholder}&${TestConstants.uriSuffix}`; // tslint:disable-line max-line-length
     public static readonly rewindPostUriTemplate: string = `${TestConstants.hostPlaceholder}${TestConstants.webhookPath}instances/${TestConstants.idPlaceholder}/rewind?reason=${TestConstants.reasonPlaceholder}&${TestConstants.uriSuffix}`; // tslint:disable-line max-line-length
     public static readonly purgeDeleteUriTemplate: string = `${TestConstants.hostPlaceholder}${TestConstants.webhookPath}instances/${TestConstants.idPlaceholder}?${TestConstants.uriSuffix}`; // tslint:disable-line max-line-length
 
-    public static readonly createPostUriTemplate = `${TestConstants.hostPlaceholder}${TestConstants.webhookPath}orchestrators/${TestConstants.functionPlaceholder}${TestConstants.idPlaceholder}?${TestConstants.testCode}`; // tslint:disable-line max-line-length
-    public static readonly waitOnPostUriTemplate = `${TestConstants.hostPlaceholder}${TestConstants.webhookPath}orchestrators/${TestConstants.functionPlaceholder}${TestConstants.idPlaceholder}?timeout=${TestConstants.timeoutPlaceholder}&pollingInterval=${TestConstants.intervalPlaceholder}&${TestConstants.testCode}`; // tslint:disable-line max-line-length
+    public static readonly createPostUriTemplate: string = `${TestConstants.hostPlaceholder}${TestConstants.webhookPath}orchestrators/${TestConstants.functionPlaceholder}${TestConstants.idPlaceholder}?${TestConstants.testCode}`; // tslint:disable-line max-line-length
+    public static readonly waitOnPostUriTemplate: string = `${TestConstants.hostPlaceholder}${TestConstants.webhookPath}orchestrators/${TestConstants.functionPlaceholder}${TestConstants.idPlaceholder}?timeout=${TestConstants.timeoutPlaceholder}&pollingInterval=${TestConstants.intervalPlaceholder}&${TestConstants.testCode}`; // tslint:disable-line max-line-length
+
+    public static readonly entityGetUriTemplate: string = `${TestConstants.hostPlaceholder}${TestConstants.webhookPath}entities/${TestConstants.entityNamePlaceholder}/${TestConstants.entityKeyPlaceholder}&${TestConstants.uriSuffix}`; // tslint:disable-line max-line-length
+    public static readonly entityPostUriTemplate: string = `${TestConstants.hostPlaceholder}${TestConstants.webhookPath}entities/${TestConstants.entityNamePlaceholder}/${TestConstants.entityKeyPlaceholder}&op=${TestConstants.operationPlaceholder}&${TestConstants.uriSuffix}`; // tslint:disable-line max-line-length
 }
diff --git a/test/testobjects/testentities.ts b/test/testobjects/testentities.ts
new file mode 100644
index 0000000..3444074
--- /dev/null
+++ b/test/testobjects/testentities.ts
@@ -0,0 +1,41 @@
+import * as df from "../../src";
+import { IEntityFunctionContext } from "../../src/classes";
+
+export class TestEntities {
+    public static StringStore: any = df.entity((context: IEntityFunctionContext): void => {
+        switch (context.df.operationName) {
+            case "set":
+                context.df.setState(context.df.getInput());
+                break;
+            case "get":
+                context.df.return(context.df.getState());
+                break;
+            default:
+                throw new Error("No such operation exists");
+        }
+    });
+
+    public static Counter: any = df.entity((context: IEntityFunctionContext): void => {
+        const input: number = context.df.getInput() as number;
+        const state: number = context.df.getState() as number;
+        switch (context.df.operationName) {
+            case "increment":
+                context.df.setState(input + 1);
+                break;
+            case "add":
+                context.df.setState(state + input);
+                break;
+            case "get":
+                context.df.return(state);
+                break;
+            case "set":
+                context.df.setState(input);
+                break;
+            case "delete":
+                context.df.destructOnExit();
+                break;
+            default:
+                throw Error("Invalid operation");
+        }
+    });
+}
diff --git a/test/testobjects/testentitybatches.ts b/test/testobjects/testentitybatches.ts
new file mode 100644
index 0000000..4f74779
--- /dev/null
+++ b/test/testobjects/testentitybatches.ts
@@ -0,0 +1,135 @@
+import * as df from "../../src";
+import { DurableEntityBindingInfo, EntityState, OperationResult, RequestMessage } from "../../src/classes";
+import { CounterOperation, EntityInputsAndOutputs, StringStoreOperation  } from "../testobjects/testentityoperations";
+
+export class TestEntityBatches {
+
+    public static GetStringStoreBatch(operations: StringStoreOperation[], existingState: string): EntityInputsAndOutputs {
+        const id = new df.EntityId("stringstore", "stringstorekey");
+
+        const entityExists = existingState != null;
+        const output = new EntityState([], []);
+        if (entityExists)  {
+            output.entityState = JSON.stringify(existingState);
+            output.entityExists = entityExists;
+        }
+
+        const batch: RequestMessage[] = [];
+        let operationCount = 0;
+        for (const operation of operations) {
+            batch[operationCount] = new RequestMessage();
+            switch (operation.kind) {
+                case "get":
+                    // Handle inputs
+                    batch[operationCount].id = JSON.stringify(operationCount);
+                    batch[operationCount].name = "get";
+                    batch[operationCount].signal = false;
+
+                    // Handle outputs
+                    output.results[operationCount] = new OperationResult(false, -1, output.entityState);
+                    break;
+                case "set":
+                    // Handle inputs
+                    const value = JSON.stringify(operation.value);
+                    batch[operationCount].id = JSON.stringify(operationCount);
+                    batch[operationCount].name = "set";
+                    batch[operationCount].signal = false;
+                    batch[operationCount].input = value;
+
+                    // Handle outputs
+                    output.results[operationCount] = new OperationResult(false, -1);
+                    output.entityExists = true;
+                    output.entityState = value;
+                    break;
+            }
+            operationCount++;
+        }
+        return  {
+            input: new DurableEntityBindingInfo(id, entityExists, JSON.stringify(existingState), batch),
+            output,
+        };
+    }
+
+    public static GetCounterBatch(operations: CounterOperation[], existingState: number | undefined): EntityInputsAndOutputs {
+        const id = new df.EntityId("stringstore", "stringstorekey");
+        let currentState: number;
+        if (existingState) {
+            currentState = Number(existingState);
+        }
+
+        const entityExists: boolean = !existingState;
+        const output = new EntityState([], []);
+        output.entityExists = entityExists;
+        const batch: RequestMessage[] = [];
+        let operationCount = 0;
+        for (const operation of operations) {
+            batch[operationCount] = new RequestMessage();
+            switch (operation.kind) {
+                case "get":
+                    // Handle inputs
+                    batch[operationCount].id = operationCount.toString();
+                    batch[operationCount].name = "get";
+                    batch[operationCount].signal = false;
+
+                    // Handle outputs
+                    output.results[operationCount] = new OperationResult(false, -1, JSON.stringify(currentState));
+                    break;
+                case "set":
+                    // Handle inputs
+                    batch[operationCount].id = operationCount.toString();
+                    batch[operationCount].name = "set";
+                    batch[operationCount].signal = false;
+                    batch[operationCount].input = operation.value.toString();
+
+                    // Handle outputs
+                    currentState = operation.value;
+                    output.results[operationCount] = new OperationResult(false, -1);
+                    output.entityExists = true;
+                    output.entityState = currentState.toString();
+                    break;
+                case "increment":
+                    batch[operationCount].id = operationCount.toString();
+                    batch[operationCount].name = "increment";
+                    batch[operationCount].signal = false;
+
+                    if (currentState != null) {
+                        currentState = currentState + 1;
+                        output.results[operationCount] = new OperationResult(false, -1);
+                        output.entityExists = true;
+                        output.entityState = currentState.toString();
+                    } else {
+                        output.results[operationCount] = new OperationResult(true, -1, "dummy error message");
+                    }
+                    break;
+                case "add":
+                    batch[operationCount].id = operationCount.toString();
+                    batch[operationCount].name = "add";
+                    batch[operationCount].signal = false;
+                    batch[operationCount].input = operation.value.toString();
+
+                    if (currentState != null) {
+                        currentState = currentState + operation.value;
+                        output.results[operationCount] = new OperationResult(false, -1);
+                        output.entityExists = true;
+                        output.entityState = currentState.toString();
+                    } else {
+                        output.results[operationCount] = new OperationResult(true, -1, "dummy error message");
+                    }
+                    break;
+                case "delete":
+                    batch[operationCount].id = operationCount.toString();
+                    batch[operationCount].name = "add";
+                    batch[operationCount].signal = false;
+
+                    output.entityExists = false;
+                    output.results[operationCount] = new OperationResult(false, -1);
+                    break;
+            }
+            operationCount++;
+        }
+        return  {
+            input: new DurableEntityBindingInfo(id, entityExists, existingState != null ? existingState.toString() : undefined, batch),
+            output,
+        };
+    }
+}
diff --git a/test/testobjects/testentityoperations.ts b/test/testobjects/testentityoperations.ts
new file mode 100644
index 0000000..684b312
--- /dev/null
+++ b/test/testobjects/testentityoperations.ts
@@ -0,0 +1,32 @@
+import { DurableEntityBindingInfo, EntityState } from "../../src/classes";
+
+export interface EntityInputsAndOutputs {
+    input: DurableEntityBindingInfo;
+    output: EntityState;
+}
+
+export interface Get {
+    kind: "get";
+}
+
+export interface Set<T> {
+    kind: "set";
+    value: T;
+}
+
+export interface Increment {
+    kind: "increment";
+}
+
+export interface Add<T> {
+    kind: "add";
+    value: T;
+}
+
+export interface Delete {
+    kind: "delete";
+}
+
+export type StringStoreOperation = Get | Set<string>;
+
+export type CounterOperation = Get | Set<number> | Increment | Add<number> | Delete;
diff --git a/test/testobjects/testhistories.ts b/test/testobjects/testhistories.ts
index 127e8ab..92b2504 100644
--- a/test/testobjects/testhistories.ts
+++ b/test/testobjects/testhistories.ts
@@ -1,6 +1,8 @@
 import * as moment from "moment";
-import { DurableHttpRequest, DurableHttpResponse, EventRaisedEvent, ExecutionStartedEvent, HistoryEvent, OrchestratorCompletedEvent,
-    OrchestratorStartedEvent, SubOrchestrationInstanceCompletedEvent, SubOrchestrationInstanceCreatedEvent,
+import * as uuidv1 from "uuid/v1";
+import { DurableHttpRequest, DurableHttpResponse, EntityId, EventRaisedEvent, EventSentEvent,
+    ExecutionStartedEvent, HistoryEvent, OrchestratorCompletedEvent, OrchestratorStartedEvent,
+    SubOrchestrationInstanceCompletedEvent, SubOrchestrationInstanceCreatedEvent,
     SubOrchestrationInstanceFailedEvent, TaskCompletedEvent, TaskFailedEvent, TaskScheduledEvent,
     TimerCreatedEvent, TimerFiredEvent } from "../../src/classes";
 
@@ -69,6 +71,71 @@ export class TestHistories {
         ];
     }
 
+    public static GetCallEntitySet(firstTimestamp: Date, entityId: EntityId) {
+        const firstMoment = moment(firstTimestamp);
+        const orchestratorId = uuidv1();
+        const messageId = uuidv1();
+
+        return [
+            new OrchestratorStartedEvent(
+                {
+                    eventId: -1,
+                    timestamp: firstTimestamp,
+                    isPlayed: false,
+                },
+            ),
+            new ExecutionStartedEvent(
+                {
+                    eventId: -1,
+                    timestamp: firstMoment.add(1, "s").toDate(),
+                    isPlayed: true,
+                    name: orchestratorId,
+                    input: JSON.stringify(entityId),
+                },
+            ),
+            new EventSentEvent(
+                {
+                    eventId: 0,
+                    timestamp: firstMoment.add(1, "s").toDate(),
+                    isPlayed: true,
+                    name: "op",
+                    input: JSON.stringify({
+                        id: messageId,
+                        op: "set",
+                        parent: orchestratorId,
+                        timestamp: firstMoment.add(1, "s").toDate(),
+                    }),
+                    instanceId: EntityId.getSchedulerIdFromEntityId(entityId),
+                },
+            ),
+            new OrchestratorCompletedEvent(
+                {
+                    eventId: -1,
+                    timestamp: firstMoment.add(1, "s").toDate(),
+                    isPlayed: true,
+                },
+            ),
+            new OrchestratorStartedEvent(
+                {
+                    eventId: -1,
+                    timestamp: firstMoment.add(2, "s").toDate(),
+                    isPlayed: false,
+                },
+            ),
+            new EventRaisedEvent(
+                {
+                    eventId: -1,
+                    timestamp: firstMoment.add(2, "s").toDate(),
+                    isPlayed: false,
+                    name: messageId,
+                    input: JSON.stringify({
+                        result: null,
+                    }),
+                },
+            ),
+        ];
+    }
+
     public static GetFanOutFanInDiskUsageComplete(firstTimestamp: Date, files: string[]): HistoryEvent[] {
         const firstMoment = moment(firstTimestamp);
 
diff --git a/test/testobjects/testutils.ts b/test/testobjects/testutils.ts
index cbc4e31..71386c7 100644
--- a/test/testobjects/testutils.ts
+++ b/test/testobjects/testutils.ts
@@ -1,8 +1,23 @@
-import { HttpCreationPayload, HttpManagementPayload, OrchestrationClientInputData } from "../../src/classes";
+import { HttpCreationPayload, HttpManagementPayload,
+    OrchestrationClientInputData } from "../../src/classes";
 import { TestConstants } from "./testconstants";
 
 export class TestUtils {
     public static createOrchestrationClientInputData(
+        id: string,
+        host: string,
+        taskHub: string = TestConstants.taskHubPlaceholder,
+        connection: string = TestConstants.connectionPlaceholder) {
+        return new OrchestrationClientInputData(
+            taskHub,
+            TestUtils.createHttpCreationPayload(host, taskHub, connection),
+            TestUtils.createHttpManagementPayload(id, host, taskHub, connection),
+            `${host}${TestConstants.webhookPath.replace(/\/$/, "")}`, // Returns baseURL with remaining whitespace trimmed.
+            TestConstants.testCode,
+        );
+    }
+
+    public static createV1OrchestrationClientInputData(
         id: string,
         host: string,
         taskHub: string = TestConstants.taskHubPlaceholder,
diff --git a/test/unit/entityid-spec.ts b/test/unit/entityid-spec.ts
new file mode 100644
index 0000000..da97645
--- /dev/null
+++ b/test/unit/entityid-spec.ts
@@ -0,0 +1,39 @@
+import { expect } from "chai";
+import "mocha";
+import { EntityId } from "../../src/classes";
+
+describe("EntityId", () => {
+    const defaultEntityName = "entity";
+    const defaultEntityKey = "123";
+    const defaultEntityId = new EntityId(defaultEntityName, defaultEntityKey);
+
+    it("returns correct toString", () => {
+        const expectedToString = `@${defaultEntityName}@${defaultEntityKey}`;
+
+        const result = defaultEntityId.toString();
+
+        expect(result).to.equal(expectedToString);
+    });
+
+    describe("getEntityIdFromSchedulerId", () => {
+        it ("constructs correct entity ID from scheduler ID", () => {
+            const schedulerId = `@${defaultEntityName}@${defaultEntityKey}`;
+
+            const expectedEntityId = new EntityId(defaultEntityName, defaultEntityKey);
+
+            const result = EntityId.getEntityIdFromSchedulerId(schedulerId);
+
+            expect(result).to.deep.equal(expectedEntityId);
+        });
+    });
+
+    describe("getSchedulerIdFromEntityId", () => {
+        it ("constructs correct scheduler ID from entity ID", () => {
+            const expectedSchedulerId = `@${defaultEntityName}@${defaultEntityKey}`;
+
+            const result = EntityId.getSchedulerIdFromEntityId(defaultEntityId);
+
+            expect(result).to.equal(expectedSchedulerId);
+        });
+    });
+});
diff --git a/test/unit/getclient-spec.ts b/test/unit/getclient-spec.ts
index da59b79..59a0ad2 100644
--- a/test/unit/getclient-spec.ts
+++ b/test/unit/getclient-spec.ts
@@ -21,12 +21,24 @@ describe("getClient()", () => {
         defaultTaskHub,
         defaultConnection);
 
+    const v1ClientInputData = TestUtils.createV1OrchestrationClientInputData(
+        TestConstants.idPlaceholder,
+        Constants.DefaultLocalOrigin,
+        defaultTaskHub,
+        defaultConnection);
+
     const defaultContext = {
         bindings: {
             starter: defaultClientInputData,
         },
     };
 
+    const v1Context = {
+        bindings: {
+            starter: v1ClientInputData,
+        },
+    };
+
     it("throws if context.bindings is undefined", async () => {
         expect(() => {
             getClient({});
@@ -49,6 +61,11 @@ describe("getClient()", () => {
         expect(client).to.be.instanceOf(DurableOrchestrationClient);
     });
 
+    it("returns DurableOrchestrationClient if called with V1 context", async () => {
+        const client = getClient(v1Context);
+        expect(client).to.be.instanceOf(DurableOrchestrationClient);
+    });
+
     describe("Azure/azure-functions-durable-js#28 patch", () => {
         beforeEach(() => {
             this.WEBSITE_HOSTNAME = process.env.WEBSITE_HOSTNAME;
diff --git a/test/unit/orchestrationclient-spec.ts b/test/unit/orchestrationclient-spec.ts
index c9a8477..5662be2 100644
--- a/test/unit/orchestrationclient-spec.ts
+++ b/test/unit/orchestrationclient-spec.ts
@@ -1,12 +1,17 @@
+// tslint:disable:member-access
+
 import { HttpRequest } from "@azure/functions";
 import chai = require("chai");
 import chaiAsPromised = require("chai-as-promised");
+import { isEqual } from "lodash";
 import "mocha";
 import nock = require("nock");
 import url = require("url");
 import uuidv1 = require("uuid/v1");
 import { Constants, DurableOrchestrationClient, DurableOrchestrationStatus,
-    HttpManagementPayload, OrchestrationRuntimeStatus, PurgeHistoryResult } from "../../src/classes";
+    EntityId, EntityStateResponse, HttpManagementPayload,
+    OrchestrationRuntimeStatus, PurgeHistoryResult,
+} from "../../src/classes";
 import { TestConstants } from "../testobjects/testconstants";
 import { TestUtils } from "../testobjects/testutils";
 
@@ -30,6 +35,11 @@ describe("Orchestration Client", () => {
     const defaultConnection = "Storage";
     const defaultInstanceId = uuidv1();
 
+    const defaultEntityName = "testEntity";
+    const defaultEntityKey = "123";
+    const defaultEntityId = new EntityId(defaultEntityName, defaultEntityKey);
+    const defaultEntityOp = "get";
+
     const defaultClientInputData = TestUtils.createOrchestrationClientInputData(
         TestConstants.idPlaceholder,
         Constants.DefaultLocalOrigin,
@@ -176,9 +186,7 @@ describe("Orchestration Client", () => {
 
             const scope = nock(expectedWebhookUrl.origin)
                 .get(expectedWebhookUrl.pathname)
-                .query(() => {
-                    return getQueryObjectFromSearchParams(expectedWebhookUrl);
-                })
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
                 .reply(202, expectedStatus);
 
             const result = await client.getStatus(defaultInstanceId);
@@ -205,10 +213,7 @@ describe("Orchestration Client", () => {
 
             const scope = nock(expectedWebhookUrl.origin)
                 .get(expectedWebhookUrl.pathname)
-                .query((actualQueryObj: { [ key: string]: string }) => {
-                    const queryObj = getQueryObjectFromSearchParams(expectedWebhookUrl);
-                    return haveSameQuery(queryObj, actualQueryObj);
-                })
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
                 .reply(202, expectedStatus);
 
             const result = await client.getStatus(defaultInstanceId, true);
@@ -235,10 +240,7 @@ describe("Orchestration Client", () => {
 
             const scope = nock(expectedWebhookUrl.origin)
                 .get(expectedWebhookUrl.pathname)
-                .query((actualQueryObj: { [ key: string]: string }) => {
-                    const queryObj = getQueryObjectFromSearchParams(expectedWebhookUrl);
-                    return haveSameQuery(queryObj, actualQueryObj);
-                })
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
                 .reply(202, expectedStatus);
 
             const result = await client.getStatus(defaultInstanceId, false, true);
@@ -265,10 +267,7 @@ describe("Orchestration Client", () => {
 
             const scope = nock(expectedWebhookUrl.origin)
                 .get(expectedWebhookUrl.pathname)
-                .query((actualQueryObj: { [ key: string]: string }) => {
-                    const queryObj = getQueryObjectFromSearchParams(expectedWebhookUrl);
-                    return haveSameQuery(queryObj, actualQueryObj);
-                })
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
                 .reply(202, expectedStatus);
 
             const result = await client.getStatus(defaultInstanceId, false, false, false);
@@ -308,9 +307,7 @@ describe("Orchestration Client", () => {
             ];
             const scope = nock(expectedWebhookUrl.origin)
                 .get(expectedWebhookUrl.pathname)
-                .query(() => {
-                    return getQueryObjectFromSearchParams(expectedWebhookUrl);
-                })
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
                 .reply(202, expectedStatuses);
 
             const result = await client.getStatusAll();
@@ -355,9 +352,7 @@ describe("Orchestration Client", () => {
             ];
             const scope = nock(expectedWebhookUrl.origin)
                 .get(expectedWebhookUrl.pathname)
-                .query(() => {
-                    return getQueryObjectFromSearchParams(expectedWebhookUrl);
-                })
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
                 .reply(202, expectedStatuses);
 
             const result = await client.getStatusBy(createdTimeFrom, createdTimeTo, runtimeStatuses);
@@ -377,9 +372,7 @@ describe("Orchestration Client", () => {
             const expectedStatuses: DurableOrchestrationStatus[] = [ ];
             const scope = nock(expectedWebhookUrl.origin)
                 .get(expectedWebhookUrl.pathname)
-                .query(() => {
-                    return getQueryObjectFromSearchParams(expectedWebhookUrl);
-                })
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
                 .reply(202, expectedStatuses);
 
             const result = await client.getStatusBy(undefined, undefined, runtimeStatuses);
@@ -525,9 +518,7 @@ describe("Orchestration Client", () => {
 
             const scope = nock(expectedWebhookUrl.origin, requiredPostHeaders)
                 .post(expectedWebhookUrl.pathname, JSON.stringify(defaultTestData))
-                .query(() => {
-                    return getQueryObjectFromSearchParams(expectedWebhookUrl);
-                })
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
                 .reply(202);
 
             const result = await client.raiseEvent(defaultInstanceId, defaultTestEvent, defaultTestData);
@@ -545,9 +536,7 @@ describe("Orchestration Client", () => {
                 .replace(defaultTaskHub, testTaskHub));
             const scope = nock(expectedWebhookUrl.origin, requiredPostHeaders)
                 .post(expectedWebhookUrl.pathname, JSON.stringify(defaultTestData))
-                .query(() => {
-                    return getQueryObjectFromSearchParams(expectedWebhookUrl);
-                })
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
                 .reply(202);
 
             const result = await client.raiseEvent(defaultInstanceId, defaultTestEvent, defaultTestData, testTaskHub);
@@ -565,9 +554,7 @@ describe("Orchestration Client", () => {
                 .replace(defaultConnection, testConnection));
             const scope = nock(expectedWebhookUrl.origin, requiredPostHeaders)
                 .post(expectedWebhookUrl.pathname, JSON.stringify(defaultTestData))
-                .query(() => {
-                    return getQueryObjectFromSearchParams(expectedWebhookUrl);
-                })
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
                 .reply(202);
 
             const result = await client.raiseEvent(
@@ -589,9 +576,7 @@ describe("Orchestration Client", () => {
                 .replace(TestConstants.eventNamePlaceholder, defaultTestEvent));
             const scope = nock(expectedWebhookUrl.origin, requiredPostHeaders)
                 .post(expectedWebhookUrl.pathname, JSON.stringify(defaultTestData))
-                .query(() => {
-                    return getQueryObjectFromSearchParams(expectedWebhookUrl);
-                })
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
                 .reply(404, undefined);
 
             await expect (client.raiseEvent(id, defaultTestEvent, defaultTestData))
@@ -600,6 +585,89 @@ describe("Orchestration Client", () => {
         });
     });
 
+    describe("readEntityState", () => {
+        beforeEach(async () => {    // TODO: move these to the top-level
+            nock.cleanAll();
+        });
+
+        afterEach(async () => {
+            nock.cleanAll();
+        });
+
+        it("calls expected webhook", async () => {
+            const client = new DurableOrchestrationClient(defaultClientInputData);
+
+            const expectedEntityStateResponse: TestEntityState = {
+                count: 30,
+                isTrue: true,
+                names: [ "Bob", "Nancy", "Geraldine" ],
+            };
+            const expectedStateResponse = new EntityStateResponse<TestEntityState>(
+                true,
+                expectedEntityStateResponse,
+            );
+
+            const expectedWebhookUrl = new url.URL(`${defaultClientInputData.baseUrl}/entities/${defaultEntityName}/${defaultEntityKey}?${TestConstants.testCode}`);
+
+            const scope = nock(expectedWebhookUrl.origin)
+                .get(expectedWebhookUrl.pathname)
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
+                .reply(200, expectedEntityStateResponse);
+
+            const result = await client.readEntityState<TestEntityState>(defaultEntityId);
+            expect(scope.isDone()).to.be.equal(true);
+            expect(result).to.be.deep.equal(expectedStateResponse);
+        });
+
+        it("calls expected webhook with specific task hub and connection", async () => {
+            const client = new DurableOrchestrationClient(defaultClientInputData);
+
+            const testTaskHub = "EntityHub";
+            const testConn = "EntityConnStr";
+
+            const expectedEntityStateResponse: TestEntityState = {
+                count: 30,
+                isTrue: true,
+                names: [ "Bob", "Nancy", "Geraldine" ],
+            };
+            const expectedStateResponse = new EntityStateResponse<TestEntityState>(
+                true,
+                expectedEntityStateResponse,
+            );
+
+            const expectedWebhookUrl = new url.URL(`${defaultClientInputData.baseUrl}/entities/${defaultEntityName}/${defaultEntityKey}?taskHub=${testTaskHub}&connection=${testConn}&${TestConstants.testCode}`);
+
+            const scope = nock(expectedWebhookUrl.origin)
+                .get(expectedWebhookUrl.pathname)
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
+                .reply(200, expectedEntityStateResponse);
+
+            const result = await client.readEntityState<TestEntityState>(defaultEntityId, testTaskHub, testConn);
+            expect(scope.isDone()).to.be.equal(true);
+            expect(result).to.be.deep.equal(expectedStateResponse);
+        });
+
+        it("returns default EntityStateResponse when if entity does not exist", async () => {
+            const client = new DurableOrchestrationClient(defaultClientInputData);
+
+            const expectedResponse: EntityStateResponse<unknown> = {
+                entityExists: false,
+                entityState: undefined,
+            };
+
+            const expectedWebhookUrl = new url.URL(`${defaultClientInputData.baseUrl}/entities/${defaultEntityName}/${defaultEntityKey}?${TestConstants.testCode}`);
+
+            const scope = nock(expectedWebhookUrl.origin)
+                .get(expectedWebhookUrl.pathname)
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
+                .reply(404);
+
+            const result = await client.readEntityState<TestEntityState>(defaultEntityId);
+            expect(scope.isDone()).to.be.equal(true);
+            expect(result).to.be.be.deep.equal(expectedResponse);
+        });
+    });
+
     describe("rewind()", () => {
         beforeEach(async () => {
             nock.cleanAll();
@@ -618,9 +686,7 @@ describe("Orchestration Client", () => {
                 .replace(TestConstants.reasonPlaceholder, testReason));
             const scope = nock(expectedWebhookUrl.origin, requiredPostHeaders)
                 .post(expectedWebhookUrl.pathname)
-                .query(() => {
-                    return getQueryObjectFromSearchParams(expectedWebhookUrl);
-                })
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
                 .reply(202);
 
             const result = await client.rewind(defaultInstanceId, testReason);
@@ -638,9 +704,7 @@ describe("Orchestration Client", () => {
                 .replace(TestConstants.reasonPlaceholder, testReason));
             const scope = nock(expectedWebhookUrl.origin, requiredPostHeaders)
                 .post(expectedWebhookUrl.pathname)
-                .query(() => {
-                    return getQueryObjectFromSearchParams(expectedWebhookUrl);
-                })
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
                 .reply(404);
 
             await expect(client.rewind(testId, testReason)).to.be
@@ -658,9 +722,7 @@ describe("Orchestration Client", () => {
                 .replace(TestConstants.reasonPlaceholder, testReason));
             const scope = nock(expectedWebhookUrl.origin, requiredPostHeaders)
                 .post(expectedWebhookUrl.pathname)
-                .query(() => {
-                    return getQueryObjectFromSearchParams(expectedWebhookUrl);
-                })
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
                 .reply(410);
 
             await expect(client.rewind(testId, testReason)).to.be
@@ -678,9 +740,7 @@ describe("Orchestration Client", () => {
                 .replace(TestConstants.reasonPlaceholder, testReason));
             const scope = nock(expectedWebhookUrl.origin, requiredPostHeaders)
                 .post(expectedWebhookUrl.pathname)
-                .query(() => {
-                    return getQueryObjectFromSearchParams(expectedWebhookUrl);
-                })
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
                 .reply(500);
 
             await expect(client.rewind(testId, testReason)).to.be
@@ -689,6 +749,71 @@ describe("Orchestration Client", () => {
         });
     });
 
+    describe("signalEntity()", () => {
+        beforeEach(async () => {
+            nock.cleanAll();
+        });
+
+        afterEach(async () => {
+            nock.cleanAll();
+        });
+
+        it("calls expected webhook", async () => {
+            const testSignalData = { data: "foo" };
+
+            const client = new DurableOrchestrationClient(defaultClientInputData);
+
+            const expectedWebhookUrl = new url.URL(`${defaultClientInputData.baseUrl}/entities/${defaultEntityName}/${defaultEntityKey}?op=${defaultEntityOp}&${TestConstants.testCode}`);
+
+            const scope = nock(expectedWebhookUrl.origin)
+                .post(expectedWebhookUrl.pathname, testSignalData)
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
+                .reply(202);
+
+            const result = await client.signalEntity(defaultEntityId, defaultEntityOp, testSignalData);
+            expect(scope.isDone()).to.be.equal(true);
+            expect(result).to.be.equal(undefined);
+        });
+
+        it("calls expected webhook with specific task hub and connection", async () => {
+            const testTaskHub = "EntityHub";
+            const testConn = "EntityConnStr";
+            const testSignalData = { data: "foo" };
+
+            const client = new DurableOrchestrationClient(defaultClientInputData);
+
+            const expectedWebhookUrl = new url.URL(`${defaultClientInputData.baseUrl}/entities/${defaultEntityName}/${defaultEntityKey}?op=${defaultEntityOp}&taskHub=${testTaskHub}&connection=${testConn}&${TestConstants.testCode}`);
+
+            const scope = nock(expectedWebhookUrl.origin)
+                .post(expectedWebhookUrl.pathname, testSignalData)
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
+                .reply(202);
+
+            const result = await client.signalEntity(defaultEntityId, defaultEntityOp, testSignalData, testTaskHub, testConn);
+            expect(scope.isDone()).to.be.equal(true);
+            expect(result).to.be.equal(undefined);
+        });
+
+        it("calls expected webhook with empty operation", async () => {
+            const testSignalData = { data: "foo" };
+
+            const client = new DurableOrchestrationClient(defaultClientInputData);
+
+            const expectedWebhookUrl = new url.URL(`${defaultClientInputData.baseUrl}/entities/${defaultEntityName}/${defaultEntityKey}?${TestConstants.testCode}`);
+
+            const scope = nock(expectedWebhookUrl.origin)
+                .post(expectedWebhookUrl.pathname, testSignalData)
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
+                .reply(202);
+
+            const result = await client.signalEntity(defaultEntityId, undefined, testSignalData);
+            expect(scope.isDone()).to.be.equal(true);
+            expect(result).to.be.equal(undefined);
+        });
+
+        // TODO: JS guards - throw if not string or undefined
+    });
+
     describe("startNew()", () => {
         beforeEach(async () => {
             nock.cleanAll();
@@ -705,9 +830,7 @@ describe("Orchestration Client", () => {
             const expectedWebhookUrl = createInstanceWebhookUrl(Constants.DefaultLocalOrigin, functionName);
             const scope = nock(expectedWebhookUrl.origin, requiredPostHeaders)
                 .post(expectedWebhookUrl.pathname)
-                .query(() => {
-                    return getQueryObjectFromSearchParams(expectedWebhookUrl);
-                })
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
                 .reply(202, new HttpManagementPayload(defaultInstanceId, "", "", "", "", ""));
 
             const result = await client.startNew(functionName);
@@ -727,9 +850,7 @@ describe("Orchestration Client", () => {
                 defaultInstanceId);
             const scope = nock(expectedWebhookUrl.origin, requiredPostHeaders)
                 .post(expectedWebhookUrl.pathname, JSON.stringify(testData))
-                .query(() => {
-                    return getQueryObjectFromSearchParams(expectedWebhookUrl);
-                })
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
                 .reply(202, new HttpManagementPayload(defaultInstanceId, "", "", "", "", ""));
 
             const result = await client.startNew(functionName, defaultInstanceId, testData);
@@ -745,9 +866,7 @@ describe("Orchestration Client", () => {
             const expectedWebhookUrl = createInstanceWebhookUrl(Constants.DefaultLocalOrigin, functionName);
             const scope = nock(expectedWebhookUrl.origin, requiredPostHeaders)
                 .post(expectedWebhookUrl.pathname)
-                .query(() => {
-                    return getQueryObjectFromSearchParams(expectedWebhookUrl);
-                })
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
                 .reply(500, functionBody);
 
             await expect(client.startNew(functionName)).to.be.rejectedWith(functionBody);
@@ -773,9 +892,7 @@ describe("Orchestration Client", () => {
                 .replace(TestConstants.reasonPlaceholder, testReason));
             const scope = nock(expectedWebhookUrl.origin, requiredPostHeaders)
                 .post(expectedWebhookUrl.pathname)
-                .query(() => {
-                    return getQueryObjectFromSearchParams(expectedWebhookUrl);
-                })
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
                 .reply(202);
 
             const result = await client.terminate(defaultInstanceId, testReason);
@@ -793,9 +910,7 @@ describe("Orchestration Client", () => {
                 .replace(TestConstants.reasonPlaceholder, testReason));
             const scope = nock(expectedWebhookUrl.origin, requiredPostHeaders)
                 .post(expectedWebhookUrl.pathname)
-                .query(() => {
-                    return getQueryObjectFromSearchParams(expectedWebhookUrl);
-                })
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
                 .reply(404);
 
             await expect(client.terminate(id, testReason)).to.be
@@ -813,9 +928,7 @@ describe("Orchestration Client", () => {
                 .replace(TestConstants.reasonPlaceholder, testReason));
             const scope = nock(expectedWebhookUrl.origin, requiredPostHeaders)
                 .post(expectedWebhookUrl.pathname)
-                .query(() => {
-                    return getQueryObjectFromSearchParams(expectedWebhookUrl);
-                })
+                .query((actualQueryObject: object) => urlQueryEqualsQueryObject(expectedWebhookUrl, actualQueryObject))
                 .reply(500);
 
             await expect(client.terminate(id, testReason)).to.be
@@ -1113,7 +1226,7 @@ function createInstanceWebhookUrl(
     return new url.URL(webhookUrl);
 }
 
-function getQueryObjectFromSearchParams(requestUrl: url.URL) {
+function getQueryObjectFromSearchParams(requestUrl: url.URL): { [key: string]: string } {
     const queryObject: { [key: string]: string } = { };
     requestUrl.searchParams.forEach((value, name) => {
         queryObject[name] = value;
@@ -1131,3 +1244,15 @@ function haveSameQuery(
     }
     return true;
 }
+
+function urlQueryEqualsQueryObject(requestUrl: url.URL, queryObj: object): boolean {
+    const expectedQueryObject = getQueryObjectFromSearchParams(requestUrl);
+    const retVal = isEqual(expectedQueryObject, queryObj);
+    return retVal;
+}
+
+class TestEntityState {
+    count: number;
+    isTrue: boolean;
+    names: string[];
+}