chore: make rquickjs, kube, otel proxy deps optional behind feature flags

- rquickjs: gated behind `quickjs` feature in windmill-jseval, propagated through windmill-worker/windmill-api, added to oss_core
- windmill-autoscaling: made optional in windmill-api (was unconditional), enabled via enterprise feature
- opentelemetry-proto, prost, hudsucker, rcgen, hyper-http-proxy, hyper-tls, hyper-util: made optional in windmill-worker, enabled via enterprise feature

This significantly reduces compilation time for vanilla `cargo check` without features.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ruben Fiszel
2026-02-08 16:13:16 +00:00
parent 5e63d31b92
commit 623407de6a
5 changed files with 60 additions and 28 deletions
+3 -1
View File
@@ -91,6 +91,7 @@ embedding = ["windmill-api/embedding"]
parquet = ["windmill-api/parquet", "windmill-common/parquet", "windmill-worker/parquet", "dep:object_store"]
prometheus = ["windmill-common/prometheus", "windmill-api/prometheus", "windmill-worker/prometheus", "windmill-queue/prometheus", "dep:prometheus"]
flow_testing = ["windmill-worker/flow_testing"]
quickjs = ["windmill-worker/quickjs", "windmill-api/quickjs"]
openidconnect = ["windmill-api/openidconnect", "windmill-common/openidconnect"]
cloud = ["windmill-queue/cloud", "windmill-worker/cloud", "windmill-common/cloud", "windmill-api/cloud"]
jemalloc = ["windmill-common/jemalloc", "dep:tikv-jemallocator", "dep:tikv-jemalloc-sys", "dep:tikv-jemalloc-ctl"]
@@ -145,7 +146,8 @@ oss_core = [
"embedding", "parquet", "openidconnect", "license",
"http_trigger", "zip", "oauth2", "postgres_trigger",
"mqtt_trigger", "websocket", "smtp", "native_trigger",
"static_frontend", "mcp", "bedrock", "inline_preview"
"static_frontend", "mcp", "bedrock", "inline_preview",
"quickjs"
]
ce_core = ["oss_core", "private"]
ee_core = [
+3 -2
View File
@@ -11,7 +11,7 @@ path = "src/lib.rs"
[features]
default = []
private = ["windmill-audit/private", "windmill-common/private", "windmill-api-auth/private", "windmill-store/private", "windmill-api-users/private", "windmill-api-workspaces/private", "windmill-api-groups/private", "windmill-trigger-kafka?/private", "windmill-trigger-postgres?/private", "windmill-trigger-mqtt?/private", "windmill-trigger-websocket?/private", "windmill-trigger-nats?/private", "windmill-trigger-sqs?/private", "windmill-trigger-gcp?/private", "windmill-trigger-email?/private"]
enterprise = ["windmill-queue/enterprise", "windmill-audit/enterprise", "windmill-git-sync/enterprise", "windmill-common/enterprise", "windmill-worker?/enterprise", "windmill-api-auth/enterprise", "windmill-store/enterprise", "windmill-api-jobs/enterprise", "windmill-api-scripts/enterprise", "windmill-api-users/enterprise", "windmill-api-workspaces/enterprise", "windmill-api-groups/enterprise", "windmill-trigger/enterprise", "windmill-trigger-kafka?/enterprise", "windmill-trigger-postgres?/enterprise", "windmill-trigger-mqtt?/enterprise", "windmill-trigger-websocket?/enterprise", "windmill-trigger-email?/enterprise", "windmill-trigger-nats?/enterprise", "windmill-trigger-sqs?/enterprise", "windmill-trigger-gcp?/enterprise", "windmill-trigger-http?/enterprise", "windmill-native-triggers?/enterprise"]
enterprise = ["windmill-queue/enterprise", "windmill-audit/enterprise", "windmill-git-sync/enterprise", "windmill-common/enterprise", "windmill-worker?/enterprise", "windmill-api-auth/enterprise", "windmill-store/enterprise", "windmill-api-jobs/enterprise", "windmill-api-scripts/enterprise", "windmill-api-users/enterprise", "windmill-api-workspaces/enterprise", "windmill-api-groups/enterprise", "windmill-trigger/enterprise", "windmill-trigger-kafka?/enterprise", "windmill-trigger-postgres?/enterprise", "windmill-trigger-mqtt?/enterprise", "windmill-trigger-websocket?/enterprise", "windmill-trigger-email?/enterprise", "windmill-trigger-nats?/enterprise", "windmill-trigger-sqs?/enterprise", "windmill-trigger-gcp?/enterprise", "windmill-trigger-http?/enterprise", "windmill-native-triggers?/enterprise", "dep:windmill-autoscaling", "windmill-autoscaling/enterprise"]
stripe = []
inline_preview = ["dep:windmill-worker"]
agent_worker_server = ["dep:windmill-worker"]
@@ -42,6 +42,7 @@ mcp = ["dep:windmill-mcp", "windmill-mcp/server", "windmill-mcp/auth", "windmill
bedrock = ["dep:aws-sdk-bedrock", "dep:aws-sdk-bedrockruntime", "windmill-common/bedrock", "dep:aws-config", "dep:aws-credential-types", "dep:aws-smithy-types"]
python = ["windmill-dep-map/python", "dep:windmill-parser-py", "dep:windmill-parser-py-imports", "windmill-api-scripts/python", "windmill-trigger/python", "windmill-common/python"]
no_auth = ["windmill-api-auth/no_auth", "windmill-store/no_auth", "windmill-api-users/no_auth"]
quickjs = ["windmill-jseval/quickjs"]
[dependencies]
windmill-mcp = { workspace = true, optional = true }
@@ -64,7 +65,7 @@ windmill-parser-py = { workspace = true, optional = true }
windmill-parser-py-imports = { workspace = true, optional = true }
windmill-git-sync.workspace = true
windmill-indexer = { workspace = true, optional = true }
windmill-autoscaling.workspace = true
windmill-autoscaling = { workspace = true, optional = true }
windmill-worker = { workspace = true, optional = true }
windmill-dep-map.workspace = true
tokio.workspace = true
+2 -1
View File
@@ -10,10 +10,11 @@ path = "src/lib.rs"
[features]
default = []
quickjs = ["dep:rquickjs"]
[dependencies]
windmill-common = { workspace = true, default-features = false }
rquickjs.workspace = true
rquickjs = { workspace = true, optional = true }
serde_json.workspace = true
tokio.workspace = true
tracing.workspace = true
+43 -16
View File
@@ -21,6 +21,7 @@ use std::sync::Arc;
use lazy_static::lazy_static;
use regex::Regex;
#[cfg(feature = "quickjs")]
use rquickjs::{
async_with,
prelude::{Async, Func, MutFn},
@@ -63,8 +64,9 @@ lazy_static! {
.unwrap();
}
// ── Shared helper functions ───────────────────────────────────────────
// ── Shared helper functions (quickjs-only) ──────────────────────────
#[cfg(feature = "quickjs")]
pub fn replace_with_await(expr: String, fn_name: &str) -> String {
let sep = format!("{}(", fn_name);
let mut split = expr.split(&sep);
@@ -75,10 +77,12 @@ pub fn replace_with_await(expr: String, fn_name: &str) -> String {
s
}
#[cfg(feature = "quickjs")]
pub fn replace_with_await_result(expr: String) -> String {
RE.replace_all(&expr, "(await $r)").to_string()
}
#[cfg(feature = "quickjs")]
fn add_closing_bracket(s: &str) -> String {
let mut s = s.to_string();
let mut level = 1;
@@ -194,23 +198,20 @@ pub async fn handle_full_regex(
return None;
}
#[cfg(feature = "quickjs")]
use windmill_common::utils::unsafe_raw;
// ── QuickJS evaluation ───────────────────────────────────────────────
#[cfg(feature = "quickjs")]
/// Shared state for async operations within QuickJS
#[derive(Clone)]
struct AsyncOpState {
client: AuthedClient,
}
/// Evaluates a JavaScript expression using QuickJS runtime.
///
/// This function provides flow expression evaluation using QuickJS
/// instead of deno_core/V8 for significantly faster startup times.
///
/// Unlike deno_core, this uses true async Rust callbacks for `variable()`,
/// `resource()`, and `results.xxx` access - no pre-fetching required.
#[cfg(feature = "quickjs")]
pub async fn eval_timeout_quickjs(
expr: String,
transform_context: HashMap<String, Arc<Box<RawValue>>>,
@@ -301,9 +302,10 @@ pub async fn eval_timeout_quickjs(
})??
}
/// Memory limit for QuickJS runtime (32MB).
#[cfg(feature = "quickjs")]
const QUICKJS_MEMORY_LIMIT: usize = 32 * 1024 * 1024;
#[cfg(feature = "quickjs")]
async fn eval_quickjs_inner(
expr: &str,
transform_context: HashMap<String, Arc<Box<RawValue>>>,
@@ -441,7 +443,7 @@ async fn eval_quickjs_inner(
.await
}
/// Set up async variable() and resource() functions using true Rust async callbacks.
#[cfg(feature = "quickjs")]
fn setup_async_ops<'js>(
ctx: &rquickjs::Ctx<'js>,
globals: &Object<'js>,
@@ -509,7 +511,7 @@ fn setup_async_ops<'js>(
Ok(())
}
/// Set up stub functions that throw errors when no client is available
#[cfg(feature = "quickjs")]
fn setup_stub_functions<'js>(
ctx: &rquickjs::Ctx<'js>,
_globals: &Object<'js>,
@@ -531,7 +533,7 @@ fn setup_stub_functions<'js>(
Ok(())
}
/// Set up the `results` Proxy object with dynamic access to step results.
#[cfg(feature = "quickjs")]
fn setup_results_proxy<'js>(
ctx: &rquickjs::Ctx<'js>,
globals: &Object<'js>,
@@ -657,7 +659,7 @@ fn setup_results_proxy<'js>(
Ok(())
}
/// Convert a serde_json::Value to a QuickJS Value
#[cfg(feature = "quickjs")]
fn json_to_js<'js>(
ctx: &rquickjs::Ctx<'js>,
val: &serde_json::Value,
@@ -696,6 +698,7 @@ fn json_to_js<'js>(
}
}
#[cfg(feature = "quickjs")]
fn should_add_return_quickjs(expr: &str) -> bool {
let trimmed = expr.trim();
@@ -741,6 +744,7 @@ fn should_add_return_quickjs(expr: &str) -> bool {
true
}
#[cfg(feature = "quickjs")]
fn contains_semicolon_outside_strings(expr: &str) -> bool {
let mut in_single_quote = false;
let mut in_double_quote = false;
@@ -769,14 +773,14 @@ fn contains_semicolon_outside_strings(expr: &str) -> bool {
false
}
#[cfg(feature = "quickjs")]
fn quickjs_error_to_anyhow(err: rquickjs::CaughtError<'_>) -> anyhow::Error {
anyhow::anyhow!("QuickJS evaluation error: {}", err)
}
// ── eval_simple_js for windmill-api batch rerun ──────────────────────
/// Evaluate a JS expression with named JSON globals in scope.
/// Used by windmill-api for batch rerun arg transforms.
#[cfg(feature = "quickjs")]
pub async fn eval_simple_js(
expr: String,
globals: HashMap<String, serde_json::Value>,
@@ -819,9 +823,32 @@ pub async fn eval_simple_js(
})??
}
// ── Fallback stubs when quickjs is disabled ──────────────────────────
#[cfg(not(feature = "quickjs"))]
pub async fn eval_timeout_quickjs(
_expr: String,
_transform_context: HashMap<String, Arc<Box<RawValue>>>,
_flow_input: Option<mappable_rc::Marc<HashMap<String, Box<RawValue>>>>,
_flow_env: Option<&HashMap<String, Box<RawValue>>>,
_authed_client: Option<&windmill_common::client::AuthedClient>,
_by_id: Option<&IdContext>,
_ctx: Option<Vec<(String, String)>>,
) -> anyhow::Result<Box<RawValue>> {
anyhow::bail!("quickjs feature is not enabled")
}
#[cfg(not(feature = "quickjs"))]
pub async fn eval_simple_js(
_expr: String,
_globals: HashMap<String, serde_json::Value>,
) -> anyhow::Result<Box<RawValue>> {
anyhow::bail!("quickjs feature is not enabled")
}
// ── Tests ────────────────────────────────────────────────────────────
#[cfg(test)]
#[cfg(all(test, feature = "quickjs"))]
mod tests {
use super::*;
use serde_json::json;
+9 -8
View File
@@ -13,7 +13,7 @@ default = []
private = []
mcp = ["dep:windmill-mcp"]
prometheus = ["dep:prometheus", "windmill-common/prometheus"]
enterprise = ["windmill-queue/enterprise", "windmill-git-sync/enterprise", "windmill-common/enterprise", "dep:pem", "dep:tokio-util"]
enterprise = ["windmill-queue/enterprise", "windmill-git-sync/enterprise", "windmill-common/enterprise", "dep:pem", "dep:tokio-util", "dep:opentelemetry-proto", "dep:prost", "dep:hudsucker", "dep:rcgen", "dep:hyper-http-proxy", "dep:hyper-tls", "dep:hyper-util"]
mssql = ["dep:tiberius"]
mssql-kerberos = ["mssql", "tiberius/integrated-auth-gssapi"] # Linux/Unix integrated auth
mssql-winauth = ["mssql", "tiberius/winauth"] # Windows integrated auth
@@ -37,6 +37,7 @@ nu = ["dep:windmill-parser-nu"]
java = ["dep:windmill-parser-java"]
ruby = ["dep:windmill-parser-ruby"]
duckdb = ["dep:libloading"]
quickjs = ["windmill-jseval/quickjs"]
bedrock = ["dep:aws-sdk-bedrockruntime", "windmill-common/bedrock", "dep:aws-config", "dep:aws-credential-types", "dep:aws-smithy-types"]
[dependencies]
@@ -127,18 +128,18 @@ process-wrap.workspace = true
async-once-cell.workspace = true
libloading = { workspace = true, optional = true }
opentelemetry-proto.workspace = true
opentelemetry-proto = { workspace = true, optional = true }
opentelemetry = { workspace = true, optional = true }
tracing-opentelemetry = { workspace = true, optional = true }
prost.workspace = true
prost = { workspace = true, optional = true }
axum.workspace = true
bollard = { workspace = true, optional = true }
oracle = { workspace = true, optional = true }
hudsucker.workspace = true
hyper-http-proxy.workspace = true
hyper-tls.workspace = true
hyper-util.workspace = true
rcgen.workspace = true
hudsucker = { workspace = true, optional = true }
hyper-http-proxy = { workspace = true, optional = true }
hyper-tls = { workspace = true, optional = true }
hyper-util = { workspace = true, optional = true }
rcgen = { workspace = true, optional = true }
[build-dependencies]
libffi-sys = { workspace = true, optional = true }