Index: frontend/node_modules/doctrine/CHANGELOG.md
===================================================================
--- frontend/node_modules/doctrine/CHANGELOG.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/doctrine/CHANGELOG.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,101 @@
+v3.0.0 - November 9, 2018
+
+* 0b5a8c7 Breaking: drop support for Node < 6 (#223) (Kai Cataldo)
+* a05e9f2 Upgrade: eslint-release@1.0.0 (#220) (Teddy Katz)
+* 36ed027 Chore: upgrade coveralls to ^3.0.1 (#213) (Teddy Katz)
+* 8667e34 Upgrade: eslint-release@^0.11.1 (#210) (Kevin Partington)
+
+v2.1.0 - January 6, 2018
+
+* 827f314 Update: support node ranges (fixes #89) (#190) (Teddy Katz)
+
+v2.0.2 - November 25, 2017
+
+* 5049ee3 Fix: Remove redundant LICENSE/README names from files (#203) (Kevin Partington)
+
+v2.0.1 - November 10, 2017
+
+* 009f33d Fix: Making sure union type stringification respects compact flag (#199) (Mitermayer Reis)
+* 19da935 Use native String.prototype.trim instead of a custom implementation. (#201) (Rouven Weßling)
+* e3a011b chore: add mocha.opts to restore custom mocha config (Jason Kurian)
+* d888200 chore: adds nyc and a newer version of mocha to accurately report coverage (Jason Kurian)
+* 6b210a8 fix: support type expression for @this tag (fixes #181) (#182) (Frédéric Junod)
+* 1c4a4c7 fix: Allow array indexes in names (#193) (Tom MacWright)
+* 9aed54d Fix incorrect behavior when arrow functions are used as default values (#189) (Gaurab Paul)
+* 9efb6ca Upgrade: Use Array.isArray instead of isarray package (#195) (medanat)
+
+v2.0.0 - November 15, 2016
+
+* 7d7c5f1 Breaking: Re-license to Apache 2 (fixes #176) (#178) (Nicholas C. Zakas)
+* 5496132 Docs: Update license copyright (Nicholas C. Zakas)
+
+v1.5.0 - October 13, 2016
+
+* e33c6bb Update: Add support for BooleanLiteralType (#173) (Erik Arvidsson)
+
+v1.4.0 - September 13, 2016
+
+* d7426e5 Update: add ability to parse optional properties in typedefs (refs #5) (#174) (ikokostya)
+
+v1.3.0 - August 22, 2016
+
+* 12c7ad9 Update: Add support for numeric and string literal types (fixes #156) (#172) (Andrew Walter)
+
+v1.2.3 - August 16, 2016
+
+* b96a884 Build: Add CI release script (Nicholas C. Zakas)
+* 8d9b3c7 Upgrade: Upgrade esutils to v2.0.2 (fixes #170) (#171) (Emeegeemee)
+
+v1.2.2 - May 19, 2016
+
+* ebe0b08 Fix: Support case insensitive tags (fixes #163) (#164) (alberto)
+* 8e6d81e Chore: Remove copyright and license from headers (Nicholas C. Zakas)
+* 79035c6 Chore: Include jQuery Foundation copyright (Nicholas C. Zakas)
+* 06910a7 Fix: Preserve whitespace in default param string values (fixes #157) (Kai Cataldo)
+
+v1.2.1 - March 29, 2016
+
+* 1f54014 Fix: allow hyphens in names (fixes #116) (Kai Cataldo)
+* bbee469 Docs: Add issue template (Nicholas C. Zakas)
+
+v1.2.0 - February 19, 2016
+
+* 18136c5 Build: Cleanup build system (Nicholas C. Zakas)
+* b082f85 Update: Add support for slash in namepaths (fixes #100) (Ryan Duffy)
+* def53a2 Docs: Fix typo in option lineNumbers (Daniel Tschinder)
+* e2cbbc5 Update: Bump isarray to v1.0.0 (Shinnosuke Watanabe)
+* ae07aa8 Fix: Allow whitespace in optional param with default value (fixes #141) (chris)
+
+v1.1.0 - January 6, 2016
+
+* Build: Switch to Makefile.js (Nicholas C. Zakas)
+* New: support name expression for @this tag (fixes #143) (Tim Schaub)
+* Build: Update ESLint settings (Nicholas C. Zakas)
+
+v1.0.0 - December 21, 2015
+
+* New: parse caption tags in examples into separate property. (fixes #131) (Tom MacWright)
+
+v0.7.2 - November 27, 2015
+
+* Fix: Line numbers for some tags (fixes #138) Fixing issue where input was not consumed via advance() but was skipped when parsing tags resulting in sometimes incorrect reported lineNumber. (TEHEK)
+* Build: Add missing linefix package (Nicholas C. Zakas)
+
+v0.7.1 - November 13, 2015
+
+* Update: Begin switch to Makefile.js (Nicholas C. Zakas)
+* Fix: permit return tag without type (fixes #136) (Tom MacWright)
+* Fix: package.json homepage field (Bogdan Chadkin)
+* Fix: Parse array default syntax. Fixes #133 (Tom MacWright)
+* Fix: Last tag always has \n in the description (fixes #87) (Burak Yigit Kaya)
+* Docs: Add changelog (Nicholas C. Zakas)
+
+v0.7.0 - September 21, 2015
+
+* Docs: Update README with new info (fixes #127) (Nicholas C. Zakas)
+* Fix: Parsing fix for param with arrays and properties (fixes #111) (Gyandeep Singh)
+* Build: Add travis build (fixes #123) (Gyandeep Singh)
+* Fix: Parsing of parameter name without a type (fixes #120) (Gyandeep Singh)
+* New: added preserveWhitespace option (Aleks Totic)
+* New: Add "files" entry to only deploy select files (Rob Loach)
+* New: Add support and tests for typedefs. Refs #5 (Tom MacWright)
Index: frontend/node_modules/doctrine/LICENSE
===================================================================
--- frontend/node_modules/doctrine/LICENSE	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/doctrine/LICENSE	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,177 @@
+
+                             Apache License
+                       Version 2.0, January 2004
+                    http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+  "License" shall mean the terms and conditions for use, reproduction,
+  and distribution as defined by Sections 1 through 9 of this document.
+
+  "Licensor" shall mean the copyright owner or entity authorized by
+  the copyright owner that is granting the License.
+
+  "Legal Entity" shall mean the union of the acting entity and all
+  other entities that control, are controlled by, or are under common
+  control with that entity. For the purposes of this definition,
+  "control" means (i) the power, direct or indirect, to cause the
+  direction or management of such entity, whether by contract or
+  otherwise, or (ii) ownership of fifty percent (50%) or more of the
+  outstanding shares, or (iii) beneficial ownership of such entity.
+
+  "You" (or "Your") shall mean an individual or Legal Entity
+  exercising permissions granted by this License.
+
+  "Source" form shall mean the preferred form for making modifications,
+  including but not limited to software source code, documentation
+  source, and configuration files.
+
+  "Object" form shall mean any form resulting from mechanical
+  transformation or translation of a Source form, including but
+  not limited to compiled object code, generated documentation,
+  and conversions to other media types.
+
+  "Work" shall mean the work of authorship, whether in Source or
+  Object form, made available under the License, as indicated by a
+  copyright notice that is included in or attached to the work
+  (an example is provided in the Appendix below).
+
+  "Derivative Works" shall mean any work, whether in Source or Object
+  form, that is based on (or derived from) the Work and for which the
+  editorial revisions, annotations, elaborations, or other modifications
+  represent, as a whole, an original work of authorship. For the purposes
+  of this License, Derivative Works shall not include works that remain
+  separable from, or merely link (or bind by name) to the interfaces of,
+  the Work and Derivative Works thereof.
+
+  "Contribution" shall mean any work of authorship, including
+  the original version of the Work and any modifications or additions
+  to that Work or Derivative Works thereof, that is intentionally
+  submitted to Licensor for inclusion in the Work by the copyright owner
+  or by an individual or Legal Entity authorized to submit on behalf of
+  the copyright owner. For the purposes of this definition, "submitted"
+  means any form of electronic, verbal, or written communication sent
+  to the Licensor or its representatives, including but not limited to
+  communication on electronic mailing lists, source code control systems,
+  and issue tracking systems that are managed by, or on behalf of, the
+  Licensor for the purpose of discussing and improving the Work, but
+  excluding communication that is conspicuously marked or otherwise
+  designated in writing by the copyright owner as "Not a Contribution."
+
+  "Contributor" shall mean Licensor and any individual or Legal Entity
+  on behalf of whom a Contribution has been received by Licensor and
+  subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+  this License, each Contributor hereby grants to You a perpetual,
+  worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+  copyright license to reproduce, prepare Derivative Works of,
+  publicly display, publicly perform, sublicense, and distribute the
+  Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+  this License, each Contributor hereby grants to You a perpetual,
+  worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+  (except as stated in this section) patent license to make, have made,
+  use, offer to sell, sell, import, and otherwise transfer the Work,
+  where such license applies only to those patent claims licensable
+  by such Contributor that are necessarily infringed by their
+  Contribution(s) alone or by combination of their Contribution(s)
+  with the Work to which such Contribution(s) was submitted. If You
+  institute patent litigation against any entity (including a
+  cross-claim or counterclaim in a lawsuit) alleging that the Work
+  or a Contribution incorporated within the Work constitutes direct
+  or contributory patent infringement, then any patent licenses
+  granted to You under this License for that Work shall terminate
+  as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+  Work or Derivative Works thereof in any medium, with or without
+  modifications, and in Source or Object form, provided that You
+  meet the following conditions:
+
+  (a) You must give any other recipients of the Work or
+      Derivative Works a copy of this License; and
+
+  (b) You must cause any modified files to carry prominent notices
+      stating that You changed the files; and
+
+  (c) You must retain, in the Source form of any Derivative Works
+      that You distribute, all copyright, patent, trademark, and
+      attribution notices from the Source form of the Work,
+      excluding those notices that do not pertain to any part of
+      the Derivative Works; and
+
+  (d) If the Work includes a "NOTICE" text file as part of its
+      distribution, then any Derivative Works that You distribute must
+      include a readable copy of the attribution notices contained
+      within such NOTICE file, excluding those notices that do not
+      pertain to any part of the Derivative Works, in at least one
+      of the following places: within a NOTICE text file distributed
+      as part of the Derivative Works; within the Source form or
+      documentation, if provided along with the Derivative Works; or,
+      within a display generated by the Derivative Works, if and
+      wherever such third-party notices normally appear. The contents
+      of the NOTICE file are for informational purposes only and
+      do not modify the License. You may add Your own attribution
+      notices within Derivative Works that You distribute, alongside
+      or as an addendum to the NOTICE text from the Work, provided
+      that such additional attribution notices cannot be construed
+      as modifying the License.
+
+  You may add Your own copyright statement to Your modifications and
+  may provide additional or different license terms and conditions
+  for use, reproduction, or distribution of Your modifications, or
+  for any such Derivative Works as a whole, provided Your use,
+  reproduction, and distribution of the Work otherwise complies with
+  the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+  any Contribution intentionally submitted for inclusion in the Work
+  by You to the Licensor shall be under the terms and conditions of
+  this License, without any additional terms or conditions.
+  Notwithstanding the above, nothing herein shall supersede or modify
+  the terms of any separate license agreement you may have executed
+  with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+  names, trademarks, service marks, or product names of the Licensor,
+  except as required for reasonable and customary use in describing the
+  origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+  agreed to in writing, Licensor provides the Work (and each
+  Contributor provides its Contributions) on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+  implied, including, without limitation, any warranties or conditions
+  of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+  PARTICULAR PURPOSE. You are solely responsible for determining the
+  appropriateness of using or redistributing the Work and assume any
+  risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+  whether in tort (including negligence), contract, or otherwise,
+  unless required by applicable law (such as deliberate and grossly
+  negligent acts) or agreed to in writing, shall any Contributor be
+  liable to You for damages, including any direct, indirect, special,
+  incidental, or consequential damages of any character arising as a
+  result of this License or out of the use or inability to use the
+  Work (including but not limited to damages for loss of goodwill,
+  work stoppage, computer failure or malfunction, or any and all
+  other commercial damages or losses), even if such Contributor
+  has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+  the Work or Derivative Works thereof, You may choose to offer,
+  and charge a fee for, acceptance of support, warranty, indemnity,
+  or other liability obligations and/or rights consistent with this
+  License. However, in accepting such obligations, You may act only
+  on Your own behalf and on Your sole responsibility, not on behalf
+  of any other Contributor, and only if You agree to indemnify,
+  defend, and hold each Contributor harmless for any liability
+  incurred by, or claims asserted against, such Contributor by reason
+  of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
Index: frontend/node_modules/doctrine/LICENSE.closure-compiler
===================================================================
--- frontend/node_modules/doctrine/LICENSE.closure-compiler	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/doctrine/LICENSE.closure-compiler	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
Index: frontend/node_modules/doctrine/LICENSE.esprima
===================================================================
--- frontend/node_modules/doctrine/LICENSE.esprima	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/doctrine/LICENSE.esprima	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,19 @@
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+  * Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Index: frontend/node_modules/doctrine/README.md
===================================================================
--- frontend/node_modules/doctrine/README.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/doctrine/README.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,165 @@
+[![NPM version][npm-image]][npm-url]
+[![build status][travis-image]][travis-url]
+[![Test coverage][coveralls-image]][coveralls-url]
+[![Downloads][downloads-image]][downloads-url]
+[![Join the chat at https://gitter.im/eslint/doctrine](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/eslint/doctrine?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+# Doctrine
+
+Doctrine is a [JSDoc](http://usejsdoc.org) parser that parses documentation comments from JavaScript (you need to pass in the comment, not a whole JavaScript file).
+
+## Installation
+
+You can install Doctrine using [npm](https://npmjs.com):
+
+```
+$ npm install doctrine --save-dev
+```
+
+Doctrine can also be used in web browsers using [Browserify](http://browserify.org).
+
+## Usage
+
+Require doctrine inside of your JavaScript:
+
+```js
+var doctrine = require("doctrine");
+```
+
+### parse()
+
+The primary method is `parse()`, which accepts two arguments: the JSDoc comment to parse and an optional options object. The available options are:
+
+* `unwrap` - set to `true` to delete the leading `/**`, any `*` that begins a line, and the trailing `*/` from the source text. Default: `false`.
+* `tags` - an array of tags to return. When specified, Doctrine returns only tags in this array. For example, if `tags` is `["param"]`, then only `@param` tags will be returned. Default: `null`.
+* `recoverable` - set to `true` to keep parsing even when syntax errors occur. Default: `false`.
+* `sloppy` - set to `true` to allow optional parameters to be specified in brackets (`@param {string} [foo]`). Default: `false`.
+* `lineNumbers` - set to `true` to add `lineNumber` to each node, specifying the line on which the node is found in the source. Default: `false`.
+* `range` - set to `true` to add `range` to each node, specifying the start and end index of the node in the original comment. Default: `false`.
+
+Here's a simple example:
+
+```js
+var ast = doctrine.parse(
+    [
+        "/**",
+        " * This function comment is parsed by doctrine",
+        " * @param {{ok:String}} userName",
+        "*/"
+    ].join('\n'), { unwrap: true });
+```
+
+This example returns the following AST:
+
+    {
+        "description": "This function comment is parsed by doctrine",
+        "tags": [
+            {
+                "title": "param",
+                "description": null,
+                "type": {
+                    "type": "RecordType",
+                    "fields": [
+                        {
+                            "type": "FieldType",
+                            "key": "ok",
+                            "value": {
+                                "type": "NameExpression",
+                                "name": "String"
+                            }
+                        }
+                    ]
+                },
+                "name": "userName"
+            }
+        ]
+    }
+
+See the [demo page](http://eslint.org/doctrine/demo/) more detail.
+
+## Team
+
+These folks keep the project moving and are resources for help:
+
+* Nicholas C. Zakas ([@nzakas](https://github.com/nzakas)) - project lead
+* Yusuke Suzuki ([@constellation](https://github.com/constellation)) - reviewer
+
+## Contributing
+
+Issues and pull requests will be triaged and responded to as quickly as possible. We operate under the [ESLint Contributor Guidelines](http://eslint.org/docs/developer-guide/contributing), so please be sure to read them before contributing. If you're not sure where to dig in, check out the [issues](https://github.com/eslint/doctrine/issues).
+
+## Frequently Asked Questions
+
+### Can I pass a whole JavaScript file to Doctrine?
+
+No. Doctrine can only parse JSDoc comments, so you'll need to pass just the JSDoc comment to Doctrine in order to work.
+
+
+### License
+
+#### doctrine
+
+Copyright JS Foundation and other contributors, https://js.foundation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+#### esprima
+
+some of functions is derived from esprima
+
+Copyright (C) 2012, 2011 [Ariya Hidayat](http://ariya.ofilabs.com/about)
+ (twitter: [@ariyahidayat](http://twitter.com/ariyahidayat)) and other contributors.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+  * Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+#### closure-compiler
+
+some of extensions is derived from closure-compiler
+
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+
+### Where to ask for help?
+
+Join our [Chatroom](https://gitter.im/eslint/doctrine)
+
+[npm-image]: https://img.shields.io/npm/v/doctrine.svg?style=flat-square
+[npm-url]: https://www.npmjs.com/package/doctrine
+[travis-image]: https://img.shields.io/travis/eslint/doctrine/master.svg?style=flat-square
+[travis-url]: https://travis-ci.org/eslint/doctrine
+[coveralls-image]: https://img.shields.io/coveralls/eslint/doctrine/master.svg?style=flat-square
+[coveralls-url]: https://coveralls.io/r/eslint/doctrine?branch=master
+[downloads-image]: http://img.shields.io/npm/dm/doctrine.svg?style=flat-square
+[downloads-url]: https://www.npmjs.com/package/doctrine
Index: frontend/node_modules/doctrine/lib/doctrine.js
===================================================================
--- frontend/node_modules/doctrine/lib/doctrine.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/doctrine/lib/doctrine.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,898 @@
+/*
+ * @fileoverview Main Doctrine object
+ * @author Yusuke Suzuki <utatane.tea@gmail.com>
+ * @author Dan Tao <daniel.tao@gmail.com>
+ * @author Andrew Eisenberg <andrew@eisenberg.as>
+ */
+
+(function () {
+    'use strict';
+
+    var typed,
+        utility,
+        jsdoc,
+        esutils,
+        hasOwnProperty;
+
+    esutils = require('esutils');
+    typed = require('./typed');
+    utility = require('./utility');
+
+    function sliceSource(source, index, last) {
+        return source.slice(index, last);
+    }
+
+    hasOwnProperty = (function () {
+        var func = Object.prototype.hasOwnProperty;
+        return function hasOwnProperty(obj, name) {
+            return func.call(obj, name);
+        };
+    }());
+    function shallowCopy(obj) {
+        var ret = {}, key;
+        for (key in obj) {
+            if (obj.hasOwnProperty(key)) {
+                ret[key] = obj[key];
+            }
+        }
+        return ret;
+    }
+
+    function isASCIIAlphanumeric(ch) {
+        return (ch >= 0x61  /* 'a' */ && ch <= 0x7A  /* 'z' */) ||
+            (ch >= 0x41  /* 'A' */ && ch <= 0x5A  /* 'Z' */) ||
+            (ch >= 0x30  /* '0' */ && ch <= 0x39  /* '9' */);
+    }
+
+    function isParamTitle(title) {
+        return title === 'param' || title === 'argument' || title === 'arg';
+    }
+
+    function isReturnTitle(title) {
+        return title === 'return' || title === 'returns';
+    }
+
+    function isProperty(title) {
+        return title === 'property' || title === 'prop';
+    }
+
+    function isNameParameterRequired(title) {
+        return isParamTitle(title) || isProperty(title) ||
+            title === 'alias' || title === 'this' || title === 'mixes' || title === 'requires';
+    }
+
+    function isAllowedName(title) {
+        return isNameParameterRequired(title) || title === 'const' || title === 'constant';
+    }
+
+    function isAllowedNested(title) {
+        return isProperty(title) || isParamTitle(title);
+    }
+
+    function isAllowedOptional(title) {
+        return isProperty(title) || isParamTitle(title);
+    }
+
+    function isTypeParameterRequired(title) {
+        return isParamTitle(title) || isReturnTitle(title) ||
+            title === 'define' || title === 'enum' ||
+            title === 'implements' || title === 'this' ||
+            title === 'type' || title === 'typedef' || isProperty(title);
+    }
+
+    // Consider deprecation instead using 'isTypeParameterRequired' and 'Rules' declaration to pick when a type is optional/required
+    // This would require changes to 'parseType'
+    function isAllowedType(title) {
+        return isTypeParameterRequired(title) || title === 'throws' || title === 'const' || title === 'constant' ||
+            title === 'namespace' || title === 'member' || title === 'var' || title === 'module' ||
+            title === 'constructor' || title === 'class' || title === 'extends' || title === 'augments' ||
+            title === 'public' || title === 'private' || title === 'protected';
+    }
+
+    // A regex character class that contains all whitespace except linebreak characters (\r, \n, \u2028, \u2029)
+    var WHITESPACE = '[ \\f\\t\\v\\u00a0\\u1680\\u180e\\u2000-\\u200a\\u202f\\u205f\\u3000\\ufeff]';
+
+    var STAR_MATCHER = '(' + WHITESPACE + '*(?:\\*' + WHITESPACE + '?)?)(.+|[\r\n\u2028\u2029])';
+
+    function unwrapComment(doc) {
+        // JSDoc comment is following form
+        //   /**
+        //    * .......
+        //    */
+
+        return doc.
+            // remove /**
+            replace(/^\/\*\*?/, '').
+            // remove */
+            replace(/\*\/$/, '').
+            // remove ' * ' at the beginning of a line
+            replace(new RegExp(STAR_MATCHER, 'g'), '$2').
+            // remove trailing whitespace
+            replace(/\s*$/, '');
+    }
+
+    /**
+     * Converts an index in an "unwrapped" JSDoc comment to the corresponding index in the original "wrapped" version
+     * @param {string} originalSource The original wrapped comment
+     * @param {number} unwrappedIndex The index of a character in the unwrapped string
+     * @returns {number} The index of the corresponding character in the original wrapped string
+     */
+    function convertUnwrappedCommentIndex(originalSource, unwrappedIndex) {
+        var replacedSource = originalSource.replace(/^\/\*\*?/, '');
+        var numSkippedChars = 0;
+        var matcher = new RegExp(STAR_MATCHER, 'g');
+        var match;
+
+        while ((match = matcher.exec(replacedSource))) {
+            numSkippedChars += match[1].length;
+
+            if (match.index + match[0].length > unwrappedIndex + numSkippedChars) {
+                return unwrappedIndex + numSkippedChars + originalSource.length - replacedSource.length;
+            }
+        }
+
+        return originalSource.replace(/\*\/$/, '').replace(/\s*$/, '').length;
+    }
+
+    // JSDoc Tag Parser
+
+    (function (exports) {
+        var Rules,
+            index,
+            lineNumber,
+            length,
+            source,
+            originalSource,
+            recoverable,
+            sloppy,
+            strict;
+
+        function advance() {
+            var ch = source.charCodeAt(index);
+            index += 1;
+            if (esutils.code.isLineTerminator(ch) && !(ch === 0x0D  /* '\r' */ && source.charCodeAt(index) === 0x0A  /* '\n' */)) {
+                lineNumber += 1;
+            }
+            return String.fromCharCode(ch);
+        }
+
+        function scanTitle() {
+            var title = '';
+            // waste '@'
+            advance();
+
+            while (index < length && isASCIIAlphanumeric(source.charCodeAt(index))) {
+                title += advance();
+            }
+
+            return title;
+        }
+
+        function seekContent() {
+            var ch, waiting, last = index;
+
+            waiting = false;
+            while (last < length) {
+                ch = source.charCodeAt(last);
+                if (esutils.code.isLineTerminator(ch) && !(ch === 0x0D  /* '\r' */ && source.charCodeAt(last + 1) === 0x0A  /* '\n' */)) {
+                    waiting = true;
+                } else if (waiting) {
+                    if (ch === 0x40  /* '@' */) {
+                        break;
+                    }
+                    if (!esutils.code.isWhiteSpace(ch)) {
+                        waiting = false;
+                    }
+                }
+                last += 1;
+            }
+            return last;
+        }
+
+        // type expression may have nest brace, such as,
+        // { { ok: string } }
+        //
+        // therefore, scanning type expression with balancing braces.
+        function parseType(title, last, addRange) {
+            var ch, brace, type, startIndex, direct = false;
+
+
+            // search '{'
+            while (index < last) {
+                ch = source.charCodeAt(index);
+                if (esutils.code.isWhiteSpace(ch)) {
+                    advance();
+                } else if (ch === 0x7B  /* '{' */) {
+                    advance();
+                    break;
+                } else {
+                    // this is direct pattern
+                    direct = true;
+                    break;
+                }
+            }
+
+
+            if (direct) {
+                return null;
+            }
+
+            // type expression { is found
+            brace = 1;
+            type = '';
+            while (index < last) {
+                ch = source.charCodeAt(index);
+                if (esutils.code.isLineTerminator(ch)) {
+                    advance();
+                } else {
+                    if (ch === 0x7D  /* '}' */) {
+                        brace -= 1;
+                        if (brace === 0) {
+                            advance();
+                            break;
+                        }
+                    } else if (ch === 0x7B  /* '{' */) {
+                        brace += 1;
+                    }
+                    if (type === '') {
+                        startIndex = index;
+                    }
+                    type += advance();
+                }
+            }
+
+            if (brace !== 0) {
+                // braces is not balanced
+                return utility.throwError('Braces are not balanced');
+            }
+
+            if (isAllowedOptional(title)) {
+                return typed.parseParamType(type, {startIndex: convertIndex(startIndex), range: addRange});
+            }
+
+            return typed.parseType(type, {startIndex: convertIndex(startIndex), range: addRange});
+        }
+
+        function scanIdentifier(last) {
+            var identifier;
+            if (!esutils.code.isIdentifierStartES5(source.charCodeAt(index)) && !source[index].match(/[0-9]/)) {
+                return null;
+            }
+            identifier = advance();
+            while (index < last && esutils.code.isIdentifierPartES5(source.charCodeAt(index))) {
+                identifier += advance();
+            }
+            return identifier;
+        }
+
+        function skipWhiteSpace(last) {
+            while (index < last && (esutils.code.isWhiteSpace(source.charCodeAt(index)) || esutils.code.isLineTerminator(source.charCodeAt(index)))) {
+                advance();
+            }
+        }
+
+        function parseName(last, allowBrackets, allowNestedParams) {
+            var name = '',
+                useBrackets,
+                insideString;
+
+
+            skipWhiteSpace(last);
+
+            if (index >= last) {
+                return null;
+            }
+
+            if (source.charCodeAt(index) === 0x5B  /* '[' */) {
+                if (allowBrackets) {
+                    useBrackets = true;
+                    name = advance();
+                } else {
+                    return null;
+                }
+            }
+
+            name += scanIdentifier(last);
+
+            if (allowNestedParams) {
+                if (source.charCodeAt(index) === 0x3A /* ':' */ && (
+                        name === 'module' ||
+                        name === 'external' ||
+                        name === 'event')) {
+                    name += advance();
+                    name += scanIdentifier(last);
+
+                }
+                if(source.charCodeAt(index) === 0x5B  /* '[' */ && source.charCodeAt(index + 1) === 0x5D  /* ']' */){
+                    name += advance();
+                    name += advance();
+                }
+                while (source.charCodeAt(index) === 0x2E  /* '.' */ ||
+                        source.charCodeAt(index) === 0x2F  /* '/' */ ||
+                        source.charCodeAt(index) === 0x23  /* '#' */ ||
+                        source.charCodeAt(index) === 0x2D  /* '-' */ ||
+                        source.charCodeAt(index) === 0x7E  /* '~' */) {
+                    name += advance();
+                    name += scanIdentifier(last);
+                }
+            }
+
+            if (useBrackets) {
+                skipWhiteSpace(last);
+                // do we have a default value for this?
+                if (source.charCodeAt(index) === 0x3D  /* '=' */) {
+                    // consume the '='' symbol
+                    name += advance();
+                    skipWhiteSpace(last);
+
+                    var ch;
+                    var bracketDepth = 1;
+
+                    // scan in the default value
+                    while (index < last) {
+                        ch = source.charCodeAt(index);
+
+                        if (esutils.code.isWhiteSpace(ch)) {
+                            if (!insideString) {
+                                skipWhiteSpace(last);
+                                ch = source.charCodeAt(index);
+                            }
+                        }
+
+                        if (ch === 0x27 /* ''' */) {
+                            if (!insideString) {
+                                insideString = '\'';
+                            } else {
+                                if (insideString === '\'') {
+                                    insideString = '';
+                                }
+                            }
+                        }
+
+                        if (ch === 0x22 /* '"' */) {
+                            if (!insideString) {
+                                insideString = '"';
+                            } else {
+                                if (insideString === '"') {
+                                    insideString = '';
+                                }
+                            }
+                        }
+
+                        if (ch === 0x5B /* '[' */) {
+                            bracketDepth++;
+                        } else if (ch === 0x5D  /* ']' */ &&
+                            --bracketDepth === 0) {
+                            break;
+                        }
+
+                        name += advance();
+                    }
+                }
+
+                skipWhiteSpace(last);
+
+                if (index >= last || source.charCodeAt(index) !== 0x5D  /* ']' */) {
+                    // we never found a closing ']'
+                    return null;
+                }
+
+                // collect the last ']'
+                name += advance();
+            }
+
+            return name;
+        }
+
+        function skipToTag() {
+            while (index < length && source.charCodeAt(index) !== 0x40  /* '@' */) {
+                advance();
+            }
+            if (index >= length) {
+                return false;
+            }
+            utility.assert(source.charCodeAt(index) === 0x40  /* '@' */);
+            return true;
+        }
+
+        function convertIndex(rangeIndex) {
+            if (source === originalSource) {
+                return rangeIndex;
+            }
+            return convertUnwrappedCommentIndex(originalSource, rangeIndex);
+        }
+
+        function TagParser(options, title) {
+            this._options = options;
+            this._title = title.toLowerCase();
+            this._tag = {
+                title: title,
+                description: null
+            };
+            if (this._options.lineNumbers) {
+                this._tag.lineNumber = lineNumber;
+            }
+            this._first = index - title.length - 1;
+            this._last = 0;
+            // space to save special information for title parsers.
+            this._extra = { };
+        }
+
+        // addError(err, ...)
+        TagParser.prototype.addError = function addError(errorText) {
+            var args = Array.prototype.slice.call(arguments, 1),
+                msg = errorText.replace(
+                    /%(\d)/g,
+                    function (whole, index) {
+                        utility.assert(index < args.length, 'Message reference must be in range');
+                        return args[index];
+                    }
+                );
+
+            if (!this._tag.errors) {
+                this._tag.errors = [];
+            }
+            if (strict) {
+                utility.throwError(msg);
+            }
+            this._tag.errors.push(msg);
+            return recoverable;
+        };
+
+        TagParser.prototype.parseType = function () {
+            // type required titles
+            if (isTypeParameterRequired(this._title)) {
+                try {
+                    this._tag.type = parseType(this._title, this._last, this._options.range);
+                    if (!this._tag.type) {
+                        if (!isParamTitle(this._title) && !isReturnTitle(this._title)) {
+                            if (!this.addError('Missing or invalid tag type')) {
+                                return false;
+                            }
+                        }
+                    }
+                } catch (error) {
+                    this._tag.type = null;
+                    if (!this.addError(error.message)) {
+                        return false;
+                    }
+                }
+            } else if (isAllowedType(this._title)) {
+                // optional types
+                try {
+                    this._tag.type = parseType(this._title, this._last, this._options.range);
+                } catch (e) {
+                    //For optional types, lets drop the thrown error when we hit the end of the file
+                }
+            }
+            return true;
+        };
+
+        TagParser.prototype._parseNamePath = function (optional) {
+            var name;
+            name = parseName(this._last, sloppy && isAllowedOptional(this._title), true);
+            if (!name) {
+                if (!optional) {
+                    if (!this.addError('Missing or invalid tag name')) {
+                        return false;
+                    }
+                }
+            }
+            this._tag.name = name;
+            return true;
+        };
+
+        TagParser.prototype.parseNamePath = function () {
+            return this._parseNamePath(false);
+        };
+
+        TagParser.prototype.parseNamePathOptional = function () {
+            return this._parseNamePath(true);
+        };
+
+
+        TagParser.prototype.parseName = function () {
+            var assign, name;
+
+            // param, property requires name
+            if (isAllowedName(this._title)) {
+                this._tag.name = parseName(this._last, sloppy && isAllowedOptional(this._title), isAllowedNested(this._title));
+                if (!this._tag.name) {
+                    if (!isNameParameterRequired(this._title)) {
+                        return true;
+                    }
+
+                    // it's possible the name has already been parsed but interpreted as a type
+                    // it's also possible this is a sloppy declaration, in which case it will be
+                    // fixed at the end
+                    if (isParamTitle(this._title) && this._tag.type && this._tag.type.name) {
+                        this._extra.name = this._tag.type;
+                        this._tag.name = this._tag.type.name;
+                        this._tag.type = null;
+                    } else {
+                        if (!this.addError('Missing or invalid tag name')) {
+                            return false;
+                        }
+                    }
+                } else {
+                    name = this._tag.name;
+                    if (name.charAt(0) === '[' && name.charAt(name.length - 1) === ']') {
+                        // extract the default value if there is one
+                        // example: @param {string} [somebody=John Doe] description
+                        assign = name.substring(1, name.length - 1).split('=');
+                        if (assign.length > 1) {
+                            this._tag['default'] = assign.slice(1).join('=');
+                        }
+                        this._tag.name = assign[0];
+
+                        // convert to an optional type
+                        if (this._tag.type && this._tag.type.type !== 'OptionalType') {
+                            this._tag.type = {
+                                type: 'OptionalType',
+                                expression: this._tag.type
+                            };
+                        }
+                    }
+                }
+            }
+
+
+            return true;
+        };
+
+        TagParser.prototype.parseDescription = function parseDescription() {
+            var description = sliceSource(source, index, this._last).trim();
+            if (description) {
+                if ((/^-\s+/).test(description)) {
+                    description = description.substring(2);
+                }
+                this._tag.description = description;
+            }
+            return true;
+        };
+
+        TagParser.prototype.parseCaption = function parseDescription() {
+            var description = sliceSource(source, index, this._last).trim();
+            var captionStartTag = '<caption>';
+            var captionEndTag = '</caption>';
+            var captionStart = description.indexOf(captionStartTag);
+            var captionEnd = description.indexOf(captionEndTag);
+            if (captionStart >= 0 && captionEnd >= 0) {
+                this._tag.caption = description.substring(
+                    captionStart + captionStartTag.length, captionEnd).trim();
+                this._tag.description = description.substring(captionEnd + captionEndTag.length).trim();
+            } else {
+                this._tag.description = description;
+            }
+            return true;
+        };
+
+        TagParser.prototype.parseKind = function parseKind() {
+            var kind, kinds;
+            kinds = {
+                'class': true,
+                'constant': true,
+                'event': true,
+                'external': true,
+                'file': true,
+                'function': true,
+                'member': true,
+                'mixin': true,
+                'module': true,
+                'namespace': true,
+                'typedef': true
+            };
+            kind = sliceSource(source, index, this._last).trim();
+            this._tag.kind = kind;
+            if (!hasOwnProperty(kinds, kind)) {
+                if (!this.addError('Invalid kind name \'%0\'', kind)) {
+                    return false;
+                }
+            }
+            return true;
+        };
+
+        TagParser.prototype.parseAccess = function parseAccess() {
+            var access;
+            access = sliceSource(source, index, this._last).trim();
+            this._tag.access = access;
+            if (access !== 'private' && access !== 'protected' && access !== 'public') {
+                if (!this.addError('Invalid access name \'%0\'', access)) {
+                    return false;
+                }
+            }
+            return true;
+        };
+
+        TagParser.prototype.parseThis = function parseThis() {
+            // this name may be a name expression (e.g. {foo.bar}),
+            // an union (e.g. {foo.bar|foo.baz}) or a name path (e.g. foo.bar)
+            var value = sliceSource(source, index, this._last).trim();
+            if (value && value.charAt(0) === '{') {
+                var gotType = this.parseType();
+                if (gotType && this._tag.type.type === 'NameExpression' || this._tag.type.type === 'UnionType') {
+                    this._tag.name = this._tag.type.name;
+                    return true;
+                } else {
+                    return this.addError('Invalid name for this');
+                }
+            } else {
+                return this.parseNamePath();
+            }
+        };
+
+        TagParser.prototype.parseVariation = function parseVariation() {
+            var variation, text;
+            text = sliceSource(source, index, this._last).trim();
+            variation = parseFloat(text, 10);
+            this._tag.variation = variation;
+            if (isNaN(variation)) {
+                if (!this.addError('Invalid variation \'%0\'', text)) {
+                    return false;
+                }
+            }
+            return true;
+        };
+
+        TagParser.prototype.ensureEnd = function () {
+            var shouldBeEmpty = sliceSource(source, index, this._last).trim();
+            if (shouldBeEmpty) {
+                if (!this.addError('Unknown content \'%0\'', shouldBeEmpty)) {
+                    return false;
+                }
+            }
+            return true;
+        };
+
+        TagParser.prototype.epilogue = function epilogue() {
+            var description;
+
+            description = this._tag.description;
+            // un-fix potentially sloppy declaration
+            if (isAllowedOptional(this._title) && !this._tag.type && description && description.charAt(0) === '[') {
+                this._tag.type = this._extra.name;
+                if (!this._tag.name) {
+                    this._tag.name = undefined;
+                }
+
+                if (!sloppy) {
+                    if (!this.addError('Missing or invalid tag name')) {
+                        return false;
+                    }
+                }
+            }
+
+            return true;
+        };
+
+        Rules = {
+            // http://usejsdoc.org/tags-access.html
+            'access': ['parseAccess'],
+            // http://usejsdoc.org/tags-alias.html
+            'alias': ['parseNamePath', 'ensureEnd'],
+            // http://usejsdoc.org/tags-augments.html
+            'augments': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
+            // http://usejsdoc.org/tags-constructor.html
+            'constructor': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
+            // Synonym: http://usejsdoc.org/tags-constructor.html
+            'class': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
+            // Synonym: http://usejsdoc.org/tags-extends.html
+            'extends': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
+            // http://usejsdoc.org/tags-example.html
+            'example': ['parseCaption'],
+            // http://usejsdoc.org/tags-deprecated.html
+            'deprecated': ['parseDescription'],
+            // http://usejsdoc.org/tags-global.html
+            'global': ['ensureEnd'],
+            // http://usejsdoc.org/tags-inner.html
+            'inner': ['ensureEnd'],
+            // http://usejsdoc.org/tags-instance.html
+            'instance': ['ensureEnd'],
+            // http://usejsdoc.org/tags-kind.html
+            'kind': ['parseKind'],
+            // http://usejsdoc.org/tags-mixes.html
+            'mixes': ['parseNamePath', 'ensureEnd'],
+            // http://usejsdoc.org/tags-mixin.html
+            'mixin': ['parseNamePathOptional', 'ensureEnd'],
+            // http://usejsdoc.org/tags-member.html
+            'member': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
+            // http://usejsdoc.org/tags-method.html
+            'method': ['parseNamePathOptional', 'ensureEnd'],
+            // http://usejsdoc.org/tags-module.html
+            'module': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
+            // Synonym: http://usejsdoc.org/tags-method.html
+            'func': ['parseNamePathOptional', 'ensureEnd'],
+            // Synonym: http://usejsdoc.org/tags-method.html
+            'function': ['parseNamePathOptional', 'ensureEnd'],
+            // Synonym: http://usejsdoc.org/tags-member.html
+            'var': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
+            // http://usejsdoc.org/tags-name.html
+            'name': ['parseNamePath', 'ensureEnd'],
+            // http://usejsdoc.org/tags-namespace.html
+            'namespace': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
+            // http://usejsdoc.org/tags-private.html
+            'private': ['parseType', 'parseDescription'],
+            // http://usejsdoc.org/tags-protected.html
+            'protected': ['parseType', 'parseDescription'],
+            // http://usejsdoc.org/tags-public.html
+            'public': ['parseType', 'parseDescription'],
+            // http://usejsdoc.org/tags-readonly.html
+            'readonly': ['ensureEnd'],
+            // http://usejsdoc.org/tags-requires.html
+            'requires': ['parseNamePath', 'ensureEnd'],
+            // http://usejsdoc.org/tags-since.html
+            'since': ['parseDescription'],
+            // http://usejsdoc.org/tags-static.html
+            'static': ['ensureEnd'],
+            // http://usejsdoc.org/tags-summary.html
+            'summary': ['parseDescription'],
+            // http://usejsdoc.org/tags-this.html
+            'this': ['parseThis', 'ensureEnd'],
+            // http://usejsdoc.org/tags-todo.html
+            'todo': ['parseDescription'],
+            // http://usejsdoc.org/tags-typedef.html
+            'typedef': ['parseType', 'parseNamePathOptional'],
+            // http://usejsdoc.org/tags-variation.html
+            'variation': ['parseVariation'],
+            // http://usejsdoc.org/tags-version.html
+            'version': ['parseDescription']
+        };
+
+        TagParser.prototype.parse = function parse() {
+            var i, iz, sequences, method;
+
+
+            // empty title
+            if (!this._title) {
+                if (!this.addError('Missing or invalid title')) {
+                    return null;
+                }
+            }
+
+            // Seek to content last index.
+            this._last = seekContent(this._title);
+
+            if (this._options.range) {
+                this._tag.range = [this._first, source.slice(0, this._last).replace(/\s*$/, '').length].map(convertIndex);
+            }
+
+            if (hasOwnProperty(Rules, this._title)) {
+                sequences = Rules[this._title];
+            } else {
+                // default sequences
+                sequences = ['parseType', 'parseName', 'parseDescription', 'epilogue'];
+            }
+
+            for (i = 0, iz = sequences.length; i < iz; ++i) {
+                method = sequences[i];
+                if (!this[method]()) {
+                    return null;
+                }
+            }
+
+            return this._tag;
+        };
+
+        function parseTag(options) {
+            var title, parser, tag;
+
+            // skip to tag
+            if (!skipToTag()) {
+                return null;
+            }
+
+            // scan title
+            title = scanTitle();
+
+            // construct tag parser
+            parser = new TagParser(options, title);
+            tag = parser.parse();
+
+            // Seek global index to end of this tag.
+            while (index < parser._last) {
+                advance();
+            }
+
+            return tag;
+        }
+
+        //
+        // Parse JSDoc
+        //
+
+        function scanJSDocDescription(preserveWhitespace) {
+            var description = '', ch, atAllowed;
+
+            atAllowed = true;
+            while (index < length) {
+                ch = source.charCodeAt(index);
+
+                if (atAllowed && ch === 0x40  /* '@' */) {
+                    break;
+                }
+
+                if (esutils.code.isLineTerminator(ch)) {
+                    atAllowed = true;
+                } else if (atAllowed && !esutils.code.isWhiteSpace(ch)) {
+                    atAllowed = false;
+                }
+
+                description += advance();
+            }
+
+            return preserveWhitespace ? description : description.trim();
+        }
+
+        function parse(comment, options) {
+            var tags = [], tag, description, interestingTags, i, iz;
+
+            if (options === undefined) {
+                options = {};
+            }
+
+            if (typeof options.unwrap === 'boolean' && options.unwrap) {
+                source = unwrapComment(comment);
+            } else {
+                source = comment;
+            }
+
+            originalSource = comment;
+
+            // array of relevant tags
+            if (options.tags) {
+                if (Array.isArray(options.tags)) {
+                    interestingTags = { };
+                    for (i = 0, iz = options.tags.length; i < iz; i++) {
+                        if (typeof options.tags[i] === 'string') {
+                            interestingTags[options.tags[i]] = true;
+                        } else {
+                            utility.throwError('Invalid "tags" parameter: ' + options.tags);
+                        }
+                    }
+                } else {
+                    utility.throwError('Invalid "tags" parameter: ' + options.tags);
+                }
+            }
+
+            length = source.length;
+            index = 0;
+            lineNumber = 0;
+            recoverable = options.recoverable;
+            sloppy = options.sloppy;
+            strict = options.strict;
+
+            description = scanJSDocDescription(options.preserveWhitespace);
+
+            while (true) {
+                tag = parseTag(options);
+                if (!tag) {
+                    break;
+                }
+                if (!interestingTags || interestingTags.hasOwnProperty(tag.title)) {
+                    tags.push(tag);
+                }
+            }
+
+            return {
+                description: description,
+                tags: tags
+            };
+        }
+        exports.parse = parse;
+    }(jsdoc = {}));
+
+    exports.version = utility.VERSION;
+    exports.parse = jsdoc.parse;
+    exports.parseType = typed.parseType;
+    exports.parseParamType = typed.parseParamType;
+    exports.unwrapComment = unwrapComment;
+    exports.Syntax = shallowCopy(typed.Syntax);
+    exports.Error = utility.DoctrineError;
+    exports.type = {
+        Syntax: exports.Syntax,
+        parseType: typed.parseType,
+        parseParamType: typed.parseParamType,
+        stringify: typed.stringify
+    };
+}());
+/* vim: set sw=4 ts=4 et tw=80 : */
Index: frontend/node_modules/doctrine/lib/typed.js
===================================================================
--- frontend/node_modules/doctrine/lib/typed.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/doctrine/lib/typed.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1305 @@
+/*
+ * @fileoverview Type expression parser.
+ * @author Yusuke Suzuki <utatane.tea@gmail.com>
+ * @author Dan Tao <daniel.tao@gmail.com>
+ * @author Andrew Eisenberg <andrew@eisenberg.as>
+ */
+
+// "typed", the Type Expression Parser for doctrine.
+
+(function () {
+    'use strict';
+
+    var Syntax,
+        Token,
+        source,
+        length,
+        index,
+        previous,
+        token,
+        value,
+        esutils,
+        utility,
+        rangeOffset,
+        addRange;
+
+    esutils = require('esutils');
+    utility = require('./utility');
+
+    Syntax = {
+        NullableLiteral: 'NullableLiteral',
+        AllLiteral: 'AllLiteral',
+        NullLiteral: 'NullLiteral',
+        UndefinedLiteral: 'UndefinedLiteral',
+        VoidLiteral: 'VoidLiteral',
+        UnionType: 'UnionType',
+        ArrayType: 'ArrayType',
+        RecordType: 'RecordType',
+        FieldType: 'FieldType',
+        FunctionType: 'FunctionType',
+        ParameterType: 'ParameterType',
+        RestType: 'RestType',
+        NonNullableType: 'NonNullableType',
+        OptionalType: 'OptionalType',
+        NullableType: 'NullableType',
+        NameExpression: 'NameExpression',
+        TypeApplication: 'TypeApplication',
+        StringLiteralType: 'StringLiteralType',
+        NumericLiteralType: 'NumericLiteralType',
+        BooleanLiteralType: 'BooleanLiteralType'
+    };
+
+    Token = {
+        ILLEGAL: 0,    // ILLEGAL
+        DOT_LT: 1,     // .<
+        REST: 2,       // ...
+        LT: 3,         // <
+        GT: 4,         // >
+        LPAREN: 5,     // (
+        RPAREN: 6,     // )
+        LBRACE: 7,     // {
+        RBRACE: 8,     // }
+        LBRACK: 9,    // [
+        RBRACK: 10,    // ]
+        COMMA: 11,     // ,
+        COLON: 12,     // :
+        STAR: 13,      // *
+        PIPE: 14,      // |
+        QUESTION: 15,  // ?
+        BANG: 16,      // !
+        EQUAL: 17,     // =
+        NAME: 18,      // name token
+        STRING: 19,    // string
+        NUMBER: 20,    // number
+        EOF: 21
+    };
+
+    function isTypeName(ch) {
+        return '><(){}[],:*|?!='.indexOf(String.fromCharCode(ch)) === -1 && !esutils.code.isWhiteSpace(ch) && !esutils.code.isLineTerminator(ch);
+    }
+
+    function Context(previous, index, token, value) {
+        this._previous = previous;
+        this._index = index;
+        this._token = token;
+        this._value = value;
+    }
+
+    Context.prototype.restore = function () {
+        previous = this._previous;
+        index = this._index;
+        token = this._token;
+        value = this._value;
+    };
+
+    Context.save = function () {
+        return new Context(previous, index, token, value);
+    };
+
+    function maybeAddRange(node, range) {
+        if (addRange) {
+            node.range = [range[0] + rangeOffset, range[1] + rangeOffset];
+        }
+        return node;
+    }
+
+    function advance() {
+        var ch = source.charAt(index);
+        index += 1;
+        return ch;
+    }
+
+    function scanHexEscape(prefix) {
+        var i, len, ch, code = 0;
+
+        len = (prefix === 'u') ? 4 : 2;
+        for (i = 0; i < len; ++i) {
+            if (index < length && esutils.code.isHexDigit(source.charCodeAt(index))) {
+                ch = advance();
+                code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase());
+            } else {
+                return '';
+            }
+        }
+        return String.fromCharCode(code);
+    }
+
+    function scanString() {
+        var str = '', quote, ch, code, unescaped, restore; //TODO review removal octal = false
+        quote = source.charAt(index);
+        ++index;
+
+        while (index < length) {
+            ch = advance();
+
+            if (ch === quote) {
+                quote = '';
+                break;
+            } else if (ch === '\\') {
+                ch = advance();
+                if (!esutils.code.isLineTerminator(ch.charCodeAt(0))) {
+                    switch (ch) {
+                    case 'n':
+                        str += '\n';
+                        break;
+                    case 'r':
+                        str += '\r';
+                        break;
+                    case 't':
+                        str += '\t';
+                        break;
+                    case 'u':
+                    case 'x':
+                        restore = index;
+                        unescaped = scanHexEscape(ch);
+                        if (unescaped) {
+                            str += unescaped;
+                        } else {
+                            index = restore;
+                            str += ch;
+                        }
+                        break;
+                    case 'b':
+                        str += '\b';
+                        break;
+                    case 'f':
+                        str += '\f';
+                        break;
+                    case 'v':
+                        str += '\v';
+                        break;
+
+                    default:
+                        if (esutils.code.isOctalDigit(ch.charCodeAt(0))) {
+                            code = '01234567'.indexOf(ch);
+
+                            // \0 is not octal escape sequence
+                            // Deprecating unused code. TODO review removal
+                            //if (code !== 0) {
+                            //    octal = true;
+                            //}
+
+                            if (index < length && esutils.code.isOctalDigit(source.charCodeAt(index))) {
+                                //TODO Review Removal octal = true;
+                                code = code * 8 + '01234567'.indexOf(advance());
+
+                                // 3 digits are only allowed when string starts
+                                // with 0, 1, 2, 3
+                                if ('0123'.indexOf(ch) >= 0 &&
+                                        index < length &&
+                                        esutils.code.isOctalDigit(source.charCodeAt(index))) {
+                                    code = code * 8 + '01234567'.indexOf(advance());
+                                }
+                            }
+                            str += String.fromCharCode(code);
+                        } else {
+                            str += ch;
+                        }
+                        break;
+                    }
+                } else {
+                    if (ch ===  '\r' && source.charCodeAt(index) === 0x0A  /* '\n' */) {
+                        ++index;
+                    }
+                }
+            } else if (esutils.code.isLineTerminator(ch.charCodeAt(0))) {
+                break;
+            } else {
+                str += ch;
+            }
+        }
+
+        if (quote !== '') {
+            utility.throwError('unexpected quote');
+        }
+
+        value = str;
+        return Token.STRING;
+    }
+
+    function scanNumber() {
+        var number, ch;
+
+        number = '';
+        ch = source.charCodeAt(index);
+
+        if (ch !== 0x2E  /* '.' */) {
+            number = advance();
+            ch = source.charCodeAt(index);
+
+            if (number === '0') {
+                if (ch === 0x78  /* 'x' */ || ch === 0x58  /* 'X' */) {
+                    number += advance();
+                    while (index < length) {
+                        ch = source.charCodeAt(index);
+                        if (!esutils.code.isHexDigit(ch)) {
+                            break;
+                        }
+                        number += advance();
+                    }
+
+                    if (number.length <= 2) {
+                        // only 0x
+                        utility.throwError('unexpected token');
+                    }
+
+                    if (index < length) {
+                        ch = source.charCodeAt(index);
+                        if (esutils.code.isIdentifierStartES5(ch)) {
+                            utility.throwError('unexpected token');
+                        }
+                    }
+                    value = parseInt(number, 16);
+                    return Token.NUMBER;
+                }
+
+                if (esutils.code.isOctalDigit(ch)) {
+                    number += advance();
+                    while (index < length) {
+                        ch = source.charCodeAt(index);
+                        if (!esutils.code.isOctalDigit(ch)) {
+                            break;
+                        }
+                        number += advance();
+                    }
+
+                    if (index < length) {
+                        ch = source.charCodeAt(index);
+                        if (esutils.code.isIdentifierStartES5(ch) || esutils.code.isDecimalDigit(ch)) {
+                            utility.throwError('unexpected token');
+                        }
+                    }
+                    value = parseInt(number, 8);
+                    return Token.NUMBER;
+                }
+
+                if (esutils.code.isDecimalDigit(ch)) {
+                    utility.throwError('unexpected token');
+                }
+            }
+
+            while (index < length) {
+                ch = source.charCodeAt(index);
+                if (!esutils.code.isDecimalDigit(ch)) {
+                    break;
+                }
+                number += advance();
+            }
+        }
+
+        if (ch === 0x2E  /* '.' */) {
+            number += advance();
+            while (index < length) {
+                ch = source.charCodeAt(index);
+                if (!esutils.code.isDecimalDigit(ch)) {
+                    break;
+                }
+                number += advance();
+            }
+        }
+
+        if (ch === 0x65  /* 'e' */ || ch === 0x45  /* 'E' */) {
+            number += advance();
+
+            ch = source.charCodeAt(index);
+            if (ch === 0x2B  /* '+' */ || ch === 0x2D  /* '-' */) {
+                number += advance();
+            }
+
+            ch = source.charCodeAt(index);
+            if (esutils.code.isDecimalDigit(ch)) {
+                number += advance();
+                while (index < length) {
+                    ch = source.charCodeAt(index);
+                    if (!esutils.code.isDecimalDigit(ch)) {
+                        break;
+                    }
+                    number += advance();
+                }
+            } else {
+                utility.throwError('unexpected token');
+            }
+        }
+
+        if (index < length) {
+            ch = source.charCodeAt(index);
+            if (esutils.code.isIdentifierStartES5(ch)) {
+                utility.throwError('unexpected token');
+            }
+        }
+
+        value = parseFloat(number);
+        return Token.NUMBER;
+    }
+
+
+    function scanTypeName() {
+        var ch, ch2;
+
+        value = advance();
+        while (index < length && isTypeName(source.charCodeAt(index))) {
+            ch = source.charCodeAt(index);
+            if (ch === 0x2E  /* '.' */) {
+                if ((index + 1) >= length) {
+                    return Token.ILLEGAL;
+                }
+                ch2 = source.charCodeAt(index + 1);
+                if (ch2 === 0x3C  /* '<' */) {
+                    break;
+                }
+            }
+            value += advance();
+        }
+        return Token.NAME;
+    }
+
+    function next() {
+        var ch;
+
+        previous = index;
+
+        while (index < length && esutils.code.isWhiteSpace(source.charCodeAt(index))) {
+            advance();
+        }
+        if (index >= length) {
+            token = Token.EOF;
+            return token;
+        }
+
+        ch = source.charCodeAt(index);
+        switch (ch) {
+        case 0x27:  /* ''' */
+        case 0x22:  /* '"' */
+            token = scanString();
+            return token;
+
+        case 0x3A:  /* ':' */
+            advance();
+            token = Token.COLON;
+            return token;
+
+        case 0x2C:  /* ',' */
+            advance();
+            token = Token.COMMA;
+            return token;
+
+        case 0x28:  /* '(' */
+            advance();
+            token = Token.LPAREN;
+            return token;
+
+        case 0x29:  /* ')' */
+            advance();
+            token = Token.RPAREN;
+            return token;
+
+        case 0x5B:  /* '[' */
+            advance();
+            token = Token.LBRACK;
+            return token;
+
+        case 0x5D:  /* ']' */
+            advance();
+            token = Token.RBRACK;
+            return token;
+
+        case 0x7B:  /* '{' */
+            advance();
+            token = Token.LBRACE;
+            return token;
+
+        case 0x7D:  /* '}' */
+            advance();
+            token = Token.RBRACE;
+            return token;
+
+        case 0x2E:  /* '.' */
+            if (index + 1 < length) {
+                ch = source.charCodeAt(index + 1);
+                if (ch === 0x3C  /* '<' */) {
+                    advance();  // '.'
+                    advance();  // '<'
+                    token = Token.DOT_LT;
+                    return token;
+                }
+
+                if (ch === 0x2E  /* '.' */ && index + 2 < length && source.charCodeAt(index + 2) === 0x2E  /* '.' */) {
+                    advance();  // '.'
+                    advance();  // '.'
+                    advance();  // '.'
+                    token = Token.REST;
+                    return token;
+                }
+
+                if (esutils.code.isDecimalDigit(ch)) {
+                    token = scanNumber();
+                    return token;
+                }
+            }
+            token = Token.ILLEGAL;
+            return token;
+
+        case 0x3C:  /* '<' */
+            advance();
+            token = Token.LT;
+            return token;
+
+        case 0x3E:  /* '>' */
+            advance();
+            token = Token.GT;
+            return token;
+
+        case 0x2A:  /* '*' */
+            advance();
+            token = Token.STAR;
+            return token;
+
+        case 0x7C:  /* '|' */
+            advance();
+            token = Token.PIPE;
+            return token;
+
+        case 0x3F:  /* '?' */
+            advance();
+            token = Token.QUESTION;
+            return token;
+
+        case 0x21:  /* '!' */
+            advance();
+            token = Token.BANG;
+            return token;
+
+        case 0x3D:  /* '=' */
+            advance();
+            token = Token.EQUAL;
+            return token;
+
+        case 0x2D: /* '-' */
+            token = scanNumber();
+            return token;
+
+        default:
+            if (esutils.code.isDecimalDigit(ch)) {
+                token = scanNumber();
+                return token;
+            }
+
+            // type string permits following case,
+            //
+            // namespace.module.MyClass
+            //
+            // this reduced 1 token TK_NAME
+            utility.assert(isTypeName(ch));
+            token = scanTypeName();
+            return token;
+        }
+    }
+
+    function consume(target, text) {
+        utility.assert(token === target, text || 'consumed token not matched');
+        next();
+    }
+
+    function expect(target, message) {
+        if (token !== target) {
+            utility.throwError(message || 'unexpected token');
+        }
+        next();
+    }
+
+    // UnionType := '(' TypeUnionList ')'
+    //
+    // TypeUnionList :=
+    //     <<empty>>
+    //   | NonemptyTypeUnionList
+    //
+    // NonemptyTypeUnionList :=
+    //     TypeExpression
+    //   | TypeExpression '|' NonemptyTypeUnionList
+    function parseUnionType() {
+        var elements, startIndex = index - 1;
+        consume(Token.LPAREN, 'UnionType should start with (');
+        elements = [];
+        if (token !== Token.RPAREN) {
+            while (true) {
+                elements.push(parseTypeExpression());
+                if (token === Token.RPAREN) {
+                    break;
+                }
+                expect(Token.PIPE);
+            }
+        }
+        consume(Token.RPAREN, 'UnionType should end with )');
+        return maybeAddRange({
+            type: Syntax.UnionType,
+            elements: elements
+        }, [startIndex, previous]);
+    }
+
+    // ArrayType := '[' ElementTypeList ']'
+    //
+    // ElementTypeList :=
+    //     <<empty>>
+    //  | TypeExpression
+    //  | '...' TypeExpression
+    //  | TypeExpression ',' ElementTypeList
+    function parseArrayType() {
+        var elements, startIndex = index - 1, restStartIndex;
+        consume(Token.LBRACK, 'ArrayType should start with [');
+        elements = [];
+        while (token !== Token.RBRACK) {
+            if (token === Token.REST) {
+                restStartIndex = index - 3;
+                consume(Token.REST);
+                elements.push(maybeAddRange({
+                    type: Syntax.RestType,
+                    expression: parseTypeExpression()
+                }, [restStartIndex, previous]));
+                break;
+            } else {
+                elements.push(parseTypeExpression());
+            }
+            if (token !== Token.RBRACK) {
+                expect(Token.COMMA);
+            }
+        }
+        expect(Token.RBRACK);
+        return maybeAddRange({
+            type: Syntax.ArrayType,
+            elements: elements
+        }, [startIndex, previous]);
+    }
+
+    function parseFieldName() {
+        var v = value;
+        if (token === Token.NAME || token === Token.STRING) {
+            next();
+            return v;
+        }
+
+        if (token === Token.NUMBER) {
+            consume(Token.NUMBER);
+            return String(v);
+        }
+
+        utility.throwError('unexpected token');
+    }
+
+    // FieldType :=
+    //     FieldName
+    //   | FieldName ':' TypeExpression
+    //
+    // FieldName :=
+    //     NameExpression
+    //   | StringLiteral
+    //   | NumberLiteral
+    //   | ReservedIdentifier
+    function parseFieldType() {
+        var key, rangeStart = previous;
+
+        key = parseFieldName();
+        if (token === Token.COLON) {
+            consume(Token.COLON);
+            return maybeAddRange({
+                type: Syntax.FieldType,
+                key: key,
+                value: parseTypeExpression()
+            }, [rangeStart, previous]);
+        }
+        return maybeAddRange({
+            type: Syntax.FieldType,
+            key: key,
+            value: null
+        }, [rangeStart, previous]);
+    }
+
+    // RecordType := '{' FieldTypeList '}'
+    //
+    // FieldTypeList :=
+    //     <<empty>>
+    //   | FieldType
+    //   | FieldType ',' FieldTypeList
+    function parseRecordType() {
+        var fields, rangeStart = index - 1, rangeEnd;
+
+        consume(Token.LBRACE, 'RecordType should start with {');
+        fields = [];
+        if (token === Token.COMMA) {
+            consume(Token.COMMA);
+        } else {
+            while (token !== Token.RBRACE) {
+                fields.push(parseFieldType());
+                if (token !== Token.RBRACE) {
+                    expect(Token.COMMA);
+                }
+            }
+        }
+        rangeEnd = index;
+        expect(Token.RBRACE);
+        return maybeAddRange({
+            type: Syntax.RecordType,
+            fields: fields
+        }, [rangeStart, rangeEnd]);
+    }
+
+    // NameExpression :=
+    //    Identifier
+    //  | TagIdentifier ':' Identifier
+    //
+    // Tag identifier is one of "module", "external" or "event"
+    // Identifier is the same as Token.NAME, including any dots, something like
+    // namespace.module.MyClass
+    function parseNameExpression() {
+        var name = value, rangeStart = index - name.length;
+        expect(Token.NAME);
+
+        if (token === Token.COLON && (
+                name === 'module' ||
+                name === 'external' ||
+                name === 'event')) {
+            consume(Token.COLON);
+            name += ':' + value;
+            expect(Token.NAME);
+        }
+
+        return maybeAddRange({
+            type: Syntax.NameExpression,
+            name: name
+        }, [rangeStart, previous]);
+    }
+
+    // TypeExpressionList :=
+    //     TopLevelTypeExpression
+    //   | TopLevelTypeExpression ',' TypeExpressionList
+    function parseTypeExpressionList() {
+        var elements = [];
+
+        elements.push(parseTop());
+        while (token === Token.COMMA) {
+            consume(Token.COMMA);
+            elements.push(parseTop());
+        }
+        return elements;
+    }
+
+    // TypeName :=
+    //     NameExpression
+    //   | NameExpression TypeApplication
+    //
+    // TypeApplication :=
+    //     '.<' TypeExpressionList '>'
+    //   | '<' TypeExpressionList '>'   // this is extension of doctrine
+    function parseTypeName() {
+        var expr, applications, startIndex = index - value.length;
+
+        expr = parseNameExpression();
+        if (token === Token.DOT_LT || token === Token.LT) {
+            next();
+            applications = parseTypeExpressionList();
+            expect(Token.GT);
+            return maybeAddRange({
+                type: Syntax.TypeApplication,
+                expression: expr,
+                applications: applications
+            }, [startIndex, previous]);
+        }
+        return expr;
+    }
+
+    // ResultType :=
+    //     <<empty>>
+    //   | ':' void
+    //   | ':' TypeExpression
+    //
+    // BNF is above
+    // but, we remove <<empty>> pattern, so token is always TypeToken::COLON
+    function parseResultType() {
+        consume(Token.COLON, 'ResultType should start with :');
+        if (token === Token.NAME && value === 'void') {
+            consume(Token.NAME);
+            return {
+                type: Syntax.VoidLiteral
+            };
+        }
+        return parseTypeExpression();
+    }
+
+    // ParametersType :=
+    //     RestParameterType
+    //   | NonRestParametersType
+    //   | NonRestParametersType ',' RestParameterType
+    //
+    // RestParameterType :=
+    //     '...'
+    //     '...' Identifier
+    //
+    // NonRestParametersType :=
+    //     ParameterType ',' NonRestParametersType
+    //   | ParameterType
+    //   | OptionalParametersType
+    //
+    // OptionalParametersType :=
+    //     OptionalParameterType
+    //   | OptionalParameterType, OptionalParametersType
+    //
+    // OptionalParameterType := ParameterType=
+    //
+    // ParameterType := TypeExpression | Identifier ':' TypeExpression
+    //
+    // Identifier is "new" or "this"
+    function parseParametersType() {
+        var params = [], optionalSequence = false, expr, rest = false, startIndex, restStartIndex = index - 3, nameStartIndex;
+
+        while (token !== Token.RPAREN) {
+            if (token === Token.REST) {
+                // RestParameterType
+                consume(Token.REST);
+                rest = true;
+            }
+
+            startIndex = previous;
+
+            expr = parseTypeExpression();
+            if (expr.type === Syntax.NameExpression && token === Token.COLON) {
+                nameStartIndex = previous - expr.name.length;
+                // Identifier ':' TypeExpression
+                consume(Token.COLON);
+                expr = maybeAddRange({
+                    type: Syntax.ParameterType,
+                    name: expr.name,
+                    expression: parseTypeExpression()
+                }, [nameStartIndex, previous]);
+            }
+            if (token === Token.EQUAL) {
+                consume(Token.EQUAL);
+                expr = maybeAddRange({
+                    type: Syntax.OptionalType,
+                    expression: expr
+                }, [startIndex, previous]);
+                optionalSequence = true;
+            } else {
+                if (optionalSequence) {
+                    utility.throwError('unexpected token');
+                }
+            }
+            if (rest) {
+                expr = maybeAddRange({
+                    type: Syntax.RestType,
+                    expression: expr
+                }, [restStartIndex, previous]);
+            }
+            params.push(expr);
+            if (token !== Token.RPAREN) {
+                expect(Token.COMMA);
+            }
+        }
+        return params;
+    }
+
+    // FunctionType := 'function' FunctionSignatureType
+    //
+    // FunctionSignatureType :=
+    //   | TypeParameters '(' ')' ResultType
+    //   | TypeParameters '(' ParametersType ')' ResultType
+    //   | TypeParameters '(' 'this' ':' TypeName ')' ResultType
+    //   | TypeParameters '(' 'this' ':' TypeName ',' ParametersType ')' ResultType
+    function parseFunctionType() {
+        var isNew, thisBinding, params, result, fnType, startIndex = index - value.length;
+        utility.assert(token === Token.NAME && value === 'function', 'FunctionType should start with \'function\'');
+        consume(Token.NAME);
+
+        // Google Closure Compiler is not implementing TypeParameters.
+        // So we do not. if we don't get '(', we see it as error.
+        expect(Token.LPAREN);
+
+        isNew = false;
+        params = [];
+        thisBinding = null;
+        if (token !== Token.RPAREN) {
+            // ParametersType or 'this'
+            if (token === Token.NAME &&
+                    (value === 'this' || value === 'new')) {
+                // 'this' or 'new'
+                // 'new' is Closure Compiler extension
+                isNew = value === 'new';
+                consume(Token.NAME);
+                expect(Token.COLON);
+                thisBinding = parseTypeName();
+                if (token === Token.COMMA) {
+                    consume(Token.COMMA);
+                    params = parseParametersType();
+                }
+            } else {
+                params = parseParametersType();
+            }
+        }
+
+        expect(Token.RPAREN);
+
+        result = null;
+        if (token === Token.COLON) {
+            result = parseResultType();
+        }
+
+        fnType = maybeAddRange({
+            type: Syntax.FunctionType,
+            params: params,
+            result: result
+        }, [startIndex, previous]);
+        if (thisBinding) {
+            // avoid adding null 'new' and 'this' properties
+            fnType['this'] = thisBinding;
+            if (isNew) {
+                fnType['new'] = true;
+            }
+        }
+        return fnType;
+    }
+
+    // BasicTypeExpression :=
+    //     '*'
+    //   | 'null'
+    //   | 'undefined'
+    //   | TypeName
+    //   | FunctionType
+    //   | UnionType
+    //   | RecordType
+    //   | ArrayType
+    function parseBasicTypeExpression() {
+        var context, startIndex;
+        switch (token) {
+        case Token.STAR:
+            consume(Token.STAR);
+            return maybeAddRange({
+                type: Syntax.AllLiteral
+            }, [previous - 1, previous]);
+
+        case Token.LPAREN:
+            return parseUnionType();
+
+        case Token.LBRACK:
+            return parseArrayType();
+
+        case Token.LBRACE:
+            return parseRecordType();
+
+        case Token.NAME:
+            startIndex = index - value.length;
+
+            if (value === 'null') {
+                consume(Token.NAME);
+                return maybeAddRange({
+                    type: Syntax.NullLiteral
+                }, [startIndex, previous]);
+            }
+
+            if (value === 'undefined') {
+                consume(Token.NAME);
+                return maybeAddRange({
+                    type: Syntax.UndefinedLiteral
+                }, [startIndex, previous]);
+            }
+
+            if (value === 'true' || value === 'false') {
+                consume(Token.NAME);
+                return maybeAddRange({
+                    type: Syntax.BooleanLiteralType,
+                    value: value === 'true'
+                }, [startIndex, previous]);
+            }
+
+            context = Context.save();
+            if (value === 'function') {
+                try {
+                    return parseFunctionType();
+                } catch (e) {
+                    context.restore();
+                }
+            }
+
+            return parseTypeName();
+
+        case Token.STRING:
+            next();
+            return maybeAddRange({
+                type: Syntax.StringLiteralType,
+                value: value
+            }, [previous - value.length - 2, previous]);
+
+        case Token.NUMBER:
+            next();
+            return maybeAddRange({
+                type: Syntax.NumericLiteralType,
+                value: value
+            }, [previous - String(value).length, previous]);
+
+        default:
+            utility.throwError('unexpected token');
+        }
+    }
+
+    // TypeExpression :=
+    //     BasicTypeExpression
+    //   | '?' BasicTypeExpression
+    //   | '!' BasicTypeExpression
+    //   | BasicTypeExpression '?'
+    //   | BasicTypeExpression '!'
+    //   | '?'
+    //   | BasicTypeExpression '[]'
+    function parseTypeExpression() {
+        var expr, rangeStart;
+
+        if (token === Token.QUESTION) {
+            rangeStart = index - 1;
+            consume(Token.QUESTION);
+            if (token === Token.COMMA || token === Token.EQUAL || token === Token.RBRACE ||
+                    token === Token.RPAREN || token === Token.PIPE || token === Token.EOF ||
+                    token === Token.RBRACK || token === Token.GT) {
+                return maybeAddRange({
+                    type: Syntax.NullableLiteral
+                }, [rangeStart, previous]);
+            }
+            return maybeAddRange({
+                type: Syntax.NullableType,
+                expression: parseBasicTypeExpression(),
+                prefix: true
+            }, [rangeStart, previous]);
+        } else if (token === Token.BANG) {
+            rangeStart = index - 1;
+            consume(Token.BANG);
+            return maybeAddRange({
+                type: Syntax.NonNullableType,
+                expression: parseBasicTypeExpression(),
+                prefix: true
+            }, [rangeStart, previous]);
+        } else {
+            rangeStart = previous;
+        }
+
+        expr = parseBasicTypeExpression();
+        if (token === Token.BANG) {
+            consume(Token.BANG);
+            return maybeAddRange({
+                type: Syntax.NonNullableType,
+                expression: expr,
+                prefix: false
+            }, [rangeStart, previous]);
+        }
+
+        if (token === Token.QUESTION) {
+            consume(Token.QUESTION);
+            return maybeAddRange({
+                type: Syntax.NullableType,
+                expression: expr,
+                prefix: false
+            }, [rangeStart, previous]);
+        }
+
+        if (token === Token.LBRACK) {
+            consume(Token.LBRACK);
+            expect(Token.RBRACK, 'expected an array-style type declaration (' + value + '[])');
+            return maybeAddRange({
+                type: Syntax.TypeApplication,
+                expression: maybeAddRange({
+                    type: Syntax.NameExpression,
+                    name: 'Array'
+                }, [rangeStart, previous]),
+                applications: [expr]
+            }, [rangeStart, previous]);
+        }
+
+        return expr;
+    }
+
+    // TopLevelTypeExpression :=
+    //      TypeExpression
+    //    | TypeUnionList
+    //
+    // This rule is Google Closure Compiler extension, not ES4
+    // like,
+    //   { number | string }
+    // If strict to ES4, we should write it as
+    //   { (number|string) }
+    function parseTop() {
+        var expr, elements;
+
+        expr = parseTypeExpression();
+        if (token !== Token.PIPE) {
+            return expr;
+        }
+
+        elements = [expr];
+        consume(Token.PIPE);
+        while (true) {
+            elements.push(parseTypeExpression());
+            if (token !== Token.PIPE) {
+                break;
+            }
+            consume(Token.PIPE);
+        }
+
+        return maybeAddRange({
+            type: Syntax.UnionType,
+            elements: elements
+        }, [0, index]);
+    }
+
+    function parseTopParamType() {
+        var expr;
+
+        if (token === Token.REST) {
+            consume(Token.REST);
+            return maybeAddRange({
+                type: Syntax.RestType,
+                expression: parseTop()
+            }, [0, index]);
+        }
+
+        expr = parseTop();
+        if (token === Token.EQUAL) {
+            consume(Token.EQUAL);
+            return maybeAddRange({
+                type: Syntax.OptionalType,
+                expression: expr
+            }, [0, index]);
+        }
+
+        return expr;
+    }
+
+    function parseType(src, opt) {
+        var expr;
+
+        source = src;
+        length = source.length;
+        index = 0;
+        previous = 0;
+        addRange = opt && opt.range;
+        rangeOffset = opt && opt.startIndex || 0;
+
+        next();
+        expr = parseTop();
+
+        if (opt && opt.midstream) {
+            return {
+                expression: expr,
+                index: previous
+            };
+        }
+
+        if (token !== Token.EOF) {
+            utility.throwError('not reach to EOF');
+        }
+
+        return expr;
+    }
+
+    function parseParamType(src, opt) {
+        var expr;
+
+        source = src;
+        length = source.length;
+        index = 0;
+        previous = 0;
+        addRange = opt && opt.range;
+        rangeOffset = opt && opt.startIndex || 0;
+
+        next();
+        expr = parseTopParamType();
+
+        if (opt && opt.midstream) {
+            return {
+                expression: expr,
+                index: previous
+            };
+        }
+
+        if (token !== Token.EOF) {
+            utility.throwError('not reach to EOF');
+        }
+
+        return expr;
+    }
+
+    function stringifyImpl(node, compact, topLevel) {
+        var result, i, iz;
+
+        switch (node.type) {
+        case Syntax.NullableLiteral:
+            result = '?';
+            break;
+
+        case Syntax.AllLiteral:
+            result = '*';
+            break;
+
+        case Syntax.NullLiteral:
+            result = 'null';
+            break;
+
+        case Syntax.UndefinedLiteral:
+            result = 'undefined';
+            break;
+
+        case Syntax.VoidLiteral:
+            result = 'void';
+            break;
+
+        case Syntax.UnionType:
+            if (!topLevel) {
+                result = '(';
+            } else {
+                result = '';
+            }
+
+            for (i = 0, iz = node.elements.length; i < iz; ++i) {
+                result += stringifyImpl(node.elements[i], compact);
+                if ((i + 1) !== iz) {
+                    result += compact ? '|' : ' | ';
+                }
+            }
+
+            if (!topLevel) {
+                result += ')';
+            }
+            break;
+
+        case Syntax.ArrayType:
+            result = '[';
+            for (i = 0, iz = node.elements.length; i < iz; ++i) {
+                result += stringifyImpl(node.elements[i], compact);
+                if ((i + 1) !== iz) {
+                    result += compact ? ',' : ', ';
+                }
+            }
+            result += ']';
+            break;
+
+        case Syntax.RecordType:
+            result = '{';
+            for (i = 0, iz = node.fields.length; i < iz; ++i) {
+                result += stringifyImpl(node.fields[i], compact);
+                if ((i + 1) !== iz) {
+                    result += compact ? ',' : ', ';
+                }
+            }
+            result += '}';
+            break;
+
+        case Syntax.FieldType:
+            if (node.value) {
+                result = node.key + (compact ? ':' : ': ') + stringifyImpl(node.value, compact);
+            } else {
+                result = node.key;
+            }
+            break;
+
+        case Syntax.FunctionType:
+            result = compact ? 'function(' : 'function (';
+
+            if (node['this']) {
+                if (node['new']) {
+                    result += (compact ? 'new:' : 'new: ');
+                } else {
+                    result += (compact ? 'this:' : 'this: ');
+                }
+
+                result += stringifyImpl(node['this'], compact);
+
+                if (node.params.length !== 0) {
+                    result += compact ? ',' : ', ';
+                }
+            }
+
+            for (i = 0, iz = node.params.length; i < iz; ++i) {
+                result += stringifyImpl(node.params[i], compact);
+                if ((i + 1) !== iz) {
+                    result += compact ? ',' : ', ';
+                }
+            }
+
+            result += ')';
+
+            if (node.result) {
+                result += (compact ? ':' : ': ') + stringifyImpl(node.result, compact);
+            }
+            break;
+
+        case Syntax.ParameterType:
+            result = node.name + (compact ? ':' : ': ') + stringifyImpl(node.expression, compact);
+            break;
+
+        case Syntax.RestType:
+            result = '...';
+            if (node.expression) {
+                result += stringifyImpl(node.expression, compact);
+            }
+            break;
+
+        case Syntax.NonNullableType:
+            if (node.prefix) {
+                result = '!' + stringifyImpl(node.expression, compact);
+            } else {
+                result = stringifyImpl(node.expression, compact) + '!';
+            }
+            break;
+
+        case Syntax.OptionalType:
+            result = stringifyImpl(node.expression, compact) + '=';
+            break;
+
+        case Syntax.NullableType:
+            if (node.prefix) {
+                result = '?' + stringifyImpl(node.expression, compact);
+            } else {
+                result = stringifyImpl(node.expression, compact) + '?';
+            }
+            break;
+
+        case Syntax.NameExpression:
+            result = node.name;
+            break;
+
+        case Syntax.TypeApplication:
+            result = stringifyImpl(node.expression, compact) + '.<';
+            for (i = 0, iz = node.applications.length; i < iz; ++i) {
+                result += stringifyImpl(node.applications[i], compact);
+                if ((i + 1) !== iz) {
+                    result += compact ? ',' : ', ';
+                }
+            }
+            result += '>';
+            break;
+
+        case Syntax.StringLiteralType:
+            result = '"' + node.value + '"';
+            break;
+
+        case Syntax.NumericLiteralType:
+            result = String(node.value);
+            break;
+
+        case Syntax.BooleanLiteralType:
+            result = String(node.value);
+            break;
+
+        default:
+            utility.throwError('Unknown type ' + node.type);
+        }
+
+        return result;
+    }
+
+    function stringify(node, options) {
+        if (options == null) {
+            options = {};
+        }
+        return stringifyImpl(node, options.compact, options.topLevel);
+    }
+
+    exports.parseType = parseType;
+    exports.parseParamType = parseParamType;
+    exports.stringify = stringify;
+    exports.Syntax = Syntax;
+}());
+/* vim: set sw=4 ts=4 et tw=80 : */
Index: frontend/node_modules/doctrine/lib/utility.js
===================================================================
--- frontend/node_modules/doctrine/lib/utility.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/doctrine/lib/utility.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,35 @@
+/*
+ * @fileoverview Utilities for Doctrine
+ * @author Yusuke Suzuki <utatane.tea@gmail.com>
+ */
+
+
+(function () {
+    'use strict';
+
+    var VERSION;
+
+    VERSION = require('../package.json').version;
+    exports.VERSION = VERSION;
+
+    function DoctrineError(message) {
+        this.name = 'DoctrineError';
+        this.message = message;
+    }
+    DoctrineError.prototype = (function () {
+        var Middle = function () { };
+        Middle.prototype = Error.prototype;
+        return new Middle();
+    }());
+    DoctrineError.prototype.constructor = DoctrineError;
+    exports.DoctrineError = DoctrineError;
+
+    function throwError(message) {
+        throw new DoctrineError(message);
+    }
+    exports.throwError = throwError;
+
+    exports.assert = require('assert');
+}());
+
+/* vim: set sw=4 ts=4 et tw=80 : */
Index: frontend/node_modules/doctrine/package.json
===================================================================
--- frontend/node_modules/doctrine/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/doctrine/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,58 @@
+{
+  "name": "doctrine",
+  "description": "JSDoc parser",
+  "homepage": "https://github.com/eslint/doctrine",
+  "main": "lib/doctrine.js",
+  "version": "3.0.0",
+  "engines": {
+    "node": ">=6.0.0"
+  },
+  "directories": {
+    "lib": "./lib"
+  },
+  "files": [
+    "lib"
+  ],
+  "maintainers": [
+    {
+      "name": "Nicholas C. Zakas",
+      "email": "nicholas+npm@nczconsulting.com",
+      "web": "https://www.nczonline.net"
+    },
+    {
+      "name": "Yusuke Suzuki",
+      "email": "utatane.tea@gmail.com",
+      "web": "https://github.com/Constellation"
+    }
+  ],
+  "repository": "eslint/doctrine",
+  "devDependencies": {
+    "coveralls": "^3.0.1",
+    "dateformat": "^1.0.11",
+    "eslint": "^1.10.3",
+    "eslint-release": "^1.0.0",
+    "linefix": "^0.1.1",
+    "mocha": "^3.4.2",
+    "npm-license": "^0.3.1",
+    "nyc": "^10.3.2",
+    "semver": "^5.0.3",
+    "shelljs": "^0.5.3",
+    "shelljs-nodecli": "^0.1.1",
+    "should": "^5.0.1"
+  },
+  "license": "Apache-2.0",
+  "scripts": {
+    "pretest": "npm run lint",
+    "test": "nyc mocha",
+    "coveralls": "nyc report --reporter=text-lcov | coveralls",
+    "lint": "eslint lib/",
+    "generate-release": "eslint-generate-release",
+    "generate-alpharelease": "eslint-generate-prerelease alpha",
+    "generate-betarelease": "eslint-generate-prerelease beta",
+    "generate-rcrelease": "eslint-generate-prerelease rc",
+    "publish-release": "eslint-publish-release"
+  },
+  "dependencies": {
+    "esutils": "^2.0.2"
+  }
+}
