mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-26 08:02:31 +00:00
Merge branch 'main' into dieri/change-workspace-id-async
This commit is contained in:
@@ -1,5 +1,36 @@
|
||||
# Changelog
|
||||
|
||||
## [1.566.1](https://github.com/windmill-labs/windmill/compare/v1.566.0...v1.566.1) (2025-10-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix dependency jobs on CE ([29b6feb](https://github.com/windmill-labs/windmill/commit/29b6febdd81cf16bd14a9ec4528e215e8221704a))
|
||||
|
||||
## [1.566.0](https://github.com/windmill-labs/windmill/compare/v1.565.0...v1.566.0) (2025-10-24)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **aichat:** add user-level custom system prompts ([#6884](https://github.com/windmill-labs/windmill/issues/6884)) ([b5d0f23](https://github.com/windmill-labs/windmill/commit/b5d0f23f4fe9ea05069cada517a47ebe1cf8267a))
|
||||
* **backend:** pass all headers and query to webhook preprocessor ([#6931](https://github.com/windmill-labs/windmill/issues/6931)) ([f0bbdd4](https://github.com/windmill-labs/windmill/commit/f0bbdd4aa8a95f1894e9a19687856afa9d73142f))
|
||||
* **backend:** stream early return ([#6896](https://github.com/windmill-labs/windmill/issues/6896)) ([e81d629](https://github.com/windmill-labs/windmill/commit/e81d6297050b468e30074ec1ac144be368040d4a))
|
||||
* **flow chat:** display image outputs ([#6880](https://github.com/windmill-labs/windmill/issues/6880)) ([53d8fbe](https://github.com/windmill-labs/windmill/commit/53d8fbe5084b3fb8d4eba3555d7d85458bf05aed))
|
||||
* job debouncing ([#6878](https://github.com/windmill-labs/windmill/issues/6878)) ([edece03](https://github.com/windmill-labs/windmill/commit/edece035f806aafde5696b0b2711a53ead2bf9c4))
|
||||
* support wildcards in http routes ([#6927](https://github.com/windmill-labs/windmill/issues/6927)) ([f43dee1](https://github.com/windmill-labs/windmill/commit/f43dee1952d28a6bbfd2912ab78623da081b60f9))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* also auto add/del igroup members to workspaces where configured ([#6888](https://github.com/windmill-labs/windmill/issues/6888)) ([1d3245e](https://github.com/windmill-labs/windmill/commit/1d3245eea260609b2f32ba4839e96b2e13d0dabc))
|
||||
* apify oauth ([#6902](https://github.com/windmill-labs/windmill/issues/6902)) ([b33e3d8](https://github.com/windmill-labs/windmill/commit/b33e3d85052699becc0d211b2d5737be4d68da4a))
|
||||
* **cli:** increase custom bundler output size ([fadfcfd](https://github.com/windmill-labs/windmill/commit/fadfcfd0fce52c3906b1cdd2d2ace5af8dfb01f7))
|
||||
* **cli:** resource type sync with protected private hub ([#6933](https://github.com/windmill-labs/windmill/issues/6933)) ([6d403da](https://github.com/windmill-labs/windmill/commit/6d403da05addca02614c9f013a56e78734c0a4f9))
|
||||
* **cloud:** better errors when failing to get team plan status ([#6908](https://github.com/windmill-labs/windmill/issues/6908)) ([7b46491](https://github.com/windmill-labs/windmill/commit/7b464915785c576484b53dea8b9a5d9ebd74ec2b))
|
||||
* **debouncing:** fix perf issues and re-enable debouncing ([#6932](https://github.com/windmill-labs/windmill/issues/6932)) ([999aaaa](https://github.com/windmill-labs/windmill/commit/999aaaacd8727f2a8afdac2007e6536c66f1c2f7))
|
||||
* detect preprocessor in re-exported named exports ([#6899](https://github.com/windmill-labs/windmill/issues/6899)) ([fa76105](https://github.com/windmill-labs/windmill/commit/fa76105d14e6ca113de494d5c3faf8175582712e))
|
||||
* **yaml-validator:** update openflow for aiagents ([#6895](https://github.com/windmill-labs/windmill/issues/6895)) ([e40d52d](https://github.com/windmill-labs/windmill/commit/e40d52d411187f9b433c765611686e30a2807037))
|
||||
|
||||
## [1.565.0](https://github.com/windmill-labs/windmill/compare/v1.564.0...v1.565.0) (2025-10-22)
|
||||
|
||||
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO script\n (workspace_id, hash, path, parent_hashes, summary, description, content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets, debounce_key, debounce_delay_s)\n\n SELECT workspace_id, $1, path, array_prepend($2::bigint, COALESCE(parent_hashes, '{}'::bigint[])), summary, description, content, created_by, schema, is_template, extra_perms, NULL, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets, debounce_key, debounce_delay_s\n\n FROM script WHERE hash = $2 AND workspace_id = $3;\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Int8",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "53dee7c119d724624b9973ee981576154cec84a09069286d2d7144dbad54f4d6"
|
||||
}
|
||||
Generated
+149
-149
File diff suppressed because it is too large
Load Diff
+4
-2
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "windmill"
|
||||
version = "1.565.0"
|
||||
version = "1.566.1"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
@@ -34,7 +34,7 @@ members = [
|
||||
exclude = ["./windmill-duckdb-ffi-internal"]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.565.0"
|
||||
version = "1.566.1"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -161,6 +161,7 @@ bitflags.workspace = true
|
||||
nom.workspace = true
|
||||
globset.workspace = true
|
||||
|
||||
|
||||
[target.'cfg(not(target_env = "msvc"))'.dependencies]
|
||||
tikv-jemallocator = { optional = true, workspace = true }
|
||||
tikv-jemalloc-sys = { optional = true, workspace = true }
|
||||
@@ -265,6 +266,7 @@ aws-sigv4 = "^1.3.4"
|
||||
aws-sdk-config = "=1.68.0"
|
||||
async-trait = "0.1.88"
|
||||
|
||||
|
||||
v8 = "=130.0.7" # Exact version NOTE: Do not forget to update version and hash in flake.nix
|
||||
deno_fetch = "0.214.0"
|
||||
deno_tls = "0.177.0"
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
-- Add down migration script here
|
||||
DROP INDEX IF EXISTS idx_debounce_key_job_id;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- Add up migration script here
|
||||
CREATE INDEX IF NOT EXISTS idx_debounce_key_job_id ON debounce_key (job_id);
|
||||
@@ -170,10 +170,10 @@ fn parse_code_for_imports(code: &str, path: &str) -> error::Result<Vec<NImport>>
|
||||
// This is needed because we've split off the real main function above
|
||||
let code_with_fake_main = format!("{}\n\ndef main(): pass", code);
|
||||
|
||||
|
||||
let ast = Suite::parse(&code_with_fake_main, "main.py").map_err(|e| {
|
||||
error::Error::ExecutionErr(format!("Error parsing code for imports: {}", e.to_string()))
|
||||
})?;
|
||||
|
||||
// Note: We're still using the original code for finding pins,
|
||||
// as the TextRange values from the parsed AST would be based on code_with_fake_main
|
||||
// but we want to match against the original code
|
||||
|
||||
@@ -577,6 +577,12 @@ def main():
|
||||
async fn test_1(db: sqlx::Pool<sqlx::Postgres>) -> anyhow::Result<()> {
|
||||
// This tests if debouncing and consolidation works.
|
||||
// Also makes sures that dependency job does not create new flow version
|
||||
{
|
||||
let mut mvsd = windmill_common::worker::MIN_VERSION_SUPPORTS_DEBOUNCING
|
||||
.write()
|
||||
.await;
|
||||
*mvsd = true;
|
||||
}
|
||||
|
||||
let (client, port, _s) = init_client(db.clone()).await;
|
||||
let mut completed = listen_for_completed_jobs(&db).await;
|
||||
@@ -792,6 +798,12 @@ def main():
|
||||
async fn test_left(db: sqlx::Pool<sqlx::Postgres>) -> anyhow::Result<()> {
|
||||
use crate::common::RunJob;
|
||||
|
||||
{
|
||||
let mut mvsd = windmill_common::worker::MIN_VERSION_SUPPORTS_DEBOUNCING
|
||||
.write()
|
||||
.await;
|
||||
*mvsd = true;
|
||||
}
|
||||
// TODO: We don't care about timer. If there is no timer, it will be set automatically for djobs??
|
||||
let (_client, port, _s) = init_client(db.clone()).await;
|
||||
let mut completed = listen_for_completed_jobs(&db).await;
|
||||
@@ -1114,6 +1126,12 @@ def main():
|
||||
// #[windmill::all_min_versions]
|
||||
async fn test_3(db: sqlx::Pool<sqlx::Postgres>) -> anyhow::Result<()> {
|
||||
// This tests checks if concurrency limit works correcly and there is no race conditions.
|
||||
{
|
||||
let mut mvsd = windmill_common::worker::MIN_VERSION_SUPPORTS_DEBOUNCING
|
||||
.write()
|
||||
.await;
|
||||
*mvsd = true;
|
||||
}
|
||||
|
||||
let (_client, port, _s) = init_client(db.clone()).await;
|
||||
let mut completed = listen_for_completed_jobs(&db).await;
|
||||
@@ -1441,6 +1459,12 @@ WHERE
|
||||
#[cfg(feature = "python")]
|
||||
#[sqlx::test(fixtures("base", "djob_debouncing"))]
|
||||
async fn test_1(db: sqlx::Pool<sqlx::Postgres>) -> anyhow::Result<()> {
|
||||
{
|
||||
let mut mvsd = windmill_common::worker::MIN_VERSION_SUPPORTS_DEBOUNCING
|
||||
.write()
|
||||
.await;
|
||||
*mvsd = true;
|
||||
}
|
||||
// This tests if debouncing and consolidation works.
|
||||
// Also makes sures that dependency job does not create new flow version
|
||||
|
||||
@@ -1620,6 +1644,12 @@ WHERE
|
||||
#[sqlx::test(fixtures("base", "djob_debouncing"))]
|
||||
async fn test_left(db: sqlx::Pool<sqlx::Postgres>) -> anyhow::Result<()> {
|
||||
use crate::common::RunJob;
|
||||
{
|
||||
let mut mvsd = windmill_common::worker::MIN_VERSION_SUPPORTS_DEBOUNCING
|
||||
.write()
|
||||
.await;
|
||||
*mvsd = true;
|
||||
}
|
||||
|
||||
// TODO: We don't care about timer. If there is no timer, it will be set automatically for djobs??
|
||||
let (_client, port, _s) = init_client(db.clone()).await;
|
||||
@@ -1929,6 +1959,12 @@ WHERE
|
||||
#[sqlx::test(fixtures("base", "djob_debouncing"))]
|
||||
// TODO: Same test_but script fails.
|
||||
async fn test_1(db: sqlx::Pool<sqlx::Postgres>) -> anyhow::Result<()> {
|
||||
{
|
||||
let mut mvsd = windmill_common::worker::MIN_VERSION_SUPPORTS_DEBOUNCING
|
||||
.write()
|
||||
.await;
|
||||
*mvsd = true;
|
||||
}
|
||||
// This tests if debouncing and consolidation works.
|
||||
// Also makes sures that dependency job does not create new flow version
|
||||
let (client, port, _s) = init_client(db.clone()).await;
|
||||
@@ -2172,6 +2208,12 @@ WHERE
|
||||
#[sqlx::test(fixtures("base", "djob_debouncing"))]
|
||||
async fn test_left(db: sqlx::Pool<sqlx::Postgres>) -> anyhow::Result<()> {
|
||||
use crate::common::RunJob;
|
||||
{
|
||||
let mut mvsd = windmill_common::worker::MIN_VERSION_SUPPORTS_DEBOUNCING
|
||||
.write()
|
||||
.await;
|
||||
*mvsd = true;
|
||||
}
|
||||
|
||||
// TODO: We don't care about timer. If there is no timer, it will be set automatically for djobs??
|
||||
let (_client, port, _s) = init_client(db.clone()).await;
|
||||
@@ -2281,6 +2323,7 @@ WHERE
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
dbg!(&r);
|
||||
assert_eq!(r.len(), 4);
|
||||
assert!(r.contains(&Some(-221349019907577876)));
|
||||
assert!(r.contains(&Some(533400)));
|
||||
|
||||
@@ -2869,13 +2869,15 @@ def heavy_compute(n: int):
|
||||
def send_result(res: int, email: str):
|
||||
print(f"Sending result {res} to {email}")
|
||||
return "OK"
|
||||
|
||||
|
||||
def main(n: int):
|
||||
l = []
|
||||
for i in range(n):
|
||||
l.append(heavy_compute(i))
|
||||
print(l)
|
||||
return [send_result(sum(l), "example@example.com"), n]
|
||||
|
||||
|
||||
"#;
|
||||
|
||||
#[cfg(feature = "python")]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.565.0
|
||||
version: 1.566.1
|
||||
title: Windmill API
|
||||
|
||||
contact:
|
||||
@@ -15442,6 +15442,10 @@ components:
|
||||
type: integer
|
||||
concurrency_key:
|
||||
type: string
|
||||
debounce_key:
|
||||
type: string
|
||||
debounce_delay_s:
|
||||
type: integer
|
||||
cache_ttl:
|
||||
type: number
|
||||
dedicated_worker:
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use axum::{
|
||||
extract::{FromRequest, FromRequestParts, Multipart, Query, Request},
|
||||
http::{HeaderMap, Uri},
|
||||
extract::{FromRequest, Multipart, Query, Request},
|
||||
http::HeaderMap,
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use http::{header::CONTENT_TYPE, request::Parts, StatusCode};
|
||||
use http::{header::CONTENT_TYPE, StatusCode};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::value::RawValue;
|
||||
use sqlx::types::JsonRawValue;
|
||||
@@ -44,11 +44,13 @@ pub enum Body {
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct WebhookArgsMetadata {
|
||||
pub raw_string: Option<String>,
|
||||
pub headers: HashMap<String, Box<RawValue>>,
|
||||
pub headers: HeaderMap,
|
||||
pub query: Option<String>,
|
||||
pub method: http::Method,
|
||||
pub query: HashMap<String, Box<RawValue>>,
|
||||
pub query_wrap_body: bool,
|
||||
pub query_use_raw: bool,
|
||||
pub query_include_header: Option<String>,
|
||||
pub query_include_query: Option<String>,
|
||||
}
|
||||
|
||||
pub struct RawWebhookArgs {
|
||||
@@ -262,6 +264,18 @@ impl WebhookArgs {
|
||||
self,
|
||||
runnable_format: RunnableFormat,
|
||||
) -> Result<PushArgsOwned, Error> {
|
||||
let headers = build_headers(
|
||||
&self.metadata.headers,
|
||||
self.metadata.query_include_header,
|
||||
runnable_format.has_preprocessor,
|
||||
);
|
||||
|
||||
let query = build_query(
|
||||
self.metadata.query.as_deref(),
|
||||
self.metadata.query_include_query,
|
||||
runnable_format.has_preprocessor,
|
||||
);
|
||||
|
||||
match runnable_format {
|
||||
RunnableFormat { has_preprocessor: true, version: RunnableFormatVersion::V2 } => {
|
||||
let mut args = HashMap::new();
|
||||
@@ -272,8 +286,8 @@ impl WebhookArgs {
|
||||
kind: "webhook".to_string(),
|
||||
body: to_raw_value(&self.body),
|
||||
raw_string: self.metadata.raw_string,
|
||||
headers: self.metadata.headers,
|
||||
query: self.metadata.query,
|
||||
headers,
|
||||
query,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -282,8 +296,7 @@ impl WebhookArgs {
|
||||
RunnableFormat { has_preprocessor, .. } => {
|
||||
let mut extra = HashMap::new();
|
||||
|
||||
let WebhookArgsMetadata { query, query_wrap_body, headers, raw_string, .. } =
|
||||
self.metadata;
|
||||
let WebhookArgsMetadata { query_wrap_body, raw_string, .. } = self.metadata;
|
||||
|
||||
for (k, v) in headers {
|
||||
extra.insert(k, v);
|
||||
@@ -332,6 +345,7 @@ pub struct RequestQuery {
|
||||
pub raw: Option<bool>,
|
||||
pub wrap_body: Option<bool>,
|
||||
pub include_header: Option<String>,
|
||||
pub include_query: Option<String>,
|
||||
}
|
||||
|
||||
async fn req_to_string<S: Send + Sync>(
|
||||
@@ -359,23 +373,21 @@ where
|
||||
let content_type = content_type_header.and_then(|value| value.to_str().ok());
|
||||
let uri = request.uri();
|
||||
let request_query = Query::<RequestQuery>::try_from_uri(uri).unwrap().0;
|
||||
let headers = build_headers(&headers_map, request_query.include_header, is_http_trigger);
|
||||
let query_decode = DecodeQueries::from_uri(uri, is_http_trigger);
|
||||
let mut query = HashMap::new();
|
||||
if let Some(DecodeQueries(queries)) = query_decode {
|
||||
query.extend(queries);
|
||||
}
|
||||
|
||||
let query = uri.query().map(|s| s.to_owned());
|
||||
let raw = !is_http_trigger && request_query.raw.unwrap_or(false);
|
||||
let wrap_body = !is_http_trigger && request_query.wrap_body.unwrap_or(false);
|
||||
(
|
||||
content_type,
|
||||
WebhookArgsMetadata {
|
||||
headers,
|
||||
headers: headers_map.clone(),
|
||||
query,
|
||||
method: request.method().clone(),
|
||||
raw_string: None,
|
||||
query_wrap_body: wrap_body,
|
||||
query_use_raw: raw,
|
||||
query_include_header: request_query.include_header,
|
||||
query_include_query: request_query.include_query,
|
||||
},
|
||||
)
|
||||
};
|
||||
@@ -460,11 +472,11 @@ lazy_static::lazy_static! {
|
||||
pub fn build_headers(
|
||||
headers: &HeaderMap,
|
||||
include_header: Option<String>,
|
||||
is_http_trigger: bool,
|
||||
include_all_headers: bool,
|
||||
) -> HashMap<String, Box<RawValue>> {
|
||||
let mut selected_headers = HashMap::new();
|
||||
|
||||
if is_http_trigger {
|
||||
if include_all_headers {
|
||||
for (k, v) in headers.iter() {
|
||||
selected_headers.insert(
|
||||
k.to_string(),
|
||||
@@ -490,73 +502,40 @@ pub fn build_headers(
|
||||
selected_headers
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct IncludeQuery {
|
||||
pub include_query: Option<String>,
|
||||
}
|
||||
pub fn build_query(
|
||||
query: Option<&str>,
|
||||
include_query: Option<String>,
|
||||
include_all_query: bool,
|
||||
) -> HashMap<String, Box<RawValue>> {
|
||||
let Some(query) = query else {
|
||||
return HashMap::new();
|
||||
};
|
||||
|
||||
pub struct DecodeQueries(pub HashMap<String, Box<RawValue>>);
|
||||
|
||||
#[axum::async_trait]
|
||||
impl<S> FromRequestParts<S> for DecodeQueries
|
||||
where
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = Response;
|
||||
|
||||
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
|
||||
Ok(DecodeQueries::from_uri(&parts.uri, false)
|
||||
.unwrap_or_else(|| DecodeQueries(HashMap::new())))
|
||||
}
|
||||
}
|
||||
|
||||
impl DecodeQueries {
|
||||
pub fn from_uri(uri: &Uri, is_http_trigger: bool) -> Option<Self> {
|
||||
let query = uri.query();
|
||||
if query.is_none() {
|
||||
return None;
|
||||
}
|
||||
let query = query.unwrap();
|
||||
if is_http_trigger {
|
||||
if include_all_query {
|
||||
let queries =
|
||||
serde_urlencoded::from_str::<HashMap<String, String>>(&query).unwrap_or_default();
|
||||
queries
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, to_raw_value(&v)))
|
||||
.collect()
|
||||
} else {
|
||||
let parse_query_args = include_query
|
||||
.map(|s| s.split(",").map(|p| p.to_string()).collect::<Vec<_>>())
|
||||
.unwrap_or_default();
|
||||
let mut args = HashMap::new();
|
||||
if !parse_query_args.is_empty() {
|
||||
let queries =
|
||||
serde_urlencoded::from_str::<HashMap<String, String>>(query).unwrap_or_default();
|
||||
Some(DecodeQueries(
|
||||
queries
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, to_raw_value(&v)))
|
||||
.collect(),
|
||||
))
|
||||
} else {
|
||||
let include_query = serde_urlencoded::from_str::<IncludeQuery>(query)
|
||||
.map(|x| x.include_query)
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or_default();
|
||||
let parse_query_args = include_query
|
||||
.split(",")
|
||||
.map(|s| s.to_string())
|
||||
.collect::<Vec<_>>();
|
||||
let mut args = HashMap::new();
|
||||
if !parse_query_args.is_empty() {
|
||||
let queries = serde_urlencoded::from_str::<HashMap<String, String>>(query)
|
||||
.unwrap_or_default();
|
||||
parse_query_args.iter().for_each(|h| {
|
||||
if let Some(v) = queries.get(h) {
|
||||
args.insert(h.to_string(), to_raw_value(v));
|
||||
}
|
||||
});
|
||||
}
|
||||
Some(DecodeQueries(args))
|
||||
serde_urlencoded::from_str::<HashMap<String, String>>(&query).unwrap_or_default();
|
||||
parse_query_args.iter().for_each(|h| {
|
||||
if let Some(v) = queries.get(h) {
|
||||
args.insert(h.to_string(), to_raw_value(v));
|
||||
}
|
||||
});
|
||||
}
|
||||
args
|
||||
}
|
||||
}
|
||||
|
||||
// impl<'c> PushArgs<'c> {
|
||||
// pub fn insert<K: Into<String>, V: Into<Box<RawValue>>>(&mut self, k: K, v: V) {
|
||||
// self.extra.insert(k.into(), v.into());
|
||||
// }
|
||||
// }
|
||||
|
||||
fn restructure_cloudevents_metadata(
|
||||
mut p: HashMap<String, Box<RawValue>>,
|
||||
) -> Result<HashMap<String, Box<RawValue>>, Error> {
|
||||
|
||||
@@ -398,7 +398,7 @@ async fn set_gcp_trigger_config(
|
||||
gcp_config.create_update,
|
||||
false,
|
||||
capture_config.is_flow,
|
||||
gcp_config.ack_deadline
|
||||
gcp_config.ack_deadline,
|
||||
)
|
||||
.await?;
|
||||
gcp_config.create_update = Some(config);
|
||||
@@ -966,6 +966,8 @@ async fn http_payload(
|
||||
Path((w_id, runnable_kind, path, route_path)): Path<(String, RunnableKind, String, StripPath)>,
|
||||
args: RawHttpTriggerArgs,
|
||||
) -> std::result::Result<StatusCode, Response> {
|
||||
use crate::args::{build_headers, build_query};
|
||||
|
||||
let path = path.replace(".", "/");
|
||||
let is_flow = matches!(runnable_kind, RunnableKind::Flow);
|
||||
let route_path = route_path.to_path();
|
||||
@@ -1002,9 +1004,18 @@ async fn http_payload(
|
||||
.map(|(k, v)| (k.to_string(), v.to_string()))
|
||||
.collect();
|
||||
|
||||
let headers = build_headers(&args.0.metadata.headers, None, true);
|
||||
let query = build_query(args.0.metadata.query.as_deref(), None, true);
|
||||
|
||||
let preprocessor_args = args
|
||||
.clone()
|
||||
.to_v2_preprocessor_args(&http_trigger_config.route_path, &route_path, ¶ms)
|
||||
.to_v2_preprocessor_args(
|
||||
&http_trigger_config.route_path,
|
||||
&route_path,
|
||||
¶ms,
|
||||
headers,
|
||||
query,
|
||||
)
|
||||
.map_err(|e| e.into_response())?;
|
||||
|
||||
let main_args = args
|
||||
|
||||
@@ -15,7 +15,10 @@ use windmill_common::{
|
||||
use windmill_queue::PushArgsOwned;
|
||||
|
||||
use crate::{
|
||||
args::{try_from_request_body, Body, RawWebhookArgs, WebhookArgs, WebhookArgsMetadata},
|
||||
args::{
|
||||
build_headers, build_query, try_from_request_body, Body, RawWebhookArgs, WebhookArgs,
|
||||
WebhookArgsMetadata,
|
||||
},
|
||||
db::ApiAuthed,
|
||||
};
|
||||
|
||||
@@ -102,8 +105,8 @@ struct HttpTriggerWmTrigger<'a> {
|
||||
route: &'a str,
|
||||
path: &'a str,
|
||||
params: &'a HashMap<String, String>,
|
||||
query: &'a HashMap<String, Box<RawValue>>,
|
||||
headers: &'a HashMap<String, Box<RawValue>>,
|
||||
query: HashMap<String, Box<RawValue>>,
|
||||
headers: HashMap<String, Box<RawValue>>,
|
||||
method: HttpMethod,
|
||||
}
|
||||
|
||||
@@ -142,14 +145,22 @@ impl HttpTriggerArgs {
|
||||
format: RunnableFormat,
|
||||
wrap_body: bool,
|
||||
) -> Result<PushArgsOwned, Error> {
|
||||
let headers = build_headers(&self.0.metadata.headers, None, true);
|
||||
let query = build_query(self.0.metadata.query.as_deref(), None, true);
|
||||
match format {
|
||||
RunnableFormat { has_preprocessor: true, version: RunnableFormatVersion::V2 } => {
|
||||
// we don't care about wrap_body in v2
|
||||
self.to_v2_preprocessor_args(route_path, called_path, params)
|
||||
}
|
||||
RunnableFormat { has_preprocessor: true, version: RunnableFormatVersion::V1 } => {
|
||||
self.to_v1_preprocessor_args(route_path, called_path, params, wrap_body)
|
||||
self.to_v2_preprocessor_args(route_path, called_path, params, headers, query)
|
||||
}
|
||||
RunnableFormat { has_preprocessor: true, version: RunnableFormatVersion::V1 } => self
|
||||
.to_v1_preprocessor_args(
|
||||
route_path,
|
||||
called_path,
|
||||
params,
|
||||
wrap_body,
|
||||
headers,
|
||||
query,
|
||||
),
|
||||
RunnableFormat { has_preprocessor: false, .. } => self.to_main_args(wrap_body),
|
||||
}
|
||||
}
|
||||
@@ -160,6 +171,8 @@ impl HttpTriggerArgs {
|
||||
called_path: &str,
|
||||
params: &HashMap<String, String>,
|
||||
wrap_body: bool,
|
||||
headers: HashMap<String, Box<RawValue>>,
|
||||
query: HashMap<String, Box<RawValue>>,
|
||||
) -> Result<PushArgsOwned, Error> {
|
||||
let mut extra = HashMap::new();
|
||||
let mut wm_trigger = HashMap::new();
|
||||
@@ -171,8 +184,8 @@ impl HttpTriggerArgs {
|
||||
path: called_path,
|
||||
method: (&self.0.metadata.method).try_into()?,
|
||||
params,
|
||||
query: &self.0.metadata.query,
|
||||
headers: &self.0.metadata.headers,
|
||||
query,
|
||||
headers,
|
||||
}),
|
||||
);
|
||||
extra.insert("wm_trigger".to_string(), to_raw_value(&wm_trigger));
|
||||
@@ -189,6 +202,8 @@ impl HttpTriggerArgs {
|
||||
route_path: &str,
|
||||
called_path: &str,
|
||||
params: &HashMap<String, String>,
|
||||
headers: HashMap<String, Box<RawValue>>,
|
||||
query: HashMap<String, Box<RawValue>>,
|
||||
) -> Result<PushArgsOwned, Error> {
|
||||
let mut args = HashMap::new();
|
||||
args.insert(
|
||||
@@ -197,8 +212,8 @@ impl HttpTriggerArgs {
|
||||
kind: "http".to_string(),
|
||||
body: to_raw_value(&self.0.body),
|
||||
raw_string: self.0.metadata.raw_string,
|
||||
headers: self.0.metadata.headers,
|
||||
query: self.0.metadata.query,
|
||||
headers,
|
||||
query,
|
||||
method: (&self.0.metadata.method).try_into()?,
|
||||
route: route_path,
|
||||
path: called_path,
|
||||
|
||||
@@ -799,14 +799,14 @@ pub async fn clone_script<'c>(
|
||||
draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, \
|
||||
dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, \
|
||||
delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, \
|
||||
codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets)
|
||||
codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets, debounce_key, debounce_delay_s)
|
||||
|
||||
SELECT workspace_id, $1, path, array_prepend($2::bigint, COALESCE(parent_hashes, '{}'::bigint[])), summary, description, \
|
||||
content, created_by, schema, is_template, extra_perms, NULL, language, kind, tag, \
|
||||
draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, \
|
||||
dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, \
|
||||
delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, \
|
||||
codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets
|
||||
codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets, debounce_key, debounce_delay_s
|
||||
|
||||
FROM script WHERE hash = $2 AND workspace_id = $3;
|
||||
", new_hash, base_hash.0, w_id).execute(&mut **tx).await?;
|
||||
|
||||
@@ -409,6 +409,10 @@ fn format_pull_query(peek: String) -> String {
|
||||
raw_flow, script_entrypoint_override, preprocessed
|
||||
FROM v2_job
|
||||
WHERE id = (SELECT id FROM peek)
|
||||
), delete_debounce AS NOT MATERIALIZED (
|
||||
DELETE FROM debounce_key
|
||||
USING j
|
||||
WHERE j.kind::text != 'flowdependencies' AND j.kind::text != 'appdependencies' AND j.kind::text != 'dependencies' AND debounce_key.job_id = j.id
|
||||
) SELECT j.id, j.workspace_id, j.parent_job, j.created_by, started_at, scheduled_for,
|
||||
j.runnable_id, j.runnable_path, j.args, canceled_by,
|
||||
canceled_reason, j.kind, j.trigger, j.trigger_kind, j.permissioned_as,
|
||||
@@ -426,7 +430,7 @@ fn format_pull_query(peek: String) -> String {
|
||||
",
|
||||
peek
|
||||
);
|
||||
tracing::debug!("pull query: {}", r);
|
||||
// tracing::debug!("pull query: {}", r);
|
||||
r
|
||||
}
|
||||
|
||||
|
||||
@@ -1140,13 +1140,13 @@ async fn commit_completed_job<T: Serialize + Send + Sync + ValidableJson>(
|
||||
.unwrap_or(false);
|
||||
|
||||
if schedule_next_tick {
|
||||
if let Err(err) = handle_maybe_scheduled_job(
|
||||
if let Err(err) = Box::pin(handle_maybe_scheduled_job(
|
||||
db,
|
||||
queued_job,
|
||||
&schedule,
|
||||
&script_path,
|
||||
&queued_job.workspace_id,
|
||||
)
|
||||
))
|
||||
.await
|
||||
{
|
||||
match err {
|
||||
@@ -2428,7 +2428,7 @@ pub async fn pull(
|
||||
|
||||
#[cfg(not(feature = "enterprise"))]
|
||||
let has_concurent_limit = false
|
||||
|| (job.is_dependency() && cfg!(feature = "private") && !*WMDEBUG_NO_DJOB_DEBOUNCING);
|
||||
|| (job.is_dependency() && job.concurrent_limit.is_some() && cfg!(feature = "private") && !*WMDEBUG_NO_DJOB_DEBOUNCING);
|
||||
// if we don't have private flag, we don't have concurrency limit
|
||||
|
||||
// concurrency check. If more than X jobs for this path are already running, we re-queue and pull another job from the queue
|
||||
@@ -3622,8 +3622,8 @@ pub async fn push<'c, 'd>(
|
||||
cache_ttl,
|
||||
dedicated_worker,
|
||||
_low_level_priority,
|
||||
mut custom_debounce_key,
|
||||
mut debounce_delay_s,
|
||||
custom_debounce_key,
|
||||
debounce_delay_s,
|
||||
) = match job_payload {
|
||||
JobPayload::ScriptHash {
|
||||
hash,
|
||||
@@ -4391,14 +4391,6 @@ pub async fn push<'c, 'd>(
|
||||
),
|
||||
};
|
||||
|
||||
if custom_debounce_key.is_some() {
|
||||
tracing::warn!("debouncing has been disabled temporarily, ignoring debounce_key");
|
||||
custom_debounce_key = None;
|
||||
}
|
||||
if debounce_delay_s.is_some() {
|
||||
tracing::warn!("debouncing has been disabled temporarily, ignoring debounce_delay_s");
|
||||
debounce_delay_s = None;
|
||||
}
|
||||
// Enforce concurrency limit on all dependency jobs.
|
||||
// TODO: We can ignore this for scripts djobs. The main reason we need all djobs to be sequential is because we have
|
||||
// nodes_to_relock and we need all locks whose corresponding steps aren't in nodes_to_relock be already present.
|
||||
|
||||
@@ -546,6 +546,7 @@ pub async fn handle_python_job(
|
||||
precomputed_agent_info: Option<PrecomputedAgentInfo>,
|
||||
has_stream: &mut bool,
|
||||
) -> windmill_common::error::Result<Box<RawValue>> {
|
||||
|
||||
let script_path = crate::common::use_flow_root_path(job.runnable_path());
|
||||
|
||||
let annotations = PythonAnnotations::parse(inner_content);
|
||||
@@ -1181,13 +1182,13 @@ async fn handle_python_deps(
|
||||
let (v, requirements_lines, error_hint) = match conn {
|
||||
Connection::Sql(db) => {
|
||||
let mut version_specifiers = vec![];
|
||||
let (r, h) = windmill_parser_py_imports::parse_python_imports(
|
||||
let (r, h) = Box::pin(windmill_parser_py_imports::parse_python_imports(
|
||||
inner_content,
|
||||
w_id,
|
||||
script_path,
|
||||
db,
|
||||
&mut version_specifiers,
|
||||
)
|
||||
))
|
||||
.await?;
|
||||
|
||||
let v = PyV::resolve(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -104,7 +104,7 @@ pub async fn update_flow_status_after_job_completion(
|
||||
let mut unrecoverable = unrecoverable;
|
||||
loop {
|
||||
potentially_crash_for_testing();
|
||||
let nrec = match update_flow_status_after_job_completion_internal(
|
||||
let nrec = match Box::pin(update_flow_status_after_job_completion_internal(
|
||||
db,
|
||||
client,
|
||||
rec.flow,
|
||||
@@ -122,13 +122,13 @@ pub async fn update_flow_status_after_job_completion(
|
||||
job_completed_tx.clone(),
|
||||
#[cfg(feature = "benchmark")]
|
||||
bench,
|
||||
)
|
||||
))
|
||||
.await
|
||||
{
|
||||
Ok(j) => j,
|
||||
Err(e) => {
|
||||
tracing::error!("Error while updating flow status of {} after completion of {}, updating flow status again with error: {e:#}", rec.flow, &rec.job_id_for_status);
|
||||
update_flow_status_after_job_completion_internal(
|
||||
Box::pin(update_flow_status_after_job_completion_internal(
|
||||
db,
|
||||
client,
|
||||
rec.flow,
|
||||
@@ -148,7 +148,7 @@ pub async fn update_flow_status_after_job_completion(
|
||||
job_completed_tx.clone(),
|
||||
#[cfg(feature = "benchmark")]
|
||||
bench,
|
||||
)
|
||||
))
|
||||
.await?
|
||||
}
|
||||
};
|
||||
@@ -1508,7 +1508,7 @@ pub async fn update_flow_status_after_job_completion_internal(
|
||||
true
|
||||
} else {
|
||||
tracing::debug!(id = %flow_job.id, "start handle flow");
|
||||
match handle_flow(
|
||||
match Box::pin(handle_flow(
|
||||
flow_job.clone(),
|
||||
&flow_data,
|
||||
db,
|
||||
@@ -1518,7 +1518,7 @@ pub async fn update_flow_status_after_job_completion_internal(
|
||||
worker_dir,
|
||||
job_completed_tx,
|
||||
worker_name,
|
||||
)
|
||||
))
|
||||
.warn_after_seconds(10)
|
||||
.await
|
||||
{
|
||||
@@ -1963,7 +1963,7 @@ pub async fn handle_flow(
|
||||
let mut rec = PushNextFlowJobRec { flow_job: flow_job, status: status };
|
||||
loop {
|
||||
let PushNextFlowJobRec { flow_job, status } = rec;
|
||||
let next = push_next_flow_job(
|
||||
let next = Box::pin(push_next_flow_job(
|
||||
flow_job,
|
||||
status,
|
||||
flow,
|
||||
@@ -1973,7 +1973,7 @@ pub async fn handle_flow(
|
||||
same_worker_tx,
|
||||
worker_dir,
|
||||
worker_name,
|
||||
)
|
||||
))
|
||||
.warn_after_seconds(10)
|
||||
.await?;
|
||||
match next {
|
||||
|
||||
@@ -417,7 +417,7 @@ pub async fn process_relative_imports(
|
||||
// Script might have no relative imports but still be referenced by someone else.
|
||||
match timeout(
|
||||
core::time::Duration::from_secs(60),
|
||||
trigger_dependents_to_recompute_dependencies(
|
||||
Box::pin(trigger_dependents_to_recompute_dependencies(
|
||||
w_id,
|
||||
script_path,
|
||||
deployment_message,
|
||||
@@ -427,7 +427,7 @@ pub async fn process_relative_imports(
|
||||
permissioned_as,
|
||||
db,
|
||||
already_visited,
|
||||
),
|
||||
)),
|
||||
)
|
||||
.warn_after_seconds(10)
|
||||
.await
|
||||
|
||||
+1
-1
@@ -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.565.0";
|
||||
export const VERSION = "v1.566.1";
|
||||
|
||||
export async function login(email: string, password: string): Promise<string> {
|
||||
return await windmill.UserService.login({
|
||||
|
||||
@@ -44,18 +44,57 @@ export async function pull(opts: GlobalOptions) {
|
||||
"X-email": userInfo.email,
|
||||
};
|
||||
|
||||
if (hubBaseUrl !== DEFAULT_HUB_BASE_URL) {
|
||||
const hubSecret = (await wmill.getGlobal({
|
||||
key: "hub_api_secret",
|
||||
})) as string | undefined;
|
||||
log.info("Fetching resource types from private hub: " + hubBaseUrl);
|
||||
if (hubSecret) {
|
||||
log.info("Using hub API secret");
|
||||
headers["X-api-secret"] = hubSecret;
|
||||
}
|
||||
}
|
||||
|
||||
if (uid) {
|
||||
headers["X-uid"] = uid;
|
||||
}
|
||||
|
||||
let list = await fetch(hubBaseUrl + "/resource_types/list", {
|
||||
let res1 = await fetch(hubBaseUrl + "/resource_types/list", {
|
||||
headers,
|
||||
}).then((r) => r.json() as Promise<HubResourceType[]>);
|
||||
});
|
||||
|
||||
if (!res1.ok) {
|
||||
if (res1.status === 401) {
|
||||
// 401 can only happen on a private hub
|
||||
throw new Error("Unauthorized access to private hub: " + hubBaseUrl);
|
||||
} else {
|
||||
throw new Error(
|
||||
"Couldn't fetch resource types from hub " +
|
||||
hubBaseUrl +
|
||||
": " +
|
||||
(await res1.text())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let list = (await res1.json()) as HubResourceType[];
|
||||
|
||||
if (list && list.length === 0 && hubBaseUrl !== DEFAULT_HUB_BASE_URL) {
|
||||
list = await fetch(DEFAULT_HUB_BASE_URL + "/resource_types/list", {
|
||||
log.info(
|
||||
"No resource types found in private hub, fetching from public hub"
|
||||
);
|
||||
delete headers["X-api-secret"];
|
||||
const res2 = await fetch(DEFAULT_HUB_BASE_URL + "/resource_types/list", {
|
||||
headers,
|
||||
}).then((r) => r.json() as Promise<HubResourceType[]>);
|
||||
});
|
||||
|
||||
if (!res2.ok) {
|
||||
throw new Error(
|
||||
"Couldn't fetch resource types from public hub: " + (await res2.text())
|
||||
);
|
||||
}
|
||||
|
||||
list = (await res2.json()) as HubResourceType[];
|
||||
}
|
||||
|
||||
const resourceTypes = await wmill.listResourceType({
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ export {
|
||||
// }
|
||||
// });
|
||||
|
||||
export const VERSION = "1.565.0";
|
||||
export const VERSION = "1.566.1";
|
||||
|
||||
export const WM_FORK_PREFIX = "wm-fork";
|
||||
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "windmill-components",
|
||||
"version": "1.565.0",
|
||||
"version": "1.566.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "windmill-components",
|
||||
"version": "1.565.0",
|
||||
"version": "1.566.1",
|
||||
"hasInstallScript": true,
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "windmill-components",
|
||||
"version": "1.565.0",
|
||||
"version": "1.566.1",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
|
||||
@@ -305,16 +305,20 @@
|
||||
{
|
||||
displayName: 'Publish to Hub',
|
||||
icon: Globe2,
|
||||
action: () => {
|
||||
action: async () => {
|
||||
const scriptData = await ScriptService.getScriptByPath({
|
||||
workspace: $workspaceStore!,
|
||||
path: script.path
|
||||
})
|
||||
window.open(
|
||||
scriptToHubUrl(
|
||||
script.content,
|
||||
script.summary,
|
||||
script.description ?? '',
|
||||
script.kind,
|
||||
script.language,
|
||||
script.schema,
|
||||
script.lock ?? '',
|
||||
scriptData.content,
|
||||
scriptData.summary,
|
||||
scriptData.description ?? '',
|
||||
scriptData.kind,
|
||||
scriptData.language,
|
||||
scriptData.schema,
|
||||
scriptData.lock ?? '',
|
||||
$hubBaseUrlStore
|
||||
).toString(),
|
||||
'_blank'
|
||||
|
||||
@@ -233,8 +233,7 @@ export const settings: Record<string, Setting[]> = {
|
||||
key: 'hub_api_secret',
|
||||
fieldType: 'password',
|
||||
storage: 'setting',
|
||||
ee_only: '',
|
||||
requiresReloadOnChange: true
|
||||
ee_only: ''
|
||||
},
|
||||
{
|
||||
label: 'App workspace prefix',
|
||||
|
||||
+2
-2
@@ -4,8 +4,8 @@ verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
wmill = ">=1.565.0"
|
||||
wmill_pg = ">=1.565.0"
|
||||
wmill = ">=1.566.1"
|
||||
wmill_pg = ">=1.566.1"
|
||||
sendgrid = "*"
|
||||
mysql-connector-python = "*"
|
||||
pymongo = "*"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.565.0
|
||||
version: 1.566.1
|
||||
title: OpenFlow Spec
|
||||
contact:
|
||||
name: Ruben Fiszel
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
RootModule = 'WindmillClient.psm1'
|
||||
|
||||
# Version number of this module.
|
||||
ModuleVersion = '1.565.0'
|
||||
ModuleVersion = '1.566.1'
|
||||
|
||||
# Supported PSEditions
|
||||
# CompatiblePSEditions = @()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "wmill"
|
||||
version = "1.565.0"
|
||||
version = "1.566.1"
|
||||
description = "A client library for accessing Windmill server wrapping the Windmill client API"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://windmill.dev"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "wmill-pg"
|
||||
version = "1.565.0"
|
||||
version = "1.566.1"
|
||||
description = "An extension client for the wmill client library focused on pg"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://windmill.dev"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@windmill/windmill",
|
||||
"version": "1.565.0",
|
||||
"version": "1.566.1",
|
||||
"exports": "./src/index.ts",
|
||||
"publish": {
|
||||
"exclude": ["!src", "./s3Types.ts", "./client.ts"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "windmill-client",
|
||||
"description": "Windmill SDK client for browsers and Node.js",
|
||||
"version": "1.565.0",
|
||||
"version": "1.566.1",
|
||||
"author": "Ruben Fiszel",
|
||||
"license": "Apache 2.0",
|
||||
"devDependencies": {
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
1.565.0
|
||||
1.566.1
|
||||
|
||||
Reference in New Issue
Block a user