From 21e8d7694c38269f7426458f590b7a6a2b59bbbe Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sun, 3 Sep 2023 09:44:16 +0200 Subject: [PATCH] perf: improve queue performance (#2222) * benchs * progress * prog * progress * progress * progress * update * progress * progress * progress * progress * progress * all * all * improve queries * improve queries * rm imv * all * all --- backend/.gitignore | 3 +- ...faa0031f630c5336c0b805590b5ea4508ed04.json | 12 + ...2b75a95b1d042b9e0995186aa5a2d0b7ee552.json | 30 + ...f655412761157cda54bbd3e58cada3de3396c.json | 14 + ...20579dfd2ffab72bb8480a1622b68703d4ea7.json | 22 + ...2176780a87f32673b53de6f899564c1494147.json | 22 + ...a1d31609aa7947a52545dce4455a4983493a7.json | 30 - backend/Cargo.lock | 45 +- backend/Cargo.toml | 3 +- .../20230902192640_add_index.down.sql | 1 + .../20230902192640_add_index.up.sql | 13 + backend/src/main.rs | 5 + backend/windmill-api/src/jobs.rs | 1 + backend/windmill-api/src/lib.rs | 4 +- backend/windmill-common/Cargo.toml | 4 +- backend/windmill-common/src/jobs.rs | 8 + backend/windmill-common/src/tracing_init.rs | 16 + backend/windmill-queue/Cargo.toml | 1 + backend/windmill-queue/src/jobs.rs | 191 +- backend/windmill-worker/Cargo.toml | 2 + backend/windmill-worker/src/worker.rs | 1294 +++++---- benchmarks/benchmark_noop.ts | 106 +- benchmarks/main.ts | 8 +- benchmarks/plot.py | 21 + benchmarks/pulumi/.gitignore | 4 +- benchmarks/pulumi/Pulumi.bench.yaml | 5 + benchmarks/pulumi/Pulumi.yaml | 3 + benchmarks/pulumi/bun.lockb | Bin 0 -> 87398 bytes benchmarks/pulumi/index.ts | 391 +++ benchmarks/pulumi/package-lock.json | 2412 +++++++++++++++++ benchmarks/pulumi/package.json | 15 + benchmarks/pulumi/tsconfig.json | 18 + .../lib/components/FlowStatusViewer.svelte | 5 +- .../wmill/dist/wmill-1.164.0-py3-none-any.whl | Bin 0 -> 4486 bytes python-client/wmill/dist/wmill-1.164.0.tar.gz | Bin 0 -> 4153 bytes .../dist/wmill_pg-1.164.0-py3-none-any.whl | Bin 0 -> 2298 bytes .../wmill_pg/dist/wmill_pg-1.164.0.tar.gz | Bin 0 -> 1923 bytes 37 files changed, 4084 insertions(+), 625 deletions(-) create mode 100644 backend/.sqlx/query-1e232d4b978d133ba542d0abe5afaa0031f630c5336c0b805590b5ea4508ed04.json create mode 100644 backend/.sqlx/query-330e85c6fe52355971262d3a44c2b75a95b1d042b9e0995186aa5a2d0b7ee552.json create mode 100644 backend/.sqlx/query-467247beca4dae321ab3acc3a99f655412761157cda54bbd3e58cada3de3396c.json create mode 100644 backend/.sqlx/query-71879f3644dde27f2d93e5511e420579dfd2ffab72bb8480a1622b68703d4ea7.json create mode 100644 backend/.sqlx/query-86652246b20f9bd023d3419ec382176780a87f32673b53de6f899564c1494147.json delete mode 100644 backend/.sqlx/query-afbae51dae93e19045aee8d2d9ea1d31609aa7947a52545dce4455a4983493a7.json create mode 100644 backend/migrations/20230902192640_add_index.down.sql create mode 100644 backend/migrations/20230902192640_add_index.up.sql create mode 100644 benchmarks/plot.py create mode 100644 benchmarks/pulumi/Pulumi.bench.yaml create mode 100644 benchmarks/pulumi/Pulumi.yaml create mode 100755 benchmarks/pulumi/bun.lockb create mode 100644 benchmarks/pulumi/index.ts create mode 100644 benchmarks/pulumi/package-lock.json create mode 100644 benchmarks/pulumi/package.json create mode 100644 benchmarks/pulumi/tsconfig.json create mode 100644 python-client/wmill/dist/wmill-1.164.0-py3-none-any.whl create mode 100644 python-client/wmill/dist/wmill-1.164.0.tar.gz create mode 100644 python-client/wmill_pg/dist/wmill_pg-1.164.0-py3-none-any.whl create mode 100644 python-client/wmill_pg/dist/wmill_pg-1.164.0.tar.gz 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 0000000000000000000000000000000000000000..cafdf2cd6e094656162b8aa128682e08895d8eca GIT binary patch literal 87398 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 literal 0 HcmV?d00001 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 djr5C&nWp+t}wkS^&)8tHBX>2_d1MWjJWKvcRe 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 KsDIy&0R9E>=)?;E literal 0 HcmV?d00001 diff --git a/python-client/wmill/dist/wmill-1.164.0.tar.gz b/python-client/wmill/dist/wmill-1.164.0.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..d521582de60bae1dfdf3bc3d040c37bb3886a536 GIT binary patch literal 4153 zcmV-95XSExiwFn+00002|95R^Y-}wtE-^MVE-)^1VR8WNJZW>=II{W7Ux8(6D$*N? zx_qjl)Or$6;u**Gvz^HYua^UfkcEWT@UWxJ`oF(!96Th-mft$A*$Gi)i^S3BZge*q z4G>nx>iqqLetE~J!=h*3Bz0w4_1obnwX?a?-5PB5t?k`zcYCmFJgd`RKHYfSb=y6wx3yt)JE!}5Z{F`)p7ZGXzqPTk zO8 zjYpe?JjX7ahujLHt4`HUl<7{>3v{~pehS>PMo3xg=p z95Ea=B_8E|qd7DPq~}3wG5?xJfp5huX_z11+vmr>?jM;gV!rg|N${EZugoSnJ~=-) zKH57ZNkE_?5e107L&kH#1JQ+IwSaxiqL>Fh8Ob8+iY2znYcxfTh{Zt~O<1Xl&TN=z z2f5SH8FomMnb|ZX;v`Ca63_j~3~&$9*qyh?4Pp?nB#nH560}U+M0b_-0xj)$67evx z5kO-`Wd$IZ}^U^RPm>MNpvgMI! z0BWBB9RWu8AYZvZaZ{(jzT1{@Y2>*VG8=TvO6DPR5Ok6I&>#4_4)Z6R#g~S3NfFvV z2iCS}6unrHn~jKZF;2PbwBvc4Fz*uZ{Uhblg}X2%>{p|n)wTNQr^<}BGl#L}leQXS z7!tro!Wjc;fCzS?F!N{}`0xu|n`NXc;9MHar(+jMPNI|<*Av9VyyA&z#K~L~m@)I9 zg=yfzfhO%w-2X%||LYHgPl>}pDSdQxN2E)Bxc&N-j48;(?Jblu7oLiFKf+_y;`Y{p z*Gb@cJW=(12SEOC3z#d^$1YHK36kcsw#)phWCmUL`dtGoLBa1v{=Eq2gRK`Wg1_79 zcjpC=_(c!vix)4921r1sA;jR{iAzB|EuGFwf`slKRH9$uoC8pZCl~|Df~ZJkZ&!-{|5c; zR_*^iMZ!QEv6KU*;$Qz$YyNkW>iAC`|H-B9!%PGduFqXn^k`VHx(LiFCu~25~=!Q*0Zh3(Ni+t;57Eq7l z7FQVfF|+&Ku5S5J6rHLPC@?C(l%1NrG?~FGpWy!DYq{|z8!8%+Sah4MPdK-3in@s+B@5~501|FPmlHv?GLAidc%`X%T0Mq{2&oM<{OU-z)P<` z60;+tT}ATjAwd#GSq2HBayxhzamQh&;|3Gz&S0d6J7DD`Gz2D?kwKDM!_aJzwrp>? zDS*nhS8gz-F0l(6$3UvBB|ToCP#27)9Y z9mnqIXX?f*1EsgB#m22hQ;H2n)^396a~x7o4KeP(0*NdtoDJkxj4|-x*Q~yj<`Dy+RD*OWFKS6P6G?(El3}`k|)g#-= z4?}2~A1!EIUPYie!vA?*hV^I#x|UE42S&Mp16}~Pe2QWzC$Ah*R1q6^K3Y`wH|O>* z$3NKzZ%hSzNnCR&tq{;E`Mg6mQA@``%!Xtf1ULyuYbGSHTz^5~@x+P}zZSWnT%MQW zc+>|sA3~QUtSFcH0UK=)Tp`b3iX@a0cwmgpaRa9EIEy5xCA3A_Egz~U45yQ>c)yw9 zegO_WLW-r6Ab~c?-$uFTU}f$=Za~4hYOa)K2zjJuYJLQ2(Gx_1ze=FDMY1i+Qsz`a z)7ZTvy>1z#Z~z;9tDD!y-JPmRo5e~R;*Z+PQCwChqJYbq+GPm=&BCKmMN@|F--9Vb zIv-@p1f%XUWhYQ!4O512zM&~Y@FVK3+Fs&kN`e~RXeU}*E3`a zwoy2qsT&~ zxC_z;pM4401oQ`Ki!P5xRzq=O!wdoofY{rb7Qx$SRb_Se9Hxg$bH0j*W;@ zz0fgr9FEvi*AAl~WKqJ4+`5-e1#f}Wk{m7)oe_?CgsX7L9ZI{@ zL`7T*`dCubTo>;&zXgTNttx$x8dj)G*1Sr2N`?P5YE)1n$*48eNOAQ6(p4UN?bu}f zT1g=NhbEq37Ya@Il^;Z`EDACdm`&a)0G7qZeE4tksSPNxDfgLEp1Eaw%%9&WF7mqo z3hpDh;@8X@@NLNV^1aD2u8x5NpU42rTZ zJWg^U5dX9-^+;EQoLSi!Tn)0 z{HSFONychgie<+o#AKF`tHy&ZTG7=KLfoiU%D4EXk8-G3sif@0NrND@|2#^Wh94J; zACXXq}iD_@HWT3)kd<()v&&?y0xrfLavt{5Lg1Kr7QSY7wjD%gmJ+DpNQIKv}f zAXXxn7cVg7!8SA#Q)NaAVr%5nHxyQxLp7By;C{dL&7LalZoW04r~ct8a#wY=s=HE! zQh(*5eDz-~Q_FU;K!+HiksPP8a*axFWjokf*a9ir>PjhCkNTBSk9&Af5fmx2<_=A0 z6nIdgEH_lr5atHz9z(Ni6HC@kaa*qY3!Thp1n#MldOrE=7P~CkL~ZdbS<74p9bCAG z7tss&gRTRUNGiTtkYGw}mCmaYseK$(Gi2y$b|jdNoL=#y{mmHA07E*XzT*~zK|-*c z)C)D?vI-Cs1xYXo+>xsD?%?ga{j+oXIO{D@ZYLpu+W zWOTtAvdSt^xK9R9#=oHi$kPmKj6eKiKKPCRZlt|OWWaP-ic^*(VTx_yGy+E$BZG2# z6{E@yD*DDM0ciYr*$}P41|f zUQm68=%?SGVqs_v|ohZ_r^?>}vHCMHx-c`&Ji#pbKF;os~~l|B*6h`+F5U)w`hL9NW<`J{SFn2zubkNW%1`uop1{aL>M&^Hu3rvBgg{pX;+ z@cn0hbGui6|M3**IeAZ`&#>GVR}A2u=*j*G+1#qH|M?#E|K!)V?SrGAj~{>jzeE2A z-HoO1Kex9Bwf;XvdJm2=ti@>iH~9&{ko2vdaYQ{fRQDie1xiSav()owG#`>jzWc{` z7kI3Vm;4M#)!jYDp}ZOhI?gTzGWNu6K0{Kx$wy_jnFr4FkSt&8W1Olha@r^2mYpGa zH5ypkt(QpNYy7y1PdP5YRF7mhB*$k5(63k#IQTAcAZNiexuFr$RZGoJqTni`o`*yq zQvWK&3yh$x>=mB|53O%)Ke)xtgInxAxJ9q~@Fu-{7~b$W8IsZ!M-9BQNC;f(B@8qk zl01=jleIsb9uCQy;&o50YJqu^hfCV8QtvX)u~Q(s#Bf*o6`RQ9xK?a0C)fZ DS2zKo literal 0 HcmV?d00001 diff --git a/python-client/wmill_pg/dist/wmill_pg-1.164.0-py3-none-any.whl b/python-client/wmill_pg/dist/wmill_pg-1.164.0-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..21e6abb47ec190c79b13975b44141ac2eeb1630c GIT binary patch literal 2298 zcmaKt2T)V_7REz~ARPphML-Bedg$!Zdo%_^=rweS2?Qh{VL<^!5fG3fu);ejOYxR0`s))DRUwVg!8%SdA`7CnolmvawK=M z`7M-9o+@5wY?QXU3JNfYjN8qC2+uAofY7Ty-7V5#^#b5ai0Bhp5itKodJC$J%oQ!@-T_=3iw z`}PHXJepPFBtunFKa3;5)!0PqYgd=@3E*JZ5cJi|RIN8G|U zlkJNusO}CprTL>W$9z*qYAm3qN6p_D;)XV|Dm?Q`HTl~P`}yn;buSY_4HLMAwVqCO zPEh--SnG2})mM_aQt5hzNk*rq2(@MXk$xqk^}?*VwKAk@YxXtEoUpS5FTV=s)u0>|ADg=MGuU>xKAhNN@kl zNaLkxm{5}zz6uS+ zRNdqMHdxAvfbHYVK>fg!;QnBR;?;2Y&;WmXr?)Vbep^)pWMJ@=BV<4w!5PX62kU4p z9?U2k>X07~Goy~ojDq%|R5jEzbhXvMYW}EjoGL0L5UUQeaxk+rb1=)X_ry+ug=4p> zn$Amc35CUlEQ{uxFRQK;^5dZ~;IMAl+&P_(WeXj($aZnWc*JYTw^>W!Lq=PZJp^is zWt3oyRW7s`L|f+*=S~#F3Y zQ=OFxyznQ)%-drz5Afvow>HJ#Y8kn@hArLnu2AI @4bxNfG#n0!6fs)ckHI`g5c z>Sl_!n~L3Ij{Hwu^fj=loWE%N{kvNfCD>X~2H%D^LMY3+nO3-7H;&gK2gt8`RLUa8 zY;2NbwhbT2BbNuS?cQVSRrp5{wiL<~%-YKY%;+TJ zggfI{OHcZA*|WQ=_XNe^=_M7%I!m8qJ4+2hq0Q^rGa2_{2zeEO^rl9Cj4gjKY{mZk z%TI^fKQL(Pt!tUsBN%zZTO#M^k?HJR{R(ZW6^o?OqKfNWpNNStAxWvnx#py;=A%Pe zjV>zm72AA=aed^)7#qTk8g^$>6H~ok1liomTLx${MI8LVhqYliEwRm477(~t9`CDa zLDzV80$o=DEHQ*7E-bH!O#4lg&lkU}bkf-vk%qKZdAU2LgnKpgJi~sQ%Ow#@F(+n- zT$noX{V+L0tgO!P_G@s(fq8&bqu*#indCVtcha3Cn#WRcf^24^1s3~iiy=@vW zEt7IWbJ$89?$%EuafxB$GvP*S68C5P6&PkR zl5!$MQZHMLGxgMNo4LewZRhTTPNzU5Bq8Q2AZVNC)Bk=8kkrF*mHZm?qJX#^txSG|L)Lp_poEu|5wKgtStbc%O%5UlF5Z&8D_VV#Tidx?1!Ai z63-KXB~Xn2%Hu#WOqxq6(`?k~+*BnbVAn02hExJ^L=YO+H8xH8hCVG)P7<-`q$2sm zeA(G1>$HIJ#S8pv&iyMjGZLYXA3vsrTqZH%QJM&eYnw#~1#FHZ%Hw8hgfJi~rXkNH zKE$7MCW@x@;q3V6@&pGgO7O+WCBCL%&Y-%X9~$`lD$wv@D(fCWwq7PZK*MD z3NerC*2}IG2q8+m|KTqsG5+89|I+^-bo)bZ|KNal{cg|e^^E`j7X3ebp3Cntkx3Lj zhyQm6Lr?ktVGj%t!T?zR-R_`g{Qq-cER!T8X~Lu^CMc$na*md^3Oij`=$Z)~2S8zE zAr?5@%ojYBRryi;<#<6iR2PPPCa5T&($6y%U?5jrgxm*a-S1IK{Ux(K)yp!8SV|YV z{GnI_GXhB)}P#idaX( z76Q?(KpfqS4gl_W?1y=*w5b@cYhYm!S)?pb9c*hIdQDjzu-NA;o1ik( zQEmRcONJIIHK<-)(pP#(v%*i(g=f!rth5PLuV9elS`$`gP!KH;VS?CHYV-avzNX3g(+Mjs*iK_Cumzd9S0Ff4reV{zV4t}}1yTdaI zX0lpAJqzJ!yX;T!xaL@`2)z196meP6#{o3?as`rCqR&DY;RK8?W_HNpg^E(Z=e3SpDxeV2HQ%G&dRz)C8mGG__^?6aS6CP}k4bEWTd9L`7Om zeP!oR)asxXQrwkPJc9tYlxn&o6?~NEyi}W(+_vKhuLc0@7X8}4i3_JBm zE|z8K|6f&)m~M`%0xGG_1LT<+M=j<$;grYV+hRuLlHd!*FqgoylRQ%SswffalxLn0 zq`Ns1i)?e$Qw>uclYK7x&(xTqzc#+%a+%~(XBnXX#&?Z18;FZdP>o%~xK(3v zi?t@*X*-xH>1bTw1qUX4lZdMEFixc&1gq0f^smnlUtwZBxrMPo!66jw`pz#<5rnau)gXH9r>>Gl9n}5zTcuVYkfBB?HOsw6(6jt3-g2srs!H z?r7UqRfAAHXVPDS0W}|zyChRuSqn?bT-%laewbvee%BOR1=WlyYcoNHiT_RfZ{mN0 z|F-zQKCgP3_5Zv1zpi&L{_hTZeG~sb2VUU!R9r#Krcakt^mTS}hWi88tp0Cv{h$5$ zC;RQ`kH0*z|Np)Hzw7ny`G2=JF!w*71@D=pYWr;er9AH*VUM`zlt!>)t#5&#bzNm7 z=pv6IsVRmuNdJshg~vkzgDC9A*5Nx=bS;SRgZWy!oOU+g?>|!T!Xx$S_XWP%;gOg ztR~%IKNHD9P}mE>KKC7s7rDB?0=#9f>Z$u!dNO=8$Nr-^4j#?nI*(^@H`8#e?$P3V zcUkUNmlo7i7gzekX}^Z)Cr%5Ul^4|P59jYj_;@>y(|*{DjyJnq`-db3dzJPj#5N-= z!FoGj%2z>h1G{f?8~Q13Lz~dSz`(%3z`(%3z`(%3z`(%3z`(%3z`(%3z~DcFe*p#M J8{_~`000R;(l`JB literal 0 HcmV?d00001