diff --git a/.eslintrc.yml b/.eslintrc.yml index 1ad66b0b..684dd8fa 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -5,7 +5,7 @@ parserOptions: jsx: true ecmaVersion: 2020 sourceType: module - extraFileExtensions: ['.cjs'] + extraFileExtensions: ['.cjs', '.mjs'] project: ./tsconfig.json env: @@ -222,6 +222,13 @@ overrides: parserOptions: project: ./bench/tsconfig.json + - files: + - ./memory-testing/*.{js,mjs} + plugins: + - '@typescript-eslint' + parserOptions: + project: ./tsconfig.root-lint.json + - files: - ./*.js plugins: diff --git a/.github/workflows/memory-test.yml b/.github/workflows/memory-test.yml new file mode 100644 index 00000000..192ea021 --- /dev/null +++ b/.github/workflows/memory-test.yml @@ -0,0 +1,82 @@ +name: Memory Leak Detect + +env: + DEBUG: 'napi:*' + +on: + push: + branches: + - main + pull_request: + +jobs: + build_and_test: + name: Memory leak detect job + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Setup node + uses: actions/setup-node@v2 + with: + node-version: 14 + check-latest: true + + - name: Install stable + uses: actions-rs/toolchain@v1 + with: + toolchain: stable-x86_64-unknown-linux-gnu + profile: minimal + override: true + + - name: Generate Cargo.lock + uses: actions-rs/cargo@v1 + with: + command: generate-lockfile + + - name: Cache cargo registry + uses: actions/cache@v1 + with: + path: ~/.cargo/registry + key: stable-memory-leak-detect-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }} + + - name: Cache cargo index + uses: actions/cache@v1 + with: + path: ~/.cargo/git + key: stable-memory-leak-detect-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }} + + - name: Cache cargo build + uses: actions/cache@v1 + with: + path: target + key: stable-memory-leak-detect-cargo-build-trimmed-${{ hashFiles('**/Cargo.lock') }} + + - name: Cache NPM dependencies + uses: actions/cache@v1 + with: + path: node_modules + key: memory-leak-detect-${{ hashFiles('yarn.lock') }} + + - name: 'Install dependencies' + run: yarn install --frozen-lockfile --registry https://registry.npmjs.org --network-timeout 300000 + + - name: 'Build TypeScript' + run: yarn build + + - name: 'Pull docker image' + run: docker pull node:lts-slim + + - name: 'Build memory test specs' + run: yarn build:memory + + - name: Memory leak tests + run: yarn test:memory + env: + RUST_BACKTRACE: 1 + + - name: Clear the cargo caches + run: | + cargo install cargo-cache --no-default-features --features ci-autoclean + cargo-cache diff --git a/Cargo.toml b/Cargo.toml index e25cb0d8..c135e1f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,11 @@ [workspace] -exclude = ["./test_module", "./bench", "./testing"] members = [ "./build", "./napi", "./napi-derive", "./napi-derive-example", "./sys", + "./test_module", + "./bench", + "./memory-testing", ] diff --git a/bench/Cargo.toml b/bench/Cargo.toml index f1d0cf5f..495b6c8e 100644 --- a/bench/Cargo.toml +++ b/bench/Cargo.toml @@ -21,7 +21,3 @@ mimalloc = {version = "0.1"} [build-dependencies] napi-build = {path = "../build"} - -[profile.release] -lto = true -opt-level = 3 diff --git a/memory-testing/Cargo.toml b/memory-testing/Cargo.toml new file mode 100644 index 00000000..35163ae7 --- /dev/null +++ b/memory-testing/Cargo.toml @@ -0,0 +1,21 @@ +[package] +authors = ["LongYinan "] +edition = "2018" +name = "memory-testing" +version = "0.1.0" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +futures = "0.3" +napi = {path = "../napi", features = ["tokio_rt", "serde-json", "latin1"]} +napi-derive = {path = "../napi-derive"} +serde = "1" +serde_bytes = "0.11" +serde_derive = "1" +serde_json = "1" +tokio = {version = "1", features = ["default", "fs"]} + +[build-dependencies] +napi-build = {path = "../build"} diff --git a/memory-testing/build.rs b/memory-testing/build.rs new file mode 100644 index 00000000..1f866b6a --- /dev/null +++ b/memory-testing/build.rs @@ -0,0 +1,5 @@ +extern crate napi_build; + +fn main() { + napi_build::setup(); +} diff --git a/memory-testing/index.mjs b/memory-testing/index.mjs new file mode 100644 index 00000000..8d5957dd --- /dev/null +++ b/memory-testing/index.mjs @@ -0,0 +1,6 @@ +import { createSuite } from './test-util.mjs' + +await createSuite('tokio-future') +await createSuite('serde') + +process.exit(0) diff --git a/memory-testing/package.json b/memory-testing/package.json new file mode 100644 index 00000000..72dcb792 --- /dev/null +++ b/memory-testing/package.json @@ -0,0 +1,16 @@ +{ + "name": "memory-testing", + "version": "1.0.0", + "scripts": { + "build": "node ../cli/scripts/index.js build --release" + }, + "dependencies": { + "chalk": "^4.1.1", + "dockerode": "^3.3.0", + "pretty-bytes": "^5.6.0", + "table": "^6.7.1" + }, + "devDependencies": { + "@types/dockerode": "^3.2.3" + } +} diff --git a/memory-testing/serde.js b/memory-testing/serde.js new file mode 100644 index 00000000..6325a66a --- /dev/null +++ b/memory-testing/serde.js @@ -0,0 +1,35 @@ +const displayMemoryUsageFromNode = require('./util') + +const initialMemoryUsage = process.memoryUsage() + +const api = require(`./index.node`) + +const data = { + id: 'ckovh15xa104945sj64rdk8oas', + name: '1883da9ff9152', + forename: '221c99bedc6a4', + description: '8bf86b62ce6a', + email: '9d57a869661cc', + phone: '7e0c58d147215', + arrivalDate: -92229669, + departureDate: 202138795, + price: -1592700387, + advance: -369294193, + advanceDueDate: 925000428, + kids: 520124290, + adults: 1160258464, + status: 'NO_PAYMENT', + nourishment: 'BB', + createdAt: '2021-05-19T12:58:37.246Z', + room: { id: 'ckovh15xa104955sj6r2tqaw1c', name: '38683b87f2664' }, +} + +let i = 1 +// eslint-disable-next-line no-constant-condition +while (true) { + api.convertFromJS(data) + if (i % 100000 === 0) { + displayMemoryUsageFromNode(initialMemoryUsage) + } + i++ +} diff --git a/memory-testing/src/lib.rs b/memory-testing/src/lib.rs new file mode 100644 index 00000000..a16fdb21 --- /dev/null +++ b/memory-testing/src/lib.rs @@ -0,0 +1,82 @@ +#[macro_use] +extern crate napi_derive; +#[macro_use] +extern crate serde_derive; + +#[derive(Serialize, Deserialize)] +pub struct Welcome { + id: String, + name: String, + forename: String, + description: String, + email: String, + phone: String, + #[serde(rename = "arrivalDate")] + arrival_date: i64, + #[serde(rename = "departureDate")] + departure_date: i64, + price: i64, + advance: i64, + #[serde(rename = "advanceDueDate")] + advance_due_date: i64, + kids: i64, + adults: i64, + status: String, + nourishment: String, + #[serde(rename = "createdAt")] + created_at: String, + room: Room, +} + +#[derive(Serialize, Deserialize)] +pub struct Room { + id: String, + name: String, +} + +#[js_function] +fn test_async(ctx: napi::CallContext) -> napi::Result { + let data = serde_json::json!({ + "findFirstBooking": { + "id": "ckovh15xa104945sj64rdk8oas", + "name": "1883da9ff9152", + "forename": "221c99bedc6a4", + "description": "8bf86b62ce6a", + "email": "9d57a869661cc", + "phone": "7e0c58d147215", + "arrivalDate": -92229669, + "departureDate": 202138795, + "price": -1592700387, + "advance": -369294193, + "advanceDueDate": 925000428, + "kids": 520124290, + "adults": 1160258464, + "status": "NO_PAYMENT", + "nourishment": "BB", + "createdAt": "2021-05-19T12:58:37.246Z", + "room": { "id": "ckovh15xa104955sj6r2tqaw1c", "name": "38683b87f2664" } + } + }); + + ctx.env.execute_tokio_future( + async move { Ok(serde_json::to_string(&data).unwrap()) }, + |env, response| { + env.adjust_external_memory(response.len() as i64)?; + env.create_string_from_std(response) + }, + ) +} + +#[js_function(1)] +fn from_js(ctx: napi::CallContext) -> napi::Result { + let input_object = ctx.get::(0)?; + let a: Welcome = ctx.env.from_js_value(&input_object)?; + ctx.env.create_string_from_std(serde_json::to_string(&a)?) +} + +#[module_exports] +fn init(mut exports: napi::JsObject) -> napi::Result<()> { + exports.create_named_method("testAsync", test_async)?; + exports.create_named_method("convertFromJS", from_js)?; + Ok(()) +} diff --git a/memory-testing/test-util.mjs b/memory-testing/test-util.mjs new file mode 100644 index 00000000..6133b8fe --- /dev/null +++ b/memory-testing/test-util.mjs @@ -0,0 +1,79 @@ +import { setTimeout } from 'timers' +import { promisify } from 'util' + +import chalk from 'chalk' +import Dockerode from 'dockerode' +import prettyBytes from 'pretty-bytes' + +const sleep = promisify(setTimeout) + +const client = new Dockerode() + +export async function createSuite(testFile, maxMemoryUsage) { + console.info(chalk.cyanBright('Create container')) + + const container = await client.createContainer({ + Image: 'node:lts-slim', + Cmd: ['/bin/bash', '-c', `node memory-testing/${testFile}.js`], + AttachStdout: true, + AttachStderr: true, + Tty: true, + WorkingDir: '/napi-rs', + Env: ['MAX_OLD_SPACE_SIZE=256', 'FORCE_COLOR=1'], + HostConfig: { + Binds: [`${process.cwd()}:/napi-rs:rw`], + Memory: 256 * 1024 * 1024, + }, + }) + + console.info(chalk.cyanBright('Container created, starting ...')) + + await container.start() + + container.attach( + { stream: true, stdout: true, stderr: true }, + function (err, stream) { + if (err) { + console.error(err) + process.exit(1) + } + stream.pipe(process.stdout) + }, + ) + + const stats = await container.stats() + + let shouldAssetMemoryUsage = false + + const initialMemoryUsage = await new Promise((resolve, reject) => { + stats.on('data', (d) => { + const { memory_stats } = JSON.parse(d.toString('utf8')) + resolve(memory_stats.usage) + if (shouldAssetMemoryUsage && memory_stats?.usage) { + const memoryGrowth = memory_stats.usage - initialMemoryUsage + if (memoryGrowth > maxMemoryUsage ?? initialMemoryUsage) { + console.info( + chalk.redBright( + `Potential memory leak, memory growth: ${prettyBytes( + memoryGrowth, + )}`, + ), + ) + process.exit(1) + } + } + }) + stats.on('error', reject) + }) + + console.info( + chalk.red(`Initial memory usage: ${prettyBytes(initialMemoryUsage)}`), + ) + + await sleep(60000) + + shouldAssetMemoryUsage = true + + await container.stop() + await container.remove() +} diff --git a/memory-testing/tokio-future.js b/memory-testing/tokio-future.js new file mode 100644 index 00000000..2d125afd --- /dev/null +++ b/memory-testing/tokio-future.js @@ -0,0 +1,22 @@ +const displayMemoryUsageFromNode = require('./util') + +const initialMemoryUsage = process.memoryUsage() + +const api = require(`./index.node`) + +async function main() { + let i = 1 + // eslint-disable-next-line no-constant-condition + while (true) { + await api.testAsync() + if (i % 100000 === 0) { + displayMemoryUsageFromNode(initialMemoryUsage) + } + i++ + } +} + +main().catch((e) => { + console.error(e) + process.exit(1) +}) diff --git a/memory-testing/util.js b/memory-testing/util.js new file mode 100644 index 00000000..cf782f2d --- /dev/null +++ b/memory-testing/util.js @@ -0,0 +1,25 @@ +const chalk = require('chalk') +const prettyBytes = require('pretty-bytes') +const { table } = require('table') + +module.exports = function displayMemoryUsageFromNode(initialMemoryUsage) { + const finalMemoryUsage = process.memoryUsage() + const titles = Object.keys(initialMemoryUsage).map((k) => + chalk.whiteBright(k), + ) + const tableData = [titles] + const diffColumn = [] + for (const [key, value] of Object.entries(initialMemoryUsage)) { + const diff = finalMemoryUsage[key] - value + const prettyDiff = prettyBytes(diff, { signed: true }) + if (diff > 0) { + diffColumn.push(chalk.red(prettyDiff)) + } else if (diff < 0) { + diffColumn.push(chalk.green(prettyDiff)) + } else { + diffColumn.push(chalk.grey(prettyDiff)) + } + } + tableData.push(diffColumn) + console.info(table(tableData)) +} diff --git a/napi/src/env.rs b/napi/src/env.rs index 937b1260..319ea073 100644 --- a/napi/src/env.rs +++ b/napi/src/env.rs @@ -1109,11 +1109,11 @@ impl Env { .map_err(|e| match e { TrySendError::Full(_) => Error::new( Status::QueueFull, - format!("Failed to run future: no available capacity"), + "Failed to run future: no available capacity".to_owned(), ), TrySendError::Closed(_) => Error::new( Status::Closing, - format!("Failed to run future: receiver closed"), + "Failed to run future: receiver closed".to_string(), ), })?; Ok(unsafe { JsObject::from_raw_unchecked(self.0, raw_promise) }) diff --git a/napi/src/js_values/de.rs b/napi/src/js_values/de.rs index 64b07827..96c080bd 100644 --- a/napi/src/js_values/de.rs +++ b/napi/src/js_values/de.rs @@ -30,7 +30,7 @@ impl<'x, 'de, 'env> serde::de::Deserializer<'x> for &'de mut De<'env> { ValueType::Number => { let js_number: f64 = unsafe { JsNumber::from_raw_unchecked(self.0.env, self.0.value).try_into()? }; - if js_number.trunc() == js_number { + if (js_number.trunc() - js_number).abs() < f64::EPSILON { visitor.visit_i64(js_number as i64) } else { visitor.visit_f64(js_number) diff --git a/napi/src/js_values/ser.rs b/napi/src/js_values/ser.rs index 41afe393..762d3be9 100644 --- a/napi/src/js_values/ser.rs +++ b/napi/src/js_values/ser.rs @@ -338,7 +338,7 @@ impl ser::SerializeSeq for SeqSerializer { self.current_index as _, JsUnknown(value.serialize(Ser::new(&env))?), )?; - self.current_index = self.current_index + 1; + self.current_index += 1; Ok(()) } @@ -362,7 +362,7 @@ impl ser::SerializeTuple for SeqSerializer { self.current_index as _, JsUnknown(value.serialize(Ser::new(&env))?), )?; - self.current_index = self.current_index + 1; + self.current_index += 1; Ok(()) } @@ -387,7 +387,7 @@ impl ser::SerializeTupleStruct for SeqSerializer { self.current_index as _, JsUnknown(value.serialize(Ser::new(&env))?), )?; - self.current_index = self.current_index + 1; + self.current_index += 1; Ok(()) } @@ -412,7 +412,7 @@ impl ser::SerializeTupleVariant for SeqSerializer { self.current_index as _, JsUnknown(value.serialize(Ser::new(&env))?), )?; - self.current_index = self.current_index + 1; + self.current_index += 1; Ok(()) } diff --git a/napi/src/js_values/string/latin1.rs b/napi/src/js_values/string/latin1.rs index 2f6e7c43..d39f5736 100644 --- a/napi/src/js_values/string/latin1.rs +++ b/napi/src/js_values/string/latin1.rs @@ -1,8 +1,5 @@ use std::mem::ManuallyDrop; -#[cfg(feature = "latin1")] -use encoding_rs; - use crate::JsString; #[cfg(feature = "latin1")] diff --git a/napi/src/threadsafe_function.rs b/napi/src/threadsafe_function.rs index 483fb910..e0d3d929 100644 --- a/napi/src/threadsafe_function.rs +++ b/napi/src/threadsafe_function.rs @@ -1,3 +1,5 @@ +#![allow(clippy::single_component_path_imports)] + use std::convert::Into; use std::ffi::CString; use std::marker::PhantomData; @@ -23,9 +25,9 @@ pub enum ThreadsafeFunctionCallMode { Blocking, } -impl Into for ThreadsafeFunctionCallMode { - fn into(self) -> napi_threadsafe_function_call_mode { - match self { +impl From for napi_threadsafe_function_call_mode { + fn from(value: ThreadsafeFunctionCallMode) -> Self { + match value { ThreadsafeFunctionCallMode::Blocking => { napi_threadsafe_function_call_mode::napi_tsfn_blocking } @@ -232,7 +234,7 @@ impl ThreadsafeFunction { if self.aborted.load(Ordering::Acquire) { return Err(Error::new( Status::Closing, - format!("Can not ref, Thread safe function already aborted"), + "Can not ref, Thread safe function already aborted".to_string(), )); } self.ref_count.fetch_add(1, Ordering::AcqRel); @@ -245,7 +247,7 @@ impl ThreadsafeFunction { if self.aborted.load(Ordering::Acquire) { return Err(Error::new( Status::Closing, - format!("Can not unref, Thread safe function already aborted"), + "Can not unref, Thread safe function already aborted".to_string(), )); } self.ref_count.fetch_sub(1, Ordering::AcqRel); @@ -536,4 +538,5 @@ macro_rules! type_level_enum {( $( #[doc = $doc] )* $item )} + use type_level_enum; diff --git a/package.json b/package.json index a55ae25f..8adeb55a 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.0.0", "description": "A minimal library for building compiled Node add-ons in Rust.", "private": "true", - "workspaces": ["cli", "triples"], + "workspaces": ["cli", "triples", "memory-testing"], "repository": { "type": "git", "url": "git@github.com:Brooooooklyn/napi-rs.git" @@ -13,6 +13,7 @@ "bench": "cross-env TS_NODE_PROJECT='./bench/tsconfig.json' node -r ts-node/register/transpile-only bench/bench.ts", "build": "tsc -p tsconfig.json && shx chmod 777 cli/scripts/index.js && node generate-triplelist.js", "build:bench": "yarn --cwd ./bench build", + "build:memory": "yarn --cwd ./memory-testing build", "build:test": "yarn --cwd ./test_module build", "build:test:aarch64": "yarn --cwd ./test_module build-aarch64", "build:test:android": "yarn --cwd ./test_module build --target aarch64-linux-android", @@ -23,9 +24,10 @@ "format:rs": "cargo fmt", "format:source": "prettier --config ./package.json --write ./**/*.{js,ts}", "format:yaml": "prettier --parser yaml --write ./**/*.{yml,yaml}", - "lint": "eslint -c .eslintrc.yml ./cli/**/*.ts ./test_module/**/*.{ts,js}", + "lint": "eslint -c .eslintrc.yml .", "prepublishOnly": "npm run build && pinst --disable", "test": "ava", + "test:memory": "node memory-testing/index.mjs", "postinstall": "husky install", "postpublish": "pinst --enable" }, diff --git a/test_module/Cargo.toml b/test_module/Cargo.toml index 4bd5ab0e..ade169e5 100644 --- a/test_module/Cargo.toml +++ b/test_module/Cargo.toml @@ -23,6 +23,3 @@ tokio = {version = "1", features = ["default", "fs"]} [build-dependencies] napi-build = {path = "../build"} - -[profile.release] -lto = true diff --git a/test_module/__test__/arraybuffer.spec.ts b/test_module/__test__/arraybuffer.spec.ts index 100dc70b..911d2037 100644 --- a/test_module/__test__/arraybuffer.spec.ts +++ b/test_module/__test__/arraybuffer.spec.ts @@ -32,7 +32,7 @@ test('should be able to mutate Int16Array', (t) => { test('should be able to mutate Float32Array', (t) => { const fixture = new Float32Array([0, 1, 2]) bindings.mutateFloat32Array(fixture) - t.true(Math.abs(fixture[0] - 3.14) <= 0.0001) + t.true(Math.abs(fixture[0] - 3.33) <= 0.0001) }) test('should be able to mutate Float64Array', (t) => { diff --git a/test_module/src/arraybuffer.rs b/test_module/src/arraybuffer.rs index 4ebb1348..2ce536c7 100644 --- a/test_module/src/arraybuffer.rs +++ b/test_module/src/arraybuffer.rs @@ -37,7 +37,7 @@ pub fn mutate_int16_array(ctx: CallContext) -> Result { pub fn mutate_float32_array(ctx: CallContext) -> Result { let mut buffer = ctx.get::(0)?.into_value()?; let buffer_mut_ref: &mut [f32] = buffer.as_mut(); - buffer_mut_ref[0] = 3.14; + buffer_mut_ref[0] = 3.33; ctx.env.get_undefined() } diff --git a/test_module/src/class.rs b/test_module/src/class.rs index 4e8438ea..615d0123 100644 --- a/test_module/src/class.rs +++ b/test_module/src/class.rs @@ -41,7 +41,7 @@ fn add_native_count(ctx: CallContext) -> Result { let add: i32 = ctx.get::(0)?.try_into()?; let this: JsObject = ctx.this_unchecked(); let native_class: &mut NativeClass = ctx.env.unwrap(&this)?; - native_class.value = native_class.value + add; + native_class.value += add; ctx.env.create_int32(native_class.value) } @@ -55,7 +55,7 @@ fn new_test_class(ctx: CallContext) -> Result { .env .define_class("TestClass", test_class_constructor, properties.as_slice())?; - test_class.new(&vec![ctx.env.create_int32(42)?]) + test_class.new(&[ctx.env.create_int32(42)?]) } pub fn register_js(exports: &mut JsObject) -> Result<()> { diff --git a/test_module/src/lib.rs b/test_module/src/lib.rs index 0749e63f..c734807d 100644 --- a/test_module/src/lib.rs +++ b/test_module/src/lib.rs @@ -1,3 +1,5 @@ +#![allow(unused_variables)] + #[macro_use] extern crate napi_derive; #[macro_use] diff --git a/test_module/src/serde.rs b/test_module/src/serde.rs index bb24543a..5ef08954 100644 --- a/test_module/src/serde.rs +++ b/test_module/src/serde.rs @@ -76,7 +76,7 @@ make_test!(make_map, { }); make_test!(make_object, { - let value = AnObjectTwo { + AnObjectTwo { a: 1, b: vec![1, 2], c: "abc".into(), @@ -97,11 +97,10 @@ make_test!(make_object, { p: vec![1., 2., 3.5], q: 9998881288248882845242411222333, r: -3332323888900001232323022221345, - }; - value + } }); -const NUMBER_BYTES: &'static [u8] = &[255u8, 254, 253]; +const NUMBER_BYTES: &[u8] = &[255u8, 254, 253]; make_test!(make_buff, { serde_bytes::Bytes::new(NUMBER_BYTES) }); diff --git a/tsconfig.root-lint.json b/tsconfig.root-lint.json index 7894a21c..77829e5b 100644 --- a/tsconfig.root-lint.json +++ b/tsconfig.root-lint.json @@ -1,4 +1,10 @@ { "extends": "./tsconfig.json", - "files": ["./ava.config.js", "./generate-triplelist.js", "triples/index.js"] + "include": [ + "./ava.config.js", + "./generate-triplelist.js", + "./triples/index.js", + "./memory-testing/*.js", + "./memory-testing/*.mjs" + ] } diff --git a/yarn.lock b/yarn.lock index 2d66df69..94177cd1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1088,6 +1088,13 @@ resolved "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd" integrity sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ== +"@types/dockerode@^3.2.3": + version "3.2.3" + resolved "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.2.3.tgz#ae363b5bfa9f2989dab07f6e7ae3791e6431d145" + integrity sha512-nZRhpSxm3PYianRBcRExcHxDvEzYHUPfGCnRL5Fe4/fSEZbtxrRNJ7okzCans3lXxj2t298EynFHGTnTC2f1Iw== + dependencies: + "@types/node" "*" + "@types/inquirer@^7.3.1": version "7.3.1" resolved "https://registry.npmjs.org/@types/inquirer/-/inquirer-7.3.1.tgz#1f231224e7df11ccfaf4cf9acbcc3b935fea292d" @@ -1463,7 +1470,7 @@ asap@^2.0.0: resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= -asn1@~0.2.3: +asn1@~0.2.0, asn1@~0.2.3: version "0.2.4" resolved "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== @@ -1572,7 +1579,7 @@ base64-js@^1.3.1: resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -bcrypt-pbkdf@^1.0.0: +bcrypt-pbkdf@^1.0.0, bcrypt-pbkdf@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= @@ -1613,7 +1620,7 @@ binary-extensions@^2.0.0: resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -bl@^4.1.0: +bl@^4.0.3, bl@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== @@ -2395,6 +2402,24 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +docker-modem@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/docker-modem/-/docker-modem-3.0.0.tgz#cb912ad8daed42f858269fb3be6944df281ec12d" + integrity sha512-WwFajJ8I5geZ/dDZ5FDMDA6TBkWa76xWwGIGw8uzUjNUGCN0to83wJ8Oi1AxrJTC0JBn+7fvIxUctnawtlwXeg== + dependencies: + debug "^4.1.1" + readable-stream "^3.5.0" + split-ca "^1.0.1" + ssh2 "^0.8.7" + +dockerode@^3.3.0: + version "3.3.0" + resolved "https://registry.npmjs.org/dockerode/-/dockerode-3.3.0.tgz#bedaf48ef9fa9124275a54a9881a92374c51008e" + integrity sha512-St08lfOjpYCOXEM8XA0VLu3B3hRjtddODphNW5GFoA0AS3JHgoPQKOz0Qmdzg3P+hUPxhb02g1o1Cu1G+U3lRg== + dependencies: + docker-modem "^3.0.0" + tar-fs "~2.0.1" + doctrine@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -2463,7 +2488,7 @@ encoding@^0.1.12: dependencies: iconv-lite "^0.6.2" -end-of-stream@^1.1.0: +end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== @@ -2922,6 +2947,11 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + fs-extra@^9.0.1, fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" @@ -4366,6 +4396,7 @@ minipass-fetch@^1.3.0, minipass-fetch@^1.3.2: resolved "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.3.3.tgz#34c7cea038c817a8658461bf35174551dce17a0a" integrity sha512-akCrLDWfbdAWkMLBxJEeWTdNsjML+dt5YgOI4gJ53vuO0vrmYQkUPxa6j6V65s9CcePIr2SSWqjT2EcrNseryQ== dependencies: + encoding "^0.1.12" minipass "^3.1.0" minipass-sized "^1.0.3" minizlib "^2.0.0" @@ -4431,6 +4462,11 @@ minizlib@^2.0.0, minizlib@^2.1.1: minipass "^3.0.0" yallist "^4.0.0" +mkdirp-classic@^0.5.2: + version "0.5.3" + resolved "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + mkdirp-infer-owner@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/mkdirp-infer-owner/-/mkdirp-infer-owner-2.0.0.tgz#55d3b368e7d89065c38f32fd38e638f0ab61d316" @@ -5247,6 +5283,11 @@ prettier@^2.1.2, prettier@^2.3.0: resolved "https://registry.npmjs.org/prettier/-/prettier-2.3.0.tgz#b6a5bf1284026ae640f17f7ff5658a7567fc0d18" integrity sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w== +pretty-bytes@^5.6.0: + version "5.6.0" + resolved "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" + integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== + pretty-ms@^7.0.1: version "7.0.1" resolved "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz#7d903eaab281f7d8e03c66f867e239dc32fb73e8" @@ -5481,7 +5522,7 @@ read@1, read@~1.0.1: dependencies: mute-stream "~0.0.4" -readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.4.0: +readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0: version "3.6.0" resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -5939,6 +5980,11 @@ spdx-license-ids@^3.0.0: resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz#e9c18a410e5ed7e12442a549fbd8afa767038d65" integrity sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ== +split-ca@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz#6c83aff3692fa61256e0cd197e05e9de157691a6" + integrity sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY= + split-on-first@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" @@ -5963,6 +6009,22 @@ sprintf-js@~1.0.2: resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= +ssh2-streams@~0.4.10: + version "0.4.10" + resolved "https://registry.npmjs.org/ssh2-streams/-/ssh2-streams-0.4.10.tgz#48ef7e8a0e39d8f2921c30521d56dacb31d23a34" + integrity sha512-8pnlMjvnIZJvmTzUIIA5nT4jr2ZWNNVHwyXfMGdRJbug9TpI3kd99ffglgfSWqujVv/0gxwMsDn9j9RVst8yhQ== + dependencies: + asn1 "~0.2.0" + bcrypt-pbkdf "^1.0.2" + streamsearch "~0.1.2" + +ssh2@^0.8.7: + version "0.8.9" + resolved "https://registry.npmjs.org/ssh2/-/ssh2-0.8.9.tgz#54da3a6c4ba3daf0d8477a538a481326091815f3" + integrity sha512-GmoNPxWDMkVpMFa9LVVzQZHF6EW3WKmBwL+4/GeILf2hFmix5Isxm7Amamo8o7bHiU0tC+wXsGcUXOxp8ChPaw== + dependencies: + ssh2-streams "~0.4.10" + sshpk@^1.7.0: version "1.16.1" resolved "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" @@ -5997,6 +6059,11 @@ stats-median@^1.0.1: resolved "https://registry.npmjs.org/stats-median/-/stats-median-1.0.1.tgz#ca8497cb1014d23d145db4d6fc93c8e815eed3ef" integrity sha512-IYsheLg6dasD3zT/w9+8Iq9tcIQqqu91ZIpJOnIEM25C3X/g4Tl8mhXwW2ZQpbrsJISr9+wizEYgsibN5/b32Q== +streamsearch@~0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" + integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= + strict-uri-encode@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" @@ -6203,7 +6270,7 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -table@^6.0.9: +table@^6.0.9, table@^6.7.1: version "6.7.1" resolved "https://registry.npmjs.org/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2" integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg== @@ -6215,6 +6282,27 @@ table@^6.0.9: string-width "^4.2.0" strip-ansi "^6.0.0" +tar-fs@~2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz#e44086c1c60d31a4f0cf893b1c4e155dabfae9e2" + integrity sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.0.0" + +tar-stream@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + tar@^4.4.12: version "4.4.13" resolved "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525"