diff --git a/backend/.gitignore b/backend/.gitignore index b72cefe4b9..249dc7370b 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,4 +1,5 @@ target/ .env oauth.json -windmill-api/openapi-deref.yaml \ No newline at end of file +windmill-api/openapi-deref.yaml +tracing.folded \ No newline at end of file diff --git a/backend/.sqlx/query-1e232d4b978d133ba542d0abe5afaa0031f630c5336c0b805590b5ea4508ed04.json b/backend/.sqlx/query-1e232d4b978d133ba542d0abe5afaa0031f630c5336c0b805590b5ea4508ed04.json new file mode 100644 index 0000000000..02b4b5d4ee --- /dev/null +++ b/backend/.sqlx/query-1e232d4b978d133ba542d0abe5afaa0031f630c5336c0b805590b5ea4508ed04.json @@ -0,0 +1,12 @@ +{ + "db_name": "PostgreSQL", + "query": "VACUUM queue", + "describe": { + "columns": [], + "parameters": { + "Left": [] + }, + "nullable": [] + }, + "hash": "1e232d4b978d133ba542d0abe5afaa0031f630c5336c0b805590b5ea4508ed04" +} diff --git a/backend/.sqlx/query-330e85c6fe52355971262d3a44c2b75a95b1d042b9e0995186aa5a2d0b7ee552.json b/backend/.sqlx/query-330e85c6fe52355971262d3a44c2b75a95b1d042b9e0995186aa5a2d0b7ee552.json new file mode 100644 index 0000000000..95c46a8841 --- /dev/null +++ b/backend/.sqlx/query-330e85c6fe52355971262d3a44c2b75a95b1d042b9e0995186aa5a2d0b7ee552.json @@ -0,0 +1,30 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT COALESCE(j.min_started_at, q.min_started_at) AS min_started_at, COALESCE(completed_count, 0) AS completed_count\n FROM\n (SELECT script_path, MIN(started_at) as min_started_at, COUNT(*) as completed_count\n FROM completed_job\n WHERE script_path = $1 AND job_kind != 'dependencies' AND started_at + INTERVAL '1 MILLISECOND' * duration_ms > (now() - INTERVAL '1 second' * $2) AND workspace_id = $3 AND canceled = false\n GROUP BY script_path) as j\n FULL OUTER JOIN\n (SELECT script_path, MIN(started_at) as min_started_at\n FROM queue\n WHERE script_path = $1 AND job_kind != 'dependencies' AND running = true AND workspace_id = $3 AND canceled = false\n GROUP BY script_path) as q\n ON q.script_path = j.script_path", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "min_started_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 1, + "name": "completed_count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Text", + "Float8", + "Text" + ] + }, + "nullable": [ + true, + true + ] + }, + "hash": "330e85c6fe52355971262d3a44c2b75a95b1d042b9e0995186aa5a2d0b7ee552" +} diff --git a/backend/.sqlx/query-467247beca4dae321ab3acc3a99f655412761157cda54bbd3e58cada3de3396c.json b/backend/.sqlx/query-467247beca4dae321ab3acc3a99f655412761157cda54bbd3e58cada3de3396c.json new file mode 100644 index 0000000000..cdd02265a1 --- /dev/null +++ b/backend/.sqlx/query-467247beca4dae321ab3acc3a99f655412761157cda54bbd3e58cada3de3396c.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE concurrency_counter SET counter = counter - 1 WHERE concurrency_id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [] + }, + "hash": "467247beca4dae321ab3acc3a99f655412761157cda54bbd3e58cada3de3396c" +} diff --git a/backend/.sqlx/query-71879f3644dde27f2d93e5511e420579dfd2ffab72bb8480a1622b68703d4ea7.json b/backend/.sqlx/query-71879f3644dde27f2d93e5511e420579dfd2ffab72bb8480a1622b68703d4ea7.json new file mode 100644 index 0000000000..2c9b770df5 --- /dev/null +++ b/backend/.sqlx/query-71879f3644dde27f2d93e5511e420579dfd2ffab72bb8480a1622b68703d4ea7.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE concurrency_counter SET counter = counter - 1 WHERE concurrency_id = $1 RETURNING counter", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "counter", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "71879f3644dde27f2d93e5511e420579dfd2ffab72bb8480a1622b68703d4ea7" +} diff --git a/backend/.sqlx/query-86652246b20f9bd023d3419ec382176780a87f32673b53de6f899564c1494147.json b/backend/.sqlx/query-86652246b20f9bd023d3419ec382176780a87f32673b53de6f899564c1494147.json new file mode 100644 index 0000000000..cee6f85329 --- /dev/null +++ b/backend/.sqlx/query-86652246b20f9bd023d3419ec382176780a87f32673b53de6f899564c1494147.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO concurrency_counter VALUES ($1, 1)\n ON CONFLICT (concurrency_id) \n DO UPDATE SET counter = concurrency_counter.counter + 1\n RETURNING concurrency_counter.counter", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "counter", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Varchar" + ] + }, + "nullable": [ + false + ] + }, + "hash": "86652246b20f9bd023d3419ec382176780a87f32673b53de6f899564c1494147" +} diff --git a/backend/.sqlx/query-afbae51dae93e19045aee8d2d9ea1d31609aa7947a52545dce4455a4983493a7.json b/backend/.sqlx/query-afbae51dae93e19045aee8d2d9ea1d31609aa7947a52545dce4455a4983493a7.json deleted file mode 100644 index 2d837adc32..0000000000 --- a/backend/.sqlx/query-afbae51dae93e19045aee8d2d9ea1d31609aa7947a52545dce4455a4983493a7.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT COALESCE(j.min_started_at, q.min_started_at) AS min_started_at, COALESCE(completed_count, 0) + COALESCE(running_count, 0) AS total_count\n FROM\n (SELECT script_path, MIN(started_at) as min_started_at, COUNT(*) as completed_count\n FROM completed_job\n WHERE script_path = $1 AND job_kind != 'dependencies' AND started_at + INTERVAL '1 MILLISECOND' * duration_ms > (now() - INTERVAL '1 second' * $2) AND workspace_id = $3 AND canceled = false\n GROUP BY script_path) as j\n FULL OUTER JOIN\n (SELECT script_path, MIN(started_at) as min_started_at, COUNT(*) as running_count\n FROM queue\n WHERE script_path = $1 AND job_kind != 'dependencies' AND running = true AND workspace_id = $3 AND canceled = false\n GROUP BY script_path) as q\n ON q.script_path = j.script_path", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "min_started_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 1, - "name": "total_count", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Text", - "Float8", - "Text" - ] - }, - "nullable": [ - true, - true - ] - }, - "hash": "afbae51dae93e19045aee8d2d9ea1d31609aa7947a52545dce4455a4983493a7" -} diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 0c236f5288..ec1cac6357 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -2639,15 +2639,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.9" @@ -2721,7 +2712,7 @@ dependencies = [ "diff", "ena", "is-terminal", - "itertools 0.10.5", + "itertools", "lalrpop-util", "petgraph", "regex", @@ -3296,9 +3287,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.4" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" dependencies = [ "num-traits", "serde", @@ -4638,7 +4629,7 @@ dependencies = [ "bincode", "bitflags 1.3.2", "bstr", - "itertools 0.10.5", + "itertools", "lz4_flex", "num-bigint", "num-complex", @@ -4655,7 +4646,7 @@ checksum = "db7348e148144ba85bc1284d97004062137306554fd00e7eb500d91720de5e78" dependencies = [ "ahash 0.7.6", "anyhow", - "itertools 0.10.5", + "itertools", "lalrpop", "lalrpop-util", "log", @@ -5282,7 +5273,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c12bc9199d1db8234678b7051747c07f517cdcf019262d1847b94ec8b1aee3e" dependencies = [ - "itertools 0.10.5", + "itertools", "nom", "unicode_categories", ] @@ -6423,6 +6414,17 @@ dependencies = [ "valuable", ] +[[package]] +name = "tracing-flame" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bae117ee14789185e129aaee5d93750abe67fdc5a9a62650452bfe4e122a3a9" +dependencies = [ + "lazy_static", + "tracing", + "tracing-subscriber", +] + [[package]] name = "tracing-futures" version = "0.2.5" @@ -7122,7 +7124,7 @@ dependencies = [ "hex", "hmac", "hyper", - "itertools 0.11.0", + "itertools", "lazy_static", "magic-crypt", "mail-send", @@ -7211,6 +7213,7 @@ dependencies = [ "thiserror", "tokio", "tracing", + "tracing-flame", "tracing-subscriber", "uuid 1.4.1", ] @@ -7240,7 +7243,7 @@ version = "1.165.0" dependencies = [ "anyhow", "gosyn", - "itertools 0.11.0", + "itertools", "lazy_static", "regex", "windmill-parser", @@ -7262,7 +7265,7 @@ name = "windmill-parser-py" version = "1.165.0" dependencies = [ "anyhow", - "itertools 0.11.0", + "itertools", "rustpython-parser", "serde_json", "windmill-parser", @@ -7274,7 +7277,7 @@ version = "1.165.0" dependencies = [ "anyhow", "async-recursion", - "itertools 0.11.0", + "itertools", "lazy_static", "phf 0.11.2", "regex", @@ -7343,7 +7346,7 @@ dependencies = [ "futures-core", "hex", "hmac", - "itertools 0.11.0", + "itertools", "lazy_static", "prometheus", "reqwest", @@ -7381,7 +7384,7 @@ dependencies = [ "futures", "gcp_auth", "git-version", - "itertools 0.11.0", + "itertools", "jsonwebtoken", "lazy_static", "mysql_async", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index aa9f0ac7ff..bff5b4a64c 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -36,7 +36,8 @@ incremental = true [features] enterprise = ["windmill-worker/enterprise", "windmill-queue/enterprise", "windmill-api/enterprise"] -benchmark = ["windmill-api/benchmark"] +benchmark = ["windmill-api/benchmark", "windmill-worker/benchmark", "windmill-queue/benchmark"] +flamegraph = ["windmill-common/flamegraph", "windmill-worker/flamegraph"] [dependencies] anyhow.workspace = true diff --git a/backend/migrations/20230902192640_add_index.down.sql b/backend/migrations/20230902192640_add_index.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20230902192640_add_index.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20230902192640_add_index.up.sql b/backend/migrations/20230902192640_add_index.up.sql new file mode 100644 index 0000000000..22e582fed9 --- /dev/null +++ b/backend/migrations/20230902192640_add_index.up.sql @@ -0,0 +1,13 @@ +-- Add up migration script here +DROP INDEX IF EXISTS queue_sort; +CREATE INDEX IF NOT EXISTS queue_sort ON queue (scheduled_for, created_at, tag) WHERE running = false; + +CREATE TABLE IF NOT EXISTS concurrency_counter ( + concurrency_id VARCHAR(1000) PRIMARY KEY, + counter INTEGER NOT NULL +); + +DROP INDEX IF EXISTS root_queue_index_suspended; + +DROP INDEX IF EXISTS queue_suspended; +CREATE INDEX IF NOT EXISTS queue_suspended ON queue (created_at, suspend_until, suspend, tag) WHERE suspend_until is not null; diff --git a/backend/src/main.rs b/backend/src/main.rs index 34594c7536..823bb0e329 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -40,8 +40,12 @@ mod monitor; async fn main() -> anyhow::Result<()> { dotenv::dotenv().ok(); + #[cfg(not(feature = "flamegraph"))] windmill_common::tracing_init::initialize_tracing(); + #[cfg(feature = "flamegraph")] + let _guard = windmill_common::tracing_init::setup_flamegraph(); + let num_workers = std::env::var("NUM_WORKERS") .ok() .and_then(|x| x.parse::().ok()) @@ -92,6 +96,7 @@ async fn main() -> anyhow::Result<()> { tracing::info!("Database connected"); let rsmq = if let Some(config) = rsmq_config { + tracing::info!("Redis config set: {:?}", config); Some(rsmq_async::MultiplexedRsmq::new(config).await.unwrap()) } else { None diff --git a/backend/windmill-api/src/jobs.rs b/backend/windmill-api/src/jobs.rs index 4d75b78971..5c5ebe280e 100644 --- a/backend/windmill-api/src/jobs.rs +++ b/backend/windmill-api/src/jobs.rs @@ -2253,6 +2253,7 @@ async fn run_preview_job( Ok((StatusCode::CREATED, uuid.to_string())) } +#[tracing::instrument(level = "trace", skip_all)] async fn add_noop_jobs( authed: ApiAuthed, Extension(db): Extension, diff --git a/backend/windmill-api/src/lib.rs b/backend/windmill-api/src/lib.rs index 2c6b4e60bf..1be5fe6c95 100644 --- a/backend/windmill-api/src/lib.rs +++ b/backend/windmill-api/src/lib.rs @@ -156,7 +156,9 @@ pub async fn run_server( for tag in ALL_TAGS.clone() { let r = rsmq_async::RsmqConnection::create_queue(&mut rsmq, &tag, None, None, None).await; - if r.is_ok() { + if let Err(e) = r { + tracing::info!("Redis queue {tag} could not be created: {e}"); + } else { tracing::info!("Redis queue {tag} created"); } } diff --git a/backend/windmill-common/Cargo.toml b/backend/windmill-common/Cargo.toml index 946fb79d7f..c0e37e8fa5 100644 --- a/backend/windmill-common/Cargo.toml +++ b/backend/windmill-common/Cargo.toml @@ -16,6 +16,7 @@ tracing_init = [ "dep:tracing", "dep:tracing-subscriber", ] +flamegraph = ["dep:tracing-flame"] [lib] @@ -41,4 +42,5 @@ hyper = { workspace = true, optional = true } tokio = { workspace = true, optional = true } reqwest = { workspace = true, optional = true } tracing-subscriber = { workspace = true, optional = true } -lazy_static.workspace = true \ No newline at end of file +lazy_static.workspace = true +tracing-flame = { version = "^0", optional = true } diff --git a/backend/windmill-common/src/jobs.rs b/backend/windmill-common/src/jobs.rs index 5e6e772165..ede9972f41 100644 --- a/backend/windmill-common/src/jobs.rs +++ b/backend/windmill-common/src/jobs.rs @@ -104,6 +104,14 @@ impl QueuedJob { pub fn is_flow(&self) -> bool { matches!(self.job_kind, JobKind::Flow | JobKind::FlowPreview) } + + pub fn full_path(&self) -> String { + format!( + "{}/{}", + if self.is_flow() { "flow" } else { "script" }, + self.script_path() + ) + } } impl QueuedJob { diff --git a/backend/windmill-common/src/tracing_init.rs b/backend/windmill-common/src/tracing_init.rs index 14b98651db..67ad871e19 100644 --- a/backend/windmill-common/src/tracing_init.rs +++ b/backend/windmill-common/src/tracing_init.rs @@ -48,3 +48,19 @@ pub fn initialize_tracing() { .init(), } } + +#[cfg(feature = "flamegraph")] +use tracing_flame::FlameLayer; + +#[cfg(feature = "flamegraph")] +pub fn setup_flamegraph() -> impl Drop { + // let fmt_layer = Layer::default(); + + let (flame_layer, _guard) = FlameLayer::with_file("./tracing.folded").unwrap(); + + tracing_subscriber::registry() + // .with(fmt_layer) + .with(flame_layer) + .init(); + _guard +} diff --git a/backend/windmill-queue/Cargo.toml b/backend/windmill-queue/Cargo.toml index cd861e317e..36c6a94fbd 100644 --- a/backend/windmill-queue/Cargo.toml +++ b/backend/windmill-queue/Cargo.toml @@ -11,6 +11,7 @@ path = "src/lib.rs" [features] default = [] enterprise = [] +benchmark = [] [dependencies] windmill-audit.workspace = true diff --git a/backend/windmill-queue/src/jobs.rs b/backend/windmill-queue/src/jobs.rs index d9a067b73f..c1fe7dd566 100644 --- a/backend/windmill-queue/src/jobs.rs +++ b/backend/windmill-queue/src/jobs.rs @@ -8,10 +8,12 @@ use std::{collections::HashMap, sync::atomic::AtomicBool, vec}; +#[cfg(feature = "benchmark")] +use std::time::Instant; + use anyhow::Context; use async_recursion::async_recursion; use chrono::{DateTime, Duration, Utc}; -use itertools::Itertools; use reqwest::Client; use rsmq_async::RsmqConnection; use serde_json::json; @@ -91,43 +93,8 @@ lazy_static::lazy_static! { pub static ref IS_WORKER_TAGS_DEFINED: bool = std::env::var("WORKER_TAGS").ok().is_some(); - pub static ref PULL_QUERY_NON_RUNNING: String = format!( - "UPDATE queue - SET running = true - , started_at = coalesce(started_at, now()) - , last_ping = now() - , suspend_until = null - WHERE id = ( - SELECT id - FROM queue - WHERE running = false AND scheduled_for <= now() AND ({}) - ORDER BY scheduled_for, created_at - FOR UPDATE SKIP LOCKED - LIMIT 1 - ) - RETURNING *", - ACCEPTED_TAGS.clone().into_iter().map(|x| format!("(tag = '{x}')")).join(" OR ") - ); - pub static ref PULL_QUERY_SUSPEND: String = format!( - "UPDATE queue - SET running = true - , started_at = coalesce(started_at, now()) - , last_ping = now() - , suspend_until = null - WHERE id = ( - SELECT id - FROM queue - WHERE suspend_until IS NOT NULL AND (suspend <= 0 OR suspend_until <= now()) AND ({}) - ORDER BY scheduled_for, created_at - FOR UPDATE SKIP LOCKED - LIMIT 1 - ) - RETURNING *", - ACCEPTED_TAGS.clone().into_iter().map(|x| format!("(tag = '{x}')")).join(" OR ") - ); - // When compiled in 'benchmark' mode, this flags is exposed via the /workers/toggle endpoint @@ -268,7 +235,7 @@ lazy_static::lazy_static! { pub static ref GLOBAL_ERROR_HANDLER_PATH_IN_ADMINS_WORKSPACE: Option = std::env::var("GLOBAL_ERROR_HANDLER_PATH_IN_ADMINS_WORKSPACE").ok(); } -#[instrument(level = "trace", skip_all)] +#[instrument(level = "trace", skip_all, name = "add_completed_job")] pub async fn add_completed_job( db: &Pool, queued_job: &QueuedJob, @@ -417,6 +384,19 @@ pub async fn add_completed_job( ) .await?; } + if queued_job.concurrent_limit.is_some() { + if let Err(e) = sqlx::query_scalar!( + "UPDATE concurrency_counter SET counter = counter - 1 WHERE concurrency_id = $1", + queued_job.full_path() + ) + .execute(&mut tx) + .await + { + tracing::error!("Could not decrement concurrency counter: {}", e); + } + tracing::debug!("decremented concurrency counter"); + } + tx.commit().await?; #[cfg(feature = "enterprise")] @@ -960,13 +940,9 @@ pub async fn pull( rsmq: Option, suspend_first: bool, ) -> windmill_common::error::Result> { - // let rs = rd_string(2); - // let instant = Instant::now(); - loop { - let tx: QueueTransaction<'_, _> = (rsmq.clone(), db.clone().begin().await?).into(); - let (job, mut tx) = pull_single_job_and_mark_as_running_no_concurrency_limit( - tx, + let job = pull_single_job_and_mark_as_running_no_concurrency_limit( + db, rsmq.clone(), suspend_first, ) @@ -985,10 +961,13 @@ pub async fn pull( if *METRICS_ENABLED { QUEUE_PULL_COUNT.inc(); } - tx.commit().await?; return Ok(Option::Some(pulled_job)); } + let itx = db.begin().await?; + + let mut tx: QueueTransaction<'_, _> = (rsmq.clone(), itx).into(); + // Else the job is subject to concurrency limits let job_script_path = pulled_job.script_path.clone().unwrap(); @@ -1002,15 +981,31 @@ pub async fn pull( job_custom_concurrency_time_window_s ); + let running_job = sqlx::query_scalar!( + "INSERT INTO concurrency_counter VALUES ($1, 1) + ON CONFLICT (concurrency_id) + DO UPDATE SET counter = concurrency_counter.counter + 1 + RETURNING concurrency_counter.counter", + pulled_job.full_path(), + ) + .fetch_one(&mut tx) + .await + .map_err(|e| { + Error::InternalErr(format!( + "Error getting concurrency count for script path {job_script_path}: {e}" + )) + })?; + tracing::debug!("running_job: {}", running_job); + let script_path_live_stats = sqlx::query!( - "SELECT COALESCE(j.min_started_at, q.min_started_at) AS min_started_at, COALESCE(completed_count, 0) + COALESCE(running_count, 0) AS total_count + "SELECT COALESCE(j.min_started_at, q.min_started_at) AS min_started_at, COALESCE(completed_count, 0) AS completed_count FROM (SELECT script_path, MIN(started_at) as min_started_at, COUNT(*) as completed_count FROM completed_job WHERE script_path = $1 AND job_kind != 'dependencies' AND started_at + INTERVAL '1 MILLISECOND' * duration_ms > (now() - INTERVAL '1 second' * $2) AND workspace_id = $3 AND canceled = false GROUP BY script_path) as j FULL OUTER JOIN - (SELECT script_path, MIN(started_at) as min_started_at, COUNT(*) as running_count + (SELECT script_path, MIN(started_at) as min_started_at FROM queue WHERE script_path = $1 AND job_kind != 'dependencies' AND running = true AND workspace_id = $3 AND canceled = false GROUP BY script_path) as q @@ -1027,20 +1022,31 @@ pub async fn pull( )) })?; - let concurrent_jobs_for_this_script: Option = script_path_live_stats.total_count; + let concurrent_jobs_for_this_script = + script_path_live_stats.completed_count.unwrap_or_default() as i32 + running_job; tracing::debug!( "Current concurrent jobs for this script: {}", - concurrent_jobs_for_this_script.unwrap_or(-1) + concurrent_jobs_for_this_script ); - if concurrent_jobs_for_this_script.is_none() - || concurrent_jobs_for_this_script.unwrap() <= i64::from(job_custom_concurrent_limit) - { + if concurrent_jobs_for_this_script <= job_custom_concurrent_limit { if *METRICS_ENABLED { QUEUE_PULL_COUNT.inc(); } tx.commit().await?; return Ok(Option::Some(pulled_job)); } + let x = sqlx::query_scalar!( + "UPDATE concurrency_counter SET counter = counter - 1 WHERE concurrency_id = $1 RETURNING counter", + pulled_job.full_path() + ) + .fetch_one(&mut tx) + .await + .map_err(|e| { + Error::InternalErr(format!( + "Error decreasing concurrency count for script path {job_script_path}: {e}" + )) + })?; + tracing::debug!("running_job after decrease: {}", x); let job_uuid: Uuid = pulled_job.id; let min_started_at: Option> = script_path_live_stats.min_started_at; @@ -1114,25 +1120,35 @@ async fn pull_single_job_and_mark_as_running_no_concurrency_limit< 'c, R: rsmq_async::RsmqConnection + Send + Clone, >( - mut tx: QueueTransaction<'c, R>, + db: &Pool, rsmq: Option, suspend_first: bool, -) -> windmill_common::error::Result<(Option, QueueTransaction<'c, R>)> { +) -> windmill_common::error::Result> { let job: Option = if let Some(mut rsmq) = rsmq { + #[cfg(feature = "benchmark")] + let instant = Instant::now(); + // TODO: REDIS: Race conditions / replace last_ping // TODO: shuffle this list to have fairness let mut all_tags = ACCEPTED_TAGS.clone(); let mut msg: Option<_> = None; + let mut tag = None; + while msg.is_none() && !all_tags.is_empty() { + let ntag = all_tags.pop().unwrap(); + tag = Some(ntag.clone()); msg = rsmq - .pop_message::>(&all_tags.pop().unwrap()) + .receive_message::>(&ntag, Some(10)) .await .map_err(|e| anyhow::anyhow!(e))?; } - // println!("3.1: {:?} {rs}", instant.elapsed()); + // #[cfg(feature = "benchmark")] + // println!("rsmq 1: {:?}", instant.elapsed()); + + // println!("3.1: {:?} {rs}", instant.elapsed()); if let Some(msg) = msg { let uuid = Uuid::from_bytes_le( msg.message @@ -1140,7 +1156,7 @@ async fn pull_single_job_and_mark_as_running_no_concurrency_limit< .map_err(|_| anyhow::anyhow!("Failed to parsed Redis message"))?, ); - sqlx::query_as::<_, QueuedJob>( + let m2 = sqlx::query_as::<_, QueuedJob>( "UPDATE queue SET running = true , started_at = coalesce(started_at, now()) @@ -1150,8 +1166,17 @@ async fn pull_single_job_and_mark_as_running_no_concurrency_limit< RETURNING *", ) .bind(uuid) - .fetch_optional(&mut tx) - .await? + .fetch_optional(db) + .await?; + + rsmq.delete_message(&tag.unwrap(), &msg.id) + .await + .map_err(|e| anyhow::anyhow!(e))?; + + #[cfg(feature = "benchmark")] + println!("rsmq 2: {:?}", instant.elapsed()); + + m2 } else { None } @@ -1165,22 +1190,60 @@ async fn pull_single_job_and_mark_as_running_no_concurrency_limit< * or suspend_until <= now() if it has timed out */ let r = if suspend_first { - sqlx::query_as::<_, QueuedJob>(&PULL_QUERY_SUSPEND) - .fetch_optional(&mut tx) + sqlx::query_as::<_, QueuedJob>("UPDATE queue + SET running = true + , started_at = coalesce(started_at, now()) + , last_ping = now() + , suspend_until = null + WHERE id = ( + SELECT id + FROM queue + WHERE suspend_until IS NOT NULL AND (suspend <= 0 OR suspend_until <= now()) AND tag = ANY($1) + ORDER BY created_at + FOR UPDATE SKIP LOCKED + LIMIT 1 + ) + RETURNING *") + .bind(ACCEPTED_TAGS.as_slice()) + .fetch_optional(db) .await? } else { None }; if r.is_none() { - sqlx::query_as::<_, QueuedJob>(&PULL_QUERY_NON_RUNNING) - .fetch_optional(&mut tx) - .await? + // #[cfg(feature = "benchmark")] + // let instant = Instant::now(); + + let r = sqlx::query_as::<_, QueuedJob>( + "UPDATE queue + SET running = true + , started_at = coalesce(started_at, now()) + , last_ping = now() + , suspend_until = null + WHERE id = ( + SELECT id + FROM queue + WHERE running = false AND scheduled_for <= now() AND tag = ANY($1) + ORDER BY scheduled_for, created_at + FOR UPDATE SKIP LOCKED + LIMIT 1 + ) + RETURNING *", + ) + .bind(ACCEPTED_TAGS.as_slice()) + .fetch_optional(db) + .await?; + + // #[cfg(feature = "benchmark")] + // println!("pull query: {:?}", instant.elapsed()); + + r } else { r } }; - Ok((job, tx)) + Ok(job) } #[async_recursion] diff --git a/backend/windmill-worker/Cargo.toml b/backend/windmill-worker/Cargo.toml index 103718342b..044297890b 100644 --- a/backend/windmill-worker/Cargo.toml +++ b/backend/windmill-worker/Cargo.toml @@ -11,6 +11,8 @@ path = "src/lib.rs" [features] default = [] enterprise = ["windmill-queue/enterprise", "dep:gcp_auth", "dep:jsonwebtoken", "dep:pem", "dep:sha2"] +benchmark = ["windmill-queue/benchmark"] +flamegraph = [] [dependencies] windmill-queue.workspace = true diff --git a/backend/windmill-worker/src/worker.rs b/backend/windmill-worker/src/worker.rs index 56d3b843bc..df865eca69 100644 --- a/backend/windmill-worker/src/worker.rs +++ b/backend/windmill-worker/src/worker.rs @@ -10,56 +10,84 @@ use anyhow::Result; use const_format::concatcp; use itertools::Itertools; use once_cell::sync::OnceCell; +#[cfg(feature = "benchmark")] +use serde::Serialize; use sqlx::{Pool, Postgres}; -use windmill_api_client::Client; use std::{ - collections::HashMap, time::Duration, - sync::{Arc, atomic::Ordering} + collections::HashMap, + sync::{atomic::Ordering, Arc}, + time::Duration, }; +use windmill_api_client::Client; -use tracing::{trace_span, Instrument}; use uuid::Uuid; use windmill_common::{ error::{self, to_anyhow, Error}, - flows::{FlowModuleValue, FlowValue, FlowModule}, - scripts::{ScriptHash, ScriptLang, get_full_hub_script_by_path}, + flows::{FlowModule, FlowModuleValue, FlowValue}, + jobs::{JobKind, Metrics, QueuedJob}, + scripts::{get_full_hub_script_by_path, ScriptHash, ScriptLang}, + users::SUPERADMIN_SECRET_EMAIL, utils::{rd_string, StripPath}, - users::SUPERADMIN_SECRET_EMAIL, jobs::{JobKind, QueuedJob, Metrics}, METRICS_ENABLED, IS_READY, DB, + DB, IS_READY, METRICS_ENABLED, +}; +use windmill_queue::{ + canceled_job_to_result, get_queued_job, pull, ACCEPTED_TAGS, CLOUD_HOSTED, HTTP_CLIENT, + IS_WORKER_TAGS_DEFINED, }; -use windmill_queue::{canceled_job_to_result, get_queued_job, pull, CLOUD_HOSTED, HTTP_CLIENT, ACCEPTED_TAGS, IS_WORKER_TAGS_DEFINED}; use serde_json::{json, Value}; use tokio::{ fs::{symlink, DirBuilder}, sync::{ - mpsc::{self, Sender}, RwLock, Barrier + mpsc::{self, Sender}, + Barrier, RwLock, }, - time::Instant + time::Instant, }; use futures::future::FutureExt; use async_recursion::async_recursion; -#[cfg(feature = "enterprise")] use rand::Rng; #[cfg(feature = "enterprise")] -use crate::global_cache::{copy_cache_to_tmp_cache, cache_global, copy_tmp_cache_to_cache, copy_denogo_cache_from_bucket_as_tar, copy_all_piptars_from_bucket}; +use crate::global_cache::{ + cache_global, copy_all_piptars_from_bucket, copy_cache_to_tmp_cache, + copy_denogo_cache_from_bucket_as_tar, copy_tmp_cache_to_cache, +}; -use windmill_queue::{add_completed_job, add_completed_job_error,IDLE_WORKERS}; +use windmill_queue::{add_completed_job, add_completed_job_error}; + +#[cfg(feature = "benchmark")] +use windmill_queue::IDLE_WORKERS; use crate::{ + bash_executor::{handle_bash_job, handle_powershell_job, ANSI_ESCAPE_RE}, + bun_executor::{gen_lockfile, handle_bun_job}, + common::{hash_args, read_result, save_in_cache, set_logs, transform_json_value, write_file}, + deno_executor::{generate_deno_lock, handle_deno_job}, + go_executor::{handle_go_job, install_go_dependencies}, + graphql_executor::do_graphql, + js_eval::{eval_fetch_timeout, transpile_ts}, + mysql_executor::do_mysql, + pg_executor::do_postgresql, + python_executor::{ + create_dependencies_dir, handle_python_job, handle_python_reqs, pip_compile, + }, worker_flow::{ - handle_flow, update_flow_status_after_job_completion, update_flow_status_in_progress, - }, python_executor::{create_dependencies_dir, pip_compile, handle_python_job, handle_python_reqs}, common::{read_result, set_logs, write_file, transform_json_value, save_in_cache, hash_args}, go_executor::{handle_go_job, install_go_dependencies}, js_eval::{transpile_ts, eval_fetch_timeout}, pg_executor::do_postgresql, mysql_executor::do_mysql, graphql_executor::do_graphql, bun_executor::{handle_bun_job, gen_lockfile}, bash_executor::{ANSI_ESCAPE_RE, handle_powershell_job, handle_bash_job}, deno_executor::{handle_deno_job, generate_deno_lock}, + handle_flow, update_flow_status_after_job_completion, update_flow_status_in_progress, + }, }; #[cfg(feature = "enterprise")] use crate::{bigquery_executor::do_bigquery, snowflake_executor::do_snowflake}; -pub async fn create_token_for_owner_in_bg(db: &Pool, job: &QueuedJob) -> Arc> { +pub async fn create_token_for_owner_in_bg( + db: &Pool, + job: &QueuedJob, +) -> Arc> { let rw_lock = Arc::new(RwLock::new(String::new())); // skipping test runs if job.workspace_id != "" { @@ -76,7 +104,8 @@ pub async fn create_token_for_owner_in_bg(db: &Pool, job: &QueuedJob) *SCRIPT_TOKEN_EXPIRY, &job.email, ) - .await.expect("could not create job token"); + .await + .expect("could not create job token"); *locked = token; }); }; @@ -94,10 +123,12 @@ pub async fn create_token_for_owner( ) -> error::Result { // TODO: Bad implementation. We should not have access to this DB here. let token: String = rd_string(30); - let is_super_admin = sqlx::query_scalar!("SELECT super_admin FROM password WHERE email = $1", email) + let is_super_admin = + sqlx::query_scalar!("SELECT super_admin FROM password WHERE email = $1", email) .fetch_optional(db) .await? - .unwrap_or(false) || email == SUPERADMIN_SECRET_EMAIL; + .unwrap_or(false) + || email == SUPERADMIN_SECRET_EMAIL; sqlx::query_scalar!( "INSERT INTO token @@ -130,7 +161,6 @@ pub const BUN_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "bun"); pub const HUB_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "hub"); pub const GO_BIN_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "gobin"); - pub const TAR_PIP_TMP_CACHE_DIR: &str = concatcp!(ROOT_TMP_CACHE_DIR, "tar/pip"); pub const DENO_TMP_CACHE_DIR: &str = concatcp!(ROOT_TMP_CACHE_DIR, "deno"); pub const BUN_TMP_CACHE_DIR: &str = concatcp!(ROOT_TMP_CACHE_DIR, "bun"); @@ -140,10 +170,8 @@ pub const DENO_TMP_CACHE_DIR_DEPS: &str = concatcp!(ROOT_TMP_CACHE_DIR, "deno/de pub const DENO_TMP_CACHE_DIR_NPM: &str = concatcp!(ROOT_TMP_CACHE_DIR, "deno/npm"); const NUM_SECS_PING: u64 = 5; - const INCLUDE_DEPS_PY_SH_CONTENT: &str = include_str!("../nsjail/download_deps.py.sh"); - pub const DEFAULT_CLOUD_TIMEOUT: u64 = 900; pub const DEFAULT_SELFHOSTED_TIMEOUT: u64 = 604800; // 7 days pub const DEFAULT_SLEEP_QUEUE: u64 = 50; @@ -151,6 +179,8 @@ pub const DEFAULT_SLEEP_QUEUE: u64 = 50; // only 1 native job so that we don't have to worry about concurrency issues on non dedicated native jobs workers pub const DEFAULT_NATIVE_JOBS: usize = 1; +const VACUUM_PERIOD: u32 = 10000; + lazy_static::lazy_static! { static ref SLEEP_QUEUE: u64 = std::env::var("SLEEP_QUEUE") @@ -248,7 +278,7 @@ pub struct AuthedClientBackgroundTask { pub base_internal_url: String, pub workspace: String, pub token: Arc>, - pub client: OnceCell + pub client: OnceCell, } impl AuthedClientBackgroundTask { @@ -257,8 +287,8 @@ impl AuthedClientBackgroundTask { base_internal_url: self.base_internal_url.clone(), workspace: self.workspace.clone(), token: self.get_token().await, - client: self.client.clone() - } + client: self.client.clone(), + }; } pub async fn get_token(&self) -> String { return self.token.read().await.clone(); @@ -269,7 +299,7 @@ pub struct AuthedClient { pub base_internal_url: String, pub workspace: String, pub token: String, - pub client: OnceCell + pub client: OnceCell, } impl AuthedClient { @@ -280,6 +310,23 @@ impl AuthedClient { } } +#[cfg(feature = "benchmark")] +#[derive(Serialize)] +struct BenchmarkInfo { + iters: u64, + timings: Vec>, +} + +#[macro_export] +macro_rules! add_time { + ($x:expr, $y:expr, $z:expr) => { + #[cfg(feature = "benchmark")] + { + $x.push($y.elapsed().as_nanos() as u32); + // println!("{}: {:?}", $z, $y.elapsed()); + } + }; +} pub async fn run_worker( db: &Pool, @@ -307,7 +354,9 @@ pub async fn run_worker = all_langs.clone().into_iter().map(|x| (x.clone(), prometheus::register_histogram!( - prometheus::HistogramOpts::new( - "worker_execution_duration", - "Duration between receiving a job and completing it", - ) - .const_label("name", &worker_name) - .const_label("language", x.map(|x| x.as_str()).unwrap_or("none"))) - .expect("register prometheus metric")) - ).collect(); - - - let worker_execution_duration_counter: HashMap<_, _> = all_langs.clone().into_iter().map(|x| (x.clone(), prometheus::register_counter!( - prometheus::Opts::new( - "worker_execution_duration_counter", - "Total number of seconds spent executing jobs" - ) - .const_label("name", &worker_name) - .const_label("language", x.map(|x| x.as_str()).unwrap_or("none"))) - .expect("register prometheus metric")) - ).collect(); + let worker_execution_duration: HashMap<_, _> = all_langs + .clone() + .into_iter() + .map(|x| { + ( + x.clone(), + prometheus::register_histogram!(prometheus::HistogramOpts::new( + "worker_execution_duration", + "Duration between receiving a job and completing it", + ) + .const_label("name", &worker_name) + .const_label("language", x.map(|x| x.as_str()).unwrap_or("none"))) + .expect("register prometheus metric"), + ) + }) + .collect(); + let worker_execution_duration_counter: HashMap<_, _> = all_langs + .clone() + .into_iter() + .map(|x| { + ( + x.clone(), + prometheus::register_counter!(prometheus::Opts::new( + "worker_execution_duration_counter", + "Total number of seconds spent executing jobs" + ) + .const_label("name", &worker_name) + .const_label("language", x.map(|x| x.as_str()).unwrap_or("none"))) + .expect("register prometheus metric"), + ) + }) + .collect(); let worker_sleep_duration_counter = prometheus::register_counter!(prometheus::opts!( "worker_sleep_duration_counter", "Total number of seconds spent sleeping between pulling jobs from the queue" ) - .const_label("name", &worker_name)) - .expect("register prometheus metric"); - + .const_label("name", &worker_name)) + .expect("register prometheus metric"); let worker_pull_duration = prometheus::register_histogram!(prometheus::HistogramOpts::new( "worker_pull_duration", @@ -389,27 +448,41 @@ pub async fn run_worker = all_langs.clone().into_iter().map(|x| (x.clone(), prometheus::register_int_counter!( - prometheus::Opts::new( - "worker_execution_failed", "Number of failed jobs", - ) - .const_label("name", &worker_name) - .const_label("language", x.map(|x| x.as_str()).unwrap_or("none"))) - .expect("register prometheus metric")) - ).collect(); + let worker_execution_failed: HashMap<_, _> = all_langs + .clone() + .into_iter() + .map(|x| { + ( + x.clone(), + prometheus::register_int_counter!(prometheus::Opts::new( + "worker_execution_failed", + "Number of failed jobs", + ) + .const_label("name", &worker_name) + .const_label("language", x.map(|x| x.as_str()).unwrap_or("none"))) + .expect("register prometheus metric"), + ) + }) + .collect(); - - let worker_execution_count: HashMap<_, _> = all_langs.into_iter().map(|x| (x.clone(), prometheus::register_int_counter!( - prometheus::Opts::new( - "worker_execution_count", "Number of executed jobs" - ) - .const_label("name", &worker_name) - .const_label("language", x.map(|x| x.as_str()).unwrap_or("none"))) - .expect("register prometheus metric")) - ).collect(); + let worker_execution_count: HashMap<_, _> = all_langs + .into_iter() + .map(|x| { + ( + x.clone(), + prometheus::register_int_counter!(prometheus::Opts::new( + "worker_execution_count", + "Number of executed jobs" + ) + .const_label("name", &worker_name) + .const_label("language", x.map(|x| x.as_str()).unwrap_or("none"))) + .expect("register prometheus metric"), + ) + }) + .collect(); let worker_busy: prometheus::IntGauge = prometheus::register_int_gauge!(prometheus::Opts::new( "worker_busy", @@ -424,7 +497,6 @@ pub async fn run_worker(2); #[cfg(feature = "enterprise")] @@ -433,8 +505,8 @@ pub async fn run_worker(5); - let (job_completed_tx, mut job_completed_rx) = mpsc::channel::(1000); + let (job_completed_tx, mut job_completed_rx) = mpsc::channel::(100); let db2 = db.clone(); let rsmq2 = rsmq.clone(); let worker_name2 = worker_name.clone(); let send_result = tokio::spawn(async move { - while let Some(JobCompleted { job, logs, result, success}) = job_completed_rx.recv().await { - if let Err(e) = add_completed_job(&db2, &job, success, false, result, logs, rsmq2.clone()).await { + while let Some(JobCompleted { job, logs, result, success }) = job_completed_rx.recv().await + { + if let Err(e) = + add_completed_job(&db2, &job, success, false, result, logs, rsmq2.clone()).await + { tracing::error!(worker = %worker_name2, "failed to add completed job: {}", e); } } }); - + tracing::info!(worker = %worker_name, "listening for jobs"); - + let mut first_run = true; let mut last_executed_job: Option = None; let mut last_checked_suspended = Instant::now(); + + #[cfg(feature = "benchmark")] + let mut started = false; + + #[cfg(feature = "benchmark")] + let mut infos = BenchmarkInfo { iters: 0, timings: vec![] }; + + let vacuum_shift = rand::thread_rng().gen_range(0..VACUUM_PERIOD); loop { - // let instant: Instant = Instant::now(); + #[cfg(feature = "benchmark")] + let loop_start = Instant::now(); + + #[cfg(feature = "benchmark")] + let mut timing = vec![]; + if *METRICS_ENABLED { worker_busy.set(0); uptime_metric.inc_by( - ((start_time.elapsed().as_millis() as f64)/1000.0 - uptime_metric.get()) + ((start_time.elapsed().as_millis() as f64) / 1000.0 - uptime_metric.get()) .try_into() .unwrap(), ); @@ -492,151 +580,182 @@ pub async fn run_worker NUM_SECS_PING { - sqlx::query!( - "UPDATE worker_ping SET ping_at = now(), jobs_executed = $1 WHERE worker = $2", - jobs_executed, - &worker_name - ) - .execute(db) - .await - .expect("update worker ping"); + if last_ping.elapsed().as_secs() > NUM_SECS_PING { + sqlx::query!( + "UPDATE worker_ping SET ping_at = now(), jobs_executed = $1 WHERE worker = $2", + jobs_executed, + &worker_name + ) + .execute(db) + .await + .expect("update worker ping"); - last_ping = Instant::now(); + last_ping = Instant::now(); + } + + if (jobs_executed as u32 + vacuum_shift) % VACUUM_PERIOD == 0 { + if let Err(e) = sqlx::query!("VACUUM queue").execute(db).await { + tracing::error!(worker = %worker_name, "failed to vacuum queue: {}", e); } + tracing::info!(worker = %worker_name, "vacuumed queue and completed_job"); + } - #[cfg(feature = "enterprise")] - if i_worker == 1 && S3_CACHE_BUCKET.is_some() { - if last_sync.elapsed().as_secs() > *GLOBAL_CACHE_INTERVAL && - (copy_cache_from_bucket_handle.is_none() || copy_cache_from_bucket_handle.as_ref().unwrap().is_finished()) { - last_sync = Instant::now(); + #[cfg(feature = "enterprise")] + if i_worker == 1 && S3_CACHE_BUCKET.is_some() { + if last_sync.elapsed().as_secs() > *GLOBAL_CACHE_INTERVAL + && (copy_cache_from_bucket_handle.is_none() + || copy_cache_from_bucket_handle + .as_ref() + .unwrap() + .is_finished()) + { + last_sync = Instant::now(); - if crate::global_cache::worker_s3_bucket_sync_enabled(&db).await { + if crate::global_cache::worker_s3_bucket_sync_enabled(&db).await { + tracing::debug!("CAN PULL LOCK START"); + let _lock = CAN_PULL.write().await; - tracing::debug!("CAN PULL LOCK START"); - let _lock = CAN_PULL.write().await; - - tracing::info!("Started syncing cache"); - // if num_workers > 1 { - // create_barrier_for_all_workers(num_workers, sync_barrier.clone()).await; - // } - if let Err(e) = copy_cache_to_tmp_cache().await { - tracing::error!("failed to copy cache to tmp cache: {}", e); - } else { - copy_cache_from_bucket_handle = Some(tokio::task::spawn(async move { - if let Some(ref s) = S3_CACHE_BUCKET.clone() { - if let Err(e) = cache_global(s, copy_tx).await { - tracing::error!("failed to sync cache: {}", e); - } - } - })); + tracing::info!("Started syncing cache"); + // if num_workers > 1 { + // create_barrier_for_all_workers(num_workers, sync_barrier.clone()).await; + // } + if let Err(e) = copy_cache_to_tmp_cache().await { + tracing::error!("failed to copy cache to tmp cache: {}", e); + } else { + copy_cache_from_bucket_handle = Some(tokio::task::spawn(async move { + if let Some(ref s) = S3_CACHE_BUCKET.clone() { + if let Err(e) = cache_global(s, copy_tx).await { + tracing::error!("failed to sync cache: {}", e); + } } - } + })); + } } } + } - // // The barrier is to avoid the sync to bucket syncing partial folders - // #[cfg(feature = "enterprise")] - // if num_workers > 1 && S3_CACHE_BUCKET.is_some() { - // let read_barrier = sync_barrier.read().await; - // if let Some(b) = read_barrier.as_ref() { - // tracing::debug!("worker #{i_worker} waiting for barrier"); - // b.wait().await; - // tracing::debug!("worker #{i_worker} done waiting for barrier"); - // drop(read_barrier); - // // wait for barrier to be reset - // let _ = CAN_PULL.read().await; - // tracing::debug!("worker #{i_worker} done waiting for lock"); - // } else { - // tracing::debug!("worker #{i_worker} no barrier"); - // }; - // } - - let (do_break, next_job) = if first_run { - (false, Ok(Some(QueuedJob::default()))) + // // The barrier is to avoid the sync to bucket syncing partial folders + // #[cfg(feature = "enterprise")] + // if num_workers > 1 && S3_CACHE_BUCKET.is_some() { + // let read_barrier = sync_barrier.read().await; + // if let Some(b) = read_barrier.as_ref() { + // tracing::debug!("worker #{i_worker} waiting for barrier"); + // b.wait().await; + // tracing::debug!("worker #{i_worker} done waiting for barrier"); + // drop(read_barrier); + // // wait for barrier to be reset + // let _ = CAN_PULL.read().await; + // tracing::debug!("worker #{i_worker} done waiting for lock"); + // } else { + // tracing::debug!("worker #{i_worker} no barrier"); + // }; + // } + + let next_job = if first_run { + Ok(Some(QueuedJob::default())) + } else { + // println!("2: {:?}", instant.elapsed()); + let _wait_signal = false; + + #[cfg(feature = "benchmark")] + let _wait_signal = IDLE_WORKERS.load(Ordering::Relaxed); + + if _wait_signal { + // tracing::warn!("Worker is marked as idle. Not pulling any job for now"); + tokio::time::sleep(Duration::from_millis(*SLEEP_QUEUE)).await; + Ok(None) } else { - // println!("2: {:?}", instant.elapsed()); - async { - if IDLE_WORKERS.load(Ordering::Relaxed) { - // TODO: Need to sleep for a little time before re-checking, maybe? - // tracing::warn!("Worker is marked as idle. Not pulling any job for now"); - return (false, Ok(None)); - } - tokio::select! { - biased; - _ = rx.recv() => { - #[cfg(feature = "enterprise")] - if let Some(copy_cache_from_bucket_handle) = copy_cache_from_bucket_handle.as_ref() { - if !copy_cache_from_bucket_handle.is_finished() { - copy_cache_from_bucket_handle.abort(); - } - } - #[cfg(feature = "enterprise")] - for handle in &handles { - if !handle.is_finished() { - handle.abort(); - } - } - println!("received killpill for worker {}", i_worker); - (true, Ok(None)) - }, - _ = copy_to_bucket_rx.recv() => { - tracing::debug!("can_pull lock start"); - let _lock = CAN_PULL.write().await; - // if num_workers > 1 { - // create_barrier_for_all_workers(num_workers, sync_barrier.clone()).await; - // } - //Arc::new(tokio::sync::Barrier::new(num_workers as usize + 1)); - #[cfg(feature = "enterprise")] - if let Err(e) = copy_tmp_cache_to_cache().await { - tracing::error!(worker = %worker_name, "failed to sync tmp cache to cache: {}", e); - } - tracing::debug!("can_pull lock end"); - (false, Ok(None)) - }, - Some(job_id) = same_worker_rx.recv() => { - (false, sqlx::query_as::<_, QueuedJob>("SELECT * FROM queue WHERE id = $1") - .bind(job_id) - .fetch_optional(db) - .await - .map_err(|_| Error::InternalErr("Impossible to fetch same_worker job".to_string()))) - }, - (job, timer) = { - let timer = if *METRICS_ENABLED { Some(worker_pull_duration.start_timer()) } else { None }; - let suspend_first = if last_checked_suspended.elapsed().as_secs() > 5 { - last_checked_suspended = Instant::now(); - true - } else { false }; - pull(&db, rsmq.clone(), suspend_first).map(|x| (x, timer)) - } => { - timer.map(|timer| { - let duration_pull_s = timer.stop_and_record(); - worker_pull_duration_counter.inc_by(duration_pull_s); - }); - // println!("Pull: {:?}", instant.elapsed()); - (false, job) - }, - } - }.await - }; + #[cfg(feature = "benchmark")] + if !started { + started = true + } - first_run = false; - IS_READY.store(true, Ordering::Relaxed); + tokio::select! { + biased; + _ = rx.recv() => { + #[cfg(feature = "enterprise")] + if let Some(copy_cache_from_bucket_handle) = copy_cache_from_bucket_handle.as_ref() { + if !copy_cache_from_bucket_handle.is_finished() { + copy_cache_from_bucket_handle.abort(); + } + } + #[cfg(feature = "enterprise")] + for handle in &handles { + if !handle.is_finished() { + handle.abort(); + } + } + println!("received killpill for worker {}", i_worker); + break + }, + _ = copy_to_bucket_rx.recv() => { + tracing::debug!("can_pull lock start"); + let _lock = CAN_PULL.write().await; + // if num_workers > 1 { + // create_barrier_for_all_workers(num_workers, sync_barrier.clone()).await; + // } + //Arc::new(tokio::sync::Barrier::new(num_workers as usize + 1)); + #[cfg(feature = "enterprise")] + if let Err(e) = copy_tmp_cache_to_cache().await { + tracing::error!(worker = %worker_name, "failed to sync tmp cache to cache: {}", e); + } + tracing::debug!("can_pull lock end"); + Ok(None) + }, + Some(job_id) = same_worker_rx.recv() => { + sqlx::query_as::<_, QueuedJob>("SELECT * FROM queue WHERE id = $1") + .bind(job_id) + .fetch_optional(db) + .await + .map_err(|_| Error::InternalErr("Impossible to fetch same_worker job".to_string())) + }, + (job, timer) = { + let timer = if *METRICS_ENABLED { Some(worker_pull_duration.start_timer()) } else { None }; + let suspend_first = if last_checked_suspended.elapsed().as_secs() > 3 { + last_checked_suspended = Instant::now(); + true + } else { false }; + pull(&db, rsmq.clone(), suspend_first).map(|x| (x, timer)) + } => { + add_time!(timing, loop_start, "post pull"); - if do_break { - return true; + timer.map(|timer| { + let duration_pull_s = timer.stop_and_record(); + worker_pull_duration_counter.inc_by(duration_pull_s); + }); + job + + }, + } } - if *METRICS_ENABLED { - worker_busy.set(1); - } - match next_job { - Ok(Some(job)) => { - last_executed_job = None; - if matches!(job.job_kind, JobKind::Noop) { - job_completed_tx.send(JobCompleted { job, success: true, result: json!({}), logs: String::new()}).await.expect("send job completed"); - return false - } + }; + + first_run = false; + + if IS_READY.load(Ordering::Relaxed) { + IS_READY.store(false, Ordering::Relaxed); + } + + if *METRICS_ENABLED { + worker_busy.set(1); + } + + match next_job { + Ok(Some(job)) => { + last_executed_job = None; + jobs_executed += 1; + + if matches!(job.job_kind, JobKind::Noop) { + job_completed_tx + .send(JobCompleted { + job, + success: true, + result: json!({}), + logs: String::new(), + }) + .await + .expect("send job completed"); + } else { let token = create_token_for_owner_in_bg(&db, &job).await; let language = job.language.clone(); let _timer = worker_execution_duration @@ -644,7 +763,6 @@ pub async fn run_worker { - if let Some(secs) = *EXIT_AFTER_NO_JOB_FOR_SECS { - if let Some(lj) = last_executed_job { - if lj.elapsed().as_secs() > secs { - tracing::info!(worker = %worker_name, "no job for {} seconds, exiting", secs); - return true; - } - } else { - last_executed_job = Some(Instant::now()); - } + #[cfg(feature = "benchmark")] + { + if started { + add_time!(timing, loop_start, format!("post iter: {}", infos.iters)); + infos.iters += 1; + infos.timings.push(timing); } - let _timer = if *METRICS_ENABLED { Some(Instant::now()) } else { None }; - tokio::time::sleep(Duration::from_millis(*SLEEP_QUEUE)).await; - _timer.map(|timer| { - let duration = timer.elapsed().as_secs_f64(); - worker_sleep_duration_counter.inc_by(duration); - }); } - Err(err) => { - tracing::error!(worker = %worker_name, "Failed to pull jobs: {}", err); + } + Ok(None) => { + if let Some(secs) = *EXIT_AFTER_NO_JOB_FOR_SECS { + if let Some(lj) = last_executed_job { + if lj.elapsed().as_secs() > secs { + tracing::info!(worker = %worker_name, "no job for {} seconds, exiting", secs); + break; + } + } else { + last_executed_job = Some(Instant::now()); + } } - }; + let _timer = if *METRICS_ENABLED { + Some(Instant::now()) + } else { + None + }; - false - } - .instrument(trace_span!("worker_loop_iteration")) - .await; - if do_break { - break; - } + #[cfg(feature = "benchmark")] + tracing::info!("no job found"); + + tokio::time::sleep(Duration::from_millis(*SLEEP_QUEUE)).await; + _timer.map(|timer| { + let duration = timer.elapsed().as_secs_f64(); + worker_sleep_duration_counter.inc_by(duration); + }); + } + Err(err) => { + tracing::error!(worker = %worker_name, "Failed to pull jobs: {}", err); + } + }; } + // #[cfg(feature = "benchmark")] + // { + // println!("Writing benchmark file"); + // write_file( + // TMP_DIR, + // "/profiling.json", + // &serde_json::to_string(&infos).unwrap(), + // ) + // .await + // .expect("write profiling"); + // } + drop(job_completed_tx); - send_result.await.expect("send result failed"); - + println!("worker {} exited", i_worker); } // pub async fn create_barrier_for_all_workers(num_workers: u32, sync_barrier: Arc>>) { @@ -826,13 +977,17 @@ pub async fn handle_job_error serde_json::Value { return json!({"message": format!("ExitCode: {i}, last log lines:\n{}", ANSI_ESCAPE_RE.replace_all(log_lines.trim(), "").to_string()), "name": "ExecutionErr"}); } @@ -911,29 +1065,37 @@ pub struct JobCompleted { pub job: QueuedJob, pub result: serde_json::Value, pub logs: String, - pub success: bool + pub success: bool, } - - pub async fn get_content(job: &QueuedJob, db: &Pool) -> Result { let query = match job.job_kind { - JobKind::Preview => job.raw_code.clone().ok_or_else(|| Error::ExecutionErr("Missing code".to_string()))?, - JobKind::Script => sqlx::query_scalar( - "SELECT content FROM script WHERE hash = $1 AND workspace_id = $2", - ) - .bind(&job.script_hash.unwrap_or(ScriptHash(0)).0) - .bind(&job.workspace_id) - .fetch_optional(db) - .await? - .ok_or_else(|| Error::InternalErr(format!("expected content")))?, - _ => unreachable!("get_content called for non-script job kind: {:#?}", job.job_kind), + JobKind::Preview => job + .raw_code + .clone() + .ok_or_else(|| Error::ExecutionErr("Missing code".to_string()))?, + JobKind::Script => { + sqlx::query_scalar("SELECT content FROM script WHERE hash = $1 AND workspace_id = $2") + .bind(&job.script_hash.unwrap_or(ScriptHash(0)).0) + .bind(&job.workspace_id) + .fetch_optional(db) + .await? + .ok_or_else(|| Error::InternalErr(format!("expected content")))? + } + _ => unreachable!( + "get_content called for non-script job kind: {:#?}", + job.job_kind + ), }; Ok(query) } - -async fn do_nativets(job: QueuedJob, logs: String, client: &AuthedClient, code: String) -> windmill_common::error::Result { +async fn do_nativets( + job: QueuedJob, + logs: String, + client: &AuthedClient, + code: String, +) -> windmill_common::error::Result { let args = if let Some(args) = &job.args { Some(transform_json_value("args", client, &job.workspace_id, args.clone()).await?) } else { @@ -952,11 +1114,10 @@ async fn do_nativets(job: QueuedJob, logs: String, client: &AuthedClient, code: job: job, result: result.0, logs: [logs, result.1].join("\n\n"), - success: true + success: true, }); } - #[tracing::instrument(level = "trace", skip_all)] async fn handle_queued_job( job: QueuedJob, @@ -994,7 +1155,7 @@ async fn handle_queued_job( }; let cached_res_path = if job.cache_ttl.is_some() { - let args_hash = hash_args(&job.args.clone().unwrap_or_else(|| json!({}))); + let args_hash = hash_args(&job.args.clone().unwrap_or_else(|| json!({}))); if job.is_flow_step { let flow_path = sqlx::query_scalar!( "SELECT script_path FROM queue WHERE id = $1", @@ -1012,7 +1173,6 @@ async fn handle_queued_job( } else { None } - } else { None }; @@ -1020,19 +1180,46 @@ async fn handle_queued_job( if let Some(cached_res_path) = cached_res_path.clone() { let authed_client = client.get_authed().await; let client: &Client = authed_client.get_client(); - let resource = client.get_resource_value(&job.workspace_id, &cached_res_path).await; + let resource = client + .get_resource_value(&job.workspace_id, &cached_res_path) + .await; if let Ok(resource) = resource { let v = resource.into_inner(); if let Some(o) = v.as_object() { let expire = o.get("expire"); - if expire.is_some() && expire.unwrap().as_i64().map(|x| x > chrono::Utc::now().timestamp()).unwrap_or(false) { - let result = v.get("value").map(|x| x.to_owned()).unwrap_or_else(|| json!({})); - let logs = "Job skipped because args & path found in cache and not expired".to_string(); - process_result(authed_client, job, Ok(result), None, db, worker_dir, job_dir, metrics, same_worker_tx, base_internal_url, rsmq, job_completed_tx, logs).await?; - return Ok(()) - } + if expire.is_some() + && expire + .unwrap() + .as_i64() + .map(|x| x > chrono::Utc::now().timestamp()) + .unwrap_or(false) + { + let result = v + .get("value") + .map(|x| x.to_owned()) + .unwrap_or_else(|| json!({})); + let logs = "Job skipped because args & path found in cache and not expired" + .to_string(); + process_result( + authed_client, + job, + Ok(result), + None, + db, + worker_dir, + job_dir, + metrics, + same_worker_tx, + base_internal_url, + rsmq, + job_completed_tx, + logs, + ) + .await?; + return Ok(()); + } } - } + } }; match job.job_kind { JobKind::FlowPreview | JobKind::Flow => { @@ -1057,7 +1244,10 @@ async fn handle_queued_job( logs.push_str("\n"); } - logs.push_str(&format!("job {} on worker {} (tag: {})\n", &job.id, &worker_name, &job.tag)); + logs.push_str(&format!( + "job {} on worker {} (tag: {})\n", + &job.id, &worker_name, &job.tag + )); set_logs(&logs, &job.id, db).await; @@ -1070,58 +1260,98 @@ async fn handle_queued_job( ); let result = match job.job_kind { - JobKind::Dependencies => { - handle_dependency_job(&job, &mut logs, job_dir, db, worker_name, worker_dir, base_internal_url, &client.get_token().await).await + JobKind::Dependencies => { + handle_dependency_job( + &job, + &mut logs, + job_dir, + db, + worker_name, + worker_dir, + base_internal_url, + &client.get_token().await, + ) + .await + } + JobKind::FlowDependencies => handle_flow_dependency_job( + &job, + &mut logs, + job_dir, + db, + worker_name, + worker_dir, + base_internal_url, + &client.get_token().await, + ) + .await + .map(|()| Value::Null), + JobKind::AppDependencies => handle_app_dependency_job( + &job, + &mut logs, + job_dir, + db, + worker_name, + worker_dir, + base_internal_url, + &client.get_token().await, + ) + .await + .map(|()| Value::Null), + JobKind::Identity => match job.args.clone() { + Some(Value::Object(args)) + if args.len() == 1 && args.contains_key("previous_result") => + { + Ok(args.get("previous_result").unwrap().clone()) } - JobKind::FlowDependencies => { - handle_flow_dependency_job(&job, &mut logs, job_dir, db, worker_name, worker_dir, base_internal_url, &client.get_token().await) - .await - .map(|()| Value::Null) - }, - JobKind::AppDependencies => { - handle_app_dependency_job(&job, &mut logs, job_dir, db, worker_name, worker_dir, base_internal_url, &client.get_token().await) - .await - .map(|()| Value::Null) - } - JobKind::Identity => match job.args.clone() { - Some(Value::Object(args)) - if args.len() == 1 && args.contains_key("previous_result") => - { - Ok(args.get("previous_result").unwrap().clone()) - } - args @ _ => Ok(args.unwrap_or_else(|| Value::Null)), - }, - _ => { - handle_code_execution_job( - &job, - db, - client, - job_dir, - worker_dir, - &mut logs, - base_internal_url, - worker_name ) - .await - } - }; + args @ _ => Ok(args.unwrap_or_else(|| Value::Null)), + }, + _ => { + handle_code_execution_job( + &job, + db, + client, + job_dir, + worker_dir, + &mut logs, + base_internal_url, + worker_name, + ) + .await + } + }; //it's a test job, no need to update the db if job.workspace_id == "" { return Ok(()); } let client = client.get_authed().await; - process_result(client, job, result, cached_res_path, db, worker_dir, job_dir, metrics, same_worker_tx, base_internal_url, rsmq, job_completed_tx, logs).await?; + process_result( + client, + job, + result, + cached_res_path, + db, + worker_dir, + job_dir, + metrics, + same_worker_tx, + base_internal_url, + rsmq, + job_completed_tx, + logs, + ) + .await?; } } Ok(()) } async fn process_result( - client: AuthedClient, - job: QueuedJob, + client: AuthedClient, + job: QueuedJob, result: error::Result, cached_res_path: Option, - db: &DB, + db: &DB, worker_dir: &str, job_dir: &str, metrics: Option, @@ -1138,7 +1368,6 @@ async fn process_result( save_in_cache(&client, &job, cached_path, &r).await; } if job.is_flow_step { - add_completed_job(db, &job, true, false, r.clone(), logs, rsmq.clone()).await?; if let Some(parent_job) = job.parent_job { update_flow_status_after_job_completion( @@ -1155,14 +1384,16 @@ async fn process_result( worker_dir, None, base_internal_url, - rsmq.clone() + rsmq.clone(), ) .await?; } } else { // in the happy path and if job not a flow step, we can delegate updating the completed job in the background - job_completed_tx.send(JobCompleted{job,result:r,logs:logs, success: true}).await.expect("send job completed"); - + job_completed_tx + .send(JobCompleted { job, result: r, logs: logs, success: true }) + .await + .expect("send job completed"); } } Err(e) => { @@ -1212,7 +1443,7 @@ async fn process_result( worker_dir, None, base_internal_url, - rsmq + rsmq, ) .await?; } @@ -1222,8 +1453,6 @@ async fn process_result( Ok(()) } - - #[tracing::instrument(level = "trace", skip_all)] async fn handle_code_execution_job( job: &QueuedJob, @@ -1233,7 +1462,8 @@ async fn handle_code_execution_job( worker_dir: &str, logs: &mut String, base_internal_url: &str, - worker_name: &str) -> error::Result { + worker_name: &str, +) -> error::Result { let (inner_content, requirements_o, language, envs) = match job.job_kind { JobKind::Preview => ( job.raw_code @@ -1280,41 +1510,49 @@ async fn handle_code_execution_job( if language == Some(ScriptLang::Postgresql) { let jc = do_postgresql(job.clone(), &client.get_authed().await, &inner_content).await?; - return Ok(jc.result) + return Ok(jc.result); } else if language == Some(ScriptLang::Mysql) { let jc = do_mysql(job.clone(), &client.get_authed().await, &inner_content).await?; - return Ok(jc.result) + return Ok(jc.result); } else if language == Some(ScriptLang::Bigquery) { #[cfg(not(feature = "enterprise"))] { - return Err(Error::ExecutionErr("Bigquery is only available with an enterprise license".to_string())) + return Err(Error::ExecutionErr( + "Bigquery is only available with an enterprise license".to_string(), + )); } #[cfg(feature = "enterprise")] { let jc = do_bigquery(job.clone(), &client.get_authed().await, &inner_content).await?; - return Ok(jc.result) + return Ok(jc.result); } } else if language == Some(ScriptLang::Snowflake) { #[cfg(not(feature = "enterprise"))] { - return Err(Error::ExecutionErr("Snowflake is only available with an enterprise license".to_string())) + return Err(Error::ExecutionErr( + "Snowflake is only available with an enterprise license".to_string(), + )); } #[cfg(feature = "enterprise")] { let jc = do_snowflake(job.clone(), &client.get_authed().await, &inner_content).await?; - return Ok(jc.result) + return Ok(jc.result); } } else if language == Some(ScriptLang::Graphql) { let jc = do_graphql(job.clone(), &client.get_authed().await, &inner_content).await?; - return Ok(jc.result) + return Ok(jc.result); } else if language == Some(ScriptLang::Nativets) { logs.push_str("\n--- FETCH TS EXECUTION ---\n"); - let code = format!("const BASE_URL = '{base_internal_url}';\nconst WM_TOKEN = '{}';\n{}", &client.get_token().await, inner_content); - let jc = do_nativets(job.clone(), logs.clone(), &client.get_authed().await, code).await?; + let code = format!( + "const BASE_URL = '{base_internal_url}';\nconst WM_TOKEN = '{}';\n{}", + &client.get_token().await, + inner_content + ); + let jc = do_nativets(job.clone(), logs.clone(), &client.get_authed().await, code).await?; *logs = jc.logs; - return Ok(jc.result) + return Ok(jc.result); } let lang_str = job @@ -1379,7 +1617,7 @@ mount {{ return Err(Error::ExecutionErr( "Require language to be not null".to_string(), ))?; - }, + } Some(ScriptLang::Python3) => { handle_python_job( requirements_o, @@ -1393,7 +1631,7 @@ mount {{ &inner_content, &shared_mount, base_internal_url, - envs + envs, ) .await } @@ -1408,7 +1646,7 @@ mount {{ &inner_content, base_internal_url, worker_name, - envs + envs, ) .await } @@ -1424,7 +1662,7 @@ mount {{ base_internal_url, worker_name, envs, - &shared_mount + &shared_mount, ) .await } @@ -1440,7 +1678,7 @@ mount {{ &shared_mount, base_internal_url, worker_name, - envs + envs, ) .await } @@ -1455,10 +1693,10 @@ mount {{ &shared_mount, base_internal_url, worker_name, - envs + envs, ) .await - }, + } Some(ScriptLang::Powershell) => { handle_powershell_job( logs, @@ -1470,7 +1708,7 @@ mount {{ &shared_mount, base_internal_url, worker_name, - envs + envs, ) .await } @@ -1490,9 +1728,6 @@ mount {{ result } - - - #[tracing::instrument(level = "trace", skip_all)] async fn handle_dependency_job( job: &QueuedJob, @@ -1574,7 +1809,19 @@ async fn handle_flow_dependency_job( })?; let mut flow = serde_json::from_value::(raw_flow).map_err(to_anyhow)?; - flow.modules = lock_modules(flow.modules, job, logs, job_dir, db, worker_name, worker_dir, &job_path, base_internal_url, token).await?; + flow.modules = lock_modules( + flow.modules, + job, + logs, + job_dir, + db, + worker_name, + worker_dir, + &job_path, + base_internal_url, + token, + ) + .await?; let new_flow_value = serde_json::to_value(flow).map_err(to_anyhow)?; // Re-check cancelation to ensure we don't accidentially override a flow. @@ -1603,7 +1850,7 @@ async fn handle_flow_dependency_job( #[async_recursion] async fn lock_modules( - modules: Vec, + modules: Vec, job: &QueuedJob, logs: &mut String, job_dir: &str, @@ -1612,41 +1859,117 @@ async fn lock_modules( worker_dir: &str, job_path: &str, base_internal_url: &str, - token: &str) -> Result> { + token: &str, +) -> Result> { let mut new_flow_modules = Vec::new(); for mut e in modules.into_iter() { - let FlowModuleValue::RawScript { lock: _, path, content, language, input_transforms, tag, concurrent_limit, concurrency_time_window_s} = e.value else { + let FlowModuleValue::RawScript { + lock: _, + path, + content, + language, + input_transforms, + tag, + concurrent_limit, + concurrency_time_window_s, + } = e.value + else { match e.value { - FlowModuleValue::ForloopFlow { iterator, modules, skip_failures, parallel, parallelism } => { - e.value = FlowModuleValue::ForloopFlow { iterator, modules: lock_modules(modules, job, logs, job_dir, db, worker_name, worker_dir, job_path.clone(), base_internal_url, token).await?, skip_failures, parallel, parallelism } - }, + FlowModuleValue::ForloopFlow { + iterator, + modules, + skip_failures, + parallel, + parallelism, + } => { + e.value = FlowModuleValue::ForloopFlow { + iterator, + modules: lock_modules( + modules, + job, + logs, + job_dir, + db, + worker_name, + worker_dir, + job_path.clone(), + base_internal_url, + token, + ) + .await?, + skip_failures, + parallel, + parallelism, + } + } FlowModuleValue::BranchAll { branches, parallel } => { let mut nbranches = vec![]; for mut b in branches { - b.modules = lock_modules(b.modules, job, logs, job_dir, db, worker_name, worker_dir, job_path.clone(), base_internal_url, token).await?; + b.modules = lock_modules( + b.modules, + job, + logs, + job_dir, + db, + worker_name, + worker_dir, + job_path.clone(), + base_internal_url, + token, + ) + .await?; nbranches.push(b) } e.value = FlowModuleValue::BranchAll { branches: nbranches, parallel } - }, + } FlowModuleValue::BranchOne { branches, default } => { let mut nbranches = vec![]; for mut b in branches { - b.modules = lock_modules(b.modules, job, logs, job_dir, db, worker_name, worker_dir, job_path.clone(), base_internal_url, token).await?; + b.modules = lock_modules( + b.modules, + job, + logs, + job_dir, + db, + worker_name, + worker_dir, + job_path.clone(), + base_internal_url, + token, + ) + .await?; nbranches.push(b) } - let default = lock_modules(default, job, logs, job_dir, db, worker_name, worker_dir, job_path.clone(), base_internal_url, token).await?; - e.value = FlowModuleValue::BranchOne { branches: nbranches, default}; - } - _ => { - () + let default = lock_modules( + default, + job, + logs, + job_dir, + db, + worker_name, + worker_dir, + job_path.clone(), + base_internal_url, + token, + ) + .await?; + e.value = FlowModuleValue::BranchOne { branches: nbranches, default }; } + _ => (), }; new_flow_modules.push(e); continue; }; // sync with windmill-api/scripts let dependencies = match language { - ScriptLang::Python3 => windmill_parser_py_imports::parse_python_imports(&content, &job.workspace_id, &path.clone().unwrap_or_else(|| job_path.to_string()), &db).await?.join("\n"), + ScriptLang::Python3 => windmill_parser_py_imports::parse_python_imports( + &content, + &job.workspace_id, + &path.clone().unwrap_or_else(|| job_path.to_string()), + &db, + ) + .await? + .join("\n"), _ => content.clone(), }; let new_lock = capture_dependency_job( @@ -1661,7 +1984,7 @@ async fn lock_modules( worker_dir, base_internal_url, token, - job.script_path() + job.script_path(), ) .await; match new_lock { @@ -1708,7 +2031,7 @@ async fn lock_modules( #[async_recursion] async fn lock_modules_app( - value: Value, + value: Value, job: &QueuedJob, logs: &mut String, job_dir: &str, @@ -1717,74 +2040,119 @@ async fn lock_modules_app( worker_dir: &str, job_path: &str, base_internal_url: &str, - token: &str) -> Result { + token: &str, +) -> Result { match value { Value::Object(mut m) => { if m.contains_key("inlineScript") { let v = m.get_mut("inlineScript").unwrap(); - if let Some(v) = v.as_object_mut() { + if let Some(v) = v.as_object_mut() { if v.contains_key("content") && v.contains_key("language") { - if let Ok(language) = serde_json::from_value::(v.get("language").unwrap().clone()) { - let content = v.get("content").unwrap().as_str().unwrap_or_default().to_string(); - let dependencies = match language { - ScriptLang::Python3 => windmill_parser_py_imports::parse_python_imports(&content, &job.workspace_id, job_path, &db).await?.join("\n"), - _ => content.clone(), - }; - logs.push_str("Found lockable inline script. Generating lock...\n"); - let new_lock = capture_dependency_job( - &job.id, - &language, - &dependencies, - logs, - job_dir, - db, - worker_name, - &job.workspace_id, - worker_dir, - base_internal_url, - token, - job.script_path() - ) - .await; - match new_lock { - Ok(new_lock) => { - v.insert("lock".to_string(), serde_json::Value::String(new_lock)); - return Ok(Value::Object(m.clone())) - } - Err(e) => { - tracing::warn!( - language = ?language, - error = ?e, - logs = ?logs, - "Failed to generate flow lock for inline script" - ); - () - } + if let Ok(language) = + serde_json::from_value::(v.get("language").unwrap().clone()) + { + let content = v + .get("content") + .unwrap() + .as_str() + .unwrap_or_default() + .to_string(); + let dependencies = match language { + ScriptLang::Python3 => { + windmill_parser_py_imports::parse_python_imports( + &content, + &job.workspace_id, + job_path, + &db, + ) + .await? + .join("\n") } + _ => content.clone(), + }; + logs.push_str("Found lockable inline script. Generating lock...\n"); + let new_lock = capture_dependency_job( + &job.id, + &language, + &dependencies, + logs, + job_dir, + db, + worker_name, + &job.workspace_id, + worker_dir, + base_internal_url, + token, + job.script_path(), + ) + .await; + match new_lock { + Ok(new_lock) => { + v.insert( + "lock".to_string(), + serde_json::Value::String(new_lock), + ); + return Ok(Value::Object(m.clone())); + } + Err(e) => { + tracing::warn!( + language = ?language, + error = ?e, + logs = ?logs, + "Failed to generate flow lock for inline script" + ); + () + } + } } } } - } + } for (a, b) in m.clone().into_iter() { m.insert( a.clone(), - lock_modules_app(b, job, logs, job_dir, db, worker_name, worker_dir, job_path, base_internal_url, token).await?, + lock_modules_app( + b, + job, + logs, + job_dir, + db, + worker_name, + worker_dir, + job_path, + base_internal_url, + token, + ) + .await?, ); } Ok(Value::Object(m)) - }, + } Value::Array(a) => { let mut nv = vec![]; for b in a.clone().into_iter() { - nv.push(lock_modules_app(b, job, logs, job_dir, db, worker_name, worker_dir, job_path, base_internal_url, token).await?); + nv.push( + lock_modules_app( + b, + job, + logs, + job_dir, + db, + worker_name, + worker_dir, + job_path, + base_internal_url, + token, + ) + .await?, + ); } Ok(Value::Array(nv)) - }, + } a @ _ => Ok(a), } } - async fn handle_app_dependency_job( job: &QueuedJob, logs: &mut String, @@ -1795,24 +2163,35 @@ async fn handle_app_dependency_job( base_internal_url: &str, token: &str, ) -> error::Result<()> { - let job_path = job.script_path.clone().ok_or_else(|| { error::Error::InternalErr( "Cannot resolve flow dependencies for flow without path".to_string(), ) })?; - let id = job.script_hash.clone().ok_or_else(|| { - Error::InternalErr( - "Flow Dependency requires script hash".to_owned(), - )})?.0; - let value = sqlx::query_scalar!("SELECT value FROM app_version WHERE id = $1", id) - .fetch_optional(db) - .await?; + let id = job + .script_hash + .clone() + .ok_or_else(|| Error::InternalErr("Flow Dependency requires script hash".to_owned()))? + .0; + let value = sqlx::query_scalar!("SELECT value FROM app_version WHERE id = $1", id) + .fetch_optional(db) + .await?; if let Some(value) = value { - let value = lock_modules_app(value, job, logs, job_dir, db, worker_name, worker_dir, &job_path, base_internal_url, token).await?; - + let value = lock_modules_app( + value, + job, + logs, + job_dir, + db, + worker_name, + worker_dir, + &job_path, + base_internal_url, + token, + ) + .await?; // Re-check cancelation to ensure we don't accidentially override a flow. if sqlx::query_scalar!("SELECT canceled FROM queue WHERE id = $1", job.id) @@ -1827,22 +2206,15 @@ async fn handle_app_dependency_job( return Ok(()); } - sqlx::query!( - "UPDATE app_version SET value = $1 WHERE id = $2", - value, - id, - ) - .execute(db) - .await?; + sqlx::query!("UPDATE app_version SET value = $1 WHERE id = $2", value, id,) + .execute(db) + .await?; Ok(()) } else { Ok(()) } } - - - async fn capture_dependency_job( job_id: &Uuid, job_language: &ScriptLang, @@ -1860,14 +2232,12 @@ async fn capture_dependency_job( match job_language { ScriptLang::Python3 => { create_dependencies_dir(job_dir).await; - let req: std::result::Result = pip_compile(job_id, job_raw_code, logs, job_dir, db, worker_name, w_id).await; + let req: std::result::Result = + pip_compile(job_id, job_raw_code, logs, job_dir, db, worker_name, w_id).await; // install the dependencies to pre-fill the cache if let Ok(req) = req.as_ref() { let r = handle_python_reqs( - req - .split("\n") - .filter(|x| !x.starts_with("--")) - .collect(), + req.split("\n").filter(|x| !x.starts_with("--")).collect(), job_id, w_id, logs, @@ -1877,9 +2247,13 @@ async fn capture_dependency_job( worker_dir, ) .await; - + if let Err(e) = r { - tracing::error!("Failed to install python dependencies to prefill the cache: {:?} \n{}", e, logs); + tracing::error!( + "Failed to install python dependencies to prefill the cache: {:?} \n{}", + e, + logs + ); } } req @@ -1895,13 +2269,23 @@ async fn capture_dependency_job( false, false, worker_name, - w_id + w_id, ) .await } ScriptLang::Deno => { - generate_deno_lock(job_id, job_raw_code, logs, job_dir, db, w_id, worker_name, base_internal_url).await - }, + generate_deno_lock( + job_id, + job_raw_code, + logs, + job_dir, + db, + w_id, + worker_name, + base_internal_url, + ) + .await + } ScriptLang::Bun => { let _ = write_file(job_dir, "main.ts", job_raw_code).await?; let req = gen_lockfile( @@ -1918,7 +2302,7 @@ async fn capture_dependency_job( ) .await?; Ok(req.unwrap_or_else(String::new)) - }, + } ScriptLang::Postgresql => Ok("".to_owned()), ScriptLang::Mysql => Ok("".to_owned()), ScriptLang::Bigquery => Ok("".to_owned()), @@ -1927,7 +2311,5 @@ async fn capture_dependency_job( ScriptLang::Bash => Ok("".to_owned()), ScriptLang::Powershell => Ok("".to_owned()), ScriptLang::Nativets => Ok("".to_owned()), - } } - diff --git a/benchmarks/benchmark_noop.ts b/benchmarks/benchmark_noop.ts index 2a7334f476..47bbc9baef 100644 --- a/benchmarks/benchmark_noop.ts +++ b/benchmarks/benchmark_noop.ts @@ -48,26 +48,16 @@ await new Command() "The workspace to spawn scripts from.", { default: "admins" } ) - .option( - "-j --jobs ", - "Number of NOOP jobs to create.", - { default: 10000 } - ) + .option("-j --jobs ", "Number of NOOP jobs to create.", { + default: 10000, + }) .option( "-b --batches ", "Number of batches to create all the jobs.", { default: 1 } ) .action( - async ({ - host, - email, - password, - token, - workspace, - jobs, - batches, - }) => { + async ({ host, email, password, token, workspace, jobs, batches }) => { windmill.setClient("", host); console.log( @@ -105,46 +95,54 @@ await new Command() windmill.setClient(final_token, host); const enc = (s: string) => new TextEncoder().encode(s); - console.log("Disabling workers before loading jobs") + console.log("Disabling workers before loading jobs"); const disable_workers = await fetch( config.server + "/api/workers/toggle?disable=true", { method: "GET", headers: { ["Authorization"]: "Bearer " + config.token }, } - ) + ); if (!disable_workers.ok) { - console.error("Unable to disable workers. Is the Windmill server running in benchmark mode?") + console.error( + "Unable to disable workers. Is the Windmill server running in benchmark mode?" + ); } const jobsSent = jobs; const batch_num = batches; - console.log(`Bulk creating ${jobsSent} jobs in ${batch_num} batches`) + console.log(`Bulk creating ${jobsSent} jobs in ${batch_num} batches`); - const start_create = Date.now() - const all_create_operations = [] + const start_create = Date.now(); + const all_create_operations = []; for (let i = 0; i < batch_num; i++) { - all_create_operations.push(fetch( - config.server + - "/api/w/" + - config.workspace_id + - `/jobs/add_noop_jobs/${jobsSent / batch_num}`, - { - method: "POST", - headers: { ["Authorization"]: "Bearer " + config.token }, - } - )); + all_create_operations.push( + fetch( + config.server + + "/api/w/" + + config.workspace_id + + `/jobs/add_noop_jobs/${jobsSent / batch_num}`, + { + method: "POST", + headers: { ["Authorization"]: "Bearer " + config.token }, + } + ) + ); } - await Promise.all(all_create_operations) + await Promise.all(all_create_operations); - const end_create = Date.now() - const create_duration = end_create - start_create - console.log(`Jobs successfully added to the queue in ${create_duration}s. Windmill will start pulling them\n`) - const start = Date.now() + const end_create = Date.now(); + const create_duration = end_create - start_create; + console.log( + `Jobs successfully added to the queue in ${create_duration}s. Windmill will start pulling them\n` + ); + const start = Date.now(); - let queue_length = jobsSent + let queue_length = jobsSent; + let lastElapsed = 0; + let lastQueueLength = queue_length; const updateState = setInterval(async () => { - const elapsed = start ? Math.ceil((Date.now() - start) / 1000) : 0; + const elapsed = start ? Date.now() - start : 0; queue_length = ( await ( await fetch( @@ -153,27 +151,41 @@ await new Command() ) ).json() ).database_length; + const avgThr = (((jobsSent - queue_length) / elapsed) * 1000).toFixed( + 2 + ); + const instThr = + lastElapsed > 0 + ? ( + ((lastQueueLength - queue_length) / (elapsed - lastElapsed)) * + 1000 + ).toFixed(2) + : 0; + + lastElapsed = elapsed; + lastQueueLength = queue_length; + await Deno.stdout.write( enc( - `elapsed: ${elapsed} | jobs executed: ${JSON.stringify( + `elapsed: ${(elapsed / 1000).toFixed(2)} | jobs executed: ${ jobsSent - queue_length - )}/${jobsSent} (thr: ${((jobsSent - queue_length) / elapsed).toFixed( - 2 - )}) | queue: ${queue_length} \r` + }/${jobsSent} (thr: inst ${instThr} - avg ${avgThr}) | queue: ${queue_length} \r` ) ); }, 100); - console.log("Enabling workers to start processing jobs") + console.log("Enabling workers to start processing jobs"); const enable_workers = await fetch( config.server + "/api/workers/toggle?disable=false", { method: "GET", headers: { ["Authorization"]: "Bearer " + config.token }, } - ) + ); if (!enable_workers.ok) { - console.error("Unable to disable workers. Is the Windmill server running in benchmark mode?") + console.error( + "Unable to disable workers. Is the Windmill server running in benchmark mode?" + ); } while (queue_length > 0) { @@ -182,10 +194,12 @@ await new Command() clearInterval(updateState); - const total_duration_sec = (Date.now() - start) / 1000; + const total_duration_sec = (Date.now() - start) / 1000.0; console.log(`jobs: ${jobsSent}`); console.log(`duration: ${total_duration_sec}s`); - console.log(`avg. throughput (jobs/time): ${jobsSent / total_duration_sec}`); + console.log( + `avg. throughput (jobs/time): ${jobsSent / total_duration_sec}` + ); console.log( "queue length:", diff --git a/benchmarks/main.ts b/benchmarks/main.ts index ea869fcdcd..29da3d5d79 100644 --- a/benchmarks/main.ts +++ b/benchmarks/main.ts @@ -3,7 +3,7 @@ import { Command } from "https://deno.land/x/cliffy@v0.25.7/command/mod.ts"; import { sleep } from "https://deno.land/x/sleep@v1.2.1/mod.ts"; -import * as windmill from "https://deno.land/x/windmill@v1.38.5/mod.ts"; +import * as windmill from "https://deno.land/x/windmill@v1.145.0/mod.ts"; import { Action } from "./action.ts"; import { UpgradeCommand } from "https://deno.land/x/cliffy@v0.25.7/command/upgrade/upgrade_command.ts"; import { DenoLandProvider } from "https://deno.land/x/cliffy@v0.25.7/command/upgrade/mod.ts"; @@ -157,6 +157,7 @@ await new Command() custom, }) => { windmill.setClient("", host); + console.log("Backend version: " + (await fetch(`${host}/api/version`))); const custom_content: Action | undefined = custom ? JSON.parse(await Deno.readTextFile(custom)) @@ -212,6 +213,7 @@ await new Command() flowPattern, scriptPattern, zombieTimeout, + continous, }, null, 4 @@ -227,7 +229,9 @@ await new Command() let final_token: string; if (!token) { if (email && password) { + console.log("Logging in with email and password..."); final_token = await login(email, password); + console.log("Logged in!"); } else { console.error("Token or email with password are required."); return; @@ -236,6 +240,8 @@ await new Command() final_token = token; } + console.log("Using token", final_token); + config.token = final_token; windmill.setClient(final_token, host); diff --git a/benchmarks/plot.py b/benchmarks/plot.py new file mode 100644 index 0000000000..4dc50848a8 --- /dev/null +++ b/benchmarks/plot.py @@ -0,0 +1,21 @@ +import matplotlib.pyplot as plt +import json + +with open("/tmp/windmill/profiling.json") as json_file: + data = json.load(json_file) + i = 0 + for impacts in data["timings"][:]: + i += 1 + if i % 200 != 0: + continue + timefilteredForce = plt.plot(impacts) + plt.text(len(impacts) - 1, impacts[-1], f"{i}") + timefilteredForce = plt.xlabel("points") + timefilteredForce = plt.ylabel("Force fast") + + # for impacts in data["timings"][0:100]: + # timefilteredForce = plt.plot(impacts) + # timefilteredForce = plt.xlabel("points") + # timefilteredForce = plt.ylabel("Force slow") + + plt.show() diff --git a/benchmarks/pulumi/.gitignore b/benchmarks/pulumi/.gitignore index 3c3629e647..57551051de 100644 --- a/benchmarks/pulumi/.gitignore +++ b/benchmarks/pulumi/.gitignore @@ -1 +1,3 @@ -node_modules +/bin/ +/node_modules/ + diff --git a/benchmarks/pulumi/Pulumi.bench.yaml b/benchmarks/pulumi/Pulumi.bench.yaml new file mode 100644 index 0000000000..2ad8a4da70 --- /dev/null +++ b/benchmarks/pulumi/Pulumi.bench.yaml @@ -0,0 +1,5 @@ +config: + aws:region: us-east-2 + tailscale:apiKey: + secure: AAABAMGx6JDiUSM4/2M7ZLmws26KJYAGVmCb+BdURSGdO32scaf96Xy3Ocu/Ncv7G1y1W6K1EwZieN6L0/cTAZDGCJpRcMrMmuDyqE8u8Uuv2uRLKRA0 + tailscale:tailnet: windmill.dev diff --git a/benchmarks/pulumi/Pulumi.yaml b/benchmarks/pulumi/Pulumi.yaml new file mode 100644 index 0000000000..a38c35fc16 --- /dev/null +++ b/benchmarks/pulumi/Pulumi.yaml @@ -0,0 +1,3 @@ +name: aws-bench +runtime: nodejs +description: Windmill Benchmarks on AWS diff --git a/benchmarks/pulumi/bun.lockb b/benchmarks/pulumi/bun.lockb new file mode 100755 index 0000000000..cafdf2cd6e Binary files /dev/null and b/benchmarks/pulumi/bun.lockb differ diff --git a/benchmarks/pulumi/index.ts b/benchmarks/pulumi/index.ts new file mode 100644 index 0000000000..c2be29eac2 --- /dev/null +++ b/benchmarks/pulumi/index.ts @@ -0,0 +1,391 @@ +import * as pulumi from "@pulumi/pulumi"; +import * as aws from "@pulumi/aws"; +import * as awsx from "@pulumi/awsx"; +import * as tailscale from "@pulumi/tailscale"; + +const vpc = new aws.ec2.Vpc("bench", { cidrBlock: "10.0.0.0/16" }); + +// Create a security group allowing inbound access over port 80 and outbound +// access to anywhere. +const securityGroup = new aws.ec2.SecurityGroup("bench", { + vpcId: vpc.id, + ingress: [ + { + cidrBlocks: ["0.0.0.0/0"], + protocol: "tcp", + fromPort: 80, + toPort: 50000, + }, + { + cidrBlocks: ["0.0.0.0/0"], + protocol: "tcp", + fromPort: 22, + toPort: 22, + }, + ], + egress: [ + { + cidrBlocks: ["0.0.0.0/0"], + fromPort: 0, + toPort: 0, + protocol: "-1", + }, + ], +}); + +// Create an an internet gateway. +const gateway = new aws.ec2.InternetGateway("gw", { + vpcId: vpc.id, +}); + +// Find the latest Amazon Linux 2 AMI. +const ami = pulumi.output( + aws.ec2.getAmi({ + owners: ["amazon"], + mostRecent: true, + filters: [ + { name: "description", values: ["Amazon Linux 2 *"] }, + { name: "architecture", values: ["x86_64"] }, + ], + }) +); + +// Create a route table. +const routes = new aws.ec2.RouteTable("route", { + vpcId: vpc.id, + routes: [ + { + cidrBlock: "0.0.0.0/0", + gatewayId: gateway.id, + }, + ], +}); + +const subnetA = new aws.ec2.Subnet("bench-a", { + availabilityZone: "us-east-2a", + vpcId: vpc.id, + cidrBlock: "10.0.5.0/24", + mapPublicIpOnLaunch: true, + tags: { + Name: "bench", + }, +}); + +const subnetB = new aws.ec2.Subnet("bench-b", { + availabilityZone: "us-east-2b", + vpcId: vpc.id, + cidrBlock: "10.0.6.0/24", + tags: { + Name: "bench", + }, +}); + +const subnetC = new aws.ec2.Subnet("bench-c", { + availabilityZone: "us-east-2c", + vpcId: vpc.id, + cidrBlock: "10.0.7.0/24", + tags: { + Name: "bench", + }, +}); + +new aws.ec2.RouteTableAssociation("bench-a", { + subnetId: subnetA.id, + routeTableId: routes.id, +}); + +new aws.ec2.RouteTableAssociation("bench-b", { + subnetId: subnetB.id, + routeTableId: routes.id, +}); + +new aws.ec2.RouteTableAssociation("bench-c", { + subnetId: subnetC.id, + routeTableId: routes.id, +}); + +const rdSubnet = new aws.rds.SubnetGroup("bench", { + subnetIds: [subnetA.id, subnetB.id], + tags: { + Name: "bench", + }, +}); + +const db = new aws.rds.Instance("bench", { + vpcSecurityGroupIds: [securityGroup.id], + dbSubnetGroupName: rdSubnet.name, + allocatedStorage: 20, + dbName: "windmill", + engine: "postgres", + engineVersion: "14.8", + instanceClass: "db.m5d.large", + password: "postgres", + skipFinalSnapshot: true, + username: "postgres", + availabilityZone: "us-east-2a", + performanceInsightsEnabled: true, +}); + +const deployer = new aws.ec2.KeyPair("bench", { + publicKey: + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDRi2xytvAIgfs4f5B8EVrWH3bsu8CI+98iBazq08TuPEkLFP/ZqIVm8ZoTnpPadj2o0DXWHNjiyDAnGwp6W1rcSou4KCxoBViRX0doXBMat5GWs6Za2c+bmKllWUH2jhy4aPrdoI3il2URi7dIMH6WpWZDtD98CFK22rTgiBxhDydFpn3X1ULOI18f/SZEKzcb+iffhP5K/Sjqo65xPiKSl2m40vtaQjn2jTE7vmTD9c/dLv/5A3Sgh7CW2PJe9HR7/8qkzZkRkXtyODOIFfWN+vk0rGvk97IpV+DlSUag+FFeSK5Jn41w9IQV7frPvJAQOZ6eLq2GNv8iO/7QnfhlIUlPeClNJBRgb76FmiuslH+AOBiy/Szy3KUtoV/LO+uzvJxRZVX9DhSvTCm4D2qUTjozS+OSeBVuug70euWe29I7djouAuIbDOFkIkxStjhwISXfgvyWYVh8IMxC6+umZMhMff5CfqzpHY0jy2Tw5r/v/2Iz8EQXNRNAxnqKMPE= rfiszel@rubx1", +}); + +const sampleKey = new tailscale.TailnetKey("bench-ipn", { + ephemeral: false, + expiry: 60 * 60 * 24 * 90, + preauthorized: true, + reusable: true, +}); + +const ipn = new aws.ec2.Instance("bench-ipn-2", { + ami: ami.id, + availabilityZone: "us-east-2a", + keyName: deployer.keyName, + instanceType: "t3.medium", + subnetId: subnetA.id, + associatePublicIpAddress: true, + rootBlockDevice: { + volumeSize: 40, + }, + vpcSecurityGroupIds: pulumi + .all([db.vpcSecurityGroupIds, securityGroup.id]) + .apply(([db, sg]) => db.concat(sg)), + userData: pulumi.interpolate`#!/bin/bash +sudo yum update -y +sudo yum install -y yum-utils +sudo yum-config-manager --add-repo https://pkgs.tailscale.com/stable/amazon-linux/2/tailscale.repo +sudo yum install -y tailscale +sudo systemctl enable --now tailscaled +sleep 30 +sudo tailscale up --authkey="${sampleKey.key}" --hostname=bench-ipn --ssh --advertise-tags=tag:ipns + +sudo yum install -y wget unzip +`, +}); + +// Find the latest Amazon Linux 2 AMI. +const amiEcs = pulumi.output( + aws.ec2.getAmi({ + owners: ["amazon"], + mostRecent: true, + filters: [{ name: "name", values: ["amzn2-ami-ecs-*-arm64-*"] }], + }) +); + +const clusterName = "windmill"; +const cluster = new aws.ecs.Cluster("cluster", { name: clusterName }); + +const lt = new aws.ec2.LaunchTemplate("lt2", { + namePrefix: "windmill", + imageId: amiEcs.id, + keyName: deployer.keyName, + instanceType: "t4g.medium", + iamInstanceProfile: { + name: "ecsInstanceRole", + }, + + updateDefaultVersion: true, + vpcSecurityGroupIds: [securityGroup.id], + userData: btoa(`#!/bin/bash +echo ECS_CLUSTER=${clusterName} >> /etc/ecs/ecs.config +`), +}); + +const asg = new aws.autoscaling.Group("asg3", { + launchTemplate: { + id: lt.id, + version: "$Latest", + }, + minSize: 0, + maxSize: 15, + vpcZoneIdentifiers: [subnetA.id], + tags: [ + { + key: "AmazonECSManaged", + value: "true", + propagateAtLaunch: true, + }, + ], +}); + +const capProvider = new aws.ecs.CapacityProvider("cap-provider", { + autoScalingGroupProvider: { + autoScalingGroupArn: asg.arn, + managedTerminationProtection: "DISABLED", + managedScaling: { + maximumScalingStepSize: 1000, + minimumScalingStepSize: 1, + status: "ENABLED", + targetCapacity: 80, + }, + }, +}); + +const clusterCapProvider = new aws.ecs.ClusterCapacityProviders( + "cap-provider", + { + clusterName: cluster.name, + capacityProviders: [capProvider.name], + } +); + +const lb = new awsx.lb.ApplicationLoadBalancer("lb2", { + defaultTargetGroup: { + targetType: "instance", + }, + subnetIds: [subnetA.id, subnetB.id, subnetC.id], +}); +// const service = new awsx.ecs.FargateService("service", { +// cluster: cluster.arn, +// assignPublicIp: true, +// desiredCount: 2, +// taskDefinitionArgs: { +// container: { +// image: "nginx:latest", +// cpu: 512, +// memory: 128, +// essential: true, +// portMappings: [ +// { +// targetGroup: lb.defaultTargetGroup, +// }, +// ], +// }, +// }, +// }); + +db.address.apply((address) => { + const worker_td = new aws.ecs.TaskDefinition("worker-2", { + family: "windmill-worker", + containerDefinitions: JSON.stringify([ + { + name: "windmill-worker", + image: "ghcr.io/windmill-labs/windmill:1.165.0", + cpu: 1024, + memory: 1800, + essential: true, + mountPaths: [ + { + containerPath: "/tmp/windmill/cache", + sourceVolume: "dependency_cache", + }, + ], + environment: [ + { name: "DISABLE_SERVER", value: "true" }, + // { name: "METRICS_ADDR", value: "true" }, + { name: "RUST_LOG", value: "info" }, + { + name: "DATABASE_URL", + value: `postgres://postgres:postgres@${address}/windmill?sslmode=disable`, + }, + ], + + logConfiguration: { + logDriver: "awslogs", + options: { + "awslogs-group": "windmill-worker", + "awslogs-region": "us-east-2", + "awslogs-create-group": "true", + "awslogs-stream-prefix": "windmill-worker", + }, + }, + dockerLabels: { + PROMETHEUS_EXPORTER_PORT: "8001", + }, + }, + ]), + volumes: [ + { + name: "dependency_cache", + dockerVolumeConfiguration: { + scope: "shared", + autoprovision: true, + }, + }, + ], + }); + + const server_td = new aws.ecs.TaskDefinition("server", { + family: "windmill-server", + containerDefinitions: JSON.stringify([ + { + name: "windmill-server", + image: "ghcr.io/windmill-labs/windmill:1.165.0", + cpu: 1024, + memory: 1024, + essential: true, + environment: [ + { name: "NUM_WORKERS", value: "0" }, + // { name: "METRICS_ADDR", value: "true" }, + { name: "RUST_LOG", value: "info" }, + { + name: "DATABASE_URL", + value: `postgres://postgres:postgres@${address}/windmill?sslmode=disable`, + }, + ], + logConfiguration: { + logDriver: "awslogs", + options: { + "awslogs-group": "windmill-server", + "awslogs-region": "us-east-2", + "awslogs-create-group": "true", + "awslogs-stream-prefix": "windmill-server", + }, + }, + dockerLabels: { + PROMETHEUS_EXPORTER_PORT: "8001", + }, + portMappings: [ + { + containerPort: 8000, + }, + { + containerPort: 8001, + }, + ], + }, + ]), + }); + + const service_server = new aws.ecs.Service("service-server", { + cluster: cluster.id, + taskDefinition: server_td.arn, + desiredCount: 2, + forceNewDeployment: true, + orderedPlacementStrategies: [ + { + type: "binpack", + field: "cpu", + }, + ], + loadBalancers: [ + { + targetGroupArn: lb.defaultTargetGroup.arn, + containerName: "windmill-server", + containerPort: 8000, + }, + ], + }); + + const service_worker = new aws.ecs.Service("service-worker", { + cluster: cluster.id, + taskDefinition: worker_td.arn, + desiredCount: 30, + forceNewDeployment: true, + orderedPlacementStrategies: [ + { + type: "binpack", + field: "cpu", + }, + ], + }); +}); + +module.exports = { + databaseAddress: db.address, + ipn: ipn.publicIp, + url: lb.loadBalancer.dnsName, + amiEcs: amiEcs.id, + // instanceURL: pulumi.interpolate`http://${instance.publicIp}`, +}; diff --git a/benchmarks/pulumi/package-lock.json b/benchmarks/pulumi/package-lock.json new file mode 100644 index 0000000000..672945c165 --- /dev/null +++ b/benchmarks/pulumi/package-lock.json @@ -0,0 +1,2412 @@ +{ + "name": "aws-bench", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "aws-bench", + "dependencies": { + "@pulumi/aws": "^5.0.0", + "@pulumi/awsx": "^1.0.4", + "@pulumi/pulumi": "^3.0.0", + "@pulumi/tls": "^4.10.0" + }, + "devDependencies": { + "@types/node": "^16", + "pulumi": "^0.0.1" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.0.tgz", + "integrity": "sha512-H8+iZh+kCE6VR/Krj6W28Y/ZlxoZ1fOzsNt77nrdE3knkbSelW1Uus192xOFCxHyeszLj8i4APQkSIXjAoOxXg==", + "dependencies": { + "@grpc/proto-loader": "^0.7.0", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.8.tgz", + "integrity": "sha512-GU12e2c8dmdXb7XUlOgYWZ2o2i+z9/VeACkxTA/zzAe2IjclC5PnVL0lpgjhrqfpDYHzM8B1TF6pqWegMYAzlA==", + "dependencies": { + "@types/long": "^4.0.1", + "lodash.camelcase": "^4.3.0", + "long": "^4.0.0", + "protobufjs": "^7.2.4", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@logdna/tail-file": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@logdna/tail-file/-/tail-file-2.2.0.tgz", + "integrity": "sha512-XGSsWDweP80Fks16lwkAUIr54ICyBs6PsI4mpfTLQaWgEJRtY9xEV+PeyDpJ+sJEGZxqINlpmAwe/6tS1pP8Ng==", + "engines": { + "node": ">=10.3.0" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz", + "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-metrics": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-metrics/-/api-metrics-0.32.0.tgz", + "integrity": "sha512-g1WLhpG8B6iuDyZJFRGsR+JKyZ94m5LEmY2f+duEJ9Xb4XRlLHrZvh6G34OH6GJ8iDHxfHb/sWjJ1ZpkI9yGMQ==", + "deprecated": "Please use @opentelemetry/api >= 1.3.0", + "dependencies": { + "@opentelemetry/api": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.15.2.tgz", + "integrity": "sha512-VAMHG67srGFQDG/N2ns5AyUT9vUcoKpZ/NpJ5fDQIPfJd7t3ju+aHwvDsMcrYBWuCh03U3Ky6o16+872CZchBg==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.15.2.tgz", + "integrity": "sha512-+gBv15ta96WqkHZaPpcDHiaz0utiiHZVfm2YOYSqFGrUaJpPkMoSuLBB58YFQGi6Rsb9EHos84X6X5+9JspmLw==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.15.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.15.2.tgz", + "integrity": "sha512-j9dPe8tyx4KqIqJAfZ/LCYfkF9+ggsT0V1+bVg9ZKTBNcLf5dTsTMdcxUxc/9s599kgcn6UERnti/tozbzwa6Q==", + "dependencies": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/resources": "1.15.2", + "@opentelemetry/sdk-trace-base": "1.15.2", + "@opentelemetry/semantic-conventions": "1.15.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.32.0.tgz", + "integrity": "sha512-y6ADjHpkUz/v1nkyyYjsQa/zorhX+0qVGpFvXMcbjU4sHnBnC02c6wcc93sIgZfiQClIWo45TGku1KQxJ5UUbQ==", + "dependencies": { + "@opentelemetry/api-metrics": "0.32.0", + "require-in-the-middle": "^5.0.3", + "semver": "^7.3.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/instrumentation-grpc": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.32.0.tgz", + "integrity": "sha512-Az6wdkPx/Mi26lT9LKFV6GhCA9prwQFPz5eCNSExTnSP49YhQ7XCjzPd2POPeLKt84ICitrBMdE1mj0zbPdLAQ==", + "dependencies": { + "@opentelemetry/api-metrics": "0.32.0", + "@opentelemetry/instrumentation": "0.32.0", + "@opentelemetry/semantic-conventions": "1.6.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/instrumentation-grpc/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.6.0.tgz", + "integrity": "sha512-aPfcBeLErM/PPiAuAbNFLN5sNbZLc3KZlar27uohllN8Zs6jJbHyJU1y7cMA6W/zuq+thkaG8mujiS+3iD/FWQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@opentelemetry/propagator-b3": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.15.2.tgz", + "integrity": "sha512-ZSrL3DpMEDsjD8dPt9Ze3ue53nEXJt512KyxXlLgLWnSNbe1mrWaXWkh7OLDoVJh9LqFw+tlvAhDVt/x3DaFGg==", + "dependencies": { + "@opentelemetry/core": "1.15.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.15.2.tgz", + "integrity": "sha512-6m1yu7PVDIRz6BwA36lacfBZJCfAEHKgu+kSyukNwVdVjsTNeyD9xNPQnkl0WN7Rvhk8/yWJ83tLPEyGhk1wCQ==", + "dependencies": { + "@opentelemetry/core": "1.15.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.15.2.tgz", + "integrity": "sha512-xmMRLenT9CXmm5HMbzpZ1hWhaUowQf8UB4jMjFlAxx1QzQcsD3KFNAVX/CAWzFPtllTyTplrA4JrQ7sCH3qmYw==", + "dependencies": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/semantic-conventions": "1.15.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.15.2.tgz", + "integrity": "sha512-BEaxGZbWtvnSPchV98qqqqa96AOcb41pjgvhfzDij10tkBhIu9m0Jd6tZ1tJB5ZHfHbTffqYVYE0AOGobec/EQ==", + "dependencies": { + "@opentelemetry/core": "1.15.2", + "@opentelemetry/resources": "1.15.2", + "@opentelemetry/semantic-conventions": "1.15.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.15.2.tgz", + "integrity": "sha512-5deakfKLCbPpKJRCE2GPI8LBE2LezyvR17y3t37ZI3sbaeogtyxmBaFV+slmG9fN8OaIT+EUsm1QAT1+z59gbQ==", + "dependencies": { + "@opentelemetry/context-async-hooks": "1.15.2", + "@opentelemetry/core": "1.15.2", + "@opentelemetry/propagator-b3": "1.15.2", + "@opentelemetry/propagator-jaeger": "1.15.2", + "@opentelemetry/sdk-trace-base": "1.15.2", + "semver": "^7.5.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz", + "integrity": "sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@pulumi/aws": { + "version": "5.42.0", + "resolved": "https://registry.npmjs.org/@pulumi/aws/-/aws-5.42.0.tgz", + "integrity": "sha512-1h7Q5DjwoWVGxhBMcNragx/Q1US1KT7g29Tk3RghTg/9N7rGUbzTQKEXSrGgRSjGA/aKTbU+gt5A9ZmhONLiLg==", + "hasInstallScript": true, + "dependencies": { + "@pulumi/pulumi": "^3.0.0", + "aws-sdk": "^2.0.0", + "builtin-modules": "3.0.0", + "mime": "^2.0.0", + "read-package-tree": "^5.2.1", + "resolve": "^1.7.1" + } + }, + "node_modules/@pulumi/awsx": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@pulumi/awsx/-/awsx-1.0.4.tgz", + "integrity": "sha512-CK17bk1CrEqMLY1220USZej3pmH1oxnRL+J2zvZ+rvfi1gJkNI9bx8rPKrV1bX4EFQJdKvN4bCm+Jca2E34pMw==", + "hasInstallScript": true, + "dependencies": { + "@pulumi/aws": "^5.35.0", + "@pulumi/docker": "^3.6.1", + "@pulumi/pulumi": "^3.0.0", + "@types/aws-lambda": "^8.10.23", + "mime": "^2.0.0" + } + }, + "node_modules/@pulumi/docker": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@pulumi/docker/-/docker-3.6.1.tgz", + "integrity": "sha512-BZME50QkT556v+LvmTXPT8ssB2xxNkp9+msB5xYFEnUnWcdGAx5yUysQw70RJCb+U0GbkJSbxtlgMJgOQf/now==", + "hasInstallScript": true, + "dependencies": { + "@pulumi/pulumi": "^3.0.0", + "semver": "^5.4.0" + } + }, + "node_modules/@pulumi/pulumi": { + "version": "3.77.1", + "resolved": "https://registry.npmjs.org/@pulumi/pulumi/-/pulumi-3.77.1.tgz", + "integrity": "sha512-smeip4uKWkaKcNhMiAmR5uW4nXRvmHSjq93C7zx+mMneNxeaD9HNIBnYW0H0FFgp7j1AdRYuiIa2ie9Ay51bpw==", + "dependencies": { + "@grpc/grpc-js": "^1.8.16", + "@logdna/tail-file": "^2.0.6", + "@opentelemetry/api": "^1.2.0", + "@opentelemetry/exporter-zipkin": "^1.6.0", + "@opentelemetry/instrumentation": "^0.32.0", + "@opentelemetry/instrumentation-grpc": "^0.32.0", + "@opentelemetry/resources": "^1.6.0", + "@opentelemetry/sdk-trace-base": "^1.6.0", + "@opentelemetry/sdk-trace-node": "^1.6.0", + "@opentelemetry/semantic-conventions": "^1.6.0", + "@pulumi/query": "^0.3.0", + "execa": "^5.1.0", + "google-protobuf": "^3.5.0", + "ini": "^2.0.0", + "js-yaml": "^3.14.0", + "minimist": "^1.2.6", + "normalize-package-data": "^3.0.0", + "pkg-dir": "^7.0.0", + "read-package-tree": "^5.3.1", + "require-from-string": "^2.0.1", + "semver": "^7.5.2", + "source-map-support": "^0.5.6", + "ts-node": "^7.0.1", + "typescript": "~3.8.3", + "upath": "^1.1.0" + }, + "engines": { + "node": ">=8.13.0 || >=10.10.0" + } + }, + "node_modules/@pulumi/pulumi/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@pulumi/query": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@pulumi/query/-/query-0.3.0.tgz", + "integrity": "sha512-xfo+yLRM2zVjVEA4p23IjQWzyWl1ZhWOGobsBqRpIarzLvwNH/RAGaoehdxlhx4X92302DrpdIFgTICMN4P38w==" + }, + "node_modules/@pulumi/tls": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@pulumi/tls/-/tls-4.10.0.tgz", + "integrity": "sha512-aK1LXJzDbeVYBIlxycUUboZJSjMr2wIkrpngNRrOFPMIyO4QYNzJHWIwRbNkS463hhpbp1it3IC3htEqtP4lzg==", + "hasInstallScript": true, + "dependencies": { + "@pulumi/pulumi": "^3.0.0" + } + }, + "node_modules/@types/aws-lambda": { + "version": "8.10.119", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.119.tgz", + "integrity": "sha512-Vqm22aZrCvCd6I5g1SvpW151jfqwTzEZ7XJ3yZ6xaZG31nUEOEyzzVImjRcsN8Wi/QyPxId/x8GTtgIbsy8kEw==" + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" + }, + "node_modules/@types/node": { + "version": "16.18.40", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.40.tgz", + "integrity": "sha512-+yno3ItTEwGxXiS/75Q/aHaa5srkpnJaH+kdkTVJ3DtJEwv92itpKbxU+FjPoh2m/5G9zmUQfrL4A4C13c+iGA==" + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.reduce": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz", + "integrity": "sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz", + "integrity": "sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==", + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws-sdk": { + "version": "2.1432.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1432.0.tgz", + "integrity": "sha512-bnPilJ0YkFOHGL2yYSnpd/oMnsDicGQKmE479vDn/AVcKhRswcg893DTqxrkkuuylEIcIIPPHBqX+IlwNv1zqw==", + "dependencies": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/builtin-modules": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.0.0.tgz", + "integrity": "sha512-hMIeU4K2ilbXV6Uv93ZZ0Avg/M91RaKXucQ+4me2Do1txxBDyDZWCBa5bJSLqoNTRpXTLwEzIk1KmloenDDjhg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debuglog": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", + "integrity": "sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "engines": { + "node": "*" + } + }, + "node_modules/define-properties": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/es-abstract": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz", + "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==", + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.1", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.2.1", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.0", + "safe-array-concat": "^1.0.0", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.7", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/google-protobuf": { + "version": "3.21.2", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.2.tgz", + "integrity": "sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA==" + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/internal-slot": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "dependencies": { + "get-intrinsic": "^1.2.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "dependencies": { + "which-typed-array": "^1.1.11" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/module-details-from-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", + "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==" + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.6.tgz", + "integrity": "sha512-lq+61g26E/BgHv0ZTFgRvi7NMEPuAxLkFU7rukXjc/AlwH4Am5xXVnIXy3un1bg/JPbXHrixRkK1itUzzPiIjQ==", + "dependencies": { + "array.prototype.reduce": "^1.0.5", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.21.2", + "safe-array-concat": "^1.0.0" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/pkg-dir": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", + "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", + "dependencies": { + "find-up": "^6.3.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/protobufjs": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.4.tgz", + "integrity": "sha512-AT+RJgD2sH8phPmCf7OUZR8xGdcJRga4+1cOaXJ64hvcSkVhNcRHOwIxUatPH15+nj59WAGTDv3LSGZPEQbJaQ==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/protobufjs/node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, + "node_modules/pulumi": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pulumi/-/pulumi-0.0.1.tgz", + "integrity": "sha512-Sow9mG2Yf3vRwQV5pux3EfZNvs7uoEjhYGTUWeGDTVrRT/6w3xybp8/a+SpDkH7dooHnGWDJW4QrSAbd+HvK5A==", + "deprecated": "To install Pulumi, use the @pulumi/pulumi package: 'npm -i @pulumi/pulumi'", + "dev": true + }, + "node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/read-package-json": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz", + "integrity": "sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==", + "dependencies": { + "glob": "^7.1.1", + "json-parse-even-better-errors": "^2.3.0", + "normalize-package-data": "^2.0.0", + "npm-normalize-package-bin": "^1.0.0" + } + }, + "node_modules/read-package-json/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" + }, + "node_modules/read-package-json/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-package-tree": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.3.1.tgz", + "integrity": "sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==", + "deprecated": "The functionality that this package provided is now in @npmcli/arborist", + "dependencies": { + "read-package-json": "^2.0.0", + "readdir-scoped-modules": "^1.0.0", + "util-promisify": "^2.1.0" + } + }, + "node_modules/readdir-scoped-modules": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", + "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "dependencies": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-in-the-middle": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-5.2.0.tgz", + "integrity": "sha512-efCx3b+0Z69/LGJmm9Yvi4cqEdxnoGnxYxGxBghkkTTFeXRtTCmmhO0AnAfHz59k957uTSuy8WaHqOs8wbYUWg==", + "dependencies": { + "debug": "^4.1.1", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/resolve": { + "version": "1.22.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", + "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", + "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" + }, + "node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", + "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==" + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", + "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ts-node": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", + "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", + "dependencies": { + "arrify": "^1.0.0", + "buffer-from": "^1.1.0", + "diff": "^3.1.0", + "make-error": "^1.1.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "source-map-support": "^0.5.6", + "yn": "^2.0.0" + }, + "bin": { + "ts-node": "dist/bin.js" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", + "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-promisify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/util-promisify/-/util-promisify-2.1.0.tgz", + "integrity": "sha512-K+5eQPYs14b3+E+hmE2J6gCZ4JmMl9DbYS6BeP2CHq6WMuNxErxf5B/n0fz85L8zUuoO6rIzNNmIQDu/j+1OcA==", + "dependencies": { + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "node_modules/uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", + "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", + "integrity": "sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/benchmarks/pulumi/package.json b/benchmarks/pulumi/package.json new file mode 100644 index 0000000000..e17be56eb2 --- /dev/null +++ b/benchmarks/pulumi/package.json @@ -0,0 +1,15 @@ +{ + "name": "aws-bench", + "main": "index.ts", + "devDependencies": { + "@types/node": "^16", + "pulumi": "^0.0.1" + }, + "dependencies": { + "@pulumi/aws": "^5.0.0", + "@pulumi/awsx": "^1.0.4", + "@pulumi/pulumi": "^3.0.0", + "@pulumi/tailscale": "^0.12.2", + "@pulumi/tls": "^4.10.0" + } +} diff --git a/benchmarks/pulumi/tsconfig.json b/benchmarks/pulumi/tsconfig.json new file mode 100644 index 0000000000..ab65afa613 --- /dev/null +++ b/benchmarks/pulumi/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "strict": true, + "outDir": "bin", + "target": "es2016", + "module": "commonjs", + "moduleResolution": "node", + "sourceMap": true, + "experimentalDecorators": true, + "pretty": true, + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.ts" + ] +} diff --git a/frontend/src/lib/components/FlowStatusViewer.svelte b/frontend/src/lib/components/FlowStatusViewer.svelte index 989059f39c..43300a5206 100644 --- a/frontend/src/lib/components/FlowStatusViewer.svelte +++ b/frontend/src/lib/components/FlowStatusViewer.svelte @@ -489,10 +489,11 @@
{#if node.job_id} -
+
Job Id