starknet-devnet
Version:
Starknet Devnet provider
133 lines (132 loc) • 5.68 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.VersionHandler = void 0;
const axios_1 = __importDefault(require("axios"));
const path_1 = __importDefault(require("path"));
const fs_1 = __importDefault(require("fs"));
const os_1 = __importDefault(require("os"));
const constants_1 = require("./constants");
const types_1 = require("./types");
const decompress_1 = __importDefault(require("decompress"));
// eslint-disable-next-line @typescript-eslint/no-var-requires
const decompressTargz = require("decompress-targz");
class VersionHandler {
static getVersionDir(version) {
return path_1.default.join(this.localStoragePath, version);
}
static getExecutablePath(versionDir) {
return path_1.default.join(versionDir, "starknet-devnet");
}
/**
* Ensure that the command corresponding to the provided `version` exists.
* @param version semver string with a prepended "v";
* should be available in https://github.com/0xSpaceShard/starknet-devnet/releases
* @returns the path to the executable corresponding to the version
*/
static async getExecutable(version) {
const versionDir = this.getVersionDir(version);
const executable = this.getExecutablePath(versionDir);
if (fs_1.default.existsSync(executable)) {
return executable;
}
const executableUrl = await this.getArchivedExecutableUrl(version);
const archivePath = await this.fetchArchivedExecutable(executableUrl, versionDir);
await this.extract(archivePath, versionDir);
return executable;
}
static getCompatibleArch() {
switch (process.arch) {
case "arm64":
return "aarch64";
case "x64":
return "x86_64";
default:
throw new types_1.DevnetError(`Incompatible architecture: ${process.platform}`);
}
}
static getCompatiblePlatform() {
switch (process.platform) {
case "linux":
return "unknown-linux-gnu";
case "darwin":
return "apple-darwin";
default:
throw new types_1.DevnetError(`Incompatible platform: ${process.platform}`);
}
}
static async getArchivedExecutableUrl(version) {
const releasesUrl = "https://api.github.com/repos/0xSpaceShard/starknet-devnet/releases";
const releasesResp = await this.httpProvider.get(releasesUrl);
if (releasesResp.status !== axios_1.default.HttpStatusCode.Ok) {
throw new types_1.GithubError(releasesResp.statusText);
}
if (!Array.isArray(releasesResp.data)) {
throw new types_1.GithubError(`Invalid response: ${JSON.stringify(releasesResp.data)}`);
}
let versionPresent = false;
for (const release of releasesResp.data) {
if (release.name === version) {
versionPresent = true;
break;
}
}
if (!versionPresent) {
throw new types_1.GithubError(`Version not found. If specifying an exact version, make sure you prepended the 'v' and that the version really exists in ${releasesUrl}.`);
}
const arch = this.getCompatibleArch();
const platform = this.getCompatiblePlatform();
return `https://github.com/0xSpaceShard/starknet-devnet/releases/download/${version}/starknet-devnet-${arch}-${platform}.tar.gz`;
}
/**
* @param url the url of the archived executable
* @param versionDir the path to the directory where the archive will be written and extracted
* @returns the path where the archive was stored
*/
static async fetchArchivedExecutable(url, versionDir) {
const resp = await this.httpProvider.get(url, { responseType: "stream" });
if (resp.status === axios_1.default.HttpStatusCode.NotFound) {
throw new types_1.GithubError(`Not found: ${url}`);
}
else if (resp.status !== axios_1.default.HttpStatusCode.Ok) {
throw new types_1.GithubError(resp.statusText);
}
if (!fs_1.default.existsSync(versionDir)) {
fs_1.default.mkdirSync(versionDir, { recursive: true });
}
const archivedExecutablePath = path_1.default.join(versionDir, "archive.tar.gz");
const writer = fs_1.default.createWriteStream(archivedExecutablePath);
return new Promise((resolve, reject) => {
resp.data.pipe(writer);
let error;
writer.on("error", (e) => {
error = e;
writer.close();
reject(error);
});
writer.on("close", () => {
if (!error) {
resolve(archivedExecutablePath);
}
// no need to call `reject` here, as it will have been called in the
// "error" stream;
});
});
}
/**
* Extract the content of the archive.
* @param archivePath the local path of the archive
*/
static async extract(archivePath, targetDir) {
await (0, decompress_1.default)(archivePath, targetDir, {
plugins: [decompressTargz()],
});
}
}
exports.VersionHandler = VersionHandler;
VersionHandler.httpProvider = axios_1.default.create({
timeout: constants_1.DEFAULT_HTTP_TIMEOUT,
});
VersionHandler.localStoragePath = path_1.default.join(os_1.default.tmpdir(), "devnet-versions");