ci: add memory leak detect job

This commit is contained in:
LongYinan 2021-05-28 18:50:16 +08:00
parent 1145f2ee0b
commit 72960906dc
No known key found for this signature in database
GPG key ID: C3666B7FC82ADAD7
27 changed files with 513 additions and 41 deletions

View file

@ -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:

82
.github/workflows/memory-test.yml vendored Normal file
View file

@ -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

View file

@ -1,9 +1,11 @@
[workspace]
exclude = ["./test_module", "./bench", "./testing"]
members = [
"./build",
"./napi",
"./napi-derive",
"./napi-derive-example",
"./sys",
"./test_module",
"./bench",
"./memory-testing",
]

View file

@ -21,7 +21,3 @@ mimalloc = {version = "0.1"}
[build-dependencies]
napi-build = {path = "../build"}
[profile.release]
lto = true
opt-level = 3

21
memory-testing/Cargo.toml Normal file
View file

@ -0,0 +1,21 @@
[package]
authors = ["LongYinan <lynweklm@gmail.com>"]
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"}

5
memory-testing/build.rs Normal file
View file

@ -0,0 +1,5 @@
extern crate napi_build;
fn main() {
napi_build::setup();
}

6
memory-testing/index.mjs Normal file
View file

@ -0,0 +1,6 @@
import { createSuite } from './test-util.mjs'
await createSuite('tokio-future')
await createSuite('serde')
process.exit(0)

View file

@ -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"
}
}

35
memory-testing/serde.js Normal file
View file

@ -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++
}

82
memory-testing/src/lib.rs Normal file
View file

@ -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<napi::JsObject> {
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<napi::JsString> {
let input_object = ctx.get::<napi::JsObject>(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(())
}

View file

@ -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()
}

View file

@ -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)
})

25
memory-testing/util.js Normal file
View file

@ -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))
}

View file

@ -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) })

View file

@ -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)

View file

@ -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(())
}

View file

@ -1,8 +1,5 @@
use std::mem::ManuallyDrop;
#[cfg(feature = "latin1")]
use encoding_rs;
use crate::JsString;
#[cfg(feature = "latin1")]

View file

@ -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<napi_threadsafe_function_call_mode> for ThreadsafeFunctionCallMode {
fn into(self) -> napi_threadsafe_function_call_mode {
match self {
impl From<ThreadsafeFunctionCallMode> 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<T: 'static, ES: ErrorStrategy::T> ThreadsafeFunction<T, ES> {
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<T: 'static, ES: ErrorStrategy::T> ThreadsafeFunction<T, ES> {
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;

View file

@ -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"
},

View file

@ -23,6 +23,3 @@ tokio = {version = "1", features = ["default", "fs"]}
[build-dependencies]
napi-build = {path = "../build"}
[profile.release]
lto = true

View file

@ -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) => {

View file

@ -37,7 +37,7 @@ pub fn mutate_int16_array(ctx: CallContext) -> Result<JsUndefined> {
pub fn mutate_float32_array(ctx: CallContext) -> Result<JsUndefined> {
let mut buffer = ctx.get::<JsTypedArray>(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()
}

View file

@ -41,7 +41,7 @@ fn add_native_count(ctx: CallContext) -> Result<JsNumber> {
let add: i32 = ctx.get::<JsNumber>(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<JsObject> {
.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<()> {

View file

@ -1,3 +1,5 @@
#![allow(unused_variables)]
#[macro_use]
extern crate napi_derive;
#[macro_use]

View file

@ -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) });

View file

@ -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"
]
}

100
yarn.lock
View file

@ -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"