From f53186653220a5213e0bffdf77187faaa9190a7e Mon Sep 17 00:00:00 2001 From: hugocasa Date: Fri, 21 Nov 2025 12:40:37 +0100 Subject: [PATCH 01/49] fix(backend): handle malformed errors in flow error handler (#7196) --- backend/windmill-worker/src/js_eval.rs | 59 +++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/backend/windmill-worker/src/js_eval.rs b/backend/windmill-worker/src/js_eval.rs index 5feeab8003..cce5d8ef5e 100644 --- a/backend/windmill-worker/src/js_eval.rs +++ b/backend/windmill-worker/src/js_eval.rs @@ -190,9 +190,24 @@ fn should_add_return(expr: &str) -> bool { // Check for common statement patterns that shouldn't have return prepended let statement_prefixes = [ - "const ", "let ", "var ", "if ", "if(", "for ", "for(", - "while ", "while(", "switch ", "switch(", "try ", "try{", - "throw ", "function ", "class ", "async ", "await " + "const ", + "let ", + "var ", + "if ", + "if(", + "for ", + "for(", + "while ", + "while(", + "switch ", + "switch(", + "try ", + "try{", + "throw ", + "function ", + "class ", + "async ", + "await ", ]; for prefix in &statement_prefixes { @@ -677,7 +692,33 @@ function get_from_env(name) {{ .map(|a| { format!("let {a} = get_from_env(\"{a}\");\n",) }) .join(""), if expr.contains("error") && transform_context.contains(&"previous_result".to_string()) { - "let error = previous_result?.error;" + r#"let error = previous_result?.error; +if (!error) { + if (Array.isArray(previous_result)) { + const errors = previous_result.filter(item => item && typeof item === 'object' && 'error' in item); + if (errors.length === 1) { + error = errors[0].error; + } else if (errors.length > 1) { + error = { + name: 'MultipleErrors', + message: errors.map(({ error: e }, i) => `[${e.step_id || i}] ${e.message || e.name}`).join('; '), + errors: previous_result + }; + } else { + error = { + name: 'MultipleErrors', + message: "Could not parse errors", + errors: previous_result + }; + } + } else { + if (previous_result) { + error = { name: 'UnknownError', message: 'Could not parse the error', error: previous_result }; + } else { + error = { name: 'UnknownError', message: 'No error found' }; + } + } +}"# } else { "" }, @@ -1614,9 +1655,15 @@ multiline template`"; assert_eq!(contains_semicolon_outside_strings("x; y"), true); // Semicolons inside strings (should NOT be detected) - assert_eq!(contains_semicolon_outside_strings("\"hello; world\""), false); + assert_eq!( + contains_semicolon_outside_strings("\"hello; world\""), + false + ); assert_eq!(contains_semicolon_outside_strings("'test; string'"), false); - assert_eq!(contains_semicolon_outside_strings("`template; literal`"), false); + assert_eq!( + contains_semicolon_outside_strings("`template; literal`"), + false + ); // Mixed cases assert_eq!( From f85ab0c5dd64f01fbd0c0c9c2a4cea029a04cd50 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Fri, 21 Nov 2025 14:01:33 +0100 Subject: [PATCH 02/49] feat(cli): app policies are generated locally (#7200) --- cli/deno.lock | 129 ++- cli/deps.ts | 1 + cli/src/commands/app/apps.ts | 23 +- cli/src/commands/sync/sync.ts | 1 + frontend/package-lock.json | 756 +++++++++++++++++- frontend/package.json | 2 + frontend/package.sharedUtils.json | 12 + .../src/lib/components/DBManagerDrawer.svelte | 14 +- .../src/lib/components/DBTableEditor.svelte | 7 +- frontend/src/lib/components/Editor.svelte | 2 +- .../lib/components/ExploreAssetButton.svelte | 2 +- frontend/src/lib/components/SqlRepl.svelte | 4 +- .../apps/components/display/AppMap.svelte | 3 +- .../display/InsertRowDrawerButton.svelte | 3 +- .../display/dbtable/AppDbExplorer.svelte | 17 +- .../display/dbtable/DbExplorerCount.svelte | 2 +- .../display/dbtable/DeleteRow.svelte | 2 +- .../display/dbtable/InsertRow.svelte | 3 +- .../display/dbtable/InsertRowRunnable.svelte | 2 +- .../display/dbtable/UpdateCell.svelte | 2 +- .../components/display/dbtable/metadata.ts | 482 +++++++++++ .../display/dbtable/queries/count.ts | 5 +- .../display/dbtable/queries/createTable.ts | 3 +- .../display/dbtable/queries/delete.ts | 5 +- .../display/dbtable/queries/deleteTable.ts | 2 +- .../display/dbtable/queries/insert.ts | 5 +- .../display/dbtable/queries/select.ts | 5 +- .../display/dbtable/queries/update.ts | 5 +- .../apps/components/display/dbtable/utils.ts | 490 +----------- .../display/table/SyncColumnDefs.svelte | 2 +- .../helpers/RunnableComponent.svelte | 3 +- .../components/apps/editor/AppEditor.svelte | 5 +- .../apps/editor/AppEditorHeader.svelte | 249 +----- .../components/apps/editor/AppInputs.svelte | 2 +- .../components/apps/editor/AppPreview.svelte | 3 +- .../components/apps/editor/GridEditor.svelte | 3 +- .../apps/editor/RecomputeAllComponents.svelte | 2 +- .../apps/editor/SettingsPanel.svelte | 2 +- .../apps/editor/SubGridEditor.svelte | 2 +- .../lib/components/apps/editor/appPolicy.ts | 237 ++++++ .../lib/components/apps/editor/appUtils.ts | 83 +- .../components/apps/editor/appUtilsCore.ts | 92 +++ .../lib/components/apps/editor/appUtilsS3.ts | 15 +- .../component/componentCallbacks.svelte.ts | 4 +- .../contextPanel/ComponentOutput.svelte | 2 +- .../editor/contextPanel/DebugPanel.svelte | 4 +- .../components/BackgroundScriptsOutput.svelte | 2 +- .../contextPanel/components/IdEditor.svelte | 2 +- .../components/OutputHeader.svelte | 5 +- .../InlineScriptsPanel.svelte | 2 +- .../InlineScriptsPanelList.svelte | 3 +- .../ArrayStaticInputEditor.svelte | 2 +- .../settingsPanel/ContextVariables.svelte | 3 +- .../inputEditor/TabSelectInput.svelte | 2 +- frontend/src/lib/components/apps/utils.ts | 13 +- .../components/copilot/chat/script/core.ts | 2 +- frontend/src/lib/components/dbOps.ts | 21 +- frontend/src/lib/components/dbTypes.ts | 18 + frontend/src/lib/components/ducklake.ts | 4 + .../src/lib/components/tutorials/utils.ts | 3 +- .../components/wizards/AgChartWizard.svelte | 2 +- frontend/src/lib/consts.ts | 2 +- frontend/vite.sharedUtils.config.js | 59 ++ 63 files changed, 1886 insertions(+), 958 deletions(-) create mode 100644 frontend/package.sharedUtils.json create mode 100644 frontend/src/lib/components/apps/components/display/dbtable/metadata.ts create mode 100644 frontend/src/lib/components/apps/editor/appPolicy.ts create mode 100644 frontend/src/lib/components/apps/editor/appUtilsCore.ts create mode 100644 frontend/src/lib/components/dbTypes.ts create mode 100644 frontend/src/lib/components/ducklake.ts create mode 100644 frontend/vite.sharedUtils.config.js diff --git a/cli/deno.lock b/cli/deno.lock index 801b233835..f8d483c42a 100644 --- a/cli/deno.lock +++ b/cli/deno.lock @@ -1,5 +1,5 @@ { - "version": "4", + "version": "5", "specifiers": { "jsr:@david/code-block-writer@^13.0.2": "13.0.2", "jsr:@deno/cache-dir@~0.10.3": "0.10.3", @@ -60,6 +60,8 @@ "jsr:@windmill-labs/cliffy-table@^1.0.0-rc.5": "1.0.0-rc.5", "npm:@ayonli/jsext@*": "1.8.0", "npm:@types/node@*": "22.12.0", + "npm:@windmill-labs/shared-utils@1.0.1": "1.0.1", + "npm:@windmill-labs/shared-utils@1.0.2": "1.0.2", "npm:centdix-utils@*": "1.0.15", "npm:diff@*": "8.0.2", "npm:es-main@*": "1.3.0", @@ -293,82 +295,134 @@ ] }, "@esbuild/aix-ppc64@0.25.8": { - "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==" + "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", + "os": ["aix"], + "cpu": ["ppc64"] }, "@esbuild/android-arm64@0.25.8": { - "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==" + "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", + "os": ["android"], + "cpu": ["arm64"] }, "@esbuild/android-arm@0.25.8": { - "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==" + "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", + "os": ["android"], + "cpu": ["arm"] }, "@esbuild/android-x64@0.25.8": { - "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==" + "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", + "os": ["android"], + "cpu": ["x64"] }, "@esbuild/darwin-arm64@0.25.8": { - "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==" + "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", + "os": ["darwin"], + "cpu": ["arm64"] }, "@esbuild/darwin-x64@0.25.8": { - "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==" + "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", + "os": ["darwin"], + "cpu": ["x64"] }, "@esbuild/freebsd-arm64@0.25.8": { - "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==" + "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", + "os": ["freebsd"], + "cpu": ["arm64"] }, "@esbuild/freebsd-x64@0.25.8": { - "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==" + "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", + "os": ["freebsd"], + "cpu": ["x64"] }, "@esbuild/linux-arm64@0.25.8": { - "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==" + "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", + "os": ["linux"], + "cpu": ["arm64"] }, "@esbuild/linux-arm@0.25.8": { - "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==" + "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", + "os": ["linux"], + "cpu": ["arm"] }, "@esbuild/linux-ia32@0.25.8": { - "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==" + "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", + "os": ["linux"], + "cpu": ["ia32"] }, "@esbuild/linux-loong64@0.25.8": { - "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==" + "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", + "os": ["linux"], + "cpu": ["loong64"] }, "@esbuild/linux-mips64el@0.25.8": { - "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==" + "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", + "os": ["linux"], + "cpu": ["mips64el"] }, "@esbuild/linux-ppc64@0.25.8": { - "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==" + "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", + "os": ["linux"], + "cpu": ["ppc64"] }, "@esbuild/linux-riscv64@0.25.8": { - "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==" + "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", + "os": ["linux"], + "cpu": ["riscv64"] }, "@esbuild/linux-s390x@0.25.8": { - "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==" + "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", + "os": ["linux"], + "cpu": ["s390x"] }, "@esbuild/linux-x64@0.25.8": { - "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==" + "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", + "os": ["linux"], + "cpu": ["x64"] }, "@esbuild/netbsd-arm64@0.25.8": { - "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==" + "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", + "os": ["netbsd"], + "cpu": ["arm64"] }, "@esbuild/netbsd-x64@0.25.8": { - "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==" + "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", + "os": ["netbsd"], + "cpu": ["x64"] }, "@esbuild/openbsd-arm64@0.25.8": { - "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==" + "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", + "os": ["openbsd"], + "cpu": ["arm64"] }, "@esbuild/openbsd-x64@0.25.8": { - "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==" + "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", + "os": ["openbsd"], + "cpu": ["x64"] }, "@esbuild/openharmony-arm64@0.25.8": { - "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==" + "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", + "os": ["openharmony"], + "cpu": ["arm64"] }, "@esbuild/sunos-x64@0.25.8": { - "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==" + "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", + "os": ["sunos"], + "cpu": ["x64"] }, "@esbuild/win32-arm64@0.25.8": { - "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==" + "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", + "os": ["win32"], + "cpu": ["arm64"] }, "@esbuild/win32-ia32@0.25.8": { - "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==" + "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", + "os": ["win32"], + "cpu": ["ia32"] }, "@esbuild/win32-x64@0.25.8": { - "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==" + "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", + "os": ["win32"], + "cpu": ["x64"] }, "@isaacs/balanced-match@4.0.1": { "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==" @@ -385,6 +439,12 @@ "undici-types" ] }, + "@windmill-labs/shared-utils@1.0.1": { + "integrity": "sha512-DUMzPIFCKImuGpbuHXXmGGUT3VXYlgrv/jIIEOW+Iig+9tZvYqOUxfgn32lDhm73k82xBg8MdAf+0qABzfqFeQ==" + }, + "@windmill-labs/shared-utils@1.0.2": { + "integrity": "sha512-3LwALmwMeO3MqglGlyTtBUF05/ogpdDM5GiZKGN7271AEctS+ZJi3pXMHZ+YZdLxdgi2qLNNnVHO8qG5vFud2Q==" + }, "accepts@2.0.0": { "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "dependencies": [ @@ -509,7 +569,7 @@ }, "esbuild@0.25.8": { "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", - "dependencies": [ + "optionalDependencies": [ "@esbuild/aix-ppc64", "@esbuild/android-arm", "@esbuild/android-arm64", @@ -536,7 +596,9 @@ "@esbuild/win32-arm64", "@esbuild/win32-ia32", "@esbuild/win32-x64" - ] + ], + "scripts": true, + "bin": true }, "escape-html@1.0.3": { "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" @@ -659,13 +721,15 @@ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, "is-docker@3.0.0": { - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==" + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "bin": true }, "is-inside-container@1.0.0": { "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", "dependencies": [ "is-docker" - ] + ], + "bin": true }, "is-promise@4.0.0": { "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" @@ -899,7 +963,8 @@ ] }, "sudo-prompt@9.2.1": { - "integrity": "sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==" + "integrity": "sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==", + "deprecated": true }, "toidentifier@1.0.1": { "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" diff --git a/cli/deps.ts b/cli/deps.ts index 23fd292365..f597a9a6c4 100644 --- a/cli/deps.ts +++ b/cli/deps.ts @@ -57,6 +57,7 @@ export { WebSocketServer, WebSocket } from "npm:ws"; export * as getPort from "npm:get-port@7.1.0"; export * as open from "npm:open"; export * as esMain from "npm:es-main"; +export * as windmillUtils from "npm:@windmill-labs/shared-utils@1.0.2"; import { OpenAPI } from "./gen/index.ts"; diff --git a/cli/src/commands/app/apps.ts b/cli/src/commands/app/apps.ts index 4f1b41281d..d04c2edbdb 100644 --- a/cli/src/commands/app/apps.ts +++ b/cli/src/commands/app/apps.ts @@ -1,7 +1,15 @@ // deno-lint-ignore-file no-explicit-any import { requireLogin } from "../../core/auth.ts"; import { resolveWorkspace, validatePath } from "../../core/context.ts"; -import { colors, Command, log, SEP, Table, yamlParseFile } from "../../../deps.ts"; +import { + colors, + Command, + log, + SEP, + Table, + windmillUtils, + yamlParseFile, +} from "../../../deps.ts"; import * as wmill from "../../../gen/services.gen.ts"; import { ListableApp, Policy } from "../../../gen/types.gen.ts"; @@ -37,6 +45,7 @@ export async function pushApp( } catch { //ignore } + app.value.policy = undefined; if (!localPath.endsWith(SEP)) { localPath += SEP; @@ -69,7 +78,7 @@ export async function pushApp( } replaceInlineScripts(localApp.value); - + await generatingPolicy(localApp.value, remotePath); if (app) { if (isSuperset(localApp, app)) { log.info(colors.green(`App ${remotePath} is up to date`)); @@ -98,6 +107,16 @@ export async function pushApp( } } +async function generatingPolicy(app: any, path: string) { + log.info(colors.gray(`Generating fresh policy for app ${path}...`)); + try { + app.policy = await windmillUtils.updatePolicy(app, undefined); + } catch (e) { + log.error(colors.red(`Error generating policy for app ${path}: ${e}`)); + throw e; + } +} + async function list(opts: GlobalOptions & { includeDraftOnly?: boolean }) { const workspace = await resolveWorkspace(opts); await requireLogin(opts); diff --git a/cli/src/commands/sync/sync.ts b/cli/src/commands/sync/sync.ts index 0c64a0d857..e1ea241dad 100644 --- a/cli/src/commands/sync/sync.ts +++ b/cli/src/commands/sync/sync.ts @@ -415,6 +415,7 @@ function ZipFSElement( }; } + app.policy = undefined; yield { isDirectory: false, path: path.join(finalPath, "app.yaml"), diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 50a0547dfa..9a230df77b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -143,6 +143,7 @@ "tslib": "^2.6.1", "typescript": "^5.5.0", "vite": "npm:rolldown-vite@latest", + "vite-plugin-dts": "^4.5.4", "vite-plugin-mkcert": "^1.17.5", "yootils": "^0.3.1" }, @@ -266,17 +267,42 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-validator-identifier": { + "node_modules/@babel/helper-string-parser": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@babel/runtime": { "version": "7.28.4", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", @@ -286,6 +312,20 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@codingame/monaco-vscode-05a2a821-e4de-5941-b7f9-bbf01c09f229-common": { "version": "21.6.0", "resolved": "https://registry.npmjs.org/@codingame/monaco-vscode-05a2a821-e4de-5941-b7f9-bbf01c09f229-common/-/monaco-vscode-05a2a821-e4de-5941-b7f9-bbf01c09f229-common-21.6.0.tgz", @@ -2376,6 +2416,141 @@ "svelte": "^3.0.0 || ^4.0.0 || ^5.0.0-next.118" } }, + "node_modules/@microsoft/api-extractor": { + "version": "7.55.0", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.55.0.tgz", + "integrity": "sha512-TYc5OtAK/9E3HGgd2bIfSjQDYIwPc0dysf9rPiwXZGsq916I6W2oww9bhm1OxPOeg6rMfOX3PoroGd7oCryYog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/api-extractor-model": "7.32.0", + "@microsoft/tsdoc": "~0.16.0", + "@microsoft/tsdoc-config": "~0.18.0", + "@rushstack/node-core-library": "5.18.0", + "@rushstack/rig-package": "0.6.0", + "@rushstack/terminal": "0.19.3", + "@rushstack/ts-command-line": "5.1.3", + "diff": "~8.0.2", + "lodash": "~4.17.15", + "minimatch": "10.0.3", + "resolve": "~1.22.1", + "semver": "~7.5.4", + "source-map": "~0.6.1", + "typescript": "5.8.2" + }, + "bin": { + "api-extractor": "bin/api-extractor" + } + }, + "node_modules/@microsoft/api-extractor-model": { + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.32.0.tgz", + "integrity": "sha512-QIVJSreb8fGGJy1Qx0yzGVXxvHJN1WXgkFNHFheVv1iBJNqgvp+xeT3ienJmRwXmPPc5Es/cxBrXtKZJR3i7iw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "~0.16.0", + "@microsoft/tsdoc-config": "~0.18.0", + "@rushstack/node-core-library": "5.18.0" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/diff": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz", + "integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/typescript": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/@microsoft/tsdoc": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.16.0.tgz", + "integrity": "sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@microsoft/tsdoc-config": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.18.0.tgz", + "integrity": "sha512-8N/vClYyfOH+l4fLkkr9+myAoR6M7akc8ntBJ4DJdWH2b09uVfr71+LTMpNyG19fNqWDg8KEDZhx5wxuqHyGjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "0.16.0", + "ajv": "~8.12.0", + "jju": "~1.4.0", + "resolve": "~1.22.2" + } + }, + "node_modules/@microsoft/tsdoc-config/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/@napi-rs/wasm-runtime": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz", @@ -2748,6 +2923,49 @@ "dev": true, "license": "MIT" }, + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/pluginutils/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@rollup/rollup-linux-x64-gnu": { "version": "4.52.4", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.4.tgz", @@ -2761,6 +2979,169 @@ "linux" ] }, + "node_modules/@rushstack/node-core-library": { + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.18.0.tgz", + "integrity": "sha512-XDebtBdw5S3SuZIt+Ra2NieT8kQ3D2Ow1HxhDQ/2soinswnOu9e7S69VSwTOLlQnx5mpWbONu+5JJjDxMAb6Fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "~8.13.0", + "ajv-draft-04": "~1.0.0", + "ajv-formats": "~3.0.1", + "fs-extra": "~11.3.0", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.22.1", + "semver": "~7.5.4" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/node-core-library/node_modules/ajv": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/@rushstack/problem-matcher": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@rushstack/problem-matcher/-/problem-matcher-0.1.1.tgz", + "integrity": "sha512-Fm5XtS7+G8HLcJHCWpES5VmeMyjAKaWeyZU5qPzZC+22mPlJzAsOxymHiWIfuirtPckX3aptWws+K2d0BzniJA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/rig-package": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.6.0.tgz", + "integrity": "sha512-ZQmfzsLE2+Y91GF15c65L/slMRVhF6Hycq04D4TwtdGaUAbIXXg9c5pKA5KFU7M4QMaihoobp9JJYpYcaY3zOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve": "~1.22.1", + "strip-json-comments": "~3.1.1" + } + }, + "node_modules/@rushstack/terminal": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.19.3.tgz", + "integrity": "sha512-0P8G18gK9STyO+CNBvkKPnWGMxESxecTYqOcikHOVIHXa9uAuTK+Fw8TJq2Gng1w7W6wTC9uPX6hGNvrMll2wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rushstack/node-core-library": "5.18.0", + "@rushstack/problem-matcher": "0.1.1", + "supports-color": "~8.1.1" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/terminal/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@rushstack/ts-command-line": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-5.1.3.tgz", + "integrity": "sha512-Kdv0k/BnnxIYFlMVC1IxrIS0oGQd4T4b7vKfx52Y2+wk2WZSDFIvedr7JrhenzSlm3ou5KwtoTGTGd5nbODRug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rushstack/terminal": "0.19.3", + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "string-argv": "~0.3.1" + } + }, + "node_modules/@rushstack/ts-command-line/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, "node_modules/@scalar/openapi-parser": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/@scalar/openapi-parser/-/openapi-parser-0.15.0.tgz", @@ -3045,6 +3426,13 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/argparse": { + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", + "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", @@ -3648,12 +4036,162 @@ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "license": "ISC" }, + "node_modules/@volar/language-core": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.23.tgz", + "integrity": "sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.23" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.23.tgz", + "integrity": "sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.23.tgz", + "integrity": "sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.23", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, "node_modules/@vscode/iconv-lite-umd": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.0.tgz", "integrity": "sha512-bRRFxLfg5dtAyl5XyiVWz/ZBPahpOpPrNYnnHpOpUZvam4tKH35wdhP4Kj6PbM0+KdliOsPzbGWpkxcdpNB/sg==", "license": "MIT" }, + "node_modules/@vue/compiler-core": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.24.tgz", + "integrity": "sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@vue/shared": "3.5.24", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-core/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/@vue/compiler-core/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.24.tgz", + "integrity": "sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.24", + "@vue/shared": "3.5.24" + } + }, + "node_modules/@vue/compiler-vue2": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz", + "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==", + "dev": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/@vue/language-core": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.2.0.tgz", + "integrity": "sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "~2.4.11", + "@vue/compiler-dom": "^3.5.0", + "@vue/compiler-vue2": "^2.7.16", + "@vue/shared": "^3.5.0", + "alien-signals": "^0.4.9", + "minimatch": "^9.0.3", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/language-core/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/language-core/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@vue/language-core/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.24.tgz", + "integrity": "sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==", + "dev": true, + "license": "MIT" + }, "node_modules/@windmill-labs/svelte-dnd-action": { "version": "0.9.48", "resolved": "https://registry.npmjs.org/@windmill-labs/svelte-dnd-action/-/svelte-dnd-action-0.9.48.tgz", @@ -3849,6 +4387,13 @@ } } }, + "node_modules/alien-signals": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-0.4.14.tgz", + "integrity": "sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q==", + "dev": true, + "license": "MIT" + }, "node_modules/amator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/amator/-/amator-1.1.0.tgz", @@ -4688,6 +5233,13 @@ "node": ">=18" } }, + "node_modules/compare-versions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", + "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", + "dev": true, + "license": "MIT" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -5123,6 +5675,13 @@ "url": "https://opencollective.com/date-fns" } }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true, + "license": "MIT" + }, "node_modules/debounce-promise": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/debounce-promise/-/debounce-promise-3.1.2.tgz", @@ -6156,6 +6715,13 @@ "node": ">=6" } }, + "node_modules/exsolve": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", + "dev": true, + "license": "MIT" + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -6422,6 +6988,21 @@ "license": "MIT", "optional": true }, + "node_modules/fs-extra": { + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.2.tgz", + "integrity": "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -6804,6 +7385,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -7081,6 +7669,16 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, "node_modules/highlight.js": { "version": "11.11.1", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", @@ -7216,7 +7814,6 @@ "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -7514,6 +8111,13 @@ "jiti": "bin/jiti.js" } }, + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true, + "license": "MIT" + }, "node_modules/js-base64": { "version": "3.7.8", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.8.tgz", @@ -7584,6 +8188,19 @@ "integrity": "sha512-WRitRfs6BGq4q8gTgOy4ek7iPFXjbra0H3PmDLKm2xnZ+Gh1HUhiKGgCZkSPNULlP7mvfu6FV/mOLhCarspADQ==", "license": "MIT" }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/jsonpointer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", @@ -7631,6 +8248,13 @@ "dev": true, "license": "MIT" }, + "node_modules/kolorist": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", + "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", + "dev": true, + "license": "MIT" + }, "node_modules/lerc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lerc/-/lerc-3.0.0.tgz", @@ -8115,6 +8739,50 @@ "dev": true, "license": "MIT" }, + "node_modules/local-pkg": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.2.tgz", + "integrity": "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mlly": "^1.7.4", + "pkg-types": "^2.3.0", + "quansync": "^0.2.11" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/local-pkg/node_modules/confbox": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", + "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/local-pkg/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/local-pkg/node_modules/pkg-types": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, "node_modules/locate-character": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", @@ -9428,6 +10096,13 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -11067,6 +11742,23 @@ "node": ">=8.x" } }, + "node_modules/quansync": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", + "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -11947,6 +12639,13 @@ "license": "CC0-1.0", "peer": true }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -11956,6 +12655,16 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -13301,6 +14010,16 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", @@ -13486,6 +14205,33 @@ } } }, + "node_modules/vite-plugin-dts": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/vite-plugin-dts/-/vite-plugin-dts-4.5.4.tgz", + "integrity": "sha512-d4sOM8M/8z7vRXHHq/ebbblfaxENjogAAekcfcDCCwAyvGqnPrc7f4NZbvItS+g4WTgerW0xDwSz5qz11JT3vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/api-extractor": "^7.50.1", + "@rollup/pluginutils": "^5.1.4", + "@volar/typescript": "^2.4.11", + "@vue/language-core": "2.2.0", + "compare-versions": "^6.1.1", + "debug": "^4.4.0", + "kolorist": "^1.8.0", + "local-pkg": "^1.0.0", + "magic-string": "^0.30.17" + }, + "peerDependencies": { + "typescript": "*", + "vite": "*" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, "node_modules/vite-plugin-mkcert": { "version": "1.17.9", "resolved": "https://registry.npmjs.org/vite-plugin-mkcert/-/vite-plugin-mkcert-1.17.9.tgz", diff --git a/frontend/package.json b/frontend/package.json index 72ec938dab..f502a8bf86 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,6 +4,7 @@ "scripts": { "dev": "vite dev", "build": "vite build", + "build:utils": "vite build --config vite.sharedUtils.config.js", "preview": "vite preview", "postinstall": "node -e \"if (require('fs').existsSync('./scripts/untar_ui_builder.js')) { require('child_process').execSync('node ./scripts/untar_ui_builder.js', {stdio: 'inherit'}) }\"", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --threshold warning", @@ -65,6 +66,7 @@ "tslib": "^2.6.1", "typescript": "^5.5.0", "vite": "npm:rolldown-vite@latest", + "vite-plugin-dts": "^4.5.4", "vite-plugin-mkcert": "^1.17.5", "yootils": "^0.3.1" }, diff --git a/frontend/package.sharedUtils.json b/frontend/package.sharedUtils.json new file mode 100644 index 0000000000..e2ebbee6fd --- /dev/null +++ b/frontend/package.sharedUtils.json @@ -0,0 +1,12 @@ +{ + "name": "@windmill-labs/shared-utils", + "version": "1.0.2", + "type": "module", + "private": false, + "main": "./lib.es.js", + "module": "./lib.es.js", + "exports": { + ".": "./lib.es.js" + }, + "types": "./lib.d.ts" +} diff --git a/frontend/src/lib/components/DBManagerDrawer.svelte b/frontend/src/lib/components/DBManagerDrawer.svelte index 583f7f30a6..39b1f331c8 100644 --- a/frontend/src/lib/components/DBManagerDrawer.svelte +++ b/frontend/src/lib/components/DBManagerDrawer.svelte @@ -7,19 +7,14 @@ import { ArrowLeft, Expand, Loader2, Minimize, RefreshCcw } from 'lucide-svelte' import { dbSupportsSchemas, - getDbSchemas, getLanguageByResourceType, - loadAllTablesMetaData, - loadTableMetaData, type TableMetadata } from './apps/components/display/dbtable/utils' import DbManager from './DBManager.svelte' import { dbDeleteTableActionWithPreviewScript, dbTableOpsWithPreviewScripts, - getDucklakeSchema, - wrapDucklakeQuery, - type DbInput + getDucklakeSchema } from './dbOps' import { makeCreateTableQuery } from './apps/components/display/dbtable/queries/createTable' import { runScriptAndPollResult } from './jobs/utils' @@ -27,6 +22,13 @@ import SqlRepl from './SqlRepl.svelte' import SimpleAgTable from './SimpleAgTable.svelte' import { untrack } from 'svelte' + import type { DbInput } from './dbTypes' + import { wrapDucklakeQuery } from './ducklake' + import { + getDbSchemas, + loadAllTablesMetaData, + loadTableMetaData + } from './apps/components/display/dbtable/metadata' let input: DbInput | undefined = $state() let open = $derived(!!input) diff --git a/frontend/src/lib/components/DBTableEditor.svelte b/frontend/src/lib/components/DBTableEditor.svelte index 2c5a1ba321..507e82236a 100644 --- a/frontend/src/lib/components/DBTableEditor.svelte +++ b/frontend/src/lib/components/DBTableEditor.svelte @@ -53,11 +53,7 @@ import { Cell } from './table' import DataTable from './table/DataTable.svelte' import Head from './table/Head.svelte' - import { - datatypeHasLength, - dbSupportsSchemas, - type DbType - } from './apps/components/display/dbtable/utils' + import { datatypeHasLength, dbSupportsSchemas } from './apps/components/display/dbtable/utils' import { DB_TYPES } from '$lib/consts' import Popover from './meltComponents/Popover.svelte' import Tooltip from './meltComponents/Tooltip.svelte' @@ -74,6 +70,7 @@ import Select from './select/Select.svelte' import { safeSelectItems } from './select/utils.svelte' import TextInput from './text_input/TextInput.svelte' + import type { DbType } from './dbTypes' const { onConfirm, dbType, previewSql, dbSchema, currentSchema }: DBTableEditorProps = $props() diff --git a/frontend/src/lib/components/Editor.svelte b/frontend/src/lib/components/Editor.svelte index 792b5184b8..9eb37b15ff 100644 --- a/frontend/src/lib/components/Editor.svelte +++ b/frontend/src/lib/components/Editor.svelte @@ -91,10 +91,10 @@ import { extToLang, langToExt } from '$lib/editorLangUtils' import { aiChatManager } from './copilot/chat/AIChatManager.svelte' import type { Selection } from 'monaco-editor' - import { getDbSchemas } from './apps/components/display/dbtable/utils' import { canHavePreprocessor, getPreprocessorModuleCode } from '$lib/script_helpers' import { setMonacoTypescriptOptions } from './monacoLanguagesOptions' import { copilotInfo } from '$lib/aiStore' + import { getDbSchemas } from './apps/components/display/dbtable/metadata' // import EditorTheme from './EditorTheme.svelte' let divEl: HTMLDivElement | null = $state(null) diff --git a/frontend/src/lib/components/ExploreAssetButton.svelte b/frontend/src/lib/components/ExploreAssetButton.svelte index 18cdf58e3b..2b59c8ef59 100644 --- a/frontend/src/lib/components/ExploreAssetButton.svelte +++ b/frontend/src/lib/components/ExploreAssetButton.svelte @@ -12,7 +12,7 @@ diff --git a/frontend/src/lib/components/apps/editor/contextPanel/components/IdEditor.svelte b/frontend/src/lib/components/apps/editor/contextPanel/components/IdEditor.svelte index 80acbfe23c..408decb8a2 100644 --- a/frontend/src/lib/components/apps/editor/contextPanel/components/IdEditor.svelte +++ b/frontend/src/lib/components/apps/editor/contextPanel/components/IdEditor.svelte @@ -1,10 +1,10 @@

Pick a Workspace Flow

- + {@render children?.()} - - {/each} - - {:else} - {#each Array(10).fill(0) as _} - - {/each} - {/if} -
diff --git a/frontend/src/lib/components/flows/pickers/WorkspaceScriptPicker.svelte b/frontend/src/lib/components/flows/pickers/WorkspaceScriptPicker.svelte index def0c1e58e..ce1a7e63e0 100644 --- a/frontend/src/lib/components/flows/pickers/WorkspaceScriptPicker.svelte +++ b/frontend/src/lib/components/flows/pickers/WorkspaceScriptPicker.svelte @@ -1,6 +1,6 @@
- + {@render children?.()}
diff --git a/frontend/src/lib/components/chat/ChatMessage.svelte b/frontend/src/lib/components/chat/ChatMessage.svelte new file mode 100644 index 0000000000..f811abd0a2 --- /dev/null +++ b/frontend/src/lib/components/chat/ChatMessage.svelte @@ -0,0 +1,127 @@ + + +
+ {#if stepName} +
+ {stepName} +
+ {/if} + + {#if role === 'user'} +

{content}

+ {:else if loading} +
+ + Processing... +
+ {:else if content} + {#if s3Object} +
+ +
+ {:else if enableMarkdown} +
+ {#if role === 'tool'} + {#if success !== false} + + {:else} + + {/if} + {/if} +
+ +
+
+ {:else} +

{content}

+ {/if} + {:else} +

No result

+ {/if} +
diff --git a/frontend/src/lib/components/chat/utils.ts b/frontend/src/lib/components/chat/utils.ts new file mode 100644 index 0000000000..60f59fce10 --- /dev/null +++ b/frontend/src/lib/components/chat/utils.ts @@ -0,0 +1,30 @@ +export function parseStreamDeltas(streamData: string): { + content: string + type?: string + success?: boolean +} { + const lines = streamData.trim().split('\n') + let content = '' + let type = 'message' + let success = true + + for (const line of lines) { + if (!line.trim()) continue + try { + const parsed = JSON.parse(line) + if (parsed.type === 'tool_result') { + type = 'tool_result' + success = parsed.success + const toolName = parsed.function_name + content = success ? `Used ${toolName} tool` : `Failed to use ${toolName} tool` + } + if (parsed.type === 'token_delta' && parsed.content) { + content += parsed.content + } + } catch (e) { + console.error('Failed to parse stream line:', line, e) + } + } + + return { content, type, success } +} diff --git a/frontend/src/lib/components/flows/conversations/FlowChatInterface.svelte b/frontend/src/lib/components/flows/conversations/FlowChatInterface.svelte index 7bbad19d25..99e3c84f88 100644 --- a/frontend/src/lib/components/flows/conversations/FlowChatInterface.svelte +++ b/frontend/src/lib/components/flows/conversations/FlowChatInterface.svelte @@ -1,8 +1,8 @@ - -
- {#if message.step_name} -
{message.step_name}
- {/if} - - {#if message.message_type === 'user'} -

{message.content}

- {:else if message.loading} -
- - Processing... -
- {:else if message.content} - {#if s3Object} -
- -
- {:else} -
- {#if message.message_type === 'tool'} - {#if message.success !== false} - - {:else} - - {/if} - {/if} -
- -
-
- {/if} - {:else} -

No result

- {/if} -
From 7eed0ddd8ef0f75a189e7238718906ae5a05ffd3 Mon Sep 17 00:00:00 2001 From: Diego Imbert <70353967+diegoimbert@users.noreply.github.com> Date: Mon, 24 Nov 2025 20:37:08 +0100 Subject: [PATCH 16/49] remove outdated tooltip (#7216) --- .../src/lib/components/apps/editor/component/components.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/frontend/src/lib/components/apps/editor/component/components.ts b/frontend/src/lib/components/apps/editor/component/components.ts index c3b93c15f7..d6edf45e97 100644 --- a/frontend/src/lib/components/apps/editor/component/components.ts +++ b/frontend/src/lib/components/apps/editor/component/components.ts @@ -2413,12 +2413,7 @@ This is a paragraph. documentationLink: `${documentationBaseUrl}/select`, dims: '2:1-3:1' as AppComponentDimensions, customCss: { - input: { - style: '', - tooltip: - 'https://github.com/rob-balfre/svelte-select/blob/master/docs/theming_variables.md', - class: '' - } + input: { style: '', class: '' } }, initialData: { recomputeIds: true, From a2d3297343338ba0b559481723e64db5f4acf3a7 Mon Sep 17 00:00:00 2001 From: Guilhem Date: Mon, 24 Nov 2025 19:43:14 +0000 Subject: [PATCH 17/49] fix(frontend): improve preprocessor discoverability (#7214) * add tooltip * show payload tooltip * Add preprocessor button * nit * improve preprocessor template * fix add preprocessor * fix oneof wrapping * clean * Use funnel cog icon for preprocessor step * nit --- frontend/src/lib/components/ArgInput.svelte | 2 + .../src/lib/components/QueuePosition.svelte | 2 +- .../src/lib/components/ScriptBuilder.svelte | 7 +- .../flows/content/FlowEditorPanel.svelte | 6 +- .../graph/renderers/nodes/InputNode.svelte | 7 +- .../src/lib/components/icons/FunnelCog.svelte | 50 ++++++++ .../components/triggers/CaptureSection.svelte | 112 +++++++++++++----- .../components/triggers/CaptureTable.svelte | 2 +- frontend/src/lib/script_helpers.ts | 46 +++---- 9 files changed, 178 insertions(+), 56 deletions(-) create mode 100644 frontend/src/lib/components/icons/FunnelCog.svelte diff --git a/frontend/src/lib/components/ArgInput.svelte b/frontend/src/lib/components/ArgInput.svelte index f912fad49f..c46f85410d 100644 --- a/frontend/src/lib/components/ArgInput.svelte +++ b/frontend/src/lib/components/ArgInput.svelte @@ -1049,6 +1049,8 @@ {#if oneOf && oneOf.length >= 2} { oneOfSelected = detail const selectedObjProperties = diff --git a/frontend/src/lib/components/QueuePosition.svelte b/frontend/src/lib/components/QueuePosition.svelte index d9180aa7e8..375942abbc 100644 --- a/frontend/src/lib/components/QueuePosition.svelte +++ b/frontend/src/lib/components/QueuePosition.svelte @@ -87,7 +87,7 @@ {#if queueState} -
+
Queue position: {queueState.position} {#if !minimal} (Waiting for an available worker) diff --git a/frontend/src/lib/components/ScriptBuilder.svelte b/frontend/src/lib/components/ScriptBuilder.svelte index df82c244ab..fa817b4058 100644 --- a/frontend/src/lib/components/ScriptBuilder.svelte +++ b/frontend/src/lib/components/ScriptBuilder.svelte @@ -849,7 +849,7 @@ captureOn.set(true) } - function addPreprocessor() { + function addPreprocessor(e?: { detail?: { args: Record } }) { const code = editor?.getCode() if (code) { const preprocessorCode = getPreprocessorFullCode(script.language, false) @@ -865,6 +865,11 @@ } } selectedInputTab = 'preprocessor' + + // Apply provided args to the preprocessor + if (e?.detail?.args && Object.keys(e.detail.args).length > 0) { + args = { ...args, ...e.detail.args } + } } function handleDeployTrigger(trigger: Trigger) { diff --git a/frontend/src/lib/components/flows/content/FlowEditorPanel.svelte b/frontend/src/lib/components/flows/content/FlowEditorPanel.svelte index f0288e6533..f5ca067eeb 100644 --- a/frontend/src/lib/components/flows/content/FlowEditorPanel.svelte +++ b/frontend/src/lib/components/flows/content/FlowEditorPanel.svelte @@ -64,7 +64,8 @@ initialPathStore, fakeInitialPath, previewArgs, - flowInputEditorState + flowInputEditorState, + stepsInputArgs } = getContext('FlowEditorContext') const selectedId = $derived(selectionManager.getSelectedId()) @@ -115,10 +116,11 @@ {:else if selectedId === 'Trigger'} { + on:addPreprocessor={async (e) => { await insertNewPreprocessorModule(flowStore, flowStateStore, { language: 'bun' }) + stepsInputArgs.setStepArgs('preprocessor', e.detail.args ?? {}) selectionManager.selectId('preprocessor') }} on:updateSchema={(e) => { diff --git a/frontend/src/lib/components/graph/renderers/nodes/InputNode.svelte b/frontend/src/lib/components/graph/renderers/nodes/InputNode.svelte index dc76c013b6..705a07b879 100644 --- a/frontend/src/lib/components/graph/renderers/nodes/InputNode.svelte +++ b/frontend/src/lib/components/graph/renderers/nodes/InputNode.svelte @@ -12,6 +12,7 @@ import { MessageSquare, DiffIcon } from 'lucide-svelte' import { Button } from '$lib/components/common' import { getGraphContext } from '../../graphContext' + import FunnelCog from '$lib/components/icons/FunnelCog.svelte' interface Props { data: InputN['data'] @@ -71,7 +72,11 @@ }} > {#snippet trigger()} - + {/snippet}
diff --git a/frontend/src/lib/components/icons/FunnelCog.svelte b/frontend/src/lib/components/icons/FunnelCog.svelte new file mode 100644 index 0000000000..12602f6849 --- /dev/null +++ b/frontend/src/lib/components/icons/FunnelCog.svelte @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + diff --git a/frontend/src/lib/components/triggers/CaptureSection.svelte b/frontend/src/lib/components/triggers/CaptureSection.svelte index 366b03dc1a..eebfe3c1a7 100644 --- a/frontend/src/lib/components/triggers/CaptureSection.svelte +++ b/frontend/src/lib/components/triggers/CaptureSection.svelte @@ -14,7 +14,7 @@ import AnimatedButton from '../common/button/AnimatedButton.svelte' import PulseButton from '../common/button/PulseButton.svelte' import Button from '../common/button/Button.svelte' - import { CircleStop, History, Play, Loader2 } from 'lucide-svelte' + import { CircleStop, History, Play, Loader2, ExternalLink } from 'lucide-svelte' import ConnectionIndicator, { type ConnectionInfo } from '../common/alert/ConnectionIndicator.svelte' @@ -67,7 +67,7 @@ const dispatch = createEventDispatcher<{ captureToggle: { disableOnly?: boolean } updateSchema: { payloadData: Record; redirect: boolean; args?: boolean } - addPreprocessor: null + addPreprocessor: { args: Record } | null testWithArgs: Record applyArgs: { kind: 'main' | 'preprocessor'; args: Record } }>() @@ -121,6 +121,7 @@ let lastCaptureId: number | undefined = undefined let displayResult: DisplayResult | undefined = $state(undefined) let toolbarLocation: 'internal' | 'external' | undefined = $state(undefined) + let showRawPayload = $state(false) function selectCapture(capture: Capture) { selectedCapture = capture @@ -225,13 +226,14 @@ } } - function getCapturePayload(capture: Capture) { + function getCapturePayload(capture: Capture | undefined, showRawPayload: boolean) { + if (!capture) return {} let payloadData: any = {} const preprocessor_args = isObject(capture.preprocessor_args) ? capture.preprocessor_args : {} if ('wm_trigger' in preprocessor_args) { // v1 payloadData = - testKind === 'preprocessor' + testKind === 'preprocessor' || showRawPayload ? { ...(typeof capture.main_args === 'object' ? capture.main_args : {}), ...preprocessor_args @@ -239,11 +241,20 @@ : capture.main_args } else { // v2 - payloadData = testKind === 'preprocessor' ? capture.preprocessor_args : capture.main_args + payloadData = + testKind === 'preprocessor' || showRawPayload + ? capture.preprocessor_args + : capture.main_args } return payloadData } + function toggleRawPayload() { + showRawPayload = !showRawPayload + } + + const payloadData = $derived(getCapturePayload(selectedCapture, showRawPayload)) + // Start or stop capture listening based on active state $effect(() => { if (captureInfo.active) { @@ -380,16 +391,16 @@ {#if selectedCapture} {@const SvelteComponent = triggerIconMap[captureType]}
- + Capture {formatDateShort(selectedCapture?.created_at)}
{/if} - {#if selectedCapture} + {#if selectedCapture && !showRawPayload} {@const label = isFlow && testKind === 'main' ? 'Test flow with args' : 'Apply args'} {@const title = isFlow && testKind === 'main' @@ -455,26 +466,73 @@ /> {/if}
-
- {#if isLoadingBigPayload} - - {:else if selectedCapture?.main_args} -
- { - toolbarLocation = detail - }} - /> +
+
+ {#if isLoadingBigPayload} + + {:else if selectedCapture?.main_args} +
+ {#if showRawPayload} + +
+
+ Add a preprocessor to use the raw payload data in the flow. + + Learn more about preprocessors + + +
+
+ +
+
+
+ {/if} + { + toolbarLocation = detail + }} + /> +
+ {:else} +
No captures to show yet.
+ {/if} +
+ + {#if !hasPreprocessor && !isLoadingBigPayload && selectedCapture?.main_args} +
+ {showRawPayload ? '' : 'Need more data about the trigger event?'} +
- {:else} -
No captures to show yet.
{/if}
diff --git a/frontend/src/lib/components/triggers/CaptureTable.svelte b/frontend/src/lib/components/triggers/CaptureTable.svelte index 20f3703921..f6bc1d209f 100644 --- a/frontend/src/lib/components/triggers/CaptureTable.svelte +++ b/frontend/src/lib/components/triggers/CaptureTable.svelte @@ -71,7 +71,7 @@ kind: 'main' | 'preprocessor' args: Record | undefined } - addPreprocessor: null + addPreprocessor: { args: Record } | undefined updateSchema: { payloadData: any redirect: boolean diff --git a/frontend/src/lib/script_helpers.ts b/frontend/src/lib/script_helpers.ts index 532c11cab1..58bec208de 100644 --- a/frontend/src/lib/script_helpers.ts +++ b/frontend/src/lib/script_helpers.ts @@ -691,16 +691,21 @@ export const TS_PREPROCESSOR_FLOW_INTRO = `/** * Learn more: https://www.windmill.dev/docs/core_concepts/preprocessors */\n` -export const TS_PREPROCESSOR_MODULE_CODE = `export async function preprocessor( - event: - | { +export const TS_PREPROCESSOR_MODULE_CODE = `export async function preprocessor(event: TriggerEvent) { + return { + // return the args to be passed to the runnable + }; +} + +type TriggerEvent = + | { kind: "webhook"; body: any; raw_string: string | null; query: Record; headers: Record; } - | { + | { kind: "http"; body: any; raw_string: string | null; @@ -711,21 +716,21 @@ export const TS_PREPROCESSOR_MODULE_CODE = `export async function preprocessor( query: Record; headers: Record; } - | { + | { kind: "email"; parsed_email: any; raw_email: string; email_extra_args?: Record; } - | { kind: "websocket"; msg: string; url: string } - | { + | { kind: "websocket"; msg: string; url: string } + | { kind: "kafka"; payload: string; brokers: string[]; topic: string; group_id: string; } - | { + | { kind: "nats"; payload: string; servers: string[]; @@ -735,7 +740,7 @@ export const TS_PREPROCESSOR_MODULE_CODE = `export async function preprocessor( description?: string; length: number; } - | { + | { kind: "sqs"; msg: string; queue_url: string; @@ -747,7 +752,7 @@ export const TS_PREPROCESSOR_MODULE_CODE = `export async function preprocessor( { string_value?: string; data_type: string } >; } - | { + | { kind: "mqtt"; payload: string; topic: string; @@ -764,7 +769,7 @@ export const TS_PREPROCESSOR_MODULE_CODE = `export async function preprocessor( content_type?: string; }; } - | { + | { kind: "gcp"; payload: string; message_id: string; @@ -776,19 +781,14 @@ export const TS_PREPROCESSOR_MODULE_CODE = `export async function preprocessor( publish_time?: string; ack_id?: string; } - | { + | { kind: "postgres"; - transaction_type: "insert" | "update" | "delete", - schema_name: string, - table_name: string, - old_row?: Record, - row: Record - } -) { - return { - // return the args to be passed to the runnable - }; -} + transaction_type: "insert" | "update" | "delete"; + schema_name: string; + table_name: string; + old_row?: Record; + row: Record; + }; ` const PYTHON_INIT_CODE_APPROVAL = `import wmill From dccee1dba08b4c4b170336b3f3e959fbe7bca8ce Mon Sep 17 00:00:00 2001 From: hugocasa Date: Mon, 24 Nov 2025 22:41:05 +0100 Subject: [PATCH 18/49] fix(frontend): forking UI issues (#7215) * fix(frontend): forking UI issues * fix dark mode --- frontend/src/lib/components/Path.svelte | 6 +- .../src/lib/components/ScriptBuilder.svelte | 4 +- .../src/lib/components/ScriptEditor.svelte | 21 ++- .../components/ScriptEditorSkeleton.svelte | 55 ++++++++ .../src/lib/components/TagsToListenTo.svelte | 5 +- .../components/common/table/ScriptRow.svelte | 2 +- .../flows/edit/[...path]/+page.svelte | 7 +- .../(root)/(logged)/run/[...run]/+page.svelte | 9 +- .../(root)/(logged)/scripts/add/+page.svelte | 130 +++++++++++------- 9 files changed, 177 insertions(+), 62 deletions(-) create mode 100644 frontend/src/lib/components/ScriptEditorSkeleton.svelte diff --git a/frontend/src/lib/components/Path.svelte b/frontend/src/lib/components/Path.svelte index 85f9df1ec5..dafd4320af 100644 --- a/frontend/src/lib/components/Path.svelte +++ b/frontend/src/lib/components/Path.svelte @@ -138,7 +138,7 @@ } export async function reset() { - if (path == '' || path == 'u//') { + if (path == '' || path == 'u//' || path?.startsWith('tmp/')) { if ($lastMetaUsed == undefined || $lastMetaUsed.owner != $userStore?.username) { meta = { ownerKind: hideUser ? 'folder' : 'user', @@ -323,12 +323,12 @@ async function initPath() { await tick() - if (path != undefined && path != '') { + if (path != undefined && path != '' && !path?.startsWith('tmp/')) { meta = pathToMeta(path, hideUser) onMetaChange() return } - if (initialPath == undefined || initialPath == '') { + if (initialPath == undefined || initialPath == '' || initialPath?.startsWith('tmp/')) { reset() } else { meta = pathToMeta(initialPath, hideUser) diff --git a/frontend/src/lib/components/ScriptBuilder.svelte b/frontend/src/lib/components/ScriptBuilder.svelte index fa817b4058..4b386aa363 100644 --- a/frontend/src/lib/components/ScriptBuilder.svelte +++ b/frontend/src/lib/components/ScriptBuilder.svelte @@ -379,7 +379,7 @@ if (templateScript) { script.content += '\r\n' + templateScript } - scriptEditor?.inferSchema(script.content, language, true) + scriptEditor?.inferSchema(script.content, { nlang: language, resetArgs: true }) if (script.content != editor?.getCode()) { setCode(script.content) } @@ -567,7 +567,7 @@ if (!disableHistoryChange) { history.replaceState(history.state, '', `/scripts/edit/${script.path}`) } - if (stay || script.kind !== 'script' || script.no_main_func) { + if (stay || (script.no_main_func && script.kind !== 'preprocessor')) { script.parent_hash = newHash sendUserToast('Deployed') } else { diff --git a/frontend/src/lib/components/ScriptEditor.svelte b/frontend/src/lib/components/ScriptEditor.svelte index a9fcdc5cba..cd9356e34b 100644 --- a/frontend/src/lib/components/ScriptEditor.svelte +++ b/frontend/src/lib/components/ScriptEditor.svelte @@ -123,6 +123,8 @@ enablePreprocessorSnippet = false }: Props = $props() + let initialArgs = structuredClone($state.snapshot(args)) + $effect.pre(() => { if (schema == undefined) { schema = emptySchema() @@ -269,7 +271,18 @@ }) } - export async function inferSchema(code: string, nlang?: SupportedLanguage, resetArgs = false) { + export async function inferSchema( + code: string, + { + nlang, + resetArgs = false, + applyInitialArgs = false + }: { + nlang?: SupportedLanguage + resetArgs?: boolean + applyInitialArgs?: boolean + } = {} + ) { let nschema = schema ?? emptySchema() try { @@ -297,6 +310,10 @@ if (resetArgs) { args = {} } + if (applyInitialArgs) { + // we reapply initial args as the schema form might have cleared them between mount and the schema inference + args = initialArgs + } schema = nschema } catch (e) { validCode = false @@ -338,7 +355,7 @@ } onMount(() => { - inferSchema(code) + inferSchema(code, { applyInitialArgs: true }) loadPastTests() aiChatManager.saveAndClear() aiChatManager.changeMode(AIMode.SCRIPT) diff --git a/frontend/src/lib/components/ScriptEditorSkeleton.svelte b/frontend/src/lib/components/ScriptEditorSkeleton.svelte new file mode 100644 index 0000000000..031b8c9ee9 --- /dev/null +++ b/frontend/src/lib/components/ScriptEditorSkeleton.svelte @@ -0,0 +1,55 @@ +
+ +
+
+
+
+ + +
+
+
+
+
+
+
+ + +
+ +
+
+
+
+
+
+
+
+ + +
+ +
+
+
+
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/lib/components/TagsToListenTo.svelte b/frontend/src/lib/components/TagsToListenTo.svelte index 76c7fe15d6..e99c67223b 100644 --- a/frontend/src/lib/components/TagsToListenTo.svelte +++ b/frontend/src/lib/components/TagsToListenTo.svelte @@ -29,6 +29,9 @@ {disabled} class={disabled ? 'border-0' : ''} allowClear={!disabled} - onCreateItem={(c) => worker_tags.push(c)} + onCreateItem={(c) => { + worker_tags.push(c) + dispatch('dirty') + }} createText="Press Enter to use this tag" /> diff --git a/frontend/src/lib/components/common/table/ScriptRow.svelte b/frontend/src/lib/components/common/table/ScriptRow.svelte index 840bac9526..1fa78d86cd 100644 --- a/frontend/src/lib/components/common/table/ScriptRow.svelte +++ b/frontend/src/lib/components/common/table/ScriptRow.svelte @@ -112,7 +112,7 @@ - import { type NewScript, ScriptService, type Script } from '$lib/gen' + import { type NewScript, ScriptService, type ScriptLang } from '$lib/gen' import { page } from '$app/stores' import { defaultScripts, initialArgsStore, workspaceStore } from '$lib/stores' import ScriptBuilder from '$lib/components/ScriptBuilder.svelte' import type { Schema } from '$lib/common' - import { decodeState, emptySchema, emptyString } from '$lib/utils' + import { decodeState, emptySchema, emptyString, sendUserToast } from '$lib/utils' import { goto } from '$lib/navigation' import { replaceState } from '$app/navigation' import UnsavedConfirmationModal from '$lib/components/common/confirmationModal/UnsavedConfirmationModal.svelte' @@ -13,6 +13,13 @@ import type { Trigger } from '$lib/components/triggers/utils' import { get } from 'svelte/store' import { untrack } from 'svelte' + import ScriptEditorSkeleton from '$lib/components/ScriptEditorSkeleton.svelte' + + type Script = NewScript & { + draft_triggers?: Trigger[] + hash?: string + extra_perms?: Record + } // Default let schema: Schema = emptySchema() @@ -20,8 +27,9 @@ const templatePath = $page.url.searchParams.get('template') const hubPath = $page.url.searchParams.get('hub') const showMeta = /true|1/i.test($page.url.searchParams.get('show_meta') ?? '0') + const urlArgs = $page.url.searchParams.get('initial_args') - let initialArgs = get(initialArgsStore) ?? {} + let initialArgs = urlArgs ? decodeState(urlArgs) : (get(initialArgsStore) ?? {}) if (get(initialArgsStore)) $initialArgsStore = undefined const path = $page.url.searchParams.get('path') @@ -40,54 +48,74 @@ } } - function defaultScript() { + function defaultScript(): Script { return { hash: '', path: path ?? '', summary: '', content: '', + description: '', schema: schema, is_template: false, extra_perms: {}, - language: - $defaultScripts?.order?.filter( - (x) => $defaultScripts?.hidden == undefined || !$defaultScripts.hidden.includes(x) - )?.[0] ?? 'bun', + language: ($defaultScripts?.order?.filter( + (x) => $defaultScripts?.hidden == undefined || !$defaultScripts.hidden.includes(x) + )?.[0] ?? 'bun') as ScriptLang, kind: 'script' } } - let script: NewScript & { draft_triggers: Trigger[] } = $state( - !path && initialState != undefined ? decodeStateAndHandleError(initialState) : defaultScript() + let script: Script | undefined = $state( + templatePath || hubPath + ? undefined + : !path && initialState != undefined + ? decodeStateAndHandleError(initialState) + : defaultScript() ) async function loadTemplate(): Promise { if (templatePath) { - const template = await ScriptService.getScriptByPath({ - workspace: $workspaceStore!, - path: templatePath - }) - - // Only copy the summary if it's not empty - script.summary = !emptyString(template.summary) ? `Copy of ${template.summary}` : '' - script.description = template.description - script.content = template.content - script.schema = template.schema - script.language = template.language - scriptBuilder?.setCode(script.content) + try { + const template = await ScriptService.getScriptByPath({ + workspace: $workspaceStore!, + path: templatePath + }) + script = { + ...defaultScript(), + summary: !emptyString(template.summary) ? `Copy of ${template.summary}` : '', + description: template.description, + content: template.content, + schema: template.schema, + language: template.language, + path: template.path + '_fork' + } + } catch (err) { + script = defaultScript() + console.error('Error loading template', err) + sendUserToast('Error loading template: ' + err.message, true) + } } } async function loadHub(): Promise { if (hubPath) { - const { content, language, summary } = await ScriptService.getHubScriptByPath({ - path: hubPath - }) - script.description = `Fork of ${hubPath}` - script.content = replaceScriptPlaceholderWithItsValues(hubPath, content) - script.summary = summary ?? '' - script.language = language as Script['language'] - scriptBuilder?.setCode(script.content) + try { + const { content, language, summary } = await ScriptService.getHubScriptByPath({ + path: hubPath + }) + script = { + ...defaultScript(), + description: `Fork of ${hubPath}`, + content: replaceScriptPlaceholderWithItsValues(hubPath, content), + summary: summary ?? '', + language: language as Script['language'], + path: hubPath + '_fork' + } + } catch (err) { + script = defaultScript() + console.error('Error loading script from hub', err) + sendUserToast('Error loading script from hub: ' + err.message, true) + } } } @@ -100,22 +128,26 @@ }) - { - goto(`/scripts/get/${e.hash}?workspace=${$workspaceStore}`) - }} - onSaveInitial={(e) => { - goto(`/scripts/edit/${e.path}`) - }} - searchParams={$page.url.searchParams} - bind:script - {showMeta} - replaceStateFn={(path) => replaceState(path, $page.state)} -> - - +{#if script} + { + goto(`/scripts/get/${e.hash}?workspace=${$workspaceStore}`) + }} + onSaveInitial={(e) => { + goto(`/scripts/edit/${e.path}`) + }} + searchParams={$page.url.searchParams} + bind:script + {showMeta} + replaceStateFn={(path) => replaceState(path, $page.state)} + > + + +{:else} + +{/if} From e87f81470904578467f42598cfce5fad2249f131 Mon Sep 17 00:00:00 2001 From: hugocasa Date: Tue, 25 Nov 2025 12:02:36 +0100 Subject: [PATCH 19/49] fix(frontend): loading saved/history inputs when json view is on (#7217) --- .../routes/(root)/(logged)/scripts/get/[...hash]/+page.svelte | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frontend/src/routes/(root)/(logged)/scripts/get/[...hash]/+page.svelte b/frontend/src/routes/(root)/(logged)/scripts/get/[...hash]/+page.svelte index 76f22642bd..5f88a212eb 100644 --- a/frontend/src/routes/(root)/(logged)/scripts/get/[...hash]/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/scripts/get/[...hash]/+page.svelte @@ -783,6 +783,9 @@ on:selected_args={(e) => { const nargs = JSON.parse(JSON.stringify(e.detail)) args = nargs + if (jsonView) { + runForm?.setCode(JSON.stringify(args ?? {}, null, '\t')) + } }} /> {/if} From 897faf085b49ada0648594797cfe627decb50952 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Tue, 25 Nov 2025 11:11:14 +0000 Subject: [PATCH 20/49] fix: list scripts without 1000 limits --- backend/windmill-api/src/scripts.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/windmill-api/src/scripts.rs b/backend/windmill-api/src/scripts.rs index a772100a50..d5d3e654a9 100644 --- a/backend/windmill-api/src/scripts.rs +++ b/backend/windmill-api/src/scripts.rs @@ -41,11 +41,11 @@ use windmill_audit::ActionKind; use windmill_worker::{process_relative_imports, scoped_dependency_map::ScopedDependencyMap}; use windmill_common::{ - assets::{clear_asset_usage, insert_asset_usage, AssetUsageKind, AssetWithAltAccessType}, + assets::{AssetUsageKind, AssetWithAltAccessType, clear_asset_usage, insert_asset_usage}, error::to_anyhow, s3_helpers::upload_artifact_to_store, scripts::hash_script, - utils::WarnAfterExt, + utils::{WarnAfterExt, paginate_without_limits}, worker::{CLOUD_HOSTED, MIN_VERSION_SUPPORTS_DEBOUNCING}, }; @@ -61,7 +61,7 @@ use windmill_common::{ }, users::username_to_permissioned_as, utils::{ - not_found_if_none, paginate, query_elems_from_hub, require_admin, Pagination, StripPath, + not_found_if_none, query_elems_from_hub, require_admin, Pagination, StripPath, }, worker::to_raw_value, HUB_BASE_URL, @@ -193,7 +193,7 @@ async fn list_search_scripts( ) -> JsonResult> { let mut tx = user_db.begin(&authed).await?; #[cfg(feature = "enterprise")] - let n = 1000; + let n = 10000; #[cfg(not(feature = "enterprise"))] let n = 10; @@ -219,7 +219,7 @@ async fn list_scripts( Query(pagination): Query, Query(lq): Query, ) -> JsonResult> { - let (per_page, offset) = paginate(pagination); + let (per_page, offset) = paginate_without_limits(pagination); let mut sqlb = SqlBuilder::select_from("script as o") .fields(&[ "hash", From de7739a3ee7ef76a8eb4fe6570ef0bdfbdb92293 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Tue, 25 Nov 2025 12:12:49 +0000 Subject: [PATCH 21/49] fix(cli): handle better public apps --- cli/src/commands/app/apps.ts | 11 ++++++++--- cli/src/commands/sync/sync.ts | 3 +++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/cli/src/commands/app/apps.ts b/cli/src/commands/app/apps.ts index 1c6baceda2..0fefd7afbc 100644 --- a/cli/src/commands/app/apps.ts +++ b/cli/src/commands/app/apps.ts @@ -18,6 +18,7 @@ import { readInlinePathSync } from "../../utils/utils.ts"; export interface AppFile { value: any; + public?: boolean; summary: string; policy: Policy; } @@ -45,6 +46,10 @@ export async function pushApp( } catch { //ignore } + if (app?.["policy"]?.["execution_mode"] == "anonymous") { + app.public = true; + } + // console.log(app); if (app) { app.policy = undefined; } @@ -80,7 +85,7 @@ export async function pushApp( } replaceInlineScripts(localApp.value); - await generatingPolicy(localApp, remotePath); + await generatingPolicy(localApp, remotePath, localApp?.["public"] ?? false); if (app) { if (isSuperset(localApp, app)) { log.info(colors.green(`App ${remotePath} is up to date`)); @@ -109,11 +114,11 @@ export async function pushApp( } } -async function generatingPolicy(app: any, path: string) { +async function generatingPolicy(app: any, path: string, publicApp: boolean) { log.info(colors.gray(`Generating fresh policy for app ${path}...`)); try { app.policy = await windmillUtils.updatePolicy(app.value, undefined); - app.policy.execution_mode = "publisher"; + app.policy.execution_mode = publicApp ? "anonymous" : "publisher"; } catch (e) { log.error(colors.red(`Error generating policy for app ${path}: ${e}`)); throw e; diff --git a/cli/src/commands/sync/sync.ts b/cli/src/commands/sync/sync.ts index b0e2b74682..d145b03490 100644 --- a/cli/src/commands/sync/sync.ts +++ b/cli/src/commands/sync/sync.ts @@ -415,6 +415,9 @@ function ZipFSElement( }; } + if (app?.["policy"]?.["execution_mode"] == "anonymous") { + app.public = true; + } app.policy = undefined; yield { isDirectory: false, From c37dde096c301a502c6ff2c570b91153a5717fcd Mon Sep 17 00:00:00 2001 From: Guilhem Date: Tue, 25 Nov 2025 12:14:19 +0000 Subject: [PATCH 22/49] fix(frontend): show trigger table when no trigger selected (#7219) * improve triggers pannel * nit * nit --- .../DraftTriggersConfirmationModal.svelte | 19 +- .../details/DetailPageHeader.svelte | 2 +- .../lib/components/graph/FlowGraphV2.svelte | 2 +- .../renderers/triggers/TriggersBadge.svelte | 8 +- .../triggers/DeleteTriggerButton.svelte | 6 +- .../triggers/TriggerEditorToolbar.svelte | 4 +- .../components/triggers/TriggerLabel.svelte | 19 +- .../components/triggers/TriggersEditor.svelte | 219 ++++++++++-------- .../components/triggers/TriggersTable.svelte | 12 +- .../triggers/TriggersWrapper.svelte | 6 +- .../(logged)/flows/get/[...path]/+page.svelte | 20 +- .../scripts/get/[...hash]/+page.svelte | 10 +- 12 files changed, 166 insertions(+), 161 deletions(-) diff --git a/frontend/src/lib/components/common/confirmationModal/DraftTriggersConfirmationModal.svelte b/frontend/src/lib/components/common/confirmationModal/DraftTriggersConfirmationModal.svelte index cf43ab8672..e8fec0b907 100644 --- a/frontend/src/lib/components/common/confirmationModal/DraftTriggersConfirmationModal.svelte +++ b/frontend/src/lib/components/common/confirmationModal/DraftTriggersConfirmationModal.svelte @@ -10,6 +10,7 @@ import ToggleButtonGroup from '../toggleButton-v2/ToggleButtonGroup.svelte' import ToggleButton from '../toggleButton-v2/ToggleButton.svelte' import { userStore } from '$lib/stores' + import Badge from '../badge/Badge.svelte' interface Props { open?: boolean @@ -132,32 +133,22 @@ value={'discard'} {item} small - class="data-[state=on]:text-white data-[state=on]:bg-red-400 w-[54px] justify-center" + class="data-[state=on]:text-white data-[state=on]:bg-red-400 justify-center" /> {/snippet}
{:else if permission === 'admin-only'} -
- Admin only -
+ Admin only {:else if permission === 'invalid-config'} -
- Invalid config -
+ Invalid config {/if} diff --git a/frontend/src/lib/components/details/DetailPageHeader.svelte b/frontend/src/lib/components/details/DetailPageHeader.svelte index c8185e2746..818c3ea76d 100644 --- a/frontend/src/lib/components/details/DetailPageHeader.svelte +++ b/frontend/src/lib/components/details/DetailPageHeader.svelte @@ -58,7 +58,7 @@
diff --git a/frontend/src/lib/components/graph/FlowGraphV2.svelte b/frontend/src/lib/components/graph/FlowGraphV2.svelte index 9be2534b9f..7664bae49a 100644 --- a/frontend/src/lib/components/graph/FlowGraphV2.svelte +++ b/frontend/src/lib/components/graph/FlowGraphV2.svelte @@ -397,7 +397,7 @@ }, select: (modId) => { // AI tools are not selectable by the flow. Selection has to be refactored to be simplier. - if (nodes.find((n) => n.data?.moduleId === modId)?.type === 'aiTool') { + if (nodes.find((n) => n.data?.moduleId === modId)?.type === 'aiTool' || modId === 'Trigger') { selectionManager.selectId(modId) } if (!notSelectable) { diff --git a/frontend/src/lib/components/graph/renderers/triggers/TriggersBadge.svelte b/frontend/src/lib/components/graph/renderers/triggers/TriggersBadge.svelte index 048f39d9c4..d064cedf77 100644 --- a/frontend/src/lib/components/graph/renderers/triggers/TriggersBadge.svelte +++ b/frontend/src/lib/components/graph/renderers/triggers/TriggersBadge.svelte @@ -246,10 +246,10 @@ aiId={`trigger-button-${type}`} aiDescription={`Trigger button for ${type}`} class={twMerge( - 'hover:bg-surface-hover rounded-md shadow-sm text-xs relative center-center cursor-pointer bg-slate-100 dark:bg-slate-700', - 'dark:outline dark:outline-1 outline-tertiary/20 group', - isSelected ? 'outline-tertiary outline' : '', - small ? 'w-[23px] h-[23px] outline-[1.5px]' : 'p-2 outline-[2px]' + 'hover:bg-surface-hover rounded-md text-xs relative center-center cursor-pointer border border-border-light', + 'group', + isSelected ? 'bg-surface-accent-selected text-accent border-border-selected' : '', + small ? 'w-[23px] h-[23px]' : 'p-2' )} on:click={(e) => { e.stopPropagation() diff --git a/frontend/src/lib/components/triggers/DeleteTriggerButton.svelte b/frontend/src/lib/components/triggers/DeleteTriggerButton.svelte index 1b4d6f8954..b7fed3d6d0 100644 --- a/frontend/src/lib/components/triggers/DeleteTriggerButton.svelte +++ b/frontend/src/lib/components/triggers/DeleteTriggerButton.svelte @@ -39,12 +39,12 @@
- {/if} - + {#if triggersState.selectedTrigger}
+ + {#if !useVerticalTriggerBar} +
+ +
+ {:else} +
+ +
+ {/if} + +
+ {#if loading} +
+ {:else} + {#key [renderCount, triggersState.selectedTriggerIndex].join('-')} +
+ { + deleteTrigger(triggersState.selectedTriggerIndex) + }} + onUpdate={(path) => { + handleUpdate(triggersState.selectedTriggerIndex, path) + }} + onConfigChange={(cfg, canSave, updated) => { + if (updated) { + handleUpdateDraftConfig(triggersState.selectedTriggerIndex, cfg, canSave) + } + }} + onCaptureConfigChange={(cfg, isValidConfig) => { + config = cfg + isValid = isValidConfig + }} + onReset={() => { + handleResetDraft(triggersState.selectedTriggerIndex) + }} + onEmailDomain={(domain) => { + emailDomain = domain + }} + /> +
+ {/key} + {/if} +
+
+ {:else} + +
{#if loading}
- {:else if triggersState.selectedTrigger} - {#key [renderCount, triggersState.selectedTriggerIndex].join('-')} -
- { - deleteTrigger(triggersState.selectedTriggerIndex) - }} - onUpdate={(path) => { - handleUpdate(triggersState.selectedTriggerIndex, path) - }} - onConfigChange={(cfg, canSave, updated) => { - if (updated) { - handleUpdateDraftConfig(triggersState.selectedTriggerIndex, cfg, canSave) - } - }} - onCaptureConfigChange={(cfg, isValidConfig) => { - config = cfg - isValid = isValidConfig - }} - onReset={() => { - handleResetDraft(triggersState.selectedTriggerIndex) - }} - onEmailDomain={(domain) => { - emailDomain = domain - }} - /> -
- {/key} {:else} - {`Select a trigger from the ${useVerticalTriggerBar ? 'left toolbar' : 'table'} or create a new one`} +
+ +
{/if}
-
+ {/if} {#if !cloudDisabled && triggersState.selectedTrigger && triggersState.selectedTrigger.type !== 'schedule' && triggersState.selectedTrigger.type != 'poll' && !noCapture} {@const captureKind = triggersState.selectedTrigger diff --git a/frontend/src/lib/components/triggers/TriggersTable.svelte b/frontend/src/lib/components/triggers/TriggersTable.svelte index 33588cb9d8..827395bcb8 100644 --- a/frontend/src/lib/components/triggers/TriggersTable.svelte +++ b/frontend/src/lib/components/triggers/TriggersTable.svelte @@ -59,8 +59,11 @@ {@const SvelteComponent = triggerIconMap[trigger.type]} onSelect?.(index)} use:triggerableByAI={{ @@ -71,10 +74,7 @@ >
- + {#if trigger.isPrimary} diff --git a/frontend/src/lib/components/triggers/TriggersWrapper.svelte b/frontend/src/lib/components/triggers/TriggersWrapper.svelte index 5030069f10..973931ceb2 100644 --- a/frontend/src/lib/components/triggers/TriggersWrapper.svelte +++ b/frontend/src/lib/components/triggers/TriggersWrapper.svelte @@ -16,7 +16,6 @@ import ClipboardPanel from '../details/ClipboardPanel.svelte' import CliHelpBox from '../CliHelpBox.svelte' import TriggerLabel from './TriggerLabel.svelte' - import { twMerge } from 'tailwind-merge' interface Props { selectedTrigger: Trigger @@ -178,10 +177,7 @@ {#snippet customLabel()} {@const IconComponent = triggerIconMap[selectedTrigger.type]}
- +
{/snippet} diff --git a/frontend/src/routes/(root)/(logged)/flows/get/[...path]/+page.svelte b/frontend/src/routes/(root)/(logged)/flows/get/[...path]/+page.svelte index 43eeab456b..55c0b4f775 100644 --- a/frontend/src/routes/(root)/(logged)/flows/get/[...path]/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/flows/get/[...path]/+page.svelte @@ -33,10 +33,10 @@ GitFork, Play, History, - Columns, Pen, Eye, - HistoryIcon + HistoryIcon, + LayoutDashboard } from 'lucide-svelte' import DetailPageHeader from '$lib/components/details/DetailPageHeader.svelte' @@ -238,7 +238,7 @@ buttonProps: { href: `${base}/flows/add?template=${flow.path}`, variant: 'subtle', - size: 'xs', + unifiedSize: 'md', startIcon: GitFork } }) @@ -252,7 +252,7 @@ label: `Runs`, buttonProps: { href: `${base}/runs/${flow.path}`, - size: 'xs', + unifiedSize: 'md', variant: 'subtle', startIcon: Play } @@ -262,7 +262,7 @@ label: `History`, buttonProps: { onClick: () => flowHistory?.open(), - size: 'xs', + unifiedSize: 'md', variant: 'subtle', startIcon: History } @@ -281,9 +281,9 @@ $importStore = JSON.parse(JSON.stringify(app)) await goto('/apps/add?nodraft=true') }, - size: 'xs', - variant: 'accent', - startIcon: Columns + unifiedSize: 'md', + variant: 'subtle', + startIcon: LayoutDashboard } }) @@ -291,8 +291,8 @@ label: 'Edit', buttonProps: { href: `${base}/flows/edit/${path}?nodraft=true`, - variant: 'accent-secondary', - size: 'xs', + variant: 'accent', + unifiedSize: 'md', disabled: !can_write, startIcon: Pen } diff --git a/frontend/src/routes/(root)/(logged)/scripts/get/[...hash]/+page.svelte b/frontend/src/routes/(root)/(logged)/scripts/get/[...hash]/+page.svelte index 5f88a212eb..0dbe481e9f 100644 --- a/frontend/src/routes/(root)/(logged)/scripts/get/[...hash]/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/scripts/get/[...hash]/+page.svelte @@ -58,10 +58,10 @@ Pen, ChevronUpSquare, Share, - Table2, Trash, Play, - ClipboardCopy + ClipboardCopy, + LayoutDashboard } from 'lucide-svelte' import { SCRIPT_VIEW_SHOW_PUBLISH_TO_HUB } from '$lib/consts' import { scriptToHubUrl } from '$lib/hub' @@ -339,8 +339,8 @@ }, unifiedSize: 'md', - variant: 'accent', - startIcon: Table2 + variant: 'subtle', + startIcon: LayoutDashboard } }) @@ -367,7 +367,7 @@ }`, unifiedSize: 'md', startIcon: Pen, - variant: 'accent-secondary', + variant: 'accent', disabled: !can_write } }) From 2388e38a9c27bccc2ce6888b5c4985ee5862590e Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Tue, 25 Nov 2025 13:17:34 +0100 Subject: [PATCH 23/49] chore(main): release 1.584.0 (#7213) * chore(main): release 1.584.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> --- CHANGELOG.md | 17 +++++ backend/Cargo.lock | 66 +++++++++---------- backend/Cargo.toml | 4 +- backend/windmill-api/openapi.yaml | 2 +- benchmarks/lib.ts | 2 +- cli/src/main.ts | 2 +- frontend/package-lock.json | 4 +- frontend/package.json | 2 +- lsp/Pipfile | 4 +- openflow.openapi.yaml | 2 +- .../WindmillClient/WindmillClient.psd1 | 2 +- python-client/wmill/pyproject.toml | 2 +- python-client/wmill_pg/pyproject.toml | 2 +- typescript-client/jsr.json | 2 +- typescript-client/package.json | 2 +- version.txt | 2 +- 16 files changed, 67 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb26b908fe..d65463af95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## [1.584.0](https://github.com/windmill-labs/windmill/compare/v1.583.3...v1.584.0) (2025-11-25) + + +### Features + +* **app:** add chat component ([#7199](https://github.com/windmill-labs/windmill/issues/7199)) ([c47669b](https://github.com/windmill-labs/windmill/commit/c47669be0bffb0821afe9d4182cc071cb3b675f0)) + + +### Bug Fixes + +* **cli:** handle better public apps ([de7739a](https://github.com/windmill-labs/windmill/commit/de7739a3ee7ef76a8eb4fe6570ef0bdfbdb92293)) +* **frontend:** forking UI issues ([#7215](https://github.com/windmill-labs/windmill/issues/7215)) ([dccee1d](https://github.com/windmill-labs/windmill/commit/dccee1dba08b4c4b170336b3f3e959fbe7bca8ce)) +* **frontend:** improve preprocessor discoverability ([#7214](https://github.com/windmill-labs/windmill/issues/7214)) ([a2d3297](https://github.com/windmill-labs/windmill/commit/a2d3297343338ba0b559481723e64db5f4acf3a7)) +* **frontend:** loading saved/history inputs when json view is on ([#7217](https://github.com/windmill-labs/windmill/issues/7217)) ([e87f814](https://github.com/windmill-labs/windmill/commit/e87f81470904578467f42598cfce5fad2249f131)) +* **frontend:** show trigger table when no trigger selected ([#7219](https://github.com/windmill-labs/windmill/issues/7219)) ([c37dde0](https://github.com/windmill-labs/windmill/commit/c37dde096c301a502c6ff2c570b91153a5717fcd)) +* list scripts without 1000 limits ([897faf0](https://github.com/windmill-labs/windmill/commit/897faf085b49ada0648594797cfe627decb50952)) + ## [1.583.3](https://github.com/windmill-labs/windmill/compare/v1.583.2...v1.583.3) (2025-11-24) diff --git a/backend/Cargo.lock b/backend/Cargo.lock index eaabb783ab..cce1bb1039 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -14082,9 +14082,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "9cf146f99d442e8e68e585f5d798ccd3cad9a7835b917e09728880a862706456" dependencies = [ "async-compression", "base64 0.22.1", @@ -15210,7 +15210,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windmill" -version = "1.583.3" +version = "1.584.0" dependencies = [ "anyhow", "aws-sdk-config", @@ -15272,7 +15272,7 @@ dependencies = [ [[package]] name = "windmill-api" -version = "1.583.3" +version = "1.584.0" dependencies = [ "anyhow", "argon2", @@ -15393,7 +15393,7 @@ dependencies = [ [[package]] name = "windmill-api-client" -version = "1.583.3" +version = "1.584.0" dependencies = [ "base64 0.22.1", "chrono", @@ -15408,7 +15408,7 @@ dependencies = [ [[package]] name = "windmill-audit" -version = "1.583.3" +version = "1.584.0" dependencies = [ "chrono", "lazy_static", @@ -15422,7 +15422,7 @@ dependencies = [ [[package]] name = "windmill-autoscaling" -version = "1.583.3" +version = "1.584.0" dependencies = [ "anyhow", "axum", @@ -15441,7 +15441,7 @@ dependencies = [ [[package]] name = "windmill-common" -version = "1.583.3" +version = "1.584.0" dependencies = [ "anyhow", "async-recursion", @@ -15530,7 +15530,7 @@ dependencies = [ [[package]] name = "windmill-git-sync" -version = "1.583.3" +version = "1.584.0" dependencies = [ "regex", "serde", @@ -15545,7 +15545,7 @@ dependencies = [ [[package]] name = "windmill-indexer" -version = "1.583.3" +version = "1.584.0" dependencies = [ "anyhow", "bytes", @@ -15569,7 +15569,7 @@ dependencies = [ [[package]] name = "windmill-macros" -version = "1.583.3" +version = "1.584.0" dependencies = [ "itertools 0.14.0", "lazy_static", @@ -15581,7 +15581,7 @@ dependencies = [ [[package]] name = "windmill-parser" -version = "1.583.3" +version = "1.584.0" dependencies = [ "convert_case 0.6.0", "serde", @@ -15590,7 +15590,7 @@ dependencies = [ [[package]] name = "windmill-parser-bash" -version = "1.583.3" +version = "1.584.0" dependencies = [ "anyhow", "lazy_static", @@ -15602,7 +15602,7 @@ dependencies = [ [[package]] name = "windmill-parser-csharp" -version = "1.583.3" +version = "1.584.0" dependencies = [ "anyhow", "serde_json", @@ -15614,7 +15614,7 @@ dependencies = [ [[package]] name = "windmill-parser-go" -version = "1.583.3" +version = "1.584.0" dependencies = [ "anyhow", "gosyn", @@ -15626,7 +15626,7 @@ dependencies = [ [[package]] name = "windmill-parser-graphql" -version = "1.583.3" +version = "1.584.0" dependencies = [ "anyhow", "lazy_static", @@ -15638,7 +15638,7 @@ dependencies = [ [[package]] name = "windmill-parser-java" -version = "1.583.3" +version = "1.584.0" dependencies = [ "anyhow", "serde_json", @@ -15650,7 +15650,7 @@ dependencies = [ [[package]] name = "windmill-parser-nu" -version = "1.583.3" +version = "1.584.0" dependencies = [ "anyhow", "nu-parser", @@ -15661,7 +15661,7 @@ dependencies = [ [[package]] name = "windmill-parser-php" -version = "1.583.3" +version = "1.584.0" dependencies = [ "anyhow", "itertools 0.14.0", @@ -15672,7 +15672,7 @@ dependencies = [ [[package]] name = "windmill-parser-py" -version = "1.583.3" +version = "1.584.0" dependencies = [ "anyhow", "itertools 0.14.0", @@ -15684,7 +15684,7 @@ dependencies = [ [[package]] name = "windmill-parser-py-imports" -version = "1.583.3" +version = "1.584.0" dependencies = [ "anyhow", "async-recursion", @@ -15707,7 +15707,7 @@ dependencies = [ [[package]] name = "windmill-parser-ruby" -version = "1.583.3" +version = "1.584.0" dependencies = [ "anyhow", "lazy_static", @@ -15721,7 +15721,7 @@ dependencies = [ [[package]] name = "windmill-parser-rust" -version = "1.583.3" +version = "1.584.0" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -15738,7 +15738,7 @@ dependencies = [ [[package]] name = "windmill-parser-sql" -version = "1.583.3" +version = "1.584.0" dependencies = [ "anyhow", "lazy_static", @@ -15752,7 +15752,7 @@ dependencies = [ [[package]] name = "windmill-parser-ts" -version = "1.583.3" +version = "1.584.0" dependencies = [ "anyhow", "lazy_static", @@ -15770,7 +15770,7 @@ dependencies = [ [[package]] name = "windmill-parser-yaml" -version = "1.583.3" +version = "1.584.0" dependencies = [ "anyhow", "serde", @@ -15781,7 +15781,7 @@ dependencies = [ [[package]] name = "windmill-queue" -version = "1.583.3" +version = "1.584.0" dependencies = [ "anyhow", "async-recursion", @@ -15818,7 +15818,7 @@ dependencies = [ [[package]] name = "windmill-sql-datatype-parser-wasm" -version = "1.583.3" +version = "1.584.0" dependencies = [ "wasm-bindgen", "wasm-bindgen-test", @@ -15828,7 +15828,7 @@ dependencies = [ [[package]] name = "windmill-worker" -version = "1.583.3" +version = "1.584.0" dependencies = [ "anyhow", "async-once-cell", @@ -16625,18 +16625,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.28" +version = "0.8.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43fa6694ed34d6e57407afbccdeecfa268c470a7d2a5b0cf49ce9fcc345afb90" +checksum = "4ea879c944afe8a2b25fef16bb4ba234f47c694565e97383b36f3a878219065c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.28" +version = "0.8.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c640b22cd9817fae95be82f0d2f90b11f7605f6c319d16705c459b27ac2cbc26" +checksum = "cf955aa904d6040f70dc8e9384444cb1030aed272ba3cb09bbc4ab9e7c1f34f5" dependencies = [ "proc-macro2", "quote", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 30d36d0c09..d533d32549 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "windmill" -version = "1.583.3" +version = "1.584.0" authors.workspace = true edition.workspace = true @@ -33,7 +33,7 @@ members = [ exclude = ["./windmill-duckdb-ffi-internal"] [workspace.package] -version = "1.583.3" +version = "1.584.0" authors = ["Ruben Fiszel "] edition = "2021" diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 49d85ec411..139c6f325a 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.583.3 + version: 1.584.0 title: Windmill API contact: diff --git a/benchmarks/lib.ts b/benchmarks/lib.ts index 3a9f64ee6b..5dc61d56a7 100644 --- a/benchmarks/lib.ts +++ b/benchmarks/lib.ts @@ -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.583.3"; +export const VERSION = "v1.584.0"; export async function login(email: string, password: string): Promise { return await windmill.UserService.login({ diff --git a/cli/src/main.ts b/cli/src/main.ts index f45ca3607e..4430410890 100644 --- a/cli/src/main.ts +++ b/cli/src/main.ts @@ -68,7 +68,7 @@ export { // } // }); -export const VERSION = "1.583.3"; +export const VERSION = "1.584.0"; export const WM_FORK_PREFIX = "wm-fork"; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 8dc67ac1c6..14868d1907 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "windmill-components", - "version": "1.583.3", + "version": "1.584.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "windmill-components", - "version": "1.583.3", + "version": "1.584.0", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/frontend/package.json b/frontend/package.json index 202b2f17c3..58a5be9fdb 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "windmill-components", - "version": "1.583.3", + "version": "1.584.0", "scripts": { "dev": "vite dev", "build": "vite build", diff --git a/lsp/Pipfile b/lsp/Pipfile index ef9fbe6a18..a535d7580a 100644 --- a/lsp/Pipfile +++ b/lsp/Pipfile @@ -4,8 +4,8 @@ verify_ssl = true name = "pypi" [packages] -wmill = ">=1.583.3" -wmill_pg = ">=1.583.3" +wmill = ">=1.584.0" +wmill_pg = ">=1.584.0" sendgrid = "*" mysql-connector-python = "*" pymongo = "*" diff --git a/openflow.openapi.yaml b/openflow.openapi.yaml index 5145d2884f..b724c0acc9 100644 --- a/openflow.openapi.yaml +++ b/openflow.openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.583.3 + version: 1.584.0 title: OpenFlow Spec contact: name: Ruben Fiszel diff --git a/powershell-client/WindmillClient/WindmillClient.psd1 b/powershell-client/WindmillClient/WindmillClient.psd1 index 9d17deaa98..fa0d0e6afa 100644 --- a/powershell-client/WindmillClient/WindmillClient.psd1 +++ b/powershell-client/WindmillClient/WindmillClient.psd1 @@ -12,7 +12,7 @@ RootModule = 'WindmillClient.psm1' # Version number of this module. - ModuleVersion = '1.583.3' + ModuleVersion = '1.584.0' # Supported PSEditions # CompatiblePSEditions = @() diff --git a/python-client/wmill/pyproject.toml b/python-client/wmill/pyproject.toml index 10af1ba2c2..4a9cabadc7 100644 --- a/python-client/wmill/pyproject.toml +++ b/python-client/wmill/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill" -version = "1.583.3" +version = "1.584.0" description = "A client library for accessing Windmill server wrapping the Windmill client API" license = "Apache-2.0" homepage = "https://windmill.dev" diff --git a/python-client/wmill_pg/pyproject.toml b/python-client/wmill_pg/pyproject.toml index f7824560f2..7b9653640e 100644 --- a/python-client/wmill_pg/pyproject.toml +++ b/python-client/wmill_pg/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill-pg" -version = "1.583.3" +version = "1.584.0" description = "An extension client for the wmill client library focused on pg" license = "Apache-2.0" homepage = "https://windmill.dev" diff --git a/typescript-client/jsr.json b/typescript-client/jsr.json index 30478fca0b..f4bfe59424 100644 --- a/typescript-client/jsr.json +++ b/typescript-client/jsr.json @@ -1,6 +1,6 @@ { "name": "@windmill/windmill", - "version": "1.583.3", + "version": "1.584.0", "exports": "./src/index.ts", "publish": { "exclude": ["!src", "./s3Types.ts", "./client.ts"] diff --git a/typescript-client/package.json b/typescript-client/package.json index b734e93d31..bfe2a641b8 100644 --- a/typescript-client/package.json +++ b/typescript-client/package.json @@ -1,7 +1,7 @@ { "name": "windmill-client", "description": "Windmill SDK client for browsers and Node.js", - "version": "1.583.3", + "version": "1.584.0", "author": "Ruben Fiszel", "license": "Apache 2.0", "devDependencies": { diff --git a/version.txt b/version.txt index f511ac6fe7..a224cb38e3 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.583.3 +1.584.0 From 79a0d3edc9e7d3a18708bc92459530e313a00ba6 Mon Sep 17 00:00:00 2001 From: Guilhem Date: Tue, 25 Nov 2025 15:56:24 +0000 Subject: [PATCH 24/49] add tip to triggers pannel (#7220) * improve triggers pannel * nit * nit * improve placement and transition * Add tip * stop input selection when adding a preprocessor --- .../flows/map/InsertModuleButton.svelte | 2 +- .../triggers/AddTriggersButton.svelte | 2 +- .../components/triggers/TriggersEditor.svelte | 181 ++++++++++-------- .../components/triggers/TriggersTable.svelte | 12 +- 4 files changed, 104 insertions(+), 93 deletions(-) diff --git a/frontend/src/lib/components/flows/map/InsertModuleButton.svelte b/frontend/src/lib/components/flows/map/InsertModuleButton.svelte index 43068966bf..9645ec7422 100644 --- a/frontend/src/lib/components/flows/map/InsertModuleButton.svelte +++ b/frontend/src/lib/components/flows/map/InsertModuleButton.svelte @@ -11,7 +11,7 @@ let { Icon = Plus, title, id }: Props = $props() -
- {/if} - +
+ + {#if !useVerticalTriggerBar || !triggersState.selectedTrigger}
+ + {#if leftPaneWidth <= 800 && !triggersState.selectedTrigger} +
+ {@render TriggerInfo()} +
+ {/if} +
+ {:else} +
+ +
+ {/if} + + {#if triggersState.selectedTrigger} +
{#if loading} @@ -401,35 +405,10 @@ {/key} {/if}
-
- {:else} - -
- {#if loading} -
- {:else} -
- -
- {/if} -
- {/if} + {:else if leftPaneWidth > 800} + {@render TriggerInfo(true)} + {/if} +
{#if !cloudDisabled && triggersState.selectedTrigger && triggersState.selectedTrigger.type !== 'schedule' && triggersState.selectedTrigger.type != 'poll' && !noCapture} {@const captureKind = triggersState.selectedTrigger @@ -460,3 +439,35 @@
+ +{#snippet TriggerInfo(margin: boolean = false)} +
+
+ +
+
+ Triggers automatically run your flow when events + happen. Select one to configure it or create a new one. + Learn more +
+
    +
  • + + Default triggers: Webhooks, email, CLI - always exist, can't be disabled +
  • +
  • + + Additional triggers: Setup advanced triggers, like HTTP routes, database changes, + message queues, cloud events, and more +
  • +
+
+
+
+{/snippet} diff --git a/frontend/src/lib/components/triggers/TriggersTable.svelte b/frontend/src/lib/components/triggers/TriggersTable.svelte index 827395bcb8..62da1bce4f 100644 --- a/frontend/src/lib/components/triggers/TriggersTable.svelte +++ b/frontend/src/lib/components/triggers/TriggersTable.svelte @@ -39,15 +39,15 @@
-
+
@@ -59,7 +59,7 @@ {@const SvelteComponent = triggerIconMap[trigger.type]} onSelect?.(index) }} > - +
- + {#if trigger.isPrimary} From 93c34c6d769dd56da02064c0abdaa060efe0259d Mon Sep 17 00:00:00 2001 From: hugocasa Date: Tue, 25 Nov 2025 17:25:34 +0100 Subject: [PATCH 25/49] feat: flow for loop squashing (#7107) * feat: flow for loop optimization * fix: make dedicated flows work with flow nodes * fix: transform dedicated inputs + better error handling + differentiate squash from same worker * fix builds * fix build * fix build * make dedicated worker private * update ee ref * frontend nits * nit * add trace to dedicated worker * update ee ref * Update ee-repo-ref.txt --------- Co-authored-by: windmill-internal-app[bot] --- backend/ee-repo-ref.txt | 2 +- backend/src/monitor.rs | 14 +- backend/tests/worker.rs | 2 + backend/windmill-api/src/flows.rs | 1 + backend/windmill-common/src/flows.rs | 7 + backend/windmill-queue/src/jobs.rs | 80 +- backend/windmill-queue/src/lib.rs | 2 +- backend/windmill-worker/src/ai/tools.rs | 4 +- backend/windmill-worker/src/bun_executor.rs | 40 +- .../windmill-worker/src/dedicated_worker.rs | 759 ------------------ .../src/dedicated_worker_oss.rs | 26 + backend/windmill-worker/src/deno_executor.rs | 15 +- backend/windmill-worker/src/lib.rs | 6 +- .../windmill-worker/src/python_executor.rs | 22 +- .../windmill-worker/src/result_processor.rs | 37 +- backend/windmill-worker/src/worker.rs | 239 ++++-- backend/windmill-worker/src/worker_flow.rs | 103 ++- .../windmill-worker/src/worker_lockfiles.rs | 5 +- .../lib/components/FlowGraphViewerStep.svelte | 4 +- .../components/flows/content/FlowLoop.svelte | 29 + .../flows/content/FlowWhileLoop.svelte | 31 +- .../lib/components/flows/map/MapItem.svelte | 2 +- openflow.openapi.yaml | 4 + 23 files changed, 548 insertions(+), 886 deletions(-) delete mode 100644 backend/windmill-worker/src/dedicated_worker.rs create mode 100644 backend/windmill-worker/src/dedicated_worker_oss.rs diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt index ee9c8abbc4..bc6e0fecec 100644 --- a/backend/ee-repo-ref.txt +++ b/backend/ee-repo-ref.txt @@ -1 +1 @@ -1de62d3f0901aac6ef9ddd7595e1032be56b1938 \ No newline at end of file +f9434efa127dadb0a04c87d6fc368cc289d93501 diff --git a/backend/src/monitor.rs b/backend/src/monitor.rs index eaf83c2e4c..d85f5f3d58 100644 --- a/backend/src/monitor.rs +++ b/backend/src/monitor.rs @@ -2316,13 +2316,14 @@ async fn handle_zombie_jobs(db: &Pool, base_internal_url: &str, node_n ); let error_message = format!( - "Job timed out after no ping from job since {} (ZOMBIE_JOB_TIMEOUT: {}, reason: {:?}).\nThis likely means that the job died on worker {}, OOM are a common reason for worker crashes.\nCheck the workers around the time of the last ping and the exit code if any.", - job.last_ping.unwrap_or_default(), - *ZOMBIE_JOB_TIMEOUT, - error_kind.to_string(), - job.worker.clone().unwrap_or_default(), - ); + "Job timed out after no ping from job since {} (ZOMBIE_JOB_TIMEOUT: {}, reason: {:?}).\nThis likely means that the job died on worker {}, OOM are a common reason for worker crashes.\nCheck the workers around the time of the last ping and the exit code if any.", + job.last_ping.unwrap_or_default(), + *ZOMBIE_JOB_TIMEOUT, + error_kind.to_string(), + job.worker.clone().unwrap_or_default(), + ); let memory_peak = job.memory_peak.unwrap_or(0); + let (_, killpill_rx_never_used) = KillpillSender::new(1); let _ = handle_job_error( db, &client, @@ -2335,6 +2336,7 @@ async fn handle_zombie_jobs(db: &Pool, base_internal_url: &str, node_n "", node_name, send_result_never_used, + &killpill_rx_never_used, #[cfg(feature = "benchmark")] &mut windmill_common::bench::BenchmarkIter::new(), ) diff --git a/backend/tests/worker.rs b/backend/tests/worker.rs index 0a92dfc74d..bb18d6e397 100644 --- a/backend/tests/worker.rs +++ b/backend/tests/worker.rs @@ -217,6 +217,7 @@ async fn test_deno_flow(db: Pool) -> anyhow::Result<()> { iterator: InputTransform::Javascript { expr: "result".to_string() }, skip_failures: false, parallel: false, + squash: None, parallelism: None, modules: vec![FlowModule { id: "c".to_string(), @@ -396,6 +397,7 @@ async fn test_deno_flow_same_worker(db: Pool) -> anyhow::Result<()> { iterator: InputTransform::Static { value: windmill_common::worker::to_raw_value(&[1, 2, 3]) }, skip_failures: false, parallel: false, + squash: None, parallelism: None, modules: vec![ FlowModule { diff --git a/backend/windmill-api/src/flows.rs b/backend/windmill-api/src/flows.rs index 2a75045f9f..98130e2d2d 100644 --- a/backend/windmill-api/src/flows.rs +++ b/backend/windmill-api/src/flows.rs @@ -1583,6 +1583,7 @@ mod tests { skip_failures: true, parallel: false, parallelism: None, + squash: None, }), stop_after_if: Some(StopAfterIf { expr: "previous.isEmpty()".to_string(), diff --git a/backend/windmill-common/src/flows.rs b/backend/windmill-common/src/flows.rs index 41e02b7ee8..104f99b301 100644 --- a/backend/windmill-common/src/flows.rs +++ b/backend/windmill-common/src/flows.rs @@ -869,6 +869,8 @@ pub enum FlowModuleValue { parallel: bool, #[serde(skip_serializing_if = "Option::is_none")] parallelism: Option, + #[serde(skip_serializing_if = "Option::is_none")] + squash: Option, }, /// While loop node @@ -878,6 +880,8 @@ pub enum FlowModuleValue { modules_node: Option, #[serde(default = "default_false")] skip_failures: bool, + #[serde(skip_serializing_if = "Option::is_none")] + squash: Option, }, /// Branch-one node @@ -989,6 +993,7 @@ struct UntaggedFlowModuleValue { assets: Option>, tools: Option>, pass_flow_input_directly: Option, + squash: Option, } impl<'de> Deserialize<'de> for FlowModuleValue { @@ -1027,6 +1032,7 @@ impl<'de> Deserialize<'de> for FlowModuleValue { skip_failures: untagged.skip_failures.unwrap_or(true), parallel: untagged.parallel.unwrap_or(false), parallelism: untagged.parallelism, + squash: untagged.squash, }), "whileloopflow" => Ok(FlowModuleValue::WhileloopFlow { modules: untagged @@ -1034,6 +1040,7 @@ impl<'de> Deserialize<'de> for FlowModuleValue { .ok_or_else(|| serde::de::Error::missing_field("modules"))?, modules_node: untagged.modules_node, skip_failures: untagged.skip_failures.unwrap_or(false), + squash: untagged.squash, }), "branchone" => Ok(FlowModuleValue::BranchOne { branches: untagged diff --git a/backend/windmill-queue/src/jobs.rs b/backend/windmill-queue/src/jobs.rs index c050d56659..cc29b94f1b 100644 --- a/backend/windmill-queue/src/jobs.rs +++ b/backend/windmill-queue/src/jobs.rs @@ -24,6 +24,9 @@ use serde::{ser::SerializeMap, Serialize}; use serde_json::{json, value::RawValue}; use sqlx::{types::Json, Pool, Postgres, Transaction}; use sqlx::{Encode, PgExecutor}; +use tokio::sync::mpsc::Sender; +use tokio::sync::oneshot; +use tokio::task::JoinHandle; use tokio::{sync::RwLock, time::sleep}; use ulid::Ulid; use uuid::Uuid; @@ -137,7 +140,7 @@ pub struct CanceledBy { pub reason: Option, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct JobCompleted { pub job: MiniCompletedJob, pub preprocessed_args: Option>>, @@ -151,6 +154,10 @@ pub struct JobCompleted { pub duration: Option, pub has_stream: Option, pub from_cache: Option, + #[serde(skip)] + pub flow_runners: Option>, + #[serde(skip)] + pub done_tx: Option>, } pub async fn cancel_single_job<'c>( @@ -2271,6 +2278,8 @@ pub struct JobAndPerms { pub parent_runnable_path: Option, pub token: String, pub precomputed_agent_info: Option, + #[serde(skip)] + pub flow_runners: Option>, } impl PulledJob { pub async fn get_job_and_perms(self, db: &DB) -> JobAndPerms { @@ -2302,6 +2311,7 @@ impl PulledJob { parent_runnable_path: self.parent_runnable_path, token, precomputed_agent_info: None, + flow_runners: None, } } } @@ -2492,6 +2502,8 @@ impl PulledJobResult { duration: None, has_stream: Some(false), from_cache: None, + flow_runners: None, + done_tx: None, }), ), PulledJobResult { job: Some(job), error_while_preprocessing: Some(e), .. } => Err( @@ -2511,6 +2523,8 @@ impl PulledJobResult { duration: None, has_stream: Some(false), from_cache: None, + flow_runners: None, + done_tx: None, }), ), PulledJobResult { job, .. } => Ok(job), @@ -5715,10 +5729,74 @@ async fn restarted_flows_resolution( )) } + + +// Wrapper struct to send both job and optional flow_runners to dedicated workers +pub struct DedicatedWorkerJob { + pub job: Arc, + pub flow_runners: Option>, + pub done_tx: Option>, +} + +#[derive(Debug)] +pub struct FlowRunners { + pub runners: HashMap>, + pub handles: Vec>, + pub job_id: Uuid, +} + +impl Drop for FlowRunners { + fn drop(&mut self) { + let total_runners = self.handles.len(); + tracing::info!("dropping {} flow runners for job {}", total_runners, self.job_id); + + // First, drop all senders to signal workers to stop gracefully + self.runners.clear(); + + // Spawn a background task to wait with timeout and abort if needed + let handles = std::mem::take(&mut self.handles); + let job_id = self.job_id; + + tokio::spawn(async move { + // Extract abort handles before consuming the join handles + let abort_handles: Vec<_> = handles.iter().map(|h| h.abort_handle()).collect(); + + // Wait up to 5 seconds for natural termination + let timeout_result = tokio::time::timeout( + tokio::time::Duration::from_secs(5), + futures::future::join_all(handles) + ).await; + + match timeout_result { + Ok(_) => { + tracing::info!("all {} flow runners for job {} terminated gracefully", total_runners, job_id); + } + Err(_) => { + // Timeout reached, abort only the handles that haven't finished + let mut aborted = 0; + for abort_handle in abort_handles { + if !abort_handle.is_finished() { + abort_handle.abort(); + aborted += 1; + } + } + let graceful = total_runners - aborted; + tracing::warn!( + "flow runners for job {}: {} terminated gracefully, {} aborted after 5s timeout", + job_id, graceful, aborted + ); + } + } + }); + } +} + #[derive(Debug, Serialize, Deserialize)] pub struct SameWorkerPayload { pub job_id: Uuid, pub recoverable: bool, + #[serde(skip)] + pub flow_runners: Option>, } pub async fn get_same_worker_job( diff --git a/backend/windmill-queue/src/lib.rs b/backend/windmill-queue/src/lib.rs index 3ea2e2c1aa..ffd102c081 100644 --- a/backend/windmill-queue/src/lib.rs +++ b/backend/windmill-queue/src/lib.rs @@ -6,7 +6,7 @@ * LICENSE-AGPL for a copy of the license. */ -mod jobs; +pub mod jobs; #[cfg(feature = "private")] pub mod jobs_ee; pub mod jobs_oss; diff --git a/backend/windmill-worker/src/ai/tools.rs b/backend/windmill-worker/src/ai/tools.rs index 478eca5e54..84304f232c 100644 --- a/backend/windmill-worker/src/ai/tools.rs +++ b/backend/windmill-worker/src/ai/tools.rs @@ -529,6 +529,7 @@ async fn execute_windmill_tool( &mut occupancy_metrics_spawn, &mut killpill_rx_spawn, None, + None, #[cfg(feature = "benchmark")] &mut bench_spawn, ) @@ -656,8 +657,9 @@ async fn handle_tool_execution_success( .. }) = send_result.as_ref() { + let result = result.clone(); ctx.job_completed_tx - .send(send_result.as_ref().unwrap().result.clone(), true) + .send(send_result.unwrap().result, true) .await .map_err(to_anyhow)?; result diff --git a/backend/windmill-worker/src/bun_executor.rs b/backend/windmill-worker/src/bun_executor.rs index b8723ad815..26c59b77ba 100644 --- a/backend/windmill-worker/src/bun_executor.rs +++ b/backend/windmill-worker/src/bun_executor.rs @@ -9,10 +9,8 @@ use serde_json::value::RawValue; use uuid::Uuid; use windmill_parser_ts::remove_pinned_imports; -use windmill_queue::{append_logs, CanceledBy, MiniPulledJob, PrecomputedAgentInfo}; -#[cfg(feature = "enterprise")] -use crate::common::build_envs_map; +use windmill_queue::{append_logs, CanceledBy, MiniPulledJob, PrecomputedAgentInfo}; use crate::{ common::{ @@ -38,12 +36,6 @@ use tokio::{fs::File, process::Command}; use tokio::io::AsyncReadExt; -#[cfg(feature = "enterprise")] -use tokio::sync::mpsc::Receiver; - -#[cfg(feature = "enterprise")] -use windmill_common::variables; - use windmill_common::{ error::{self, Result}, get_latest_hash_for_path, @@ -1567,10 +1559,18 @@ pub async fn get_common_bun_proc_envs(base_internal_url: Option<&str>) -> HashMa return bun_envs; } -#[cfg(feature = "enterprise")] -use crate::{dedicated_worker::handle_dedicated_process, JobCompletedSender}; +#[cfg(feature = "private")] +use crate::{ + common::build_envs_map, dedicated_worker_oss::handle_dedicated_process, JobCompletedSender, +}; +#[cfg(feature = "private")] +use tokio::sync::mpsc::Receiver; +#[cfg(feature = "private")] +use windmill_common::variables; +#[cfg(feature = "private")] +use windmill_queue::DedicatedWorkerJob; -#[cfg(feature = "enterprise")] +#[cfg(feature = "private")] pub async fn start_worker( requirements_o: Option, codebase: Option, @@ -1584,8 +1584,9 @@ pub async fn start_worker( script_path: &str, token: &str, job_completed_tx: JobCompletedSender, - jobs_rx: Receiver>, + jobs_rx: Receiver, killpill_rx: tokio::sync::broadcast::Receiver<()>, + client: windmill_common::client::AuthedClient, ) -> Result<()> { let mut logs = "".to_string(); let mut mem_peak: i32 = 0; @@ -1737,6 +1738,12 @@ BigInt.prototype.toJSON = function () {{ console.log('start'); +function getArgs(line) {{ + let {{ {spread} }} = JSON.parse(line) + {dates} + return [ {spread} ]; +}} + for await (const line of Readline.createInterface({{ input: process.stdin }})) {{ {print_lines} @@ -1744,9 +1751,8 @@ for await (const line of Readline.createInterface({{ input: process.stdin }})) { process.exit(0); }} try {{ - let {{ {spread} }} = JSON.parse(line) - {dates} - let res = await Main.main(...[ {spread} ]); + const args = getArgs(line); + const res = await Main.main(...args); console.log("wm_res[success]:" + JSON.stringify(res ?? null, (key, value) => typeof value === 'undefined' ? null : value)); }} catch (e) {{ console.log("wm_res[error]:" + JSON.stringify({{ message: e.message, name: e.name, stack: e.stack, line: line }})); @@ -1812,6 +1818,7 @@ for await (const line of Readline.createInterface({{ input: process.stdin }})) { db, &script_path, "nodejs", + client, ) .await } else { @@ -1838,6 +1845,7 @@ for await (const line of Readline.createInterface({{ input: process.stdin }})) { db, script_path, "bun", + client, ) .await } diff --git a/backend/windmill-worker/src/dedicated_worker.rs b/backend/windmill-worker/src/dedicated_worker.rs deleted file mode 100644 index e65655c94d..0000000000 --- a/backend/windmill-worker/src/dedicated_worker.rs +++ /dev/null @@ -1,759 +0,0 @@ -// use tokio::sync::mpsc; - -// pub fn create_dedicated_worker() { -// let (job_completed_tx, mut new_job) = mpsc::channel::(100); -// } - -use async_recursion::async_recursion; -use std::{collections::VecDeque, process::Stdio, sync::Arc}; -use tokio::sync::mpsc::Sender; -use tokio::{ - io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, - process::Command, - task::JoinHandle, -}; -use windmill_common::error::Error; -use windmill_common::flows::FlowValue; -use windmill_common::worker::WORKER_CONFIG; -use windmill_common::KillpillSender; -use windmill_common::{ - cache, error, - flows::{FlowModule, FlowModuleValue}, - scripts::{ScriptHash, ScriptLang}, - variables, - worker::to_raw_value, - DB, -}; -use windmill_queue::append_logs; -use windmill_queue::MiniPulledJob; - -use anyhow::Context; - -use crate::{common::start_child_process, JobCompletedSender, MAX_BUFFERED_DEDICATED_JOBS}; - -use futures::{future, Future}; -use std::{collections::HashMap, task::Poll}; - -use tokio::sync::mpsc::Receiver; - -fn conditional_polling( - fut: impl Future, - predicate: bool, -) -> impl Future { - let mut fut = Box::pin(fut); - future::poll_fn(move |cx| { - if predicate { - fut.as_mut().poll(cx) - } else { - Poll::Pending - } - }) -} - -async fn write_stdin(stdin: &mut tokio::process::ChildStdin, s: &str) -> error::Result<()> { - let _ = &stdin.write_all(format!("{s}\n").as_bytes()).await?; - stdin.flush().await.context("stdin flush")?; - Ok(()) -} - -#[cfg(feature = "enterprise")] -pub async fn handle_dedicated_process( - command_path: &String, - job_dir: &str, - context_envs: HashMap, - envs: HashMap, - reserved_variables: Vec, - common_bun_proc_envs: HashMap, - args: Vec<&str>, - mut killpill_rx: tokio::sync::broadcast::Receiver<()>, - job_completed_tx: JobCompletedSender, - token: &str, - mut jobs_rx: Receiver>, - worker_name: &str, - db: &DB, - script_path: &str, - mode: &str, -) -> std::result::Result<(), error::Error> { - //do not cache local dependencies - - use windmill_queue::{JobCompleted, MiniCompletedJob}; - - use crate::{handle_child::process_status, PROXY_ENVS}; - let cmd_name = format!("dedicated {command_path}"); - let mut child = { - let mut cmd = Command::new(command_path); - cmd.current_dir(job_dir) - .env_clear() - .envs(context_envs) - .envs(envs) - .envs(PROXY_ENVS.clone()) - .envs( - reserved_variables - .iter() - .map(|x| (x.name.clone(), x.value.clone())) - .collect::>(), - ) - .envs(common_bun_proc_envs) - .args(args) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()); - start_child_process(cmd, command_path, false).await? - }; - - let stdout = child - .stdout() - .take() - .expect("child did not have a handle to stdout"); - - let stderr = child - .stderr() - .take() - .expect("child did not have a handle to stderr"); - - let mut reader = BufReader::new(stdout).lines(); - - let mut err_reader = BufReader::new(stderr).lines(); - - let mut stdin = child - .stdin() - .take() - .expect("child did not have a handle to stdin"); - - // Ensure the child process is spawned in the runtime so it can - // make progress on its own while we await for any output. - let child = tokio::spawn(async move { - let status = Box::into_pin(child.wait()) - .await - .expect("child process encountered an error"); - if let Err(e) = process_status(&cmd_name, status, vec![]) { - tracing::error!("child exit status was not success: {e:#}"); - } else { - tracing::info!("child exit status was success"); - } - }); - - let mut jobs: VecDeque = VecDeque::with_capacity(MAX_BUFFERED_DEDICATED_JOBS); - // let mut i = 0; - // let mut j = 0; - let mut alive = true; - - let init_log = format!("dedicated worker {mode}: {worker_name}\n\n"); - let mut logs = init_log.clone(); - loop { - tokio::select! { - biased; - _ = killpill_rx.recv(), if alive => { - println!("received killpill for dedicated worker"); - alive = false; - if let Err(e) = write_stdin(&mut stdin, "end").await { - tracing::info!("Could not write end message to stdin: {e:?}") - } - stdin.flush().await.context("stdin flush")?; - }, - line = err_reader.next_line() => { - if let Some(line) = line.expect("line is ok") { - tracing::error!("stderr dedicated worker: {line}"); - logs.push_str("[stderr] "); - logs.push_str(&line); - logs.push_str("\n"); - } else { - tracing::info!("dedicated worker process exited {script_path}"); - let mut last_stdout = "".to_string(); - while let Some(line) = reader.next_line().await.ok().flatten() { - last_stdout = line; - last_stdout.push_str("\n"); - } - tracing::info!("Last stdout for {script_path}: {last_stdout}"); - break; - } - }, - line = reader.next_line() => { - // j += 1; - - if let Some(line) = line.expect("line is ok") { - if line == "start" { - tracing::info!("dedicated worker process started {script_path}"); - continue; - } - tracing::debug!("processed job: |{line}|"); - if line.starts_with("wm_res[") { - let job = jobs.pop_front().expect("pop"); - tracing::info!("job completed on dedicated worker {script_path}: {}", job.id); - match serde_json::from_str::>(&line.replace("wm_res[success]:", "").replace("wm_res[error]:", "")) { - Ok(result) => { - let result = Arc::new(result); - append_logs(&job.id, &job.workspace_id, logs.clone(), &db.into()).await; - if line.starts_with("wm_res[success]:") { - job_completed_tx.send_job(JobCompleted { job , result, result_columns: None, mem_peak: 0, canceled_by: None, success: true, cached_res_path: None, token: token.to_string(), duration: None, preprocessed_args: None, has_stream: Some(false), from_cache: None }, true).await.unwrap() - } else { - job_completed_tx.send_job(JobCompleted { job , result, result_columns: None, mem_peak: 0, canceled_by: None, success: false, cached_res_path: None, token: token.to_string(), duration: None, preprocessed_args: None, has_stream: Some(false), from_cache: None }, true).await.unwrap() - } - }, - Err(e) => { - tracing::error!("Could not deserialize job result `{line}`: {e:?}"); - job_completed_tx.send_job(JobCompleted { job , result: Arc::new(to_raw_value(&serde_json::json!({"error": format!("Could not deserialize job result `{line}`: {e:?}")}))), result_columns: None, mem_peak: 0, canceled_by: None, success: false, cached_res_path: None, token: token.to_string(), duration: None, preprocessed_args: None, has_stream: Some(false), from_cache: None }, true).await.unwrap(); - }, - }; - logs = init_log.clone(); - } else { - logs.push_str(&line); - logs.push_str("\n"); - } - } else { - tracing::info!("dedicated worker {script_path} process exited"); - let mut last_stderr = "".to_string(); - while let Some(line) = err_reader.next_line().await.ok().flatten() { - last_stderr = line; - last_stderr.push_str("\n"); - } - tracing::info!("Last stderr for {script_path}: {last_stderr}"); - break; - } - }, - job = conditional_polling(jobs_rx.recv(), alive && jobs.len() < MAX_BUFFERED_DEDICATED_JOBS) => { - // i += 1; - if let Some(job) = job { - let id = job.id; - let args = serde_json::to_string(&job.args).expect("serialize"); - jobs.push_back(MiniCompletedJob::from(job)); - tracing::info!("received job and adding to queue on dedicated worker for {script_path}: {} (queue_size: {})", id, jobs.len()); - - // write_stdin(&mut stdin, &serde_json::to_string(&job.args.unwrap_or_else(|| serde_json::json!({"x": job.id}))).expect("serialize")).await?; - write_stdin(&mut stdin, &args).await?; - stdin.flush().await.context("stdin flush")?; - // tracing::info!("wrote job to stdin for {script_path}: {} (queue_size: {})", id, jobs.len()); - } else { - tracing::debug!("job channel closed"); - alive = false; - if let Err(e) = write_stdin(&mut stdin, "end").await { - tracing::error!("Could not write end message to stdin: {e:?}") - } - } - } - } - } - - child - .await - .map_err(|e| anyhow::anyhow!("child process {script_path} encountered an error: {e:#}"))?; - - tracing::info!("dedicated worker {script_path} child process exited successfully"); - Ok(()) -} - -type DedicatedWorker = ( - String, - Sender>, - Option>, -); - -// spawn one dedicated worker per compatible steps of the flow, associating the node id to the dedicated worker channel send -#[async_recursion] -#[cfg(feature = "enterprise")] -async fn spawn_dedicated_workers_for_flow( - modules: &Vec, - w_id: &str, - path: &str, - killpill_tx: KillpillSender, - killpill_rx: &tokio::sync::broadcast::Receiver<()>, - db: &DB, - worker_dir: &str, - base_internal_url: &str, - worker_name: &str, - job_completed_tx: &JobCompletedSender, -) -> Vec { - let mut workers = vec![]; - let mut script_path_to_worker: HashMap>> = - HashMap::new(); - for module in modules.iter() { - let value = module.get_value(); - if let Ok(value) = value { - match &value { - FlowModuleValue::Script { path, hash, .. } => { - let key = format!( - "{}:{}", - path, - hash.clone() - .map(|x| x.to_string()) - .unwrap_or_else(|| "".to_string()) - ); - if let Some(sender) = script_path_to_worker.get(&key) { - workers.push((module.id.clone(), sender.clone(), None)); - } else { - if let Some(dedi_w) = spawn_dedicated_worker( - SpawnWorker::Script { path: path.to_string(), hash: hash.clone() }, - w_id, - killpill_tx.clone(), - killpill_rx, - db, - worker_dir, - base_internal_url, - worker_name, - job_completed_tx, - Some(module.id.clone()), - ) - .await - { - script_path_to_worker.insert(key, dedi_w.1.clone()); - workers.push(dedi_w); - } - } - } - FlowModuleValue::ForloopFlow { modules, .. } => { - let w = spawn_dedicated_workers_for_flow( - &modules, - w_id, - path, - killpill_tx.clone(), - killpill_rx, - db, - worker_dir, - base_internal_url, - worker_name, - job_completed_tx, - ) - .await; - workers.extend(w); - } - FlowModuleValue::WhileloopFlow { modules, .. } => { - let w = spawn_dedicated_workers_for_flow( - &modules, - w_id, - path, - killpill_tx.clone(), - killpill_rx, - db, - worker_dir, - base_internal_url, - worker_name, - job_completed_tx, - ) - .await; - workers.extend(w); - } - FlowModuleValue::BranchOne { branches, default, .. } => { - for modules in branches - .iter() - .map(|x| &x.modules) - .chain(std::iter::once(default)) - { - let w = spawn_dedicated_workers_for_flow( - &modules, - w_id, - path, - killpill_tx.clone(), - killpill_rx, - db, - worker_dir, - base_internal_url, - worker_name, - job_completed_tx, - ) - .await; - workers.extend(w); - } - } - FlowModuleValue::BranchAll { branches, .. } => { - for branch in branches { - let w = spawn_dedicated_workers_for_flow( - &branch.modules, - w_id, - path, - killpill_tx.clone(), - killpill_rx, - db, - worker_dir, - base_internal_url, - worker_name, - job_completed_tx, - ) - .await; - workers.extend(w); - } - } - FlowModuleValue::RawScript { content, lock, path: spath, language, .. } => { - if let Some(dedi_w) = spawn_dedicated_worker( - SpawnWorker::RawScript { - path: spath.clone().unwrap_or(path.to_string()), - content: content.to_string(), - lock: lock.clone(), - lang: language.clone(), - }, - w_id, - killpill_tx.clone(), - killpill_rx, - db, - worker_dir, - base_internal_url, - worker_name, - job_completed_tx, - Some(module.id.clone()), - ) - .await - { - workers.push(dedi_w); - } - } - FlowModuleValue::FlowScript { id, language, .. } => { - let spawn = cache::flow::fetch_script( - &windmill_common::worker::Connection::Sql(db.clone()), - *id, - ) - .await - .map(|data| SpawnWorker::RawScript { - path: "".to_string(), - content: data.code.clone(), - lock: data.lock.clone(), - lang: *language, - }); - match spawn { - Ok(spawn) => { - if let Some(dedi_w) = spawn_dedicated_worker( - spawn, - w_id, - killpill_tx.clone(), - killpill_rx, - db, - worker_dir, - base_internal_url, - worker_name, - job_completed_tx, - Some(module.id.clone()), - ) - .await - { - workers.push(dedi_w); - } - } - Err(err) => tracing::error!( - "failed to get script for module: {:?}, err: {:?}", - module, - err - ), - } - } - FlowModuleValue::Flow { .. } => (), - FlowModuleValue::Identity => (), - FlowModuleValue::AIAgent { .. } => (), - } - } else { - tracing::error!("failed to get value for module: {:?}", module); - } - } - workers -} - -pub async fn create_dedicated_worker_map( - killpill_tx: &KillpillSender, - killpill_rx: &tokio::sync::broadcast::Receiver<()>, - db: &DB, - worker_dir: &str, - base_internal_url: &str, - worker_name: &str, - job_completed_tx: &JobCompletedSender, -) -> ( - HashMap>>, - bool, - Vec>, -) { - let mut dedicated_handles = vec![]; - if let Some(_wp) = WORKER_CONFIG.read().await.dedicated_worker.clone() { - let mut hm = HashMap::new(); - let is_flow_worker; - if let Some(flow_path) = _wp.path.strip_prefix("flow/") { - is_flow_worker = true; - let value = sqlx::query_scalar!( - "SELECT flow_version.value AS \"value!: sqlx::types::Json>\" - FROM flow - LEFT JOIN flow_version - ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] - WHERE flow.path = $1 AND flow.workspace_id = $2", - flow_path, - _wp.workspace_id - ) - .fetch_optional(db) - .await; - if let Ok(v) = value { - if let Some(v) = v { - let value = serde_json::from_str::(v.get()).map_err(|err| { - Error::internal_err(format!( - "could not convert json to flow for {flow_path}: {err:?}" - )) - }); - if let Ok(flow) = value { - let workers = spawn_dedicated_workers_for_flow( - &flow.modules, - &_wp.workspace_id, - &_wp.path, - killpill_tx.clone(), - &killpill_rx, - db, - &worker_dir, - base_internal_url, - &worker_name, - &job_completed_tx, - ) - .await; - workers.into_iter().for_each(|(path, sender, handle)| { - tracing::info!("spawned dedicated worker for flow: {}", path.as_str()); - if let Some(h) = handle { - dedicated_handles.push(h); - } - hm.insert(path, sender); - }); - } - } else { - tracing::error!( - "flow present but value not found for dedicated worker. {}", - flow_path - ); - } - } else { - tracing::error!("flow not found for dedicated worker: {}. Waiting for dependency job and expected to restart.", flow_path); - } - } else { - is_flow_worker = false; - if let Some((path, sender, handle)) = spawn_dedicated_worker( - SpawnWorker::Script { path: _wp.path.clone(), hash: None }, - &_wp.workspace_id, - killpill_tx.clone(), - &killpill_rx, - db, - &worker_dir, - base_internal_url, - &worker_name, - &job_completed_tx, - None, - ) - .await - { - if let Some(h) = handle { - dedicated_handles.push(h); - } - hm.insert(path, sender); - } else { - tracing::error!( - "failed to spawn dedicated worker for {}, script not found", - _wp.path - ); - } - } - (hm, is_flow_worker, dedicated_handles) - } else { - (HashMap::new(), false, dedicated_handles) - } -} - -#[derive(Debug, Clone)] -pub enum SpawnWorker { - Script { path: String, hash: Option }, - RawScript { path: String, content: String, lock: Option, lang: ScriptLang }, -} - -// spawn one dedicated worker and return the key, the channel sender and the join handle -// note that for it will return none for language that do not support dedicated workers -// note that go using cache binary does not need dedicated workers so all languages are supported -#[cfg(feature = "enterprise")] -async fn spawn_dedicated_worker( - sw: SpawnWorker, - w_id: &str, - killpill_tx: KillpillSender, - killpill_rx: &tokio::sync::broadcast::Receiver<()>, - db: &DB, - worker_dir: &str, - base_internal_url: &str, - worker_name: &str, - job_completed_tx: &JobCompletedSender, - node_id: Option, -) -> Option { - use windmill_common::{ - error::Error, - scripts::{ScriptHash, ScriptLang}, - utils::rd_string, - }; - use windmill_queue::MiniPulledJob; - - use crate::{build_envs, get_script_content_by_hash, ContentReqLangEnvs}; - - #[cfg(not(feature = "enterprise"))] - { - tracing::error!("Dedicated worker is an enterprise feature"); - killpill_tx.send(()).expect("send"); - return None; - } - - #[cfg(feature = "enterprise")] - { - let (dedicated_worker_tx, dedicated_worker_rx) = tokio::sync::mpsc::channel::< - std::sync::Arc, - >(MAX_BUFFERED_DEDICATED_JOBS); - let killpill_rx = killpill_rx.resubscribe(); - let db2 = db.clone(); - let base_internal_url = base_internal_url.to_string(); - let worker_name = worker_name.to_string(); - let job_completed_tx = job_completed_tx.clone(); - let job_dir = format!( - "{}/dedicated{}", - worker_dir, - node_id - .as_ref() - .map(|x| format!("-{x}")) - .unwrap_or_else(|| "".to_string()) - ); - tokio::fs::create_dir_all(&job_dir) - .await - .expect("create dir"); - - let path = match &sw { - SpawnWorker::RawScript { path, .. } => path.to_string(), - SpawnWorker::Script { path, .. } => path.to_string(), - }; - - let path2 = path.clone(); - let w_id = w_id.to_string(); - - let (content, lock, language, envs, codebase) = match sw.clone() { - SpawnWorker::Script { path, hash } => { - let q = if let Some(hash) = hash { - get_script_content_by_hash(&hash, &w_id, &db2.into()) - .await - .map(|r: ContentReqLangEnvs| { - Some((r.content, r.lockfile, r.language, r.envs, r.codebase)) - }) - } else { - sqlx::query_as::<_, (String, Option, Option, Option>, bool, Option)>( - "SELECT content, lock, language, envs, codebase IS NOT NULL, hash FROM script WHERE path = $1 AND workspace_id = $2 AND - archived = false AND lock IS not NULL AND lock_error_logs IS NULL ORDER BY created_at DESC LIMIT 1", - ) - .bind(&path) - .bind(&w_id) - .fetch_optional(&db2) - .await - .map_err(|e| Error::internal_err(format!("expected content and lock: {e:#}"))) - .map(|x| x.map(|y| (y.0, y.1, y.2, y.3, if y.4 { y.5.map(|z| z.to_string()) } else { None }))) - }; - if let Ok(q) = q { - if let Some(wp) = q { - wp - } else { - tracing::error!( - "Failed to fetch script `{}` in workspace {} for dedicated worker.", - path, - w_id - ); - return None; - } - } else { - tracing::error!("Failed to fetch script for dedicated worker"); - killpill_tx.send(); - return None; - } - } - SpawnWorker::RawScript { content, lock, lang, .. } => { - (content, lock, Some(lang), None, None) - } - }; - - match language { - Some(ScriptLang::Python3) | Some(ScriptLang::Bun) | Some(ScriptLang::Deno) => {} - _ => return None, - } - - let db = db.clone(); - let handle = tokio::spawn(async move { - let token = { - let token = rd_string(32); - if let Err(e) = sqlx::query_scalar!( - "INSERT INTO token - (token, label, super_admin, email) - VALUES ($1, $2, $3, $4)", - token, - "dedicated_worker", - true, - "dedicated_worker@windmill.dev" - ) - .execute(&db) - .await - { - tracing::error!("failed to create token for dedicated worker: {:?}", e); - killpill_tx.clone().send(); - }; - token - }; - - let worker_envs = build_envs(envs.as_ref()).expect("failed to build envs"); - - if let Err(e) = match language { - Some(ScriptLang::Python3) => { - #[cfg(not(feature = "python"))] - { - tracing::error!("Python requires the python feature to be enabled"); - killpill_tx.send(); - return; - } - - #[cfg(feature = "python")] - crate::python_executor::start_worker( - lock.as_ref(), - &db, - &content, - &base_internal_url, - &job_dir, - &worker_name, - worker_envs, - &w_id, - &path, - &token, - job_completed_tx, - dedicated_worker_rx, - killpill_rx, - ) - .await - } - Some(ScriptLang::Bun) => { - crate::bun_executor::start_worker( - lock, - codebase, - &db, - &content, - &base_internal_url, - &job_dir, - &worker_name, - worker_envs, - &w_id, - &path, - &token, - job_completed_tx, - dedicated_worker_rx, - killpill_rx, - ) - .await - } - Some(ScriptLang::Deno) => { - crate::deno_executor::start_worker( - &content, - &base_internal_url, - &job_dir, - &worker_name, - worker_envs, - &w_id, - &path, - &token, - job_completed_tx, - dedicated_worker_rx, - killpill_rx, - &db, - ) - .await - } - _ => unreachable!("Non supported language for dedicated worker"), - } { - tracing::error!("error in dedicated worker for {sw:#?}: {:?}", e); - }; - killpill_tx.clone().send(); - }); - return Some((node_id.unwrap_or(path2), dedicated_worker_tx, Some(handle))); - // (Some(dedi_path), Some(dedicated_worker_tx), Some(handle)) - } -} diff --git a/backend/windmill-worker/src/dedicated_worker_oss.rs b/backend/windmill-worker/src/dedicated_worker_oss.rs new file mode 100644 index 0000000000..09e86193e0 --- /dev/null +++ b/backend/windmill-worker/src/dedicated_worker_oss.rs @@ -0,0 +1,26 @@ +#[cfg(feature = "private")] +// #[allow(unused)] +pub use crate::dedicated_worker_ee::*; + +#[cfg(not(feature = "private"))] +pub async fn spawn_flow_module_runners( + _job: &windmill_queue::MiniPulledJob, + _module: &windmill_common::flows::FlowModule, + _failure_module: Option<&Box>, + _killpill_rx: &tokio::sync::broadcast::Receiver<()>, + _db: &windmill_common::db::DB, + _worker_dir: &str, + _base_internal_url: &str, + _worker_name: &str, + _job_completed_tx: &crate::JobCompletedSender, +) -> windmill_common::error::Result<( + std::collections::HashMap< + String, + tokio::sync::mpsc::Sender, + >, + Vec>, +)> { + Err(windmill_common::error::Error::internal_err( + "Squashing flow loops is not available in OSS".to_string(), + )) +} diff --git a/backend/windmill-worker/src/deno_executor.rs b/backend/windmill-worker/src/deno_executor.rs index 332cd142db..e80eec265d 100644 --- a/backend/windmill-worker/src/deno_executor.rs +++ b/backend/windmill-worker/src/deno_executor.rs @@ -514,13 +514,14 @@ async fn build_import_map( Ok(()) as error::Result<()> } -#[cfg(feature = "enterprise")] -use crate::{dedicated_worker::handle_dedicated_process, JobCompletedSender}; - -#[cfg(feature = "enterprise")] +#[cfg(feature = "private")] +use crate::{dedicated_worker_oss::handle_dedicated_process, JobCompletedSender}; +#[cfg(feature = "private")] use tokio::sync::mpsc::Receiver; +#[cfg(feature = "private")] +use windmill_queue::DedicatedWorkerJob; -#[cfg(feature = "enterprise")] +#[cfg(feature = "private")] pub async fn start_worker( inner_content: &str, base_internal_url: &str, @@ -531,9 +532,10 @@ pub async fn start_worker( script_path: &str, token: &str, job_completed_tx: JobCompletedSender, - jobs_rx: Receiver>, + jobs_rx: Receiver, killpill_rx: tokio::sync::broadcast::Receiver<()>, db: &sqlx::Pool, + client: windmill_common::client::AuthedClient, ) -> Result<()> { use windmill_common::variables; @@ -654,6 +656,7 @@ for await (const chunk of Deno.stdin.readable) {{ db, script_path, "deno", + client, ) .await } diff --git a/backend/windmill-worker/src/lib.rs b/backend/windmill-worker/src/lib.rs index 4bf60f6d4a..c5db591c42 100644 --- a/backend/windmill-worker/src/lib.rs +++ b/backend/windmill-worker/src/lib.rs @@ -23,8 +23,10 @@ mod bun_executor; pub mod common; mod config; mod csharp_executor; -#[cfg(feature = "enterprise")] -mod dedicated_worker; + +#[cfg(feature = "private")] +mod dedicated_worker_ee; +mod dedicated_worker_oss; mod deno_executor; #[cfg(feature = "duckdb")] mod duckdb_executor; diff --git a/backend/windmill-worker/src/python_executor.rs b/backend/windmill-worker/src/python_executor.rs index bc84a2e19f..b417014d85 100644 --- a/backend/windmill-worker/src/python_executor.rs +++ b/backend/windmill-worker/src/python_executor.rs @@ -18,6 +18,8 @@ use tokio::{ sync::Semaphore, task, }; +use windmill_queue::MiniPulledJob; + use uuid::Uuid; #[cfg(all(feature = "enterprise", feature = "parquet", unix))] use windmill_common::ee_oss::{get_license_plan, LicensePlan}; @@ -546,7 +548,6 @@ pub async fn handle_python_job( precomputed_agent_info: Option, has_stream: &mut bool, ) -> windmill_common::error::Result> { - let script_path = crate::common::use_flow_root_path(job.runnable_path()); let annotations = PythonAnnotations::parse(inner_content); @@ -2130,16 +2131,16 @@ fn get_result_postprocessor<'a>(skip: bool) -> &'a str { } } -#[cfg(feature = "enterprise")] +#[cfg(feature = "private")] use crate::JobCompletedSender; -#[cfg(feature = "enterprise")] -use crate::{common::build_envs_map, dedicated_worker::handle_dedicated_process}; -#[cfg(feature = "enterprise")] +#[cfg(feature = "private")] +use crate::{common::build_envs_map, dedicated_worker_oss::handle_dedicated_process}; +#[cfg(feature = "private")] use windmill_common::variables; +#[cfg(feature = "private")] +use windmill_queue::DedicatedWorkerJob; -use windmill_queue::MiniPulledJob; - -#[cfg(feature = "enterprise")] +#[cfg(feature = "private")] pub async fn start_worker( requirements_o: Option<&String>, db: &sqlx::Pool, @@ -2152,10 +2153,12 @@ pub async fn start_worker( script_path: &str, token: &str, job_completed_tx: JobCompletedSender, - jobs_rx: tokio::sync::mpsc::Receiver>, + jobs_rx: tokio::sync::mpsc::Receiver, killpill_rx: tokio::sync::broadcast::Receiver<()>, + client: windmill_common::client::AuthedClient, ) -> error::Result<()> { use crate::{PyV, PyVAlias}; + tracing::info!("script path: {}", script_path); let mut mem_peak: i32 = 0; let mut canceled_by: Option = None; @@ -2346,6 +2349,7 @@ for line in sys.stdin: db, script_path, "python", + client, ) .await } diff --git a/backend/windmill-worker/src/result_processor.rs b/backend/windmill-worker/src/result_processor.rs index 1e168d3a30..e5a7a0052c 100644 --- a/backend/windmill-worker/src/result_processor.rs +++ b/backend/windmill-worker/src/result_processor.rs @@ -31,8 +31,8 @@ use windmill_common::{ use windmill_common::bench::{BenchmarkInfo, BenchmarkIter}; use windmill_queue::{ - append_logs, get_mini_completed_job, CanceledBy, JobCompleted, MiniCompletedJob, MiniPulledJob, - ValidableJson, WrappedError, INIT_SCRIPT_TAG, + append_logs, get_mini_completed_job, CanceledBy, FlowRunners, JobCompleted, MiniCompletedJob, + MiniPulledJob, ValidableJson, WrappedError, INIT_SCRIPT_TAG, }; use serde_json::{json, value::RawValue, Value}; @@ -66,6 +66,7 @@ async fn process_jc( same_worker_tx: Option<&SameWorkerSender>, job_completed_sender: &JobCompletedSender, stats_map: &JobStatsMap, + killpill_rx: &tokio::sync::broadcast::Receiver<()>, #[cfg(feature = "benchmark")] bench: &mut BenchmarkIter, ) { let success: bool = jc.success; @@ -155,6 +156,7 @@ async fn process_jc( same_worker_tx, &worker_name, job_completed_sender.clone(), + killpill_rx, #[cfg(feature = "benchmark")] bench, ) @@ -285,6 +287,7 @@ pub fn start_background_processor( Some(&same_worker_tx), &job_completed_sender, &stats_map, + &killpill_rx, #[cfg(feature = "benchmark")] &mut bench, ) @@ -350,6 +353,8 @@ pub fn start_background_processor( stop_early_override, &worker_name, job_completed_sender.clone(), + None, + &killpill_rx, #[cfg(feature = "benchmark")] &mut bench, ) @@ -413,6 +418,7 @@ pub async fn process_result( conn: &Connection, duration: Option, has_stream: bool, + flow_runners: Option>, ) -> error::Result { match result { Ok(result) => { @@ -431,6 +437,8 @@ pub async fn process_result( duration, has_stream: Some(has_stream), from_cache: None, + flow_runners, + done_tx: None, }, ) .with_context(windmill_common::otel_oss::otel_ctx()) @@ -495,6 +503,8 @@ pub async fn process_result( duration, has_stream: Some(has_stream), from_cache: None, + flow_runners, + done_tx: None, }, ) .with_context(windmill_common::otel_oss::otel_ctx()) @@ -512,6 +522,7 @@ pub async fn handle_receive_completed_job( same_worker_tx: Option<&SameWorkerSender>, worker_name: &str, job_completed_tx: JobCompletedSender, + killpill_rx: &tokio::sync::broadcast::Receiver<()>, #[cfg(feature = "benchmark")] bench: &mut BenchmarkIter, ) -> Option> { let token = jc.token.clone(); @@ -529,6 +540,7 @@ pub async fn handle_receive_completed_job( same_worker_tx.clone(), worker_name, job_completed_tx.clone(), + killpill_rx, #[cfg(feature = "benchmark")] bench, ) @@ -548,6 +560,7 @@ pub async fn handle_receive_completed_job( &worker_dir, worker_name, job_completed_tx, + killpill_rx, #[cfg(feature = "benchmark")] bench, ) @@ -571,6 +584,8 @@ pub async fn process_completed_job( preprocessed_args, has_stream, from_cache, + flow_runners, + done_tx, .. }: JobCompleted, client: &AuthedClient, @@ -579,6 +594,7 @@ pub async fn process_completed_job( same_worker_tx: Option<&SameWorkerSender>, worker_name: &str, job_completed_tx: JobCompletedSender, + killpill_rx: &tokio::sync::broadcast::Receiver<()>, #[cfg(feature = "benchmark")] bench: &mut BenchmarkIter, ) -> error::Result>> { if success { @@ -661,12 +677,19 @@ pub async fn process_completed_job( None, worker_name, job_completed_tx, + flow_runners, + killpill_rx, #[cfg(feature = "benchmark")] bench, ) .warn_after_seconds(10) .await?; add_time!(bench, "updated flow status END"); + if let Some(done_tx) = done_tx { + done_tx + .send(()) + .expect("done receiver should still be alive"); + } return Ok(r); } } @@ -707,11 +730,18 @@ pub async fn process_completed_job( None, worker_name, job_completed_tx, + flow_runners, + killpill_rx, #[cfg(feature = "benchmark")] bench, ) .warn_after_seconds(10) .await?; + if let Some(done_tx) = done_tx { + done_tx + .send(()) + .expect("done receiver should still be alive"); + } return Ok(r); } } @@ -761,6 +791,7 @@ pub async fn handle_job_error( worker_dir: &str, worker_name: &str, job_completed_tx: JobCompletedSender, + killpill_rx: &tokio::sync::broadcast::Receiver<()>, #[cfg(feature = "benchmark")] bench: &mut BenchmarkIter, ) { let err_string = format!("{}: {}", err.name(), err.to_string()); @@ -809,6 +840,8 @@ pub async fn handle_job_error( None, worker_name, job_completed_tx.clone(), + None, + killpill_rx, #[cfg(feature = "benchmark")] bench, ) diff --git a/backend/windmill-worker/src/worker.rs b/backend/windmill-worker/src/worker.rs index 9b47aa5113..2919243dbc 100644 --- a/backend/windmill-worker/src/worker.rs +++ b/backend/windmill-worker/src/worker.rs @@ -11,6 +11,7 @@ use anyhow::anyhow; use futures::TryFutureExt; +use tokio::sync::Mutex; use tokio::time::sleep; use tokio::time::timeout; use windmill_common::client::AuthedClient; @@ -59,6 +60,8 @@ use std::{ time::Duration, }; use windmill_parser::MainArgSignature; +use windmill_queue::DedicatedWorkerJob; +use windmill_queue::FlowRunners; use windmill_queue::MiniCompletedJob; use windmill_queue::PulledJobResultToJobErr; @@ -167,8 +170,8 @@ use crate::duckdb_executor::do_duckdb; #[cfg(all(feature = "enterprise", feature = "oracledb"))] use crate::oracledb_executor::do_oracledb; -#[cfg(feature = "enterprise")] -use crate::dedicated_worker::create_dedicated_worker_map; +#[cfg(all(feature = "private", feature = "enterprise"))] +use crate::dedicated_worker_oss::create_dedicated_worker_map; #[cfg(feature = "enterprise")] use crate::snowflake_executor::do_snowflake; @@ -532,6 +535,8 @@ lazy_static::lazy_static! { .ok() .and_then(|x| x.parse::().ok()) .unwrap_or(1000); + + pub static ref FLOW_RUNNER_RUNNING: Mutex = Mutex::new(false); } type Envs = Vec<(String, String)>; @@ -558,14 +563,14 @@ lazy_static::lazy_static! { #[derive(Debug)] pub enum NextJob { - Sql(PulledJob), + Sql { job: PulledJob, flow_runners: Option> }, Http(JobAndPerms), } impl NextJob { pub fn job(self) -> MiniPulledJob { match self { - NextJob::Sql(job) => job.job, + NextJob::Sql { job, .. } => job.job, NextJob::Http(job) => job.job, } } @@ -575,7 +580,7 @@ impl std::ops::Deref for NextJob { type Target = MiniPulledJob; fn deref(&self) -> &Self::Target { match self { - NextJob::Sql(job) => &job.job, + NextJob::Sql { job, .. } => &job.job, NextJob::Http(job) => &job.job, } } @@ -733,10 +738,10 @@ impl JobCompletedSender { impl SameWorkerSender { pub async fn send( &self, - payload: SameWorkerPayload, + message: SameWorkerPayload, ) -> Result<(), tokio::sync::mpsc::error::SendError> { self.1.fetch_add(1, Ordering::Relaxed); - self.0.send(payload).await + self.0.send(message).await } } @@ -829,18 +834,36 @@ fn add_outstanding_wait_time( async fn extract_job_and_perms(job: NextJob, conn: &Connection) -> JobAndPerms { match (job, conn) { - (NextJob::Sql(job), Connection::Sql(db)) => job.get_job_and_perms(db).await, - (NextJob::Sql(_), Connection::Http(_)) => panic!("sql job on http connection"), + (NextJob::Sql { job, flow_runners, .. }, Connection::Sql(db)) => { + JobAndPerms { flow_runners, ..job.get_job_and_perms(db).await } + } + (NextJob::Sql { .. }, Connection::Http(_)) => panic!("sql job on http connection"), (NextJob::Http(job), _) => job, } } -fn create_span(arc_job: &Arc, worker_name: &str, hostname: &str) -> Span { - let span = tracing::span!(tracing::Level::INFO, "job", - job_id = %arc_job.id, root_job = field::Empty, workspace_id = %arc_job.workspace_id, worker = %worker_name, hostname = %hostname, tag = %arc_job.tag, +pub fn create_span_with_name( + arc_job: &MiniPulledJob, + worker_name: &str, + hostname: Option<&str>, + span_name: &str, +) -> Span { + // The span macro requires a literal, so we use a fixed name and set otel.name dynamically + let span = tracing::span!( + tracing::Level::INFO, + "job", + job_id = %arc_job.id, + root_job = field::Empty, + workspace_id = %arc_job.workspace_id, + worker = %worker_name, + hostname = field::Empty, + tag = %arc_job.tag, language = field::Empty, - script_path = field::Empty, flow_step_id = field::Empty, parent_job = field::Empty, - otel.name = field::Empty); + script_path = field::Empty, + flow_step_id = field::Empty, + parent_job = field::Empty, + otel.name = field::Empty + ); let rj = arc_job.flow_innermost_root_job.unwrap_or(arc_job.id); @@ -848,10 +871,10 @@ fn create_span(arc_job: &Arc, worker_name: &str, hostname: &str) span.record("language", lg.as_str()); } if let Some(step_id) = arc_job.flow_step_id.as_ref() { - span.record("otel.name", format!("job {}", step_id).as_str()); + span.record("otel.name", format!("{} {}", span_name, step_id).as_str()); span.record("flow_step_id", step_id.as_str()); } else { - span.record("otel.name", "job"); + span.record("otel.name", span_name); } if let Some(parent_job) = arc_job.parent_job.as_ref() { span.record("parent_job", parent_job.to_string().as_str()); @@ -862,6 +885,9 @@ fn create_span(arc_job: &Arc, worker_name: &str, hostname: &str) if let Some(root_job) = arc_job.flow_innermost_root_job.as_ref() { span.record("root_job", root_job.to_string().as_str()); } + if let Some(hostname) = hostname { + span.record("hostname", hostname); + } windmill_common::otel_oss::set_span_parent(&span, &rj); span @@ -876,6 +902,7 @@ pub async fn handle_all_job_kind_error( worker_dir: &str, worker_name: &str, job_completed_tx: JobCompletedSender, + killpill_rx: &tokio::sync::broadcast::Receiver<()>, #[cfg(feature = "benchmark")] bench: &mut BenchmarkIter, ) { match conn { @@ -892,6 +919,7 @@ pub async fn handle_all_job_kind_error( &worker_dir, &worker_name, job_completed_tx.clone(), + &killpill_rx, #[cfg(feature = "benchmark")] bench, ) @@ -915,6 +943,8 @@ pub async fn handle_all_job_kind_error( duration: None, has_stream: Some(false), from_cache: None, + flow_runners: None, + done_tx: None, }, false, ) @@ -962,27 +992,21 @@ pub fn start_interactive_worker_shell( .await; match job { - Ok(j) => match j.to_pulled_job() { - Ok(j) => Ok(j.clone().map(NextJob::Sql)), - ref e @ (Err(PulledJobResultToJobErr::MissingConcurrencyKey( - ref jc, - )) - | Err(PulledJobResultToJobErr::ErrorWhilePreprocessing( - ref jc, - ))) => { - if let Err(err) = - job_completed_tx.send_job(jc.clone(), true).await - { - let e_fmt = match e { - Ok(_) => "unknown error".to_owned(), - Err(e) => e.to_string(), - }; - - tracing::error!("An error occurred while sending job completed ({e_fmt}): {:#?}", err) + Ok(j) => { + match j.to_pulled_job() { + Ok(j) => Ok(j + .clone() + .map(|job| NextJob::Sql { flow_runners: None, job })), + Err(PulledJobResultToJobErr::MissingConcurrencyKey(jc)) + | Err(PulledJobResultToJobErr::ErrorWhilePreprocessing(jc)) => { + if let Err(err) = job_completed_tx.send_job(jc, true).await + { + tracing::error!("An error occurred while sending job completed: {:#?}", err) + } + Ok(None) } - Ok(None) } - }, + } Err(err) => Err(err), } } @@ -1010,6 +1034,7 @@ pub fn start_interactive_worker_shell( parent_runnable_path, token, precomputed_agent_info: precomputed_bundle, + flow_runners, } = extract_job_and_perms(job, &conn).await; let authed_client = AuthedClient::new( @@ -1039,6 +1064,7 @@ pub fn start_interactive_worker_shell( &mut occupancy_metrics, &mut killpill_rx, precomputed_bundle, + flow_runners, #[cfg(feature = "benchmark")] &mut bench, ) @@ -1476,9 +1502,9 @@ pub async fn run_worker( // Option>>, // Option>, - #[cfg(feature = "enterprise")] + #[cfg(all(feature = "private", feature = "enterprise"))] let (dedicated_workers, is_flow_worker, dedicated_handles): ( - HashMap>>, + HashMap>, bool, Vec>, ) = match conn { @@ -1497,9 +1523,9 @@ pub async fn run_worker( Connection::Http(_) => (HashMap::new(), false, vec![]), }; - #[cfg(not(feature = "enterprise"))] + #[cfg(any(not(feature = "private"), not(feature = "enterprise")))] let (dedicated_workers, is_flow_worker, dedicated_handles): ( - HashMap>>, + HashMap>, bool, Vec>, ) = (HashMap::new(), false, vec![]); @@ -1656,7 +1682,7 @@ pub async fn run_worker( if let Ok(same_worker_job) = same_worker_rx.try_recv() { same_worker_queue_size.fetch_sub(1, Ordering::SeqCst); - tracing::debug!( + tracing::info!( worker = %worker_name, hostname = %hostname, "received {} from same worker channel", same_worker_job.job_id @@ -1676,7 +1702,12 @@ pub async fn run_worker( .expect("send kill to job completed tx"); break; } else { - job.map(|x| x.map(NextJob::Sql)) + job.map(|x| { + x.map(|job| NextJob::Sql { + flow_runners: same_worker_job.flow_runners, + job, + }) + }) } } Connection::Http(client) => client @@ -1827,22 +1858,14 @@ pub async fn run_worker( } match job { Ok(pulled_job_result) => match pulled_job_result.to_pulled_job() { - Ok(j) => Ok(j.map(NextJob::Sql)), - ref e @ (Err(PulledJobResultToJobErr::MissingConcurrencyKey( - ref jc, - )) - | Err(PulledJobResultToJobErr::ErrorWhilePreprocessing( - ref jc, - ))) => { - if let Err(err) = - job_completed_tx.send_job(jc.clone(), true).await - { - let e_fmt = match e { - Ok(_) => "unknown error".to_owned(), - Err(e) => e.to_string(), - }; - - tracing::error!("An error occurred while sending job completed ({e_fmt}): {:#?}", err) + Ok(j) => Ok(j.map(|job| NextJob::Sql { flow_runners: None, job })), + Err(PulledJobResultToJobErr::MissingConcurrencyKey(jc)) + | Err(PulledJobResultToJobErr::ErrorWhilePreprocessing(jc)) => { + if let Err(err) = job_completed_tx.send_job(jc, true).await { + tracing::error!( + "An error occurred while sending job completed: {:#?}", + err + ) } Ok(None) } @@ -1874,7 +1897,10 @@ pub async fn run_worker( tracing::debug!(worker = %worker_name, hostname = %hostname, "started handling of job {}", job.id); - if matches!(job.kind, JobKind::Script | JobKind::Preview) { + if matches!( + job.kind, + JobKind::Script | JobKind::Preview | JobKind::FlowScript + ) { if !dedicated_workers.is_empty() { let key_o = if is_flow_worker { job.flow_step_id.as_ref().map(|x| x.to_string()) @@ -1883,8 +1909,12 @@ pub async fn run_worker( }; if let Some(key) = key_o { if let Some(dedicated_worker_tx) = dedicated_workers.get(&key) { - if let Err(e) = dedicated_worker_tx.send(Arc::new(job.job())).await - { + let dedicated_job = DedicatedWorkerJob { + job: Arc::new(job.job()), + flow_runners: None, + done_tx: None, + }; + if let Err(e) = dedicated_worker_tx.send(dedicated_job).await { tracing::info!("failed to send jobs to dedicated workers. Likely dedicated worker has been shut down. This is normal: {e:?}"); } @@ -1898,6 +1928,77 @@ pub async fn run_worker( } } } + + // Extract flow_runners early to use in both dedicated workers and flow runners + let flow_runners = match &job { + NextJob::Sql { flow_runners, .. } => flow_runners.clone(), + NextJob::Http(_) => None, + }; + + if let Some(flow_runners) = flow_runners { + let key_o = job.flow_step_id.as_ref().map(|x| x.to_string()); + if let Some(key) = key_o { + if let Some(flow_runner_tx) = flow_runners.runners.get(&key) { + tracing::info!( + "sending job {} to flow runner step {}", + job.id, + key + ); + let (done_tx, done_rx) = tokio::sync::oneshot::channel::<()>(); + let flow_runners = flow_runners.clone(); + + let job = job.job(); + + let dedicated_job = DedicatedWorkerJob { + job: Arc::new(job.clone()), + flow_runners: Some(flow_runners), + done_tx: Some(done_tx), + }; + + if let Err(e) = flow_runner_tx.send(dedicated_job).await { + let token = match &conn { + Connection::Sql(db) => { + windmill_queue::jobs::create_token(db, &job, None).await + } + _ => "".to_string(), + }; + handle_all_job_kind_error( + &conn, + &AuthedClient::new( + base_internal_url.to_owned(), + job.workspace_id.clone(), + token, + None, + ), + MiniCompletedJob::from(job), + error::Error::InternalErr(format!( + "failed to send jobs to flow runners: {e:?}" + )), + Some(&same_worker_tx), + &worker_dir, + &worker_name, + job_completed_tx.clone(), + &killpill_rx, + #[cfg(feature = "benchmark")] + &mut bench, + ) + .await; + } else { + if let Err(err) = done_rx.await { + tracing::error!("Flow runner done channel has been dropped without being received: {err:?}"); + } + } + + #[cfg(feature = "benchmark")] + { + add_time!(bench, "sent to flow runner"); + infos.add_iter(bench, true); + } + + continue; + } + } + } } if matches!(job.kind, JobKind::Noop) { @@ -1917,6 +2018,8 @@ pub async fn run_worker( duration: None, has_stream: Some(false), from_cache: None, + flow_runners: None, + done_tx: None, }, true, ) @@ -2048,6 +2151,7 @@ pub async fn run_worker( parent_runnable_path, token, precomputed_agent_info: precomputed_bundle, + flow_runners, } = extract_job_and_perms(job, &conn).await; let authed_client = AuthedClient::new( @@ -2059,7 +2163,7 @@ pub async fn run_worker( let arc_job = Arc::new(job); - let span = create_span(&arc_job, &worker_name, hostname); + let span = create_span_with_name(&arc_job, &worker_name, Some(hostname), "job"); let job_result = handle_queued_job( arc_job.clone(), @@ -2079,6 +2183,7 @@ pub async fn run_worker( &mut occupancy_metrics, &mut killpill_rx2, precomputed_bundle, + flow_runners, #[cfg(feature = "benchmark")] &mut bench, ) @@ -2138,6 +2243,7 @@ pub async fn run_worker( &worker_dir, &worker_name, job_completed_tx.clone(), + &killpill_rx, #[cfg(feature = "benchmark")] &mut bench, ) @@ -2302,7 +2408,7 @@ async fn queue_init_bash_maybe<'c>( }; if let Some((uuid, content)) = uuid_content { same_worker_tx - .send(SameWorkerPayload { job_id: uuid, recoverable: false }) + .send(SameWorkerPayload { job_id: uuid, recoverable: false, flow_runners: None }) .await .map_err(to_anyhow)?; tracing::info!("Creating initial job {uuid} from initial script script: {content}"); @@ -2420,7 +2526,7 @@ async fn queue_periodic_script_bash_maybe<'c>( }; same_worker_tx - .send(SameWorkerPayload { job_id: uuid, recoverable: false }) + .send(SameWorkerPayload { job_id: uuid, recoverable: false, flow_runners: None }) .await .map_err(to_anyhow)?; tracing::info!("Creating periodic script job {uuid} from periodic script: {content}"); @@ -2432,7 +2538,6 @@ pub struct SendResult { pub time: Instant, } -#[derive(Clone)] pub enum SendResultPayload { JobCompleted(JobCompleted), UpdateFlow(UpdateFlow), @@ -2519,6 +2624,7 @@ pub async fn handle_queued_job( occupancy_metrics: &mut OccupancyMetrics, killpill_rx: &mut tokio::sync::broadcast::Receiver<()>, precomputed_agent_info: Option, + flow_runners: Option>, #[cfg(feature = "benchmark")] _bench: &mut BenchmarkIter, ) -> windmill_common::error::Result { // Extract the active span from the context @@ -2646,6 +2752,8 @@ pub async fn handle_queued_job( duration: None, has_stream: Some(false), from_cache: Some(true), + flow_runners: None, + done_tx: None, }, true, ) @@ -2681,6 +2789,8 @@ pub async fn handle_queued_job( worker_dir, job_completed_tx.clone(), worker_name, + flow_runners, + &killpill_rx, )) .warn_after_seconds(10) .await?; @@ -2911,6 +3021,7 @@ pub async fn handle_queued_job( conn, Some(started.elapsed().as_millis() as i64), has_stream, + flow_runners, ) .await } diff --git a/backend/windmill-worker/src/worker_flow.rs b/backend/windmill-worker/src/worker_flow.rs index f0bc4cfc1e..dd45d3b786 100644 --- a/backend/windmill-worker/src/worker_flow.rs +++ b/backend/windmill-worker/src/worker_flow.rs @@ -64,7 +64,7 @@ use windmill_common::{ use windmill_queue::schedule::get_schedule_opt; use windmill_queue::{ add_completed_job, add_completed_job_error, append_logs, get_mini_pulled_job, - handle_maybe_scheduled_job, insert_concurrency_key, interpolate_args, CanceledBy, + handle_maybe_scheduled_job, insert_concurrency_key, interpolate_args, CanceledBy, FlowRunners, MiniCompletedJob, MiniPulledJob, PushArgs, PushIsolationLevel, SameWorkerPayload, WrappedError, }; @@ -88,6 +88,8 @@ pub async fn update_flow_status_after_job_completion( stop_early_override: Option, worker_name: &str, job_completed_tx: JobCompletedSender, + flow_runners: Option>, + killpill_rx: &tokio::sync::broadcast::Receiver<()>, #[cfg(feature = "benchmark")] bench: &mut BenchmarkIter, ) -> error::Result>> { // this is manual tailrecursion because async_recursion blows up the stack @@ -121,6 +123,8 @@ pub async fn update_flow_status_after_job_completion( rec.has_triggered_error_handler, worker_name, job_completed_tx.clone(), + flow_runners.clone(), + killpill_rx, #[cfg(feature = "benchmark")] bench, )) @@ -147,6 +151,8 @@ pub async fn update_flow_status_after_job_completion( rec.has_triggered_error_handler, worker_name, job_completed_tx.clone(), + flow_runners.clone(), + killpill_rx, #[cfg(feature = "benchmark")] bench, )) @@ -285,6 +291,8 @@ pub async fn update_flow_status_after_job_completion_internal( has_triggered_error_handler: bool, worker_name: &str, job_completed_tx: JobCompletedSender, + flow_runners: Option>, + killpill_rx: &tokio::sync::broadcast::Receiver<()>, #[cfg(feature = "benchmark")] bench: &mut BenchmarkIter, ) -> error::Result { let mut has_triggered_error_handler = has_triggered_error_handler; @@ -434,7 +442,6 @@ pub async fn update_flow_status_after_job_completion_internal( args })); - let from_result_to_args = |args: &Result>, sqlx::Error>| { let args = args .as_ref() @@ -473,7 +480,7 @@ pub async fn update_flow_status_after_job_completion_internal( _ => None, }; let args = from_result_to_args(args.as_ref().await.get_ref())?; - + compute_bool_from_expr( &expr, Marc::new(args), @@ -742,7 +749,6 @@ pub async fn update_flow_status_after_job_completion_internal( &mut stop_early_err_msg, &mut nresult, args, - ) .await?; } @@ -1565,6 +1571,8 @@ pub async fn update_flow_status_after_job_completion_internal( worker_dir, job_completed_tx, worker_name, + flow_runners, + &killpill_rx, )) .warn_after_seconds(10) .await @@ -2044,6 +2052,8 @@ pub async fn handle_flow( worker_dir: &str, job_completed_tx: JobCompletedSender, worker_name: &str, + flow_runners: Option>, + killpill_rx: &tokio::sync::broadcast::Receiver<()>, ) -> anyhow::Result<()> { let flow = flow_data.value(); let status = flow_job @@ -2101,6 +2111,9 @@ pub async fn handle_flow( same_worker_tx, worker_dir, worker_name, + flow_runners.clone(), + job_completed_tx.clone(), + &killpill_rx, )) .warn_after_seconds(10) .await?; @@ -2185,6 +2198,7 @@ struct PushNextFlowJobRec { flow_job: Arc, status: FlowStatus, } + // #[async_recursion] // #[instrument(level = "trace", skip_all)] async fn push_next_flow_job( @@ -2197,6 +2211,9 @@ async fn push_next_flow_job( same_worker_tx: &SameWorkerSender, worker_dir: &str, worker_name: &str, + flow_runners: Option>, + job_completed_tx: JobCompletedSender, + killpill_rx: &tokio::sync::broadcast::Receiver<()>, ) -> error::Result { let job_root = flow_job .flow_innermost_root_job @@ -3028,6 +3045,22 @@ async fn push_next_flow_job( } }; + // only start runners if we're not already in a squash for loop + let start_runners = flow_runners.is_none() + && matches!( + next_status, + NextStatus::NextLoopIteration { start_runners: true, .. } + ); + + let do_not_pass_runners = matches!(next_status, NextStatus::NextStep { .. }) + && flow_runners + .as_ref() + .is_some_and(|fr| fr.job_id == flow_job.id); + + let continue_with_runners = (start_runners || (flow_runners.is_some() && !do_not_pass_runners)) + && module.suspend.is_none() + && module.sleep.is_none(); + // Also check `flow_job.same_worker` for [`JobKind::Flow`] jobs as it's no // more reflected to the flow value on push. let job_same_worker = flow_job.same_worker @@ -3083,6 +3116,7 @@ async fn push_next_flow_job( NextStatus::NextLoopIteration { next: ForloopNextIteration { new_args, .. }, simple_input_transforms, + .. } => { let mut args = if let Ok(args) = nargs { args.as_ref().clone() @@ -3300,7 +3334,7 @@ async fn push_next_flow_job( Some(module.id.clone()), new_job_priority_override, job_perms.as_ref(), - false, + continue_with_runners, None, None, None, @@ -3308,7 +3342,7 @@ async fn push_next_flow_job( .warn_after_seconds(2) .await?; - if continue_on_same_worker { + if continue_on_same_worker || continue_with_runners { let _ = sqlx::query!( "UPDATE v2_job_queue SET worker = $2 WHERE id = $1", uuid, @@ -3583,7 +3617,7 @@ async fn push_next_flow_job( .warn_after_seconds(3) .await?; - if continue_on_same_worker { + if continue_on_same_worker || continue_with_runners { if !is_one_uuid { return Err(Error::BadRequest( "Cannot continue on same worker with multiple jobs, parallel cannot be used in conjunction with same_worker".to_string(), @@ -3593,9 +3627,43 @@ async fn push_next_flow_job( tx.commit().warn_after_seconds(3).await?; tracing::info!(id = %flow_job.id, root_id = %job_root, "all next flow jobs pushed: {uuids:?}"); - if continue_on_same_worker { + if continue_on_same_worker || continue_with_runners { + let flow_runners = if start_runners { + tracing::info!(id = %flow_job.id, "starting flow runners for module {}", module.id); + let (new_flow_runners, new_flow_runner_handles) = + crate::dedicated_worker_oss::spawn_flow_module_runners( + &flow_job, + module, + flow.failure_module.as_ref(), + &killpill_rx, + db, + worker_dir, + &client.base_internal_url, + worker_name, + &job_completed_tx, + ) + .await + .with_context(|| { + format!( + "failed to spawn flow module runners for job: {}", + flow_job.id + ) + })?; + + let flow_runners = FlowRunners { + runners: new_flow_runners, + handles: new_flow_runner_handles, + job_id: flow_job.id, + }; + Some(Arc::new(flow_runners)) + } else if !do_not_pass_runners { + flow_runners + } else { + None + }; + same_worker_tx - .send(SameWorkerPayload { job_id: first_uuid, recoverable: true }) + .send(SameWorkerPayload { job_id: first_uuid, recoverable: true, flow_runners }) .warn_after_seconds(3) .await .map_err(to_anyhow)?; @@ -3702,6 +3770,7 @@ enum NextStatus { NextLoopIteration { next: ForloopNextIteration, simple_input_transforms: Option>, + start_runners: bool, }, AllFlowJobs { branchall: Option, @@ -3943,7 +4012,7 @@ async fn compute_next_flow_transform( NextStatus::NextStep, )) } - FlowModuleValue::WhileloopFlow { modules, modules_node, .. } => { + FlowModuleValue::WhileloopFlow { modules, modules_node, squash, .. } => { // if it's a simple single step flow, we will collapse it as an optimization and need to pass flow_input as an arg let is_simple = is_simple_modules(&modules, flow.failure_module.as_ref()); let (flow_jobs, flow_jobs_success, flow_jobs_duration) = match status_module { @@ -3960,6 +4029,8 @@ async fn compute_next_flow_transform( _ => (vec![], Some(vec![]), Some(FlowJobsDuration::new(0))), }; let next_loop_idx = flow_jobs.len(); + + let start_runners = next_loop_idx == 0 && squash.unwrap_or(false); next_loop_iteration( flow, status, @@ -3982,11 +4053,14 @@ async fn compute_next_flow_transform( db, module, delete_after_use, + start_runners, ) .await } /* forloop modules are expected set `iter: { value: Value, index: usize }` as job arguments */ - FlowModuleValue::ForloopFlow { modules, modules_node, iterator, parallel, .. } => { + FlowModuleValue::ForloopFlow { + modules, modules_node, iterator, parallel, squash, .. + } => { // if it's a simple single step flow, we will collapse it as an optimization and need to pass flow_input as an arg let is_simple = !matches!(flow_job.kind, JobKind::FlowPreview) && !parallel @@ -4025,6 +4099,7 @@ async fn compute_next_flow_transform( Ok(NextFlowTransform::EmptyInnerFlows { branch_chosen: None }) } ForLoopStatus::NextIteration(ns) => { + let start_runners = ns.index == 0 && squash.unwrap_or(false); next_loop_iteration( flow, status, @@ -4036,6 +4111,7 @@ async fn compute_next_flow_transform( db, module, delete_after_use, + start_runners, ) .await } @@ -4300,6 +4376,7 @@ async fn next_loop_iteration( db: &sqlx::Pool, module: &FlowModule, delete_after_use: bool, + start_runners: bool, ) -> Result { let inner_path = || format!("{}/loop-{}", flow_job.runnable_path(), ns.index); if is_simple { @@ -4317,7 +4394,7 @@ async fn next_loop_iteration( ContinuePayload::SingleJob( payload_from_simple_module(value, db, flow_job, module, inner_path()).await?, ), - NextStatus::NextLoopIteration { next: ns, simple_input_transforms }, + NextStatus::NextLoopIteration { next: ns, simple_input_transforms, start_runners }, )); } @@ -4341,7 +4418,7 @@ async fn next_loop_iteration( timeout: None, on_behalf_of: None, }), - NextStatus::NextLoopIteration { next: ns, simple_input_transforms: None }, + NextStatus::NextLoopIteration { next: ns, simple_input_transforms: None, start_runners }, )) } diff --git a/backend/windmill-worker/src/worker_lockfiles.rs b/backend/windmill-worker/src/worker_lockfiles.rs index 824a45c661..4ec0fa648e 100644 --- a/backend/windmill-worker/src/worker_lockfiles.rs +++ b/backend/windmill-worker/src/worker_lockfiles.rs @@ -1193,6 +1193,7 @@ async fn lock_modules<'c>( skip_failures, parallel, parallelism, + squash, } => { let nmodules; (nmodules, tx, nmodified_ids, nerrors) = Box::pin(lock_modules( @@ -1222,6 +1223,7 @@ async fn lock_modules<'c>( skip_failures, parallel, parallelism, + squash, } .into() } @@ -1258,7 +1260,7 @@ async fn lock_modules<'c>( } e.value = FlowModuleValue::BranchAll { branches: nbranches, parallel }.into() } - FlowModuleValue::WhileloopFlow { modules, modules_node, skip_failures } => { + FlowModuleValue::WhileloopFlow { modules, modules_node, skip_failures, squash } => { let nmodules; (nmodules, tx, nmodified_ids, nerrors) = Box::pin(lock_modules( modules, @@ -1284,6 +1286,7 @@ async fn lock_modules<'c>( modules: nmodules, modules_node, skip_failures, + squash, } .into() } diff --git a/frontend/src/lib/components/FlowGraphViewerStep.svelte b/frontend/src/lib/components/FlowGraphViewerStep.svelte index 6fcb5c902c..07ad7756c0 100644 --- a/frontend/src/lib/components/FlowGraphViewerStep.svelte +++ b/frontend/src/lib/components/FlowGraphViewerStep.svelte @@ -121,6 +121,7 @@ {:else if stepDetail.value.type == 'forloopflow'} For loop {#if stepDetail.value.parallel}(parallel){/if} {#if stepDetail.value.skip_failures}(skip failures){/if} + {#if stepDetail.value.squash}(squash){/if} {:else if stepDetail.value.type == 'branchall'} Run all branches {#if stepDetail.value.parallel}(parallel){/if} {:else if stepDetail.value.type == 'branchone'} @@ -128,7 +129,8 @@ {:else if stepDetail.value.type == 'flow'} Inner flow {:else if stepDetail.value.type == 'whileloopflow'} - While loop + While loop {#if stepDetail.value.skip_failures}(skip failures){/if} + {#if stepDetail.value.squash}(squash){/if} {:else if stepDetail.id === 'failure'} Error handler {:else if stepDetail.id === 'preprocessor'} diff --git a/frontend/src/lib/components/flows/content/FlowLoop.svelte b/frontend/src/lib/components/flows/content/FlowLoop.svelte index 50e7bd2a82..4169f49f34 100644 --- a/frontend/src/lib/components/flows/content/FlowLoop.svelte +++ b/frontend/src/lib/components/flows/content/FlowLoop.svelte @@ -32,6 +32,7 @@ import { slide } from 'svelte/transition' import ToggleButtonGroup from '$lib/components/common/toggleButton-v2/ToggleButtonGroup.svelte' import ToggleButton from '$lib/components/common/toggleButton-v2/ToggleButton.svelte' + import Badge from '$lib/components/common/badge/Badge.svelte' const { previewArgs, flowStateStore, flowStore, currentEditor } = getContext('FlowEditorContext') @@ -186,6 +187,33 @@ class="whitespace-nowrap" />
+
+
Squash + + Beta + This can result in unexpected behavior, use at your own risk for now.
+ Squashing a for loop runs all iterations on the same worker, using a single runner + per step for the entire loop. This eliminates cold starts between iterations for supported + languages (Bun, Deno, and Python). +
+
+
+ { + ;(mod.value as ForloopFlow).squash = detail + }} + options={{ + right: 'Squash' + }} + class="whitespace-nowrap" + disabled={mod.value.parallel} + /> +
Run in parallel
diff --git a/frontend/src/lib/components/flows/content/FlowWhileLoop.svelte b/frontend/src/lib/components/flows/content/FlowWhileLoop.svelte index 81bf6c4c11..6983f7c1c6 100644 --- a/frontend/src/lib/components/flows/content/FlowWhileLoop.svelte +++ b/frontend/src/lib/components/flows/content/FlowWhileLoop.svelte @@ -14,12 +14,13 @@ import FlowModuleSleep from './FlowModuleSleep.svelte' import FlowModuleMock from './FlowModuleMock.svelte' import { Play } from 'lucide-svelte' - import type { FlowModule, Job } from '$lib/gen' + import type { FlowModule, Job, WhileloopFlow } from '$lib/gen' import FlowLoopIterationPreview from '$lib/components/FlowLoopIterationPreview.svelte' import FlowModuleDeleteAfterUse from './FlowModuleDeleteAfterUse.svelte' import FlowModuleSkip from './FlowModuleSkip.svelte' import TabsV2 from '$lib/components/common/tabs/TabsV2.svelte' import { useUiIntent } from '$lib/components/copilot/chat/flow/useUiIntent' + import Badge from '$lib/components/common/badge/Badge.svelte' const { flowStateStore } = getContext('FlowEditorContext') @@ -86,7 +87,7 @@ {#if mod.value.type === 'whileloopflow'}
-
+
Skip failures
+
+
Squash + + Beta + This can result in unexpected behavior, use at your own risk for now.
+ Squashing a for loop runs all iterations on the same worker, using a single runner + per step for the entire loop. This eliminates cold starts between iterations for supported + languages (Bun, Deno, and Python). +
+
+
+ { + ;(mod.value as WhileloopFlow).squash = detail + }} + options={{ + right: 'Squash' + }} + class="whitespace-nowrap" + /> +
diff --git a/frontend/src/lib/components/flows/map/MapItem.svelte b/frontend/src/lib/components/flows/map/MapItem.svelte index 08d88f26aa..4e8d6302a2 100644 --- a/frontend/src/lib/components/flows/map/MapItem.svelte +++ b/frontend/src/lib/components/flows/map/MapItem.svelte @@ -164,7 +164,7 @@ mod.summary || (mod.value.type == 'forloopflow' ? 'For loop' : 'While loop') } ${mod.value.parallel ? '(parallel)' : ''} ${ mod.value.skip_failures ? '(skip failures)' : '' - }`} + } ${mod.value.squash ? '(squash)' : ''}`} id={mod.id} on:changeId on:move diff --git a/openflow.openapi.yaml b/openflow.openapi.yaml index b724c0acc9..3fcf48b801 100644 --- a/openflow.openapi.yaml +++ b/openflow.openapi.yaml @@ -446,6 +446,8 @@ components: type: boolean parallelism: $ref: "#/components/schemas/InputTransform" + squash: + type: boolean required: - modules - iterator @@ -469,6 +471,8 @@ components: type: boolean parallelism: $ref: "#/components/schemas/InputTransform" + squash: + type: boolean required: - modules - skip_failures From f56f3b0fe6185167e9c4c8541ddefba01c509b8d Mon Sep 17 00:00:00 2001 From: wendrul <53628737+wendrul@users.noreply.github.com> Date: Tue, 25 Nov 2025 18:26:05 +0100 Subject: [PATCH 26/49] fix: workspace forks shouldn't inherit promotion mode repo (#7223) * fix: workspace forks shouldn't inherit promotion mode repo * fix: git sync: don't default to main when talking about target branches in git --- backend/windmill-api/src/workspaces.rs | 9 +++++++-- .../src/lib/components/git_sync/DetectionFlow.svelte | 2 +- .../lib/components/git_sync/GitSyncContext.svelte.ts | 10 ++++++---- .../lib/components/git_sync/GitSyncModeDisplay.svelte | 9 ++++++--- .../components/git_sync/GitSyncRepositoryCard.svelte | 7 ++++--- 5 files changed, 24 insertions(+), 13 deletions(-) diff --git a/backend/windmill-api/src/workspaces.rs b/backend/windmill-api/src/workspaces.rs index cea6c2ba5c..cd624e7c9d 100644 --- a/backend/windmill-api/src/workspaces.rs +++ b/backend/windmill-api/src/workspaces.rs @@ -2566,9 +2566,14 @@ async fn update_workspace_settings( WorkspaceGitSyncSettings::default() }; - // We only keep the first git sync repo, since it is considered the main one + // We only keep the first git sync repo that is sync mode (use_individual_branch = false), since it is considered the main one // Context: see WIN-1559 - git_sync_settings.repositories.truncate(1); + git_sync_settings.repositories = git_sync_settings + .repositories + .into_iter() + .filter(|r| !r.use_individual_branch.unwrap_or(false)) + .take(1) + .collect(); let serialized_config = serde_json::to_value::(git_sync_settings) .map_err(|err| Error::internal_err(err.to_string()))?; diff --git a/frontend/src/lib/components/git_sync/DetectionFlow.svelte b/frontend/src/lib/components/git_sync/DetectionFlow.svelte index 817b9942f7..c4b25c8401 100644 --- a/frontend/src/lib/components/git_sync/DetectionFlow.svelte +++ b/frontend/src/lib/components/git_sync/DetectionFlow.svelte @@ -12,7 +12,7 @@ const gitSyncContext = getGitSyncContext() const repo = $derived(gitSyncContext.getRepository(idx)) - let targetBranch = $state('main') + let targetBranch = $state(undefined) // Update target branch when repository changes $effect(() => { diff --git a/frontend/src/lib/components/git_sync/GitSyncContext.svelte.ts b/frontend/src/lib/components/git_sync/GitSyncContext.svelte.ts index 33c2f2fa5c..33b299cba0 100644 --- a/frontend/src/lib/components/git_sync/GitSyncContext.svelte.ts +++ b/frontend/src/lib/components/git_sync/GitSyncContext.svelte.ts @@ -664,12 +664,13 @@ export function createGitSyncContext(workspace: string) { } // Helper to get target branch from git resource - async function getTargetBranch(repo: GitSyncRepository): Promise { + async function getTargetBranch(repo: GitSyncRepository): Promise { if (!repo.git_repo_resource_path) { - return 'main' + return undefined } if (repo._targetBranch) { + if (repo._targetBranch === '') return undefined return repo._targetBranch } @@ -681,14 +682,15 @@ export function createGitSyncContext(workspace: string) { // Extract branch from git resource value const resourceValue = resource.value as any - const targetBranch = resourceValue?.branch || 'main' + const targetBranch = resourceValue?.branch // Cache the result repo._targetBranch = targetBranch + if (targetBranch === '') return undefined return targetBranch } catch (error) { console.warn('Failed to fetch git resource for branch info:', error) - return 'main' + return undefined } } diff --git a/frontend/src/lib/components/git_sync/GitSyncModeDisplay.svelte b/frontend/src/lib/components/git_sync/GitSyncModeDisplay.svelte index 599eb08f4a..94c77a5974 100644 --- a/frontend/src/lib/components/git_sync/GitSyncModeDisplay.svelte +++ b/frontend/src/lib/components/git_sync/GitSyncModeDisplay.svelte @@ -3,7 +3,7 @@ let { mode, targetBranch, repository } = $props<{ mode?: 'sync' | 'promotion' | null - targetBranch: string + targetBranch: string | undefined repository?: GitSyncRepository | null }>() @@ -11,12 +11,15 @@
{#if mode === 'promotion'}
Promotion: Creating branches whose promotion target is {targetBranch}
Promotion: Creating branches whose promotion target is {targetBranch? `'${targetBranch}'` : + "the repo's default branch"}
{#if repository?.group_by_folder}
Grouped by folder
{/if} + {:else if targetBranch} +
Sync: Syncing back to branch '{targetBranch}'
{:else} -
Sync: Syncing back to branch {targetBranch}
+
Sync: Syncing back to the repo's default branch
{/if}
diff --git a/frontend/src/lib/components/git_sync/GitSyncRepositoryCard.svelte b/frontend/src/lib/components/git_sync/GitSyncRepositoryCard.svelte index c5700622d4..6d8bd34919 100644 --- a/frontend/src/lib/components/git_sync/GitSyncRepositoryCard.svelte +++ b/frontend/src/lib/components/git_sync/GitSyncRepositoryCard.svelte @@ -50,7 +50,7 @@ const validation = $derived(idx !== null ? gitSyncContext.getValidation(idx) : null) const gitSyncTestJob = $derived(idx !== null ? gitSyncContext.gitSyncTestJobs?.[idx] : null) let confirmingDelete = $state(false) - let targetBranch = $state('main') // Default to main, will be updated when resource is available + let targetBranch = $state(undefined) // Default to main, will be updated when resource is available // Update target branch when repository changes $effect(() => { @@ -108,12 +108,13 @@ ) // Determine display description based on variant and mode + const targetOrDefaultBranch = $derived(targetBranch ? `'${targetBranch}'` : 'repo\'s default' ) const displayDescription = $derived( variant === 'primary-sync' || variant === 'primary-promotion' ? mode === 'sync' - ? `Changes will be committed directly to the ${targetBranch} branch` + ? `Changes will be committed directly to the ${targetOrDefaultBranch} branch` : mode === 'promotion' - ? `Changes will be made to new branches whose promotion target is ${targetBranch}` + ? `Changes will be made to new branches whose promotion target is the ${targetOrDefaultBranch} branch` : null : null ) From f8a0bfc20d1ccb6ce36e3adf4188533470270bdb Mon Sep 17 00:00:00 2001 From: hugocasa Date: Tue, 25 Nov 2025 19:22:34 +0100 Subject: [PATCH 27/49] feat(frontend): move app reports script to hub (#7227) --- .../apps/editor/AppReportsDrawerInner.svelte | 72 +------------------ frontend/src/lib/hub.ts | 1 + frontend/src/lib/hubPaths.json | 1 + 3 files changed, 5 insertions(+), 69 deletions(-) diff --git a/frontend/src/lib/components/apps/editor/AppReportsDrawerInner.svelte b/frontend/src/lib/components/apps/editor/AppReportsDrawerInner.svelte index 40c72b061d..3f25cca3b0 100644 --- a/frontend/src/lib/components/apps/editor/AppReportsDrawerInner.svelte +++ b/frontend/src/lib/components/apps/editor/AppReportsDrawerInner.svelte @@ -159,71 +159,6 @@ sendUserToast('App reporting disabled') } - const appPreviewScript = `import puppeteer from 'puppeteer-core'; -import dayjs from 'dayjs'; -export async function main( - app_path: string, - startup_duration = 5, - kind: 'pdf' | 'png' = 'pdf', - customWidth: null | number, - customHeight: null | number, -) { - let browser = null - try { - browser = await puppeteer.launch({ headless: true, executablePath: '/usr/bin/chromium', args: ['--no-sandbox', - '--no-zygote', - '--disable-setuid-sandbox', - '--disable-dev-shm-usage', - '--disable-gpu'] }); - const page = await browser.newPage(); - await page.setCookie({ - "name": "token", - "value": Bun.env["WM_TOKEN"], - "domain": Bun.env["BASE_URL"]?.replace(/https?:\\/\\//, '') - }) - page - .on('console', msg => - console.log(dayjs().format("HH:mm:ss") + " " + msg.type().substr(0, 3).toUpperCase() + " " + msg.text())) - .on('pageerror', ({ msg }) => console.log(dayjs().format("HH:mm:ss") + " " + msg)); - await page.setViewport({ width: 1200, height: 2000 }); - await page.goto(Bun.env["BASE_URL"] + '/apps/get/' + app_path + '?workspace=' + Bun.env["WM_WORKSPACE"] + "&hideRefreshBar=true&hideEditBtn=true"); - await page.waitForSelector("#app-content", { timeout: 20000 }) - - const elem = await page.$('#app-content'); - let width: null | number = customWidth || 1200 - let height: null | number = customHeight || (await elem.boundingBox()).height - await page.setViewport({ width, height }); - await new Promise((resolve, _) => { - setTimeout(resolve, startup_duration * 1000) - }) - try { - await page.$eval("#sidebar", el => el.remove()) - } catch {} - await page.$eval("#content", el => el.classList.remove("md:pl-12")) - await page.$$eval(".app-component-refresh-btn", els => els.forEach(el => el.remove())) - await page.$$eval(".app-table-footer-btn", els => els.forEach(el => el.remove())) - - await new Promise((resolve, _) => setTimeout(resolve, 500)) - - const screenshot = kind === "pdf" ? await page.pdf({ - printBackground: true, - width, - height - }) : await page.screenshot({ - fullPage: true, - type: "png", - captureBeyondViewport: false - }); - await browser.close(); - return Buffer.from(screenshot).toString('base64'); - } catch (err) { - if (browser) { - await browser.close(); - } - throw err; - } -}` - const notificationScripts = { discord: { path: hubPaths.discordReport, @@ -347,10 +282,9 @@ export async function main( { id: 'a', value: { - type: 'rawscript' as const, - tag: 'chromium', - content: appPreviewScript, - language: 'bun' as const, + type: 'script' as const, + tag_override: 'chromium', + path: hubPaths.appReport, input_transforms: { app_path: { expr: 'flow_input.app_path', diff --git a/frontend/src/lib/hub.ts b/frontend/src/lib/hub.ts index 13e702a2bf..78a617397b 100644 --- a/frontend/src/lib/hub.ts +++ b/frontend/src/lib/hub.ts @@ -98,6 +98,7 @@ type HubPaths = { teamsSuccessHandler: string emailErrorHandler: string cloneRepoToS3forGitRepoViewer: string + appReport: string } export const hubPaths = JSON.parse(rawHubPaths) as HubPaths diff --git a/frontend/src/lib/hubPaths.json b/frontend/src/lib/hubPaths.json index e5244469ea..f336843ffe 100644 --- a/frontend/src/lib/hubPaths.json +++ b/frontend/src/lib/hubPaths.json @@ -39,6 +39,7 @@ "slackReport": "hub/9084/slack", "discordReport": "hub/9085/discord", "smtpReport": "hub/9086/smtp", + "appReport": "hub/28076/app-report", "cloneRepoToS3forGitRepoViewer_0": "hub/19825/clone_repo_and_upload_to_instance_storage", "cloneRepoToS3forGitRepoViewer": "hub/19827/clone_repo_and_upload_to_instance_storage" } From cba9552e2fea38f527fadc6af217248dfbd48011 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 26 Nov 2025 07:37:05 +0000 Subject: [PATCH 28/49] cookie_domain impacts csrf cookie name --- backend/ee-repo-ref.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt index bc6e0fecec..a73d73713d 100644 --- a/backend/ee-repo-ref.txt +++ b/backend/ee-repo-ref.txt @@ -1 +1 @@ -f9434efa127dadb0a04c87d6fc368cc289d93501 +1191204c415e86d429ec0ea9d67c0a9c94af7894 \ No newline at end of file From 8d00730c90fa49a1488c07489ec5d7ae9d19e1c5 Mon Sep 17 00:00:00 2001 From: Diego Imbert <70353967+diegoimbert@users.noreply.github.com> Date: Wed, 26 Nov 2025 10:56:46 +0100 Subject: [PATCH 29/49] fix $app.darkMode not set (#7230) --- frontend/src/lib/components/apps/editor/AppEditorHeader.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte b/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte index 38da3ab9c1..82a08668cc 100644 --- a/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte +++ b/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte @@ -676,6 +676,7 @@ document.documentElement.classList.remove('dark') } $darkMode = newDarkMode ?? globalDarkMode + $app.darkMode = newDarkMode } let priorDarkMode = document.documentElement.classList.contains('dark') From c0a92f83b00341b0eee36e2dc88d301a75b6fdfb Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 26 Nov 2025 11:00:59 +0000 Subject: [PATCH 30/49] fix(cli): improve back-compatibility with app.yaml with policy still present --- cli/src/commands/app/apps.ts | 9 +++++++-- cli/src/commands/sync/sync.ts | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/cli/src/commands/app/apps.ts b/cli/src/commands/app/apps.ts index 0fefd7afbc..9ed1c0d158 100644 --- a/cli/src/commands/app/apps.ts +++ b/cli/src/commands/app/apps.ts @@ -25,6 +25,9 @@ export interface AppFile { const alreadySynced: string[] = []; +export function isExecutionModeAnonymous(app: any) { + return app?.["policy"]?.["execution_mode"] == "anonymous"; +} export async function pushApp( workspace: string, remotePath: string, @@ -46,7 +49,7 @@ export async function pushApp( } catch { //ignore } - if (app?.["policy"]?.["execution_mode"] == "anonymous") { + if (isExecutionModeAnonymous(app)) { app.public = true; } // console.log(app); @@ -85,7 +88,9 @@ export async function pushApp( } replaceInlineScripts(localApp.value); - await generatingPolicy(localApp, remotePath, localApp?.["public"] ?? false); + // console.log(localApp, localApp?.["policy"]); + await generatingPolicy(localApp, remotePath, localApp?.["public"] ?? (localApp.policy ? isExecutionModeAnonymous(localApp) : false)); + // console.log(localApp, localApp?.["policy"]); if (app) { if (isSuperset(localApp, app)) { log.info(colors.green(`App ${remotePath} is up to date`)); diff --git a/cli/src/commands/sync/sync.ts b/cli/src/commands/sync/sync.ts index d145b03490..3ea6ced18c 100644 --- a/cli/src/commands/sync/sync.ts +++ b/cli/src/commands/sync/sync.ts @@ -67,6 +67,7 @@ import { PathAssigner, } from "../../../windmill-utils-internal/src/path-utils/path-assigner.ts"; import { extractInlineScripts as extractInlineScriptsForFlows } from "../../../windmill-utils-internal/src/inline-scripts/extractor.ts"; +import { isExecutionModeAnonymous } from "../app/apps.ts"; // Merge CLI options with effective settings, preserving CLI flags as overrides function mergeCliWithEffectiveOptions< @@ -415,7 +416,7 @@ function ZipFSElement( }; } - if (app?.["policy"]?.["execution_mode"] == "anonymous") { + if (isExecutionModeAnonymous(app)) { app.public = true; } app.policy = undefined; From 8922be11a6935d200e1909f14aa410c6eff67a13 Mon Sep 17 00:00:00 2001 From: hugocasa Date: Wed, 26 Nov 2025 12:02:21 +0100 Subject: [PATCH 31/49] fix(frontend): workspace color race condition for superadmins (#7229) --- ...3cebdf8d777f48e148bcb171fa15e8bf6f098.json | 58 +++++++++++++++++++ backend/windmill-api/openapi.yaml | 16 +++++ backend/windmill-api/src/workspaces.rs | 41 ++++++++++++- frontend/src/lib/stores.ts | 28 ++++++--- 4 files changed, 131 insertions(+), 12 deletions(-) create mode 100644 backend/.sqlx/query-00c1dd0cfaf15aafdcfcabc1f123cebdf8d777f48e148bcb171fa15e8bf6f098.json diff --git a/backend/.sqlx/query-00c1dd0cfaf15aafdcfcabc1f123cebdf8d777f48e148bcb171fa15e8bf6f098.json b/backend/.sqlx/query-00c1dd0cfaf15aafdcfcabc1f123cebdf8d777f48e148bcb171fa15e8bf6f098.json new file mode 100644 index 0000000000..72942b3788 --- /dev/null +++ b/backend/.sqlx/query-00c1dd0cfaf15aafdcfcabc1f123cebdf8d777f48e148bcb171fa15e8bf6f098.json @@ -0,0 +1,58 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT\n workspace.id AS \"id!\",\n workspace.name AS \"name!\",\n workspace.owner AS \"owner!\",\n workspace.deleted AS \"deleted!\",\n workspace.premium AS \"premium!\",\n workspace_settings.color AS \"color\",\n workspace.parent_workspace_id AS \"parent_workspace_id\"\n FROM workspace\n LEFT JOIN workspace_settings ON workspace.id = workspace_settings.workspace_id\n WHERE workspace.id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id!", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "name!", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "owner!", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "deleted!", + "type_info": "Bool" + }, + { + "ordinal": 4, + "name": "premium!", + "type_info": "Bool" + }, + { + "ordinal": 5, + "name": "color", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "parent_workspace_id", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + true, + true + ] + }, + "hash": "00c1dd0cfaf15aafdcfcabc1f123cebdf8d777f48e148bcb171fa15e8bf6f098" +} diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 139c6f325a..9ce19ef33a 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -688,6 +688,22 @@ paths: schema: $ref: "#/components/schemas/UserWorkspaceList" + /w/{workspace}/workspaces/get_as_superadmin: + get: + summary: get workspace as super admin (require to be super admin) + operationId: getWorkspaceAsSuperAdmin + tags: + - workspace + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: workspace + content: + application/json: + schema: + $ref: "#/components/schemas/Workspace" + /workspaces/list_as_superadmin: get: summary: list all workspaces as super admin (require to be super admin) diff --git a/backend/windmill-api/src/workspaces.rs b/backend/windmill-api/src/workspaces.rs index cd624e7c9d..ebc53eb733 100644 --- a/backend/windmill-api/src/workspaces.rs +++ b/backend/windmill-api/src/workspaces.rs @@ -50,7 +50,7 @@ use windmill_common::{ oauth2::WORKSPACE_SLACK_BOT_TOKEN_PATH, utils::{paginate, rd_string, require_admin, Pagination}, }; -use windmill_git_sync::{handle_fork_branch_creation, handle_deployment_metadata, DeployedObject}; +use windmill_git_sync::{handle_deployment_metadata, handle_fork_branch_creation, DeployedObject}; use windmill_worker::scoped_dependency_map::{DependencyMap, ScopedDependencyMap}; #[cfg(feature = "enterprise")] @@ -73,6 +73,7 @@ lazy_static::lazy_static! { pub fn workspaced_service() -> Router { let router = Router::new() + .route("/get_as_superadmin", get(get_workspace_as_superadmin)) .route("/list_pending_invites", get(list_pending_invites)) .route("/update", post(edit_workspace)) .route("/archive", post(archive_workspace)) @@ -163,7 +164,10 @@ pub fn workspaced_service() -> Router { post(acknowledge_all_critical_alerts), ) .route("/critical_alerts/mute", post(mute_critical_alerts)) - .route("/create_workspace_fork_branch", post(create_workspace_fork_branch)) + .route( + "/create_workspace_fork_branch", + post(create_workspace_fork_branch), + ) .route("/operator_settings", post(update_operator_settings)); #[cfg(all(feature = "stripe", feature = "enterprise"))] @@ -706,7 +710,9 @@ async fn get_slack_oauth_config( .await?; // Mask the secret if it exists - let masked_secret = settings.slack_oauth_client_secret.map(|_| "***".to_string()); + let masked_secret = settings + .slack_oauth_client_secret + .map(|_| "***".to_string()); Ok(Json(GetSlackOAuthConfigResponse { slack_oauth_client_id: settings.slack_oauth_client_id, @@ -2227,6 +2233,35 @@ async fn get_used_triggers( Ok(Json(websocket_used)) } +async fn get_workspace_as_superadmin( + authed: ApiAuthed, + Extension(db): Extension, + Path(w_id): Path, +) -> JsonResult { + require_super_admin(&db, &authed.email).await?; + let workspace = sqlx::query_as!( + Workspace, + "SELECT + workspace.id AS \"id!\", + workspace.name AS \"name!\", + workspace.owner AS \"owner!\", + workspace.deleted AS \"deleted!\", + workspace.premium AS \"premium!\", + workspace_settings.color AS \"color\", + workspace.parent_workspace_id AS \"parent_workspace_id\" + FROM workspace + LEFT JOIN workspace_settings ON workspace.id = workspace_settings.workspace_id + WHERE workspace.id = $1", + w_id + ) + .fetch_optional(&db) + .await?; + + let workspace = not_found_if_none(workspace, "workspace", w_id)?; + + Ok(Json(workspace)) +} + async fn list_workspaces_as_super_admin( authed: ApiAuthed, Extension(db): Extension, diff --git a/frontend/src/lib/stores.ts b/frontend/src/lib/stores.ts index 91ecfd1262..5632bff47a 100644 --- a/frontend/src/lib/stores.ts +++ b/frontend/src/lib/stores.ts @@ -3,9 +3,11 @@ import { derived, type Readable, writable } from 'svelte/store' import type { IntrospectionQuery } from 'graphql' import { + CancelablePromise, type OperatorSettings, type TokenResponse, type UserWorkspaceList, + type Workspace, type WorkspaceDefaultScripts, WorkspaceService } from './gen' @@ -168,32 +170,40 @@ export const instanceSettingsSelectedTab = writable('Core') export const isCriticalAlertsUIOpen = writable(false) +let getWorkspacePromise: CancelablePromise | null = null export const workspaceColor: Readable = derived( [workspaceStore, usersWorkspaceStore, superadmin], ([workspaceStore, usersWorkspaceStore, superadmin], set: (value: string | undefined) => void) => { - if (!workspaceStore) { - set(undefined) + if (!workspaceStore || !usersWorkspaceStore) { return } // First try to get the color from usersWorkspaceStore - const color = usersWorkspaceStore?.workspaces.find((w) => w.id === workspaceStore)?.color + const workspace = usersWorkspaceStore.workspaces.find((w) => w.id === workspaceStore) - if (color) { - set(color) + if (workspace) { + set(workspace.color) return } - // If not found and user is superadmin, try to get it from superadmin list + // If workspace not found and user is superadmin, get it as superadmin if (!superadmin) { set(undefined) return } - WorkspaceService.listWorkspacesAsSuperAdmin().then((workspaces) => { - const superadminColor = workspaces.find((w) => w.id === workspaceStore)?.color - set(superadminColor) + getWorkspacePromise?.cancel() + + getWorkspacePromise = WorkspaceService.getWorkspaceAsSuperAdmin({ + workspace: workspaceStore }) + + getWorkspacePromise + .then((workspace) => set(workspace.color)) + .catch((error) => { + console.error('error getting workspace as superadmin', error) + set(undefined) + }) } ) From d450ab29e16281966e318acc1629f4e52e675422 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 26 Nov 2025 12:09:36 +0100 Subject: [PATCH 32/49] chore(main): release 1.585.0 (#7222) * chore(main): release 1.585.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> --- CHANGELOG.md | 15 +++++ backend/Cargo.lock | 66 +++++++++---------- backend/Cargo.toml | 4 +- backend/windmill-api/openapi.yaml | 2 +- benchmarks/lib.ts | 2 +- cli/src/main.ts | 2 +- frontend/package-lock.json | 4 +- frontend/package.json | 2 +- lsp/Pipfile | 4 +- openflow.openapi.yaml | 2 +- .../WindmillClient/WindmillClient.psd1 | 2 +- python-client/wmill/pyproject.toml | 2 +- python-client/wmill_pg/pyproject.toml | 2 +- typescript-client/jsr.json | 2 +- typescript-client/package.json | 2 +- version.txt | 2 +- 16 files changed, 65 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d65463af95..4944da2051 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## [1.585.0](https://github.com/windmill-labs/windmill/compare/v1.584.0...v1.585.0) (2025-11-26) + + +### Features + +* flow for loop squashing ([#7107](https://github.com/windmill-labs/windmill/issues/7107)) ([93c34c6](https://github.com/windmill-labs/windmill/commit/93c34c6d769dd56da02064c0abdaa060efe0259d)) +* **frontend:** move app reports script to hub ([#7227](https://github.com/windmill-labs/windmill/issues/7227)) ([f8a0bfc](https://github.com/windmill-labs/windmill/commit/f8a0bfc20d1ccb6ce36e3adf4188533470270bdb)) + + +### Bug Fixes + +* **cli:** improve back-compatibility with app.yaml with policy still present ([c0a92f8](https://github.com/windmill-labs/windmill/commit/c0a92f83b00341b0eee36e2dc88d301a75b6fdfb)) +* **frontend:** workspace color race condition for superadmins ([#7229](https://github.com/windmill-labs/windmill/issues/7229)) ([8922be1](https://github.com/windmill-labs/windmill/commit/8922be11a6935d200e1909f14aa410c6eff67a13)) +* workspace forks shouldn't inherit promotion mode repo ([#7223](https://github.com/windmill-labs/windmill/issues/7223)) ([f56f3b0](https://github.com/windmill-labs/windmill/commit/f56f3b0fe6185167e9c4c8541ddefba01c509b8d)) + ## [1.584.0](https://github.com/windmill-labs/windmill/compare/v1.583.3...v1.584.0) (2025-11-25) diff --git a/backend/Cargo.lock b/backend/Cargo.lock index cce1bb1039..9463e75efd 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -1688,9 +1688,9 @@ dependencies = [ [[package]] name = "borsh" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce" +checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" dependencies = [ "borsh-derive", "cfg_aliases 0.2.1", @@ -1698,9 +1698,9 @@ dependencies = [ [[package]] name = "borsh-derive" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd1d3c0c2f5833f22386f252fe8ed005c7f59fdcddeef025c01b4c3b9fd9ac3" +checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" dependencies = [ "once_cell", "proc-macro-crate", @@ -14155,9 +14155,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" dependencies = [ "once_cell", "valuable", @@ -15210,7 +15210,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windmill" -version = "1.584.0" +version = "1.585.0" dependencies = [ "anyhow", "aws-sdk-config", @@ -15272,7 +15272,7 @@ dependencies = [ [[package]] name = "windmill-api" -version = "1.584.0" +version = "1.585.0" dependencies = [ "anyhow", "argon2", @@ -15393,7 +15393,7 @@ dependencies = [ [[package]] name = "windmill-api-client" -version = "1.584.0" +version = "1.585.0" dependencies = [ "base64 0.22.1", "chrono", @@ -15408,7 +15408,7 @@ dependencies = [ [[package]] name = "windmill-audit" -version = "1.584.0" +version = "1.585.0" dependencies = [ "chrono", "lazy_static", @@ -15422,7 +15422,7 @@ dependencies = [ [[package]] name = "windmill-autoscaling" -version = "1.584.0" +version = "1.585.0" dependencies = [ "anyhow", "axum", @@ -15441,7 +15441,7 @@ dependencies = [ [[package]] name = "windmill-common" -version = "1.584.0" +version = "1.585.0" dependencies = [ "anyhow", "async-recursion", @@ -15530,7 +15530,7 @@ dependencies = [ [[package]] name = "windmill-git-sync" -version = "1.584.0" +version = "1.585.0" dependencies = [ "regex", "serde", @@ -15545,7 +15545,7 @@ dependencies = [ [[package]] name = "windmill-indexer" -version = "1.584.0" +version = "1.585.0" dependencies = [ "anyhow", "bytes", @@ -15569,7 +15569,7 @@ dependencies = [ [[package]] name = "windmill-macros" -version = "1.584.0" +version = "1.585.0" dependencies = [ "itertools 0.14.0", "lazy_static", @@ -15581,7 +15581,7 @@ dependencies = [ [[package]] name = "windmill-parser" -version = "1.584.0" +version = "1.585.0" dependencies = [ "convert_case 0.6.0", "serde", @@ -15590,7 +15590,7 @@ dependencies = [ [[package]] name = "windmill-parser-bash" -version = "1.584.0" +version = "1.585.0" dependencies = [ "anyhow", "lazy_static", @@ -15602,7 +15602,7 @@ dependencies = [ [[package]] name = "windmill-parser-csharp" -version = "1.584.0" +version = "1.585.0" dependencies = [ "anyhow", "serde_json", @@ -15614,7 +15614,7 @@ dependencies = [ [[package]] name = "windmill-parser-go" -version = "1.584.0" +version = "1.585.0" dependencies = [ "anyhow", "gosyn", @@ -15626,7 +15626,7 @@ dependencies = [ [[package]] name = "windmill-parser-graphql" -version = "1.584.0" +version = "1.585.0" dependencies = [ "anyhow", "lazy_static", @@ -15638,7 +15638,7 @@ dependencies = [ [[package]] name = "windmill-parser-java" -version = "1.584.0" +version = "1.585.0" dependencies = [ "anyhow", "serde_json", @@ -15650,7 +15650,7 @@ dependencies = [ [[package]] name = "windmill-parser-nu" -version = "1.584.0" +version = "1.585.0" dependencies = [ "anyhow", "nu-parser", @@ -15661,7 +15661,7 @@ dependencies = [ [[package]] name = "windmill-parser-php" -version = "1.584.0" +version = "1.585.0" dependencies = [ "anyhow", "itertools 0.14.0", @@ -15672,7 +15672,7 @@ dependencies = [ [[package]] name = "windmill-parser-py" -version = "1.584.0" +version = "1.585.0" dependencies = [ "anyhow", "itertools 0.14.0", @@ -15684,7 +15684,7 @@ dependencies = [ [[package]] name = "windmill-parser-py-imports" -version = "1.584.0" +version = "1.585.0" dependencies = [ "anyhow", "async-recursion", @@ -15707,7 +15707,7 @@ dependencies = [ [[package]] name = "windmill-parser-ruby" -version = "1.584.0" +version = "1.585.0" dependencies = [ "anyhow", "lazy_static", @@ -15721,7 +15721,7 @@ dependencies = [ [[package]] name = "windmill-parser-rust" -version = "1.584.0" +version = "1.585.0" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -15738,7 +15738,7 @@ dependencies = [ [[package]] name = "windmill-parser-sql" -version = "1.584.0" +version = "1.585.0" dependencies = [ "anyhow", "lazy_static", @@ -15752,7 +15752,7 @@ dependencies = [ [[package]] name = "windmill-parser-ts" -version = "1.584.0" +version = "1.585.0" dependencies = [ "anyhow", "lazy_static", @@ -15770,7 +15770,7 @@ dependencies = [ [[package]] name = "windmill-parser-yaml" -version = "1.584.0" +version = "1.585.0" dependencies = [ "anyhow", "serde", @@ -15781,7 +15781,7 @@ dependencies = [ [[package]] name = "windmill-queue" -version = "1.584.0" +version = "1.585.0" dependencies = [ "anyhow", "async-recursion", @@ -15818,7 +15818,7 @@ dependencies = [ [[package]] name = "windmill-sql-datatype-parser-wasm" -version = "1.584.0" +version = "1.585.0" dependencies = [ "wasm-bindgen", "wasm-bindgen-test", @@ -15828,7 +15828,7 @@ dependencies = [ [[package]] name = "windmill-worker" -version = "1.584.0" +version = "1.585.0" dependencies = [ "anyhow", "async-once-cell", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index d533d32549..05c86807be 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "windmill" -version = "1.584.0" +version = "1.585.0" authors.workspace = true edition.workspace = true @@ -33,7 +33,7 @@ members = [ exclude = ["./windmill-duckdb-ffi-internal"] [workspace.package] -version = "1.584.0" +version = "1.585.0" authors = ["Ruben Fiszel "] edition = "2021" diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 9ce19ef33a..339df53205 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.584.0 + version: 1.585.0 title: Windmill API contact: diff --git a/benchmarks/lib.ts b/benchmarks/lib.ts index 5dc61d56a7..a936855fe2 100644 --- a/benchmarks/lib.ts +++ b/benchmarks/lib.ts @@ -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.584.0"; +export const VERSION = "v1.585.0"; export async function login(email: string, password: string): Promise { return await windmill.UserService.login({ diff --git a/cli/src/main.ts b/cli/src/main.ts index 4430410890..4f0f455522 100644 --- a/cli/src/main.ts +++ b/cli/src/main.ts @@ -68,7 +68,7 @@ export { // } // }); -export const VERSION = "1.584.0"; +export const VERSION = "1.585.0"; export const WM_FORK_PREFIX = "wm-fork"; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 14868d1907..a0527f34f7 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "windmill-components", - "version": "1.584.0", + "version": "1.585.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "windmill-components", - "version": "1.584.0", + "version": "1.585.0", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/frontend/package.json b/frontend/package.json index 58a5be9fdb..a1c0c39cbe 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "windmill-components", - "version": "1.584.0", + "version": "1.585.0", "scripts": { "dev": "vite dev", "build": "vite build", diff --git a/lsp/Pipfile b/lsp/Pipfile index a535d7580a..d3f2ccb54e 100644 --- a/lsp/Pipfile +++ b/lsp/Pipfile @@ -4,8 +4,8 @@ verify_ssl = true name = "pypi" [packages] -wmill = ">=1.584.0" -wmill_pg = ">=1.584.0" +wmill = ">=1.585.0" +wmill_pg = ">=1.585.0" sendgrid = "*" mysql-connector-python = "*" pymongo = "*" diff --git a/openflow.openapi.yaml b/openflow.openapi.yaml index 3fcf48b801..cd534f3fac 100644 --- a/openflow.openapi.yaml +++ b/openflow.openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.584.0 + version: 1.585.0 title: OpenFlow Spec contact: name: Ruben Fiszel diff --git a/powershell-client/WindmillClient/WindmillClient.psd1 b/powershell-client/WindmillClient/WindmillClient.psd1 index fa0d0e6afa..ffe3220eb2 100644 --- a/powershell-client/WindmillClient/WindmillClient.psd1 +++ b/powershell-client/WindmillClient/WindmillClient.psd1 @@ -12,7 +12,7 @@ RootModule = 'WindmillClient.psm1' # Version number of this module. - ModuleVersion = '1.584.0' + ModuleVersion = '1.585.0' # Supported PSEditions # CompatiblePSEditions = @() diff --git a/python-client/wmill/pyproject.toml b/python-client/wmill/pyproject.toml index 4a9cabadc7..73e5cf3026 100644 --- a/python-client/wmill/pyproject.toml +++ b/python-client/wmill/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill" -version = "1.584.0" +version = "1.585.0" description = "A client library for accessing Windmill server wrapping the Windmill client API" license = "Apache-2.0" homepage = "https://windmill.dev" diff --git a/python-client/wmill_pg/pyproject.toml b/python-client/wmill_pg/pyproject.toml index 7b9653640e..2732b66b10 100644 --- a/python-client/wmill_pg/pyproject.toml +++ b/python-client/wmill_pg/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill-pg" -version = "1.584.0" +version = "1.585.0" description = "An extension client for the wmill client library focused on pg" license = "Apache-2.0" homepage = "https://windmill.dev" diff --git a/typescript-client/jsr.json b/typescript-client/jsr.json index f4bfe59424..908424b59f 100644 --- a/typescript-client/jsr.json +++ b/typescript-client/jsr.json @@ -1,6 +1,6 @@ { "name": "@windmill/windmill", - "version": "1.584.0", + "version": "1.585.0", "exports": "./src/index.ts", "publish": { "exclude": ["!src", "./s3Types.ts", "./client.ts"] diff --git a/typescript-client/package.json b/typescript-client/package.json index bfe2a641b8..5afa72aaf9 100644 --- a/typescript-client/package.json +++ b/typescript-client/package.json @@ -1,7 +1,7 @@ { "name": "windmill-client", "description": "Windmill SDK client for browsers and Node.js", - "version": "1.584.0", + "version": "1.585.0", "author": "Ruben Fiszel", "license": "Apache 2.0", "devDependencies": { diff --git a/version.txt b/version.txt index a224cb38e3..30d77f1213 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.584.0 +1.585.0 From 67895abc9f08f5278cf907b8a22bfc79c2da696e Mon Sep 17 00:00:00 2001 From: HugoCasa Date: Wed, 26 Nov 2025 16:19:55 +0100 Subject: [PATCH 33/49] add option to build wasm packages for nodejs --- backend/parsers/windmill-parser-wasm/build.nu | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/backend/parsers/windmill-parser-wasm/build.nu b/backend/parsers/windmill-parser-wasm/build.nu index 7c3bf9d4a6..d954366113 100755 --- a/backend/parsers/windmill-parser-wasm/build.nu +++ b/backend/parsers/windmill-parser-wasm/build.nu @@ -65,15 +65,19 @@ const targets = [ # -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort # Build all separately -def 'main all' [ --no-opt(-n), --cli] { +def 'main all' [ --no-opt(-n), --cli, --node ] { if ($no_opt) { - if ($cli) { + if ($node) { + $targets | each { main $in.ident -n --node } + } else if ($cli) { $targets | each { main $in.ident -n --cli } } else { $targets | each { main $in.ident -n } } } else { - if ($cli) { + if ($node) { + $targets | each { main $in.ident --node } + } else if ($cli) { $targets | each { main $in.ident --cli } } else { $targets | each { main $in.ident } @@ -86,6 +90,7 @@ def main [ target: string, # Language, e.g.: py, ts, rust --no-opt(-n), # Compile in debug mode for dev --cli # Compile and place binaries in cli. + --node ] { let t = $targets | where ident == $target; @@ -97,7 +102,10 @@ def main [ mut profile = ""; mut tar = ""; - if ($cli) { + if ($node) { + $env.OUT_DIR = $"node-pkg-($t.ident)" + $tar = "nodejs" + } else if ($cli) { $env.OUT_DIR = $"../../../cli/wasm/($t.ident)" $tar = "deno" } else { From a573e6de950cc72f4951328321aecd814cb8eecb Mon Sep 17 00:00:00 2001 From: HugoCasa Date: Wed, 26 Nov 2025 17:07:50 +0100 Subject: [PATCH 34/49] chore: update cli versions in git sync scripts --- frontend/src/lib/hubPaths.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/src/lib/hubPaths.json b/frontend/src/lib/hubPaths.json index f336843ffe..80ec7eff19 100644 --- a/frontend/src/lib/hubPaths.json +++ b/frontend/src/lib/hubPaths.json @@ -16,7 +16,8 @@ "gitSync_14": "hub/19803/sync-script-to-git-repo-windmill", "gitSync_15": "hub/19816/sync-script-to-git-repo-windmill", "gitSync_16": "hub/19818/sync-script-to-git-repo-windmill", - "gitSync": "hub/28073/sync-script-to-git-repo-windmill", + "gitSync_17": "hub/28073/sync-script-to-git-repo-windmill", + "gitSync": "hub/28078/sync-script-to-git-repo-windmill", "gitSyncTest_0": "hub/9073/git-repo-test-read-write-windmill", "gitSyncTest_1": "hub/11499/git-repo-test-read-write-windmill", "gitSyncTest_2": "hub/11667/git-repo-test-read-write-windmill", @@ -25,7 +26,8 @@ "gitInitRepo_0": "hub/19787/git-sync%3A-init-repository-windmill", "gitInitRepo_1": "hub/19797/git-sync%3A-init-repository-windmill", "gitInitRepo_2": "hub/19817/git-sync%3A-init-repository-windmill", - "gitInitRepo": "hub/28072/git-sync%3A-init-repository-windmill", + "gitInitRepo_3": "hub/28072/git-sync%3A-init-repository-windmill", + "gitInitRepo": "hub/28077/git-sync%3A-init-repository-windmill", "slackErrorHandler": "hub/19741/workspace-or-schedule-error-handler-slack", "slackErrorHandler_0": "hub/9079/workspace-or-schedule-error-handler-slack", "slackErrorHandler_1": "hub/9206/workspace-or-schedule-error-handler-slack", From 3f9dae39c9eed4f338cec82d07af442f43612081 Mon Sep 17 00:00:00 2001 From: hugocasa Date: Wed, 26 Nov 2025 20:48:13 +0100 Subject: [PATCH 35/49] fix build (#7233) * fix build * Update ee-repo-ref.txt --------- Co-authored-by: windmill-internal-app[bot] --- backend/ee-repo-ref.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt index a73d73713d..2a3de2d939 100644 --- a/backend/ee-repo-ref.txt +++ b/backend/ee-repo-ref.txt @@ -1 +1 @@ -1191204c415e86d429ec0ea9d67c0a9c94af7894 \ No newline at end of file +99828e4a3a27af428a45d1e6e65092ca15082b1e From faad0006b430262f1ab6b749ccf60a6860a54405 Mon Sep 17 00:00:00 2001 From: wendrul <53628737+wendrul@users.noreply.github.com> Date: Wed, 26 Nov 2025 20:49:12 +0100 Subject: [PATCH 36/49] fix: clone group members on workspace forks (#7232) * Clone usr_to_group table on windmill fork * Update sqlx --- ...3ca8889ea164d9cfb4962997650d922d82f000df9.json | 15 +++++++++++++++ ...4ff3153c43903f929ae5d62fbba12610f89c36d55.json | 2 +- backend/windmill-api/src/workspaces.rs | 11 +++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 backend/.sqlx/query-3f9004b5542afaaef40942a3ca8889ea164d9cfb4962997650d922d82f000df9.json diff --git a/backend/.sqlx/query-3f9004b5542afaaef40942a3ca8889ea164d9cfb4962997650d922d82f000df9.json b/backend/.sqlx/query-3f9004b5542afaaef40942a3ca8889ea164d9cfb4962997650d922d82f000df9.json new file mode 100644 index 0000000000..094331bb82 --- /dev/null +++ b/backend/.sqlx/query-3f9004b5542afaaef40942a3ca8889ea164d9cfb4962997650d922d82f000df9.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO usr_to_group (workspace_id, group_, usr)\n SELECT $2, group_, usr\n FROM usr_to_group\n WHERE workspace_id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Varchar" + ] + }, + "nullable": [] + }, + "hash": "3f9004b5542afaaef40942a3ca8889ea164d9cfb4962997650d922d82f000df9" +} diff --git a/backend/.sqlx/query-5a219a2532517869578c4504ff3153c43903f929ae5d62fbba12610f89c36d55.json b/backend/.sqlx/query-5a219a2532517869578c4504ff3153c43903f929ae5d62fbba12610f89c36d55.json index 713ccb9dd3..36ddb8ab9f 100644 --- a/backend/.sqlx/query-5a219a2532517869578c4504ff3153c43903f929ae5d62fbba12610f89c36d55.json +++ b/backend/.sqlx/query-5a219a2532517869578c4504ff3153c43903f929ae5d62fbba12610f89c36d55.json @@ -15,7 +15,7 @@ ] }, "nullable": [ - null + true ] }, "hash": "5a219a2532517869578c4504ff3153c43903f929ae5d62fbba12610f89c36d55" diff --git a/backend/windmill-api/src/workspaces.rs b/backend/windmill-api/src/workspaces.rs index ebc53eb733..217d48dc7d 100644 --- a/backend/windmill-api/src/workspaces.rs +++ b/backend/windmill-api/src/workspaces.rs @@ -2678,6 +2678,17 @@ async fn clone_groups( .execute(&mut **tx) .await?; + sqlx::query!( + "INSERT INTO usr_to_group (workspace_id, group_, usr) + SELECT $2, group_, usr + FROM usr_to_group + WHERE workspace_id = $1", + source_workspace_id, + target_workspace_id, + ) + .execute(&mut **tx) + .await?; + Ok(()) } From f7d3d165c5ca8d48ecb9c75eb9db52988470bf43 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 26 Nov 2025 20:54:47 +0100 Subject: [PATCH 37/49] chore(main): release 1.585.1 (#7235) * chore(main): release 1.585.1 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> --- CHANGELOG.md | 7 + backend/Cargo.lock | 220 +++++++----------- backend/Cargo.toml | 4 +- backend/windmill-api/openapi.yaml | 2 +- benchmarks/lib.ts | 2 +- cli/src/main.ts | 2 +- frontend/package-lock.json | 4 +- frontend/package.json | 2 +- lsp/Pipfile | 4 +- openflow.openapi.yaml | 2 +- .../WindmillClient/WindmillClient.psd1 | 2 +- python-client/wmill/pyproject.toml | 2 +- python-client/wmill_pg/pyproject.toml | 2 +- typescript-client/jsr.json | 2 +- typescript-client/package.json | 2 +- version.txt | 2 +- 16 files changed, 110 insertions(+), 151 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4944da2051..e4f2ce0b34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.585.1](https://github.com/windmill-labs/windmill/compare/v1.585.0...v1.585.1) (2025-11-26) + + +### Bug Fixes + +* clone group members on workspace forks ([#7232](https://github.com/windmill-labs/windmill/issues/7232)) ([faad000](https://github.com/windmill-labs/windmill/commit/faad0006b430262f1ab6b749ccf60a6860a54405)) + ## [1.585.0](https://github.com/windmill-labs/windmill/compare/v1.584.0...v1.585.0) (2025-11-26) diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 9463e75efd..65647da64c 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -2397,9 +2397,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" dependencies = [ "crc-catalog", ] @@ -2747,7 +2747,7 @@ dependencies = [ "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core 0.9.12", + "parking_lot_core", ] [[package]] @@ -2761,7 +2761,7 @@ dependencies = [ "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core 0.9.12", + "parking_lot_core", ] [[package]] @@ -2818,7 +2818,7 @@ dependencies = [ "itertools 0.14.0", "log", "object_store", - "parking_lot 0.12.5", + "parking_lot", "parquet", "rand 0.8.5", "regex", @@ -2853,7 +2853,7 @@ dependencies = [ "itertools 0.14.0", "log", "object_store", - "parking_lot 0.12.5", + "parking_lot", "tokio", ] @@ -3026,7 +3026,7 @@ dependencies = [ "itertools 0.14.0", "log", "object_store", - "parking_lot 0.12.5", + "parking_lot", "parquet", "rand 0.8.5", "tokio", @@ -3051,7 +3051,7 @@ dependencies = [ "futures", "log", "object_store", - "parking_lot 0.12.5", + "parking_lot", "rand 0.8.5", "tempfile", "url", @@ -3187,7 +3187,7 @@ dependencies = [ "datafusion-common", "datafusion-expr", "datafusion-physical-plan", - "parking_lot 0.12.5", + "parking_lot", "paste", ] @@ -3328,7 +3328,7 @@ dependencies = [ "indexmap 2.11.1", "itertools 0.14.0", "log", - "parking_lot 0.12.5", + "parking_lot", "pin-project-lite", "tokio", ] @@ -3353,7 +3353,7 @@ dependencies = [ "itertools 0.14.0", "log", "object_store", - "parking_lot 0.12.5", + "parking_lot", "tokio", ] @@ -3482,7 +3482,7 @@ dependencies = [ "indexmap 2.11.1", "log", "once_cell", - "parking_lot 0.12.5", + "parking_lot", "serde", "serde_json", "sha2 0.10.9", @@ -3564,7 +3564,7 @@ dependencies = [ "indexmap 2.11.1", "libc", "memoffset", - "parking_lot 0.12.5", + "parking_lot", "percent-encoding", "pin-project", "serde", @@ -3809,7 +3809,7 @@ dependencies = [ "log", "once_cell", "os_pipe", - "parking_lot 0.12.5", + "parking_lot", "pin-project", "rand 0.8.5", "tokio", @@ -4189,7 +4189,7 @@ dependencies = [ "log", "node_resolver", "once_cell", - "parking_lot 0.12.5", + "parking_lot", "sys_traits", "thiserror 2.0.17", "url", @@ -4346,7 +4346,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6742a724e8becb372a74c650a1aefb8924a5b8107f7d75b3848763ea24b27a87" dependencies = [ "futures-util", - "parking_lot 0.12.5", + "parking_lot", "tokio", ] @@ -5590,7 +5590,7 @@ checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" dependencies = [ "futures-core", "lock_api", - "parking_lot 0.12.5", + "parking_lot", ] [[package]] @@ -6488,7 +6488,7 @@ dependencies = [ "ipconfig", "moka", "once_cell", - "parking_lot 0.12.5", + "parking_lot", "rand 0.9.0", "resolv-conf", "serde", @@ -7098,18 +7098,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "integer-encoding" version = "3.0.4" @@ -7553,7 +7541,7 @@ dependencies = [ "json-patch", "k8s-openapi", "kube-client", - "parking_lot 0.12.5", + "parking_lot", "pin-project", "serde", "serde_json", @@ -8283,7 +8271,7 @@ dependencies = [ "crossbeam-epoch", "crossbeam-utils", "equivalent", - "parking_lot 0.12.5", + "parking_lot", "portable-atomic", "rustc_version 0.4.1", "smallvec", @@ -9011,7 +8999,7 @@ dependencies = [ "hyper 1.8.1", "itertools 0.14.0", "md-5 0.10.6", - "parking_lot 0.12.5", + "parking_lot", "percent-encoding", "quick-xml 0.37.5", "rand 0.9.0", @@ -9430,17 +9418,6 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - [[package]] name = "parking_lot" version = "0.12.5" @@ -9448,21 +9425,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", - "parking_lot_core 0.9.12", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", + "parking_lot_core", ] [[package]] @@ -10140,7 +10103,7 @@ dependencies = [ "fnv", "lazy_static", "memchr", - "parking_lot 0.12.5", + "parking_lot", "thiserror 2.0.17", ] @@ -10309,7 +10272,7 @@ dependencies = [ "ahash 0.8.12", "equivalent", "hashbrown 0.16.1", - "parking_lot 0.12.5", + "parking_lot", ] [[package]] @@ -10597,15 +10560,6 @@ dependencies = [ "syn 2.0.111", ] -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.3.5" @@ -10825,9 +10779,9 @@ dependencies = [ [[package]] name = "reqwest-retry" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c73e4195a6bfbcb174b790d9b3407ab90646976c55de58a6515da25d851178" +checksum = "105747e3a037fe5bf17458d794de91149e575b6183fc72c85623a44abb9683f5" dependencies = [ "anyhow", "async-trait", @@ -10835,14 +10789,13 @@ dependencies = [ "getrandom 0.2.16", "http 1.4.0", "hyper 1.8.1", - "parking_lot 0.11.2", "reqwest 0.12.24", "reqwest-middleware", "retry-policies", - "thiserror 1.0.69", + "thiserror 2.0.17", "tokio", "tracing", - "wasm-timer", + "wasmtimer", ] [[package]] @@ -10853,9 +10806,9 @@ checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" [[package]] name = "retry-policies" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5875471e6cab2871bc150ecb8c727db5113c9338cc3354dc5ee3425b6aa40a1c" +checksum = "503c78f59814e2664c9980b739b19e40f549233ecf39928040ee54fdd431f614" dependencies = [ "rand 0.8.5", ] @@ -13692,7 +13645,7 @@ dependencies = [ "io-uring", "libc", "mio 1.1.0", - "parking_lot 0.12.5", + "parking_lot", "pin-project-lite", "signal-hook-registry", "slab", @@ -13757,7 +13710,7 @@ dependencies = [ "futures-channel", "futures-util", "log", - "parking_lot 0.12.5", + "parking_lot", "percent-encoding", "phf 0.11.3", "pin-project-lite", @@ -13783,7 +13736,7 @@ dependencies = [ "futures-channel", "futures-util", "log", - "parking_lot 0.12.5", + "parking_lot", "percent-encoding", "phf 0.11.3", "pin-project-lite", @@ -13979,7 +13932,7 @@ dependencies = [ "indexmap 2.11.1", "toml_datetime 0.7.0", "toml_parser", - "winnow 0.7.13", + "winnow 0.7.14", ] [[package]] @@ -13988,7 +13941,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" dependencies = [ - "winnow 0.7.13", + "winnow 0.7.14", ] [[package]] @@ -14074,7 +14027,7 @@ dependencies = [ "cookie 0.18.1", "futures-util", "http 1.4.0", - "parking_lot 0.12.5", + "parking_lot", "pin-project-lite", "tower-layer", "tower-service", @@ -14132,21 +14085,21 @@ dependencies = [ [[package]] name = "tracing-appender" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" +checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf" dependencies = [ "crossbeam-channel", - "thiserror 1.0.69", + "thiserror 2.0.17", "time", "tracing-subscriber", ] [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", @@ -14972,21 +14925,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "wasm-timer" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" -dependencies = [ - "futures", - "js-sys", - "parking_lot 0.11.2", - "pin-utils", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - [[package]] name = "wasm_dep_analyzer" version = "0.2.0" @@ -14997,6 +14935,20 @@ dependencies = [ "thiserror 2.0.17", ] +[[package]] +name = "wasmtimer" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c598d6b99ea013e35844697fc4670d08339d5cda15588f193c6beedd12f644b" +dependencies = [ + "futures", + "js-sys", + "parking_lot", + "pin-utils", + "slab", + "wasm-bindgen", +] + [[package]] name = "web-sys" version = "0.3.80" @@ -15069,7 +15021,7 @@ dependencies = [ "log", "naga", "once_cell", - "parking_lot 0.12.5", + "parking_lot", "profiling", "raw-window-handle", "ron", @@ -15111,7 +15063,7 @@ dependencies = [ "ndk-sys", "objc", "once_cell", - "parking_lot 0.12.5", + "parking_lot", "profiling", "range-alloc", "raw-window-handle", @@ -15210,7 +15162,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windmill" -version = "1.585.0" +version = "1.585.1" dependencies = [ "anyhow", "aws-sdk-config", @@ -15272,7 +15224,7 @@ dependencies = [ [[package]] name = "windmill-api" -version = "1.585.0" +version = "1.585.1" dependencies = [ "anyhow", "argon2", @@ -15393,7 +15345,7 @@ dependencies = [ [[package]] name = "windmill-api-client" -version = "1.585.0" +version = "1.585.1" dependencies = [ "base64 0.22.1", "chrono", @@ -15408,7 +15360,7 @@ dependencies = [ [[package]] name = "windmill-audit" -version = "1.585.0" +version = "1.585.1" dependencies = [ "chrono", "lazy_static", @@ -15422,7 +15374,7 @@ dependencies = [ [[package]] name = "windmill-autoscaling" -version = "1.585.0" +version = "1.585.1" dependencies = [ "anyhow", "axum", @@ -15441,7 +15393,7 @@ dependencies = [ [[package]] name = "windmill-common" -version = "1.585.0" +version = "1.585.1" dependencies = [ "anyhow", "async-recursion", @@ -15530,7 +15482,7 @@ dependencies = [ [[package]] name = "windmill-git-sync" -version = "1.585.0" +version = "1.585.1" dependencies = [ "regex", "serde", @@ -15545,7 +15497,7 @@ dependencies = [ [[package]] name = "windmill-indexer" -version = "1.585.0" +version = "1.585.1" dependencies = [ "anyhow", "bytes", @@ -15569,7 +15521,7 @@ dependencies = [ [[package]] name = "windmill-macros" -version = "1.585.0" +version = "1.585.1" dependencies = [ "itertools 0.14.0", "lazy_static", @@ -15581,7 +15533,7 @@ dependencies = [ [[package]] name = "windmill-parser" -version = "1.585.0" +version = "1.585.1" dependencies = [ "convert_case 0.6.0", "serde", @@ -15590,7 +15542,7 @@ dependencies = [ [[package]] name = "windmill-parser-bash" -version = "1.585.0" +version = "1.585.1" dependencies = [ "anyhow", "lazy_static", @@ -15602,7 +15554,7 @@ dependencies = [ [[package]] name = "windmill-parser-csharp" -version = "1.585.0" +version = "1.585.1" dependencies = [ "anyhow", "serde_json", @@ -15614,7 +15566,7 @@ dependencies = [ [[package]] name = "windmill-parser-go" -version = "1.585.0" +version = "1.585.1" dependencies = [ "anyhow", "gosyn", @@ -15626,7 +15578,7 @@ dependencies = [ [[package]] name = "windmill-parser-graphql" -version = "1.585.0" +version = "1.585.1" dependencies = [ "anyhow", "lazy_static", @@ -15638,7 +15590,7 @@ dependencies = [ [[package]] name = "windmill-parser-java" -version = "1.585.0" +version = "1.585.1" dependencies = [ "anyhow", "serde_json", @@ -15650,7 +15602,7 @@ dependencies = [ [[package]] name = "windmill-parser-nu" -version = "1.585.0" +version = "1.585.1" dependencies = [ "anyhow", "nu-parser", @@ -15661,7 +15613,7 @@ dependencies = [ [[package]] name = "windmill-parser-php" -version = "1.585.0" +version = "1.585.1" dependencies = [ "anyhow", "itertools 0.14.0", @@ -15672,7 +15624,7 @@ dependencies = [ [[package]] name = "windmill-parser-py" -version = "1.585.0" +version = "1.585.1" dependencies = [ "anyhow", "itertools 0.14.0", @@ -15684,7 +15636,7 @@ dependencies = [ [[package]] name = "windmill-parser-py-imports" -version = "1.585.0" +version = "1.585.1" dependencies = [ "anyhow", "async-recursion", @@ -15707,7 +15659,7 @@ dependencies = [ [[package]] name = "windmill-parser-ruby" -version = "1.585.0" +version = "1.585.1" dependencies = [ "anyhow", "lazy_static", @@ -15721,7 +15673,7 @@ dependencies = [ [[package]] name = "windmill-parser-rust" -version = "1.585.0" +version = "1.585.1" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -15738,7 +15690,7 @@ dependencies = [ [[package]] name = "windmill-parser-sql" -version = "1.585.0" +version = "1.585.1" dependencies = [ "anyhow", "lazy_static", @@ -15752,7 +15704,7 @@ dependencies = [ [[package]] name = "windmill-parser-ts" -version = "1.585.0" +version = "1.585.1" dependencies = [ "anyhow", "lazy_static", @@ -15770,7 +15722,7 @@ dependencies = [ [[package]] name = "windmill-parser-yaml" -version = "1.585.0" +version = "1.585.1" dependencies = [ "anyhow", "serde", @@ -15781,7 +15733,7 @@ dependencies = [ [[package]] name = "windmill-queue" -version = "1.585.0" +version = "1.585.1" dependencies = [ "anyhow", "async-recursion", @@ -15818,7 +15770,7 @@ dependencies = [ [[package]] name = "windmill-sql-datatype-parser-wasm" -version = "1.585.0" +version = "1.585.1" dependencies = [ "wasm-bindgen", "wasm-bindgen-test", @@ -15828,7 +15780,7 @@ dependencies = [ [[package]] name = "windmill-worker" -version = "1.585.0" +version = "1.585.1" dependencies = [ "anyhow", "async-once-cell", @@ -16451,9 +16403,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 05c86807be..dba5305701 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "windmill" -version = "1.585.0" +version = "1.585.1" authors.workspace = true edition.workspace = true @@ -33,7 +33,7 @@ members = [ exclude = ["./windmill-duckdb-ffi-internal"] [workspace.package] -version = "1.585.0" +version = "1.585.1" authors = ["Ruben Fiszel "] edition = "2021" diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 339df53205..deb0f882d3 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.585.0 + version: 1.585.1 title: Windmill API contact: diff --git a/benchmarks/lib.ts b/benchmarks/lib.ts index a936855fe2..f94f88118b 100644 --- a/benchmarks/lib.ts +++ b/benchmarks/lib.ts @@ -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.585.0"; +export const VERSION = "v1.585.1"; export async function login(email: string, password: string): Promise { return await windmill.UserService.login({ diff --git a/cli/src/main.ts b/cli/src/main.ts index 4f0f455522..9f5d05f959 100644 --- a/cli/src/main.ts +++ b/cli/src/main.ts @@ -68,7 +68,7 @@ export { // } // }); -export const VERSION = "1.585.0"; +export const VERSION = "1.585.1"; export const WM_FORK_PREFIX = "wm-fork"; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index a0527f34f7..1c35a6ed17 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "windmill-components", - "version": "1.585.0", + "version": "1.585.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "windmill-components", - "version": "1.585.0", + "version": "1.585.1", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/frontend/package.json b/frontend/package.json index a1c0c39cbe..d5fbfc0d15 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "windmill-components", - "version": "1.585.0", + "version": "1.585.1", "scripts": { "dev": "vite dev", "build": "vite build", diff --git a/lsp/Pipfile b/lsp/Pipfile index d3f2ccb54e..cd1c2f7c31 100644 --- a/lsp/Pipfile +++ b/lsp/Pipfile @@ -4,8 +4,8 @@ verify_ssl = true name = "pypi" [packages] -wmill = ">=1.585.0" -wmill_pg = ">=1.585.0" +wmill = ">=1.585.1" +wmill_pg = ">=1.585.1" sendgrid = "*" mysql-connector-python = "*" pymongo = "*" diff --git a/openflow.openapi.yaml b/openflow.openapi.yaml index cd534f3fac..9b58309056 100644 --- a/openflow.openapi.yaml +++ b/openflow.openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.585.0 + version: 1.585.1 title: OpenFlow Spec contact: name: Ruben Fiszel diff --git a/powershell-client/WindmillClient/WindmillClient.psd1 b/powershell-client/WindmillClient/WindmillClient.psd1 index ffe3220eb2..44fe48eec6 100644 --- a/powershell-client/WindmillClient/WindmillClient.psd1 +++ b/powershell-client/WindmillClient/WindmillClient.psd1 @@ -12,7 +12,7 @@ RootModule = 'WindmillClient.psm1' # Version number of this module. - ModuleVersion = '1.585.0' + ModuleVersion = '1.585.1' # Supported PSEditions # CompatiblePSEditions = @() diff --git a/python-client/wmill/pyproject.toml b/python-client/wmill/pyproject.toml index 73e5cf3026..1bc8342d33 100644 --- a/python-client/wmill/pyproject.toml +++ b/python-client/wmill/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill" -version = "1.585.0" +version = "1.585.1" description = "A client library for accessing Windmill server wrapping the Windmill client API" license = "Apache-2.0" homepage = "https://windmill.dev" diff --git a/python-client/wmill_pg/pyproject.toml b/python-client/wmill_pg/pyproject.toml index 2732b66b10..309ed062d0 100644 --- a/python-client/wmill_pg/pyproject.toml +++ b/python-client/wmill_pg/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill-pg" -version = "1.585.0" +version = "1.585.1" description = "An extension client for the wmill client library focused on pg" license = "Apache-2.0" homepage = "https://windmill.dev" diff --git a/typescript-client/jsr.json b/typescript-client/jsr.json index 908424b59f..58579bf00d 100644 --- a/typescript-client/jsr.json +++ b/typescript-client/jsr.json @@ -1,6 +1,6 @@ { "name": "@windmill/windmill", - "version": "1.585.0", + "version": "1.585.1", "exports": "./src/index.ts", "publish": { "exclude": ["!src", "./s3Types.ts", "./client.ts"] diff --git a/typescript-client/package.json b/typescript-client/package.json index 5afa72aaf9..b2a49a5f3a 100644 --- a/typescript-client/package.json +++ b/typescript-client/package.json @@ -1,7 +1,7 @@ { "name": "windmill-client", "description": "Windmill SDK client for browsers and Node.js", - "version": "1.585.0", + "version": "1.585.1", "author": "Ruben Fiszel", "license": "Apache 2.0", "devDependencies": { diff --git a/version.txt b/version.txt index 30d77f1213..26ec7f16a6 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.585.0 +1.585.1 From eebcbf068d517e5489ccca56137a6df9119b44a1 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Thu, 27 Nov 2025 06:08:25 +0000 Subject: [PATCH 38/49] update rolldown-vite to 7.2.8 --- frontend/package-lock.json | 177 +++++++++++++++++-------------------- frontend/package.json | 2 +- 2 files changed, 84 insertions(+), 95 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 1c35a6ed17..ca86dea1d7 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -142,7 +142,7 @@ "tar": "^7.4.3", "tslib": "^2.6.1", "typescript": "^5.5.0", - "vite": "npm:rolldown-vite@latest", + "vite": "npm:rolldown-vite@7.2.8", "vite-plugin-dts": "^4.5.4", "vite-plugin-mkcert": "^1.17.5", "yootils": "^0.3.1" @@ -1904,9 +1904,9 @@ } }, "node_modules/@emnapi/core": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.5.0.tgz", - "integrity": "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz", + "integrity": "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==", "dev": true, "license": "MIT", "optional": true, @@ -1916,9 +1916,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz", - "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", "dev": true, "license": "MIT", "optional": true, @@ -2603,9 +2603,9 @@ } }, "node_modules/@oxc-project/runtime": { - "version": "0.92.0", - "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.92.0.tgz", - "integrity": "sha512-Z7x2dZOmznihvdvCvLKMl+nswtOSVxS2H2ocar+U9xx6iMfTp0VGIrX6a4xB1v80IwOPC7dT1LXIJrY70Xu3Jw==", + "version": "0.99.0", + "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.99.0.tgz", + "integrity": "sha512-8iE5/4OK0SLHqWzRxSvI1gjFPmIH6718s8iwkuco95rBZsCZIHq+5wy4lYsASxnH+8FOhbGndiUrcwsVG5i2zw==", "dev": true, "license": "MIT", "engines": { @@ -2613,9 +2613,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.94.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.94.0.tgz", - "integrity": "sha512-+UgQT/4o59cZfH6Cp7G0hwmqEQ0wE+AdIwhikdwnhWI9Dp8CgSY081+Q3O67/wq3VJu8mgUEB93J9EHHn70fOw==", + "version": "0.99.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.99.0.tgz", + "integrity": "sha512-LLDEhXB7g1m5J+woRSgfKsFPS3LhR9xRhTeIoEBm5WrkwMxn6eZ0Ld0c0K5eHB57ChZX6I3uSmmLjZ8pcjlRcw==", "dev": true, "license": "MIT", "funding": { @@ -2679,9 +2679,9 @@ "license": "SEE LICENSE IN LICENSE" }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-beta.42", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-beta.42.tgz", - "integrity": "sha512-W5ZKF3TP3bOWuBfotAGp+UGjxOkGV7jRmIRbBA7NFjggx7Oi6vOmGDqpHEIX7kDCiry1cnIsWQaxNvWbMdkvzQ==", + "version": "1.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-beta.52.tgz", + "integrity": "sha512-MBGIgysimZPqTDcLXI+i9VveijkP5C3EAncEogXhqfax6YXj1Tr2LY3DVuEOMIjWfMPMhtQSPup4fSTAmgjqIw==", "cpu": [ "arm64" ], @@ -2696,9 +2696,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-beta.42", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-beta.42.tgz", - "integrity": "sha512-abw/wtgJA8OCgaTlL+xJxnN/Z01BwV1rfzIp5Hh9x+IIO6xOBfPsQ0nzi0+rWx3TyZ9FZXyC7bbC+5NpQ9EaXQ==", + "version": "1.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-beta.52.tgz", + "integrity": "sha512-MmKeoLnKu1d9j6r19K8B+prJnIZ7u+zQ+zGQ3YHXGnr41rzE3eqQLovlkvoZnRoxDGPA4ps0pGiwXy6YE3lJyg==", "cpu": [ "arm64" ], @@ -2713,9 +2713,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-beta.42", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-beta.42.tgz", - "integrity": "sha512-Y/UrZIRVr8CvXVEB88t6PeC46r1K9/QdPEo2ASE/b/KBEyXIx+QbM6kv9QfQVWU2Atly2+SVsQzxQsIvuk3lZQ==", + "version": "1.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-beta.52.tgz", + "integrity": "sha512-qpHedvQBmIjT8zdnjN3nWPR2qjQyJttbXniCEKKdHeAbZG9HyNPBUzQF7AZZGwmS9coQKL+hWg9FhWzh2dZ2IA==", "cpu": [ "x64" ], @@ -2730,9 +2730,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-beta.42", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-beta.42.tgz", - "integrity": "sha512-zRM0oOk7BZiy6DoWBvdV4hyEg+j6+WcBZIMHVirMEZRu8hd18kZdJkg+bjVMfCEhwpWeFUfBfZ1qcaZ5UdYzlQ==", + "version": "1.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-beta.52.tgz", + "integrity": "sha512-dDp7WbPapj/NVW0LSiH/CLwMhmLwwKb3R7mh2kWX+QW85X1DGVnIEyKh9PmNJjB/+suG1dJygdtdNPVXK1hylg==", "cpu": [ "x64" ], @@ -2747,9 +2747,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-beta.42", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-beta.42.tgz", - "integrity": "sha512-6RjFaC52QNwo7ilU8C5H7swbGlgfTkG9pudXwzr3VYyT18s0C9gLg3mvc7OMPIGqNxnQ0M5lU8j6aQCk2DTRVg==", + "version": "1.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-beta.52.tgz", + "integrity": "sha512-9e4l6vy5qNSliDPqNfR6CkBOAx6PH7iDV4OJiEJzajajGrVy8gc/IKKJUsoE52G8ud8MX6r3PMl97NfwgOzB7g==", "cpu": [ "arm" ], @@ -2764,9 +2764,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-beta.42", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-beta.42.tgz", - "integrity": "sha512-LMYHM5Sf6ROq+VUwHMDVX2IAuEsWTv4SnlFEedBnMGpvRuQ14lCmD4m5Q8sjyAQCgyha9oghdGoK8AEg1sXZKg==", + "version": "1.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-beta.52.tgz", + "integrity": "sha512-V48oDR84feRU2KRuzpALp594Uqlx27+zFsT6+BgTcXOtu7dWy350J1G28ydoCwKB+oxwsRPx2e7aeQnmd3YJbQ==", "cpu": [ "arm64" ], @@ -2781,9 +2781,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-beta.42", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-beta.42.tgz", - "integrity": "sha512-/bNTYb9aKNhzdbPn3O4MK2aLv55AlrkUKPE4KNfBYjkoZUfDr4jWp7gsSlvTc5A/99V1RCm9axvt616ZzeXGyA==", + "version": "1.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-beta.52.tgz", + "integrity": "sha512-ENLmSQCWqSA/+YN45V2FqTIemg7QspaiTjlm327eUAMeOLdqmSOVVyrQexJGNTQ5M8sDYCgVAig2Kk01Ggmqaw==", "cpu": [ "arm64" ], @@ -2798,9 +2798,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-beta.42", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-beta.42.tgz", - "integrity": "sha512-n/SLa4h342oyeGykZdch7Y3GNCNliRPL4k5wkeZ/5eQZs+c6/ZG1SHCJQoy7bZcmxiMyaXs9HoFmv1PEKrZgWg==", + "version": "1.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-beta.52.tgz", + "integrity": "sha512-klahlb2EIFltSUubn/VLjuc3qxp1E7th8ukayPfdkcKvvYcQ5rJztgx8JsJSuAKVzKtNTqUGOhy4On71BuyV8g==", "cpu": [ "x64" ], @@ -2815,9 +2815,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-beta.42", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-beta.42.tgz", - "integrity": "sha512-4PSd46sFzqpLHSGdaSViAb1mk55sCUMpJg+X8ittXaVocQsV3QLG/uydSH8RyL0ngHX5fy3D70LcCzlB15AgHw==", + "version": "1.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-beta.52.tgz", + "integrity": "sha512-UuA+JqQIgqtkgGN2c/AQ5wi8M6mJHrahz/wciENPTeI6zEIbbLGoth5XN+sQe2pJDejEVofN9aOAp0kaazwnVg==", "cpu": [ "x64" ], @@ -2832,9 +2832,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-beta.42", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-beta.42.tgz", - "integrity": "sha512-BmWoeJJyeZXmZBcfoxG6J9+rl2G7eO47qdTkAzEegj4n3aC6CBIHOuDcbE8BvhZaEjQR0nh0nJrtEDlt65Q7Sw==", + "version": "1.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-beta.52.tgz", + "integrity": "sha512-1BNQW8u4ro8bsN1+tgKENJiqmvc+WfuaUhXzMImOVSMw28pkBKdfZtX2qJPADV3terx+vNJtlsgSGeb3+W6Jiw==", "cpu": [ "arm64" ], @@ -2849,9 +2849,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-beta.42", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-beta.42.tgz", - "integrity": "sha512-2Ft32F7uiDTrGZUKws6CLNTlvTWHC33l4vpXrzUucf9rYtUThAdPCOt89Pmn13tNX6AulxjGEP2R0nZjTSW3eQ==", + "version": "1.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-beta.52.tgz", + "integrity": "sha512-K/p7clhCqJOQpXGykrFaBX2Dp9AUVIDHGc+PtFGBwg7V+mvBTv/tsm3LC3aUmH02H2y3gz4y+nUTQ0MLpofEEg==", "cpu": [ "wasm32" ], @@ -2859,16 +2859,16 @@ "license": "MIT", "optional": true, "dependencies": { - "@napi-rs/wasm-runtime": "^1.0.6" + "@napi-rs/wasm-runtime": "^1.0.7" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-beta.42", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-beta.42.tgz", - "integrity": "sha512-hC1kShXW/z221eG+WzQMN06KepvPbMBknF0iGR3VMYJLOe9gwnSTfGxFT5hf8XrPv7CEZqTWRd0GQpkSHRbGsw==", + "version": "1.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-beta.52.tgz", + "integrity": "sha512-a4EkXBtnYYsKipjS7QOhEBM4bU5IlR9N1hU+JcVEVeuTiaslIyhWVKsvf7K2YkQHyVAJ+7/A9BtrGqORFcTgng==", "cpu": [ "arm64" ], @@ -2883,9 +2883,9 @@ } }, "node_modules/@rolldown/binding-win32-ia32-msvc": { - "version": "1.0.0-beta.42", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.0.0-beta.42.tgz", - "integrity": "sha512-AICBYromawouGjj+GS33369E8Vwhy6UwhQEhQ5evfS8jPCsyVvoICJatbDGDGH01dwtVGLD5eDFzPicUOVpe4g==", + "version": "1.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.0.0-beta.52.tgz", + "integrity": "sha512-5ZXcYyd4GxPA6QfbGrNcQjmjbuLGvfz6728pZMsQvGHI+06LT06M6TPtXvFvLgXtexc+OqvFe1yAIXJU1gob/w==", "cpu": [ "ia32" ], @@ -2900,9 +2900,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-beta.42", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-beta.42.tgz", - "integrity": "sha512-XpZ0M+tjoEiSc9c+uZR7FCnOI0uxDRNs1elGOMjeB0pUP1QmvVbZGYNsyLbLoP4u7e3VQN8rie1OQ8/mB6rcJg==", + "version": "1.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-beta.52.tgz", + "integrity": "sha512-tzpnRQXJrSzb8Z9sm97UD3cY0toKOImx+xRKsDLX4zHaAlRXWh7jbaKBePJXEN7gNw7Nm03PBNwphdtA8KSUYQ==", "cpu": [ "x64" ], @@ -2917,9 +2917,9 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.42", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.42.tgz", - "integrity": "sha512-N7pQzk9CyE7q0bBN/q0J8s6Db279r5kUZc6d7/wWRe9/zXqC52HQovVyu6iXPIDY4BEzzgbVLhVFXrOuGJ22ZQ==", + "version": "1.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.52.tgz", + "integrity": "sha512-/L0htLJZbaZFL1g9OHOblTxbCYIGefErJjtYOwgl9ZqNx27P3L0SDfjhhHIss32gu5NWgnxuT2a2Hnnv6QGHKA==", "dev": true, "license": "MIT" }, @@ -4437,16 +4437,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/ansis": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.2.0.tgz", - "integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - } - }, "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", @@ -12260,15 +12250,14 @@ } }, "node_modules/rolldown": { - "version": "1.0.0-beta.42", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-beta.42.tgz", - "integrity": "sha512-xaPcckj+BbJhYLsv8gOqezc8EdMcKKe/gk8v47B0KPvgABDrQ0qmNPAiT/gh9n9Foe0bUkEv2qzj42uU5q1WRg==", + "version": "1.0.0-beta.52", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-beta.52.tgz", + "integrity": "sha512-Hbnpljue+JhMJrlOjQ1ixp9me7sUec7OjFvS+A1Qm8k8Xyxmw3ZhxFu7LlSXW1s9AX3POE9W9o2oqCEeR5uDmg==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.94.0", - "@rolldown/pluginutils": "1.0.0-beta.42", - "ansis": "=4.2.0" + "@oxc-project/types": "=0.99.0", + "@rolldown/pluginutils": "1.0.0-beta.52" }, "bin": { "rolldown": "bin/cli.mjs" @@ -12277,20 +12266,20 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-beta.42", - "@rolldown/binding-darwin-arm64": "1.0.0-beta.42", - "@rolldown/binding-darwin-x64": "1.0.0-beta.42", - "@rolldown/binding-freebsd-x64": "1.0.0-beta.42", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.42", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.42", - "@rolldown/binding-linux-arm64-musl": "1.0.0-beta.42", - "@rolldown/binding-linux-x64-gnu": "1.0.0-beta.42", - "@rolldown/binding-linux-x64-musl": "1.0.0-beta.42", - "@rolldown/binding-openharmony-arm64": "1.0.0-beta.42", - "@rolldown/binding-wasm32-wasi": "1.0.0-beta.42", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.42", - "@rolldown/binding-win32-ia32-msvc": "1.0.0-beta.42", - "@rolldown/binding-win32-x64-msvc": "1.0.0-beta.42" + "@rolldown/binding-android-arm64": "1.0.0-beta.52", + "@rolldown/binding-darwin-arm64": "1.0.0-beta.52", + "@rolldown/binding-darwin-x64": "1.0.0-beta.52", + "@rolldown/binding-freebsd-x64": "1.0.0-beta.52", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.52", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.52", + "@rolldown/binding-linux-arm64-musl": "1.0.0-beta.52", + "@rolldown/binding-linux-x64-gnu": "1.0.0-beta.52", + "@rolldown/binding-linux-x64-musl": "1.0.0-beta.52", + "@rolldown/binding-openharmony-arm64": "1.0.0-beta.52", + "@rolldown/binding-wasm32-wasi": "1.0.0-beta.52", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.52", + "@rolldown/binding-win32-ia32-msvc": "1.0.0-beta.52", + "@rolldown/binding-win32-x64-msvc": "1.0.0-beta.52" } }, "node_modules/run-parallel": { @@ -14130,18 +14119,18 @@ }, "node_modules/vite": { "name": "rolldown-vite", - "version": "7.1.16", - "resolved": "https://registry.npmjs.org/rolldown-vite/-/rolldown-vite-7.1.16.tgz", - "integrity": "sha512-cK6tCmZyEC0KRAcXTjQ+ara+wkqmaE7WUoI0ZfZzDuvaRaZ3mtvbhTJc4cH+PjKRok++++Z1bZZaNlf3+SnnGA==", + "version": "7.2.8", + "resolved": "https://registry.npmjs.org/rolldown-vite/-/rolldown-vite-7.2.8.tgz", + "integrity": "sha512-8wKihlF6EDF8grimwd7GPOhLkQkSIgj6Hlcp0CXhtO3HAXeUUqhgZmJmn07OF8e4PbTusMX6Yxmy1BptVRZsdw==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/runtime": "0.92.0", + "@oxc-project/runtime": "0.99.0", "fdir": "^6.5.0", "lightningcss": "^1.30.2", "picomatch": "^4.0.3", "postcss": "^8.5.6", - "rolldown": "1.0.0-beta.42", + "rolldown": "1.0.0-beta.52", "tinyglobby": "^0.2.15" }, "bin": { diff --git a/frontend/package.json b/frontend/package.json index d5fbfc0d15..7c3340235c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -65,7 +65,7 @@ "tar": "^7.4.3", "tslib": "^2.6.1", "typescript": "^5.5.0", - "vite": "npm:rolldown-vite@latest", + "vite": "npm:rolldown-vite@7.2.8", "vite-plugin-dts": "^4.5.4", "vite-plugin-mkcert": "^1.17.5", "yootils": "^0.3.1" From 4639a5f9d9cb73c3729f057e6f7bc9a7e6f30802 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Thu, 27 Nov 2025 06:41:20 +0000 Subject: [PATCH 39/49] fix ol use --- frontend/src/lib/components/MapResult.svelte | 12 ++++++------ .../components/apps/components/display/AppMap.svelte | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/frontend/src/lib/components/MapResult.svelte b/frontend/src/lib/components/MapResult.svelte index 60a33c8a66..a6851e9082 100644 --- a/frontend/src/lib/components/MapResult.svelte +++ b/frontend/src/lib/components/MapResult.svelte @@ -1,12 +1,12 @@