UNPKG

near-workspaces

Version:

Write tests in TypeScript/JavaScript to run in a controlled NEAR Sandbox local environment.

189 lines 7.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SandboxServer = void 0; const buffer_1 = require("buffer"); const process_1 = __importDefault(require("process")); const promises_1 = require("fs/promises"); const path_1 = require("path"); const http = __importStar(require("http")); const temp_dir_1 = __importDefault(require("temp-dir")); const portCheck = __importStar(require("node-port-check")); const pure_uuid_1 = __importDefault(require("pure-uuid")); const internal_utils_1 = require("../internal-utils"); const pollData = JSON.stringify({ jsonrpc: '2.0', id: 'dontcare', method: 'block', params: { finality: 'final' }, }); async function pingServer(port) { const options = { hostname: '0.0.0.0', port, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': buffer_1.Buffer.byteLength(pollData), }, }; return new Promise(resolve => { const request = http.request(options, result => { if (result.statusCode === 200) { resolve(true); } else { (0, internal_utils_1.debug)(`Sandbox running but got non-200 response: ${JSON.stringify(result)}`); resolve(false); } }); request.on('error', _ => { resolve(false); }); // Write data to request body request.write(pollData); request.end(); }); } async function sandboxStarted(port, timeout = 60_000) { const checkUntil = Date.now() + timeout + 250; do { if (await pingServer(port)) { // eslint-disable-line no-await-in-loop return; } await new Promise(resolve => { setTimeout(() => resolve(true), 250); // eslint-disable-line @typescript-eslint/no-confusing-void-expression }); } while (Date.now() < checkUntil); throw new Error(`Sandbox Server with port: ${port} failed to start after ${timeout}ms`); } // 5001-60000, increase the range of initialPort to decrease the possibility of port conflict function initialPort() { return Math.max(5001, Math.floor(Math.random() * 60_000)); } class SandboxServer { static async nextPort() { this.lastPort = await portCheck.nextAvailable(this.lastPort + Math.max(1, Math.floor(Math.random() * 4)), '0.0.0.0'); return this.lastPort; } static lockfilePath(filename) { return (0, path_1.join)(temp_dir_1.default, filename); } static randomHomeDir() { return (0, path_1.join)(temp_dir_1.default, 'sandbox', (new pure_uuid_1.default(4).toString())); } static async init(config) { (0, internal_utils_1.debug)('Lifecycle.SandboxServer.init()', 'config:', config); this.binPath = await (0, internal_utils_1.ensureBinary)(); const server = new SandboxServer(config); if (server.config.refDir) { await (0, internal_utils_1.rm)(server.homeDir); await (0, internal_utils_1.copyDirection)(server.config.refDir, server.config.homeDir); } if ((await (0, internal_utils_1.exists)(server.homeDir))) { await (0, internal_utils_1.rm)(server.homeDir); } const { stderr, code } = await server.spawn('init'); if (code && code < 0) { (0, internal_utils_1.debug)(stderr); throw new Error('Failed to spawn sandbox server'); } return server; } static lastPort = initialPort(); static binPath; subprocess; readyToDie = false; config; constructor(config) { (0, internal_utils_1.debug)('Lifecycle.SandboxServer.constructor', 'config:', config); this.config = config; } get homeDir() { return this.config.homeDir; } get port() { return this.config.port; } get rpcAddr() { return `http://127.0.0.1:${this.port}`; } async start() { (0, internal_utils_1.debug)('Lifecycle.SandboxServer.start()'); const args = [ '--home', this.homeDir, 'run', '--rpc-addr', `0.0.0.0:${this.port}`, '--network-addr', `0.0.0.0:${await SandboxServer.nextPort()}`, ]; if (process_1.default.env.NEAR_WORKSPACES_DEBUG) { const filePath = (0, path_1.join)(this.homeDir, 'sandboxServer.log'); (0, internal_utils_1.debug)(`near-sandbox logs writing to file: ${filePath}`); const fd = await (0, promises_1.open)(filePath, 'a'); this.subprocess = (0, internal_utils_1.spawn)(SandboxServer.binPath, args, { env: { RUST_BACKTRACE: 'full' }, // @ts-expect-error FileHandle not assignable to Stream | IOType stdio: ['ignore', 'ignore', fd], }); this.subprocess.on('exit', async () => { await fd.close(); }); } else { this.subprocess = (0, internal_utils_1.spawn)(SandboxServer.binPath, args, { stdio: ['ignore', 'ignore', 'ignore'], }); } this.subprocess.on('exit', () => { if (!this.readyToDie) { (0, internal_utils_1.debug)(`Server with port ${this.port}: died horribly`); } }); await sandboxStarted(this.port); return this; } async close() { (0, internal_utils_1.debug)('Lifecycle.SandboxServer.close()'); this.readyToDie = true; if (!this.subprocess.kill('SIGINT')) { console.error(`Failed to kill child process with PID: ${this.subprocess.pid ?? 'undefined'}`); } if (this.config.rm) { await (0, internal_utils_1.rm)(this.homeDir); } } async spawn(command) { (0, internal_utils_1.debug)('Lifecycle.SandboxServer.spawn()'); return (0, internal_utils_1.asyncSpawn)(SandboxServer.binPath, '--home', this.homeDir, command); } } exports.SandboxServer = SandboxServer; //# sourceMappingURL=server.js.map