Merge branch 'main' into alp/msft_teams

This commit is contained in:
Alexander Petric
2025-01-22 11:59:45 -05:00
committed by GitHub
37 changed files with 1646 additions and 1011 deletions
+13
View File
@@ -1,5 +1,18 @@
# Changelog
## [1.448.0](https://github.com/windmill-labs/windmill/compare/v1.447.6...v1.448.0) (2025-01-22)
### Features
* Migrate to bun.lock ([#5112](https://github.com/windmill-labs/windmill/issues/5112)) ([fcfad69](https://github.com/windmill-labs/windmill/commit/fcfad69195a776d4958c035dca16100368966a66))
### Bug Fixes
* improve workspace specific tags behavior ([669a1ff](https://github.com/windmill-labs/windmill/commit/669a1ff8e6ab782ce1168170db684deb5f0ec99a))
* improve workspace specific tags behavior ([e81c2c9](https://github.com/windmill-labs/windmill/commit/e81c2c983d139cd24e983f6a76de56c10a694954))
## [1.447.6](https://github.com/windmill-labs/windmill/compare/v1.447.5...v1.447.6) (2025-01-21)
+65 -65
View File
@@ -713,14 +713,14 @@ dependencies = [
"percent-encoding",
"pin-project-lite",
"tracing",
"uuid 1.12.0",
"uuid 1.12.1",
]
[[package]]
name = "aws-sdk-sso"
version = "1.54.0"
version = "1.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "921a13ed6aabe2d1258f65ef7804946255c799224440774c30e1a2c65cdf983a"
checksum = "33993c0b054f4251ff2946941b56c26b582677303eeca34087594eb901ece022"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -740,9 +740,9 @@ dependencies = [
[[package]]
name = "aws-sdk-ssooidc"
version = "1.55.0"
version = "1.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "196c952738b05dfc917d82a3e9b5ba850822a6d6a86d677afda2a156cc172ceb"
checksum = "3bd3ceba74a584337a8f3839c818f14f1a2288bfd24235120ff22d7e17a0dd54"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -762,9 +762,9 @@ dependencies = [
[[package]]
name = "aws-sdk-sts"
version = "1.55.0"
version = "1.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ef5b73a927ed80b44096f8c20fb4abae65469af15198367e179ae267256e9d"
checksum = "07835598e52dd354368429cb2abf447ce523ea446d0a533a63cb42cd0d2d9280"
dependencies = [
"aws-credential-types",
"aws-runtime",
@@ -1359,9 +1359,9 @@ dependencies = [
[[package]]
name = "brotli-decompressor"
version = "4.0.1"
version = "4.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362"
checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
@@ -2011,9 +2011,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crunchy"
version = "0.2.2"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929"
[[package]]
name = "crypto-bigint"
@@ -2264,7 +2264,7 @@ dependencies = [
"tokio",
"tokio-util",
"url",
"uuid 1.12.0",
"uuid 1.12.1",
"xz2",
"zstd",
]
@@ -2364,7 +2364,7 @@ dependencies = [
"regex",
"sha2 0.10.8",
"unicode-segmentation",
"uuid 1.12.0",
"uuid 1.12.1",
]
[[package]]
@@ -2531,7 +2531,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d"
dependencies = [
"serde",
"uuid 1.12.0",
"uuid 1.12.1",
]
[[package]]
@@ -2826,7 +2826,7 @@ dependencies = [
"serde",
"thiserror 1.0.69",
"tokio",
"uuid 1.12.0",
"uuid 1.12.1",
]
[[package]]
@@ -5382,7 +5382,7 @@ dependencies = [
"sha2 0.10.8",
"subprocess",
"thiserror 1.0.69",
"uuid 1.12.0",
"uuid 1.12.1",
"zstd",
]
@@ -6297,7 +6297,7 @@ dependencies = [
"postgres-protocol",
"serde",
"serde_json",
"uuid 1.12.0",
"uuid 1.12.1",
]
[[package]]
@@ -7123,7 +7123,7 @@ dependencies = [
"rkyv_derive",
"seahash",
"tinyvec",
"uuid 1.12.0",
"uuid 1.12.1",
]
[[package]]
@@ -7270,9 +7270,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.38.43"
version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [
"bitflags 2.8.0",
"errno",
@@ -7514,7 +7514,7 @@ dependencies = [
"serde",
"thiserror 1.0.69",
"url",
"uuid 1.12.0",
"uuid 1.12.1",
]
[[package]]
@@ -7552,7 +7552,7 @@ dependencies = [
"schemars_derive",
"serde",
"serde_json",
"uuid 1.12.0",
"uuid 1.12.1",
]
[[package]]
@@ -8286,7 +8286,7 @@ dependencies = [
"tokio-stream",
"tracing",
"url",
"uuid 1.12.0",
"uuid 1.12.1",
"webpki-roots",
]
@@ -8370,7 +8370,7 @@ dependencies = [
"stringprep",
"thiserror 2.0.11",
"tracing",
"uuid 1.12.0",
"uuid 1.12.1",
"whoami",
]
@@ -8411,7 +8411,7 @@ dependencies = [
"stringprep",
"thiserror 2.0.11",
"tracing",
"uuid 1.12.0",
"uuid 1.12.1",
"whoami",
]
@@ -8437,7 +8437,7 @@ dependencies = [
"sqlx-core",
"tracing",
"url",
"uuid 1.12.0",
"uuid 1.12.1",
]
[[package]]
@@ -9084,7 +9084,7 @@ dependencies = [
"tempfile",
"thiserror 1.0.69",
"time",
"uuid 1.12.0",
"uuid 1.12.1",
"winapi",
]
@@ -9314,7 +9314,7 @@ dependencies = [
"tokio-rustls 0.24.1",
"tokio-util",
"tracing",
"uuid 1.12.0",
"uuid 1.12.1",
]
[[package]]
@@ -10125,7 +10125,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f294bff79170ed1c5633812aff1e565c35d993a36e757f9bc0accf5eec4e6045"
dependencies = [
"rand 0.8.5",
"uuid 1.12.0",
"uuid 1.12.1",
"web-time",
]
@@ -10407,9 +10407,9 @@ dependencies = [
[[package]]
name = "uuid"
version = "1.12.0"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "744018581f9a3454a9e15beb8a33b017183f1e7c0cd170232a2d1453b23a51c4"
checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b"
dependencies = [
"getrandom 0.2.15",
"serde",
@@ -10724,7 +10724,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windmill"
version = "1.447.6"
version = "1.448.0"
dependencies = [
"anyhow",
"axum",
@@ -10752,7 +10752,7 @@ dependencies = [
"tokio",
"tracing",
"url",
"uuid 1.12.0",
"uuid 1.12.1",
"windmill-api",
"windmill-api-client",
"windmill-autoscaling",
@@ -10765,7 +10765,7 @@ dependencies = [
[[package]]
name = "windmill-api"
version = "1.447.6"
version = "1.448.0"
dependencies = [
"anyhow",
"argon2",
@@ -10840,7 +10840,7 @@ dependencies = [
"ulid",
"url",
"urlencoding",
"uuid 1.12.0",
"uuid 1.12.1",
"windmill-audit",
"windmill-common",
"windmill-git-sync",
@@ -10852,7 +10852,7 @@ dependencies = [
[[package]]
name = "windmill-api-client"
version = "1.447.6"
version = "1.448.0"
dependencies = [
"base64 0.22.1",
"chrono",
@@ -10865,12 +10865,12 @@ dependencies = [
"serde",
"serde_json",
"syn 1.0.109",
"uuid 1.12.0",
"uuid 1.12.1",
]
[[package]]
name = "windmill-audit"
version = "1.447.6"
version = "1.448.0"
dependencies = [
"chrono",
"serde",
@@ -10883,21 +10883,21 @@ dependencies = [
[[package]]
name = "windmill-autoscaling"
version = "1.447.6"
version = "1.448.0"
dependencies = [
"anyhow",
"serde",
"serde_json",
"sqlx",
"tracing",
"uuid 1.12.0",
"uuid 1.12.1",
"windmill-common",
"windmill-queue",
]
[[package]]
name = "windmill-common"
version = "1.447.6"
version = "1.448.0"
dependencies = [
"anyhow",
"async-stream",
@@ -10950,27 +10950,27 @@ dependencies = [
"tracing-loki",
"tracing-opentelemetry",
"tracing-subscriber",
"uuid 1.12.0",
"uuid 1.12.1",
"windmill-macros",
]
[[package]]
name = "windmill-git-sync"
version = "1.447.6"
version = "1.448.0"
dependencies = [
"regex",
"serde",
"serde_json",
"sqlx",
"tracing",
"uuid 1.12.0",
"uuid 1.12.1",
"windmill-common",
"windmill-queue",
]
[[package]]
name = "windmill-indexer"
version = "1.447.6"
version = "1.448.0"
dependencies = [
"anyhow",
"bytes",
@@ -10987,13 +10987,13 @@ dependencies = [
"tokio",
"tokio-tar",
"tracing",
"uuid 1.12.0",
"uuid 1.12.1",
"windmill-common",
]
[[package]]
name = "windmill-macros"
version = "1.447.6"
version = "1.448.0"
dependencies = [
"itertools 0.14.0",
"lazy_static",
@@ -11005,7 +11005,7 @@ dependencies = [
[[package]]
name = "windmill-parser"
version = "1.447.6"
version = "1.448.0"
dependencies = [
"convert_case 0.6.0",
"serde",
@@ -11014,7 +11014,7 @@ dependencies = [
[[package]]
name = "windmill-parser-bash"
version = "1.447.6"
version = "1.448.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -11026,7 +11026,7 @@ dependencies = [
[[package]]
name = "windmill-parser-csharp"
version = "1.447.6"
version = "1.448.0"
dependencies = [
"anyhow",
"serde_json",
@@ -11038,7 +11038,7 @@ dependencies = [
[[package]]
name = "windmill-parser-go"
version = "1.447.6"
version = "1.448.0"
dependencies = [
"anyhow",
"gosyn",
@@ -11050,7 +11050,7 @@ dependencies = [
[[package]]
name = "windmill-parser-graphql"
version = "1.447.6"
version = "1.448.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -11062,7 +11062,7 @@ dependencies = [
[[package]]
name = "windmill-parser-php"
version = "1.447.6"
version = "1.448.0"
dependencies = [
"anyhow",
"itertools 0.14.0",
@@ -11073,7 +11073,7 @@ dependencies = [
[[package]]
name = "windmill-parser-py"
version = "1.447.6"
version = "1.448.0"
dependencies = [
"anyhow",
"itertools 0.14.0",
@@ -11084,7 +11084,7 @@ dependencies = [
[[package]]
name = "windmill-parser-py-imports"
version = "1.447.6"
version = "1.448.0"
dependencies = [
"anyhow",
"async-recursion",
@@ -11102,7 +11102,7 @@ dependencies = [
[[package]]
name = "windmill-parser-rust"
version = "1.447.6"
version = "1.448.0"
dependencies = [
"anyhow",
"convert_case 0.6.0",
@@ -11119,7 +11119,7 @@ dependencies = [
[[package]]
name = "windmill-parser-sql"
version = "1.447.6"
version = "1.448.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -11131,7 +11131,7 @@ dependencies = [
[[package]]
name = "windmill-parser-ts"
version = "1.447.6"
version = "1.448.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -11149,7 +11149,7 @@ dependencies = [
[[package]]
name = "windmill-parser-wasm"
version = "1.447.6"
version = "1.448.0"
dependencies = [
"anyhow",
"getrandom 0.2.15",
@@ -11171,7 +11171,7 @@ dependencies = [
[[package]]
name = "windmill-parser-yaml"
version = "1.447.6"
version = "1.448.0"
dependencies = [
"anyhow",
"serde_json",
@@ -11181,7 +11181,7 @@ dependencies = [
[[package]]
name = "windmill-queue"
version = "1.447.6"
version = "1.448.0"
dependencies = [
"anyhow",
"async-recursion",
@@ -11208,14 +11208,14 @@ dependencies = [
"tokio",
"tracing",
"ulid",
"uuid 1.12.0",
"uuid 1.12.1",
"windmill-audit",
"windmill-common",
]
[[package]]
name = "windmill-sql-datatype-parser-wasm"
version = "1.447.6"
version = "1.448.0"
dependencies = [
"wasm-bindgen",
"wasm-bindgen-test",
@@ -11225,7 +11225,7 @@ dependencies = [
[[package]]
name = "windmill-worker"
version = "1.447.6"
version = "1.448.0"
dependencies = [
"anyhow",
"async-recursion",
@@ -11282,7 +11282,7 @@ dependencies = [
"tokio-util",
"tracing",
"urlencoding",
"uuid 1.12.0",
"uuid 1.12.1",
"windmill-audit",
"windmill-common",
"windmill-git-sync",
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "windmill"
version = "1.447.6"
version = "1.448.0"
authors.workspace = true
edition.workspace = true
@@ -30,7 +30,7 @@ members = [
]
[workspace.package]
version = "1.447.6"
version = "1.448.0"
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
edition = "2021"
+1 -1
View File
@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.447.6
version: 1.448.0
title: Windmill API
contact:
+36 -9
View File
@@ -512,16 +512,43 @@ pub fn get_vcpus() -> Option<i64> {
}
}
pub fn get_memory() -> Option<i64> {
if Path::new("/sys/fs/cgroup/memory/memory.limit_in_bytes").exists() {
// cgroup v1
parse_file("/sys/fs/cgroup/memory/memory.limit_in_bytes")
} else {
// cgroup v2
let cgroup_path = get_cgroupv2_path()?;
let memory_max_path = format!("{cgroup_path}/memory.max");
fn get_memory_from_meminfo() -> Option<i64> {
let memory_info = parse_file::<String>("/proc/meminfo")?;
if memory_info.contains("MemTotal") {
let memory_total = memory_info
.split("MemTotal:")
.nth(1)?
.split("kB")
.next()?
.trim()
.parse::<i64>()
.ok()?;
return Some(memory_total * 1024);
}
None
}
parse_file(&memory_max_path)
pub fn get_memory() -> Option<i64> {
let memory_limit: Option<i64> =
if Path::new("/sys/fs/cgroup/memory/memory.limit_in_bytes").exists() {
// cgroup v1
parse_file("/sys/fs/cgroup/memory/memory.limit_in_bytes")
} else {
// cgroup v2
let cgroup_path = get_cgroupv2_path()?;
let memory_max_path = format!("{cgroup_path}/memory.max");
parse_file(&memory_max_path)
};
// if memory_max is super high, read machine total memory (meminfo)
if memory_limit
.as_ref()
.is_some_and(|x| x > &(1024 * 1024 * 1024 * 1024 * 1024))
{
get_memory_from_meminfo()
} else {
memory_limit
}
}
@@ -77,6 +77,12 @@ mount {
mandatory: false
}
mount {
src: "{JOB_DIR}/bun.lock"
dst: "/tmp/{LANG}/bun.lock"
is_bind: true
mandatory: false
}
mount {
src: "{JOB_DIR}/wrapper.mjs"
+62 -41
View File
@@ -62,24 +62,37 @@ const RELATIVE_BUN_BUILDER: &str = include_str!("../loader_builder.bun.js");
const NSJAIL_CONFIG_RUN_BUN_CONTENT: &str = include_str!("../nsjail/run.bun.config.proto");
pub const BUN_LOCK_SPLIT: &str = "\n//bun.lock\n";
pub const BUN_LOCKB_SPLIT: &str = "\n//bun.lockb\n";
pub const BUN_LOCK_SPLIT_WINDOWS: &str = "\r\n//bun.lock\r\n";
pub const BUN_LOCKB_SPLIT_WINDOWS: &str = "\r\n//bun.lockb\r\n";
pub const EMPTY_FILE: &str = "<empty>";
fn split_lockfile(lockfile: &str) -> (&str, Option<&str>, bool) {
if let Some(index) = lockfile.find(BUN_LOCKB_SPLIT) {
/// Returns (package.json, bun.lock(b), is_empty, is_binary)
fn split_lockfile(lockfile: &str) -> (&str, Option<&str>, bool, bool) {
if let Some(index) = lockfile.find(BUN_LOCK_SPLIT) {
// Split using "\n//bun.lock\n"
let (before, after_with_sep) = lockfile.split_at(index);
let after = &after_with_sep[BUN_LOCK_SPLIT.len()..];
(before, Some(after), after == EMPTY_FILE, false)
} else if let Some(index) = lockfile.find(BUN_LOCKB_SPLIT) {
// Split using "\n//bun.lockb\n"
let (before, after_with_sep) = lockfile.split_at(index);
let after = &after_with_sep[BUN_LOCKB_SPLIT.len()..];
(before, Some(after), after == EMPTY_FILE)
(before, Some(after), after == EMPTY_FILE, true)
} else if let Some(index) = lockfile.find(BUN_LOCK_SPLIT_WINDOWS) {
// Split using "\r\n//bun.lock\r\n"
let (before, after_with_sep) = lockfile.split_at(index);
let after = &after_with_sep[BUN_LOCK_SPLIT_WINDOWS.len()..];
(before, Some(after), after == EMPTY_FILE, false)
} else if let Some(index) = lockfile.find(BUN_LOCKB_SPLIT_WINDOWS) {
// Split using "\r\n//bun.lockb\r\n"
let (before, after_with_sep) = lockfile.split_at(index);
let after = &after_with_sep[BUN_LOCKB_SPLIT_WINDOWS.len()..];
(before, Some(after), after == EMPTY_FILE)
(before, Some(after), after == EMPTY_FILE, true)
} else {
(lockfile, None, false)
(lockfile, None, false, false)
}
}
@@ -199,18 +212,18 @@ pub async fn gen_bun_lockfile(
}
if !npm_mode {
#[cfg(any(target_os = "linux", target_os = "macos"))]
content.push_str(BUN_LOCKB_SPLIT);
content.push_str(BUN_LOCK_SPLIT);
#[cfg(target_os = "windows")]
content.push_str(BUN_LOCKB_SPLIT_WINDOWS);
content.push_str(BUN_LOCK_SPLIT_WINDOWS);
{
let file = format!("{job_dir}/bun.lockb");
let file = format!("{job_dir}/bun.lock");
if !empty_deps && tokio::fs::metadata(&file).await.is_ok() {
let mut file = File::open(&file).await?;
let mut buf = vec![];
file.read_to_end(&mut buf).await?;
content.push_str(&base64::engine::general_purpose::STANDARD.encode(&buf));
let mut buf = String::default();
file.read_to_string(&mut buf).await?;
content.push_str(&buf);
} else {
content.push_str(&EMPTY_FILE);
}
@@ -277,7 +290,7 @@ pub async fn install_bun_lockfile(
.env_clear()
.envs(PROXY_ENVS.clone())
.envs(common_bun_proc_envs)
.args(vec!["install"])
.args(vec!["install", "--save-text-lockfile"])
.stdout(Stdio::piped())
.stderr(Stdio::piped());
@@ -785,26 +798,30 @@ async fn compute_bundle_local_and_remote_path(
}
pub async fn prepare_job_dir(reqs: &str, job_dir: &str) -> Result<()> {
let (pkg, lock, empty) = split_lockfile(reqs);
let (pkg, lock, empty, is_binary) = split_lockfile(reqs);
let _ = write_file(job_dir, "package.json", pkg)?;
if !empty {
if let Some(lock) = lock {
let _ = write_lockb(lock, job_dir).await?;
let _ = write_lock(lock, job_dir, is_binary).await?;
}
}
Ok(())
}
async fn write_lockb(splitted_lockb_2: &str, job_dir: &str) -> Result<()> {
write_file_binary(
job_dir,
"bun.lockb",
&base64::engine::general_purpose::STANDARD
.decode(splitted_lockb_2)
.map_err(|_| error::Error::InternalErr("Could not decode bun.lockb".to_string()))?,
)
.await?;
async fn write_lock(splitted_lockb_2: &str, job_dir: &str, is_binary: bool) -> Result<()> {
if is_binary {
write_file_binary(
job_dir,
"bun.lockb",
&base64::engine::general_purpose::STANDARD
.decode(splitted_lockb_2)
.map_err(|_| error::Error::InternalErr(format!("Could not decode bun.lockb")))?,
)
.await?;
} else {
write_file(job_dir, "bun.lock", splitted_lockb_2)?;
};
Ok(())
}
@@ -900,26 +917,26 @@ pub async fn handle_bun_job(
} else if let Some(codebase) = codebase.as_ref() {
pull_codebase(&job.workspace_id, codebase, job_dir).await?;
} else if let Some(reqs) = requirements_o.as_ref() {
let (pkg, lock, empty) = split_lockfile(reqs);
let (pkg, lock, empty, is_binary) = split_lockfile(reqs);
if lock.is_none() && !annotation.npm {
return Err(error::Error::ExecutionErr(
format!("Invalid requirements, expected to find //bun.lockb split pattern in reqs. Found: |{reqs}|")
format!("Invalid requirements, expected to find //bun.lock{} split pattern in reqs. Found: |{reqs}|", if is_binary {"b"} else {""})
));
}
let _ = write_file(job_dir, "package.json", pkg)?;
let lockb = if annotation.npm { "" } else { lock.unwrap() };
let lock = if annotation.npm { "" } else { lock.unwrap() };
if !empty {
let mut skip_install = false;
let mut create_buntar = false;
let mut buntar_path = "".to_string();
if !annotation.npm {
let _ = write_lockb(lockb, job_dir).await?;
let _ = write_lock(lock, job_dir, is_binary).await?;
let mut sha_path = sha2::Sha256::new();
sha_path.update(lockb.as_bytes());
sha_path.update(lock.as_bytes());
let buntar_name =
base64::engine::general_purpose::URL_SAFE.encode(sha_path.finalize());
@@ -962,7 +979,7 @@ pub async fn handle_bun_job(
Some(&vec![
"main.ts".to_string(),
"package.json".to_string(),
"bun.lockb".to_string(),
if is_binary { "bun.lockb" } else { "bun.lock" }.to_string(),
"shared".to_string(),
"bunfig.toml".to_string(),
]),
@@ -1592,25 +1609,29 @@ pub async fn start_worker(
if let Some(codebase) = codebase.as_ref() {
pull_codebase(w_id, codebase, job_dir).await?;
} else if let Some(reqs) = requirements_o {
let (pkg, lock, empty) = split_lockfile(&reqs);
let (pkg, lock, empty, is_binary) = split_lockfile(&reqs);
if lock.is_none() {
return Err(error::Error::ExecutionErr(
format!("Invalid requirements, expected to find //bun.lockb split pattern in reqs. Found: |{reqs}|")
));
}
let _ = write_file(job_dir, "package.json", pkg)?;
let lockb = lock.unwrap();
let lock = lock.unwrap();
if !empty {
let _ = write_file_binary(
job_dir,
"bun.lockb",
&base64::engine::general_purpose::STANDARD
.decode(lockb)
.map_err(|_| {
error::Error::InternalErr("Could not decode bun.lockb".to_string())
})?,
)
.await?;
if is_binary {
let _ = write_file_binary(
job_dir,
"bun.lockb",
&base64::engine::general_purpose::STANDARD
.decode(lock)
.map_err(|_| {
error::Error::InternalErr("Could not decode bun.lockb".to_string())
})?,
)
.await?;
} else {
write_file(job_dir, "bun.lock", lock)?;
}
install_bun_lockfile(
&mut mem_peak,
+1 -1
View File
@@ -2,7 +2,7 @@ import { sleep } from "https://deno.land/x/sleep@v1.2.1/mod.ts";
import * as windmill from "https://deno.land/x/windmill@v1.174.0/mod.ts";
import * as api from "https://deno.land/x/windmill@v1.174.0/windmill-api/index.ts";
export const VERSION = "v1.447.6";
export const VERSION = "v1.448.0";
export async function login(email: string, password: string): Promise<string> {
return await windmill.UserService.login({
+1 -1
View File
@@ -60,7 +60,7 @@ export {
// }
// });
export const VERSION = "1.447.6";
export const VERSION = "1.448.0";
const command = new Command()
.name("wmill")
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "windmill-components",
"version": "1.447.6",
"version": "1.448.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "windmill-components",
"version": "1.447.6",
"version": "1.448.0",
"license": "AGPL-3.0",
"dependencies": {
"@anthropic-ai/sdk": "^0.32.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "windmill-components",
"version": "1.447.6",
"version": "1.448.0",
"scripts": {
"dev": "vite dev",
"build": "vite build",
File diff suppressed because it is too large Load Diff
@@ -12,7 +12,6 @@
import PropertyEditor from './schema/PropertyEditor.svelte'
import SimpleEditor from './SimpleEditor.svelte'
import { createEventDispatcher } from 'svelte'
import ToggleButton from './common/toggleButton-v2/ToggleButton.svelte'
import ToggleButtonGroup from './common/toggleButton-v2/ToggleButtonGroup.svelte'
import Label from './Label.svelte'
@@ -23,6 +22,7 @@
import SchemaFormDnd from './schema/SchemaFormDND.svelte'
import { deepEqual } from 'fast-equals'
import { tweened } from 'svelte/motion'
import type { SchemaDiff } from '$lib/components/schema/schemaUtils'
export let schema: Schema | any
export let schemaSkippedValues: string[] = []
@@ -49,6 +49,9 @@
export let previewSchema: Record<string, any> | undefined = undefined
export let editPanelInitialSize: number | undefined = undefined
export let editPanelSize = 0
export let diff: Record<string, SchemaDiff> = {}
export let disableDnd: boolean = false
export let shouldDispatchChanges: boolean = false
const dispatch = createEventDispatcher()
@@ -232,6 +235,11 @@
let pannelButtonWidth: number = 0
export let pannelExtraButtonWidth: number = 0
export function updateJson() {
schemaString = JSON.stringify(schema, null, '\t')
editor?.setCode(schemaString)
}
</script>
<div class="w-full h-full">
@@ -247,37 +255,60 @@
<Splitpanes class="splitter-hidden w-full">
{#if !noPreview}
<Pane bind:size={inputPanelSize} minSize={20}>
<div class="flex flex-col pr-2 gap-2">
<div
class="h-full flex flex-col gap-2 {$$slots.openEditTab && editPanelSize > 0
? 'pr-[38px]'
: 'pr-2'}"
>
{#if $$slots.addProperty}
<div class="w-full justify-left pr-2">
<div style={`width: calc(100% - ${pannelButtonWidth - pannelExtraButtonWidth}px);`}>
<div class="w-full justify-left pr-2 grow-0">
<div
style={editPanelSize > 0
? `width: 100%;`
: `width: calc(100% - ${pannelButtonWidth - pannelExtraButtonWidth}px);`}
>
<slot name="addProperty" />
</div>
</div>
{/if}
<SchemaFormDnd
schema={previewSchema ? previewSchema : schema}
{dndType}
bind:args
on:click={(e) => {
opened = e.detail
}}
on:reorder={(e) => {
schema.order = e.detail
schema = schema
dispatch('change', schema)
}}
on:change={() => {
schema = schema
dispatch('change', schema)
}}
{lightweightMode}
prettifyHeader={isAppInput}
disabled={!!previewSchema}
/>
<div
class="min-h-0 overflow-y-auto grow rounded-md {$$slots.runButton
? 'flex flex-col gap-2'
: ''}"
>
<SchemaFormDnd
nestedClasses={'flex flex-col gap-1'}
schema={previewSchema ? previewSchema : schema}
{dndType}
{disableDnd}
bind:args
on:click={(e) => {
opened = e.detail
}}
on:reorder={(e) => {
schema.order = e.detail
schema = schema
dispatch('change', schema)
}}
on:change={() => {
schema = schema
dispatch('change', schema)
}}
{lightweightMode}
prettifyHeader={isAppInput}
disabled={!!previewSchema}
{diff}
on:acceptChange
on:rejectChange
on:nestedChange={() => {
dispatch('change', schema)
}}
{shouldDispatchChanges}
/>
<slot name="runButton" />
<slot name="runButton" />
</div>
</div>
</Pane>
{/if}
@@ -425,7 +456,6 @@
{isAppInput}
on:change={() => {
schema = schema
// console.log('schema', schema)
dispatch('change', schema)
}}
>
@@ -440,8 +470,6 @@
const isS3 = e.detail == 'S3'
const isOneOf = e.detail == 'oneOf'
selected = e.detail
const emptyProperty = {
contentEncoding: undefined,
enum_: undefined,
@@ -508,8 +536,12 @@
type: e.detail
}
}
schema = schema
dispatch('change', schema)
// No better solution than this, needs future rework
setTimeout(() => {
schema = schema
dispatch('change', schema)
}, 100)
dispatch('schemaChange')
}}
>
{#each [['String', 'string'], ['Number', 'number'], ['Integer', 'integer'], ['Object', 'object'], ['OneOf', 'oneOf'], ['Array', 'array'], ['Boolean', 'boolean'], ['S3 Object', 'S3']] as x}
@@ -558,6 +590,7 @@
on:schemaChange={(e) => {
schema = schema
dispatch('change', schema)
dispatch('schemaChange')
}}
/>
{/if}
@@ -27,7 +27,7 @@
error = e
}
}
loadSchema()
$: $flowStore && $flowStateStore && loadSchema()
function handleClick() {
selected = !selected
@@ -51,6 +51,8 @@
if (event.key === 'Escape' && selected) {
selected = false
dispatch('select', undefined)
event.stopPropagation()
event.preventDefault()
}
}
@@ -41,6 +41,7 @@
let jobProgressReset: () => void
export function test() {
renderCount++
runPreview($previewArgs, undefined)
}
@@ -159,53 +160,53 @@
on:close={captureLibraryDrawer?.toggleDrawer}
>
<div class="h-full flex flex-col gap-2">
<CapturesInputs
flowPath={$pathStore}
headless={false}
addButton={true}
on:select={(e) => {
selectedCapture = e.detail
}}
on:openTriggers={(e) => {
dispatch('openTriggers', e.detail)
captureLibraryDrawer?.closeDrawer()
}}
/>
<div class="min-h-0 grow h-full">
<CapturesInputs
flowPath={$pathStore}
headless={false}
addButton={true}
on:select={(e) => {
selectedCapture = e.detail
}}
on:openTriggers={(e) => {
dispatch('openTriggers', e.detail)
captureLibraryDrawer?.closeDrawer()
}}
/>
</div>
<div class="h-full overflow-hidden min-h-0 flex flex-col justify-between">
<div class="w-full flex flex-col min-h-0 gap-2 px-2 py-2 grow">
<div class="w-full flex flex-col">
<Button
color="blue"
btnClasses="w-full"
size="sm"
spacingSize="xl"
on:click={async () => {
$previewArgs = JSON.parse(JSON.stringify(selectedCapture))
captureLibraryDrawer?.closeDrawer()
renderCount++
}}
disabled={!selectedCapture}
<div class="w-full flex flex-col gap-2 px-2 py-2 h-[50%]">
<div class="w-full flex flex-col">
<Button
color="blue"
btnClasses="w-full"
size="sm"
spacingSize="xl"
on:click={async () => {
$previewArgs = JSON.parse(JSON.stringify(selectedCapture))
captureLibraryDrawer?.closeDrawer()
renderCount++
}}
disabled={!selectedCapture}
>
<ArrowLeftIcon class="w-4 h-4 mr-2" />
Use input
</Button>
</div>
<div class="w-full min-h-0 grow overflow-auto">
{#if typeof selectedCapture == 'string' && selectedCapture == 'WINDMILL_TOO_BIG'}
<div class="text-secondary mt-2">
Payload too big to preview but can still be loaded</div
>
<ArrowLeftIcon class="w-4 h-4 mr-2" />
Use input
</Button>
</div>
<div class="w-full min-h-0 grow overflow-auto">
{#if typeof selectedCapture == 'string' && selectedCapture == 'WINDMILL_TOO_BIG'}
<div class="text-secondary mt-2">
Payload too big to preview but can still be loaded</div
>
{:else if Object.keys(selectedCapture || {}).length > 0}
<div class=" overflow-auto h-full p-2">
<ObjectViewer json={selectedCapture} />
</div>
{:else}
<div class="text-center text-tertiary">
Select an Input to preview scripts arguments
</div>
{/if}
</div>
{:else if Object.keys(selectedCapture || {}).length > 0}
<div class=" overflow-auto h-full p-2">
<ObjectViewer json={selectedCapture} />
</div>
{:else}
<div class="text-center text-tertiary">
Select an Input to preview scripts arguments
</div>
{/if}
</div>
</div>
</div>
@@ -102,6 +102,8 @@
if (event.key === 'Escape' && selected) {
selected = undefined
dispatch('select', undefined)
event.stopPropagation()
event.preventDefault()
}
}
@@ -119,16 +119,22 @@
function handleKeydown(event: KeyboardEvent) {
if (event.key === 'Escape' && isEditing) {
setEditing(null)
event.stopPropagation()
event.preventDefault()
} else if (event.key === 'Escape' && selectedInput) {
selectedInput = null
selectedArgs = undefined
dispatch('select', undefined)
event.stopPropagation()
event.preventDefault()
}
if (event.key === 'Enter' && isEditing) {
updateInput(isEditing).then(() => {
setEditing(null)
})
event.stopPropagation()
event.preventDefault()
}
}
+71 -4
View File
@@ -15,6 +15,7 @@
import LightweightArgInput from './LightweightArgInput.svelte'
import { deepEqual } from 'fast-equals'
import { dragHandleZone, type Options as DndOptions } from '@windmill-labs/svelte-dnd-action'
import type { SchemaDiff } from '$lib/components/schema/schemaUtils'
export let schema: Schema | any
export let schemaSkippedValues: string[] = []
@@ -46,6 +47,10 @@
| { type: 'hash'; hash: string }
| undefined = undefined
export let lightHeader = false
export let diff: Record<string, SchemaDiff> = {}
export let nestedParent: { label: string; nestedParent: any | undefined } | undefined = undefined
export let shouldDispatchChanges = false
export let nestedClasses = ''
const dispatch = createEventDispatcher()
@@ -166,7 +171,9 @@
{/if}
<div
class="w-full {$$props.class} {flexWrap ? 'flex flex-row flex-wrap gap-x-6 ' : ''}"
class="w-full {$$props.class} {flexWrap
? 'flex flex-row flex-wrap gap-x-6 '
: ''} {nestedClasses}"
use:dragHandleZone={dndConfig ?? { items: [], dragDisabled: true }}
on:finalize
on:consider
@@ -174,12 +181,61 @@
{#if keys.length > 0}
{#each fields as item, i (item.id)}
{@const argName = item.value}
<div>
<div
class={typeof diff[argName] === 'object' && diff[argName].diff !== 'same'
? 'bg-red-300 dark:bg-red-800 rounded-md'
: ''}
>
<!-- svelte-ignore a11y-click-events-have-key-events -->
{#if !schemaSkippedValues.includes(argName) && keys.includes(argName)}
{#if typeof diff[argName] === 'object' && diff[argName].oldSchema}
{@const formerProperty = diff[argName].oldSchema}
<ArgInput
{disablePortal}
{resourceTypes}
{prettifyHeader}
autofocus={i == 0 && autofocus ? true : null}
label={argName}
description={formerProperty?.description}
value={args[argName]}
type={formerProperty?.type}
oneOf={formerProperty?.oneOf}
required={formerProperty?.required}
pattern={formerProperty?.pattern}
valid={inputCheck[argName]}
defaultValue={structuredClone(formerProperty?.default)}
enum_={formerProperty?.enum}
format={formerProperty?.format}
contentEncoding={formerProperty?.contentEncoding}
customErrorMessage={formerProperty?.customErrorMessage}
properties={formerProperty?.properties}
order={formerProperty?.order}
nestedRequired={formerProperty?.required}
itemsType={formerProperty?.items}
disabled={disabledArgs.includes(argName) || disabled || formerProperty?.disabled}
{compact}
{variableEditor}
{itemPicker}
{pickForField}
password={linkedSecret == argName}
extra={formerProperty}
{showSchemaExplorer}
simpleTooltip={schemaFieldTooltip[argName]}
{onlyMaskPassword}
nullable={formerProperty?.nullable}
title={formerProperty?.title}
placeholder={formerProperty?.placeholder}
orderEditable={dndConfig != undefined}
otherArgs={args}
{helperScript}
{lightHeader}
hideNested={typeof diff[argName].diff === 'object'}
diffStatus={undefined}
/>
{/if}
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
class="flex flex-row items-center bg-surface"
class="flex flex-row items-center"
on:click={() => {
dispatch('click', argName)
}}
@@ -244,7 +300,14 @@
</LightweightArgInput>
{:else}
<ArgInput
on:change={() => dispatch('change')}
on:change={() => {
dispatch('change')
}}
on:nestedChange={() => {
dispatch('nestedChange')
}}
on:acceptChange={(e) => dispatch('acceptChange', e.detail)}
on:rejectChange={(e) => dispatch('rejectChange', e.detail)}
{disablePortal}
{resourceTypes}
{prettifyHeader}
@@ -285,6 +348,10 @@
otherArgs={args}
{helperScript}
{lightHeader}
diffStatus={diff[argName] ?? undefined}
{nestedParent}
{shouldDispatchChanges}
{nestedClasses}
>
<svelte:fragment slot="actions">
<slot name="actions" />
@@ -116,7 +116,10 @@
let placeholderVisible = false
function updatePlaceholderVisibility(value: string) {
if (!value) return
if (!value) {
placeholderVisible = true
return
}
placeholderVisible = value.trim() === ''
}
@@ -1,7 +1,7 @@
<script lang="ts">
import { Button } from '$lib/components/common'
import { ButtonType } from '$lib/components/common/button/model'
import { getContext } from 'svelte'
import { getContext, tick } from 'svelte'
import FlowCard from '../common/FlowCard.svelte'
import type { FlowEditorContext } from '../types'
import Drawer from '$lib/components/common/drawer/Drawer.svelte'
@@ -18,21 +18,30 @@
CornerDownLeft,
Pen,
ChevronRight,
ChevronDown,
Plus,
History,
Braces,
Code,
Save,
X
X,
Check
} from 'lucide-svelte'
import CaptureIcon from '$lib/components/triggers/CaptureIcon.svelte'
import FlowPreviewContent from '$lib/components/FlowPreviewContent.svelte'
import FlowInputEditor from './FlowInputEditor.svelte'
import CapturesInputs from '$lib/components/CapturesInputs.svelte'
import { twMerge } from 'tailwind-merge'
import ButtonDropDown from '$lib/components/meltComponents/ButtonDropDown.svelte'
import CaptureButton from '$lib/components/triggers/CaptureButton.svelte'
import {
type SchemaDiff,
getFullPath,
setNestedProperty,
getNestedProperty,
schemaFromDiff,
computeDiff,
applyDiff
} from '$lib/components/schema/schemaUtils'
import SideBarTab from '$lib/components/meltComponents/SideBarTab.svelte'
export let noEditor: boolean
export let disabled: boolean
@@ -45,14 +54,18 @@
let previewSchema: Record<string, any> | undefined = undefined
let payloadData: Record<string, any> | undefined = undefined
let previewArguments: Record<string, any> | undefined = $previewArgs
let editOptionsOpen = false
let dropdownItems: Array<{
label: string
onClick: () => void
disabled?: boolean
}> = []
let diff: Record<string, SchemaDiff> = {}
let editPanelSize = $flowInputEditorState?.editPanelSize ?? 0
let selectedSchema: Record<string, any> | undefined = undefined
let runDisabled: boolean = false
let editableSchemaForm: EditableSchemaForm | undefined = undefined
let savedPreviewArgs: Record<string, any> | undefined = undefined
function updateEditPanelSize(size: number | undefined) {
if (!$flowInputEditorState) return
if (!size || size === 0) {
@@ -73,7 +86,8 @@
disabled:
!$flowInputEditorState?.selectedTab ||
$flowInputEditorState?.selectedTab === 'inputEditor',
icon: Pen
icon: Pen,
selected: $flowInputEditorState?.selectedTab === 'inputEditor'
},
{
label: 'Trigger captures',
@@ -81,7 +95,8 @@
handleEditSchema('captures')
},
disabled: $flowInputEditorState?.selectedTab === 'captures',
icon: CaptureIcon
icon: CaptureIcon,
selected: $flowInputEditorState?.selectedTab === 'captures'
},
{
label: 'History',
@@ -89,7 +104,8 @@
handleEditSchema('history')
},
disabled: $flowInputEditorState?.selectedTab === 'history',
icon: History
icon: History,
selected: $flowInputEditorState?.selectedTab === 'history'
},
{
label: 'Json payload',
@@ -97,14 +113,16 @@
handleEditSchema('json')
},
disabled: $flowInputEditorState?.selectedTab === 'json',
icon: Braces
icon: Braces,
selected: $flowInputEditorState?.selectedTab === 'json'
},
{
label: "First step's inputs",
onClick: () => {
handleEditSchema('firstStepInputs')
},
icon: Code
icon: Code,
selected: $flowInputEditorState?.selectedTab === 'firstStepInputs'
},
{
label: 'Saved inputs',
@@ -112,9 +130,10 @@
handleEditSchema('savedInputs')
},
disabled: $flowInputEditorState?.selectedTab === 'savedInputs',
icon: Save
icon: Save,
selected: $flowInputEditorState?.selectedTab === 'savedInputs'
}
].filter((item) => !item.disabled)
]
}
function handleEditSchema(editTab?: any) {
@@ -130,7 +149,8 @@
}
}
function schemaFromPayload(payload: any) {
function schemaFromPayload(payloadData: any) {
const payload = structuredClone(payloadData)
const parsed = JSON.parse(JSON.stringify(payload))
if (!parsed) {
@@ -157,43 +177,59 @@
function handleKeydown(event: KeyboardEvent) {
if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {
runPreview()
} else if (event.key === 'Enter' && previewSchema && !preventEnter) {
applySchemaAndArgs()
connectFirstNode()
event.stopPropagation()
event.preventDefault()
}
}
function runPreview() {
if (previewArguments) {
$previewArgs = previewArguments
$previewArgs = structuredClone(previewArguments)
}
previewOpen = true
flowPreviewContent?.test()
}
function updatePreviewSchemaAndArgs(payload: any) {
payloadData = payload
if (!payload) {
payloadData = undefined
selectedSchema = undefined
updatePreviewArguments(undefined)
updatePreviewSchema(undefined)
return
}
payloadData = structuredClone(payload)
selectedSchema = schemaFromPayload(payloadData)
updatePreviewSchema(selectedSchema)
updatePreviewArguments(payloadData)
}
async function updatePreviewSchema(newSchema: Record<string, any> | undefined) {
if (!newSchema) {
previewSchema = undefined
if (runDisabled) {
await tick()
runDisabled = false
}
diff = {}
return
}
previewSchema = schemaFromPayload(payload)
updatePreviewArguments(payload)
diff = {}
const diffSchema = computeDiff(newSchema, $flowStore.schema)
diff = diffSchema
previewSchema = schemaFromDiff(diffSchema, $flowStore.schema)
runDisabled = true
}
function applySchemaAndArgs() {
if (!previewSchema) {
return
}
$flowStore.schema = previewSchema
async function applySchemaAndArgs() {
$flowStore.schema = applyDiff($flowStore.schema, diff)
if (previewArguments) {
$previewArgs = previewArguments
savedPreviewArgs = structuredClone(previewArguments)
}
if ($flowInputEditorState) {
$flowInputEditorState.selectedTab = undefined
}
}
function applySchema() {
$flowStore.schema = previewSchema
updatePreviewSchemaAndArgs(undefined)
if ($flowInputEditorState) {
$flowInputEditorState.selectedTab = undefined
}
@@ -201,41 +237,28 @@
function updatePreviewArguments(payloadData: Record<string, any> | undefined) {
if (!payloadData) {
previewArguments = $previewArgs
previewArguments = savedPreviewArgs
return
}
previewArguments = payloadData
savedPreviewArgs = structuredClone(previewArguments)
previewArguments = structuredClone(payloadData)
}
let jsonValid = false
function updatePayloadFromJson(jsonInput: string) {
if (jsonInput === undefined || jsonInput === null || jsonInput.trim() === '') {
updatePreviewSchemaAndArgs(undefined)
jsonValid = false
return
}
try {
const parsed = JSON.parse(jsonInput)
updatePreviewSchemaAndArgs(parsed)
jsonValid = true
} catch (error) {
updatePreviewSchemaAndArgs(undefined)
jsonValid = false
}
}
let tabButtonWidth = 0
const TAB_TITLES: Record<string, string> = {
inputEditor: 'Input editor',
captures: 'Captures',
history: 'History',
savedInputs: 'Saved inputs',
json: 'JSON',
firstStepInputs: 'First step',
undefined: ''
}
let connectFirstNode: () => void = () => {}
let init = false
@@ -249,6 +272,54 @@
$: $flowInputEditorState && ((dropdownItems = getDropdownItems()), initPayloadData())
let preventEnter = false
async function acceptChange(arg: { label: string; nestedParent: any | undefined }) {
handleChange(arg, $flowStore.schema, diff, (newSchema) => {
$flowStore.schema = newSchema
})
}
async function rejectChange(arg: { label: string; nestedParent: any | undefined }) {
const revertDiff = computeDiff($flowStore.schema, selectedSchema)
handleChange(arg, selectedSchema, revertDiff, (newSchema) => {
selectedSchema = newSchema
})
}
function handleChange(
arg: { label: string; nestedParent: any | undefined },
currentSchema: Record<string, any> | undefined,
diffSchema: Record<string, SchemaDiff> | undefined,
updateCurrentSchema: (newSchema: Record<string, any> | undefined) => void
) {
if (!diff || !currentSchema) {
return
}
const path = getFullPath(arg)
const parentPath = path.slice(0, -1)
const diffStatus = getNestedProperty({ diff: diffSchema }, path, 'diff')
const schema = getNestedProperty(currentSchema, parentPath, 'properties')
const localDiff = {
[arg.label]: diffStatus
}
const schemaUpdated = applyDiff(schema, localDiff)
if (parentPath.length > 0) {
setNestedProperty(currentSchema, parentPath, schemaUpdated)
} else {
updateCurrentSchema(schemaUpdated)
}
diff = computeDiff(selectedSchema, $flowStore.schema)
previewSchema = schemaFromDiff(diff, $flowStore.schema)
}
function resetArgs() {
if (!previewSchema) {
previewArguments = undefined
savedPreviewArgs = undefined
}
}
</script>
<!-- Add svelte:window to listen for keyboard events -->
@@ -269,6 +340,7 @@
{#if !disabled}
<div class="py-2 px-4 h-full">
<EditableSchemaForm
bind:this={editableSchemaForm}
bind:schema={$flowStore.schema}
isFlowInput
on:edit={(e) => {
@@ -284,78 +356,84 @@
bind:editPanelSize
editPanelInitialSize={$flowInputEditorState?.editPanelSize}
pannelExtraButtonWidth={$flowInputEditorState?.editPanelSize ? tabButtonWidth : 0}
{diff}
disableDnd={!!previewSchema}
on:rejectChange={(e) => {
rejectChange(e.detail).then(() => {
updatePreviewSchema(selectedSchema)
})
}}
on:acceptChange={(e) => {
acceptChange(e.detail).then(() => {
updatePreviewSchema(selectedSchema)
})
}}
shouldDispatchChanges={true}
on:change={() => {
previewArguments = previewArguments
if (!previewSchema) {
savedPreviewArgs = structuredClone(previewArguments)
}
}}
on:schemaChange={() => {
resetArgs()
}}
>
<svelte:fragment slot="openEditTab">
<div
class={twMerge(
'flex flex-row divide-x rounded-md bg-surface overflow-hidden',
!!$flowInputEditorState?.selectedTab ? 'rounded-r-none' : '',
ButtonType.ColorVariants.blue.divider
)}
>
<button
on:click={() => {
handleEditSchema()
}}
title={!!$flowInputEditorState?.selectedTab
? 'Close input editor'
: 'Open input editor'}
class={ButtonType.ColorVariants.blue.contained}
>
<div class="p-2 center-center">
<svelte:component
this={!!$flowInputEditorState?.selectedTab ? ChevronRight : Pen}
size={14}
/>
</div>
</button>
<ButtonDropDown
{dropdownItems}
closeOnClick={true}
bind:open={editOptionsOpen}
placement="bottom-end"
>
<div
class={twMerge(
'p-2 center-center hover:bg-surface-hover',
ButtonType.ColorVariants.blue.contained,
'flex flex-row items-center rounded-br-md',
'transition-all duration-150 ease-in-out overflow-hidden whitespace-nowrap',
!!$flowInputEditorState?.selectedTab ? 'w-[122px] px-3' : 'w-[30px]'
)}
bind:clientWidth={tabButtonWidth}
>
<div class="flex flex-row items-center gap-1 justify-between w-full">
{#if !!$flowInputEditorState?.selectedTab}
<h2 class="text-xs">{TAB_TITLES[$flowInputEditorState?.selectedTab]}</h2>
{/if}
<ChevronDown size={14} />
</div>
</div>
</ButtonDropDown>
<div class={twMerge('flex flex-row divide-x', ButtonType.ColorVariants.blue.divider)}>
<SideBarTab {dropdownItems} fullMenu={!!$flowInputEditorState?.selectedTab}>
<svelte:fragment slot="close button">
<button
on:click={() => {
handleEditSchema()
}}
title={!!$flowInputEditorState?.selectedTab
? 'Close input editor'
: 'Open input editor'}
class={twMerge(
ButtonType.ColorVariants.blue.contained,
!!$flowInputEditorState?.selectedTab
? 'rounded-tl-md border-l border-t'
: 'rounded-md border'
)}
>
<div class="p-2 center-center">
<svelte:component
this={!!$flowInputEditorState?.selectedTab ? ChevronRight : Pen}
size={14}
/>
</div>
</button>
</svelte:fragment>
</SideBarTab>
</div>
</svelte:fragment>
<svelte:fragment slot="addProperty">
{#if !!previewSchema}
<div
class={twMerge(
'bg-blue-50 border-blue-200 border dark:bg-blue-900/40 dark:border-blue-700/40 text-xs p-2 w-full flex flex-row gap-2 items-center justify-left rounded-md',
'text-blue-700 dark:text-blue-100',
'relative'
)}
>
<span> Preview only, update schema to save.</span>
<div class="flex flex-row items-center gap-2 absolute right-2">
<Button
variant="contained"
color="light"
size="xs2"
startIcon={{ icon: X }}
shortCut={{ key: 'esc', withoutModifier: true }}
nonCaptureEvent
/>
</div>
<div class="flex flex-row items-center gap-2 right-2 justify-end">
<Button
size="xs"
color="green"
disabled={!previewSchema}
shortCut={{ Icon: CornerDownLeft, hide: false, withoutModifier: true }}
startIcon={{ icon: Check }}
on:click={() => {
applySchemaAndArgs()
connectFirstNode()
}}
>
{Object.values(diff).every((el) => el.diff === 'same')
? 'Apply args'
: 'Update schema'}
</Button>
<Button
variant="border"
color="light"
size="xs"
startIcon={{ icon: X }}
shortCut={{ key: 'esc', withoutModifier: true }}
nonCaptureEvent
/>
</div>
{:else}
<AddPropertyV2
@@ -363,6 +441,14 @@
bind:this={addProperty}
on:change={() => {
$flowStore = $flowStore
if (editableSchemaForm) {
editableSchemaForm.updateJson()
}
}}
on:addNew={(e) => {
handleEditSchema('inputEditor')
editableSchemaForm?.openField(e.detail)
$flowStore = $flowStore
}}
>
<svelte:fragment slot="trigger">
@@ -378,9 +464,7 @@
<svelte:fragment slot="extraTab">
{#if $flowInputEditorState?.selectedTab === 'history'}
<FlowInputEditor
disabled={!payloadData}
on:applySchemaAndArgs={applySchemaAndArgs}
on:applySchema={applySchema}
title="History"
on:destroy={() => {
updatePreviewSchemaAndArgs(undefined)
}}
@@ -396,12 +480,10 @@
</FlowInputEditor>
{:else if $flowInputEditorState?.selectedTab === 'captures'}
<FlowInputEditor
disabled={!payloadData}
on:applySchemaAndArgs={applySchemaAndArgs}
on:applySchema={applySchema}
on:destroy={() => {
updatePreviewSchemaAndArgs(undefined)
}}
title="Trigger captures"
>
<svelete:fragment slot="action">
<div class="center-center">
@@ -417,13 +499,10 @@
</FlowInputEditor>
{:else if $flowInputEditorState?.selectedTab === 'savedInputs'}
<FlowInputEditor
{preventEnter}
disabled={!payloadData}
on:applySchemaAndArgs={applySchemaAndArgs}
on:applySchema={applySchema}
on:destroy={() => {
updatePreviewSchemaAndArgs(undefined)
}}
title="Saved inputs"
>
<SavedInputsPicker
flowPath={initialPath}
@@ -438,13 +517,10 @@
</FlowInputEditor>
{:else if $flowInputEditorState?.selectedTab === 'json'}
<FlowInputEditor
{preventEnter}
disabled={!jsonValid}
on:applySchemaAndArgs={applySchemaAndArgs}
on:applySchema={applySchema}
on:destroy={() => {
updatePreviewSchemaAndArgs(undefined)
}}
title="Json payload"
>
<SimpleEditor
on:focus={() => {
@@ -470,22 +546,25 @@
</FlowInputEditor>
{:else if $flowInputEditorState?.selectedTab === 'firstStepInputs'}
<FlowInputEditor
disabled={!previewSchema}
on:applySchemaAndArgs={() => {
applySchemaAndArgs()
connectFirstNode()
}}
on:applySchema={applySchema}
on:destroy={() => {
updatePreviewSchemaAndArgs(undefined)
connectFirstNode = () => {}
}}
title="First step's inputs"
>
<FirstStepInputs
on:connectFirstNode={({ detail }) => {
connectFirstNode = detail.connectFirstNode
}}
on:select={(e) => {
previewSchema = e.detail ?? undefined
if (e.detail) {
const diffSchema = computeDiff(e.detail, $flowStore.schema)
diff = diffSchema
previewSchema = schemaFromDiff(diffSchema, $flowStore.schema)
runDisabled = true
} else {
updatePreviewSchemaAndArgs(undefined)
}
}}
/>
</FlowInputEditor>
@@ -496,7 +575,7 @@
<Button
color="dark"
btnClasses="w-fit"
disabled={!!previewSchema}
disabled={runDisabled}
size="xs"
shortCut={{ Icon: CornerDownLeft, hide: false }}
on:click={() => {
@@ -1,51 +1,26 @@
<script lang="ts">
import { createEventDispatcher, onDestroy } from 'svelte'
import Section from '$lib/components/Section.svelte'
import Button from '$lib/components/common/button/Button.svelte'
import { CornerDownLeft, Save } from 'lucide-svelte'
export let name: string = ''
export let disabled = false
export let preventEnter = false
const dispatch = createEventDispatcher()
function applySchemaAndArgs() {
dispatch('applySchemaAndArgs')
}
export let title: string = ''
onDestroy(() => {
dispatch('destroy')
})
</script>
<svelte:window
on:keydown={(e) => {
if (e.key === 'Enter' && !preventEnter) {
applySchemaAndArgs()
e.preventDefault()
}
}}
/>
<div class="flex flex-col h-full px-3 pb-3">
<div
class="items-center grow-0 flex flex-row justify-between gap-2 data-schema-picker min-h-[40px]"
>
<h2 class="font-semibold text-secondary text-sm flex flex-row items-center gap-1 leading-6">
{title}
</h2>
<slot name="action" />
</div>
<div class="h-full p-2">
<Section label={name} class="h-full" small={true}>
<svelte:fragment slot="header">
<slot name="header" />
</svelte:fragment>
<svelte:fragment slot="action">
<div class="flex flex-row gap-2 data-schema-picker">
<slot name="action" />
<Button
size="xs2"
color="dark"
{disabled}
shortCut={{ Icon: CornerDownLeft, hide: false, withoutModifier: true }}
startIcon={{ icon: Save }}
on:click={applySchemaAndArgs}>Update schema</Button
>
</div>
</svelte:fragment>
<div class="w-full min-h-0 grow">
<slot />
</Section>
</div>
</div>
@@ -0,0 +1,69 @@
<script lang="ts">
export let dropdownItems: Array<{
label: string
onClick: () => void
icon?: any
selected?: boolean
}> = []
export let fullMenu: boolean = false
let open = false
let timeout: NodeJS.Timeout | null = null
function handleMouseEnter() {
if (!fullMenu) return
timeout = setTimeout(() => {
open = true
}, 500)
}
function handleMouseLeave() {
if (timeout) {
clearTimeout(timeout)
timeout = null
}
open = false
}
</script>
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
class="flex flex-col relative !overflow-visible"
on:mouseenter={handleMouseEnter}
on:mouseleave={handleMouseLeave}
>
<slot name="close button" />
{#if fullMenu}
<div
class="absolute flex-col top-[30px] left-0 z-50 bg-surface border-l border-b {open
? 'rounded-md rounded-tl-none overflow-hidden shadow-md'
: 'rounded-bl-md'}"
>
{#each dropdownItems as item}
<button
class="hover:bg-surface-hover p-2 transition-colors duration-150 w-full {item.selected
? 'bg-surface-selected'
: 'text-secondary'}"
on:click={() => {
item.onClick()
open = false
}}
>
<div class="flex flex-row items-center gap-2 min-h-[20px]">
{#if item.icon}
<svelte:component this={item.icon} size={14} />
{/if}
{#if open}
<p class="text-xs text-secondary whitespace-nowrap text-left">
{item.label}
</p>
{/if}
</div>
</button>
{/each}
</div>
{/if}
</div>
@@ -138,7 +138,9 @@
if (Object.keys(modifiedProperties).includes(argName)) {
delete modifiedProperties[argName]
modifiedObject.required = schema.required.filter((arg) => arg !== argName)
if (modifiedObject.required) {
modifiedObject.required = schema.required.filter((arg) => arg !== argName)
}
if (modifiedObject.order) {
modifiedObject.order = modifiedObject.order.filter((arg) => arg !== argName)
}
@@ -7,7 +7,6 @@
} from '$lib/common'
import { emptySchema, sendUserToast } from '$lib/utils'
import { createEventDispatcher } from 'svelte'
import SimpleEditor from '../SimpleEditor.svelte'
import AddPropertyFormV2 from './AddPropertyFormV2.svelte'
export let schema: Schema | any = emptySchema()
@@ -24,13 +23,10 @@
schema = emptySchema()
}
let schemaString: string = ''
// Internal state: bound to args builder modal
let argError = ''
let editing = false
let oldArgName: string | undefined // when editing argument and changing name
let jsonEditor: SimpleEditor
reorder()
@@ -59,7 +55,7 @@
}
}
function handleAddOrEditArgument(modalProperty: ModalSchemaProperty): void {
export function handleAddOrEditArgument(modalProperty: ModalSchemaProperty): void {
// If editing the arg's name, oldName containing the old argument name must be provided
argError = ''
modalProperty.name = modalProperty.name.trim()
@@ -109,8 +105,6 @@
}
schema = schema
schemaString = JSON.stringify(schema, null, '\t')
jsonEditor?.setCode(schemaString)
if (argError !== '') {
sendUserToast(argError, true)
@@ -137,12 +131,13 @@
if (Object.keys(modifiedProperties).includes(argName)) {
delete modifiedProperties[argName]
modifiedObject.required = schema.required.filter((arg) => arg !== argName)
if (modifiedObject.required) {
modifiedObject.required = schema.required.filter((arg) => arg !== argName)
}
if (modifiedObject.order) {
modifiedObject.order = modifiedObject.order.filter((arg) => arg !== argName)
}
schema = schema
schemaString = JSON.stringify(schema, null, '\t')
dispatch('change', schema)
} else {
throw Error('Argument not found!')
@@ -163,6 +158,7 @@
selectedType: 'string',
name: e.detail.name
})
dispatch('addNew', e.detail.name)
} catch (err) {
sendUserToast(`Could not add argument: ${err}`, true)
}
@@ -154,8 +154,11 @@
<div class="flex flex-col w-full mt-2">
<Label label="Nested properties">
<svelte:self
on:change={() => (schema = schema)}
schema={schema.properties[item.value]}
on:change={() => {
schema = schema
dispatch('change', schema)
}}
bind:schema={schema.properties[item.value]}
parentId={item.value}
/>
</Label>
@@ -187,7 +190,13 @@
editTab="inputEditor"
>
<svelte:fragment slot="addProperty">
<AddPropertyV2 bind:schema on:change>
<AddPropertyV2
bind:schema
on:change
on:addNew={(e) => {
editableSchemaForm?.openField(e.detail)
}}
>
<svelte:fragment slot="trigger">
<div
class="w-full py-2 flex justify-center items-center border border-dashed rounded-md hover:bg-surface-hover"
@@ -19,6 +19,7 @@
let resourceIsTextFile: boolean = false
let addProperty: AddPropertyV2 | undefined = undefined
let editableSchemaForm: EditableSchemaForm | undefined = undefined
const dispatch = createEventDispatcher()
@@ -90,6 +91,9 @@
bind:schema
bind:this={addProperty}
on:change={() => dispatch('change', schema)}
on:addNew={(e) => {
editableSchemaForm?.openField(e.detail)
}}
>
<svelte:fragment slot="trigger">
<div
@@ -101,6 +105,7 @@
</AddPropertyV2>
{/if}
<EditableSchemaForm
bind:this={editableSchemaForm}
bind:schema
on:change={() => dispatch('change', schema)}
isFlowInput
@@ -5,7 +5,7 @@
import { GripVertical } from 'lucide-svelte'
import type { Schema } from '$lib/common'
import { deepEqual } from 'fast-equals'
import type { SchemaDiff } from '$lib/components/schema/schemaUtils'
export let dndType: string | undefined = undefined
export let schema: Schema
export let args: Record<string, any> = {}
@@ -15,6 +15,11 @@
export let disablePortal: boolean = false
export let disabled: boolean = false
export let schemaSkippedValues: string[] = []
export let nestedParent: { label: string; nestedParent: any | undefined } | undefined = undefined
export let disableDnd: boolean = false
export let shouldDispatchChanges: boolean = false
export let diff: Record<string, SchemaDiff> = {}
export let nestedClasses = ''
const dispatch = createEventDispatcher()
const flipDurationMs = 200
@@ -66,12 +71,16 @@
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
<SchemaForm
{nestedClasses}
{schemaSkippedValues}
on:click
on:change
on:reorder
on:consider={handleConsider}
on:finalize={handleFinalize}
on:acceptChange
on:rejectChange
on:nestedChange
{lightweightMode}
bind:args
{prettifyHeader}
@@ -79,17 +88,24 @@
{disablePortal}
{disabled}
bind:schema
dndConfig={{
items,
flipDurationMs,
dropTargetStyle: {},
type: dndType ?? 'top-level'
}}
dndConfig={disableDnd
? undefined
: {
items,
flipDurationMs,
dropTargetStyle: {},
type: dndType ?? 'top-level'
}}
{items}
{diff}
{nestedParent}
{shouldDispatchChanges}
>
<svelte:fragment slot="actions">
<div class="w-4 h-8 cursor-move ml-2 handle" aria-label="drag-handle" use:dragHandle>
<GripVertical size={16} />
</div>
{#if !disableDnd}
<div class="w-4 h-8 cursor-move ml-2 handle" aria-label="drag-handle" use:dragHandle>
<GripVertical size={16} />
</div>
{/if}
</svelte:fragment>
</SchemaForm>
@@ -0,0 +1,217 @@
export type SchemaDiff = {
diff: 'same' | 'added' | 'removed' | 'modified' | Record<string, SchemaDiff>
fullSchema: { [key: string]: any } | undefined
oldSchema?: { [key: string]: any } | undefined
}
function isCompatible(diff: Record<string, SchemaDiff>) {
let compatible = true
Object.values(diff).forEach((diff) => {
if (diff.diff === 'added' || diff.diff === 'modified' || diff.diff === 'removed') {
compatible = false
} else if (isRecordSchemaDiff(diff.diff)) {
compatible = isCompatible(diff.diff)
}
})
return compatible
}
function isCompatibleObject(a: any, b: any): boolean {
if (!a || !b) {
return false
}
if (a.type === 'null' || b.type === 'null') {
return true
}
if (a.type !== b.type) {
if (
(a.type === 'number' || a.type === 'integer') &&
(b.type === 'number' || b.type === 'integer')
) {
return true
}
return false
}
switch (a.type) {
case 'object':
if (a.oneOf || b.oneOf) {
//TODO: handle oneOf compatibility. here we assume that only b is oneOf
return true
}
return a.format === b.format
case 'array':
return !a.items || !b.items || a.items?.type === b.items?.type
case 'string':
return a.format === b.format
case 'boolean':
case 'number':
case 'integer':
return true
default:
return false
}
}
export function computeDiff(
previewSchema: { [key: string]: any } | undefined,
currentSchema: { [key: string]: any } | undefined
) {
if (!previewSchema || !currentSchema) {
return {}
}
const diff: Record<string, SchemaDiff> = {}
if (previewSchema?.properties) {
Object.keys(previewSchema.properties).forEach((key) => {
if (!currentSchema?.properties?.[key]) {
diff[key] = {
diff: 'added',
fullSchema: previewSchema.properties[key]
}
} else {
const previewProp = previewSchema.properties[key]
const currentProp = currentSchema.properties[key]
if (previewProp.type === 'object' && currentProp.type === 'object') {
if (JSON.stringify(previewProp) === JSON.stringify(currentProp)) {
diff[key] = { diff: 'same', fullSchema: undefined }
} else if (previewProp.oneOf || currentProp.oneOf) {
if (isCompatibleObject(previewProp, currentProp)) {
diff[key] = { diff: 'same', fullSchema: undefined }
} else {
diff[key] = { diff: 'modified', fullSchema: previewProp, oldSchema: currentProp }
}
} else if (previewProp.format || currentProp.format) {
//TODO: handle s3 object compatibility
diff[key] = { diff: 'modified', fullSchema: previewProp, oldSchema: currentProp }
} else {
const diffProp = computeDiff(previewProp, currentProp)
const checkIfSame = isCompatible(diffProp)
if (checkIfSame) {
diff[key] = { diff: 'same', fullSchema: undefined }
} else {
diff[key] = {
diff: diffProp,
fullSchema: previewProp,
oldSchema: currentProp
}
}
}
} else if (isCompatibleObject(previewProp, currentProp)) {
diff[key] = { diff: 'same', fullSchema: undefined }
} else {
diff[key] = { diff: 'modified', fullSchema: previewProp, oldSchema: currentProp }
}
}
})
}
if (currentSchema?.properties) {
Object.keys(currentSchema.properties).forEach((key) => {
if (!previewSchema?.properties?.[key]) {
diff[key] = { diff: 'removed', fullSchema: undefined }
}
})
}
return diff
}
export function schemaFromDiff(
diff: Record<string, SchemaDiff>,
schema: { [key: string]: any } | undefined
) {
if (!schema) {
return undefined
}
const newSchema = structuredClone(schema)
Object.keys(diff).forEach((key) => {
const diffValue = diff[key].diff
if (diffValue === 'added' || diffValue === 'modified') {
newSchema.properties[key] = diff[key].fullSchema
if (newSchema.order && !newSchema.order.includes(key)) {
newSchema.order.push(key)
}
} else if (isRecordSchemaDiff(diffValue)) {
// Handle nested diffs
newSchema.properties[key] = schemaFromDiff(diffValue, schema.properties[key])
}
})
return newSchema
}
export function getFullPath(arg: { label: string; nestedParent: any | undefined }): string[] {
const getPath = (current: { label: string; nestedParent: any | undefined }): string[] => {
if (!current.nestedParent) {
return [current.label]
}
return [...getPath(current.nestedParent), current.label]
}
return getPath(arg)
}
export function getNestedProperty(obj: any, path: string[], field: string = 'properties') {
return path.reduce((curr, key) => curr?.[field]?.[key], obj)
}
export function setNestedProperty(
obj: any,
path: string[],
value: any,
field: string = 'properties'
) {
const pathCopy = [...path]
const lastKey = pathCopy.pop()
const target = pathCopy.reduce((curr, key) => {
if (!(key in curr[field])) {
curr[field][key] = { [field]: {} }
}
return curr[field][key]
}, obj)
if (lastKey && value) {
const newValue = structuredClone(value)
target[field][lastKey] = newValue
return
} else if (lastKey && !value) {
delete target[field][lastKey]
}
}
function isRecordSchemaDiff(value: SchemaDiff['diff']): value is Record<string, SchemaDiff> {
return typeof value === 'object' && value !== null
}
export function applyDiff(
schema: Record<string, any> | undefined,
diff: Record<string, SchemaDiff> | undefined
) {
if (!diff || !schema) {
return
}
let newSchema = structuredClone(schema)
Object.keys(diff).forEach((key) => {
const diffValue = diff[key].diff
if (diffValue === 'removed') {
delete newSchema.properties[key]
if (newSchema.order) {
newSchema.order = newSchema.order.filter((x) => x !== key)
}
} else if (diffValue === 'added' || diffValue === 'modified' || isRecordSchemaDiff(diffValue)) {
newSchema.properties[key] = diff[key].fullSchema
if (newSchema.order && !newSchema.order.includes(key)) {
newSchema.order.push(key)
}
}
})
return newSchema
}
@@ -133,6 +133,8 @@
function handleKeydown(event: KeyboardEvent) {
if (event.key === 'Escape' && selected) {
deselect()
event.stopPropagation()
event.preventDefault()
}
}
@@ -149,7 +151,7 @@
<svelte:window on:keydown={handleKeydown} />
<Label label="Trigger captures" {headless} class="h-full flex flex-col gap-1">
<Label label="Trigger captures" {headless} class="h-full {headless ? '' : 'flex flex-col gap-1'}">
<svelte:fragment slot="header">
{#if addButton}
<div class="inline-block">
@@ -180,7 +182,13 @@
</svelte:fragment>
<div
class={fullHeight ? 'h-full' : capturesLength > 7 ? 'h-[300px]' : 'h-fit'}
class={fullHeight
? headless
? 'h-full'
: 'min-h-0 grow'
: capturesLength > 7
? 'h-[300px]'
: 'h-fit'}
use:clickOutside={{ capture: false, exclude: getPropPickerElements }}
on:click_outside={() => {
if (firstClick) {
+2 -2
View File
@@ -4,8 +4,8 @@ verify_ssl = true
name = "pypi"
[packages]
wmill = ">=1.447.6"
wmill_pg = ">=1.447.6"
wmill = ">=1.448.0"
wmill_pg = ">=1.448.0"
sendgrid = "*"
mysql-connector-python = "*"
pymongo = "*"
+1 -1
View File
@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.447.6
version: 1.448.0
title: OpenFlow Spec
contact:
name: Ruben Fiszel
@@ -12,7 +12,7 @@
RootModule = 'WindmillClient.psm1'
# Version number of this module.
ModuleVersion = '1.447.6'
ModuleVersion = '1.448.0'
# Supported PSEditions
# CompatiblePSEditions = @()
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "wmill"
version = "1.447.6"
version = "1.448.0"
description = "A client library for accessing Windmill server wrapping the Windmill client API"
license = "Apache-2.0"
homepage = "https://windmill.dev"
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "wmill-pg"
version = "1.447.6"
version = "1.448.0"
description = "An extension client for the wmill client library focused on pg"
license = "Apache-2.0"
homepage = "https://windmill.dev"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@windmill/windmill",
"version": "1.447.6",
"version": "1.448.0",
"exports": "./src/index.ts",
"publish": {
"exclude": ["!src", "./s3Types.ts", "./client.ts"]
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "windmill-client",
"description": "Windmill SDK client for browsers and Node.js",
"version": "1.447.6",
"version": "1.448.0",
"author": "Ruben Fiszel",
"license": "Apache 2.0",
"devDependencies": {
+1 -1
View File
@@ -1 +1 @@
1.447.6
1.448.0