[
  {
    "path": ".eslintrc.json",
    "content": "{\n  \"extends\": [\n    \"plugin:@typescript-eslint/eslint-recommended\",\n    \"plugin:@typescript-eslint/recommended\"\n  ],\n  \"parser\": \"@typescript-eslint/parser\",\n  \"plugins\": [\"@typescript-eslint\"],\n  \"parserOptions\": {\n    \"ecmaVersion\": 2017,\n    \"sourceType\": \"module\"\n  },\n  \"rules\": {\n    \"@typescript-eslint/no-non-null-assertion\": \"off\",\n    \"no-trailing-spaces\": \"error\",\n    \"eol-last\": \"error\"\n  },\n  \"env\": {\n    \"node\": true,\n    \"jasmine\": true\n  }\n}"
  },
  {
    "path": ".gitignore",
    "content": "node_modules/\ndist/\n.DS_Store\nwq\n"
  },
  {
    "path": ".npmignore",
    "content": "tsconfig.json\nsrc\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# How to Contribute\n\nWe'd love to accept your patches and contributions to this project. There are\njust a few small guidelines you need to follow.\n\n## Contributor License Agreement\n\nContributions to this project must be accompanied by a Contributor License\nAgreement. You (or your employer) retain the copyright to your contribution,\nthis simply gives us permission to use and redistribute your contributions as\npart of the project. Head over to <https://cla.developers.google.com/> to see\nyour current agreements on file or to sign a new one.\n\nYou generally only need to submit a CLA once, so if you've already submitted one\n(even if it was for a different project), you probably don't need to do it\nagain.\n\n## Code reviews\n\nAll submissions, including submissions by project members, require review. We\nuse GitHub pull requests for this purpose. Consult\n[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more\ninformation on using pull requests.\n"
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n   "
  },
  {
    "path": "README.md",
    "content": "# An ADB Implementation using WebUSB\n\nThis project is a TypeScript implementation of the Android Debug Bridge(ADB) protocol over WebUSB.\nThe implementation inspired on the [webadb.js][1], with the main difference being that\nimplementation supports multiple concurrent streams.\n\nThis is not an exhaustive implementation of the protocol and hasn't been tested on a wide range of\ndevices.\n\nA non-exhaustive list of things that are not implemented:\n\n- `STAT`: reads stats from the Android filesystem (file size, mode and time).\n\n## Usage\n\n### Connecting to a device\n```typescript\n  const options: Options = {\n    debug: true,\n    useChecksum: false,\n    dump: false,\n    keySize: 2048,\n  };\n\n  const transport = await WebUsbTransport.open(options);\n  const adbClient = new AdbClient(transport, options, keyStore);\n  await adbClient.connect();\n```\n\n### Downloading a file from the device (adb pull)\n```typescript\n  const result: Blob = await adbClient.pull('/sdcard/my-video.mp4');\n```\n\n### Sending shell commands\n```typescript\n  const result: string = await adbClient.shell('uname -a');\n```\n\n### Interactive shell\n```typescript\n  const callback = (output: string) => {\n    console.log('server: ' + output);\n  };\n  const shell: Shell = await adbClient.interactiveShell(callback);\n  await shell.write('ls /sdcard\\n');\n  await shell.close();\n```\n\n## Related Documents\n- https://github.com/webadb/webadb.js\n- https://github.com/cstyan/adbDocumentation\n- https://android.googlesource.com/platform/system/core/+/master/adb/\n\n## Contributing\n\nSee [CONTRIBUTING](./CONTRIBUTING.md) for more.\n\n## License\n\nSee [LICENSE](./LICENSE) for more.\n\n## Disclaimer\n\nThis is not a Google product.\n\n[1]: https://github.com/webadb/webadb.js\n"
  },
  {
    "path": "demo/.firebaserc",
    "content": "{\n  \"projects\": {\n    \"default\": \"screenrecord-bandarra-me\"\n  }\n}\n"
  },
  {
    "path": "demo/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\n\n# Firebase cache\n.firebase/\n\n# Firebase config\n\n# Uncomment this if you'd like others to create their own Firebase project.\n# For a team working on the same Firebase project(s), it is recommended to leave\n# it commented so all members can deploy to the same project(s) in .firebaserc.\n# .firebaserc\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n"
  },
  {
    "path": "demo/firebase.json",
    "content": "{\n  \"hosting\": {\n    \"public\": \"public\",\n    \"ignore\": [\n      \"firebase.json\",\n      \"**/.*\",\n      \"**/node_modules/**\"\n    ]\n  }\n}\n"
  },
  {
    "path": "demo/package.json",
    "content": "{\n  \"name\": \"demo\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"private\": true,\n  \"scripts\": {\n    \"build\": \"tsc\",\n    \"dev\": \"webpack serve --mode=development\",\n    \"package\": \"webpack --mode=production && workbox generateSW\",\n    \"serve\": \"serve\"\n  },\n  \"author\": \"André Cipriani Bandarra\",\n  \"license\": \"Apache-2.0\",\n  \"devDependencies\": {\n    \"serve\": \"^14.2.5\",\n    \"ts-loader\": \"^9.5.4\",\n    \"typescript\": \"^5.9.3\",\n    \"webpack\": \"^5.105.2\",\n    \"webpack-cli\": \"^6.0.1\",\n    \"webpack-dev-server\": \"^5.2.3\",\n    \"workbox-cli\": \"^7.4.0\"\n  },\n  \"dependencies\": {\n    \"wadb\": \"file:../\"\n  }\n}\n"
  },
  {
    "path": "demo/public/404.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    <title>Page Not Found</title>\n\n    <style media=\"screen\">\n      body { background: #ECEFF1; color: rgba(0,0,0,0.87); font-family: Roboto, Helvetica, Arial, sans-serif; margin: 0; padding: 0; }\n      #message { background: white; max-width: 360px; margin: 100px auto 16px; padding: 32px 24px 16px; border-radius: 3px; }\n      #message h3 { color: #888; font-weight: normal; font-size: 16px; margin: 16px 0 12px; }\n      #message h2 { color: #ffa100; font-weight: bold; font-size: 16px; margin: 0 0 8px; }\n      #message h1 { font-size: 22px; font-weight: 300; color: rgba(0,0,0,0.6); margin: 0 0 16px;}\n      #message p { line-height: 140%; margin: 16px 0 24px; font-size: 14px; }\n      #message a { display: block; text-align: center; background: #039be5; text-transform: uppercase; text-decoration: none; color: white; padding: 16px; border-radius: 4px; }\n      #message, #message a { box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); }\n      #load { color: rgba(0,0,0,0.4); text-align: center; font-size: 13px; }\n      @media (max-width: 600px) {\n        body, #message { margin-top: 0; background: white; box-shadow: none; }\n        body { border-top: 16px solid #ffa100; }\n      }\n    </style>\n  </head>\n  <body>\n    <div id=\"message\">\n      <h2>404</h2>\n      <h1>Page Not Found</h1>\n      <p>The specified file was not found on this website. Please check the URL for mistakes and try again.</p>\n      <h3>Why am I seeing this?</h3>\n      <p>This page was generated by the Firebase Command-Line Interface. To modify it, edit the <code>404.html</code> file in your project's configured <code>public</code> directory.</p>\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "demo/public/index.html",
    "content": "<!--\n Copyright 2020 Google Inc. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<!doctype html>\n<html>\n  <head>\n    <meta name=\"viewport\" content=\"initial-scale=1, maximum-scale=1\">\n    <meta charset=\"utf-8\">\n    <title>WebADB Screenrecord</title>\n    <script src=\"js/screenrecord.bundle.js\" defer></script>\n    <style>\n      html {\n        font-family: 'Roboto', sans-serif;\n        background-color: lightsteelblue;\n        box-sizing: border-box;\n        min-width: 320px;\n      }\n\n      body {\n        margin: 0;\n      }\n\n      header, .status {\n        box-sizing: border-box;\n        box-shadow: 0px 2px 4px #999999;\n      }\n\n      header > h1, .status > div, content > div {\n        margin: 0px auto;\n        max-width: 900px;\n        padding: 0px 16px;\n        box-sizing: border-box;\n      }\n\n      header {\n        background-color: #789ac7;\n      }\n      \n      header > h1 {\n        height: 56px;\n        padding: 8px 16px;\n      }\n\n      .status {\n        height: 28px;\n        background-color: steelblue;\n        font-size: 12px;\n        padding-top: 6px;\n        text-transform: uppercase;\n        margin-bottom: 16px;\n      }\n\n      h2 {\n        margin: 16px 0 8px 0;\n      }\n\n      .video-container > div {\n        background-color: #222222;\n        width: 100%;\n        margin: 0px auto;        \n      }\n\n      video, #screenshot {\n        width: 100%;\n        max-width: 100%;\n        max-height: 100%;\n      } \n      \n      .video-container {\n        margin-top: 16px;\n        margin-bottom: 16px;\n        width: 100%;\n      }\n\n      .button {\n        width: 145px;\n        height: 30px;\n        text-transform: uppercase;\n        padding-left: 20px;\n      }\n\n      #connect {\n        background-image: url(./assets/phonelink.svg);\n        background-repeat: no-repeat;\n        background-position-x: 4px;\n        background-position-y: 2px;   \n      }\n\n      #disconnect {\n        background-image: url(./assets/phonelink_off.svg);\n        background-repeat: no-repeat;\n        background-position-x: 4px;\n        background-position-y: 2px;\n      }\n\n      #start {\n        background-image: url(./assets/video-start.svg);\n        background-repeat: no-repeat;\n        background-position-x: 4px;\n        background-position-y: 0px;\n      }\n\n      #stop {\n        background-image: url(./assets/video-stop.svg);\n        background-repeat: no-repeat;\n        background-position-x: 4px;\n        background-position-y: 0px;\n      }\n\n      #screencapture {\n        background-image: url(./assets/screenshot.svg);\n        background-repeat: no-repeat;\n        background-position-x: 4px;\n        background-position-y: 0px;        \n      }\n\n      /* #download {\n        background-image: url(./assets/save.svg);\n        background-repeat: no-repeat;\n        background-position-x: 4px;\n        background-position-y: 0px;\n        text-decoration: none;\n        color: black;\n        background-color: lightgrey;\n        width: 14px;\n      } */\n\n      .hidden {\n        display: none;\n      }\n    </style>\n  </head>\n  <body>\n    <header>\n      <h1>WebADB Screenrecord</h1>\n    </header>\n    <div class=\"status\">\n      <div id=\"status\">\n        Connect to a device to start\n      </div>\n    </div>            \n    <content>\n      <div class=\"buttons\">\n        <button id=\"connect\" class=\"button\">Connect</button>\n        <button id=\"disconnect\" class=\"button hidden\">Disconnect</button>\n        <button id=\"start\" class=\"button\" disabled>Record</button>\n        <button id=\"stop\" class=\"button hidden\" disabled>Stop</button>\n        <button id=\"screencapture\" class=\"button\" disabled>Screenshot</button>\n      </div>\n      <div class=\"video-container\">\n        <div>\n          <video id=\"video\" controls></video>\n          <img id=\"screenshot\" class=\"hidden\"></img>\n        </div>\n        <a id=\"download\" class='hidden' href=\"#\" download=\"recording.mp4\">Download</a>          \n      </div>\n      <div>\n        <h2>How to use:</h2>\n        <ol>\n          <li>Enable developer mode on the Android device (<a href=\"https://developer.android.com/studio/debug/dev-options\">more info</a>).</li>\n          <li>Connect the Android device to the computer with an USB cable.</li>\n          <li>Click the <em>Connect</em> button.</li>\n          <li>Accept the prompt on the Android device.</li>\n          <li>Click the <em>Record</em> or <em>Screenshot</em> button.</li>\n        </ol>\n      </div>\n  </content>\n  <script>\n    if ('serviceWorker' in navigator) {\n      window.addEventListener('load', () => {\n        navigator.serviceWorker.register('/sw.js').then(registration => {\n          console.log('SW registered: ', registration);\n        }).catch(registrationError => {\n          console.log('SW registration failed: ', registrationError);\n        });\n      });\n    }\n  </script>\n  </body>\n</html>"
  },
  {
    "path": "demo/public/interactiveshell.html",
    "content": "<!--\n Copyright 2020 Google Inc. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<!doctype html>\n<html>\n  <head>\n    <meta name=\"viewport\" content=\"initial-scale=1, maximum-scale=1\">\n    <meta charset=\"utf-8\">\n    <title>Web ADB Interactive Shell</title>    \n    <script src=\"js/interactiveshell.bundle.js\" defer></script>\n    <link href=\"https://fonts.googleapis.com/css?family=Roboto|Roboto+Mono&display=swap\" rel=\"stylesheet\">\n    <style>\n      html {\n        font-family: 'Roboto', sans-serif;\n        background-color: lightsteelblue;\n        box-sizing: border-box;\n      }\n\n      body {\n        margin: 0;\n      }\n\n      h1 {\n        height: 56px;\n        margin: 0 0 16px 0;\n        padding: 8px 16px;\n        background-color: #789ac7;\n        box-sizing: border-box;\n        box-shadow: 0px 2px 4px #999999;\n      }\n\n      h2 {\n        margin: 16px 0 8px 0;\n      }\n\n      code {\n        font-family: 'Roboto Mono', monospace;\n        width: 100%;\n        height: 70vh;\n        border: 0.5px solid;\n        border-bottom: 0px;\n        display: block;\n        background-color: black;\n        color: lightgreen;\n        overflow: auto;\n      }\n\n      input {\n        font-family: 'Roboto Mono', monospace;\n        width: 100%;\n        border: 0.5px solid;\n        border-top: 0px;\n        background-color: black;\n        color: lightgreen;\n      }\n\n      .content-wrapper {\n        padding: 8px;\n      }\n\n      code > span {\n        display: block;\n      }\n\n      .buttons {\n        height: 30px;\n      }\n\n      .button {\n        width: 130px;\n        height: 30px;\n        text-transform: uppercase;\n        padding-left: 20px;\n      }\n\n      #connect {\n        background-image: url(./assets/phonelink.svg);\n        background-repeat: no-repeat;\n        background-position-x: 4px;\n        background-position-y: 2px;   \n      }\n\n      #disconnect {\n        background-image: url(./assets/phonelink_off.svg);\n        background-repeat: no-repeat;\n        background-position-x: 4px;\n        background-position-y: 2px;\n      }\n\n      .hidden {\n        display: none;\n      }\n    </style>\n  </head>\n  <body>\n    <h1>WebADB Shell</h1>\n    <div class=\"content-wrapper\">\n      <div class=\"buttons\">\n        <button id=\"connect\" class=\"button\">Connect</button>\n        <button id=\"disconnect\" class=\"button hidden\">Disconnect</button>\n      </div>\n      <div>\n        <h2>Console</h2>\n        <code id=\"output\">\n        </code>\n        <input id=\"input\" type=\"text\"></input>\n      </div>\n  </div>\n  </body>\n</html>"
  },
  {
    "path": "demo/public/js/interactiveshell.bundle.js",
    "content": "(()=>{\"use strict\";var e={834(e,t,n){var i=this&&this.__createBinding||(Object.create?function(e,t,n,i){void 0===i&&(i=n);var s=Object.getOwnPropertyDescriptor(t,n);s&&!(\"get\"in s?!t.__esModule:s.writable||s.configurable)||(s={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,i,s)}:function(e,t,n,i){void 0===i&&(i=n),e[i]=t[n]}),s=this&&this.__exportStar||function(e,t){for(var n in e)\"default\"===n||Object.prototype.hasOwnProperty.call(t,n)||i(t,e,n)};Object.defineProperty(t,\"__esModule\",{value:!0}),s(n(620),t),s(n(449),t),s(n(540),t),s(n(432),t),s(n(16),t),s(n(908),t),s(n(870),t),s(n(858),t),s(n(980),t)},620(e,t,n){var i=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(s,o){function r(e){try{c(i.next(e))}catch(e){o(e)}}function a(e){try{c(i.throw(e))}catch(e){o(e)}}function c(e){var t;e.done?s(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};Object.defineProperty(t,\"__esModule\",{value:!0}),t.AdbClient=void 0;const s=n(858),o=n(351),r=n(449),a=n(908),c=n(16),d=n(364),u=n(681);class h{constructor(e,t,n){this.transport=e,this.options=t,this.keyStore=n,this.messageQueue=new d.AsyncBlockingQueue,this.openStreams=new Set,this.messageChannel=new s.MessageChannel(e,t,this)}registerStream(e){this.openStreams.add(e)}unregisterStream(e){this.openStreams.delete(e)}newMessage(e){const t=Array.from(this.openStreams);for(const n of t)if(n.consumeMessage(e))return;this.messageQueue.enqueue(e)}awaitMessage(){return i(this,void 0,void 0,function*(){return this.messageQueue.dequeue()})}connect(){return i(this,void 0,void 0,function*(){const e=this.options.useChecksum?16777216:16777217,t=s.Message.cnxn(e,262144,\"host::\\0\",this.options.useChecksum);let n;yield this.sendMessage(t);do{n=yield this.awaitMessage()}while(\"CNXN\"!==n.header.cmd&&\"AUTH\"!==n.header.cmd);if(\"CNXN\"===n.header.cmd){if(!n.data)throw new Error(\"Connection doesn't have data\");return r.AdbConnectionInformation.fromDataView(n.data)}if(n=yield this.doAuth(n),!n.data)throw new Error(\"Connection doesn't have data\");return r.AdbConnectionInformation.fromDataView(n.data)})}disconnect(){return i(this,void 0,void 0,function*(){this.messageChannel.close()})}shell(e){return i(this,void 0,void 0,function*(){const t=yield a.Stream.open(this,`shell:${e}`,this.options),n=yield t.read();return yield t.close(),n.dataAsString()||\"\"})}framebuffer(){return i(this,void 0,void 0,function*(){return u.Framebuffer.create(this,this.options)})}interactiveShell(e){return i(this,void 0,void 0,function*(){const t=yield a.Stream.open(this,\"shell:\",this.options);return new c.Shell(t,e)})}sync(){return i(this,void 0,void 0,function*(){return yield a.Stream.open(this,\"sync:\",this.options)})}pull(e){return i(this,void 0,void 0,function*(){const t=yield this.sync(),n=yield t.pull(e);return yield t.close(),n})}push(e,t,n,s){return i(this,void 0,void 0,function*(){const i=yield this.sync();yield i.push(e,t,n,s),yield i.close()})}doAuth(e){return i(this,void 0,void 0,function*(){if(\"AUTH\"!==e.header.cmd)throw new Error(\"Not an AUTH response\");if(1!==e.header.arg0)throw new Error(`\\n          Invalid AUTH parameter. Expected 1 and received ${e.header.arg0}`);if(!e.data)throw new Error(\"AUTH message doens't contain data\");const t=e.data.buffer,n=yield this.keyStore.loadKeys();for(const e of n){const n=yield crypto.subtle.sign(\"RSASSA-PKCS1-v1_5\",e.privateKey,t),i=s.Message.authSignature(new DataView(n),this.options.useChecksum);yield this.sendMessage(i);const o=yield this.awaitMessage();if(\"CNXN\"===o.header.cmd)return o;console.log(\"Received message \",o,\"from phone\")}const i=yield h.generateKey(this.options.dump,this.options.keySize);yield this.keyStore.saveKey(i);const o=new DataView(yield crypto.subtle.exportKey(\"spki\",i.publicKey)),r=s.Message.authPublicKey(o,this.options.useChecksum);yield this.sendMessage(r),this.options.debug&&console.log(\"Waiting for key to be accepted on the device.\");const a=yield this.awaitMessage();if(\"CNXN\"!==a.header.cmd)throw console.error(\"AUTH failed. Phone didn't accept key\",a),new Error(\"AUTH failed. Phone didn't accept key\");return a})}sendMessage(e){return i(this,void 0,void 0,function*(){yield this.messageChannel.write(e)})}static generateKey(e,t){return i(this,void 0,void 0,function*(){const n=e,i=yield crypto.subtle.generateKey({name:\"RSASSA-PKCS1-v1_5\",modulusLength:t,publicExponent:new Uint8Array([1,0,1]),hash:{name:\"SHA-1\"}},n,[\"sign\",\"verify\"]);return e&&(yield(0,o.privateKeyDump)(i)),i})}}t.AdbClient=h},449(e,t){Object.defineProperty(t,\"__esModule\",{value:!0}),t.AdbConnectionInformation=void 0;const n=\"<unkwnown>\";class i{constructor(e,t,n,i){this.productName=e,this.productDevice=t,this.productModel=n,this.features=i}static fromDataView(e){const t=(new TextDecoder).decode(e);return i.fromString(t)}static fromString(e){const t=e.indexOf(\"::\"),s=e.substring(t+2).split(\";\");let o=n,r=n,a=n,c=[];for(const e of s)e.startsWith(\"ro.product.name\")?o=e.substring(16):e.startsWith(\"ro.product.model\")?a=e.substring(17):e.startsWith(\"ro.product.device\")?r=e.substring(18):e.startsWith(\"features\")&&(c=e.substring(9).split(\",\"));return new i(o,r,a,c)}}t.AdbConnectionInformation=i},681(e,t,n){var i=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(s,o){function r(e){try{c(i.next(e))}catch(e){o(e)}}function a(e){try{c(i.throw(e))}catch(e){o(e)}}function c(e){var t;e.done?s(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};Object.defineProperty(t,\"__esModule\",{value:!0}),t.Framebuffer=void 0;const s=n(908);class o{constructor(e,t,n,i,s,o,r,a,c,d,u,h,l,f,y){this.version=e,this.bpp=t,this.colorSpace=n,this.size=i,this.width=s,this.height=o,this.redOffset=r,this.redLength=a,this.blueOffset=c,this.blueLength=d,this.greenOffset=u,this.greenLength=h,this.alphaOffset=l,this.alphaLength=f,this.imageData=y}static create(e,t){return i(this,void 0,void 0,function*(){var n;const i=yield s.Stream.open(e,\"framebuffer:\",t);let r=yield i.read();if(\"WRTE\"!==r.header.cmd)throw yield i.write(\"CLSE\"),new Error(`Expected WRTE message but received ${r.header.cmd}`);if(!r.data)throw yield i.write(\"CLSE\"),new Error(\"message doesn't contain data\");yield i.write(\"OKAY\");const a=r.data.getUint32(0,!0),c=r.data.getUint32(4,!0),d=r.data.getUint32(8,!0),u=r.data.getUint32(12,!0),h=r.data.getUint32(16,!0),l=r.data.getUint32(20,!0),f=r.data.getUint32(24,!0),y=r.data.getUint32(28,!0),g=r.data.getUint32(32,!0),w=r.data.getUint32(36,!0),v=r.data.getUint32(40,!0),p=r.data.getUint32(44,!0),m=r.data.getUint32(48,!0),b=r.data.getUint32(52,!0),E=new Uint8Array(u);let M=0,_=new Uint8Array(r.data.buffer.slice(o.BYTE_LENGTH));for(E.set(_,0),M=_.length;M<u&&(r=yield i.read(),\"CLSE\"!==r.header.cmd);){if(!r.data)throw yield i.write(\"CLSE\"),new Error(\"message doesn't contain data\");_=new Uint8Array(null===(n=r.data)||void 0===n?void 0:n.buffer),E.set(_,M),M+=_.length,yield i.write(\"OKAY\")}return yield i.close(),new o(a,c,d,u,h,l,f,y,g,w,v,p,m,b,Uint8ClampedArray.from(E))})}}t.Framebuffer=o,o.BYTE_LENGTH=56},351(e,t){var n=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(s,o){function r(e){try{c(i.next(e))}catch(e){o(e)}}function a(e){try{c(i.throw(e))}catch(e){o(e)}}function c(e){var t;e.done?s(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};function i(e,t,n){const i=t-e.length;let s=\"\";for(let e=0;e<i;e++)s+=n;return s+e}function s(e){return i(e.toString(16),2,\"0\")}function o(e){return i(e.toString(16),4,\"0\")}function r(e){return btoa(new Uint8Array(e).reduce((e,t)=>e+String.fromCharCode(t),\"\"))}Object.defineProperty(t,\"__esModule\",{value:!0}),t.toHex8=s,t.toHex16=o,t.toHex32=function(e){return i(e.toString(16),8,\"0\")},t.hexdump=function(e,t=\"\"){const n=new TextDecoder;for(let i=0;i<e.byteLength;i+=16){const r=e.byteLength-i>16?16:e.byteLength-i;let a,c=t+o(i)+\" \";for(a=0;a<r;a++)c+=\" \"+s(e.getUint8(i+a));for(;a<16;a++)c+=\"   \";c+=\" | \"+n.decode(new DataView(e.buffer,i,r)),console.log(c)}},t.toB64=r,t.privateKeyDump=function(e){return n(this,void 0,void 0,function*(){if(!e.privateKey.extractable)return void console.log(\"cannot dump the private key, it's not extractable\");const t=yield crypto.subtle.exportKey(\"pkcs8\",e.privateKey);console.log(`-----BEGIN PRIVATE KEY-----\\n${r(t)}\\n-----END PRIVATE KEY-----`)})},t.publicKeyDump=function(e){return n(this,void 0,void 0,function*(){if(!e.publicKey.extractable)return void console.log(\"cannot dump the public key, it's not extractable\");const t=yield crypto.subtle.exportKey(\"spki\",e.publicKey);console.log(`-----BEGIN PUBLIC KEY-----\\n${r(t)}'\\n-----END PUBLIC KEY-----`)})},t.encodeCmd=function(e){const t=(new TextEncoder).encode(e).buffer;return new DataView(t).getUint32(0,!0)},t.decodeCmd=function(e){const t=new TextDecoder,n=new ArrayBuffer(4);return new DataView(n).setUint32(0,e,!0),t.decode(n)}},540(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},432(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},364(e,t){var n=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(s,o){function r(e){try{c(i.next(e))}catch(e){o(e)}}function a(e){try{c(i.throw(e))}catch(e){o(e)}}function c(e){var t;e.done?s(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};Object.defineProperty(t,\"__esModule\",{value:!0}),t.AsyncBlockingQueue=t.Queue=void 0;class i{constructor(e){this.data=e}}class s{enqueue(e){const t=new i(e);this.tail&&(this.tail.next=t),this.tail=t,this.head||(this.head=this.tail)}dequeue(){if(this.isEmpty())throw new Error(\"Cannot dequeue. Queue is empty\");const e=this.head.data;return this.head=this.head.next,e}isEmpty(){return null==this.head}}t.Queue=s,t.AsyncBlockingQueue=class{constructor(){this.promiseQueue=new s,this.resolverQueue=new s}add(){const e=new Promise(e=>{this.resolverQueue.enqueue(e)});this.promiseQueue.enqueue(e)}enqueue(e){this.resolverQueue.isEmpty()&&this.add(),this.resolverQueue.dequeue()(e)}dequeue(){return n(this,void 0,void 0,function*(){return this.promiseQueue.isEmpty()&&this.add(),this.promiseQueue.dequeue()})}hasPendingPromises(){return!this.promiseQueue.isEmpty()}hasPendingResolvers(){return!this.resolverQueue.isEmpty()}}},16(e,t){var n=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(s,o){function r(e){try{c(i.next(e))}catch(e){o(e)}}function a(e){try{c(i.throw(e))}catch(e){o(e)}}function c(e){var t;e.done?s(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};Object.defineProperty(t,\"__esModule\",{value:!0}),t.Shell=void 0,t.Shell=class{constructor(e,t){this.stream=e,this.callbackFunction=t,this.textDecoder=new TextDecoder,this.textEncoder=new TextEncoder,this.messageListener=[],this.closed=!1,this.loopRead()}loopRead(){return n(this,void 0,void 0,function*(){try{let e;do{if(e=yield this.stream.read(),\"WRTE\"===e.header.cmd){this.stream.write(\"OKAY\");const t=this.textDecoder.decode(e.data);this.callbackFunction&&this.callbackFunction(t)}for(const t of this.messageListener)t(e)}while(!this.closed)}catch(e){console.error(\"loopRead crashed\",e)}this.stream.client.unregisterStream(this.stream)})}waitForMessage(e){return new Promise(t=>{const n=i=>{if(i.header.cmd===e){const e=this.messageListener.indexOf(n);this.messageListener.splice(e,1),t(i)}};this.messageListener.push(n)})}write(e){return n(this,void 0,void 0,function*(){const t=this.textEncoder.encode(e);yield this.stream.write(\"WRTE\",new DataView(t.buffer)),yield this.waitForMessage(\"OKAY\")})}close(){return n(this,void 0,void 0,function*(){this.closed=!0,yield this.write(\"CLSE\")})}}},908(e,t,n){var i=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(s,o){function r(e){try{c(i.next(e))}catch(e){o(e)}}function a(e){try{c(i.throw(e))}catch(e){o(e)}}function c(e){var t;e.done?s(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};Object.defineProperty(t,\"__esModule\",{value:!0}),t.Stream=void 0;const s=n(858),o=n(351),r=n(870),a=n(364);class c{constructor(e,t,n,i,s){this.client=e,this.service=t,this.localId=n,this.remoteId=i,this.options=s,this.messageQueue=new a.AsyncBlockingQueue}close(){return i(this,void 0,void 0,function*(){yield this.write(\"CLSE\"),this.options.debug&&(console.log(`Closed stream ${this.service}`),console.log(` local_id: 0x${(0,o.toHex32)(this.localId)}`),console.log(` remote_id: 0x${(0,o.toHex32)(this.remoteId)}`)),this.client.unregisterStream(this)})}consumeMessage(e){return 0!==e.header.arg0&&e.header.arg0===this.remoteId&&0!==e.header.arg1&&e.header.arg1===this.localId&&(this.messageQueue.enqueue(e),!0)}write(e,t){return i(this,void 0,void 0,function*(){const n=this.newMessage(e,t);yield this.client.sendMessage(n)})}read(){return i(this,void 0,void 0,function*(){return this.messageQueue.dequeue()})}sendReceive(e,t){return i(this,void 0,void 0,function*(){yield this.client.sendMessage(e);const n=yield this.read();if(n.header.cmd!==t)throw new Error(\"WRTE/SEND failed: \"+n)})}pull(e){return i(this,void 0,void 0,function*(){const t=(new TextEncoder).encode(e),n=new r.SyncFrame(\"RECV\",t.byteLength),i=this.newMessage(\"WRTE\",n.toDataView());yield this.client.sendMessage(i);const s=yield this.read();if(\"OKAY\"!==s.header.cmd)throw new Error(\"WRTE/RECV failed: \"+s);const o=this.newMessage(\"WRTE\",new DataView(t.buffer));yield this.client.sendMessage(o);const a=yield this.read();if(\"OKAY\"!==a.header.cmd)throw new Error(\"WRTE/filename failed: \"+a);const c=this.newMessage(\"OKAY\");let d=yield this.read();yield this.client.sendMessage(c);let u=r.SyncFrame.fromDataView(new DataView(d.data.buffer.slice(0,8))),h=new Uint8Array(d.data.buffer.slice(8));const l=[];for(;\"DONE\"!==u.cmd;){for(;u.byteLength>=h.byteLength;){d=yield this.read(),yield this.client.sendMessage(c);const e=h.byteLength+d.data.byteLength,t=new Uint8Array(e);t.set(h,0),t.set(new Uint8Array(d.data.buffer),h.byteLength),h=t}l.push(h.slice(0,u.byteLength).buffer),h=h.slice(u.byteLength),u=r.SyncFrame.fromDataView(new DataView(h.slice(0,8).buffer)),h=h.slice(8)}return new Blob(l)})}push(e,t,n,s){return i(this,void 0,void 0,function*(){const i=new FileReader,o=new TextEncoder,a=o.encode(t),c=new r.SyncFrame(\"SEND\",t.length+1+n.length),d=this.newMessage(\"WRTE\",c.toDataView());yield this.sendReceive(d,\"OKAY\");const u=this.newMessage(\"WRTE\",new DataView(a.buffer));yield this.sendReceive(u,\"OKAY\");const h=this.newMessage(\"WRTE\",new DataView(o.encode(\",\"+n).buffer));yield this.sendReceive(h,\"OKAY\");const l=new Promise((t,n)=>{i.onload=e=>t(e.target.result),i.onerror=n,i.readAsArrayBuffer(e)}),f=yield l,y=[];for(let e=0;e<f.byteLength;e+=s)y.push(f.slice(e,Math.min(e+s,f.byteLength)));for(const e of y){const t=new r.SyncFrame(\"DATA\",e.byteLength),n=this.newMessage(\"WRTE\",t.toDataView());yield this.sendReceive(n,\"OKAY\");const i=new DataView(e),s=this.newMessage(\"WRTE\",i);yield this.sendReceive(s,\"OKAY\")}const g=new r.SyncFrame(\"DONE\",Math.round(Date.now()/1e3)),w=this.newMessage(\"WRTE\",g.toDataView());yield this.client.sendMessage(w);const v=this.newMessage(\"OKAY\");yield this.sendReceive(v,\"OKAY\")})}newMessage(e,t){return s.Message.newMessage(e,this.localId,this.remoteId,this.options.useChecksum,t)}static open(e,t,n){return i(this,void 0,void 0,function*(){const i=c.nextId++;let r=0;const a=s.Message.open(i,r,t,n.useChecksum);let d;yield e.sendMessage(a);do{d=yield e.awaitMessage()}while(d.header.arg1!==i);if(\"OKAY\"!==d.header.cmd)throw new Error(\"OPEN Failed\");r=d.header.arg0,n.debug&&(console.log(`Opened stream ${t}`),console.log(` local_id: 0x${(0,o.toHex32)(i)}`),console.log(` remote_id: 0x${(0,o.toHex32)(r)}`));const u=new c(e,t,i,r,n);return e.registerStream(u),u})}}t.Stream=c,c.nextId=1},870(e,t,n){Object.defineProperty(t,\"__esModule\",{value:!0}),t.SyncFrame=void 0;const i=n(351);class s{constructor(e,t){this.cmd=e,this.byteLength=t}toDataView(){const e=new ArrayBuffer(8),t=(0,i.encodeCmd)(this.cmd),n=new DataView(e);return n.setUint32(0,t,!0),n.setUint32(4,this.byteLength,!0),n}static fromDataView(e){const t=(0,i.decodeCmd)(e.getUint32(0,!0)),n=e.getUint32(4,!0);return new s(t,n)}}t.SyncFrame=s},397(e,t,n){Object.defineProperty(t,\"__esModule\",{value:!0}),t.Message=void 0;const i=n(864),s=n(351);class o{constructor(e,t){this.header=e,this.data=t}dataAsString(){return this.data?(new TextDecoder).decode(this.data):null}static newMessage(e,t,n,s,r){let a=0,c=0;r&&(c=r.byteLength,s&&(a=o.checksum(r)));const d=new i.MessageHeader(e,t,n,c,a);return new o(d,r)}static open(e,t,n,i){const s=new TextEncoder,r=new DataView(s.encode(n+\"\\0\").buffer);return o.newMessage(\"OPEN\",e,t,i,r)}static cnxn(e,t,n,i){const s=new TextEncoder,r=new DataView(s.encode(n).buffer);return o.newMessage(\"CNXN\",e,t,i,r)}static authSignature(e,t){return o.newMessage(\"AUTH\",2,0,t,e)}static authPublicKey(e,t){const n=(new TextEncoder).encode((0,s.toB64)(e.buffer)+\"\\0\");return o.newMessage(\"AUTH\",3,0,t,new DataView(n.buffer))}static checksum(e){let t=0;for(let n=0;n<e.byteLength;n++)t+=e.getUint8(n);return 4294967295&t}}t.Message=o},734(e,t,n){var i=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(s,o){function r(e){try{c(i.next(e))}catch(e){o(e)}}function a(e){try{c(i.throw(e))}catch(e){o(e)}}function c(e){var t;e.done?s(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};Object.defineProperty(t,\"__esModule\",{value:!0}),t.MessageChannel=void 0;const s=n(397),o=n(864);t.MessageChannel=class{constructor(e,t,n){this.transport=e,this.options=t,this.listener=n,this.active=!0,this.readLoop()}readLoop(){return i(this,void 0,void 0,function*(){let e;do{e=yield this.read(),this.options.debug&&console.log(\"<<<\",e),this.listener.newMessage(e)}while(this.active)})}readHeader(){return i(this,void 0,void 0,function*(){const e=yield this.transport.read(24);return o.MessageHeader.parse(e,this.options.useChecksum)})}read(){return i(this,void 0,void 0,function*(){const e=yield this.readHeader();let t;return e.cmd,e.length>0&&(t=yield this.transport.read(e.length)),new s.Message(e,t)})}close(){this.active=!1}write(e){return i(this,void 0,void 0,function*(){this.options.debug&&console.log(\">>>\",e);const t=e.header.toDataView();yield this.transport.write(t.buffer),e.data&&(yield this.transport.write(e.data.buffer))})}}},864(e,t,n){Object.defineProperty(t,\"__esModule\",{value:!0}),t.MessageHeader=void 0;const i=n(351);class s{constructor(e,t,n,i,s){this.cmd=e,this.arg0=t,this.arg1=n,this.length=i,this.checksum=s}toDataView(){const e=new DataView(new ArrayBuffer(24)),t=(0,i.encodeCmd)(this.cmd),n=4294967295^t;return e.setUint32(0,t,!0),e.setUint32(4,this.arg0,!0),e.setUint32(8,this.arg1,!0),e.setUint32(12,this.length,!0),e.setUint32(16,this.checksum,!0),e.setUint32(20,n,!0),e}static parse(e,t=!1){const n=e.getUint32(0,!0),o=e.getUint32(4,!0),r=e.getUint32(8,!0),a=e.getUint32(12,!0),c=e.getUint32(16,!0);if(t&&e.byteLength>20&&-1!==(n^e.getUint32(20,!0)))throw new Error(\"magic mismatch\");return new s((0,i.decodeCmd)(n),o,r,a,c)}}t.MessageHeader=s},257(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},858(e,t,n){var i=this&&this.__createBinding||(Object.create?function(e,t,n,i){void 0===i&&(i=n);var s=Object.getOwnPropertyDescriptor(t,n);s&&!(\"get\"in s?!t.__esModule:s.writable||s.configurable)||(s={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,i,s)}:function(e,t,n,i){void 0===i&&(i=n),e[i]=t[n]}),s=this&&this.__exportStar||function(e,t){for(var n in e)\"default\"===n||Object.prototype.hasOwnProperty.call(t,n)||i(t,e,n)};Object.defineProperty(t,\"__esModule\",{value:!0}),s(n(397),t),s(n(734),t),s(n(864),t),s(n(257),t)},225(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},289(e,t,n){var i=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(s,o){function r(e){try{c(i.next(e))}catch(e){o(e)}}function a(e){try{c(i.throw(e))}catch(e){o(e)}}function c(e){var t;e.done?s(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};Object.defineProperty(t,\"__esModule\",{value:!0}),t.WebUsbTransport=void 0;const s=n(351),o={classCode:255,subclassCode:66,protocolCode:1},r={classCode:255,subclassCode:66,protocolCode:3},a=[o,r];class c{constructor(e,t,n,i,s,o=console.log){this.device=e,this.match=t,this.endpointIn=n,this.endpointOut=i,this.options=s,this.log=o}close(){return i(this,void 0,void 0,function*(){yield this.device.releaseInterface(this.match.intf.interfaceNumber),yield this.device.close()})}write(e){return i(this,void 0,void 0,function*(){this.options.dump&&(0,s.hexdump)(new DataView(e),this.endpointOut+\"==> \"),yield this.device.transferOut(this.endpointOut,e)})}read(e){return i(this,void 0,void 0,function*(){const t=yield this.device.transferIn(this.endpointIn,e);if(!t.data)throw new Error(\"Response didn't contain any data\");return t.data})}isAdb(){return null!=c.findMatch(this.device,o)}isFastboot(){return null!=c.findMatch(this.device,r)}static open(e){return i(this,void 0,void 0,function*(){const t=yield navigator.usb.requestDevice({filters:a});yield t.open();const n=this.findMatch(t,o);if(!n)throw new Error(\"Could not find an ADB device\");yield t.selectConfiguration(n.conf.configurationValue),yield t.claimInterface(n.intf.interfaceNumber);const i=c.getEndpointNum(n.alternate.endpoints,\"in\"),s=c.getEndpointNum(n.alternate.endpoints,\"out\"),r=new c(t,n,i,s,e);return e.debug&&console.log(\"Created new Transport: \",r),r})}static findMatch(e,t){for(const n of e.configurations)for(const e of n.interfaces)for(const i of e.alternates)if(t.classCode===i.interfaceClass&&t.subclassCode===i.interfaceSubclass&&t.protocolCode===i.interfaceProtocol)return{conf:n,intf:e,alternate:i};return null}static getEndpointNum(e,t,n=\"bulk\"){for(const i of e)if(i.direction===t&&i.type===n)return i.endpointNumber;throw new Error(`Cannot find ${t} endpoint`)}}t.WebUsbTransport=c},980(e,t,n){var i=this&&this.__createBinding||(Object.create?function(e,t,n,i){void 0===i&&(i=n);var s=Object.getOwnPropertyDescriptor(t,n);s&&!(\"get\"in s?!t.__esModule:s.writable||s.configurable)||(s={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,i,s)}:function(e,t,n,i){void 0===i&&(i=n),e[i]=t[n]}),s=this&&this.__exportStar||function(e,t){for(var n in e)\"default\"===n||Object.prototype.hasOwnProperty.call(t,n)||i(t,e,n)};Object.defineProperty(t,\"__esModule\",{value:!0}),s(n(225),t),s(n(289),t)}},t={};function n(i){var s=t[i];if(void 0!==s)return s.exports;var o=t[i]={exports:{}};return e[i].call(o.exports,o,o.exports,n),o.exports}n.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},n.d=(e,t)=>{for(var i in t)n.o(t,i)&&!n.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var i=n(834),s=function(e,t,n,i){return new(n||(n=Promise))(function(s,o){function r(e){try{c(i.next(e))}catch(e){o(e)}}function a(e){try{c(i.throw(e))}catch(e){o(e)}}function c(e){var t;e.done?s(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};const o=document.querySelector(\"#connect\"),r=document.querySelector(\"#disconnect\"),a=document.querySelector(\"#output\"),c=document.querySelector(\"#input\"),d={debug:!0,useChecksum:!1,dump:!1,keySize:2048},u=new class{constructor(){this.keys=[]}loadKeys(){return s(this,void 0,void 0,function*(){return this.keys})}saveKey(e){return s(this,void 0,void 0,function*(){this.keys.push(e),console.log(\"Saving Key\"+e)})}};let h=null,l=null,f=null;function y(e){const t=document.createElement(\"span\");t.innerText=e,a.appendChild(t),a.scrollTop=a.scrollHeight}o.addEventListener(\"click\",e=>s(void 0,void 0,void 0,function*(){try{h=yield i.WebUsbTransport.open(d),l=new i.AdbClient(h,d,u),yield l.connect(),f=yield l.interactiveShell(y),r.classList.toggle(\"hidden\"),o.classList.toggle(\"hidden\")}catch(e){console.error(\"Connection Failed: \",e)}})),r.addEventListener(\"click\",e=>s(void 0,void 0,void 0,function*(){try{yield null==f?void 0:f.close(),yield null==h?void 0:h.close(),h=null,l=null,f=null}catch(e){console.error(\"Error closing the connection\",e)}r.classList.toggle(\"hidden\"),o.classList.toggle(\"hidden\")})),c.addEventListener(\"keyup\",e=>{return 13!==e.keyCode||(e.preventDefault(),t=c.value,f.write(t+\"\\n\"),c.value=\"\",!1);var t})})();"
  },
  {
    "path": "demo/public/js/interactiveshell.d.ts",
    "content": "export {};\n//# sourceMappingURL=interactiveshell.d.ts.map"
  },
  {
    "path": "demo/public/js/interactiveshell.js",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\nvar __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {\n    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n    return new (P || (P = Promise))(function (resolve, reject) {\n        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n        function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n        step((generator = generator.apply(thisArg, _arguments || [])).next());\n    });\n};\nimport { AdbClient, WebUsbTransport } from 'wadb';\nconst connectButton = document.querySelector('#connect');\nconst disconnectButton = document.querySelector('#disconnect');\nconst output = document.querySelector('#output');\nconst input = document.querySelector('#input');\nclass MyKeyStore {\n    constructor() {\n        this.keys = [];\n    }\n    loadKeys() {\n        return __awaiter(this, void 0, void 0, function* () {\n            return this.keys;\n        });\n    }\n    saveKey(key) {\n        return __awaiter(this, void 0, void 0, function* () {\n            this.keys.push(key);\n            console.log('Saving Key' + key);\n        });\n    }\n}\nconst options = {\n    debug: true,\n    useChecksum: false,\n    dump: false,\n    keySize: 2048,\n};\nconst keyStore = new MyKeyStore();\nlet transport = null;\nlet adbClient = null;\nlet shell = null;\nfunction appendToCode(text) {\n    const span = document.createElement('span');\n    span.innerText = text;\n    output.appendChild(span);\n    output.scrollTop = output.scrollHeight;\n}\nfunction sendCommand(cmd) {\n    shell.write(cmd + '\\n');\n}\nconnectButton.addEventListener('click', (e) => __awaiter(void 0, void 0, void 0, function* () {\n    try {\n        transport = yield WebUsbTransport.open(options);\n        adbClient = new AdbClient(transport, options, keyStore);\n        yield adbClient.connect();\n        shell = yield adbClient.interactiveShell(appendToCode);\n        disconnectButton.classList.toggle('hidden');\n        connectButton.classList.toggle('hidden');\n    }\n    catch (e) {\n        console.error('Connection Failed: ', e);\n    }\n}));\ndisconnectButton.addEventListener('click', (e) => __awaiter(void 0, void 0, void 0, function* () {\n    try {\n        yield (shell === null || shell === void 0 ? void 0 : shell.close());\n        yield (transport === null || transport === void 0 ? void 0 : transport.close());\n        transport = null;\n        adbClient = null;\n        shell = null;\n    }\n    catch (e) {\n        console.error('Error closing the connection', e);\n    }\n    disconnectButton.classList.toggle('hidden');\n    connectButton.classList.toggle('hidden');\n}));\ninput.addEventListener('keyup', (e) => {\n    if (e.keyCode === 13) {\n        e.preventDefault();\n        sendCommand(input.value);\n        input.value = '';\n        return false;\n    }\n    return true;\n});\n//# sourceMappingURL=interactiveshell.js.map"
  },
  {
    "path": "demo/public/js/livestream.bundle.js",
    "content": "(()=>{\"use strict\";var e={834(e,t,n){var i=this&&this.__createBinding||(Object.create?function(e,t,n,i){void 0===i&&(i=n);var o=Object.getOwnPropertyDescriptor(t,n);o&&!(\"get\"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,i,o)}:function(e,t,n,i){void 0===i&&(i=n),e[i]=t[n]}),o=this&&this.__exportStar||function(e,t){for(var n in e)\"default\"===n||Object.prototype.hasOwnProperty.call(t,n)||i(t,e,n)};Object.defineProperty(t,\"__esModule\",{value:!0}),o(n(620),t),o(n(449),t),o(n(540),t),o(n(432),t),o(n(16),t),o(n(908),t),o(n(870),t),o(n(858),t),o(n(980),t)},620(e,t,n){var i=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(o,s){function r(e){try{c(i.next(e))}catch(e){s(e)}}function a(e){try{c(i.throw(e))}catch(e){s(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};Object.defineProperty(t,\"__esModule\",{value:!0}),t.AdbClient=void 0;const o=n(858),s=n(351),r=n(449),a=n(908),c=n(16),d=n(364),u=n(681);class l{constructor(e,t,n){this.transport=e,this.options=t,this.keyStore=n,this.messageQueue=new d.AsyncBlockingQueue,this.openStreams=new Set,this.messageChannel=new o.MessageChannel(e,t,this)}registerStream(e){this.openStreams.add(e)}unregisterStream(e){this.openStreams.delete(e)}newMessage(e){const t=Array.from(this.openStreams);for(const n of t)if(n.consumeMessage(e))return;this.messageQueue.enqueue(e)}awaitMessage(){return i(this,void 0,void 0,function*(){return this.messageQueue.dequeue()})}connect(){return i(this,void 0,void 0,function*(){const e=this.options.useChecksum?16777216:16777217,t=o.Message.cnxn(e,262144,\"host::\\0\",this.options.useChecksum);let n;yield this.sendMessage(t);do{n=yield this.awaitMessage()}while(\"CNXN\"!==n.header.cmd&&\"AUTH\"!==n.header.cmd);if(\"CNXN\"===n.header.cmd){if(!n.data)throw new Error(\"Connection doesn't have data\");return r.AdbConnectionInformation.fromDataView(n.data)}if(n=yield this.doAuth(n),!n.data)throw new Error(\"Connection doesn't have data\");return r.AdbConnectionInformation.fromDataView(n.data)})}disconnect(){return i(this,void 0,void 0,function*(){this.messageChannel.close()})}shell(e){return i(this,void 0,void 0,function*(){const t=yield a.Stream.open(this,`shell:${e}`,this.options),n=yield t.read();return yield t.close(),n.dataAsString()||\"\"})}framebuffer(){return i(this,void 0,void 0,function*(){return u.Framebuffer.create(this,this.options)})}interactiveShell(e){return i(this,void 0,void 0,function*(){const t=yield a.Stream.open(this,\"shell:\",this.options);return new c.Shell(t,e)})}sync(){return i(this,void 0,void 0,function*(){return yield a.Stream.open(this,\"sync:\",this.options)})}pull(e){return i(this,void 0,void 0,function*(){const t=yield this.sync(),n=yield t.pull(e);return yield t.close(),n})}push(e,t,n,o){return i(this,void 0,void 0,function*(){const i=yield this.sync();yield i.push(e,t,n,o),yield i.close()})}doAuth(e){return i(this,void 0,void 0,function*(){if(\"AUTH\"!==e.header.cmd)throw new Error(\"Not an AUTH response\");if(1!==e.header.arg0)throw new Error(`\\n          Invalid AUTH parameter. Expected 1 and received ${e.header.arg0}`);if(!e.data)throw new Error(\"AUTH message doens't contain data\");const t=e.data.buffer,n=yield this.keyStore.loadKeys();for(const e of n){const n=yield crypto.subtle.sign(\"RSASSA-PKCS1-v1_5\",e.privateKey,t),i=o.Message.authSignature(new DataView(n),this.options.useChecksum);yield this.sendMessage(i);const s=yield this.awaitMessage();if(\"CNXN\"===s.header.cmd)return s;console.log(\"Received message \",s,\"from phone\")}const i=yield l.generateKey(this.options.dump,this.options.keySize);yield this.keyStore.saveKey(i);const s=new DataView(yield crypto.subtle.exportKey(\"spki\",i.publicKey)),r=o.Message.authPublicKey(s,this.options.useChecksum);yield this.sendMessage(r),this.options.debug&&console.log(\"Waiting for key to be accepted on the device.\");const a=yield this.awaitMessage();if(\"CNXN\"!==a.header.cmd)throw console.error(\"AUTH failed. Phone didn't accept key\",a),new Error(\"AUTH failed. Phone didn't accept key\");return a})}sendMessage(e){return i(this,void 0,void 0,function*(){yield this.messageChannel.write(e)})}static generateKey(e,t){return i(this,void 0,void 0,function*(){const n=e,i=yield crypto.subtle.generateKey({name:\"RSASSA-PKCS1-v1_5\",modulusLength:t,publicExponent:new Uint8Array([1,0,1]),hash:{name:\"SHA-1\"}},n,[\"sign\",\"verify\"]);return e&&(yield(0,s.privateKeyDump)(i)),i})}}t.AdbClient=l},449(e,t){Object.defineProperty(t,\"__esModule\",{value:!0}),t.AdbConnectionInformation=void 0;const n=\"<unkwnown>\";class i{constructor(e,t,n,i){this.productName=e,this.productDevice=t,this.productModel=n,this.features=i}static fromDataView(e){const t=(new TextDecoder).decode(e);return i.fromString(t)}static fromString(e){const t=e.indexOf(\"::\"),o=e.substring(t+2).split(\";\");let s=n,r=n,a=n,c=[];for(const e of o)e.startsWith(\"ro.product.name\")?s=e.substring(16):e.startsWith(\"ro.product.model\")?a=e.substring(17):e.startsWith(\"ro.product.device\")?r=e.substring(18):e.startsWith(\"features\")&&(c=e.substring(9).split(\",\"));return new i(s,r,a,c)}}t.AdbConnectionInformation=i},681(e,t,n){var i=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(o,s){function r(e){try{c(i.next(e))}catch(e){s(e)}}function a(e){try{c(i.throw(e))}catch(e){s(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};Object.defineProperty(t,\"__esModule\",{value:!0}),t.Framebuffer=void 0;const o=n(908);class s{constructor(e,t,n,i,o,s,r,a,c,d,u,l,h,f,y){this.version=e,this.bpp=t,this.colorSpace=n,this.size=i,this.width=o,this.height=s,this.redOffset=r,this.redLength=a,this.blueOffset=c,this.blueLength=d,this.greenOffset=u,this.greenLength=l,this.alphaOffset=h,this.alphaLength=f,this.imageData=y}static create(e,t){return i(this,void 0,void 0,function*(){var n;const i=yield o.Stream.open(e,\"framebuffer:\",t);let r=yield i.read();if(\"WRTE\"!==r.header.cmd)throw yield i.write(\"CLSE\"),new Error(`Expected WRTE message but received ${r.header.cmd}`);if(!r.data)throw yield i.write(\"CLSE\"),new Error(\"message doesn't contain data\");yield i.write(\"OKAY\");const a=r.data.getUint32(0,!0),c=r.data.getUint32(4,!0),d=r.data.getUint32(8,!0),u=r.data.getUint32(12,!0),l=r.data.getUint32(16,!0),h=r.data.getUint32(20,!0),f=r.data.getUint32(24,!0),y=r.data.getUint32(28,!0),g=r.data.getUint32(32,!0),w=r.data.getUint32(36,!0),v=r.data.getUint32(40,!0),p=r.data.getUint32(44,!0),m=r.data.getUint32(48,!0),b=r.data.getUint32(52,!0),E=new Uint8Array(u);let M=0,_=new Uint8Array(r.data.buffer.slice(s.BYTE_LENGTH));for(E.set(_,0),M=_.length;M<u&&(r=yield i.read(),\"CLSE\"!==r.header.cmd);){if(!r.data)throw yield i.write(\"CLSE\"),new Error(\"message doesn't contain data\");_=new Uint8Array(null===(n=r.data)||void 0===n?void 0:n.buffer),E.set(_,M),M+=_.length,yield i.write(\"OKAY\")}return yield i.close(),new s(a,c,d,u,l,h,f,y,g,w,v,p,m,b,Uint8ClampedArray.from(E))})}}t.Framebuffer=s,s.BYTE_LENGTH=56},351(e,t){var n=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(o,s){function r(e){try{c(i.next(e))}catch(e){s(e)}}function a(e){try{c(i.throw(e))}catch(e){s(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};function i(e,t,n){const i=t-e.length;let o=\"\";for(let e=0;e<i;e++)o+=n;return o+e}function o(e){return i(e.toString(16),2,\"0\")}function s(e){return i(e.toString(16),4,\"0\")}function r(e){return btoa(new Uint8Array(e).reduce((e,t)=>e+String.fromCharCode(t),\"\"))}Object.defineProperty(t,\"__esModule\",{value:!0}),t.toHex8=o,t.toHex16=s,t.toHex32=function(e){return i(e.toString(16),8,\"0\")},t.hexdump=function(e,t=\"\"){const n=new TextDecoder;for(let i=0;i<e.byteLength;i+=16){const r=e.byteLength-i>16?16:e.byteLength-i;let a,c=t+s(i)+\" \";for(a=0;a<r;a++)c+=\" \"+o(e.getUint8(i+a));for(;a<16;a++)c+=\"   \";c+=\" | \"+n.decode(new DataView(e.buffer,i,r)),console.log(c)}},t.toB64=r,t.privateKeyDump=function(e){return n(this,void 0,void 0,function*(){if(!e.privateKey.extractable)return void console.log(\"cannot dump the private key, it's not extractable\");const t=yield crypto.subtle.exportKey(\"pkcs8\",e.privateKey);console.log(`-----BEGIN PRIVATE KEY-----\\n${r(t)}\\n-----END PRIVATE KEY-----`)})},t.publicKeyDump=function(e){return n(this,void 0,void 0,function*(){if(!e.publicKey.extractable)return void console.log(\"cannot dump the public key, it's not extractable\");const t=yield crypto.subtle.exportKey(\"spki\",e.publicKey);console.log(`-----BEGIN PUBLIC KEY-----\\n${r(t)}'\\n-----END PUBLIC KEY-----`)})},t.encodeCmd=function(e){const t=(new TextEncoder).encode(e).buffer;return new DataView(t).getUint32(0,!0)},t.decodeCmd=function(e){const t=new TextDecoder,n=new ArrayBuffer(4);return new DataView(n).setUint32(0,e,!0),t.decode(n)}},540(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},432(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},364(e,t){var n=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(o,s){function r(e){try{c(i.next(e))}catch(e){s(e)}}function a(e){try{c(i.throw(e))}catch(e){s(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};Object.defineProperty(t,\"__esModule\",{value:!0}),t.AsyncBlockingQueue=t.Queue=void 0;class i{constructor(e){this.data=e}}class o{enqueue(e){const t=new i(e);this.tail&&(this.tail.next=t),this.tail=t,this.head||(this.head=this.tail)}dequeue(){if(this.isEmpty())throw new Error(\"Cannot dequeue. Queue is empty\");const e=this.head.data;return this.head=this.head.next,e}isEmpty(){return null==this.head}}t.Queue=o,t.AsyncBlockingQueue=class{constructor(){this.promiseQueue=new o,this.resolverQueue=new o}add(){const e=new Promise(e=>{this.resolverQueue.enqueue(e)});this.promiseQueue.enqueue(e)}enqueue(e){this.resolverQueue.isEmpty()&&this.add(),this.resolverQueue.dequeue()(e)}dequeue(){return n(this,void 0,void 0,function*(){return this.promiseQueue.isEmpty()&&this.add(),this.promiseQueue.dequeue()})}hasPendingPromises(){return!this.promiseQueue.isEmpty()}hasPendingResolvers(){return!this.resolverQueue.isEmpty()}}},16(e,t){var n=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(o,s){function r(e){try{c(i.next(e))}catch(e){s(e)}}function a(e){try{c(i.throw(e))}catch(e){s(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};Object.defineProperty(t,\"__esModule\",{value:!0}),t.Shell=void 0,t.Shell=class{constructor(e,t){this.stream=e,this.callbackFunction=t,this.textDecoder=new TextDecoder,this.textEncoder=new TextEncoder,this.messageListener=[],this.closed=!1,this.loopRead()}loopRead(){return n(this,void 0,void 0,function*(){try{let e;do{if(e=yield this.stream.read(),\"WRTE\"===e.header.cmd){this.stream.write(\"OKAY\");const t=this.textDecoder.decode(e.data);this.callbackFunction&&this.callbackFunction(t)}for(const t of this.messageListener)t(e)}while(!this.closed)}catch(e){console.error(\"loopRead crashed\",e)}this.stream.client.unregisterStream(this.stream)})}waitForMessage(e){return new Promise(t=>{const n=i=>{if(i.header.cmd===e){const e=this.messageListener.indexOf(n);this.messageListener.splice(e,1),t(i)}};this.messageListener.push(n)})}write(e){return n(this,void 0,void 0,function*(){const t=this.textEncoder.encode(e);yield this.stream.write(\"WRTE\",new DataView(t.buffer)),yield this.waitForMessage(\"OKAY\")})}close(){return n(this,void 0,void 0,function*(){this.closed=!0,yield this.write(\"CLSE\")})}}},908(e,t,n){var i=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(o,s){function r(e){try{c(i.next(e))}catch(e){s(e)}}function a(e){try{c(i.throw(e))}catch(e){s(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};Object.defineProperty(t,\"__esModule\",{value:!0}),t.Stream=void 0;const o=n(858),s=n(351),r=n(870),a=n(364);class c{constructor(e,t,n,i,o){this.client=e,this.service=t,this.localId=n,this.remoteId=i,this.options=o,this.messageQueue=new a.AsyncBlockingQueue}close(){return i(this,void 0,void 0,function*(){yield this.write(\"CLSE\"),this.options.debug&&(console.log(`Closed stream ${this.service}`),console.log(` local_id: 0x${(0,s.toHex32)(this.localId)}`),console.log(` remote_id: 0x${(0,s.toHex32)(this.remoteId)}`)),this.client.unregisterStream(this)})}consumeMessage(e){return 0!==e.header.arg0&&e.header.arg0===this.remoteId&&0!==e.header.arg1&&e.header.arg1===this.localId&&(this.messageQueue.enqueue(e),!0)}write(e,t){return i(this,void 0,void 0,function*(){const n=this.newMessage(e,t);yield this.client.sendMessage(n)})}read(){return i(this,void 0,void 0,function*(){return this.messageQueue.dequeue()})}sendReceive(e,t){return i(this,void 0,void 0,function*(){yield this.client.sendMessage(e);const n=yield this.read();if(n.header.cmd!==t)throw new Error(\"WRTE/SEND failed: \"+n)})}pull(e){return i(this,void 0,void 0,function*(){const t=(new TextEncoder).encode(e),n=new r.SyncFrame(\"RECV\",t.byteLength),i=this.newMessage(\"WRTE\",n.toDataView());yield this.client.sendMessage(i);const o=yield this.read();if(\"OKAY\"!==o.header.cmd)throw new Error(\"WRTE/RECV failed: \"+o);const s=this.newMessage(\"WRTE\",new DataView(t.buffer));yield this.client.sendMessage(s);const a=yield this.read();if(\"OKAY\"!==a.header.cmd)throw new Error(\"WRTE/filename failed: \"+a);const c=this.newMessage(\"OKAY\");let d=yield this.read();yield this.client.sendMessage(c);let u=r.SyncFrame.fromDataView(new DataView(d.data.buffer.slice(0,8))),l=new Uint8Array(d.data.buffer.slice(8));const h=[];for(;\"DONE\"!==u.cmd;){for(;u.byteLength>=l.byteLength;){d=yield this.read(),yield this.client.sendMessage(c);const e=l.byteLength+d.data.byteLength,t=new Uint8Array(e);t.set(l,0),t.set(new Uint8Array(d.data.buffer),l.byteLength),l=t}h.push(l.slice(0,u.byteLength).buffer),l=l.slice(u.byteLength),u=r.SyncFrame.fromDataView(new DataView(l.slice(0,8).buffer)),l=l.slice(8)}return new Blob(h)})}push(e,t,n,o){return i(this,void 0,void 0,function*(){const i=new FileReader,s=new TextEncoder,a=s.encode(t),c=new r.SyncFrame(\"SEND\",t.length+1+n.length),d=this.newMessage(\"WRTE\",c.toDataView());yield this.sendReceive(d,\"OKAY\");const u=this.newMessage(\"WRTE\",new DataView(a.buffer));yield this.sendReceive(u,\"OKAY\");const l=this.newMessage(\"WRTE\",new DataView(s.encode(\",\"+n).buffer));yield this.sendReceive(l,\"OKAY\");const h=new Promise((t,n)=>{i.onload=e=>t(e.target.result),i.onerror=n,i.readAsArrayBuffer(e)}),f=yield h,y=[];for(let e=0;e<f.byteLength;e+=o)y.push(f.slice(e,Math.min(e+o,f.byteLength)));for(const e of y){const t=new r.SyncFrame(\"DATA\",e.byteLength),n=this.newMessage(\"WRTE\",t.toDataView());yield this.sendReceive(n,\"OKAY\");const i=new DataView(e),o=this.newMessage(\"WRTE\",i);yield this.sendReceive(o,\"OKAY\")}const g=new r.SyncFrame(\"DONE\",Math.round(Date.now()/1e3)),w=this.newMessage(\"WRTE\",g.toDataView());yield this.client.sendMessage(w);const v=this.newMessage(\"OKAY\");yield this.sendReceive(v,\"OKAY\")})}newMessage(e,t){return o.Message.newMessage(e,this.localId,this.remoteId,this.options.useChecksum,t)}static open(e,t,n){return i(this,void 0,void 0,function*(){const i=c.nextId++;let r=0;const a=o.Message.open(i,r,t,n.useChecksum);let d;yield e.sendMessage(a);do{d=yield e.awaitMessage()}while(d.header.arg1!==i);if(\"OKAY\"!==d.header.cmd)throw new Error(\"OPEN Failed\");r=d.header.arg0,n.debug&&(console.log(`Opened stream ${t}`),console.log(` local_id: 0x${(0,s.toHex32)(i)}`),console.log(` remote_id: 0x${(0,s.toHex32)(r)}`));const u=new c(e,t,i,r,n);return e.registerStream(u),u})}}t.Stream=c,c.nextId=1},870(e,t,n){Object.defineProperty(t,\"__esModule\",{value:!0}),t.SyncFrame=void 0;const i=n(351);class o{constructor(e,t){this.cmd=e,this.byteLength=t}toDataView(){const e=new ArrayBuffer(8),t=(0,i.encodeCmd)(this.cmd),n=new DataView(e);return n.setUint32(0,t,!0),n.setUint32(4,this.byteLength,!0),n}static fromDataView(e){const t=(0,i.decodeCmd)(e.getUint32(0,!0)),n=e.getUint32(4,!0);return new o(t,n)}}t.SyncFrame=o},397(e,t,n){Object.defineProperty(t,\"__esModule\",{value:!0}),t.Message=void 0;const i=n(864),o=n(351);class s{constructor(e,t){this.header=e,this.data=t}dataAsString(){return this.data?(new TextDecoder).decode(this.data):null}static newMessage(e,t,n,o,r){let a=0,c=0;r&&(c=r.byteLength,o&&(a=s.checksum(r)));const d=new i.MessageHeader(e,t,n,c,a);return new s(d,r)}static open(e,t,n,i){const o=new TextEncoder,r=new DataView(o.encode(n+\"\\0\").buffer);return s.newMessage(\"OPEN\",e,t,i,r)}static cnxn(e,t,n,i){const o=new TextEncoder,r=new DataView(o.encode(n).buffer);return s.newMessage(\"CNXN\",e,t,i,r)}static authSignature(e,t){return s.newMessage(\"AUTH\",2,0,t,e)}static authPublicKey(e,t){const n=(new TextEncoder).encode((0,o.toB64)(e.buffer)+\"\\0\");return s.newMessage(\"AUTH\",3,0,t,new DataView(n.buffer))}static checksum(e){let t=0;for(let n=0;n<e.byteLength;n++)t+=e.getUint8(n);return 4294967295&t}}t.Message=s},734(e,t,n){var i=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(o,s){function r(e){try{c(i.next(e))}catch(e){s(e)}}function a(e){try{c(i.throw(e))}catch(e){s(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};Object.defineProperty(t,\"__esModule\",{value:!0}),t.MessageChannel=void 0;const o=n(397),s=n(864);t.MessageChannel=class{constructor(e,t,n){this.transport=e,this.options=t,this.listener=n,this.active=!0,this.readLoop()}readLoop(){return i(this,void 0,void 0,function*(){let e;do{e=yield this.read(),this.options.debug&&console.log(\"<<<\",e),this.listener.newMessage(e)}while(this.active)})}readHeader(){return i(this,void 0,void 0,function*(){const e=yield this.transport.read(24);return s.MessageHeader.parse(e,this.options.useChecksum)})}read(){return i(this,void 0,void 0,function*(){const e=yield this.readHeader();let t;return e.cmd,e.length>0&&(t=yield this.transport.read(e.length)),new o.Message(e,t)})}close(){this.active=!1}write(e){return i(this,void 0,void 0,function*(){this.options.debug&&console.log(\">>>\",e);const t=e.header.toDataView();yield this.transport.write(t.buffer),e.data&&(yield this.transport.write(e.data.buffer))})}}},864(e,t,n){Object.defineProperty(t,\"__esModule\",{value:!0}),t.MessageHeader=void 0;const i=n(351);class o{constructor(e,t,n,i,o){this.cmd=e,this.arg0=t,this.arg1=n,this.length=i,this.checksum=o}toDataView(){const e=new DataView(new ArrayBuffer(24)),t=(0,i.encodeCmd)(this.cmd),n=4294967295^t;return e.setUint32(0,t,!0),e.setUint32(4,this.arg0,!0),e.setUint32(8,this.arg1,!0),e.setUint32(12,this.length,!0),e.setUint32(16,this.checksum,!0),e.setUint32(20,n,!0),e}static parse(e,t=!1){const n=e.getUint32(0,!0),s=e.getUint32(4,!0),r=e.getUint32(8,!0),a=e.getUint32(12,!0),c=e.getUint32(16,!0);if(t&&e.byteLength>20&&-1!==(n^e.getUint32(20,!0)))throw new Error(\"magic mismatch\");return new o((0,i.decodeCmd)(n),s,r,a,c)}}t.MessageHeader=o},257(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},858(e,t,n){var i=this&&this.__createBinding||(Object.create?function(e,t,n,i){void 0===i&&(i=n);var o=Object.getOwnPropertyDescriptor(t,n);o&&!(\"get\"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,i,o)}:function(e,t,n,i){void 0===i&&(i=n),e[i]=t[n]}),o=this&&this.__exportStar||function(e,t){for(var n in e)\"default\"===n||Object.prototype.hasOwnProperty.call(t,n)||i(t,e,n)};Object.defineProperty(t,\"__esModule\",{value:!0}),o(n(397),t),o(n(734),t),o(n(864),t),o(n(257),t)},225(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},289(e,t,n){var i=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(o,s){function r(e){try{c(i.next(e))}catch(e){s(e)}}function a(e){try{c(i.throw(e))}catch(e){s(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};Object.defineProperty(t,\"__esModule\",{value:!0}),t.WebUsbTransport=void 0;const o=n(351),s={classCode:255,subclassCode:66,protocolCode:1},r={classCode:255,subclassCode:66,protocolCode:3},a=[s,r];class c{constructor(e,t,n,i,o,s=console.log){this.device=e,this.match=t,this.endpointIn=n,this.endpointOut=i,this.options=o,this.log=s}close(){return i(this,void 0,void 0,function*(){yield this.device.releaseInterface(this.match.intf.interfaceNumber),yield this.device.close()})}write(e){return i(this,void 0,void 0,function*(){this.options.dump&&(0,o.hexdump)(new DataView(e),this.endpointOut+\"==> \"),yield this.device.transferOut(this.endpointOut,e)})}read(e){return i(this,void 0,void 0,function*(){const t=yield this.device.transferIn(this.endpointIn,e);if(!t.data)throw new Error(\"Response didn't contain any data\");return t.data})}isAdb(){return null!=c.findMatch(this.device,s)}isFastboot(){return null!=c.findMatch(this.device,r)}static open(e){return i(this,void 0,void 0,function*(){const t=yield navigator.usb.requestDevice({filters:a});yield t.open();const n=this.findMatch(t,s);if(!n)throw new Error(\"Could not find an ADB device\");yield t.selectConfiguration(n.conf.configurationValue),yield t.claimInterface(n.intf.interfaceNumber);const i=c.getEndpointNum(n.alternate.endpoints,\"in\"),o=c.getEndpointNum(n.alternate.endpoints,\"out\"),r=new c(t,n,i,o,e);return e.debug&&console.log(\"Created new Transport: \",r),r})}static findMatch(e,t){for(const n of e.configurations)for(const e of n.interfaces)for(const i of e.alternates)if(t.classCode===i.interfaceClass&&t.subclassCode===i.interfaceSubclass&&t.protocolCode===i.interfaceProtocol)return{conf:n,intf:e,alternate:i};return null}static getEndpointNum(e,t,n=\"bulk\"){for(const i of e)if(i.direction===t&&i.type===n)return i.endpointNumber;throw new Error(`Cannot find ${t} endpoint`)}}t.WebUsbTransport=c},980(e,t,n){var i=this&&this.__createBinding||(Object.create?function(e,t,n,i){void 0===i&&(i=n);var o=Object.getOwnPropertyDescriptor(t,n);o&&!(\"get\"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,i,o)}:function(e,t,n,i){void 0===i&&(i=n),e[i]=t[n]}),o=this&&this.__exportStar||function(e,t){for(var n in e)\"default\"===n||Object.prototype.hasOwnProperty.call(t,n)||i(t,e,n)};Object.defineProperty(t,\"__esModule\",{value:!0}),o(n(225),t),o(n(289),t)}},t={};function n(i){var o=t[i];if(void 0!==o)return o.exports;var s=t[i]={exports:{}};return e[i].call(s.exports,s,s.exports,n),s.exports}n.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},n.d=(e,t)=>{for(var i in t)n.o(t,i)&&!n.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var i=n(834),o=function(e,t,n,i){return new(n||(n=Promise))(function(o,s){function r(e){try{c(i.next(e))}catch(e){s(e)}}function a(e){try{c(i.throw(e))}catch(e){s(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};let s,r;const a=document.querySelector(\"#connect\"),c=document.querySelector(\"#disconnect\"),d=document.querySelector(\"#start\"),u=document.querySelector(\"#stop\"),l=document.querySelector(\"#video\"),h=document.querySelector(\"#download\"),f=document.querySelector(\"#status\"),y={debug:!0,useChecksum:!1,dump:!1,keySize:2048},g=new class{constructor(){this.keys=[]}loadKeys(){return o(this,void 0,void 0,function*(){return this.keys})}saveKey(e){return o(this,void 0,void 0,function*(){this.keys.push(e),console.log(\"Saving Key\"+e)})}};a.addEventListener(\"click\",e=>o(void 0,void 0,void 0,function*(){try{s=yield i.WebUsbTransport.open(y),r=new i.AdbClient(s,y,g),f.textContent=\"Accept prompt on device\";const e=yield r.connect();f.textContent=\"Connected and ready\",console.log(\"Connected: \",e),a.classList.toggle(\"hidden\"),c.classList.toggle(\"hidden\")}catch(e){console.error(\"Connection Failed: \",e),f.textContent=\"Failed to connect to a device\"}})),c.addEventListener(\"click\",e=>o(void 0,void 0,void 0,function*(){try{if(r){try{yield r.disconnect()}catch(e){console.log(\"Error disconnecting ADB Client: \",e)}r=null}s&&(yield s.close(),s=null),a.classList.toggle(\"hidden\"),c.classList.toggle(\"hidden\"),f.textContent=\"Connect to a device to start\"}catch(e){console.error(\"Disconnecting Failed: \",e)}}));let w=null;d.addEventListener(\"click\",()=>o(void 0,void 0,void 0,function*(){f.textContent=\"Recording...\",u.classList.toggle(\"hidden\"),d.classList.toggle(\"hidden\");const e=new TextDecoder;w=yield i.Stream.open(r,\"exec:screenrecord --output-format=h264 -\",y);const t=[];let n,o=0;for(;console.log(++o),n=yield w.read(),yield w.write(\"OKAY\"),\"CLSE\"!==n.header.cmd;)console.log(e.decode(n.data.buffer)),t.push(new Uint8Array(n.data.buffer));console.log(t.length);const s=URL.createObjectURL(new Blob(t));l.src=s,h.href=s})),u.addEventListener(\"click\",()=>o(void 0,void 0,void 0,function*(){yield null==w?void 0:w.write(\"CLSE\")}))})();"
  },
  {
    "path": "demo/public/js/livestream.d.ts",
    "content": "export {};\n//# sourceMappingURL=livestream.d.ts.map"
  },
  {
    "path": "demo/public/js/livestream.js",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\nvar __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {\n    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n    return new (P || (P = Promise))(function (resolve, reject) {\n        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n        function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n        step((generator = generator.apply(thisArg, _arguments || [])).next());\n    });\n};\nimport { WebUsbTransport, AdbClient, Stream } from 'wadb';\nlet transport;\nlet adbClient;\nconst connectButton = document.querySelector('#connect');\nconst disconnectButton = document.querySelector('#disconnect');\nconst startButton = document.querySelector('#start');\nconst stopButton = document.querySelector('#stop');\nconst video = document.querySelector('#video');\nconst download = document.querySelector('#download');\nconst status = document.querySelector('#status');\nconst options = {\n    debug: true,\n    useChecksum: false,\n    dump: false,\n    keySize: 2048,\n};\nclass MyKeyStore {\n    constructor() {\n        this.keys = [];\n    }\n    loadKeys() {\n        return __awaiter(this, void 0, void 0, function* () {\n            return this.keys;\n        });\n    }\n    saveKey(key) {\n        return __awaiter(this, void 0, void 0, function* () {\n            this.keys.push(key);\n            console.log('Saving Key' + key);\n        });\n    }\n}\nconst keyStore = new MyKeyStore();\nconnectButton.addEventListener('click', (_) => __awaiter(void 0, void 0, void 0, function* () {\n    try {\n        transport = yield WebUsbTransport.open(options);\n        adbClient = new AdbClient(transport, options, keyStore);\n        status.textContent = 'Accept prompt on device';\n        const adbConnectionInformation = yield adbClient.connect();\n        status.textContent = 'Connected and ready';\n        console.log('Connected: ', adbConnectionInformation);\n        connectButton.classList.toggle('hidden');\n        disconnectButton.classList.toggle('hidden');\n    }\n    catch (e) {\n        console.error('Connection Failed: ', e);\n        status.textContent = 'Failed to connect to a device';\n    }\n}));\ndisconnectButton.addEventListener('click', (_) => __awaiter(void 0, void 0, void 0, function* () {\n    try {\n        if (adbClient) {\n            try {\n                yield adbClient.disconnect();\n            }\n            catch (e) {\n                console.log('Error disconnecting ADB Client: ', e);\n            }\n            adbClient = null;\n        }\n        if (transport) {\n            yield transport.close();\n            transport = null;\n        }\n        connectButton.classList.toggle('hidden');\n        disconnectButton.classList.toggle('hidden');\n        status.textContent = 'Connect to a device to start';\n    }\n    catch (e) {\n        console.error('Disconnecting Failed: ', e);\n    }\n}));\nlet shell = null;\nstartButton.addEventListener('click', () => __awaiter(void 0, void 0, void 0, function* () {\n    // const mediaSource = new MediaSource();\n    // const url = URL.createObjectURL(mediaSource);\n    // video.src = url;\n    status.textContent = 'Recording...';\n    stopButton.classList.toggle('hidden');\n    startButton.classList.toggle('hidden');\n    const textDecoder = new TextDecoder();\n    // mediaSource.addEventListener('sourceopen', async () => {\n    shell = yield Stream.open(adbClient, 'exec:screenrecord --output-format=h264 -', options);\n    // const audioSourceBuffer = mediaSource.addSourceBuffer('video/mp4; codecs=\"mp4a.40.2\"');\n    const chunks = [];\n    let i = 0;\n    let msg;\n    while (true) {\n        console.log(++i);\n        msg = yield shell.read();\n        yield shell.write('OKAY');\n        if (msg.header.cmd === 'CLSE') {\n            break;\n        }\n        console.log(textDecoder.decode(msg.data.buffer));\n        chunks.push(new Uint8Array(msg.data.buffer));\n    }\n    console.log(chunks.length);\n    const objectUrl = URL.createObjectURL(new Blob(chunks));\n    video.src = objectUrl;\n    download.href = objectUrl;\n    // });\n}));\nstopButton.addEventListener('click', () => __awaiter(void 0, void 0, void 0, function* () {\n    yield (shell === null || shell === void 0 ? void 0 : shell.write('CLSE'));\n}));\n//# sourceMappingURL=livestream.js.map"
  },
  {
    "path": "demo/public/js/screenrecord.bundle.js",
    "content": "(()=>{\"use strict\";var e={834(e,t,n){var i=this&&this.__createBinding||(Object.create?function(e,t,n,i){void 0===i&&(i=n);var o=Object.getOwnPropertyDescriptor(t,n);o&&!(\"get\"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,i,o)}:function(e,t,n,i){void 0===i&&(i=n),e[i]=t[n]}),o=this&&this.__exportStar||function(e,t){for(var n in e)\"default\"===n||Object.prototype.hasOwnProperty.call(t,n)||i(t,e,n)};Object.defineProperty(t,\"__esModule\",{value:!0}),o(n(620),t),o(n(449),t),o(n(540),t),o(n(432),t),o(n(16),t),o(n(908),t),o(n(870),t),o(n(858),t),o(n(980),t)},620(e,t,n){var i=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(o,s){function r(e){try{c(i.next(e))}catch(e){s(e)}}function a(e){try{c(i.throw(e))}catch(e){s(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};Object.defineProperty(t,\"__esModule\",{value:!0}),t.AdbClient=void 0;const o=n(858),s=n(351),r=n(449),a=n(908),c=n(16),d=n(364),u=n(681);class l{constructor(e,t,n){this.transport=e,this.options=t,this.keyStore=n,this.messageQueue=new d.AsyncBlockingQueue,this.openStreams=new Set,this.messageChannel=new o.MessageChannel(e,t,this)}registerStream(e){this.openStreams.add(e)}unregisterStream(e){this.openStreams.delete(e)}newMessage(e){const t=Array.from(this.openStreams);for(const n of t)if(n.consumeMessage(e))return;this.messageQueue.enqueue(e)}awaitMessage(){return i(this,void 0,void 0,function*(){return this.messageQueue.dequeue()})}connect(){return i(this,void 0,void 0,function*(){const e=this.options.useChecksum?16777216:16777217,t=o.Message.cnxn(e,262144,\"host::\\0\",this.options.useChecksum);let n;yield this.sendMessage(t);do{n=yield this.awaitMessage()}while(\"CNXN\"!==n.header.cmd&&\"AUTH\"!==n.header.cmd);if(\"CNXN\"===n.header.cmd){if(!n.data)throw new Error(\"Connection doesn't have data\");return r.AdbConnectionInformation.fromDataView(n.data)}if(n=yield this.doAuth(n),!n.data)throw new Error(\"Connection doesn't have data\");return r.AdbConnectionInformation.fromDataView(n.data)})}disconnect(){return i(this,void 0,void 0,function*(){this.messageChannel.close()})}shell(e){return i(this,void 0,void 0,function*(){const t=yield a.Stream.open(this,`shell:${e}`,this.options),n=yield t.read();return yield t.close(),n.dataAsString()||\"\"})}framebuffer(){return i(this,void 0,void 0,function*(){return u.Framebuffer.create(this,this.options)})}interactiveShell(e){return i(this,void 0,void 0,function*(){const t=yield a.Stream.open(this,\"shell:\",this.options);return new c.Shell(t,e)})}sync(){return i(this,void 0,void 0,function*(){return yield a.Stream.open(this,\"sync:\",this.options)})}pull(e){return i(this,void 0,void 0,function*(){const t=yield this.sync(),n=yield t.pull(e);return yield t.close(),n})}push(e,t,n,o){return i(this,void 0,void 0,function*(){const i=yield this.sync();yield i.push(e,t,n,o),yield i.close()})}doAuth(e){return i(this,void 0,void 0,function*(){if(\"AUTH\"!==e.header.cmd)throw new Error(\"Not an AUTH response\");if(1!==e.header.arg0)throw new Error(`\\n          Invalid AUTH parameter. Expected 1 and received ${e.header.arg0}`);if(!e.data)throw new Error(\"AUTH message doens't contain data\");const t=e.data.buffer,n=yield this.keyStore.loadKeys();for(const e of n){const n=yield crypto.subtle.sign(\"RSASSA-PKCS1-v1_5\",e.privateKey,t),i=o.Message.authSignature(new DataView(n),this.options.useChecksum);yield this.sendMessage(i);const s=yield this.awaitMessage();if(\"CNXN\"===s.header.cmd)return s;console.log(\"Received message \",s,\"from phone\")}const i=yield l.generateKey(this.options.dump,this.options.keySize);yield this.keyStore.saveKey(i);const s=new DataView(yield crypto.subtle.exportKey(\"spki\",i.publicKey)),r=o.Message.authPublicKey(s,this.options.useChecksum);yield this.sendMessage(r),this.options.debug&&console.log(\"Waiting for key to be accepted on the device.\");const a=yield this.awaitMessage();if(\"CNXN\"!==a.header.cmd)throw console.error(\"AUTH failed. Phone didn't accept key\",a),new Error(\"AUTH failed. Phone didn't accept key\");return a})}sendMessage(e){return i(this,void 0,void 0,function*(){yield this.messageChannel.write(e)})}static generateKey(e,t){return i(this,void 0,void 0,function*(){const n=e,i=yield crypto.subtle.generateKey({name:\"RSASSA-PKCS1-v1_5\",modulusLength:t,publicExponent:new Uint8Array([1,0,1]),hash:{name:\"SHA-1\"}},n,[\"sign\",\"verify\"]);return e&&(yield(0,s.privateKeyDump)(i)),i})}}t.AdbClient=l},449(e,t){Object.defineProperty(t,\"__esModule\",{value:!0}),t.AdbConnectionInformation=void 0;const n=\"<unkwnown>\";class i{constructor(e,t,n,i){this.productName=e,this.productDevice=t,this.productModel=n,this.features=i}static fromDataView(e){const t=(new TextDecoder).decode(e);return i.fromString(t)}static fromString(e){const t=e.indexOf(\"::\"),o=e.substring(t+2).split(\";\");let s=n,r=n,a=n,c=[];for(const e of o)e.startsWith(\"ro.product.name\")?s=e.substring(16):e.startsWith(\"ro.product.model\")?a=e.substring(17):e.startsWith(\"ro.product.device\")?r=e.substring(18):e.startsWith(\"features\")&&(c=e.substring(9).split(\",\"));return new i(s,r,a,c)}}t.AdbConnectionInformation=i},681(e,t,n){var i=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(o,s){function r(e){try{c(i.next(e))}catch(e){s(e)}}function a(e){try{c(i.throw(e))}catch(e){s(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};Object.defineProperty(t,\"__esModule\",{value:!0}),t.Framebuffer=void 0;const o=n(908);class s{constructor(e,t,n,i,o,s,r,a,c,d,u,l,h,f,g){this.version=e,this.bpp=t,this.colorSpace=n,this.size=i,this.width=o,this.height=s,this.redOffset=r,this.redLength=a,this.blueOffset=c,this.blueLength=d,this.greenOffset=u,this.greenLength=l,this.alphaOffset=h,this.alphaLength=f,this.imageData=g}static create(e,t){return i(this,void 0,void 0,function*(){var n;const i=yield o.Stream.open(e,\"framebuffer:\",t);let r=yield i.read();if(\"WRTE\"!==r.header.cmd)throw yield i.write(\"CLSE\"),new Error(`Expected WRTE message but received ${r.header.cmd}`);if(!r.data)throw yield i.write(\"CLSE\"),new Error(\"message doesn't contain data\");yield i.write(\"OKAY\");const a=r.data.getUint32(0,!0),c=r.data.getUint32(4,!0),d=r.data.getUint32(8,!0),u=r.data.getUint32(12,!0),l=r.data.getUint32(16,!0),h=r.data.getUint32(20,!0),f=r.data.getUint32(24,!0),g=r.data.getUint32(28,!0),y=r.data.getUint32(32,!0),v=r.data.getUint32(36,!0),w=r.data.getUint32(40,!0),p=r.data.getUint32(44,!0),m=r.data.getUint32(48,!0),b=r.data.getUint32(52,!0),E=new Uint8Array(u);let M=0,x=new Uint8Array(r.data.buffer.slice(s.BYTE_LENGTH));for(E.set(x,0),M=x.length;M<u&&(r=yield i.read(),\"CLSE\"!==r.header.cmd);){if(!r.data)throw yield i.write(\"CLSE\"),new Error(\"message doesn't contain data\");x=new Uint8Array(null===(n=r.data)||void 0===n?void 0:n.buffer),E.set(x,M),M+=x.length,yield i.write(\"OKAY\")}return yield i.close(),new s(a,c,d,u,l,h,f,g,y,v,w,p,m,b,Uint8ClampedArray.from(E))})}}t.Framebuffer=s,s.BYTE_LENGTH=56},351(e,t){var n=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(o,s){function r(e){try{c(i.next(e))}catch(e){s(e)}}function a(e){try{c(i.throw(e))}catch(e){s(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};function i(e,t,n){const i=t-e.length;let o=\"\";for(let e=0;e<i;e++)o+=n;return o+e}function o(e){return i(e.toString(16),2,\"0\")}function s(e){return i(e.toString(16),4,\"0\")}function r(e){return btoa(new Uint8Array(e).reduce((e,t)=>e+String.fromCharCode(t),\"\"))}Object.defineProperty(t,\"__esModule\",{value:!0}),t.toHex8=o,t.toHex16=s,t.toHex32=function(e){return i(e.toString(16),8,\"0\")},t.hexdump=function(e,t=\"\"){const n=new TextDecoder;for(let i=0;i<e.byteLength;i+=16){const r=e.byteLength-i>16?16:e.byteLength-i;let a,c=t+s(i)+\" \";for(a=0;a<r;a++)c+=\" \"+o(e.getUint8(i+a));for(;a<16;a++)c+=\"   \";c+=\" | \"+n.decode(new DataView(e.buffer,i,r)),console.log(c)}},t.toB64=r,t.privateKeyDump=function(e){return n(this,void 0,void 0,function*(){if(!e.privateKey.extractable)return void console.log(\"cannot dump the private key, it's not extractable\");const t=yield crypto.subtle.exportKey(\"pkcs8\",e.privateKey);console.log(`-----BEGIN PRIVATE KEY-----\\n${r(t)}\\n-----END PRIVATE KEY-----`)})},t.publicKeyDump=function(e){return n(this,void 0,void 0,function*(){if(!e.publicKey.extractable)return void console.log(\"cannot dump the public key, it's not extractable\");const t=yield crypto.subtle.exportKey(\"spki\",e.publicKey);console.log(`-----BEGIN PUBLIC KEY-----\\n${r(t)}'\\n-----END PUBLIC KEY-----`)})},t.encodeCmd=function(e){const t=(new TextEncoder).encode(e).buffer;return new DataView(t).getUint32(0,!0)},t.decodeCmd=function(e){const t=new TextDecoder,n=new ArrayBuffer(4);return new DataView(n).setUint32(0,e,!0),t.decode(n)}},540(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},432(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},364(e,t){var n=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(o,s){function r(e){try{c(i.next(e))}catch(e){s(e)}}function a(e){try{c(i.throw(e))}catch(e){s(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};Object.defineProperty(t,\"__esModule\",{value:!0}),t.AsyncBlockingQueue=t.Queue=void 0;class i{constructor(e){this.data=e}}class o{enqueue(e){const t=new i(e);this.tail&&(this.tail.next=t),this.tail=t,this.head||(this.head=this.tail)}dequeue(){if(this.isEmpty())throw new Error(\"Cannot dequeue. Queue is empty\");const e=this.head.data;return this.head=this.head.next,e}isEmpty(){return null==this.head}}t.Queue=o,t.AsyncBlockingQueue=class{constructor(){this.promiseQueue=new o,this.resolverQueue=new o}add(){const e=new Promise(e=>{this.resolverQueue.enqueue(e)});this.promiseQueue.enqueue(e)}enqueue(e){this.resolverQueue.isEmpty()&&this.add(),this.resolverQueue.dequeue()(e)}dequeue(){return n(this,void 0,void 0,function*(){return this.promiseQueue.isEmpty()&&this.add(),this.promiseQueue.dequeue()})}hasPendingPromises(){return!this.promiseQueue.isEmpty()}hasPendingResolvers(){return!this.resolverQueue.isEmpty()}}},16(e,t){var n=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(o,s){function r(e){try{c(i.next(e))}catch(e){s(e)}}function a(e){try{c(i.throw(e))}catch(e){s(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};Object.defineProperty(t,\"__esModule\",{value:!0}),t.Shell=void 0,t.Shell=class{constructor(e,t){this.stream=e,this.callbackFunction=t,this.textDecoder=new TextDecoder,this.textEncoder=new TextEncoder,this.messageListener=[],this.closed=!1,this.loopRead()}loopRead(){return n(this,void 0,void 0,function*(){try{let e;do{if(e=yield this.stream.read(),\"WRTE\"===e.header.cmd){this.stream.write(\"OKAY\");const t=this.textDecoder.decode(e.data);this.callbackFunction&&this.callbackFunction(t)}for(const t of this.messageListener)t(e)}while(!this.closed)}catch(e){console.error(\"loopRead crashed\",e)}this.stream.client.unregisterStream(this.stream)})}waitForMessage(e){return new Promise(t=>{const n=i=>{if(i.header.cmd===e){const e=this.messageListener.indexOf(n);this.messageListener.splice(e,1),t(i)}};this.messageListener.push(n)})}write(e){return n(this,void 0,void 0,function*(){const t=this.textEncoder.encode(e);yield this.stream.write(\"WRTE\",new DataView(t.buffer)),yield this.waitForMessage(\"OKAY\")})}close(){return n(this,void 0,void 0,function*(){this.closed=!0,yield this.write(\"CLSE\")})}}},908(e,t,n){var i=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(o,s){function r(e){try{c(i.next(e))}catch(e){s(e)}}function a(e){try{c(i.throw(e))}catch(e){s(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};Object.defineProperty(t,\"__esModule\",{value:!0}),t.Stream=void 0;const o=n(858),s=n(351),r=n(870),a=n(364);class c{constructor(e,t,n,i,o){this.client=e,this.service=t,this.localId=n,this.remoteId=i,this.options=o,this.messageQueue=new a.AsyncBlockingQueue}close(){return i(this,void 0,void 0,function*(){yield this.write(\"CLSE\"),this.options.debug&&(console.log(`Closed stream ${this.service}`),console.log(` local_id: 0x${(0,s.toHex32)(this.localId)}`),console.log(` remote_id: 0x${(0,s.toHex32)(this.remoteId)}`)),this.client.unregisterStream(this)})}consumeMessage(e){return 0!==e.header.arg0&&e.header.arg0===this.remoteId&&0!==e.header.arg1&&e.header.arg1===this.localId&&(this.messageQueue.enqueue(e),!0)}write(e,t){return i(this,void 0,void 0,function*(){const n=this.newMessage(e,t);yield this.client.sendMessage(n)})}read(){return i(this,void 0,void 0,function*(){return this.messageQueue.dequeue()})}sendReceive(e,t){return i(this,void 0,void 0,function*(){yield this.client.sendMessage(e);const n=yield this.read();if(n.header.cmd!==t)throw new Error(\"WRTE/SEND failed: \"+n)})}pull(e){return i(this,void 0,void 0,function*(){const t=(new TextEncoder).encode(e),n=new r.SyncFrame(\"RECV\",t.byteLength),i=this.newMessage(\"WRTE\",n.toDataView());yield this.client.sendMessage(i);const o=yield this.read();if(\"OKAY\"!==o.header.cmd)throw new Error(\"WRTE/RECV failed: \"+o);const s=this.newMessage(\"WRTE\",new DataView(t.buffer));yield this.client.sendMessage(s);const a=yield this.read();if(\"OKAY\"!==a.header.cmd)throw new Error(\"WRTE/filename failed: \"+a);const c=this.newMessage(\"OKAY\");let d=yield this.read();yield this.client.sendMessage(c);let u=r.SyncFrame.fromDataView(new DataView(d.data.buffer.slice(0,8))),l=new Uint8Array(d.data.buffer.slice(8));const h=[];for(;\"DONE\"!==u.cmd;){for(;u.byteLength>=l.byteLength;){d=yield this.read(),yield this.client.sendMessage(c);const e=l.byteLength+d.data.byteLength,t=new Uint8Array(e);t.set(l,0),t.set(new Uint8Array(d.data.buffer),l.byteLength),l=t}h.push(l.slice(0,u.byteLength).buffer),l=l.slice(u.byteLength),u=r.SyncFrame.fromDataView(new DataView(l.slice(0,8).buffer)),l=l.slice(8)}return new Blob(h)})}push(e,t,n,o){return i(this,void 0,void 0,function*(){const i=new FileReader,s=new TextEncoder,a=s.encode(t),c=new r.SyncFrame(\"SEND\",t.length+1+n.length),d=this.newMessage(\"WRTE\",c.toDataView());yield this.sendReceive(d,\"OKAY\");const u=this.newMessage(\"WRTE\",new DataView(a.buffer));yield this.sendReceive(u,\"OKAY\");const l=this.newMessage(\"WRTE\",new DataView(s.encode(\",\"+n).buffer));yield this.sendReceive(l,\"OKAY\");const h=new Promise((t,n)=>{i.onload=e=>t(e.target.result),i.onerror=n,i.readAsArrayBuffer(e)}),f=yield h,g=[];for(let e=0;e<f.byteLength;e+=o)g.push(f.slice(e,Math.min(e+o,f.byteLength)));for(const e of g){const t=new r.SyncFrame(\"DATA\",e.byteLength),n=this.newMessage(\"WRTE\",t.toDataView());yield this.sendReceive(n,\"OKAY\");const i=new DataView(e),o=this.newMessage(\"WRTE\",i);yield this.sendReceive(o,\"OKAY\")}const y=new r.SyncFrame(\"DONE\",Math.round(Date.now()/1e3)),v=this.newMessage(\"WRTE\",y.toDataView());yield this.client.sendMessage(v);const w=this.newMessage(\"OKAY\");yield this.sendReceive(w,\"OKAY\")})}newMessage(e,t){return o.Message.newMessage(e,this.localId,this.remoteId,this.options.useChecksum,t)}static open(e,t,n){return i(this,void 0,void 0,function*(){const i=c.nextId++;let r=0;const a=o.Message.open(i,r,t,n.useChecksum);let d;yield e.sendMessage(a);do{d=yield e.awaitMessage()}while(d.header.arg1!==i);if(\"OKAY\"!==d.header.cmd)throw new Error(\"OPEN Failed\");r=d.header.arg0,n.debug&&(console.log(`Opened stream ${t}`),console.log(` local_id: 0x${(0,s.toHex32)(i)}`),console.log(` remote_id: 0x${(0,s.toHex32)(r)}`));const u=new c(e,t,i,r,n);return e.registerStream(u),u})}}t.Stream=c,c.nextId=1},870(e,t,n){Object.defineProperty(t,\"__esModule\",{value:!0}),t.SyncFrame=void 0;const i=n(351);class o{constructor(e,t){this.cmd=e,this.byteLength=t}toDataView(){const e=new ArrayBuffer(8),t=(0,i.encodeCmd)(this.cmd),n=new DataView(e);return n.setUint32(0,t,!0),n.setUint32(4,this.byteLength,!0),n}static fromDataView(e){const t=(0,i.decodeCmd)(e.getUint32(0,!0)),n=e.getUint32(4,!0);return new o(t,n)}}t.SyncFrame=o},397(e,t,n){Object.defineProperty(t,\"__esModule\",{value:!0}),t.Message=void 0;const i=n(864),o=n(351);class s{constructor(e,t){this.header=e,this.data=t}dataAsString(){return this.data?(new TextDecoder).decode(this.data):null}static newMessage(e,t,n,o,r){let a=0,c=0;r&&(c=r.byteLength,o&&(a=s.checksum(r)));const d=new i.MessageHeader(e,t,n,c,a);return new s(d,r)}static open(e,t,n,i){const o=new TextEncoder,r=new DataView(o.encode(n+\"\\0\").buffer);return s.newMessage(\"OPEN\",e,t,i,r)}static cnxn(e,t,n,i){const o=new TextEncoder,r=new DataView(o.encode(n).buffer);return s.newMessage(\"CNXN\",e,t,i,r)}static authSignature(e,t){return s.newMessage(\"AUTH\",2,0,t,e)}static authPublicKey(e,t){const n=(new TextEncoder).encode((0,o.toB64)(e.buffer)+\"\\0\");return s.newMessage(\"AUTH\",3,0,t,new DataView(n.buffer))}static checksum(e){let t=0;for(let n=0;n<e.byteLength;n++)t+=e.getUint8(n);return 4294967295&t}}t.Message=s},734(e,t,n){var i=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(o,s){function r(e){try{c(i.next(e))}catch(e){s(e)}}function a(e){try{c(i.throw(e))}catch(e){s(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};Object.defineProperty(t,\"__esModule\",{value:!0}),t.MessageChannel=void 0;const o=n(397),s=n(864);t.MessageChannel=class{constructor(e,t,n){this.transport=e,this.options=t,this.listener=n,this.active=!0,this.readLoop()}readLoop(){return i(this,void 0,void 0,function*(){let e;do{e=yield this.read(),this.options.debug&&console.log(\"<<<\",e),this.listener.newMessage(e)}while(this.active)})}readHeader(){return i(this,void 0,void 0,function*(){const e=yield this.transport.read(24);return s.MessageHeader.parse(e,this.options.useChecksum)})}read(){return i(this,void 0,void 0,function*(){const e=yield this.readHeader();let t;return e.cmd,e.length>0&&(t=yield this.transport.read(e.length)),new o.Message(e,t)})}close(){this.active=!1}write(e){return i(this,void 0,void 0,function*(){this.options.debug&&console.log(\">>>\",e);const t=e.header.toDataView();yield this.transport.write(t.buffer),e.data&&(yield this.transport.write(e.data.buffer))})}}},864(e,t,n){Object.defineProperty(t,\"__esModule\",{value:!0}),t.MessageHeader=void 0;const i=n(351);class o{constructor(e,t,n,i,o){this.cmd=e,this.arg0=t,this.arg1=n,this.length=i,this.checksum=o}toDataView(){const e=new DataView(new ArrayBuffer(24)),t=(0,i.encodeCmd)(this.cmd),n=4294967295^t;return e.setUint32(0,t,!0),e.setUint32(4,this.arg0,!0),e.setUint32(8,this.arg1,!0),e.setUint32(12,this.length,!0),e.setUint32(16,this.checksum,!0),e.setUint32(20,n,!0),e}static parse(e,t=!1){const n=e.getUint32(0,!0),s=e.getUint32(4,!0),r=e.getUint32(8,!0),a=e.getUint32(12,!0),c=e.getUint32(16,!0);if(t&&e.byteLength>20&&-1!==(n^e.getUint32(20,!0)))throw new Error(\"magic mismatch\");return new o((0,i.decodeCmd)(n),s,r,a,c)}}t.MessageHeader=o},257(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},858(e,t,n){var i=this&&this.__createBinding||(Object.create?function(e,t,n,i){void 0===i&&(i=n);var o=Object.getOwnPropertyDescriptor(t,n);o&&!(\"get\"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,i,o)}:function(e,t,n,i){void 0===i&&(i=n),e[i]=t[n]}),o=this&&this.__exportStar||function(e,t){for(var n in e)\"default\"===n||Object.prototype.hasOwnProperty.call(t,n)||i(t,e,n)};Object.defineProperty(t,\"__esModule\",{value:!0}),o(n(397),t),o(n(734),t),o(n(864),t),o(n(257),t)},225(e,t){Object.defineProperty(t,\"__esModule\",{value:!0})},289(e,t,n){var i=this&&this.__awaiter||function(e,t,n,i){return new(n||(n=Promise))(function(o,s){function r(e){try{c(i.next(e))}catch(e){s(e)}}function a(e){try{c(i.throw(e))}catch(e){s(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};Object.defineProperty(t,\"__esModule\",{value:!0}),t.WebUsbTransport=void 0;const o=n(351),s={classCode:255,subclassCode:66,protocolCode:1},r={classCode:255,subclassCode:66,protocolCode:3},a=[s,r];class c{constructor(e,t,n,i,o,s=console.log){this.device=e,this.match=t,this.endpointIn=n,this.endpointOut=i,this.options=o,this.log=s}close(){return i(this,void 0,void 0,function*(){yield this.device.releaseInterface(this.match.intf.interfaceNumber),yield this.device.close()})}write(e){return i(this,void 0,void 0,function*(){this.options.dump&&(0,o.hexdump)(new DataView(e),this.endpointOut+\"==> \"),yield this.device.transferOut(this.endpointOut,e)})}read(e){return i(this,void 0,void 0,function*(){const t=yield this.device.transferIn(this.endpointIn,e);if(!t.data)throw new Error(\"Response didn't contain any data\");return t.data})}isAdb(){return null!=c.findMatch(this.device,s)}isFastboot(){return null!=c.findMatch(this.device,r)}static open(e){return i(this,void 0,void 0,function*(){const t=yield navigator.usb.requestDevice({filters:a});yield t.open();const n=this.findMatch(t,s);if(!n)throw new Error(\"Could not find an ADB device\");yield t.selectConfiguration(n.conf.configurationValue),yield t.claimInterface(n.intf.interfaceNumber);const i=c.getEndpointNum(n.alternate.endpoints,\"in\"),o=c.getEndpointNum(n.alternate.endpoints,\"out\"),r=new c(t,n,i,o,e);return e.debug&&console.log(\"Created new Transport: \",r),r})}static findMatch(e,t){for(const n of e.configurations)for(const e of n.interfaces)for(const i of e.alternates)if(t.classCode===i.interfaceClass&&t.subclassCode===i.interfaceSubclass&&t.protocolCode===i.interfaceProtocol)return{conf:n,intf:e,alternate:i};return null}static getEndpointNum(e,t,n=\"bulk\"){for(const i of e)if(i.direction===t&&i.type===n)return i.endpointNumber;throw new Error(`Cannot find ${t} endpoint`)}}t.WebUsbTransport=c},980(e,t,n){var i=this&&this.__createBinding||(Object.create?function(e,t,n,i){void 0===i&&(i=n);var o=Object.getOwnPropertyDescriptor(t,n);o&&!(\"get\"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,i,o)}:function(e,t,n,i){void 0===i&&(i=n),e[i]=t[n]}),o=this&&this.__exportStar||function(e,t){for(var n in e)\"default\"===n||Object.prototype.hasOwnProperty.call(t,n)||i(t,e,n)};Object.defineProperty(t,\"__esModule\",{value:!0}),o(n(225),t),o(n(289),t)}},t={};function n(i){var o=t[i];if(void 0!==o)return o.exports;var s=t[i]={exports:{}};return e[i].call(s.exports,s,s.exports,n),s.exports}n.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},n.d=(e,t)=>{for(var i in t)n.o(t,i)&&!n.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);var i=n(834),o=function(e,t,n,i){return new(n||(n=Promise))(function(o,s){function r(e){try{c(i.next(e))}catch(e){s(e)}}function a(e){try{c(i.throw(e))}catch(e){s(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n(function(e){e(t)})).then(r,a)}c((i=i.apply(e,t||[])).next())})};let s,r;const a=document.querySelector(\"#connect\"),c=document.querySelector(\"#disconnect\"),d=document.querySelector(\"#start\"),u=document.querySelector(\"#stop\"),l=document.querySelector(\"#screencapture\"),h=document.querySelector(\"#video\"),f=document.querySelector(\"#screenshot\"),g=document.querySelector(\"#download\"),y=document.querySelector(\"#status\"),v={debug:!0,useChecksum:!1,dump:!1,keySize:2048},w=new class{constructor(){this.keys=[]}loadKeys(){return o(this,void 0,void 0,function*(){return this.keys})}saveKey(e){return o(this,void 0,void 0,function*(){this.keys.push(e),console.log(\"Saving Key\"+e)})}};a.addEventListener(\"click\",e=>o(void 0,void 0,void 0,function*(){try{s=yield i.WebUsbTransport.open(v),r=new i.AdbClient(s,v,w),y.textContent=\"Accept prompt on device\";const e=yield r.connect();y.textContent=\"Connected and ready\",console.log(\"Connected: \",e),a.classList.toggle(\"hidden\"),c.classList.toggle(\"hidden\"),d.removeAttribute(\"disabled\"),l.removeAttribute(\"disabled\"),u.removeAttribute(\"disabled\")}catch(e){console.error(\"Connection Failed: \",e),y.textContent=\"Failed to connect to a device\"}})),c.addEventListener(\"click\",e=>o(void 0,void 0,void 0,function*(){try{if(r){try{yield r.disconnect()}catch(e){console.log(\"Error disconnecting ADB Client: \",e)}r=null}s&&(yield s.close(),s=null),a.classList.toggle(\"hidden\"),c.classList.toggle(\"hidden\"),d.disabled=!0,u.disabled=!0,l.disabled=!0,y.textContent=\"Connect to a device to start\"}catch(e){console.error(\"Disconnecting Failed: \",e)}}));const p=\"/sdcard/webadb-record-2.mp4\";let m=null;d.addEventListener(\"click\",()=>o(void 0,void 0,void 0,function*(){m=yield i.Stream.open(r,`shell:screenrecord ${p}`,v),y.textContent=\"Recording...\",u.classList.toggle(\"hidden\"),d.classList.toggle(\"hidden\")})),u.addEventListener(\"click\",()=>o(void 0,void 0,void 0,function*(){y.textContent=\"Finishing Recording...\",yield m.close(),y.textContent=\"Pulling video...\",setTimeout(()=>o(void 0,void 0,void 0,function*(){console.log(\"Starting ADB Pull\");const e=yield r.pull(p),t=window.URL.createObjectURL(e);h.src=t,g.href=t,g.download=\"recording.mp4\",u.classList.toggle(\"hidden\"),d.classList.toggle(\"hidden\"),f.classList.add(\"hidden\"),h.classList.remove(\"hidden\"),g.classList.remove(\"hidden\"),y.textContent=\"Done! Connected and ready\"}),2e3)})),l.addEventListener(\"click\",()=>o(void 0,void 0,void 0,function*(){y.textContent=\"Generating Screenshot...\",yield r.shell(\"screencap -p /sdcard/screenshot.png\"),y.textContent=\"Pulling image...\",setTimeout(()=>o(void 0,void 0,void 0,function*(){console.log(\"Starting ADB Pull\");const e=yield r.pull(\"/sdcard/screenshot.png\"),t=window.URL.createObjectURL(e);f.src=t,g.href=t,g.download=\"screenshot.png\",g.classList.remove(\"hidden\"),f.classList.remove(\"hidden\"),h.classList.add(\"hidden\"),y.textContent=\"Done! Connected and ready\"}),2e3)}))})();"
  },
  {
    "path": "demo/public/js/screenrecord.d.ts",
    "content": "export {};\n//# sourceMappingURL=screenrecord.d.ts.map"
  },
  {
    "path": "demo/public/js/screenrecord.js",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\nvar __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {\n    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n    return new (P || (P = Promise))(function (resolve, reject) {\n        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n        function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n        step((generator = generator.apply(thisArg, _arguments || [])).next());\n    });\n};\nimport { AdbClient, WebUsbTransport, Stream } from 'wadb';\nlet transport;\nlet adbClient;\nconst connectButton = document.querySelector('#connect');\nconst disconnectButton = document.querySelector('#disconnect');\nconst startButton = document.querySelector('#start');\nconst stopButton = document.querySelector('#stop');\nconst screenshotButton = document.querySelector('#screencapture');\nconst video = document.querySelector('#video');\nconst screenshot = document.querySelector('#screenshot');\nconst download = document.querySelector('#download');\nconst status = document.querySelector('#status');\nconst options = {\n    debug: true,\n    useChecksum: false,\n    dump: false,\n    keySize: 2048,\n};\nclass MyKeyStore {\n    constructor() {\n        this.keys = [];\n    }\n    loadKeys() {\n        return __awaiter(this, void 0, void 0, function* () {\n            return this.keys;\n        });\n    }\n    saveKey(key) {\n        return __awaiter(this, void 0, void 0, function* () {\n            this.keys.push(key);\n            console.log('Saving Key' + key);\n        });\n    }\n}\nconst keyStore = new MyKeyStore();\nconnectButton.addEventListener('click', (_) => __awaiter(void 0, void 0, void 0, function* () {\n    try {\n        transport = yield WebUsbTransport.open(options);\n        adbClient = new AdbClient(transport, options, keyStore);\n        status.textContent = 'Accept prompt on device';\n        const adbConnectionInformation = yield adbClient.connect();\n        status.textContent = 'Connected and ready';\n        console.log('Connected: ', adbConnectionInformation);\n        connectButton.classList.toggle('hidden');\n        disconnectButton.classList.toggle('hidden');\n        startButton.removeAttribute('disabled');\n        screenshotButton.removeAttribute('disabled');\n        stopButton.removeAttribute('disabled');\n    }\n    catch (e) {\n        console.error('Connection Failed: ', e);\n        status.textContent = 'Failed to connect to a device';\n    }\n}));\ndisconnectButton.addEventListener('click', (_) => __awaiter(void 0, void 0, void 0, function* () {\n    try {\n        if (adbClient) {\n            try {\n                yield adbClient.disconnect();\n            }\n            catch (e) {\n                console.log('Error disconnecting ADB Client: ', e);\n            }\n            adbClient = null;\n        }\n        if (transport) {\n            yield transport.close();\n            transport = null;\n        }\n        connectButton.classList.toggle('hidden');\n        disconnectButton.classList.toggle('hidden');\n        startButton.disabled = true;\n        stopButton.disabled = true;\n        screenshotButton.disabled = true;\n        status.textContent = 'Connect to a device to start';\n    }\n    catch (e) {\n        console.error('Disconnecting Failed: ', e);\n    }\n}));\nconst RECORD_FILE_NAME = '/sdcard/webadb-record-2.mp4';\nlet shell = null;\nstartButton.addEventListener('click', () => __awaiter(void 0, void 0, void 0, function* () {\n    shell = yield Stream.open(adbClient, `shell:screenrecord ${RECORD_FILE_NAME}`, options);\n    status.textContent = 'Recording...';\n    stopButton.classList.toggle('hidden');\n    startButton.classList.toggle('hidden');\n}));\nstopButton.addEventListener('click', () => __awaiter(void 0, void 0, void 0, function* () {\n    // await shell!.write(String.fromCharCode(3) + '\\n'); // CTRL+C\n    status.textContent = 'Finishing Recording...';\n    yield shell.close();\n    status.textContent = 'Pulling video...';\n    // Trying to load the file straight away results in a broken file.\n    // Waiting for a couple of seconds fixes it. Maybe send STAT before\n    // attempting download.\n    setTimeout(() => __awaiter(void 0, void 0, void 0, function* () {\n        console.log('Starting ADB Pull');\n        const result = yield adbClient.pull(RECORD_FILE_NAME);\n        const videoSrc = window.URL.createObjectURL(result);\n        video.src = videoSrc;\n        download.href = videoSrc;\n        download.download = 'recording.mp4';\n        stopButton.classList.toggle('hidden');\n        startButton.classList.toggle('hidden');\n        screenshot.classList.add('hidden');\n        video.classList.remove('hidden');\n        download.classList.remove('hidden');\n        status.textContent = 'Done! Connected and ready';\n    }), 2000);\n}));\nscreenshotButton.addEventListener('click', () => __awaiter(void 0, void 0, void 0, function* () {\n    status.textContent = 'Generating Screenshot...';\n    yield adbClient.shell('screencap -p /sdcard/screenshot.png');\n    status.textContent = 'Pulling image...';\n    setTimeout(() => __awaiter(void 0, void 0, void 0, function* () {\n        console.log('Starting ADB Pull');\n        const result = yield adbClient.pull('/sdcard/screenshot.png');\n        const imageSrc = window.URL.createObjectURL(result);\n        screenshot.src = imageSrc;\n        download.href = imageSrc;\n        download.download = 'screenshot.png';\n        download.classList.remove('hidden');\n        screenshot.classList.remove('hidden');\n        video.classList.add('hidden');\n        status.textContent = 'Done! Connected and ready';\n    }), 2000);\n}));\n//# sourceMappingURL=screenrecord.js.map"
  },
  {
    "path": "demo/public/livestream.html",
    "content": "<!--\n Copyright 2020 Google Inc. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<!doctype html>\n<html>\n  <head>\n    <meta name=\"viewport\" content=\"initial-scale=1, maximum-scale=1\">\n    <meta charset=\"utf-8\">\n    <title>Web ADB Livestream</title>    \n    <script src=\"js/livestream.bundle.js\" defer></script>\n    <style>\n      html {\n        font-family: 'Roboto', sans-serif;\n        background-color: lightsteelblue;\n        box-sizing: border-box;\n      }\n\n      body {\n        margin: 0;\n      }\n\n      h1 {\n        height: 56px;\n        margin: 0;\n        padding: 8px 16px;\n        background-color: #789ac7;\n        box-sizing: border-box;\n        box-shadow: 0px 2px 4px #999999;\n      }\n\n      h2 {\n        margin: 16px 0 8px 0;\n      }\n\n      video {\n        background-color: darkgrey;\n        max-width: 100%;\n        max-height: 60vh;\n        display: block;\n      }\n      \n      .video-container {\n        margin-top: 16px;\n        width: 100%;\n      }\n\n      .content-wrapper {\n        padding: 0 16px;\n      }\n\n      .buttons {\n        height: 30px;\n      }\n\n      .button {\n        width: 145px;\n        height: 30px;\n        text-transform: uppercase;\n        padding-left: 20px;\n      }\n\n      #connect {\n        background-image: url(./assets/phonelink.svg);\n        background-repeat: no-repeat;\n        background-position-x: 4px;\n        background-position-y: 2px;   \n      }\n\n      #disconnect {\n        background-image: url(./assets/phonelink_off.svg);\n        background-repeat: no-repeat;\n        background-position-x: 4px;\n        background-position-y: 2px;\n      }\n\n      #start {\n        background-image: url(./assets/video-start.svg);\n        background-repeat: no-repeat;\n        background-position-x: 4px;\n        background-position-y: 0px;\n      }\n\n      #stop {\n        background-image: url(./assets/video-stop.svg);\n        background-repeat: no-repeat;\n        background-position-x: 4px;\n        background-position-y: 0px;\n      }\n\n      #screencapture {\n        background-image: url(./assets/screenshot.svg);\n        background-repeat: no-repeat;\n        background-position-x: 4px;\n        background-position-y: 0px;        \n      }\n\n      .hidden {\n        display: none;\n      }\n\n      .status {\n        height: 28px;\n        background-color: steelblue;\n        font-size: 12px;\n        box-sizing: border-box;\n        padding-top: 6px;\n        text-transform: uppercase;\n        margin-bottom: 16px;        \n      }\n    </style>\n  </head>\n  <body>\n    <h1>WebADB Screenrecord</h1>\n    <div id=\"status\" class=\"content-wrapper status\">\n      Connect to a device to start\n    </div>    \n    <div class=\"content-wrapper\">\n      <div class=\"buttons\">\n        <button id=\"connect\" class=\"button\">Connect</button>\n        <button id=\"disconnect\" class=\"button hidden\">Disconnect</button>\n      </div>\n      <div>\n        <h2>Controls</h2>\n        <div class=\"buttons\">\n          <button id=\"start\" class=\"button\">Record</button>\n          <button id=\"stop\" class=\"button hidden\">Stop</button>\n        </div>\n      </div>\n      <div class=\"video-container\">\n        <h2>Output:</h2>\n        <video id=\"video\" controls></video>\n        <a id=\"download\" href=\"#\" download=\"recording.mp4\">Download</a>          \n      </div>\n  </div>\n  </body>\n</html>"
  },
  {
    "path": "demo/public/screenrecord.html",
    "content": "<!--\n Copyright 2020 Google Inc. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<!doctype html>\n<html>\n  <head>\n    <meta name=\"viewport\" content=\"initial-scale=1, maximum-scale=1\">\n    <meta charset=\"utf-8\">\n    <title>WebADB Screenrecord</title>\n    <script src=\"js/screenrecord.bundle.js\" defer></script>\n    <style>\n      html {\n        font-family: 'Roboto', sans-serif;\n        background-color: lightsteelblue;\n        box-sizing: border-box;\n        min-width: 320px;\n      }\n\n      body {\n        margin: 0;\n      }\n\n      header, .status {\n        box-sizing: border-box;\n        box-shadow: 0px 2px 4px #999999;\n      }\n\n      header > h1, .status > div, content > div {\n        margin: 0px auto;\n        max-width: 900px;\n        padding: 0px 16px;\n        box-sizing: border-box;\n      }\n\n      header {\n        background-color: #789ac7;\n      }\n      \n      header > h1 {\n        height: 56px;\n        padding: 8px 16px;\n      }\n\n      .status {\n        height: 28px;\n        background-color: steelblue;\n        font-size: 12px;\n        padding-top: 6px;\n        text-transform: uppercase;\n        margin-bottom: 16px;\n      }\n\n      h2 {\n        margin: 16px 0 8px 0;\n      }\n\n      .video-container > div {\n        background-color: #222222;\n        width: 100%;\n        margin: 0px auto;        \n      }\n\n      video, #screenshot {\n        width: 100%;\n        max-width: 100%;\n        max-height: 100%;\n      } \n      \n      .video-container {\n        margin-top: 16px;\n        margin-bottom: 16px;\n        width: 100%;\n      }\n\n      .button {\n        width: 145px;\n        height: 30px;\n        text-transform: uppercase;\n        padding-left: 20px;\n      }\n\n      #connect {\n        background-image: url(./assets/phonelink.svg);\n        background-repeat: no-repeat;\n        background-position-x: 4px;\n        background-position-y: 2px;   \n      }\n\n      #disconnect {\n        background-image: url(./assets/phonelink_off.svg);\n        background-repeat: no-repeat;\n        background-position-x: 4px;\n        background-position-y: 2px;\n      }\n\n      #start {\n        background-image: url(./assets/video-start.svg);\n        background-repeat: no-repeat;\n        background-position-x: 4px;\n        background-position-y: 0px;\n      }\n\n      #stop {\n        background-image: url(./assets/video-stop.svg);\n        background-repeat: no-repeat;\n        background-position-x: 4px;\n        background-position-y: 0px;\n      }\n\n      #screencapture {\n        background-image: url(./assets/screenshot.svg);\n        background-repeat: no-repeat;\n        background-position-x: 4px;\n        background-position-y: 0px;        \n      }\n\n      /* #download {\n        background-image: url(./assets/save.svg);\n        background-repeat: no-repeat;\n        background-position-x: 4px;\n        background-position-y: 0px;\n        text-decoration: none;\n        color: black;\n        background-color: lightgrey;\n        width: 14px;\n      } */\n\n      .hidden {\n        display: none;\n      }\n    </style>\n  </head>\n  <body>\n    <header>\n      <h1>WebADB Screenrecord</h1>\n    </header>\n    <div class=\"status\">\n      <div id=\"status\">\n        Connect to a device to start\n      </div>\n    </div>            \n    <content>\n      <div class=\"buttons\">\n        <button id=\"connect\" class=\"button\">Connect</button>\n        <button id=\"disconnect\" class=\"button hidden\">Disconnect</button>\n        <button id=\"start\" class=\"button\" disabled>Record</button>\n        <button id=\"stop\" class=\"button hidden\" disabled>Stop</button>\n        <button id=\"screencapture\" class=\"button\" disabled>Screenshot</button>\n      </div>\n      <div class=\"video-container\">\n        <div>\n          <video id=\"video\" controls></video>\n          <img id=\"screenshot\" class=\"hidden\"></img>\n        </div>\n        <a id=\"download\" class='hidden' href=\"#\" download=\"recording.mp4\">Download</a>          \n      </div>\n  </content>\n  </body>\n</html>"
  },
  {
    "path": "demo/public/sw.js",
    "content": "if(!self.define){let e,s={};const r=(r,i)=>(r=new URL(r+\".js\",i).href,s[r]||new Promise(s=>{if(\"document\"in self){const e=document.createElement(\"script\");e.src=r,e.onload=s,document.head.appendChild(e)}else e=r,importScripts(r),s()}).then(()=>{let e=s[r];if(!e)throw new Error(`Module ${r} didn’t register its module`);return e}));self.define=(i,c)=>{const a=e||(\"document\"in self?document.currentScript.src:\"\")||location.href;if(s[a])return;let f={};const d=e=>r(e,a),n={module:{uri:a},exports:f,require:d};s[a]=Promise.all(i.map(e=>n[e]||d(e))).then(e=>(c(...e),f))}}define([\"./workbox-aa2f3006\"],function(e){\"use strict\";self.addEventListener(\"message\",e=>{e.data&&\"SKIP_WAITING\"===e.data.type&&self.skipWaiting()}),e.precacheAndRoute([{url:\"video.html\",revision:\"b6d0ffeea0e993d0e974b66370f26452\"},{url:\"screenrecord.html\",revision:\"377711bee26708ee49bbc958ff816a69\"},{url:\"livestream.html\",revision:\"a324589fdd328bb93f6223a75a1b5272\"},{url:\"interactiveshell.html\",revision:\"522987ae9971ed8dacec16fec3b34e96\"},{url:\"index.html\",revision:\"50d0895f12bf0f8f169e31c243dcc4d8\"},{url:\"404.html\",revision:\"0a27a4163254fc8fce870c8cc3a3f94f\"},{url:\"js/screenrecord.js\",revision:\"e4311f5051f5fda43649113c8f23beaa\"},{url:\"js/screenrecord.d.ts\",revision:\"3a0443f883901bd8fc6324e5b777dafc\"},{url:\"js/screenrecord.bundle.js\",revision:\"2e8477d5880a4f415bf0e46b36f72b41\"},{url:\"js/livestream.js\",revision:\"b03ac911e603183acd922c0828c896da\"},{url:\"js/livestream.d.ts\",revision:\"39ab92c97178c18e749a42347f51a2a5\"},{url:\"js/livestream.bundle.js\",revision:\"dd97e4b03accf0fc87306d7c9fda0b38\"},{url:\"js/interactiveshell.js\",revision:\"13a41015a7ad92da6c1be7f759fcfaf0\"},{url:\"js/interactiveshell.d.ts\",revision:\"5681954b090c4fca99f7f787498253b6\"},{url:\"js/interactiveshell.bundle.js\",revision:\"d5c056ec7005ec28a6397fe1aa86239c\"},{url:\"assets/video-stop.svg\",revision:\"89455ac48d09d5d61abde3e3353da59b\"},{url:\"assets/video-start.svg\",revision:\"3cbeae541c34c92d309251e4fcfcf243\"},{url:\"assets/screenshot.svg\",revision:\"db7f624d5f5cd6a032c8d65890615069\"},{url:\"assets/save.svg\",revision:\"ce3185959ba8c7cdb1f81ce5003d76bc\"},{url:\"assets/phonelink_off.svg\",revision:\"55d6032b4173a7fdbef16989c6844211\"},{url:\"assets/phonelink.svg\",revision:\"b16eaf9232fa7862f7ba5870c335e042\"}],{})});\n//# sourceMappingURL=sw.js.map\n"
  },
  {
    "path": "demo/public/video.html",
    "content": "<!doctype html>\n<html>\n<head>\n  <meta name=\"viewport\" content=\"initial-scale=1, maximum-scale=1\">\n  <meta charset=\"utf-8\">\n</head>\n<body>\n  <h1>Video</h1>\n  <video src=\"./recording.mp4\"  controls  type=\"video/mp4\"></video>\n</body>\n</html>"
  },
  {
    "path": "demo/public/workbox-69b5a3b7.js",
    "content": "define(\"./workbox-69b5a3b7.js\",[\"exports\"],(function(e){\"use strict\";try{self[\"workbox:core:5.1.4\"]&&_()}catch(e){}const t={googleAnalytics:\"googleAnalytics\",precache:\"precache-v2\",prefix:\"workbox\",runtime:\"runtime\",suffix:\"undefined\"!=typeof registration?registration.scope:\"\"},n=e=>[t.prefix,e,t.suffix].filter(e=>e&&e.length>0).join(\"-\"),s=e=>e||n(t.precache),i=e=>new URL(String(e),location.href).href.replace(new RegExp(\"^\"+location.origin),\"\"),c=(e,...t)=>{let n=e;return t.length>0&&(n+=\" :: \"+JSON.stringify(t)),n};class o extends Error{constructor(e,t){super(c(e,t)),this.name=e,this.details=t}}const r=new Set;const a=(e,t)=>e.filter(e=>t in e),u=async({request:e,mode:t,plugins:n=[]})=>{const s=a(n,\"cacheKeyWillBeUsed\");let i=e;for(const e of s)i=await e.cacheKeyWillBeUsed.call(e,{mode:t,request:i}),\"string\"==typeof i&&(i=new Request(i));return i},l=async({cacheName:e,request:t,event:n,matchOptions:s,plugins:i=[]})=>{const c=await self.caches.open(e),o=await u({plugins:i,request:t,mode:\"read\"});let r=await c.match(o,s);for(const t of i)if(\"cachedResponseWillBeUsed\"in t){const i=t.cachedResponseWillBeUsed;r=await i.call(t,{cacheName:e,event:n,matchOptions:s,cachedResponse:r,request:o})}return r},h=async({cacheName:e,request:t,response:n,event:s,plugins:c=[],matchOptions:h})=>{const f=await u({plugins:c,request:t,mode:\"write\"});if(!n)throw new o(\"cache-put-with-no-response\",{url:i(f.url)});const w=await(async({request:e,response:t,event:n,plugins:s=[]})=>{let i=t,c=!1;for(const t of s)if(\"cacheWillUpdate\"in t){c=!0;const s=t.cacheWillUpdate;if(i=await s.call(t,{request:e,response:i,event:n}),!i)break}return c||(i=i&&200===i.status?i:void 0),i||null})({event:s,plugins:c,response:n,request:f});if(!w)return;const d=await self.caches.open(e),p=a(c,\"cacheDidUpdate\"),y=p.length>0?await l({cacheName:e,matchOptions:h,request:f}):null;try{await d.put(f,w)}catch(e){throw\"QuotaExceededError\"===e.name&&await async function(){for(const e of r)await e()}(),e}for(const t of p)await t.cacheDidUpdate.call(t,{cacheName:e,event:s,oldResponse:y,newResponse:w,request:f})},f=async({request:e,fetchOptions:t,event:n,plugins:s=[]})=>{if(\"string\"==typeof e&&(e=new Request(e)),n instanceof FetchEvent&&n.preloadResponse){const e=await n.preloadResponse;if(e)return e}const i=a(s,\"fetchDidFail\"),c=i.length>0?e.clone():null;try{for(const t of s)if(\"requestWillFetch\"in t){const s=t.requestWillFetch,i=e.clone();e=await s.call(t,{request:i,event:n})}}catch(e){throw new o(\"plugin-error-request-will-fetch\",{thrownError:e})}const r=e.clone();try{let i;i=\"navigate\"===e.mode?await fetch(e):await fetch(e,t);for(const e of s)\"fetchDidSucceed\"in e&&(i=await e.fetchDidSucceed.call(e,{event:n,request:r,response:i}));return i}catch(e){for(const t of i)await t.fetchDidFail.call(t,{error:e,event:n,originalRequest:c.clone(),request:r.clone()});throw e}};let w;async function d(e,t){const n=e.clone(),s={headers:new Headers(n.headers),status:n.status,statusText:n.statusText},i=t?t(s):s,c=function(){if(void 0===w){const e=new Response(\"\");if(\"body\"in e)try{new Response(e.body),w=!0}catch(e){w=!1}w=!1}return w}()?n.body:await n.blob();return new Response(c,i)}try{self[\"workbox:precaching:5.1.4\"]&&_()}catch(e){}function p(e){if(!e)throw new o(\"add-to-cache-list-unexpected-type\",{entry:e});if(\"string\"==typeof e){const t=new URL(e,location.href);return{cacheKey:t.href,url:t.href}}const{revision:t,url:n}=e;if(!n)throw new o(\"add-to-cache-list-unexpected-type\",{entry:e});if(!t){const e=new URL(n,location.href);return{cacheKey:e.href,url:e.href}}const s=new URL(n,location.href),i=new URL(n,location.href);return s.searchParams.set(\"__WB_REVISION__\",t),{cacheKey:s.href,url:i.href}}class y{constructor(e){this.t=s(e),this.s=new Map,this.i=new Map,this.o=new Map}addToCacheList(e){const t=[];for(const n of e){\"string\"==typeof n?t.push(n):n&&void 0===n.revision&&t.push(n.url);const{cacheKey:e,url:s}=p(n),i=\"string\"!=typeof n&&n.revision?\"reload\":\"default\";if(this.s.has(s)&&this.s.get(s)!==e)throw new o(\"add-to-cache-list-conflicting-entries\",{firstEntry:this.s.get(s),secondEntry:e});if(\"string\"!=typeof n&&n.integrity){if(this.o.has(e)&&this.o.get(e)!==n.integrity)throw new o(\"add-to-cache-list-conflicting-integrities\",{url:s});this.o.set(e,n.integrity)}if(this.s.set(s,e),this.i.set(s,i),t.length>0){const e=`Workbox is precaching URLs without revision info: ${t.join(\", \")}\\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(e)}}}async install({event:e,plugins:t}={}){const n=[],s=[],i=await self.caches.open(this.t),c=await i.keys(),o=new Set(c.map(e=>e.url));for(const[e,t]of this.s)o.has(t)?s.push(e):n.push({cacheKey:t,url:e});const r=n.map(({cacheKey:n,url:s})=>{const i=this.o.get(n),c=this.i.get(s);return this.u({cacheKey:n,cacheMode:c,event:e,integrity:i,plugins:t,url:s})});return await Promise.all(r),{updatedURLs:n.map(e=>e.url),notUpdatedURLs:s}}async activate(){const e=await self.caches.open(this.t),t=await e.keys(),n=new Set(this.s.values()),s=[];for(const i of t)n.has(i.url)||(await e.delete(i),s.push(i.url));return{deletedURLs:s}}async u({cacheKey:e,url:t,cacheMode:n,event:s,plugins:i,integrity:c}){const r=new Request(t,{integrity:c,cache:n,credentials:\"same-origin\"});let a,u=await f({event:s,plugins:i,request:r});for(const e of i||[])\"cacheWillUpdate\"in e&&(a=e);if(!(a?await a.cacheWillUpdate({event:s,request:r,response:u}):u.status<400))throw new o(\"bad-precaching-response\",{url:t,status:u.status});u.redirected&&(u=await d(u)),await h({event:s,plugins:i,response:u,request:e===t?r:new Request(e),cacheName:this.t,matchOptions:{ignoreSearch:!0}})}getURLsToCacheKeys(){return this.s}getCachedURLs(){return[...this.s.keys()]}getCacheKeyForURL(e){const t=new URL(e,location.href);return this.s.get(t.href)}async matchPrecache(e){const t=e instanceof Request?e.url:e,n=this.getCacheKeyForURL(t);if(n){return(await self.caches.open(this.t)).match(n)}}createHandler(e=!0){return async({request:t})=>{try{const e=await this.matchPrecache(t);if(e)return e;throw new o(\"missing-precache-entry\",{cacheName:this.t,url:t instanceof Request?t.url:t})}catch(n){if(e)return fetch(t);throw n}}}createHandlerBoundToURL(e,t=!0){if(!this.getCacheKeyForURL(e))throw new o(\"non-precached-url\",{url:e});const n=this.createHandler(t),s=new Request(e);return()=>n({request:s})}}let g;const R=()=>(g||(g=new y),g);const q=(e,t)=>{const n=R().getURLsToCacheKeys();for(const s of function*(e,{ignoreURLParametersMatching:t,directoryIndex:n,cleanURLs:s,urlManipulation:i}={}){const c=new URL(e,location.href);c.hash=\"\",yield c.href;const o=function(e,t=[]){for(const n of[...e.searchParams.keys()])t.some(e=>e.test(n))&&e.searchParams.delete(n);return e}(c,t);if(yield o.href,n&&o.pathname.endsWith(\"/\")){const e=new URL(o.href);e.pathname+=n,yield e.href}if(s){const e=new URL(o.href);e.pathname+=\".html\",yield e.href}if(i){const e=i({url:c});for(const t of e)yield t.href}}(e,t)){const e=n.get(s);if(e)return e}};let U=!1;function m(e){U||((({ignoreURLParametersMatching:e=[/^utm_/],directoryIndex:t=\"index.html\",cleanURLs:n=!0,urlManipulation:i}={})=>{const c=s();self.addEventListener(\"fetch\",s=>{const o=q(s.request.url,{cleanURLs:n,directoryIndex:t,ignoreURLParametersMatching:e,urlManipulation:i});if(!o)return;let r=self.caches.open(c).then(e=>e.match(o)).then(e=>e||fetch(o));s.respondWith(r)})})(e),U=!0)}const v=[],L={get:()=>v,add(e){v.push(...e)}},x=e=>{const t=R(),n=L.get();e.waitUntil(t.install({event:e,plugins:n}).catch(e=>{throw e}))},K=e=>{const t=R();e.waitUntil(t.activate())};e.precacheAndRoute=function(e,t){!function(e){R().addToCacheList(e),e.length>0&&(self.addEventListener(\"install\",x),self.addEventListener(\"activate\",K))}(e),m(t)}}));\n//# sourceMappingURL=workbox-69b5a3b7.js.map\n"
  },
  {
    "path": "demo/public/workbox-aa2f3006.js",
    "content": "define([\"exports\"],function(t){\"use strict\";try{self[\"workbox:core:7.3.0\"]&&_()}catch(t){}const e=(t,...e)=>{let s=t;return e.length>0&&(s+=` :: ${JSON.stringify(e)}`),s};class s extends Error{constructor(t,s){super(e(t,s)),this.name=t,this.details=s}}try{self[\"workbox:routing:7.3.0\"]&&_()}catch(t){}const n=t=>t&&\"object\"==typeof t?t:{handle:t};class i{constructor(t,e,s=\"GET\"){this.handler=n(e),this.match=t,this.method=s}setCatchHandler(t){this.catchHandler=n(t)}}class r extends i{constructor(t,e,s){super(({url:e})=>{const s=t.exec(e.href);if(s&&(e.origin===location.origin||0===s.index))return s.slice(1)},e,s)}}class o{constructor(){this.t=new Map,this.i=new Map}get routes(){return this.t}addFetchListener(){self.addEventListener(\"fetch\",t=>{const{request:e}=t,s=this.handleRequest({request:e,event:t});s&&t.respondWith(s)})}addCacheListener(){self.addEventListener(\"message\",t=>{if(t.data&&\"CACHE_URLS\"===t.data.type){const{payload:e}=t.data,s=Promise.all(e.urlsToCache.map(e=>{\"string\"==typeof e&&(e=[e]);const s=new Request(...e);return this.handleRequest({request:s,event:t})}));t.waitUntil(s),t.ports&&t.ports[0]&&s.then(()=>t.ports[0].postMessage(!0))}})}handleRequest({request:t,event:e}){const s=new URL(t.url,location.href);if(!s.protocol.startsWith(\"http\"))return;const n=s.origin===location.origin,{params:i,route:r}=this.findMatchingRoute({event:e,request:t,sameOrigin:n,url:s});let o=r&&r.handler;const c=t.method;if(!o&&this.i.has(c)&&(o=this.i.get(c)),!o)return;let a;try{a=o.handle({url:s,request:t,event:e,params:i})}catch(t){a=Promise.reject(t)}const h=r&&r.catchHandler;return a instanceof Promise&&(this.o||h)&&(a=a.catch(async n=>{if(h)try{return await h.handle({url:s,request:t,event:e,params:i})}catch(t){t instanceof Error&&(n=t)}if(this.o)return this.o.handle({url:s,request:t,event:e});throw n})),a}findMatchingRoute({url:t,sameOrigin:e,request:s,event:n}){const i=this.t.get(s.method)||[];for(const r of i){let i;const o=r.match({url:t,sameOrigin:e,request:s,event:n});if(o)return i=o,(Array.isArray(i)&&0===i.length||o.constructor===Object&&0===Object.keys(o).length||\"boolean\"==typeof o)&&(i=void 0),{route:r,params:i}}return{}}setDefaultHandler(t,e=\"GET\"){this.i.set(e,n(t))}setCatchHandler(t){this.o=n(t)}registerRoute(t){this.t.has(t.method)||this.t.set(t.method,[]),this.t.get(t.method).push(t)}unregisterRoute(t){if(!this.t.has(t.method))throw new s(\"unregister-route-but-not-found-with-method\",{method:t.method});const e=this.t.get(t.method).indexOf(t);if(!(e>-1))throw new s(\"unregister-route-route-not-registered\");this.t.get(t.method).splice(e,1)}}let c;const a={googleAnalytics:\"googleAnalytics\",precache:\"precache-v2\",prefix:\"workbox\",runtime:\"runtime\",suffix:\"undefined\"!=typeof registration?registration.scope:\"\"},h=t=>[a.prefix,t,a.suffix].filter(t=>t&&t.length>0).join(\"-\"),u=t=>t||h(a.precache),l=t=>t||h(a.runtime);function f(t,e){const s=e();return t.waitUntil(s),s}try{self[\"workbox:precaching:7.3.0\"]&&_()}catch(t){}function w(t){if(!t)throw new s(\"add-to-cache-list-unexpected-type\",{entry:t});if(\"string\"==typeof t){const e=new URL(t,location.href);return{cacheKey:e.href,url:e.href}}const{revision:e,url:n}=t;if(!n)throw new s(\"add-to-cache-list-unexpected-type\",{entry:t});if(!e){const t=new URL(n,location.href);return{cacheKey:t.href,url:t.href}}const i=new URL(n,location.href),r=new URL(n,location.href);return i.searchParams.set(\"__WB_REVISION__\",e),{cacheKey:i.href,url:r.href}}class d{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:t,state:e})=>{e&&(e.originalRequest=t)},this.cachedResponseWillBeUsed=async({event:t,state:e,cachedResponse:s})=>{if(\"install\"===t.type&&e&&e.originalRequest&&e.originalRequest instanceof Request){const t=e.originalRequest.url;s?this.notUpdatedURLs.push(t):this.updatedURLs.push(t)}return s}}}class p{constructor({precacheController:t}){this.cacheKeyWillBeUsed=async({request:t,params:e})=>{const s=(null==e?void 0:e.cacheKey)||this.h.getCacheKeyForURL(t.url);return s?new Request(s,{headers:t.headers}):t},this.h=t}}let y;async function g(t,e){let n=null;if(t.url){n=new URL(t.url).origin}if(n!==self.location.origin)throw new s(\"cross-origin-copy-response\",{origin:n});const i=t.clone(),r={headers:new Headers(i.headers),status:i.status,statusText:i.statusText},o=e?e(r):r,c=function(){if(void 0===y){const t=new Response(\"\");if(\"body\"in t)try{new Response(t.body),y=!0}catch(t){y=!1}y=!1}return y}()?i.body:await i.blob();return new Response(c,o)}function R(t,e){const s=new URL(t);for(const t of e)s.searchParams.delete(t);return s.href}class m{constructor(){this.promise=new Promise((t,e)=>{this.resolve=t,this.reject=e})}}const v=new Set;try{self[\"workbox:strategies:7.3.0\"]&&_()}catch(t){}function q(t){return\"string\"==typeof t?new Request(t):t}class U{constructor(t,e){this.u={},Object.assign(this,e),this.event=e.event,this.l=t,this.p=new m,this.R=[],this.m=[...t.plugins],this.v=new Map;for(const t of this.m)this.v.set(t,{});this.event.waitUntil(this.p.promise)}async fetch(t){const{event:e}=this;let n=q(t);if(\"navigate\"===n.mode&&e instanceof FetchEvent&&e.preloadResponse){const t=await e.preloadResponse;if(t)return t}const i=this.hasCallback(\"fetchDidFail\")?n.clone():null;try{for(const t of this.iterateCallbacks(\"requestWillFetch\"))n=await t({request:n.clone(),event:e})}catch(t){if(t instanceof Error)throw new s(\"plugin-error-request-will-fetch\",{thrownErrorMessage:t.message})}const r=n.clone();try{let t;t=await fetch(n,\"navigate\"===n.mode?void 0:this.l.fetchOptions);for(const s of this.iterateCallbacks(\"fetchDidSucceed\"))t=await s({event:e,request:r,response:t});return t}catch(t){throw i&&await this.runCallbacks(\"fetchDidFail\",{error:t,event:e,originalRequest:i.clone(),request:r.clone()}),t}}async fetchAndCachePut(t){const e=await this.fetch(t),s=e.clone();return this.waitUntil(this.cachePut(t,s)),e}async cacheMatch(t){const e=q(t);let s;const{cacheName:n,matchOptions:i}=this.l,r=await this.getCacheKey(e,\"read\"),o=Object.assign(Object.assign({},i),{cacheName:n});s=await caches.match(r,o);for(const t of this.iterateCallbacks(\"cachedResponseWillBeUsed\"))s=await t({cacheName:n,matchOptions:i,cachedResponse:s,request:r,event:this.event})||void 0;return s}async cachePut(t,e){const n=q(t);var i;await(i=0,new Promise(t=>setTimeout(t,i)));const r=await this.getCacheKey(n,\"write\");if(!e)throw new s(\"cache-put-with-no-response\",{url:(o=r.url,new URL(String(o),location.href).href.replace(new RegExp(`^${location.origin}`),\"\"))});var o;const c=await this.q(e);if(!c)return!1;const{cacheName:a,matchOptions:h}=this.l,u=await self.caches.open(a),l=this.hasCallback(\"cacheDidUpdate\"),f=l?await async function(t,e,s,n){const i=R(e.url,s);if(e.url===i)return t.match(e,n);const r=Object.assign(Object.assign({},n),{ignoreSearch:!0}),o=await t.keys(e,r);for(const e of o)if(i===R(e.url,s))return t.match(e,n)}(u,r.clone(),[\"__WB_REVISION__\"],h):null;try{await u.put(r,l?c.clone():c)}catch(t){if(t instanceof Error)throw\"QuotaExceededError\"===t.name&&await async function(){for(const t of v)await t()}(),t}for(const t of this.iterateCallbacks(\"cacheDidUpdate\"))await t({cacheName:a,oldResponse:f,newResponse:c.clone(),request:r,event:this.event});return!0}async getCacheKey(t,e){const s=`${t.url} | ${e}`;if(!this.u[s]){let n=t;for(const t of this.iterateCallbacks(\"cacheKeyWillBeUsed\"))n=q(await t({mode:e,request:n,event:this.event,params:this.params}));this.u[s]=n}return this.u[s]}hasCallback(t){for(const e of this.l.plugins)if(t in e)return!0;return!1}async runCallbacks(t,e){for(const s of this.iterateCallbacks(t))await s(e)}*iterateCallbacks(t){for(const e of this.l.plugins)if(\"function\"==typeof e[t]){const s=this.v.get(e),n=n=>{const i=Object.assign(Object.assign({},n),{state:s});return e[t](i)};yield n}}waitUntil(t){return this.R.push(t),t}async doneWaiting(){for(;this.R.length;){const t=this.R.splice(0),e=(await Promise.allSettled(t)).find(t=>\"rejected\"===t.status);if(e)throw e.reason}}destroy(){this.p.resolve(null)}async q(t){let e=t,s=!1;for(const t of this.iterateCallbacks(\"cacheWillUpdate\"))if(e=await t({request:this.request,response:e,event:this.event})||void 0,s=!0,!e)break;return s||e&&200!==e.status&&(e=void 0),e}}class L{constructor(t={}){this.cacheName=l(t.cacheName),this.plugins=t.plugins||[],this.fetchOptions=t.fetchOptions,this.matchOptions=t.matchOptions}handle(t){const[e]=this.handleAll(t);return e}handleAll(t){t instanceof FetchEvent&&(t={event:t,request:t.request});const e=t.event,s=\"string\"==typeof t.request?new Request(t.request):t.request,n=\"params\"in t?t.params:void 0,i=new U(this,{event:e,request:s,params:n}),r=this.U(i,s,e);return[r,this.L(r,i,s,e)]}async U(t,e,n){let i;await t.runCallbacks(\"handlerWillStart\",{event:n,request:e});try{if(i=await this._(e,t),!i||\"error\"===i.type)throw new s(\"no-response\",{url:e.url})}catch(s){if(s instanceof Error)for(const r of t.iterateCallbacks(\"handlerDidError\"))if(i=await r({error:s,event:n,request:e}),i)break;if(!i)throw s}for(const s of t.iterateCallbacks(\"handlerWillRespond\"))i=await s({event:n,request:e,response:i});return i}async L(t,e,s,n){let i,r;try{i=await t}catch(r){}try{await e.runCallbacks(\"handlerDidRespond\",{event:n,request:s,response:i}),await e.doneWaiting()}catch(t){t instanceof Error&&(r=t)}if(await e.runCallbacks(\"handlerDidComplete\",{event:n,request:s,response:i,error:r}),e.destroy(),r)throw r}}class b extends L{constructor(t={}){t.cacheName=u(t.cacheName),super(t),this.C=!1!==t.fallbackToNetwork,this.plugins.push(b.copyRedirectedCacheableResponsesPlugin)}async _(t,e){const s=await e.cacheMatch(t);return s||(e.event&&\"install\"===e.event.type?await this.O(t,e):await this.N(t,e))}async N(t,e){let n;const i=e.params||{};if(!this.C)throw new s(\"missing-precache-entry\",{cacheName:this.cacheName,url:t.url});{const s=i.integrity,r=t.integrity,o=!r||r===s;n=await e.fetch(new Request(t,{integrity:\"no-cors\"!==t.mode?r||s:void 0})),s&&o&&\"no-cors\"!==t.mode&&(this.j(),await e.cachePut(t,n.clone()))}return n}async O(t,e){this.j();const n=await e.fetch(t);if(!await e.cachePut(t,n.clone()))throw new s(\"bad-precaching-response\",{url:t.url,status:n.status});return n}j(){let t=null,e=0;for(const[s,n]of this.plugins.entries())n!==b.copyRedirectedCacheableResponsesPlugin&&(n===b.defaultPrecacheCacheabilityPlugin&&(t=s),n.cacheWillUpdate&&e++);0===e?this.plugins.push(b.defaultPrecacheCacheabilityPlugin):e>1&&null!==t&&this.plugins.splice(t,1)}}b.defaultPrecacheCacheabilityPlugin={cacheWillUpdate:async({response:t})=>!t||t.status>=400?null:t},b.copyRedirectedCacheableResponsesPlugin={cacheWillUpdate:async({response:t})=>t.redirected?await g(t):t};class C{constructor({cacheName:t,plugins:e=[],fallbackToNetwork:s=!0}={}){this.k=new Map,this.K=new Map,this.P=new Map,this.l=new b({cacheName:u(t),plugins:[...e,new p({precacheController:this})],fallbackToNetwork:s}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this.l}precache(t){this.addToCacheList(t),this.T||(self.addEventListener(\"install\",this.install),self.addEventListener(\"activate\",this.activate),this.T=!0)}addToCacheList(t){const e=[];for(const n of t){\"string\"==typeof n?e.push(n):n&&void 0===n.revision&&e.push(n.url);const{cacheKey:t,url:i}=w(n),r=\"string\"!=typeof n&&n.revision?\"reload\":\"default\";if(this.k.has(i)&&this.k.get(i)!==t)throw new s(\"add-to-cache-list-conflicting-entries\",{firstEntry:this.k.get(i),secondEntry:t});if(\"string\"!=typeof n&&n.integrity){if(this.P.has(t)&&this.P.get(t)!==n.integrity)throw new s(\"add-to-cache-list-conflicting-integrities\",{url:i});this.P.set(t,n.integrity)}if(this.k.set(i,t),this.K.set(i,r),e.length>0){const t=`Workbox is precaching URLs without revision info: ${e.join(\", \")}\\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(t)}}}install(t){return f(t,async()=>{const e=new d;this.strategy.plugins.push(e);for(const[e,s]of this.k){const n=this.P.get(s),i=this.K.get(e),r=new Request(e,{integrity:n,cache:i,credentials:\"same-origin\"});await Promise.all(this.strategy.handleAll({params:{cacheKey:s},request:r,event:t}))}const{updatedURLs:s,notUpdatedURLs:n}=e;return{updatedURLs:s,notUpdatedURLs:n}})}activate(t){return f(t,async()=>{const t=await self.caches.open(this.strategy.cacheName),e=await t.keys(),s=new Set(this.k.values()),n=[];for(const i of e)s.has(i.url)||(await t.delete(i),n.push(i.url));return{deletedURLs:n}})}getURLsToCacheKeys(){return this.k}getCachedURLs(){return[...this.k.keys()]}getCacheKeyForURL(t){const e=new URL(t,location.href);return this.k.get(e.href)}getIntegrityForCacheKey(t){return this.P.get(t)}async matchPrecache(t){const e=t instanceof Request?t.url:t,s=this.getCacheKeyForURL(e);if(s){return(await self.caches.open(this.strategy.cacheName)).match(s)}}createHandlerBoundToURL(t){const e=this.getCacheKeyForURL(t);if(!e)throw new s(\"non-precached-url\",{url:t});return s=>(s.request=new Request(t),s.params=Object.assign({cacheKey:e},s.params),this.strategy.handle(s))}}let E;const O=()=>(E||(E=new C),E);class x extends i{constructor(t,e){super(({request:s})=>{const n=t.getURLsToCacheKeys();for(const i of function*(t,{ignoreURLParametersMatching:e=[/^utm_/,/^fbclid$/],directoryIndex:s=\"index.html\",cleanURLs:n=!0,urlManipulation:i}={}){const r=new URL(t,location.href);r.hash=\"\",yield r.href;const o=function(t,e=[]){for(const s of[...t.searchParams.keys()])e.some(t=>t.test(s))&&t.searchParams.delete(s);return t}(r,e);if(yield o.href,s&&o.pathname.endsWith(\"/\")){const t=new URL(o.href);t.pathname+=s,yield t.href}if(n){const t=new URL(o.href);t.pathname+=\".html\",yield t.href}if(i){const t=i({url:r});for(const e of t)yield e.href}}(s.url,e)){const e=n.get(i);if(e){return{cacheKey:e,integrity:t.getIntegrityForCacheKey(e)}}}},t.strategy)}}function N(t){const e=O();!function(t,e,n){let a;if(\"string\"==typeof t){const s=new URL(t,location.href);a=new i(({url:t})=>t.href===s.href,e,n)}else if(t instanceof RegExp)a=new r(t,e,n);else if(\"function\"==typeof t)a=new i(t,e,n);else{if(!(t instanceof i))throw new s(\"unsupported-route-type\",{moduleName:\"workbox-routing\",funcName:\"registerRoute\",paramName:\"capture\"});a=t}(c||(c=new o,c.addFetchListener(),c.addCacheListener()),c).registerRoute(a)}(new x(e,t))}t.precacheAndRoute=function(t,e){!function(t){O().precache(t)}(t),N(e)}});\n//# sourceMappingURL=workbox-aa2f3006.js.map\n"
  },
  {
    "path": "demo/src/interactiveshell.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nimport {AdbClient, IndexedDbKeyStore, Options, Shell, WebUsbTransport} from 'wadb';\n\nconst connectButton = document.querySelector('#connect')!;\nconst disconnectButton = document.querySelector('#disconnect')!;\nconst output = document.querySelector('#output')!;\nconst input = (document.querySelector('#input') as HTMLInputElement)!;\n\nconst options: Options = {\n  debug: true,\n  useChecksum: false,\n  dump: false,\n  keySize: 2048,\n};\n\nconst keyStore = new IndexedDbKeyStore();\n\nlet transport: WebUsbTransport | null = null;\nlet adbClient: AdbClient | null = null;\nlet shell: Shell | null = null;\n\nfunction appendToCode(text: string) {\n  const span = document.createElement('span');\n  span.innerText = text;\n  output.appendChild(span);\n  output.scrollTop = output.scrollHeight;\n}\n\nfunction sendCommand(cmd: string) {\n  shell!.write(cmd + '\\n');\n}\n\nconnectButton.addEventListener('click', async (_e) => {\n  try {\n    transport = await WebUsbTransport.open(options);\n    adbClient = new AdbClient(transport, options, keyStore);\n    await adbClient.connect();\n    shell = await adbClient.interactiveShell(appendToCode);\n\n    disconnectButton.classList.toggle('hidden');\n    connectButton.classList.toggle('hidden');\n  } catch(e) {\n    console.error('Connection Failed: ', e);\n  }\n});\n\ndisconnectButton.addEventListener('click', async (_e) => {\n  try {\n    await shell?.close();\n    await transport?.close();\n    transport = null;\n    adbClient = null;\n    shell = null;\n  } catch (e) {\n    console.error('Error closing the connection', e);\n  }\n  disconnectButton.classList.toggle('hidden');\n  connectButton.classList.toggle('hidden');\n});\n\ninput.addEventListener('keyup', (e) => {\n  if (e.keyCode === 13) {\n    e.preventDefault();\n    sendCommand(input.value);\n    input.value = '';\n    return false;\n  }\n  return true;\n});\n"
  },
  {
    "path": "demo/src/livestream.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nimport {WebUsbTransport, AdbClient, Options, IndexedDbKeyStore, Stream} from 'wadb';\n\nlet transport: WebUsbTransport | null;\nlet adbClient: AdbClient | null;\n\nconst connectButton = document.querySelector('#connect')!;\nconst disconnectButton = document.querySelector('#disconnect')!;\nconst startButton = document.querySelector('#start')!;\nconst stopButton = document.querySelector('#stop')!;\nconst video: HTMLVideoElement = (document.querySelector('#video') as HTMLVideoElement)!;\nconst download = (document.querySelector('#download') as HTMLAnchorElement)!;\nconst status = document.querySelector('#status')!;\n\nconst options: Options = {\n  debug: true,\n  useChecksum: false,\n  dump: false,\n  keySize: 2048,\n};\n\nconst keyStore = new IndexedDbKeyStore();\n\nconnectButton.addEventListener('click', async (_event) => {\n  try {\n    transport = await WebUsbTransport.open(options);\n    adbClient = new AdbClient(transport, options, keyStore);\n\n    status.textContent = 'Accept prompt on device';\n    const adbConnectionInformation = await adbClient.connect();\n    status.textContent = 'Connected and ready';\n    console.log('Connected: ', adbConnectionInformation);\n\n    connectButton.classList.toggle('hidden');\n    disconnectButton.classList.toggle('hidden');\n  } catch(e) {\n    console.error('Connection Failed: ', e);\n    status.textContent = 'Failed to connect to a device';\n  }\n});\n\ndisconnectButton.addEventListener('click', async (_event) => {\n  try {\n    if (adbClient) {\n      try {\n        await adbClient.disconnect();\n      } catch (e) {\n        console.log('Error disconnecting ADB Client: ', e);\n      }\n      adbClient = null;\n    }\n    if (transport) {\n      await transport.close();\n      transport = null;\n    }\n    connectButton.classList.toggle('hidden');\n    disconnectButton.classList.toggle('hidden');\n    status.textContent = 'Connect to a device to start';\n  } catch(e) {\n    console.error('Disconnecting Failed: ', e);\n  }\n});\n\nlet shell: Stream | null = null;\nstartButton.addEventListener('click', async() => {\n  // const mediaSource = new MediaSource();\n  // const url = URL.createObjectURL(mediaSource);\n  // video.src = url;\n\n  status.textContent = 'Recording...';\n  stopButton.classList.toggle('hidden');\n  startButton.classList.toggle('hidden');\n\n  const textDecoder = new TextDecoder();\n  // mediaSource.addEventListener('sourceopen', async () => {\n    shell = await Stream.open(adbClient!, 'exec:screenrecord --output-format=h264 -', options);\n    // const audioSourceBuffer = mediaSource.addSourceBuffer('video/mp4; codecs=\"mp4a.40.2\"');\n    const chunks: Uint8Array<ArrayBuffer>[] = [];\n    let i = 0;\n\n    let msg;\n    while (true) {\n      console.log(++i);\n      msg = await shell!.read();\n      await shell!.write('OKAY');\n      if (msg.header.cmd === 'CLSE') {\n        break;\n      }\n      console.log(textDecoder.decode(msg.data!.buffer));\n      chunks.push(new Uint8Array(msg.data!.buffer as ArrayBuffer));\n    }\n    console.log(chunks.length);\n    const objectUrl = URL.createObjectURL(new Blob(chunks));\n    video.src = objectUrl;\n    download.href = objectUrl;\n  // });\n});\n\nstopButton.addEventListener('click', async() => {\n  await shell?.write('CLSE');\n});\n"
  },
  {
    "path": "demo/src/screenrecord.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nimport {AdbClient, WebUsbTransport, Options, IndexedDbKeyStore, Stream} from 'wadb';\n\nlet transport: WebUsbTransport | null;\nlet adbClient: AdbClient | null;\n\nconst connectButton = document.querySelector('#connect')!;\nconst disconnectButton = document.querySelector('#disconnect')!;\nconst startButton = (document.querySelector('#start') as HTMLButtonElement)!;\nconst stopButton = (document.querySelector('#stop') as HTMLButtonElement)!;\nconst screenshotButton = (document.querySelector('#screencapture') as HTMLButtonElement)!;\nconst video: HTMLVideoElement = (document.querySelector('#video') as HTMLVideoElement)!;\nconst screenshot = (document.querySelector('#screenshot') as HTMLImageElement)!;\nconst download = (document.querySelector('#download') as HTMLAnchorElement)!;\nconst status = document.querySelector('#status')!;\n\nconst options: Options = {\n  debug: true,\n  useChecksum: false,\n  dump: false,\n  keySize: 2048,\n};\n\nconst keyStore = new IndexedDbKeyStore();\n\nconnectButton.addEventListener('click', async (_event) => {\n  try {\n    transport = await WebUsbTransport.open(options);\n    adbClient = new AdbClient(transport, options, keyStore);\n\n    status.textContent = 'Accept prompt on device';\n    const adbConnectionInformation = await adbClient.connect();\n    status.textContent = 'Connected and ready';\n    console.log('Connected: ', adbConnectionInformation);\n\n    connectButton.classList.toggle('hidden');\n    disconnectButton.classList.toggle('hidden');\n    startButton.removeAttribute('disabled');\n    screenshotButton.removeAttribute('disabled');\n    stopButton.removeAttribute('disabled');\n  } catch(e) {\n    console.error('Connection Failed: ', e);\n    status.textContent = 'Failed to connect to a device';\n  }\n});\n\ndisconnectButton.addEventListener('click', async (_event) => {\n  try {\n    if (adbClient) {\n      try {\n        await adbClient.disconnect();\n      } catch (e) {\n        console.log('Error disconnecting ADB Client: ', e);\n      }\n      adbClient = null;\n    }\n    if (transport) {\n      await transport.close();\n      transport = null;\n    }\n    connectButton.classList.toggle('hidden');\n    disconnectButton.classList.toggle('hidden');\n    startButton.disabled = true;\n    stopButton.disabled = true;\n    screenshotButton.disabled = true;\n    status.textContent = 'Connect to a device to start';\n  } catch(e) {\n    console.error('Disconnecting Failed: ', e);\n  }\n});\n\nconst RECORD_FILE_NAME = '/data/local/tmp/webadb-record.mp4';\n\nlet shell: Stream | null = null;\nstartButton.addEventListener('click', async() => {\n  shell = await Stream.open(adbClient!, `shell:screenrecord ${RECORD_FILE_NAME}`, options);\n  status.textContent = 'Recording...';\n  stopButton.classList.toggle('hidden');\n  startButton.classList.toggle('hidden');\n});\n\nstopButton.addEventListener('click', async() => {\n  // await shell!.write(String.fromCharCode(3) + '\\n'); // CTRL+C\n  status.textContent = 'Finishing Recording...';\n  await shell!.close();\n  status.textContent = 'Pulling video...';\n  // Trying to load the file straight away results in a broken file.\n  // Waiting for a couple of seconds fixes it. Maybe send STAT before\n  // attempting download.\n  setTimeout(async () => {\n    const result = await adbClient!.pull(RECORD_FILE_NAME);\n    const videoSrc = window.URL.createObjectURL(result);\n    video!.src = videoSrc;\n    download!.href = videoSrc;\n    download.download = 'recording.mp4';\n    stopButton.classList.toggle('hidden');\n    startButton.classList.toggle('hidden');\n    screenshot.classList.add('hidden');\n    video.classList.remove('hidden');\n    download.classList.remove('hidden');\n    status.textContent = 'Done! Connected and ready';\n  }, 2000);\n});\n\nscreenshotButton.addEventListener('click', async() => {\n  status.textContent = 'Generating Screenshot...';\n  await adbClient!.shell('screencap -p /data/local/tmp/screenshot.png');\n  status.textContent = 'Pulling image...';\n  const result = await adbClient!.pull('/data/local/tmp/screenshot.png');\n  const imageSrc = window.URL.createObjectURL(result);\n  screenshot.src = imageSrc;\n  download.href = imageSrc;\n  download.download = 'screenshot.png';\n  download.classList.remove('hidden');\n  screenshot.classList.remove('hidden');\n  video.classList.add('hidden');\n  status.textContent = 'Done! Connected and ready';\n});\n"
  },
  {
    "path": "demo/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    /* Basic Options */\n    // \"incremental\": true,                   /* Enable incremental compilation */\n    \"target\": \"ES2015\",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */\n    \"module\": \"es6\",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */\n    \"lib\": [\"ES2015\", \"dom\"],                             /* Specify library files to be included in the compilation. */\n    // \"allowJs\": true,                       /* Allow javascript files to be compiled. */\n    // \"checkJs\": true,                       /* Report errors in .js files. */\n    // \"jsx\": \"preserve\",                     /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */\n    \"declaration\": true,                   /* Generates corresponding '.d.ts' file. */\n    \"declarationMap\": true,                /* Generates a sourcemap for each corresponding '.d.ts' file. */\n    \"sourceMap\": true,                        /* Generates corresponding '.map' file. */\n    // \"outFile\": \"./\",                       /* Concatenate and emit output to single file. */\n    \"outDir\": \"./public/js\",                       /* Redirect output structure to the directory. */\n    // \"rootDir\": \"./\",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */\n    // \"composite\": true,                     /* Enable project compilation */\n    // \"tsBuildInfoFile\": \"./\",               /* Specify file to store incremental compilation information */\n    // \"removeComments\": true,                /* Do not emit comments to output. */\n    // \"noEmit\": true,                        /* Do not emit outputs. */\n    // \"importHelpers\": true,                 /* Import emit helpers from 'tslib'. */\n    // \"downlevelIteration\": true,            /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */\n    // \"isolatedModules\": true,               /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */\n\n    /* Strict Type-Checking Options */\n    \"strict\": true,                           /* Enable all strict type-checking options. */\n    // \"noImplicitAny\": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */\n    // \"strictNullChecks\": true,              /* Enable strict null checks. */\n    // \"strictFunctionTypes\": true,           /* Enable strict checking of function types. */\n    // \"strictBindCallApply\": true,           /* Enable strict 'bind', 'call', and 'apply' methods on functions. */\n    // \"strictPropertyInitialization\": true,  /* Enable strict checking of property initialization in classes. */\n    // \"noImplicitThis\": true,                /* Raise error on 'this' expressions with an implied 'any' type. */\n    // \"alwaysStrict\": true,                  /* Parse in strict mode and emit \"use strict\" for each source file. */\n\n    /* Additional Checks */\n    // \"noUnusedLocals\": true,                /* Report errors on unused locals. */\n    // \"noUnusedParameters\": true,            /* Report errors on unused parameters. */\n    // \"noImplicitReturns\": true,             /* Report error when not all code paths in function return a value. */\n    // \"noFallthroughCasesInSwitch\": true,    /* Report errors for fallthrough cases in switch statement. */\n\n    /* Module Resolution Options */\n    \"moduleResolution\": \"node\",            /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */\n    // \"baseUrl\": \"./\",                       /* Base directory to resolve non-absolute module names. */\n    // \"paths\": {},                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */\n    // \"rootDirs\": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */\n    // \"typeRoots\": [],                       /* List of folders to include type definitions from. */\n    // \"types\": [],                           /* Type declaration files to be included in compilation. */\n    // \"allowSyntheticDefaultImports\": true,  /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */\n    // \"esModuleInterop\": true,                  /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */\n    // \"preserveSymlinks\": true,              /* Do not resolve the real path of symlinks. */\n    // \"allowUmdGlobalAccess\": true,          /* Allow accessing UMD globals from modules. */\n\n    /* Source Map Options */\n    // \"sourceRoot\": \"\",                      /* Specify the location where debugger should locate TypeScript files instead of source locations. */\n    // \"mapRoot\": \"\",                         /* Specify the location where debugger should locate map files instead of generated locations. */\n    // \"inlineSourceMap\": true,               /* Emit a single file with source maps instead of having a separate file. */\n    // \"inlineSources\": true,                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */\n\n    /* Experimental Options */\n    // \"experimentalDecorators\": true,        /* Enables experimental support for ES7 decorators. */\n    // \"emitDecoratorMetadata\": true,         /* Enables experimental support for emitting type metadata for decorators. */\n\n    /* Advanced Options */\n    \"forceConsistentCasingInFileNames\": true,  /* Disallow inconsistently-cased references to the same file. */\n    \"skipLibCheck\": true,\n  },\n  \"include\": [\"**/*.ts\"]\n}\n"
  },
  {
    "path": "demo/webpack.config.js",
    "content": "/**\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nconst path = require('path');\n\nmodule.exports = {\n  entry: {\n    screenrecord: './src/screenrecord.ts',\n    interactiveshell: './src/interactiveshell.ts',\n    livestream: './src/livestream.ts'\n  },\n  module: {\n    rules: [\n      {\n        test: /\\.tsx?$/,\n        use: 'ts-loader',\n        exclude: /node_modules/,\n      },\n    ],\n  },\n  resolve: {\n    extensions: [ '.tsx', '.ts', '.js' ],\n  },\n  output: {\n    filename: 'js/[name].bundle.js',\n    path: path.resolve(__dirname, 'public'),\n  },\n  devServer: {\n    static: path.resolve(__dirname, 'public'),\n    watchFiles: [path.resolve(__dirname, '../dist/**/*')],\n  },\n};\n"
  },
  {
    "path": "demo/workbox-config.js",
    "content": "module.exports = {\n  \"globDirectory\": \"public/\",\n  \"globPatterns\": [\n    \"**/*.{html,svg,js,ts}\"\n  ],\n  \"swDest\": \"public/sw.js\"\n};"
  },
  {
    "path": "eslint.config.mjs",
    "content": "// @ts-check\nimport tsPlugin from '@typescript-eslint/eslint-plugin';\nimport tsParser from '@typescript-eslint/parser';\n\nexport default [\n  {\n    files: ['**/*.ts'],\n    languageOptions: {\n      parser: tsParser,\n      parserOptions: {\n        ecmaVersion: 2017,\n        sourceType: 'module',\n      },\n    },\n    plugins: {\n      '@typescript-eslint': tsPlugin,\n    },\n    rules: {\n      ...tsPlugin.configs['eslint-recommended'].overrides[0].rules,\n      ...tsPlugin.configs['recommended'].rules,\n      '@typescript-eslint/no-non-null-assertion': 'off',\n      '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],\n      'no-trailing-spaces': 'error',\n      'eol-last': 'error',\n    },\n  },\n];\n"
  },
  {
    "path": "jasmine.json",
    "content": "{\n  \"spec_dir\": \"dist/spec\",\n  \"spec_files\": [\n    \"**/*[sS]pec.js\"\n  ],\n  \"helpers\": [\n    \"helpers/**/*.js\"\n  ],\n  \"stopSpecOnExpectationFailure\": false,\n  \"random\": true\n}"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"wadb\",\n  \"version\": \"0.1.0\",\n  \"description\": \"\",\n  \"main\": \"dist/index.js\",\n  \"types\": \"dist/index.d.ts\",\n  \"scripts\": {\n    \"lint\": \"eslint \\\"src/**/*.ts\\\" \\\"demo/src/**/*.ts\\\"\",\n    \"lint-fix\": \"eslint \\\"src/**/*.ts\\\" \\\"demo/src/**/*.ts\\\" --fix\",\n    \"build\": \"tsc -p .\",\n    \"test\": \"tsc -p . && jasmine --config=jasmine.json\",\n    \"dev\": \"concurrently \\\"tsc -p . --watch\\\" \\\"npm run dev --prefix demo\\\"\"\n  },\n  \"files\": [\n    \"dist/lib\",\n    \"dist/index.d.ts\",\n    \"dist/index.js\"\n  ],\n  \"author\": \"André Cipriani Bandarra\",\n  \"license\": \"Apache-2.0\",\n  \"devDependencies\": {\n    \"concurrently\": \"^9.0.0\",\n    \"@types/jasmine\": \"^6.0.0\",\n    \"@types/node\": \"^25.3.0\",\n    \"@types/w3c-web-usb\": \"^1.0.13\",\n    \"@typescript-eslint/eslint-plugin\": \"^8.56.1\",\n    \"@typescript-eslint/parser\": \"^8.56.1\",\n    \"eslint\": \"^10.0.2\",\n    \"fake-indexeddb\": \"^6.2.5\",\n    \"jasmine\": \"^6.1.0\",\n    \"typescript\": \"^5.9.3\"\n  }\n}\n"
  },
  {
    "path": "src/index.ts",
    "content": "/**\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n export * from './lib/AdbClient';\n export * from './lib/AdbConnectionInformation';\n export * from './lib/IndexedDbKeyStore';\n export * from './lib/KeyStore'\n export * from './lib/Options';\n export * from './lib/Shell';\n export * from './lib/Stream';\n export * from './lib/SyncFrame';\n export * from './lib/message';\n export * from './lib/transport';\n"
  },
  {
    "path": "src/lib/AdbClient.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nimport {Transport} from './transport/Transport';\nimport {Options} from './Options';\nimport {Message, MessageChannel, MessageListener} from './message';\nimport {KeyStore} from './KeyStore';\nimport {privateKeyDump} from './Helpers';\nimport {AdbConnectionInformation} from './AdbConnectionInformation';\nimport {Stream} from './Stream';\nimport {Shell} from './Shell';\nimport {AsyncBlockingQueue} from './Queues';\nimport {Framebuffer} from './Framebuffer';\n\nconst VERSION = 0x01000000;\nconst VERSION_NO_CHECKSUM = 0x01000001;\nconst MAX_PAYLOAD = 256 * 1024;\n\nconst MACHINE_BANNER = 'host::\\0';\n\nexport class AdbClient implements MessageListener {\n  private messageChannel: MessageChannel;\n  private messageQueue = new AsyncBlockingQueue<Message>();\n  private openStreams: Set<Stream> = new Set();\n\n  /**\n   * Creates a new AdbClient\n   *\n   * @param {Transport} transport the transport layer.\n   */\n  constructor(\n    readonly transport: Transport,\n    readonly options: Options,\n    readonly keyStore: KeyStore,) {\n      this.messageChannel = new MessageChannel(transport, options, this);\n  }\n\n  registerStream(stream: Stream): void {\n    this.openStreams.add(stream);\n  }\n\n  unregisterStream(stream: Stream): void {\n    this.openStreams.delete(stream);\n  }\n\n  newMessage(msg: Message): void {\n    // Check if this message matches one of the open streams.\n    const streams = Array.from(this.openStreams);\n    for (const stream of streams) {\n      if (stream.consumeMessage(msg)) {\n        return;\n      }\n    }\n    this.messageQueue.enqueue(msg);\n  }\n\n  public async awaitMessage(): Promise<Message> {\n    return this.messageQueue.dequeue();\n  }\n\n  async connect(): Promise<AdbConnectionInformation> {\n    const version = this.options.useChecksum ? VERSION : VERSION_NO_CHECKSUM;\n    const cnxn = Message.cnxn(version, MAX_PAYLOAD, MACHINE_BANNER, this.options.useChecksum);\n    await this.sendMessage(cnxn); // Send the Message\n\n    // Response to connect must be CNXN or AUTH. Ignore different responses until the right one\n    // arrives.\n    let response;\n    do {\n      response = await this.awaitMessage();\n    } while (response.header.cmd !== 'CNXN' && response.header.cmd !== 'AUTH');\n\n    // Server connected\n    if (response.header.cmd === 'CNXN') {\n      if (!response.data) {\n        throw new Error('Connection doesn\\'t have data');\n      }\n      return AdbConnectionInformation.fromDataView(response.data);\n    }\n\n    // Server asked to authenticate\n    response = await this.doAuth(response);\n    if (!response.data) {\n      throw new Error('Connection doesn\\'t have data');\n    }\n    return AdbConnectionInformation.fromDataView(response.data);\n  }\n\n  async disconnect(): Promise<void> {\n    this.messageChannel.close();\n  }\n\n  async shell(command: string): Promise<string> {\n    const stream = await Stream.open(this, `shell:${command}`, this.options);\n    const okayMessage = Message.newMessage('OKAY', stream.localId, stream.remoteId, this.options.useChecksum);\n    let result = '';\n    let message;\n    do {\n      message = await stream.read();\n      if (message.header.cmd === 'WRTE') {\n        await this.sendMessage(okayMessage);\n        result += message.dataAsString() || '';\n      }\n    } while (message.header.cmd !== 'CLSE');\n    stream.client.unregisterStream(stream);\n    return result;\n  }\n\n  async framebuffer(): Promise<Framebuffer> {\n    return Framebuffer.create(this, this.options);\n  }\n\n  async interactiveShell(callback?: (result: string) => void): Promise<Shell> {\n    const stream = await Stream.open(this, 'shell:', this.options);\n    return new Shell(stream, callback);\n  }\n\n  async sync(): Promise<Stream> {\n    return await Stream.open(this, 'sync:', this.options);\n  }\n\n  async pull(filename: string): Promise<Blob> {\n    const syncStream = await this.sync();\n    const result = await syncStream.pull(filename);\n    await syncStream.close();\n    return result;\n  }\n\n  /**\n   * Pushes a blob of data to the device at the specified remote path.\n   *\n   * @param {Blob} blob The data to push.\n   * @param {string} remotePath The path on the device to write the data to.\n   * @param {string} mode The mode to set on the file (e.g., \"0755\").\n   * @param {number} chunkSize The size of data chunks to send at a time.\n   */\n  async push(blob: Blob, remotePath: string, mode: string, chunkSize: number):\n      Promise<void> {\n    const syncStream = await this.sync();\n    await syncStream.push(blob, remotePath, mode, chunkSize);\n    await syncStream.close();\n  }\n\n  private async doAuth(authResponse: Message): Promise<Message> {\n    if (authResponse.header.cmd !== 'AUTH') {\n      throw new Error('Not an AUTH response');\n    }\n\n    if (authResponse.header.arg0 !== 1) {\n      throw new Error(`\n          Invalid AUTH parameter. Expected 1 and received ${authResponse.header.arg0}`);\n    }\n\n    if (!authResponse.data) {\n      throw new Error('AUTH message doens\\'t contain data');\n    }\n\n    const token = authResponse.data.buffer as ArrayBuffer;\n\n    // Try signing with one of the stored keys\n    const keys = await this.keyStore.loadKeys();\n    for (const key of keys) {\n      const signed = await crypto.subtle.sign('RSASSA-PKCS1-v1_5', key.privateKey, token);\n      const signatureMessage =\n          Message.authSignature(new DataView(signed), this.options.useChecksum);\n      await this.sendMessage(signatureMessage);\n      const signatureResponse = await this.awaitMessage();\n      if (signatureResponse.header.cmd === 'CNXN') {\n        return signatureResponse;\n      }\n      console.log('Received message ', signatureResponse, 'from phone');\n    }\n\n    // None of they saved Keys is usable. Create new key\n    const key = await AdbClient.generateKey(this.options.dump, this.options.keySize);\n    await this.keyStore.saveKey(key);\n    const exportedKey = new DataView(await crypto.subtle.exportKey('spki', key.publicKey));\n    const keyMessage = Message.authPublicKey(exportedKey, this.options.useChecksum);\n    await this.sendMessage(keyMessage);\n\n    if (this.options.debug) {\n      console.log('Waiting for key to be accepted on the device.');\n    }\n    const keyResponse = await this.awaitMessage()\n    if (keyResponse.header.cmd !== 'CNXN') {\n      console.error('AUTH failed. Phone didn\\'t accept key', keyResponse);\n      throw new Error('AUTH failed. Phone didn\\'t accept key');\n    }\n    return keyResponse;\n  }\n\n  public async sendMessage(m: Message): Promise<void> {\n    await this.messageChannel.write(m);\n  }\n\n  static async generateKey(dump: boolean, keySize: number): Promise<CryptoKeyPair> {\n    const extractable = dump;\n    const key = await crypto.subtle.generateKey({\n      name: 'RSASSA-PKCS1-v1_5',\n      modulusLength: keySize,\n      publicExponent: new Uint8Array([0x01, 0x00, 0x01]),\n      hash: { name: 'SHA-1' }\n    }, extractable, [ 'sign', 'verify' ])\n\n    if (dump) {\n      await privateKeyDump(key);\n    }\n\n    return key;\n  }\n}\n"
  },
  {
    "path": "src/lib/AdbConnectionInformation.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nconst PRODUCT_NAME_KEY = 'ro.product.name';\nconst PRODUCT_MODEL_KEY = 'ro.product.model';\nconst PRODUCT_DEVICE_KEY = 'ro.product.device';\nconst FEATURES_KEY = 'features';\nconst DEFAULT_PRODUCT_VALUE = '<unkwnown>';\n\nexport class AdbConnectionInformation {\n  constructor(\n    readonly productName: string,\n    readonly productDevice: string,\n    readonly productModel: string,\n    readonly features: string[]\n    ){\n\n    }\n\n  static fromDataView(input: DataView): AdbConnectionInformation {\n    const textDecoder = new TextDecoder();\n    const decodedInput = textDecoder.decode(input);\n    return AdbConnectionInformation.fromString(decodedInput);\n  }\n\n  /**\n   * Creates an AdbConnectionInformation from a Connection string\n   * @param input the string sent as data from a Connection response\n   */\n  static fromString(input: string): AdbConnectionInformation {\n    const start = input.indexOf('::');\n    const properties = input.substring(start + 2).split(';');\n    let productName = DEFAULT_PRODUCT_VALUE;\n    let productDevice = DEFAULT_PRODUCT_VALUE;\n    let productModel = DEFAULT_PRODUCT_VALUE;\n    let features: string[] = [];\n    for (const property of properties) {\n      if (property.startsWith(PRODUCT_NAME_KEY)) {\n        productName = property.substring(PRODUCT_NAME_KEY.length + 1);\n        continue;\n      }\n\n      if (property.startsWith(PRODUCT_MODEL_KEY)) {\n        productModel = property.substring(PRODUCT_MODEL_KEY.length + 1);\n        continue;\n      }\n\n      if (property.startsWith(PRODUCT_DEVICE_KEY)) {\n        productDevice = property.substring(PRODUCT_DEVICE_KEY.length + 1);\n        continue;\n      }\n\n      if (property.startsWith(FEATURES_KEY)) {\n        features = property.substring(FEATURES_KEY.length + 1).split(',');\n      }\n    }\n    return new AdbConnectionInformation(productName, productDevice, productModel, features);\n  }\n}\n"
  },
  {
    "path": "src/lib/Framebuffer.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nimport {Stream} from './Stream';\nimport {AdbClient} from './AdbClient';\nimport {Options} from './Options';\n\n /**\n  * framebuffer:\n  *   This service is used to send snapshots of the framebuffer to a client.\n  *   It requires sufficient privileges but works as follow:\n  *\n  *     After the OKAY, the service sends 16-byte binary structure\n  *     containing the following fields (little-endian format):\n  *\n  *           depth:   uint32_t:    framebuffer depth\n  *           size:    uint32_t:    framebuffer size in bytes\n  *           width:   uint32_t:    framebuffer width in pixels\n  *           height:  uint32_t:    framebuffer height in pixels\n  *\n  *     With the current implementation, depth is always 16, and\n  *     size is always width*height*2\n  *\n  *     Then, each time the client wants a snapshot, it should send\n  *     one byte through the channel, which will trigger the service\n  *     to send it 'size' bytes of framebuffer data.\n  *\n  *     If the adbd daemon doesn't have sufficient privileges to open\n  *     the framebuffer device, the connection is simply closed immediately.\n  *\n  * Definitions from `system/core/adb/daemon/framebuffer_service.cpp`\n  *\n  * struct fbinfo {\n  *   unsigned int version;\n  *   unsigned int bpp;\n  *   unsigned int colorSpace;\n  *   unsigned int size;\n  *   unsigned int width;\n  *   unsigned int height;\n  *   unsigned int red_offset;\n  *   unsigned int red_length;\n  *   unsigned int blue_offset;\n  *   unsigned int blue_length;\n  *   unsigned int green_offset;\n  *   unsigned int green_length;\n  *   unsigned int alpha_offset;\n  *   unsigned int alpha_length;\n  * }\n  */\nexport class Framebuffer {\n  // static DDMS_RAWIMAGE_VERSION = 2;\n  static BYTE_LENGTH = 56;\n\n  private constructor(\n    readonly version: number,\n    readonly bpp: number,\n    readonly colorSpace: number,\n    readonly size: number,\n    readonly width: number,\n    readonly height: number,\n    readonly redOffset: number,\n    readonly redLength: number,\n    readonly blueOffset: number,\n    readonly blueLength: number,\n    readonly greenOffset: number,\n    readonly greenLength: number,\n    readonly alphaOffset: number,\n    readonly alphaLength: number,\n    readonly imageData: Uint8ClampedArray) {}\n\n  static async create(adbClient: AdbClient, options: Options): Promise<Framebuffer> {\n    const stream = await Stream.open(adbClient, 'framebuffer:', options);\n    let message = await stream.read();\n\n    if (message.header.cmd !== 'WRTE') {\n      await stream.write('CLSE');\n      throw new Error(`Expected WRTE message but received ${message.header.cmd}`);\n    }\n\n    if (!message.data) {\n      await stream.write('CLSE');\n      throw new Error('message doesn\\'t contain data');\n    }\n\n    await stream.write('OKAY');\n\n    const version = message.data.getUint32(0, true);\n    const bpp = message.data.getUint32(4, true);\n    const colorSpace = message.data.getUint32(8, true);\n    const size = message.data.getUint32(12, true);\n    const width = message.data.getUint32(16, true);\n    const height = message.data.getUint32(20, true);\n    const redOffset = message.data.getUint32(24, true);\n    const redLength = message.data.getUint32(28, true);\n    const blueOffset = message.data.getUint32(32, true);\n    const blueLength = message.data.getUint32(36, true);\n    const greenOffset = message.data.getUint32(40, true);\n    const greenLength = message.data.getUint32(44, true);\n    const alphaOffset = message.data.getUint32(48, true);\n    const alphaLength = message.data.getUint32(52, true);\n\n    const buffer = new Uint8Array(size);\n\n    let bytesReceived = 0;\n    let data = new Uint8Array(message.data.buffer.slice(Framebuffer.BYTE_LENGTH));\n    buffer.set(data, 0);\n    bytesReceived = data.length;\n\n    while (bytesReceived < size) {\n      message = await stream.read();\n\n      if (message.header.cmd === 'CLSE') {\n        break;\n      }\n\n      if (!message.data) {\n        await stream.write('CLSE');\n        throw new Error('message doesn\\'t contain data');\n      }\n\n      data = new Uint8Array(message.data.buffer as ArrayBuffer);\n\n      buffer.set(data, bytesReceived);\n      bytesReceived += data.length;\n      await stream.write('OKAY');\n    }\n\n    await stream.close();\n\n    return new Framebuffer(\n      version,\n      bpp,\n      colorSpace,\n      size,\n      width,\n      height,\n      redOffset,\n      redLength,\n      blueOffset,\n      blueLength,\n      greenOffset,\n      greenLength,\n      alphaOffset,\n      alphaLength,\n      Uint8ClampedArray.from(buffer),\n    );\n  }\n}\n"
  },
  {
    "path": "src/lib/Helpers.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nfunction paddit(text: string, width: number, padding: string): string {\n  const padlen = width - text.length;\n  let padded = '';\n\n  for (let i = 0; i < padlen; i++) {\n      padded += padding;\n    }\n\n  return padded + text;\n}\n\nexport function toHex8(num: number): string {\n  return paddit(num.toString(16), 2, '0');\n}\n\nexport function toHex16(num: number): string {\n  return paddit(num.toString(16), 4, '0');\n}\n\nexport function toHex32(num: number): string {\n  return paddit(num.toString(16), 8, '0');\n}\n\nexport function hexdump(view: DataView, prefix = ''): void {\n  const decoder = new TextDecoder();\n  for (let i = 0; i < view.byteLength; i += 16) {\n    const max = (view.byteLength - i) > 16 ? 16 : (view.byteLength - i);\n    let row = prefix + toHex16(i) + ' ';\n    let j;\n\n    for (j = 0; j < max; j++) {\n      row += ' ' + toHex8(view.getUint8(i + j));\n    }\n\n    for (; j < 16; j++){\n      row += '   ';\n    }\n\n    row += ' | ' + decoder.decode(new DataView(view.buffer, i, max));\n    console.log(row);\n  }\n}\n\nexport function toB64(buffer: ArrayBuffer): string {\n  return btoa(new Uint8Array(buffer).reduce((s, b) => s + String.fromCharCode(b), ''));\n}\n\nexport async function privateKeyDump(key: CryptoKeyPair): Promise<void> {\n  if (!key.privateKey.extractable) {\n    console.log('cannot dump the private key, it\\'s not extractable');\n    return;\n  }\n\n  const privkey = await crypto.subtle.exportKey('pkcs8', key.privateKey);\n  console.log(`-----BEGIN PRIVATE KEY-----\\n${toB64(privkey)}\\n-----END PRIVATE KEY-----`);\n}\n\nexport async function publicKeyDump(key: CryptoKeyPair): Promise<void> {\n  if (!key.publicKey.extractable) {\n    console.log('cannot dump the public key, it\\'s not extractable');\n    return;\n  }\n\n  const pubKey = await crypto.subtle.exportKey('spki', key.publicKey);\n  console.log(`-----BEGIN PUBLIC KEY-----\\n${toB64(pubKey)}'\\n-----END PUBLIC KEY-----`);\n}\n\nexport function encodeCmd(cmd: string): number {\n  const encoder = new TextEncoder();\n  const buffer = encoder.encode(cmd).buffer;\n  const view = new DataView(buffer);\n  return view.getUint32(0, true);\n}\n\nexport function decodeCmd(cmd: number): string {\n  const decoder = new TextDecoder();\n  const buffer = new ArrayBuffer(4);\n  const view = new DataView(buffer);\n  view.setUint32(0, cmd, true);\n  return decoder.decode(buffer);\n}\n"
  },
  {
    "path": "src/lib/IndexedDbKeyStore.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nimport {KeyStore} from './KeyStore';\n\nconst DB_NAME = 'wadb';\nconst DB_VERSION = 1;\nconst STORE_NAME = 'keys';\n\nfunction openDb(): Promise<IDBDatabase> {\n  return new Promise((resolve, reject) => {\n    const request = indexedDB.open(DB_NAME, DB_VERSION);\n    request.onupgradeneeded = () => {\n      request.result.createObjectStore(STORE_NAME, {autoIncrement: true});\n    };\n    request.onsuccess = () => resolve(request.result);\n    request.onerror = () => reject(request.error);\n  });\n}\n\n/**\n * A KeyStore implementation that persists keys across page reloads using\n * IndexedDB. CryptoKey objects are stored directly, which avoids the need to\n * export and re-import them.\n */\nexport class IndexedDbKeyStore implements KeyStore {\n  async loadKeys(): Promise<CryptoKeyPair[]> {\n    const db = await openDb();\n    return new Promise((resolve, reject) => {\n      const tx = db.transaction(STORE_NAME, 'readonly');\n      const request = tx.objectStore(STORE_NAME).getAll();\n      request.onsuccess = () => resolve(request.result as CryptoKeyPair[]);\n      request.onerror = () => reject(request.error);\n      tx.oncomplete = () => db.close();\n    });\n  }\n\n  async saveKey(key: CryptoKeyPair): Promise<void> {\n    const db = await openDb();\n    return new Promise((resolve, reject) => {\n      const tx = db.transaction(STORE_NAME, 'readwrite');\n      const request = tx.objectStore(STORE_NAME).add(key);\n      request.onerror = () => reject(request.error);\n      tx.oncomplete = () => { db.close(); resolve(); };\n    });\n  }\n}\n"
  },
  {
    "path": "src/lib/KeyStore.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nexport interface KeyStore {\n  loadKeys(): Promise<CryptoKeyPair[]>;\n  saveKey(key: CryptoKeyPair): Promise<void>;\n}\n"
  },
  {
    "path": "src/lib/Log.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nexport default class Log {\n  debug(message: string): void {\n    console.log(message);\n  }\n\n  info(message: string): void {\n    console.info(message);\n  }\n\n  error(message: string): void {\n    console.error(message);\n  }\n}\n"
  },
  {
    "path": "src/lib/Options.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n export interface Options {\n   debug: boolean;\n   dump: boolean;\n   useChecksum: boolean;\n   keySize: number;\n }\n"
  },
  {
    "path": "src/lib/Queues.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\ntype Resolver<T> = (value: T | PromiseLike<T>) => void;\n\nclass QueueEntry<T> {\n  data: T;\n  next?: QueueEntry<T>\n\n  constructor(data: T) {\n    this.data = data;\n  }\n}\n\n/**\n * A linked queue implementation.\n */\nexport class Queue<T> {\n  head?: QueueEntry<T>;\n  tail?: QueueEntry<T>;\n\n  /**\n   * Adds an item to the queue.\n   * @param data\n   */\n  enqueue(data: T): void {\n    const newNode = new QueueEntry<T>(data);\n    if (this.tail) {\n      this.tail.next = newNode;\n    }\n    this.tail = newNode;\n\n    // Queue is empty. Initialise the head.\n    if (!this.head) {\n      this.head = this.tail;\n    }\n  }\n\n  /**\n   * Removes an item from the queue and returns it.\n   * @returns {T} the removed item.\n   * @throws an error if the list is empty.\n   */\n  dequeue(): T {\n    if (this.isEmpty()) {\n      throw new Error('Cannot dequeue. Queue is empty');\n    }\n    const node = this.head!.data;\n    this.head = this.head!.next;\n    return node;\n  }\n\n  /**\n   * Checks if the Queues is empty\n   * @returns {boolean} true if the Queue is empty.\n   */\n  isEmpty(): boolean {\n    return this.head == null;\n  }\n}\n\n/**\n * The AsyncBlockingQueue implements a queue with an asynchronous programming model. Items can\n * be added to the Queue as usual. When dequeing, a Promise is returned.\n *\n * The promise will resolve instantly if the Queue is not empty. If the Queue is empty, the Promise\n * will be resolved when a new item is added to the queue.\n */\nexport class AsyncBlockingQueue<T> {\n  private promiseQueue: Queue<Promise<T>> = new Queue<Promise<T>>();\n  private resolverQueue: Queue<Resolver<T>> = new Queue<Resolver<T>>();\n\n  private add(): void {\n    const promise = new Promise<T>(resolve => {\n      this.resolverQueue.enqueue(resolve);\n    });\n    this.promiseQueue.enqueue(promise);\n  }\n\n  /**\n   * Enqueues an item\n   * @param data\n   */\n  enqueue(data: T): void {\n    if (this.resolverQueue.isEmpty()) {\n      this.add();\n    }\n    const resolve = this.resolverQueue.dequeue();\n    resolve(data);\n  }\n\n  /**\n   * Asynchronously dequeues an item. If the queue is empty, the returned Promise is resolved when\n   * an item is added. Otherwise, it will return one o the existing items.\n   * @returns {Promise<T>} that resolves to the data.\n   */\n  async dequeue(): Promise<T> {\n    if (this.promiseQueue.isEmpty()) {\n      this.add();\n    }\n    return this.promiseQueue.dequeue();\n  }\n\n  hasPendingPromises(): boolean {\n    return !this.promiseQueue.isEmpty();\n  }\n\n  hasPendingResolvers(): boolean {\n    return !this.resolverQueue.isEmpty();\n  }\n}\n"
  },
  {
    "path": "src/lib/Shell.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nimport {Stream} from './Stream';\nimport {Message} from './message';\n\ntype callbackFunction = (text: string) => void;\n\nexport class Shell {\n  private textDecoder = new TextDecoder();\n  private textEncoder = new TextEncoder();\n  private messageListener: ((message: Message) => void)[] = [];\n  private closed = false;\n\n  constructor(readonly stream: Stream, readonly callbackFunction?: callbackFunction) {\n    this.loopRead();\n  }\n\n  private async loopRead(): Promise<void> {\n    try {\n      let message;\n      do {\n        message = await this.stream.read();\n\n        if (message.header.cmd === 'WRTE') {\n          this.stream.write('OKAY');\n          const data = this.textDecoder.decode(message.data!);\n          if (this.callbackFunction) {\n            this.callbackFunction(data);\n          }\n        }\n\n        // Resolve Messages waiting for this event\n        for (const listener of this.messageListener) {\n          listener(message);\n        }\n\n      } while (!this.closed)\n    } catch(e) {\n      console.error('loopRead crashed', e);\n    }\n    this.stream.client.unregisterStream(this.stream);\n  }\n\n  private waitForMessage(cmd: string): Promise<Message> {\n    return new Promise<Message>(resolve => {\n      const callback = (message: Message): void => {\n        if (message.header.cmd === cmd) {\n          const pos = this.messageListener.indexOf(callback);\n          this.messageListener.splice(pos, 1);\n          resolve(message);\n        }\n      };\n      this.messageListener.push(callback);\n    });\n  }\n\n  async write(command: string): Promise<void> {\n    const data = this.textEncoder.encode(command);\n    await this.stream.write('WRTE', new DataView(data.buffer));\n    await this.waitForMessage('OKAY');\n  }\n\n  async close(): Promise<void> {\n    this.closed = true;\n    await this.write('CLSE');\n  }\n}\n"
  },
  {
    "path": "src/lib/Stream.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nimport {AdbClient} from './AdbClient';\nimport {Message} from './message';\nimport {Options} from './Options';\nimport {toHex32} from './Helpers';\nimport {SyncFrame} from './SyncFrame';\nimport {AsyncBlockingQueue} from './Queues';\n\nexport class Stream {\n  private static nextId = 1;\n  private messageQueue = new AsyncBlockingQueue<Message>();\n\n  constructor(readonly client: AdbClient, readonly service: string, readonly localId: number,\n              readonly remoteId: number, private options: Options) {\n  }\n\n  async close(): Promise<void> {\n    await this.write('CLSE');\n\n    if (this.options.debug) {\n      console.log(`Closed stream ${this.service}`);\n      console.log(` local_id: 0x${toHex32(this.localId)}`);\n      console.log(` remote_id: 0x${toHex32(this.remoteId)}`);\n    }\n    this.client.unregisterStream(this);\n  }\n\n  consumeMessage(msg: Message): boolean {\n    if (msg.header.arg0 === 0 || msg.header.arg0 !== this.remoteId ||\n      msg.header.arg1 === 0 || msg.header.arg1 !== this.localId) {\n      return false;\n    }\n    this.messageQueue.enqueue(msg);\n    return true;\n  }\n\n  async write(cmd: string, data?: DataView): Promise<void> {\n    const message = this.newMessage(cmd, data);\n    await this.client.sendMessage(message);\n  }\n\n  async read(): Promise<Message> {\n    return this.messageQueue.dequeue();\n  }\n\n  /**\n   * Sends a message and waits for a specific response message.\n   *\n   * @param {Message} m The message to send.\n   * @param {string} responseCmd The expected command of the response message.\n   * @throws {Error} If the response message has a different command.\n   */\n  async sendReceive(m: Message, responseCmd: string): Promise<void> {\n    await this.client.sendMessage(m);\n    const response = await this.read();\n    if (response.header.cmd !== responseCmd) {\n      throw new Error('WRTE/SEND failed: ' + response);\n    }\n  }\n\n\n  /**\n   *\n   * Retrieves a file from device to a local file. The remote path is the path to\n   * the file that will be returned. Just as for the SEND sync request the file\n   * received is split up into chunks. The sync response id is \"DATA\" and length is\n   * the chunk size. After follows chunk size number of bytes. This is repeated\n   * until the file is transferred. Each chunk will not be larger than 64k.\n   * When the file is transferred a sync response \"DONE\" is retrieved where the\n   * length can be ignored.\n   *\n   * @param {string} remotePath path to the file to be pulled from the device\n   * @returns {Promise<Blob>} a Blog with the file contents.\n   */\n  async pull(remotePath: string): Promise<Blob> {\n    const encoder = new TextEncoder();\n    const encodedFilename = encoder.encode(remotePath);\n\n    // Sends RECV with filename length.\n    const recvFrame = new SyncFrame('RECV', encodedFilename.byteLength);\n    const wrteRecvMessage = this.newMessage('WRTE', recvFrame.toDataView());\n    await this.client.sendMessage(wrteRecvMessage);\n    const wrteRecvResponse = await this.read();\n    if (wrteRecvResponse.header.cmd !== 'OKAY') {\n      throw new Error('WRTE/RECV failed: ' + wrteRecvResponse);\n    }\n\n    // 17. We send the path of the file we want again sdcard/someFile.txt\n    const wrteFilenameMessage = this.newMessage('WRTE', new DataView(encodedFilename.buffer));\n    await this.client.sendMessage(wrteFilenameMessage);\n\n    // 18. Device sends us OKAY\n    const wrteFilenameResponse = await this.read();\n    if (wrteFilenameResponse.header.cmd !== 'OKAY') {\n      throw new Error('WRTE/filename failed: ' + wrteFilenameResponse);\n    }\n\n    const okayMessage = this.newMessage('OKAY');\n    let fileDataMessage = await this.read();\n    while (!fileDataMessage.data) {\n      fileDataMessage = await this.read();\n    }\n    await this.client.sendMessage(okayMessage);\n\n    let syncFrame = SyncFrame.fromDataView(new DataView(fileDataMessage.data.buffer.slice(0, 8)));\n    let buffer = new Uint8Array(fileDataMessage.data.buffer.slice(8));\n    const chunks: ArrayBuffer[] = [];\n    while (syncFrame.cmd !== 'DONE') {\n      while (syncFrame.byteLength >= buffer.byteLength) {\n        fileDataMessage = await this.read();\n\n        if (!fileDataMessage.data) {\n          continue;\n        }\n\n        await this.client.sendMessage(okayMessage);\n\n        // Join both arrays\n        const newLength = buffer.byteLength + fileDataMessage.data.byteLength;\n        const newBuffer = new Uint8Array(newLength);\n        newBuffer.set(buffer, 0);\n        newBuffer.set(new Uint8Array(fileDataMessage.data.buffer), buffer.byteLength);\n        buffer = newBuffer;\n      }\n      chunks.push(buffer.slice(0, syncFrame.byteLength).buffer);\n      buffer = buffer.slice(syncFrame.byteLength);\n      syncFrame = SyncFrame.fromDataView(new DataView(buffer.slice(0, 8).buffer));\n      buffer = buffer.slice(8);\n    }\n    return new Blob(chunks);\n  }\n\n\n  /**\n   * Pushes a blob of data to the device at the specified remote path.\n   *\n   * @param {Blob} blob The data to push.\n   * @param {string} remotePath The path on the device to write the data to.\n   * @param {string} mode The mode to set on the file.\n   * @param {number} chunkSize The size of data chunks to send at a time.\n   */\n  async push(blob: Blob, remotePath: string, mode: string, chunkSize: number):\n    Promise<void> {\n    const reader = new FileReader();\n    const encoder = new TextEncoder();\n\n    // Encodes the remote path for sending over ADB.\n    const encodedFilename = encoder.encode(remotePath);\n\n    // --- Negotiation Phase ---\n    // 1. Sends SEND command with total filename+mode length.\n    const sendFrame =\n      new SyncFrame('SEND', remotePath.length + 1 + mode.length);\n    const wrteSendMessage = this.newMessage('WRTE', sendFrame.toDataView());\n    await this.sendReceive(wrteSendMessage, 'OKAY');\n\n    // 2. Sends the filename.\n    const wrteFilenameMessage =\n      this.newMessage('WRTE', new DataView(encodedFilename.buffer));\n    await this.sendReceive(wrteFilenameMessage, 'OKAY');\n\n    // 3. Sends the mode.\n    const wrteModeMessage = this.newMessage(\n      'WRTE', new DataView(encoder.encode(',' + mode).buffer));\n    await this.sendReceive(wrteModeMessage, 'OKAY');\n\n    // --- Data Transfer Phase ---\n    // 1. Reads the Blob as an ArrayBuffer.\n    const arrayBufferPromise = new Promise<ArrayBuffer>((resolve, reject) => {\n      reader.onload = (event) => {\n        return resolve(event.target!.result as ArrayBuffer);\n      };\n      reader.onerror = reject;\n      reader.readAsArrayBuffer(blob);\n    });\n    const buffer: ArrayBuffer = await arrayBufferPromise;\n\n    // 2. Splits the buffer into chunks.\n    const chunks: ArrayBufferLike[] = [];\n    for (let i = 0; i < buffer.byteLength; i += chunkSize) {\n      chunks.push(buffer.slice(i, Math.min(i + chunkSize, buffer.byteLength)));\n    }\n\n    // 3. Sends each chunk with its size.\n    for (const chunk of chunks) {\n      const syncFrame = new SyncFrame('DATA', chunk.byteLength);\n      const wrteByteLengthMessage =\n        this.newMessage('WRTE', syncFrame.toDataView());\n      await this.sendReceive(wrteByteLengthMessage, 'OKAY');\n\n      const dataView = new DataView(chunk);\n      const wrteChunkMessage = this.newMessage('WRTE', dataView);\n      await this.sendReceive(wrteChunkMessage, 'OKAY');\n    }\n\n    // --- Finishing Up ---\n    // 1. Sends DONE frame with current timestamp.\n    const doneFrame = new SyncFrame('DONE', Math.round(Date.now() / 1000));\n    const doneMessage = this.newMessage('WRTE', doneFrame.toDataView());\n    await this.client.sendMessage(doneMessage);\n\n    // 2. Reads response (should be OKAY) and send final OKAY.\n    const okayMessage = this.newMessage('OKAY');\n    await this.sendReceive(okayMessage, 'OKAY');\n  }\n\n\n  private newMessage(cmd: string, data?: DataView): Message {\n    return Message.newMessage(\n      cmd, this.localId, this.remoteId, this.options.useChecksum, data);\n  }\n\n  static async open(adbClient: AdbClient, service: string, options: Options): Promise<Stream> {\n    const localId = Stream.nextId++;\n    let remoteId = 0;\n    const m = Message.open(localId, remoteId, service, options.useChecksum);\n    await adbClient.sendMessage(m);\n\n    let response;\n    do {\n      response = await adbClient.awaitMessage();\n    } while (response.header.arg1 !== localId);\n\n    if (response.header.cmd !== 'OKAY') {\n      throw new Error('OPEN Failed');\n    }\n\n    remoteId = response.header.arg0;\n    if (options.debug) {\n      console.log(`Opened stream ${service}`);\n      console.log(` local_id: 0x${toHex32(localId)}`);\n      console.log(` remote_id: 0x${toHex32(remoteId)}`);\n    }\n\n    const stream = new Stream(adbClient, service, localId, remoteId, options);\n    adbClient.registerStream(stream);\n    return stream;\n  }\n}\n"
  },
  {
    "path": "src/lib/SyncFrame.ts",
    "content": "/**\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nimport {encodeCmd, decodeCmd} from './Helpers';\n\nexport class SyncFrame {\n  constructor(readonly cmd: string, readonly byteLength: number) {\n\n  }\n\n  toDataView(): DataView {\n    const data = new ArrayBuffer(8);\n    const cmd = encodeCmd(this.cmd);\n\n    const view = new DataView(data);\n    view.setUint32(0, cmd, true);\n    view.setUint32(4, this.byteLength, true);\n    return view;\n  }\n\n  static fromDataView(dataView: DataView): SyncFrame {\n    const cmd = decodeCmd(dataView.getUint32(0, true));\n    const byteLength = dataView.getUint32(4, true);\n    return new SyncFrame(cmd, byteLength);\n  }\n}\n"
  },
  {
    "path": "src/lib/message/Message.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nimport {MessageHeader} from './MessageHeader';\nimport {toB64} from '../Helpers';\n\n/**\n * An ADB Message. Contains a {@link MessageHeader} and an optional {@link DataView} with the\n * data for the message.\n */\nexport class Message {\n  constructor(\n    readonly header: MessageHeader,\n    readonly data?: DataView,\n  ){}\n\n  /**\n   * Returns the data content as a {@link string} or {@link null} if data is not available.\n   * @returns {string | null} a {@link string} or {@link null} if data is not available.\n   */\n  dataAsString(): string | null {\n    if (!this.data) {\n      return null;\n    }\n\n    const textDecoder = new TextDecoder();\n    return textDecoder.decode(this.data);\n  }\n\n  /**\n   * Creates a new Message. See {@link MessageHeader}.\n   * @param {string} cmd the command.\n   * @param {number} arg0 value for the first argument.\n   * @param {number} arg1 value for the second argument.\n   * @param {boolean} useChecksum if the checksum for the data should be calculated.\n   * @param {DataView} data message data.\n   * @returns {Message} a new Message\n   */\n  static newMessage(\n      cmd: string, arg0: number, arg1: number, useChecksum: boolean, data?: DataView): Message {\n    let checksum = 0;\n    let byteLength = 0;\n    if (data) {\n      byteLength = data.byteLength;\n      if (useChecksum) {\n        checksum = Message.checksum(data);\n      }\n    }\n    const header = new MessageHeader(cmd, arg0, arg1, byteLength, checksum);\n    return new Message(header, data);\n  }\n\n  /**\n   * Creates a new `OPEN` message.\n   * @param {number} localId local stream ID\n   * @param {number} remoteId remote stream ID.\n   * @param {string} service service description\n   * @param {boolean} useChecksum if the checksum for the data should be calculated.\n   * @returns {Message} a correctly setup message with an 'OPEN' command\n   */\n  static open(localId: number, remoteId: number, service: string, useChecksum: boolean): Message {\n    const encoder = new TextEncoder();\n    const data = new DataView(encoder.encode('' + service + '\\0').buffer);\n    return Message.newMessage('OPEN', localId, remoteId, useChecksum, data);\n  }\n\n  /**\n   * Creates a new `CNXN` message.\n   * @param {number} version version of the protocol to be used.\n   * @param {number} maxPayload maximum payload size for the connection.\n   * @param {string} banner host description.\n   * @param {boolean} useChecksum if the checksum for the data should be calculated.\n   * @returns {Message} a correctly setup message with an 'CNXN' command\n   */\n  static cnxn(version: number, maxPayload: number, banner: string, useChecksum: boolean): Message {\n    const encoder = new TextEncoder();\n    const data = new DataView(encoder.encode(banner).buffer);\n    return Message.newMessage('CNXN', version, maxPayload, useChecksum, data);\n  }\n\n  /**\n   * Creates a new `AUTH` message, with the a signed token.\n   * @param {DataView} signedToken a DataView with the signed token.\n   * @param {boolean} useChecksum if the checksum for the data should be calculated.\n   * @returns {Message} a correctly setup message with an 'AUTH' command\n   */\n  static authSignature(signedToken: DataView, useChecksum: boolean): Message {\n    return Message.newMessage('AUTH', 2, 0, useChecksum, signedToken);\n  }\n\n  /**\n   * Creates a new `AUTH` message, with the a Public Key.\n   * @param {DataView} publicKey a DataView with the public key\n   * @param {boolean} useChecksum if the checksum for the data should be calculated.\n   * @returns {Message} a correctly setup message with an 'AUTH' command\n   */\n  static authPublicKey(publicKey: DataView, useChecksum: boolean): Message {\n    const textEncoder = new TextEncoder();\n    const data = textEncoder.encode(toB64(publicKey.buffer as ArrayBuffer) + '\\0');\n    return Message.newMessage('AUTH', 3, 0, useChecksum, new DataView(data.buffer));\n  }\n\n  private static checksum(dataView: DataView): number {\n    let sum = 0;\n\t\tfor (let i = 0; i < dataView.byteLength; i++) {\n      sum += dataView.getUint8(i);\n    }\n\t\treturn sum & 0xffffffff;\n  }\n}\n"
  },
  {
    "path": "src/lib/message/MessageChannel.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nimport {Transport} from '../transport';\nimport {Message} from './Message';\nimport {MessageHeader} from './MessageHeader';\nimport {Options} from '../Options';\nimport {MessageListener} from './MessageListener';\n\nexport class MessageChannel {\n  private active = true;\n\n  constructor(\n      readonly transport: Transport,\n      readonly options: Options,\n      readonly listener: MessageListener) {\n    this.readLoop();\n  }\n\n  private async readLoop(): Promise<void> {\n    let message: Message;\n    do {\n      message = await this.read();\n      if (this.options.debug) {\n        console.log('<<<', message);\n      }\n      this.listener.newMessage(message);\n    } while(this.active);\n  }\n\n  private async readHeader(): Promise<MessageHeader> {\n    const response = await this.transport.read(24);\n    return MessageHeader.parse(response, this.options.useChecksum);\n  }\n\n  private async read(): Promise<Message> {\n    const header = await this.readHeader();\n    let receivedData;\n    switch (header.cmd) {\n      default: {\n        if (header.length > 0) {\n          receivedData = await this.transport.read(header.length);\n        }\n      }\n    }\n    const message = new Message(header, receivedData);\n    return message;\n  }\n\n  close(): void {\n    this.active = false;\n  }\n\n  async write(m: Message): Promise<void> {\n    if (this.options.debug) {\n      console.log('>>>', m);\n    }\n    const data = m.header.toDataView();\n    await this.transport.write(data.buffer as ArrayBuffer);\n    if (m.data) {\n      await this.transport.write(m.data.buffer as ArrayBuffer);\n    }\n  }\n}\n"
  },
  {
    "path": "src/lib/message/MessageHeader.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nimport {encodeCmd, decodeCmd} from '../Helpers';\n\n/**\n * The header of an ADB message. A header is made of 6 fields, each one with 4 bytes:\n *\n * - command: The command that this message represents.\n * - arg0: The meaning depends on the command.\n * - arg1: The meaning depends on the command.\n * - length: The length of the data part of the message.\n * - checksum: Checksum for the data part of the message. Only used in version 0x01000000 of the\n *   protocol.\n * - magic: a checksum for the command. Effectivelly, `command ^ 0xffffffff`.\n */\nexport class MessageHeader {\n\n  /**\n   * Creates a new MessageHeader\n   *\n   * @param {string} cmd The command that this message represents.\n   * @param {number} arg0 The meaning depends on the command.\n   * @param {number} arg1 The meaning depends on the command.\n   * @param {number} length The length of the data part of the message.\n   * @param {number} checksum Checksum for the data part of the message. Only used in version 0x01000000 of the\n   * protocol.\n   */\n  constructor(\n    readonly cmd: string,\n    readonly arg0: number,\n    readonly arg1: number,\n    readonly length: number,\n    readonly checksum: number) {\n  }\n\n  /**\n   * Converts the MessageHeader into a {@link DataView}.\n   * @returns {DataView} a DataView with 24 bytes, with the header content.\n   */\n  toDataView(): DataView {\n    const view = new DataView(new ArrayBuffer(24));\n    const rawCmd = encodeCmd(this.cmd);\n    const magic = rawCmd ^ 0xffffffff;\n\t\tview.setUint32(0, rawCmd, true);\n\t\tview.setUint32(4, this.arg0, true);\n\t\tview.setUint32(8, this.arg1, true);\n\t\tview.setUint32(12, this.length, true);\n\t\tview.setUint32(16, this.checksum, true);\n    view.setUint32(20, magic, true);\n    return view;\n  }\n\n  /**\n   * Creates a header from a {@link DataView}.\n   * @param {DataView} data the {@link DataView} that will be used to create the header.\n   * @param {boolean} useChecksum if the checksum should be verified.\n   */\n  static parse(data: DataView, useChecksum = false): MessageHeader {\n    const cmd = data.getUint32(0, true);\n    const arg0 = data.getUint32(4, true);\n    const arg1 = data.getUint32(8, true);\n    const len = data.getUint32(12, true);\n    const checksum = data.getUint32(16, true);\n\n     // Android seems to have stopped providing checksums\n     if (useChecksum && data.byteLength > 20) {\n      const magic = data.getUint32(20, true);\n\n      if ((cmd ^ magic) !== -1) {\n        throw new Error('magic mismatch');\n      }\n    }\n\n    return new MessageHeader(decodeCmd(cmd), arg0, arg1, len, checksum);\n  }\n}\n"
  },
  {
    "path": "src/lib/message/MessageListener.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nimport {Message} from './Message';\n\nexport interface MessageListener {\n  newMessage(msg: Message): void;\n}\n"
  },
  {
    "path": "src/lib/message/index.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nexport * from './Message';\nexport * from './MessageChannel';\nexport * from './MessageHeader';\nexport * from './MessageListener';\n"
  },
  {
    "path": "src/lib/transport/Transport.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n/**\n * A transport layer for data. Implementations must provide a read and write method.\n */\nexport interface Transport {\n  /**\n   * Writes data to the transport layer.\n   * @param {DataView} data the data to be written to the layer.\n   */\n  write(data: ArrayBuffer): Promise<void>;\n\n  /**\n   * Reands `len` bytes from the transport layer.\n   * @param {number} len the number of bytes to read.\n   */\n  read(len: number): Promise<DataView>;\n}\n"
  },
  {
    "path": "src/lib/transport/WebUsbTransport.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nimport {Transport} from './Transport';\nimport {hexdump} from '../Helpers';\nimport {Options} from '../Options';\n\nconst ADB_DEVICE = {classCode: 255, subclassCode: 66, protocolCode: 1} as USBDeviceFilter;\nconst FASTBOOT_DEVICE = {classCode: 255, subclassCode: 66, protocolCode: 3} as USBDeviceFilter;\nconst DEVICE_FILTERS = [ADB_DEVICE, FASTBOOT_DEVICE];\n\ninterface DeviceMatch {\n  conf: USBConfiguration;\n  intf: USBInterface;\n  alternate: USBAlternateInterface;\n}\n\n/**\n * An implementation of {@link Transport} using WebUSB as the tranport layer.\n */\nexport class WebUsbTransport implements Transport {\n  private constructor(\n    readonly device: USBDevice,\n    readonly match: DeviceMatch,\n    readonly endpointIn: number,\n    readonly endpointOut: number,\n    readonly options: Options,\n    readonly log = console.log) {\n  }\n\n  /**\n   *  Releases the interface and closes the connection to the WebUSB device\n   */\n  async close(): Promise<void> {\n    await this.device.releaseInterface(this.match.intf.interfaceNumber);\n    await this.device.close();\n  }\n\n  /**\n   * Sends data to the USB device\n   *\n   * @param {ArrayBuffer} data the data to be sent to the interface\n   */\n  async write(data: ArrayBuffer): Promise<void> {\n    if (this.options.dump) {\n      hexdump(new DataView(data), '' + this.endpointOut + '==> ');\n    }\n\n    await this.device.transferOut(this.endpointOut, data);\n  }\n\n  /**\n   * Receives data from the USB device\n   *\n   * @param {number} len the length of date to be read\n   * @returns {Promise<DataView} data read from the device\n   */\n  async read(len: number): Promise<DataView> {\n    const response = await this.device.transferIn(this.endpointIn, len);\n    if (!response.data) {\n      throw new Error('Response didn\\'t contain any data');\n    }\n    return response.data;\n  }\n\n  /**\n   * @returns {boolean} true if the connected device is an ADB device.\n   */\n\tisAdb(): boolean {\n\t\tconst match = WebUsbTransport.findMatch(this.device, ADB_DEVICE);\n\t\treturn match != null;\n\t};\n\n  /**\n   * @returns {boolean} true if the connected device is a Fastboot device.\n   */\n\tisFastboot(): boolean {\n\t\tconst match = WebUsbTransport.findMatch(this.device, FASTBOOT_DEVICE);\n\t\treturn match != null;\n  };\n\n  /**\n   * Opens a connection to a WebUSB device\n   *\n   * @param options\n   */\n  static async open(options: Options): Promise<WebUsbTransport> {\n    if (!navigator.usb) {\n      throw new Error(\n          'WebUSB is not available. Ensure the page is served over HTTPS or localhost, ' +\n          'and that you are using a Chromium-based browser.');\n    }\n    const device = await navigator.usb.requestDevice({filters: DEVICE_FILTERS});\n    await device.open();\n\n    // Find the WebUSB device\n    const match = this.findMatch(device, ADB_DEVICE);\n    if (!match) {\n      throw new Error('Could not find an ADB device');\n    }\n\n    // Select the configuration and claim the interface\n    await device.selectConfiguration(match.conf.configurationValue);\n    await device.claimInterface(match.intf.interfaceNumber);\n    // await device.selectAlternateInterface(\n    //     match.intf.interfaceNumber, match.alternate.alternateSetting);\n\n    // Store the correct endpoints\n    const endpointIn = WebUsbTransport.getEndpointNum(match.alternate.endpoints, 'in');\n    const endpointOut = WebUsbTransport.getEndpointNum(match.alternate.endpoints, 'out');\n\n    const transport = new WebUsbTransport(device, match, endpointIn, endpointOut, options);\n    if (options.debug) {\n      console.log('Created new Transport: ', transport);\n    }\n    return transport;\n  }\n\n  private static findMatch(device: USBDevice, filter: USBDeviceFilter): DeviceMatch | null {\n    for (const configuration of device.configurations) {\n      for (const intf of configuration.interfaces) {\n        for (const alternate of intf.alternates) {\n          if (filter.classCode === alternate.interfaceClass &&\n              filter.subclassCode === alternate.interfaceSubclass &&\n              filter.protocolCode === alternate.interfaceProtocol) {\n            return {\n              conf: configuration,\n              intf,\n              alternate\n            };\n          }\n        }\n      }\n    }\n    return null;\n  }\n\n  private static getEndpointNum(endpoints: USBEndpoint[], dir: 'in' | 'out', type = 'bulk'): number {\n    for(const ep of endpoints) {\n      if (ep.direction === dir && ep.type === type) {\n        return ep.endpointNumber;\n      }\n    }\n    throw new Error(`Cannot find ${dir} endpoint`);\n  }\n}\n"
  },
  {
    "path": "src/lib/transport/index.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nexport * from './Transport';\nexport * from './WebUsbTransport';\n"
  },
  {
    "path": "src/spec/AdbClientSpec.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nimport {AdbClient} from '../lib/AdbClient';\nimport {MockTransport} from './mock/MockTransport';\nimport {MockKeyStore} from './mock/MockKeyStore';\nimport {Options} from '../lib/Options';\n\ndescribe('AdbClient', () => {\n  const keyStore = new MockKeyStore();\n  const options = {\n    debug: false,\n    dump: false,\n    useChecksum: false,\n    keySize: 2048,\n  } as Options;\n\n  describe('#connect', () => {\n\n    let transport: MockTransport;\n\n    beforeEach(() => {\n      transport = new MockTransport();\n    });\n\n    it('Server doesn\\'t request AUTH and responds with CNXN', async () => {\n      await transport.pushFromFile('src/spec/data/messages/connect/connect_simple.json');\n      const adbClient = new AdbClient(transport, options, keyStore);\n      const adbDeviceInfo = await adbClient.connect();\n      expect(adbDeviceInfo).toBeDefined();\n    });\n\n    it('Server responds with AUTH and then CNXN', async () => {\n      await transport.pushFromFile('src/spec/data/messages/connect/connect_auth_public_key.json');\n      const adbClient = new AdbClient(transport, options, keyStore);\n      const adbDeviceInfo = await adbClient.connect();\n      expect(adbDeviceInfo).toBeDefined();\n    });\n  });\n});\n"
  },
  {
    "path": "src/spec/IndexedDbKeyStoreSpec.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nimport {IDBFactory} from 'fake-indexeddb';\nimport {IndexedDbKeyStore} from '../lib/IndexedDbKeyStore';\n\n// A minimal CryptoKeyPair stub for testing storage/retrieval without Web Crypto.\nfunction makeFakeKeyPair(id: number): CryptoKeyPair {\n  return {\n    privateKey: {type: 'private', id} as unknown as CryptoKey,\n    publicKey: {type: 'public', id} as unknown as CryptoKey,\n  };\n}\n\ndescribe('IndexedDbKeyStore', () => {\n  beforeEach(() => {\n    // Each test gets a fresh in-memory IndexedDB to prevent state leakage.\n    (global as unknown as Record<string, unknown>)['indexedDB'] = new IDBFactory();\n  });\n\n  describe('#loadKeys', () => {\n    it('returns an empty array when no keys have been saved', async () => {\n      const store = new IndexedDbKeyStore();\n      const keys = await store.loadKeys();\n      expect(keys).toEqual([]);\n    });\n  });\n\n  describe('#saveKey', () => {\n    it('persists a key that is returned by loadKeys', async () => {\n      const store = new IndexedDbKeyStore();\n      const pair = makeFakeKeyPair(1);\n      await store.saveKey(pair);\n      const keys = await store.loadKeys();\n      expect(keys.length).toBe(1);\n      expect(keys[0]).toEqual(pair);\n    });\n\n    it('persists multiple keys in insertion order', async () => {\n      const store = new IndexedDbKeyStore();\n      const pair1 = makeFakeKeyPair(1);\n      const pair2 = makeFakeKeyPair(2);\n      await store.saveKey(pair1);\n      await store.saveKey(pair2);\n      const keys = await store.loadKeys();\n      expect(keys.length).toBe(2);\n      expect(keys[0]).toEqual(pair1);\n      expect(keys[1]).toEqual(pair2);\n    });\n\n    it('keys survive across separate IndexedDbKeyStore instances sharing the same DB', async () => {\n      const store1 = new IndexedDbKeyStore();\n      const pair = makeFakeKeyPair(1);\n      await store1.saveKey(pair);\n\n      // Simulate a page reload: new instance, same underlying DB (global.indexedDB unchanged).\n      const store2 = new IndexedDbKeyStore();\n      const keys = await store2.loadKeys();\n      expect(keys.length).toBe(1);\n      expect(keys[0]).toEqual(pair);\n    });\n  });\n});\n"
  },
  {
    "path": "src/spec/QueuesSpec.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nimport {Queue, AsyncBlockingQueue} from '../lib/Queues';\n\ndescribe('Queues', () => {\n  describe('Queue', () => {\n    describe('#constructor', () => {\n      it('Builds an empty queue', () => {\n        const queue = new Queue<string>();\n        expect(queue.isEmpty()).toBeTrue();\n      });\n    });\n\n    describe('#enqueue', () => {\n      it('Queue is not empty after enqueing an item', () => {\n        const queue = new Queue<string>();\n        queue.enqueue('test');\n        expect(queue.isEmpty()).toBeFalse();\n      });\n    });\n\n    describe('#dequeue', () => {\n      it('Throws Error when dequeing an empty list', () => {\n        const queue = new Queue<string>();\n        expect(queue.dequeue).toThrowError();\n      });\n\n      it('Dequeues with one value', () => {\n        const queue = new Queue<string>();\n        queue.enqueue('one');\n        expect(queue.dequeue()).toBe('one');\n        expect(queue.isEmpty()).toBeTrue();\n      });\n\n      it('Dequeues in the correct order', () => {\n        const queue = new Queue<string>();\n        queue.enqueue('one');\n        queue.enqueue('two');\n        expect(queue.dequeue()).toBe('one');\n        expect(queue.dequeue()).toBe('two');\n        queue.enqueue('three');\n        expect(queue.dequeue()).toBe('three');\n        expect(queue.isEmpty()).toBeTrue();\n      });\n    });\n  });\n\n  describe('AsyncBlockingQueue', () => {\n    describe('#contructor', () => {\n      it('Constructs and empty queue', () => {\n        const queue = new AsyncBlockingQueue<string>();\n        expect(queue.hasPendingPromises()).toBeFalse();\n        expect(queue.hasPendingResolvers()).toBeFalse();\n      });\n    });\n\n    describe('#enqueue', () => {\n      it('enqueue adds a pending Promise', () => {\n        const queue = new AsyncBlockingQueue<string>();\n        queue.enqueue('test');\n        expect(queue.hasPendingPromises()).toBeTrue();\n        expect(queue.hasPendingResolvers()).toBeFalse();\n      });\n\n      it('enqueue after dequeue clears Promises and Resolvers', () => {\n        const queue = new AsyncBlockingQueue<string>();\n        queue.dequeue();\n        queue.enqueue('test');\n        expect(queue.hasPendingPromises()).toBeFalse();\n        expect(queue.hasPendingResolvers()).toBeFalse();\n      });\n    });\n\n    describe('#dequeue', () => {\n      it('dequeue adds a pending Resolver', () => {\n        const queue = new AsyncBlockingQueue<string>();\n        queue.dequeue();\n        expect(queue.hasPendingPromises()).toBeFalse();\n        expect(queue.hasPendingResolvers()).toBeTrue();\n      });\n\n      it('dequeue after enqueue clears Promises and Resolvers', () => {\n        const queue = new AsyncBlockingQueue<string>();\n        queue.enqueue('test');\n        queue.dequeue();\n        expect(queue.hasPendingPromises()).toBeFalse();\n        expect(queue.hasPendingResolvers()).toBeFalse();\n      });\n\n      it('Dequeues correctly after enqueue', async () => {\n        const queue = new AsyncBlockingQueue<string>();\n        queue.enqueue('test');\n        const value = await queue.dequeue();\n        expect(value).toBe('test');\n      });\n\n      it('Dequeues correctly when dequeue is called before enqueue', async () => {\n        const queue = new AsyncBlockingQueue<string>();\n        const promise = queue.dequeue();\n        queue.enqueue('test');\n        const value = await promise;\n        expect(value).toBe('test');\n      });\n\n      it('Dequeues in the correct order, with enqueues then dequeues', async () => {\n        const queue = new AsyncBlockingQueue<string>();\n        queue.enqueue('one');\n        queue.enqueue('two');\n        expect(await queue.dequeue()).toBe('one');\n        expect(await queue.dequeue()).toBe('two');\n      });\n\n      it('Dequeues in the correct order, enqueue > dequeue > dequeue > enqueue', async () => {\n        const queue = new AsyncBlockingQueue<string>();\n        queue.enqueue('one');\n        expect(await queue.dequeue()).toBe('one');\n        const promise = queue.dequeue();\n        queue.enqueue('two');\n        expect(await promise).toBe('two');\n      });\n\n      it('Dequeues in the correct order, dequeue > dequeue > enqueue > enqueue', async () => {\n        const queue = new AsyncBlockingQueue<string>();\n        const p1 = queue.dequeue();\n        const p2 = queue.dequeue();\n\n        queue.enqueue('one');\n        queue.enqueue('two');\n\n        expect(await p1).toBe('one');\n        expect(await p2).toBe('two');\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "src/spec/StreamSpec.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nimport {AdbClient} from '../lib/AdbClient';\nimport {MockKeyStore} from './mock/MockKeyStore';\nimport {Options} from '../lib/Options';\nimport {MockTransport} from './mock/MockTransport';\nimport {Stream} from '../lib/Stream';\n\nconst options = {\n  debug: false,\n  dump: false,\n  useChecksum: false,\n  keySize: 2048,\n} as Options;\n\ndescribe('Stream', () => {\n  describe('#open', () => {\n    it('Opens a stream', async () => {\n      const mockTransport = new MockTransport();\n      await mockTransport.pushFromFile('src/spec/data/messages/stream/open.json');\n      const adbClient = new AdbClient(mockTransport, options, new MockKeyStore());\n      const stream = await Stream.open(adbClient, 'test:', options);\n      expect(stream.localId).toBe(1);\n      expect(stream.remoteId).toBe(34); // Defined in open.json\n      expect(stream.service).toBe('test:');\n    });\n  });\n});\n"
  },
  {
    "path": "src/spec/SyncFrameSpec.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nimport {encodeCmd} from '../lib/Helpers';\nimport {SyncFrame} from '../lib/SyncFrame';\n\ndescribe('SyncFrame', () => {\n  describe('#fromDataView', () => {\n    it('Reads a SyncFrame from a DataView', () => {\n      const dataView = new DataView(new ArrayBuffer(8));\n      dataView.setUint32(0, encodeCmd('WRTE'), true);\n      dataView.setUint32(4, 256, true);\n      const syncFrame = SyncFrame.fromDataView(dataView);\n      expect(syncFrame.cmd).toBe('WRTE');\n      expect(syncFrame.byteLength).toBe(256);\n    });\n  });\n\n  describe('#toDataView', () => {\n    it('Writes a SyncFrame to a DataView', () => {\n      const syncFrame = new SyncFrame('WRTE', 256);\n      const dataView = syncFrame.toDataView();\n      const encodedCmd = encodeCmd('WRTE');\n      expect(dataView.getUint32(0, true)).toBe(encodedCmd);\n      expect(dataView.getUint32(4, true)).toBe(256);\n    });\n  })\n});\n"
  },
  {
    "path": "src/spec/data/messages/connect/connect_auth_public_key.json",
    "content": "[\n  {\"cmd\": \"AUTH\", \"arg0\": 1, \"arg1\": 0, \"data\": \"auth_token\"},\n  {\"cmd\": \"CNXN\", \"arg0\": 1, \"arg1\": 65536, \"data\": \"host://\"}\n]\n"
  },
  {
    "path": "src/spec/data/messages/connect/connect_simple.json",
    "content": "[{\"cmd\": \"CNXN\", \"arg0\": 1, \"arg1\": 65536, \"data\": \"host://\"}]"
  },
  {
    "path": "src/spec/data/messages/stream/open.json",
    "content": "[\n  {\"cmd\": \"OKAY\", \"arg0\": 34, \"arg1\": 1}\n]\n"
  },
  {
    "path": "src/spec/message/MessageChannelSpec.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nimport {MockTransport} from '../mock/MockTransport';\nimport {MockMessageListener} from '../mock/MockMessageListener';\nimport {Message, MessageChannel, MessageHeader} from '../../lib/message';\nimport {Options} from '../../lib/Options';\n\ndescribe('MessageChannel', () => {\n  const options = {\n    debug: false,\n    dump: false,\n    useChecksum: false,\n    keySize: 2048,\n  } as Options;\n\n  let messageListener: MockMessageListener;\n  let transport: MockTransport;\n  let messageChannel: MessageChannel;\n\n  describe('#write', () => {\n    beforeEach(() => {\n      messageListener = new MockMessageListener();\n      transport = new MockTransport();\n      messageChannel = new MessageChannel(transport, options, messageListener);\n    });\n\n    it('writes a message with without data', async () => {\n      const message = Message.newMessage('CNXN', 1, 2, true);\n      await messageChannel.write(message);\n      messageChannel.close();\n      expect(transport.receivedData.length).toBe(1);\n      expect(MessageHeader.parse(transport.receivedData[0])).toEqual(message.header);\n    });\n\n    it('writes a message with with data', async () => {\n      const data = new DataView(new TextEncoder().encode('test').buffer);\n      const message = Message.newMessage('CNXN', 1, 2, true, data);\n      await messageChannel.write(message);\n      messageChannel.close();\n      expect(transport.receivedData.length).toBe(2);\n      expect(MessageHeader.parse(transport.receivedData[0])).toEqual(message.header);\n      expect(transport.receivedData[1].byteLength).toBe(4);\n    });\n  });\n\n  describe('#readLoop', () => {\n    const messageWithoutData = Message.newMessage('MOCK', 0, 0, true);\n    const data = new DataView(new TextEncoder().encode('test').buffer);\n    const messageWithData = Message.newMessage('MOCK', 0, 0, true, data);\n\n    beforeEach(() => {\n      messageListener = new MockMessageListener();\n      transport = new MockTransport();\n    });\n\n    it('Receives a Message', async () => {\n      transport.pushData(messageWithoutData.header.toDataView());\n      messageChannel = new MessageChannel(transport, options, messageListener);\n      const receivedMessage = await messageListener.messageQueue.dequeue();\n      expect(receivedMessage.header).toEqual(messageWithoutData.header);\n    });\n\n    it('Receives a Message with data', async () => {\n      transport.pushData(messageWithData.header.toDataView());\n      transport.pushData(messageWithData.data!);\n      messageChannel = new MessageChannel(transport, options, messageListener);\n      const receivedMessage = await messageListener.messageQueue.dequeue();\n      expect(receivedMessage).toEqual(messageWithData);\n    });\n\n    it('Receives Messages in the right order', async () => {\n      transport.pushData(messageWithoutData.header.toDataView());\n      transport.pushData(messageWithData.header.toDataView());\n      transport.pushData(messageWithData.data!);\n      messageChannel = new MessageChannel(transport, options, messageListener);\n      const receivedMessage1 = await messageListener.messageQueue.dequeue();\n      const receivedMessage2 = await messageListener.messageQueue.dequeue();\n      expect(receivedMessage1).toEqual(messageWithoutData);\n      expect(receivedMessage2).toEqual(messageWithData);\n    });\n  });\n});\n"
  },
  {
    "path": "src/spec/message/MessageHeaderSpec.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nimport {MessageHeader} from '../../lib/message';\n\ndescribe('MessageHeader', () => {\n  describe('#parse', () => {\n    it('parses a messsage header, with checksum enabled', () => {\n      const data = new DataView(new ArrayBuffer(24));\n      data.setUint32(0, 0x4e584e43, true); // CNXN\n      data.setUint32(4, 0x01000000, true); // Version\n      data.setUint32(8, 256 * 1024, true); // Length\n      data.setUint32(12, 16, true); // Length\n      data.setUint32(16, 0, true); // Checksum\n      data.setUint32(20, 0x4e584e43 ^ 0xffffffff, true); // Magic\n\n      const header = MessageHeader.parse(data, true);\n      expect(header.cmd).toBe('CNXN');\n      expect(header.arg0).toBe(0x01000000);\n      expect(header.arg1).toBe(256 * 1024);\n      expect(header.length).toBe(16);\n      expect(header.checksum).toBe(0);\n    });\n\n    it('Fails to parse a header with an invalid magic / checksum enabled', () => {\n      const data = new DataView(new ArrayBuffer(24));\n      data.setUint32(0, 0x4e584e43, true); // CNXN\n      data.setUint32(4, 0x01000000, true); // Version\n      data.setUint32(8, 256 * 1024, true); // Length\n      data.setUint32(12, 16, true); // Length\n      data.setUint32(16, 0, true); // Checksum\n      data.setUint32(20, 0, true); // Magic\n      expect((() => MessageHeader.parse(data, true))).toThrowError('magic mismatch');\n    });\n  });\n\n  describe('#toDataView', () => {\n    it ('converts a header to a DataView', () => {\n      const data = new MessageHeader('CNXN', 0x01000000, 256 * 1024, 16, 0).toDataView();\n      expect(data.getUint32(0, true)).toBe(0x4e584e43);\n      expect(data.getUint32(4, true)).toBe(0x01000000);\n      expect(data.getUint32(8, true)).toBe(256 * 1024);\n      expect(data.getUint32(12, true)).toBe(16);\n      expect(data.getUint32(16, true)).toBe(0);\n      expect(data.getInt32(20, true)).toBe((0x4e584e43 ^ 0xffffffff));\n    });\n  });\n});\n"
  },
  {
    "path": "src/spec/message/MessageSpec.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nimport {Message} from '../../lib/message';\n\ndescribe('Message', () => {\n  describe('#newMessage', () => {\n    it('Creates a message without data', () => {\n      const message = Message.newMessage('CNXN', 1, 2, true);\n      expect(message.header.cmd).toBe('CNXN');\n      expect(message.header.arg0).toBe(1);\n      expect(message.header.arg1).toBe(2);\n      expect(message.header.length).toBe(0);\n      expect(message.header.checksum).toBe(0);\n      expect(message.data).toBeUndefined();\n    });\n\n    it('Creates a message with data and checksum enabled', () => {\n      const data = new DataView(new TextEncoder().encode('test').buffer);\n      const message = Message.newMessage('CNXN', 1, 2, true, data);\n      expect(message.header.cmd).toBe('CNXN');\n      expect(message.header.arg0).toBe(1);\n      expect(message.header.arg1).toBe(2);\n      expect(message.header.length).toBe(data.byteLength);\n      expect(message.header.checksum).toBe(448);\n      expect(message.data).toEqual(data);\n    });\n\n    it('Creates a message with data and checksum disabled', () => {\n      const data = new DataView(new TextEncoder().encode('test').buffer);\n      const message = Message.newMessage('CNXN', 1, 2, false, data);\n      expect(message.header.cmd).toBe('CNXN');\n      expect(message.header.arg0).toBe(1);\n      expect(message.header.arg1).toBe(2);\n      expect(message.header.length).toBe(data.byteLength);\n      expect(message.header.checksum).toBe(0);\n      expect(message.data).toEqual(data);\n    });\n  });\n\n  describe('#dataAsString', () => {\n    it('Returns correct string value for data', () => {\n      const data = new DataView(new TextEncoder().encode('test').buffer);\n      const message = Message.newMessage('CNXN', 1, 2, false, data);\n      expect(message.dataAsString()).toBe('test');\n    });\n\n    it('Returns null if data is not available', () => {\n      const message = Message.newMessage('CNXN', 1, 2, false);\n      expect(message.dataAsString()).toBeNull()\n    });\n  });\n\n  describe('#open', () => {\n    it('Creates an OPEN message', () => {\n      const message = Message.open(1, 2, 'service', true);\n      expect(message.header.cmd).toBe('OPEN');\n      expect(message.header.arg0).toBe(1);\n      expect(message.header.arg1).toBe(2);\n      expect(message.header.length).toBe(8);\n      expect(message.header.checksum).toBe(753);\n    });\n  });\n\n  describe('#cnxn', () => {\n    it('Creates an CNXN message', () => {\n      const message = Message.cnxn(1, 2, 'banner', true);\n      expect(message.header.cmd).toBe('CNXN');\n      expect(message.header.arg0).toBe(1);\n      expect(message.header.arg1).toBe(2);\n      expect(message.header.length).toBe(6);\n      expect(message.header.checksum).toBe(630);\n    });\n  });\n\n  describe('#authSignature', () => {\n    it('Creates an AUTH message with a signed token', () => {\n      const data = new DataView(new TextEncoder().encode('signed').buffer);\n      const message = Message.authSignature(data, true);\n      expect(message.header.cmd).toBe('AUTH');\n      expect(message.header.arg0).toBe(2);\n      expect(message.header.arg1).toBe(0);\n      expect(message.header.length).toBe(6);\n      expect(message.header.checksum).toBe(634);\n    });\n  });\n\n  describe('#authPublicKey', () => {\n    it('Creates an AUTH message with a public key', () => {\n      // Node doesn't have a global btoa function. We patch it here for the test.\n      globalThis.btoa = (input: string): string => {\n        return Buffer.from(input).toString('base64');\n      };\n      const data = new DataView(new TextEncoder().encode('publickey').buffer);\n      const message = Message.authPublicKey(data, true);\n      expect(message.header.cmd).toBe('AUTH');\n      expect(message.header.arg0).toBe(3);\n      expect(message.header.arg1).toBe(0);\n      expect(message.header.length).toBe(13);\n      expect(message.header.checksum).toBe(1031);\n    });\n  });\n});\n"
  },
  {
    "path": "src/spec/mock/MockKeyStore.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nimport {KeyStore} from '../../lib/KeyStore';\n\nexport class MockKeyStore implements KeyStore {\n  loadKeys(): Promise<CryptoKeyPair[]> {\n    return Promise.resolve([]);\n  }\n\n  saveKey(): Promise<void> {\n    return Promise.resolve();\n  };\n}\n"
  },
  {
    "path": "src/spec/mock/MockMessageListener.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nimport {AsyncBlockingQueue} from '../../lib/Queues';\nimport {Message, MessageListener} from '../../lib/message';\n\nexport class MockMessageListener implements MessageListener {\n  messageQueue = new AsyncBlockingQueue<Message>();\n\n  newMessage(msg: Message): void {\n    this.messageQueue.enqueue(msg);\n  }\n}\n"
  },
  {
    "path": "src/spec/mock/MockTransport.ts",
    "content": "/*\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\nimport {Transport} from '../../lib/transport';\nimport {Message} from '../../lib/message';\nimport * as fs from 'fs';\n\nexport class MockTransport implements Transport {\n  receivedData: DataView[] = [];\n  pendingData: ArrayBuffer = new ArrayBuffer(0);\n  pos = 0;\n  reject?: (reason: Error) => void;\n\n  async pushFromFile(fileName: string): Promise<void> {\n    const textEncoder = new TextEncoder();\n    const messages = JSON.parse(await fs.promises.readFile(fileName, {encoding: \"utf-8\"}));\n    for (const jsonMessage of messages) {\n      const cmd = jsonMessage.cmd;\n      const arg0 = jsonMessage.arg0;\n      const arg1 = jsonMessage.arg1;\n      const data = jsonMessage.data ?\n          new DataView(textEncoder.encode(jsonMessage.data).buffer) : jsonMessage.data;\n      const useChecksum = !!jsonMessage.useChecksum;\n      this.pushMessage(Message.newMessage(cmd, arg0, arg1, useChecksum, data));\n    }\n  }\n\n  pushData(data: DataView): void {\n    const buffer = data.buffer;\n    const tmp = new Uint8Array(this.pendingData.byteLength + buffer.byteLength);\n    tmp.set(new Uint8Array(this.pendingData), 0);\n    tmp.set(new Uint8Array(buffer), this.pendingData.byteLength);\n    this.pendingData = tmp.buffer;\n  }\n\n  pushMessage(msg: Message): void {\n    this.pushData(msg.header.toDataView());\n    if (msg.data) {\n      this.pushData(msg.data);\n    }\n  }\n\n  async read(len: number): Promise<DataView> {\n    // Our buffer doesn't have enough data.\n    if (this.pendingData.byteLength - this.pos < len) {\n      return new Promise((_, reject) => {\n        this.reject = reject;\n      });\n    }\n\n    const dataView = new DataView(this.pendingData, this.pos, len);\n    this.pos += len;\n    return dataView;\n  }\n\n  async write(data: ArrayBuffer): Promise<void> {\n    this.receivedData.push(new DataView(data));\n    return Promise.resolve();\n  }\n\n  close(): void {\n    if (this.reject) {\n      this.reject(new Error('Transport Closed'));\n    }\n  }\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es2015\",\n    \"module\": \"commonjs\",\n    \"outDir\": \"./dist\",\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"declaration\": true,\n    \"moduleResolution\": \"node\",\n    \"lib\": [\"ES2015\", \"DOM\"]\n  },\n  \"typedocOptions\": {\n    \"mode\": \"modules\",\n    \"out\": \"docs\",\n  },\n  \"include\": [\"src/**/*.ts\"]\n}\n"
  }
]