├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── .gitmodules ├── .npmignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── SUPPORT.md ├── azure-pipelines.yml ├── package-lock.json ├── package.json ├── sample ├── .gitignore ├── index.js ├── logs │ └── .gitignore ├── package-lock.json ├── package.json ├── profiles │ └── .gitignore └── traces │ └── .gitignore └── src ├── index.ts └── tsconfig.json /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to npm 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: actions/setup-node@v1 15 | with: 16 | registry-url: 'https://registry.npmjs.org' 17 | 18 | # Ensure everything is set up right 19 | - run: 'npm ci' 20 | - run: 'npm run build' 21 | 22 | - uses: orta/npm-should-deploy-action@main 23 | id: check 24 | 25 | - run: 'npm publish' 26 | if: ${{ steps.check.outputs.deploy == 'true' }} 27 | env: 28 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.js 2 | *.map 3 | *.d.ts 4 | node_modules 5 | *.tsbuildinfo 6 | *.tgz -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "sample/reproProject"] 2 | path = sample/reproProject 3 | url = https://github.com/microsoft/typescript-analyze-trace 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | package-lock.json 3 | CODE_OF_CONDUCT.md 4 | CONTRIBUTING.md 5 | *.map 6 | .github 7 | azure-pipelines.yml 8 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 4 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 5 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 6 | 7 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 8 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 9 | provided by the bot. You will only need to do this once across all repos using our CLA. 10 | 11 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 12 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 13 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 14 | 15 | ## Building 16 | 17 | To build, run `npm ci` to download the required packages and `npm run build` to compile. 18 | 19 | ## Submodule 20 | 21 | It should never be necessary to update the submodule - it was just a convenient way to incorporate a sample project without duplicating all the files. 22 | (In fact, updating the submodule would like necessitate updates to the demo script.) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @typescript/server-harness 2 | Tool for communicationg with a TypeScript tsserver process (i.e. a node process running the JS/TS language service). 3 | Pairs responses with requests and provides hooks for listening to events. 4 | Chiefly useful for reproducibly running editor scenarios for standalone investigation. 5 | 6 | ## Usage 7 | 8 | See `samples/index.js` for an example of how to use the harness. 9 | It can be run with `npm ci && node index`. 10 | 11 | The easiest way to figure out what the requests should contain is to look at the server log from a real editor session. 12 | In VS Code, you can access the server log by running the command `TypeScript: Open TS Server Log`. 13 | 14 | ## Deployment 15 | 16 | To publish a new version of this package, change the version in `package.json` and push to main. 17 | 18 | ## Trademarks 19 | 20 | This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft 21 | trademarks or logos is subject to and must follow 22 | [Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). 23 | Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. 24 | Any use of third-party trademarks or logos are subject to those third-party's policies. 25 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support 2 | 3 | ## How to file issues and get help 4 | 5 | This project uses GitHub Issues to track bugs and feature requests. Please search the existing 6 | issues before filing new issues to avoid duplicates. For new issues, file your bug or 7 | feature request as a new Issue. 8 | 9 | ## Microsoft Support Policy 10 | 11 | Support for this **PROJECT or PRODUCT** is limited to the resources listed above. 12 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | pr: 2 | - main 3 | 4 | pool: 5 | name: VSEngSS-MicroBuild2019-1ES 6 | 7 | variables: 8 | TeamName: TypeScript 9 | 10 | steps: 11 | - task: NuGetToolInstaller@1 12 | inputs: 13 | versionSpec: '5.x' 14 | - task: CredScan@3 15 | - task: PoliCheck@2 16 | - task: AntiMalware@4 17 | - task: PublishSecurityAnalysisLogs@3 18 | - task: PostAnalysis@2 19 | 20 | - task: NodeTool@0 21 | inputs: 22 | versionSpec: '14.x' 23 | displayName: 'Install Node.js' 24 | - script: | 25 | npm ci 26 | npm run build 27 | displayName: 'npm install and build' 28 | 29 | - task: MicroBuildCleanup@1 30 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@typescript/server-harness", 3 | "version": "0.3.5", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@typescript/server-harness", 9 | "version": "0.3.5", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "@types/node": "^14.18.12", 13 | "typescript": "^4.8.2" 14 | } 15 | }, 16 | "node_modules/@types/node": { 17 | "version": "14.18.12", 18 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.12.tgz", 19 | "integrity": "sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A==", 20 | "dev": true 21 | }, 22 | "node_modules/typescript": { 23 | "version": "4.8.2", 24 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", 25 | "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==", 26 | "dev": true, 27 | "license": "Apache-2.0", 28 | "bin": { 29 | "tsc": "bin/tsc", 30 | "tsserver": "bin/tsserver" 31 | }, 32 | "engines": { 33 | "node": ">=4.2.0" 34 | } 35 | } 36 | }, 37 | "dependencies": { 38 | "@types/node": { 39 | "version": "14.18.12", 40 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.12.tgz", 41 | "integrity": "sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A==", 42 | "dev": true 43 | }, 44 | "typescript": { 45 | "version": "4.8.2", 46 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", 47 | "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==", 48 | "dev": true 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@typescript/server-harness", 3 | "author": "Microsoft Corp.", 4 | "homepage": "https://github.com/microsoft/typescript-server-harness#readme", 5 | "version": "0.3.5", 6 | "license": "MIT", 7 | "description": "Communicate with a tsserver process", 8 | "keywords": [ 9 | "TypeScript", 10 | "Microsoft", 11 | "tsserver" 12 | ], 13 | "publishConfig": { 14 | "access": "public" 15 | }, 16 | "bugs": { 17 | "url": "https://github.com/microsoft/typescript-server-harness/issues" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/microsoft/typescript-server-harness.git" 22 | }, 23 | "main": "./dist/index.js", 24 | "typings": "./dist/index.d.ts", 25 | "scripts": { 26 | "build": "tsc -p src" 27 | }, 28 | "devDependencies": { 29 | "@types/node": "^14.18.12", 30 | "typescript": "^4.8.2" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | !index.js -------------------------------------------------------------------------------- /sample/index.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | const serverHarness = require("@typescript/server-harness"); 4 | 5 | const fs = require("fs"); 6 | const path = require("path"); 7 | const process = require("process"); 8 | const { performance } = require("perf_hooks"); 9 | 10 | const testDir = path.join(__dirname, "reproProject"); 11 | 12 | // Needed for excludedDirectories 13 | process.chdir(testDir); 14 | 15 | main().catch(e => console.error(e)); 16 | 17 | async function main() { 18 | const server = serverHarness.launchServer( 19 | path.join(__dirname, "node_modules", "typescript", "lib", "tsserver.js"), 20 | // Arguments to tsserver.js 21 | [ 22 | // ATA generates some extra network traffic and isn't usually relevant when profiling 23 | "--disableAutomaticTypingAcquisition", 24 | 25 | // Enable this if you're emulating VS 26 | // "--suppressDiagnosticEvents", 27 | 28 | // Produce a performance trace 29 | "--traceDirectory", path.join(__dirname, "traces"), 30 | 31 | // Produce a server log 32 | "--logVerbosity", "verbose", 33 | "--logFile", path.join(__dirname, "logs", "tsserver.PID.log"), 34 | ], 35 | // Arguments to node 36 | [ 37 | // Enable this to debug the server process (not the driver process) 38 | // "--inspect-brk=9230", 39 | 40 | // Generate time and heap profiles (see https://github.com/jakebailey/pprof-it for config options) 41 | // Disable logging if profiling - their cleanup handlers conflict 42 | // Disable tracing if profiling - it causes unrealistic slowdowns 43 | // `--require=${path.join(__dirname, "node_modules", "pprof-it", "dist", "index.js")}`, 44 | 45 | // Increasing the heap size is just generally a good idea 46 | "--max-old-space-size=4096", 47 | 48 | // This will enable some GC output in the server log 49 | "--expose-gc" 50 | ], 51 | // Environment variables for server process (mostly useful for pprof-it) 52 | { 53 | "PPROF_OUT": path.join(__dirname, "profiles") 54 | }); 55 | 56 | server.on("exit", code => console.log(code ? `Exited with code ${code}` : `Terminated`)); 57 | server.on("event", e => console.log(e)); 58 | 59 | let seq = 1; 60 | 61 | // Always start with a `configure` message (possibly preceded by `status` if emulating VS) 62 | await server.message({ 63 | "seq": seq++, 64 | "type": "request", 65 | "command": "configure", 66 | "arguments": { 67 | "preferences": { 68 | "includePackageJsonAutoImports": "auto" 69 | }, 70 | "watchOptions": { 71 | "excludeDirectories": ["**/node_modules"] 72 | } 73 | } 74 | }); 75 | 76 | // Open a file 77 | let start = performance.now(); 78 | const openFilePath = path.join(testDir, "src", "analyze-trace-utilities.ts"); 79 | const updateOpenResponse = await server.message({ 80 | "seq": seq++, 81 | "type": "request", 82 | "command": "updateOpen", 83 | "arguments": { 84 | "changedFiles": [], 85 | "closedFiles": [], 86 | "openFiles": [ 87 | { 88 | "file": openFilePath, 89 | "fileContent": await fs.promises.readFile(openFilePath, { encoding: "utf-8" }), 90 | "projectRootPath": testDir, 91 | "scriptKindName": "TS", // It's easy to get this wrong when copy-pasting 92 | } 93 | ] 94 | } 95 | }); 96 | let end = performance.now(); 97 | // Note: reporting the time like this is redundant if you're also collecting a trace 98 | console.log(`Initial file open took ${Math.round(end - start)} ms including ${Math.round(updateOpenResponse.performanceData.updateGraphDurationMs)} ms of update graph time`); 99 | 100 | // Request squiggles 101 | // Note: the response is uninteresting - the actual diagnostics are in events 102 | start = performance.now(); 103 | await server.message({ 104 | "seq": seq++, 105 | "type": "request", 106 | "command": "geterr", 107 | "arguments": { 108 | "delay": 0, 109 | "files": [openFilePath], 110 | } 111 | }); 112 | end = performance.now(); 113 | console.log(`Computing squiggles took ${Math.round(end - start)} ms`); 114 | 115 | // Edit the file, introducing an error 116 | // - import fs = require("fs"); 117 | // + import fs = require("fs1"); 118 | await server.message({ 119 | "seq": seq++, 120 | "type": "request", 121 | "command": "updateOpen", 122 | "arguments": { 123 | "closedFiles": [], 124 | "openFiles": [], 125 | "changedFiles": [ 126 | { 127 | "fileName": openFilePath, 128 | "textChanges": [{ "newText": "1", "start": { "line": 4, "offset": 24 }, "end": { "line": 4, "offset": 24 } }], 129 | } 130 | ], 131 | } 132 | }); 133 | 134 | start = performance.now(); 135 | await server.message({ 136 | "seq": seq++, 137 | "type": "request", 138 | "command": "geterr", 139 | "arguments": { 140 | "delay": 0, 141 | "files": [openFilePath], 142 | } 143 | }); 144 | end = performance.now(); 145 | console.log(`Computing squiggles (with error) took ${Math.round(end - start)} ms`); 146 | 147 | // Revert edit 148 | await server.message({ 149 | "seq": seq++, 150 | "type": "request", 151 | "command": "updateOpen", 152 | "arguments": { 153 | "closedFiles": [], 154 | "openFiles": [], 155 | "changedFiles": [ 156 | { 157 | "fileName": openFilePath, 158 | "textChanges": [{ "newText": "", "start": { "line": 4, "offset": 24 }, "end": { "line": 4, "offset": 25 } }], 159 | } 160 | ], 161 | } 162 | }); 163 | 164 | 165 | 166 | try { 167 | // Find all references to `normalizePositions` 168 | start = performance.now(); 169 | const references = await server.message({ 170 | "seq": seq++, 171 | "type": "request", 172 | "command": "references", 173 | "arguments": { 174 | "file": openFilePath, 175 | "line": 7, 176 | "offset": 8 177 | } 178 | }); 179 | end = performance.now(); 180 | console.log(`Found ${references.body.refs.length} references in ${Math.round(end - start)} ms`); 181 | } 182 | catch (err) { 183 | console.error("Caught " + err); 184 | } 185 | 186 | // Tell the server to shut down 187 | // await server.message({ "seq": seq++, "command": "exit" }); 188 | 189 | // console.log("Killing"); 190 | // server.kill(); 191 | // console.log("Waiting"); 192 | // const sss = performance.now(); 193 | // while (performance.now() - sss < 2000) { } 194 | 195 | console.log("Exiting"); 196 | console.log(await server.exitOrKill(1)); 197 | } -------------------------------------------------------------------------------- /sample/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /sample/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-server-driver", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "typescript-server-driver", 9 | "version": "1.0.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "@typescript/server-harness": "file:..", 13 | "pprof-it": "^1.0.0", 14 | "typescript": "^4.8.2" 15 | } 16 | }, 17 | "..": { 18 | "name": "@typescript/server-harness", 19 | "version": "0.1.5", 20 | "license": "MIT", 21 | "devDependencies": { 22 | "@types/node": "^14.18.12", 23 | "typescript": "^4.8.2" 24 | } 25 | }, 26 | "node_modules/@datadog/pprof": { 27 | "version": "1.0.2", 28 | "resolved": "https://registry.npmjs.org/@datadog/pprof/-/pprof-1.0.2.tgz", 29 | "integrity": "sha512-AMTK55W3Aa2QX2X8mN9SQfDGw3HvwIK9Or8pXQFss+kjtH5pCkO9oqE5838MeXgRh9BR8HWrjAQE3Ji7FRCK2g==", 30 | "hasInstallScript": true, 31 | "dependencies": { 32 | "delay": "^5.0.0", 33 | "findit2": "^2.2.3", 34 | "nan": "^2.16.0", 35 | "node-gyp-build": "^3.9.0", 36 | "p-limit": "^3.1.0", 37 | "pify": "^5.0.0", 38 | "protobufjs": "^7.0.0", 39 | "rimraf": "^3.0.2", 40 | "semver": "^7.3.5", 41 | "source-map": "^0.7.3", 42 | "split": "^1.0.1" 43 | }, 44 | "engines": { 45 | "node": ">=12" 46 | } 47 | }, 48 | "node_modules/@protobufjs/aspromise": { 49 | "version": "1.1.2", 50 | "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", 51 | "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" 52 | }, 53 | "node_modules/@protobufjs/base64": { 54 | "version": "1.1.2", 55 | "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", 56 | "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" 57 | }, 58 | "node_modules/@protobufjs/codegen": { 59 | "version": "2.0.4", 60 | "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", 61 | "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" 62 | }, 63 | "node_modules/@protobufjs/eventemitter": { 64 | "version": "1.1.0", 65 | "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", 66 | "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" 67 | }, 68 | "node_modules/@protobufjs/fetch": { 69 | "version": "1.1.0", 70 | "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", 71 | "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", 72 | "dependencies": { 73 | "@protobufjs/aspromise": "^1.1.1", 74 | "@protobufjs/inquire": "^1.1.0" 75 | } 76 | }, 77 | "node_modules/@protobufjs/float": { 78 | "version": "1.0.2", 79 | "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", 80 | "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" 81 | }, 82 | "node_modules/@protobufjs/inquire": { 83 | "version": "1.1.0", 84 | "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", 85 | "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" 86 | }, 87 | "node_modules/@protobufjs/path": { 88 | "version": "1.1.2", 89 | "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", 90 | "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" 91 | }, 92 | "node_modules/@protobufjs/pool": { 93 | "version": "1.1.0", 94 | "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", 95 | "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" 96 | }, 97 | "node_modules/@protobufjs/utf8": { 98 | "version": "1.1.0", 99 | "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", 100 | "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" 101 | }, 102 | "node_modules/@types/long": { 103 | "version": "4.0.2", 104 | "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", 105 | "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" 106 | }, 107 | "node_modules/@types/node": { 108 | "version": "18.7.13", 109 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.13.tgz", 110 | "integrity": "sha512-46yIhxSe5xEaJZXWdIBP7GU4HDTG8/eo0qd9atdiL+lFpA03y8KS+lkTN834TWJj5767GbWv4n/P6efyTFt1Dw==" 111 | }, 112 | "node_modules/@typescript/server-harness": { 113 | "resolved": "..", 114 | "link": true 115 | }, 116 | "node_modules/balanced-match": { 117 | "version": "1.0.2", 118 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 119 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 120 | }, 121 | "node_modules/brace-expansion": { 122 | "version": "1.1.11", 123 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 124 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 125 | "dependencies": { 126 | "balanced-match": "^1.0.0", 127 | "concat-map": "0.0.1" 128 | } 129 | }, 130 | "node_modules/concat-map": { 131 | "version": "0.0.1", 132 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 133 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" 134 | }, 135 | "node_modules/delay": { 136 | "version": "5.0.0", 137 | "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", 138 | "integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==", 139 | "engines": { 140 | "node": ">=10" 141 | }, 142 | "funding": { 143 | "url": "https://github.com/sponsors/sindresorhus" 144 | } 145 | }, 146 | "node_modules/findit2": { 147 | "version": "2.2.3", 148 | "resolved": "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz", 149 | "integrity": "sha512-lg/Moejf4qXovVutL0Lz4IsaPoNYMuxt4PA0nGqFxnJ1CTTGGlEO2wKgoDpwknhvZ8k4Q2F+eesgkLbG2Mxfog==", 150 | "engines": { 151 | "node": ">=0.8.22" 152 | } 153 | }, 154 | "node_modules/fs.realpath": { 155 | "version": "1.0.0", 156 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 157 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" 158 | }, 159 | "node_modules/glob": { 160 | "version": "7.2.3", 161 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 162 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 163 | "dependencies": { 164 | "fs.realpath": "^1.0.0", 165 | "inflight": "^1.0.4", 166 | "inherits": "2", 167 | "minimatch": "^3.1.1", 168 | "once": "^1.3.0", 169 | "path-is-absolute": "^1.0.0" 170 | }, 171 | "engines": { 172 | "node": "*" 173 | }, 174 | "funding": { 175 | "url": "https://github.com/sponsors/isaacs" 176 | } 177 | }, 178 | "node_modules/inflight": { 179 | "version": "1.0.6", 180 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 181 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 182 | "dependencies": { 183 | "once": "^1.3.0", 184 | "wrappy": "1" 185 | } 186 | }, 187 | "node_modules/inherits": { 188 | "version": "2.0.4", 189 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 190 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 191 | }, 192 | "node_modules/long": { 193 | "version": "5.2.0", 194 | "resolved": "https://registry.npmjs.org/long/-/long-5.2.0.tgz", 195 | "integrity": "sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w==" 196 | }, 197 | "node_modules/lru-cache": { 198 | "version": "6.0.0", 199 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 200 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 201 | "dependencies": { 202 | "yallist": "^4.0.0" 203 | }, 204 | "engines": { 205 | "node": ">=10" 206 | } 207 | }, 208 | "node_modules/minimatch": { 209 | "version": "3.1.2", 210 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 211 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 212 | "dependencies": { 213 | "brace-expansion": "^1.1.7" 214 | }, 215 | "engines": { 216 | "node": "*" 217 | } 218 | }, 219 | "node_modules/nan": { 220 | "version": "2.16.0", 221 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz", 222 | "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==" 223 | }, 224 | "node_modules/node-gyp-build": { 225 | "version": "3.9.0", 226 | "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.9.0.tgz", 227 | "integrity": "sha512-zLcTg6P4AbcHPq465ZMFNXx7XpKKJh+7kkN699NiQWisR2uWYOWNWqRHAmbnmKiL4e9aLSlmy5U7rEMUXV59+A==", 228 | "bin": { 229 | "node-gyp-build": "bin.js", 230 | "node-gyp-build-optional": "optional.js", 231 | "node-gyp-build-test": "build-test.js" 232 | } 233 | }, 234 | "node_modules/once": { 235 | "version": "1.4.0", 236 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 237 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 238 | "dependencies": { 239 | "wrappy": "1" 240 | } 241 | }, 242 | "node_modules/p-limit": { 243 | "version": "3.1.0", 244 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 245 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 246 | "dependencies": { 247 | "yocto-queue": "^0.1.0" 248 | }, 249 | "engines": { 250 | "node": ">=10" 251 | }, 252 | "funding": { 253 | "url": "https://github.com/sponsors/sindresorhus" 254 | } 255 | }, 256 | "node_modules/path-is-absolute": { 257 | "version": "1.0.1", 258 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 259 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 260 | "engines": { 261 | "node": ">=0.10.0" 262 | } 263 | }, 264 | "node_modules/pify": { 265 | "version": "5.0.0", 266 | "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", 267 | "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==", 268 | "engines": { 269 | "node": ">=10" 270 | }, 271 | "funding": { 272 | "url": "https://github.com/sponsors/sindresorhus" 273 | } 274 | }, 275 | "node_modules/pprof-it": { 276 | "version": "1.0.0", 277 | "resolved": "https://registry.npmjs.org/pprof-it/-/pprof-it-1.0.0.tgz", 278 | "integrity": "sha512-omAhAs2Q6LUrFs16QyR8iNa9pEOTxoCOJs5Mqz+/3WdPZ3HOmEVOYo62WmhZCk+NK/4j2lA977CvZH5CEdYxCg==", 279 | "dependencies": { 280 | "@datadog/pprof": "^1.0.2", 281 | "signal-exit": "^3.0.7" 282 | }, 283 | "engines": { 284 | "node": ">=12.0.0" 285 | } 286 | }, 287 | "node_modules/protobufjs": { 288 | "version": "7.0.0", 289 | "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.0.0.tgz", 290 | "integrity": "sha512-ffNIEm+quOcYtQvHdW406v1NQmZSuqVklxsXk076BtuFnlYZfigLU+JOMrTD8TUOyqHYbRI/fSVNvgd25YeN3w==", 291 | "hasInstallScript": true, 292 | "dependencies": { 293 | "@protobufjs/aspromise": "^1.1.2", 294 | "@protobufjs/base64": "^1.1.2", 295 | "@protobufjs/codegen": "^2.0.4", 296 | "@protobufjs/eventemitter": "^1.1.0", 297 | "@protobufjs/fetch": "^1.1.0", 298 | "@protobufjs/float": "^1.0.2", 299 | "@protobufjs/inquire": "^1.1.0", 300 | "@protobufjs/path": "^1.1.2", 301 | "@protobufjs/pool": "^1.1.0", 302 | "@protobufjs/utf8": "^1.1.0", 303 | "@types/long": "^4.0.1", 304 | "@types/node": ">=13.7.0", 305 | "long": "^5.0.0" 306 | }, 307 | "engines": { 308 | "node": ">=12.0.0" 309 | } 310 | }, 311 | "node_modules/rimraf": { 312 | "version": "3.0.2", 313 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 314 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 315 | "dependencies": { 316 | "glob": "^7.1.3" 317 | }, 318 | "bin": { 319 | "rimraf": "bin.js" 320 | }, 321 | "funding": { 322 | "url": "https://github.com/sponsors/isaacs" 323 | } 324 | }, 325 | "node_modules/semver": { 326 | "version": "7.3.7", 327 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", 328 | "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", 329 | "dependencies": { 330 | "lru-cache": "^6.0.0" 331 | }, 332 | "bin": { 333 | "semver": "bin/semver.js" 334 | }, 335 | "engines": { 336 | "node": ">=10" 337 | } 338 | }, 339 | "node_modules/signal-exit": { 340 | "version": "3.0.7", 341 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", 342 | "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" 343 | }, 344 | "node_modules/source-map": { 345 | "version": "0.7.4", 346 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", 347 | "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", 348 | "engines": { 349 | "node": ">= 8" 350 | } 351 | }, 352 | "node_modules/split": { 353 | "version": "1.0.1", 354 | "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", 355 | "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", 356 | "dependencies": { 357 | "through": "2" 358 | }, 359 | "engines": { 360 | "node": "*" 361 | } 362 | }, 363 | "node_modules/through": { 364 | "version": "2.3.8", 365 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 366 | "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" 367 | }, 368 | "node_modules/typescript": { 369 | "version": "4.8.2", 370 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", 371 | "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==", 372 | "license": "Apache-2.0", 373 | "bin": { 374 | "tsc": "bin/tsc", 375 | "tsserver": "bin/tsserver" 376 | }, 377 | "engines": { 378 | "node": ">=4.2.0" 379 | } 380 | }, 381 | "node_modules/wrappy": { 382 | "version": "1.0.2", 383 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 384 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" 385 | }, 386 | "node_modules/yallist": { 387 | "version": "4.0.0", 388 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 389 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 390 | }, 391 | "node_modules/yocto-queue": { 392 | "version": "0.1.0", 393 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 394 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 395 | "engines": { 396 | "node": ">=10" 397 | }, 398 | "funding": { 399 | "url": "https://github.com/sponsors/sindresorhus" 400 | } 401 | } 402 | }, 403 | "dependencies": { 404 | "@datadog/pprof": { 405 | "version": "1.0.2", 406 | "resolved": "https://registry.npmjs.org/@datadog/pprof/-/pprof-1.0.2.tgz", 407 | "integrity": "sha512-AMTK55W3Aa2QX2X8mN9SQfDGw3HvwIK9Or8pXQFss+kjtH5pCkO9oqE5838MeXgRh9BR8HWrjAQE3Ji7FRCK2g==", 408 | "requires": { 409 | "delay": "^5.0.0", 410 | "findit2": "^2.2.3", 411 | "nan": "^2.16.0", 412 | "node-gyp-build": "^3.9.0", 413 | "p-limit": "^3.1.0", 414 | "pify": "^5.0.0", 415 | "protobufjs": "^7.0.0", 416 | "rimraf": "^3.0.2", 417 | "semver": "^7.3.5", 418 | "source-map": "^0.7.3", 419 | "split": "^1.0.1" 420 | } 421 | }, 422 | "@protobufjs/aspromise": { 423 | "version": "1.1.2", 424 | "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", 425 | "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" 426 | }, 427 | "@protobufjs/base64": { 428 | "version": "1.1.2", 429 | "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", 430 | "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" 431 | }, 432 | "@protobufjs/codegen": { 433 | "version": "2.0.4", 434 | "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", 435 | "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" 436 | }, 437 | "@protobufjs/eventemitter": { 438 | "version": "1.1.0", 439 | "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", 440 | "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" 441 | }, 442 | "@protobufjs/fetch": { 443 | "version": "1.1.0", 444 | "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", 445 | "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", 446 | "requires": { 447 | "@protobufjs/aspromise": "^1.1.1", 448 | "@protobufjs/inquire": "^1.1.0" 449 | } 450 | }, 451 | "@protobufjs/float": { 452 | "version": "1.0.2", 453 | "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", 454 | "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" 455 | }, 456 | "@protobufjs/inquire": { 457 | "version": "1.1.0", 458 | "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", 459 | "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" 460 | }, 461 | "@protobufjs/path": { 462 | "version": "1.1.2", 463 | "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", 464 | "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" 465 | }, 466 | "@protobufjs/pool": { 467 | "version": "1.1.0", 468 | "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", 469 | "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" 470 | }, 471 | "@protobufjs/utf8": { 472 | "version": "1.1.0", 473 | "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", 474 | "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" 475 | }, 476 | "@types/long": { 477 | "version": "4.0.2", 478 | "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", 479 | "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" 480 | }, 481 | "@types/node": { 482 | "version": "18.7.13", 483 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.13.tgz", 484 | "integrity": "sha512-46yIhxSe5xEaJZXWdIBP7GU4HDTG8/eo0qd9atdiL+lFpA03y8KS+lkTN834TWJj5767GbWv4n/P6efyTFt1Dw==" 485 | }, 486 | "@typescript/server-harness": { 487 | "version": "file:..", 488 | "requires": { 489 | "@types/node": "^14.18.12", 490 | "typescript": "^4.8.2" 491 | } 492 | }, 493 | "balanced-match": { 494 | "version": "1.0.2", 495 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 496 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 497 | }, 498 | "brace-expansion": { 499 | "version": "1.1.11", 500 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 501 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 502 | "requires": { 503 | "balanced-match": "^1.0.0", 504 | "concat-map": "0.0.1" 505 | } 506 | }, 507 | "concat-map": { 508 | "version": "0.0.1", 509 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 510 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" 511 | }, 512 | "delay": { 513 | "version": "5.0.0", 514 | "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", 515 | "integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==" 516 | }, 517 | "findit2": { 518 | "version": "2.2.3", 519 | "resolved": "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz", 520 | "integrity": "sha512-lg/Moejf4qXovVutL0Lz4IsaPoNYMuxt4PA0nGqFxnJ1CTTGGlEO2wKgoDpwknhvZ8k4Q2F+eesgkLbG2Mxfog==" 521 | }, 522 | "fs.realpath": { 523 | "version": "1.0.0", 524 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 525 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" 526 | }, 527 | "glob": { 528 | "version": "7.2.3", 529 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 530 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 531 | "requires": { 532 | "fs.realpath": "^1.0.0", 533 | "inflight": "^1.0.4", 534 | "inherits": "2", 535 | "minimatch": "^3.1.1", 536 | "once": "^1.3.0", 537 | "path-is-absolute": "^1.0.0" 538 | } 539 | }, 540 | "inflight": { 541 | "version": "1.0.6", 542 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 543 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 544 | "requires": { 545 | "once": "^1.3.0", 546 | "wrappy": "1" 547 | } 548 | }, 549 | "inherits": { 550 | "version": "2.0.4", 551 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 552 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 553 | }, 554 | "long": { 555 | "version": "5.2.0", 556 | "resolved": "https://registry.npmjs.org/long/-/long-5.2.0.tgz", 557 | "integrity": "sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w==" 558 | }, 559 | "lru-cache": { 560 | "version": "6.0.0", 561 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 562 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 563 | "requires": { 564 | "yallist": "^4.0.0" 565 | } 566 | }, 567 | "minimatch": { 568 | "version": "3.1.2", 569 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 570 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 571 | "requires": { 572 | "brace-expansion": "^1.1.7" 573 | } 574 | }, 575 | "nan": { 576 | "version": "2.16.0", 577 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz", 578 | "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==" 579 | }, 580 | "node-gyp-build": { 581 | "version": "3.9.0", 582 | "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.9.0.tgz", 583 | "integrity": "sha512-zLcTg6P4AbcHPq465ZMFNXx7XpKKJh+7kkN699NiQWisR2uWYOWNWqRHAmbnmKiL4e9aLSlmy5U7rEMUXV59+A==" 584 | }, 585 | "once": { 586 | "version": "1.4.0", 587 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 588 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 589 | "requires": { 590 | "wrappy": "1" 591 | } 592 | }, 593 | "p-limit": { 594 | "version": "3.1.0", 595 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 596 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 597 | "requires": { 598 | "yocto-queue": "^0.1.0" 599 | } 600 | }, 601 | "path-is-absolute": { 602 | "version": "1.0.1", 603 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 604 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" 605 | }, 606 | "pify": { 607 | "version": "5.0.0", 608 | "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", 609 | "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==" 610 | }, 611 | "pprof-it": { 612 | "version": "1.0.0", 613 | "resolved": "https://registry.npmjs.org/pprof-it/-/pprof-it-1.0.0.tgz", 614 | "integrity": "sha512-omAhAs2Q6LUrFs16QyR8iNa9pEOTxoCOJs5Mqz+/3WdPZ3HOmEVOYo62WmhZCk+NK/4j2lA977CvZH5CEdYxCg==", 615 | "requires": { 616 | "@datadog/pprof": "^1.0.2", 617 | "signal-exit": "^3.0.7" 618 | } 619 | }, 620 | "protobufjs": { 621 | "version": "7.0.0", 622 | "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.0.0.tgz", 623 | "integrity": "sha512-ffNIEm+quOcYtQvHdW406v1NQmZSuqVklxsXk076BtuFnlYZfigLU+JOMrTD8TUOyqHYbRI/fSVNvgd25YeN3w==", 624 | "requires": { 625 | "@protobufjs/aspromise": "^1.1.2", 626 | "@protobufjs/base64": "^1.1.2", 627 | "@protobufjs/codegen": "^2.0.4", 628 | "@protobufjs/eventemitter": "^1.1.0", 629 | "@protobufjs/fetch": "^1.1.0", 630 | "@protobufjs/float": "^1.0.2", 631 | "@protobufjs/inquire": "^1.1.0", 632 | "@protobufjs/path": "^1.1.2", 633 | "@protobufjs/pool": "^1.1.0", 634 | "@protobufjs/utf8": "^1.1.0", 635 | "@types/long": "^4.0.1", 636 | "@types/node": ">=13.7.0", 637 | "long": "^5.0.0" 638 | } 639 | }, 640 | "rimraf": { 641 | "version": "3.0.2", 642 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 643 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 644 | "requires": { 645 | "glob": "^7.1.3" 646 | } 647 | }, 648 | "semver": { 649 | "version": "7.3.7", 650 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", 651 | "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", 652 | "requires": { 653 | "lru-cache": "^6.0.0" 654 | } 655 | }, 656 | "signal-exit": { 657 | "version": "3.0.7", 658 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", 659 | "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" 660 | }, 661 | "source-map": { 662 | "version": "0.7.4", 663 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", 664 | "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==" 665 | }, 666 | "split": { 667 | "version": "1.0.1", 668 | "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", 669 | "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", 670 | "requires": { 671 | "through": "2" 672 | } 673 | }, 674 | "through": { 675 | "version": "2.3.8", 676 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 677 | "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" 678 | }, 679 | "typescript": { 680 | "version": "4.8.2", 681 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", 682 | "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==" 683 | }, 684 | "wrappy": { 685 | "version": "1.0.2", 686 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 687 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" 688 | }, 689 | "yallist": { 690 | "version": "4.0.0", 691 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 692 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 693 | }, 694 | "yocto-queue": { 695 | "version": "0.1.0", 696 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 697 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" 698 | } 699 | } 700 | } 701 | -------------------------------------------------------------------------------- /sample/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-server-driver", 3 | "author": "Microsoft Corp.", 4 | "version": "1.0.0", 5 | "description": "An example of how to consume @typescript/server-harness", 6 | "main": "index.js", 7 | "license": "MIT", 8 | "dependencies": { 9 | "@typescript/server-harness": "file:..", 10 | "pprof-it": "^1.0.0", 11 | "typescript": "^4.8.2" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /sample/profiles/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /sample/traces/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import cp = require("child_process"); 2 | import os = require("os"); 3 | 4 | type EventListener = (obj: any) => void; 5 | type ErrorListener = (err: any) => void; 6 | type ExitListener = (code: number | null) => void; 7 | type CloseListener = (code: number | null, signal: NodeJS.Signals | null) => void; 8 | 9 | export interface Server { 10 | /** Returns the response (or event) with the matching `request_seq`. */ 11 | message(request: any): Promise; 12 | /** 13 | * Sends an exit message to the server and forcibly kills it if it hasn't exited after `timeoutMs`. 14 | * Throws if the request fails (e.g. if the server has already exited). 15 | * Throws if the server doesn't exit before `timeoutMs` and it cannot be killed. 16 | * Returns true if the server exited before `timeoutMs` and false if it was killed. 17 | */ 18 | exitOrKill(timoutMs: number): Promise; 19 | /** Kills the server, regardless of its current state. */ 20 | kill(): Promise; 21 | /** Fires when an event is received from the server. */ 22 | on(event: "event", listener: EventListener): void; 23 | /** Fires when an internal error occurs in the harness (not for language service errors). */ 24 | on(event: "communicationError", listener: ErrorListener): void; 25 | /** Fires when the server exits. */ 26 | on(event: "exit", listener: ExitListener): void; 27 | /** Fires when the server closes (exits + closes IO streams). */ 28 | on(event: "close", listener: CloseListener): void; 29 | /** The PID of the subprocess. */ 30 | pid?: number; 31 | } 32 | 33 | /** 34 | * Forks a new server process. By default, the server will not have ATA or produce diagnostic events. 35 | */ 36 | export function launchServer(tsserverPath: string, args?: string[], execArgv?: string[], env?: NodeJS.ProcessEnv): Server { 37 | const eventListeners: EventListener[] = []; 38 | const errorListeners: ErrorListener[] = []; 39 | const exitListeners: ExitListener[] = []; 40 | const closeListeners: CloseListener[] = []; 41 | 42 | const serverProc = cp.fork( 43 | tsserverPath, 44 | args ?? ["--disableAutomaticTypingAcquisition"], 45 | { 46 | execArgv: execArgv ?? process.execArgv?.map(arg => bumpDebugPort(arg)), 47 | env, 48 | stdio: ["pipe", "pipe", "ignore", "ipc"] 49 | }); 50 | 51 | const useNodeIpc = !!args && !!args.filter(a => a.toLocaleLowerCase() === "--useNodeIpc".toLocaleLowerCase()).length; 52 | 53 | const getNext = makeListeners(serverProc, useNodeIpc, eventListeners, errorListeners); 54 | 55 | serverProc.on("exit", code => { 56 | for (const listener of exitListeners) { 57 | listener(code); 58 | } 59 | }); 60 | 61 | serverProc.on("close", (code, signal) => { 62 | for (const listener of closeListeners) { 63 | listener(code, signal); 64 | } 65 | }); 66 | 67 | function on(event: "event", listener: EventListener): void; 68 | function on(event: "communicationError", listener: ErrorListener): void; 69 | function on(event: "exit", listener: ExitListener): void; 70 | function on(event: "close", listener: CloseListener): void; 71 | function on(event: "event" | "communicationError" | "exit" | "close", listener: EventListener | ErrorListener | ExitListener | CloseListener): void { 72 | switch (event) { 73 | case "event": 74 | eventListeners.push(listener as EventListener); 75 | break; 76 | case "communicationError": 77 | errorListeners.push(listener as ErrorListener); 78 | break; 79 | case "exit": 80 | exitListeners.push(listener as ExitListener); 81 | break; 82 | case "close": 83 | closeListeners.push(listener as CloseListener); 84 | break; 85 | } 86 | } 87 | 88 | return { 89 | message: request => message(serverProc, useNodeIpc, getNext, request), 90 | exitOrKill: timeoutMs => exitOrKill(serverProc, useNodeIpc, timeoutMs), 91 | kill: () => kill(serverProc), 92 | on, 93 | pid: serverProc.pid, 94 | }; 95 | } 96 | 97 | function bumpDebugPort(arg: string): string { 98 | const match = /^(--inspect(?:-brk)?)(?:=(\d+))?$/.exec(arg); 99 | return match 100 | ? `${match[1]}=${match[2] ? (+match[2] + 1) : 9230}` 101 | : arg; 102 | } 103 | 104 | function makeListeners(serverProc: cp.ChildProcess, useNodeIpc: boolean, eventListeners: readonly EventListener[], errorListeners: readonly ErrorListener[]): (seq: number) => Promise { 105 | const waiters = new Map void)>(); 106 | const objects = new Map(); 107 | 108 | // Once we get out of sync with the incoming stream, we can't recover. 109 | // Ignore further data, rather than producing more errors. 110 | let hadCommunicationError = false; 111 | 112 | if (useNodeIpc) { 113 | serverProc.on('message', obj => { 114 | if (hadCommunicationError) return; 115 | 116 | try { 117 | handleMessage(obj); 118 | } catch (e) { 119 | hadCommunicationError = true; 120 | 121 | for (const listener of errorListeners) { 122 | listener(e); 123 | } 124 | } 125 | }); 126 | } 127 | else { 128 | let unconsumedChunks: Buffer[] = []; 129 | let unconsumedByteLength = 0; 130 | let headerByteLength = -1; 131 | let currentByteLength = -1; 132 | serverProc.stdout!.on('data', buffer => { 133 | if (hadCommunicationError) return; 134 | 135 | try { 136 | unconsumedChunks.push(buffer); 137 | unconsumedByteLength += buffer.byteLength; 138 | 139 | while (true) { 140 | if (headerByteLength < 0) { 141 | // This could be done directly in the buffer, but strings are much simpler 142 | const text = Buffer.concat(unconsumedChunks, unconsumedByteLength).toString("utf8"); 143 | const headerMatch = text.match(/Content-Length: (\d+)/); // Receiving a chunk shorter than this is very unlikely 144 | if (!headerMatch) break; 145 | headerByteLength = text.indexOf("{", headerMatch.index! + headerMatch[0].length); // All single-byte characters 146 | if (headerByteLength < 0) return; // Don't have the body yet 147 | const bodyByteLength = +headerMatch[1]; 148 | currentByteLength = headerByteLength + bodyByteLength + (os.EOL.length - 1); // tsserver assumes the final newline has length one on every OS 149 | } 150 | 151 | if (unconsumedByteLength < currentByteLength) return; 152 | 153 | const combined = Buffer.concat(unconsumedChunks, unconsumedByteLength); 154 | const jsonText = combined.toString("utf8", headerByteLength, currentByteLength); 155 | const obj = JSON.parse(jsonText); 156 | unconsumedByteLength -= currentByteLength; 157 | unconsumedChunks = unconsumedByteLength > 0 ? [combined.subarray(currentByteLength)] : []; 158 | headerByteLength = -1; 159 | currentByteLength = -1; 160 | 161 | handleMessage(obj); 162 | } 163 | } catch (e) { 164 | hadCommunicationError = true; 165 | 166 | for (const listener of errorListeners) { 167 | listener(e); 168 | } 169 | } 170 | }); 171 | } 172 | 173 | function handleMessage(obj: any): void { 174 | if (obj.type === "event" && obj.event !== "requestCompleted") { 175 | for (const listener of eventListeners) { 176 | listener(obj); 177 | } 178 | return; 179 | } 180 | 181 | const requestSeq = obj.type === "event" 182 | ? obj.body.request_seq 183 | : obj.request_seq 184 | 185 | const w = waiters.get(requestSeq); 186 | if (w) { 187 | waiters.delete(requestSeq); 188 | w(obj); 189 | } 190 | else { 191 | objects.set(requestSeq, obj); 192 | } 193 | } 194 | 195 | const getResponse = (seq: number) => new Promise(resolve => { 196 | const obj = objects.get(seq); 197 | if (obj) { 198 | objects.delete(seq); 199 | resolve(obj); 200 | } 201 | else { 202 | waiters.set(seq, resolve); 203 | } 204 | }); 205 | 206 | return getResponse; 207 | } 208 | 209 | async function message(serverProc: cp.ChildProcess, useNodeIpc: boolean, getResponse: (seq: number) => Promise, request: any) { 210 | // TODO: It would be more robust to handle write/send failures 211 | if (!serverProc.connected || serverProc.killed || serverProc.exitCode !== null || serverProc.signalCode !== null) { 212 | throw new Error("Server has exited"); 213 | } 214 | 215 | const seq: number = request.seq; 216 | if (useNodeIpc) { 217 | serverProc.send(request); 218 | } else { 219 | serverProc.stdin!.write(JSON.stringify(request) + "\n"); 220 | } 221 | 222 | // Several commands, such as `configure`, are flagged as not requiring a response. In practice, however, 223 | // they do return trivial responses. The only command that definitely won't return a response is `exit`. 224 | // If it eventually becomes necessary to handle to handle another non-responding command, we'll probably 225 | // need to make message take an optional parameter to that effect. 226 | if (request.command === "exit") { 227 | return Promise.resolve(undefined); 228 | } 229 | 230 | return await getResponse(seq); 231 | } 232 | 233 | async function exitOrKill(serverProc: cp.ChildProcess, useNodeIpc: boolean, timeoutMs: number): Promise { 234 | return new Promise((resolve, reject) => { 235 | let timedOut = false; 236 | 237 | serverProc.once("close", () => { 238 | if (!timedOut) { 239 | clearTimeout(timeout); 240 | resolve(true); 241 | } 242 | }); 243 | 244 | const timeout = setTimeout(async () => { 245 | timedOut = true; 246 | await kill(serverProc); 247 | resolve(false); 248 | }, timeoutMs); 249 | 250 | // No response, so nothing to await 251 | message(serverProc, useNodeIpc, /*getResponse*/ undefined as never, { "command": "exit" }).catch(err => reject(err));; 252 | }); 253 | } 254 | 255 | function kill(serverProc: cp.ChildProcess) { 256 | return new Promise((resolve, reject) => { 257 | serverProc.once("close", () => { 258 | resolve(); 259 | }); 260 | 261 | // If the server has already exited, there won't be a close event 262 | if (serverProc.exitCode !== null || serverProc.signalCode !== null) { 263 | resolve(); 264 | } 265 | 266 | if (!serverProc.kill("SIGKILL")) { 267 | reject(new Error("Failed to send kill signal to server")); 268 | } 269 | }); 270 | } 271 | -------------------------------------------------------------------------------- /src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "../dist", 4 | "target": "ES2017", 5 | "module": "commonjs", 6 | "sourceMap": true, 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "declaration": true, 10 | "noImplicitAny": false, 11 | "composite": true, 12 | }, 13 | "files": [ 14 | "index.ts" 15 | ] 16 | } --------------------------------------------------------------------------------