From 3489e24ee4865211b41781ccf17c7f9361f32110 Mon Sep 17 00:00:00 2001 From: Diego Imbert Date: Mon, 8 Dec 2025 23:59:22 +0100 Subject: [PATCH] ignore asset parsing errors in frontend (avoid flow layout shift) --- .../parsers/windmill-parser-wasm/src/lib.rs | 29 ++++++++----------- .../src/lib/components/ScriptEditor.svelte | 7 +++-- .../components/assets/JobAssetsViewer.svelte | 7 ++--- .../components/flows/FlowAssetsHandler.svelte | 16 +++++----- frontend/src/lib/infer.ts | 28 ++++++++++++------ 5 files changed, 47 insertions(+), 40 deletions(-) diff --git a/backend/parsers/windmill-parser-wasm/src/lib.rs b/backend/parsers/windmill-parser-wasm/src/lib.rs index f6de0a7b05..61cc11d81d 100644 --- a/backend/parsers/windmill-parser-wasm/src/lib.rs +++ b/backend/parsers/windmill-parser-wasm/src/lib.rs @@ -190,41 +190,36 @@ pub fn parse_ruby(code: &str) -> String { #[cfg(feature = "sql-parser")] #[wasm_bindgen] pub fn parse_assets_sql(code: &str) -> String { - if let Ok(r) = windmill_parser_sql::parse_assets(code) { - return serde_json::to_string(&r).unwrap(); - } else { - return "Invalid".to_string(); + match windmill_parser_sql::parse_assets(code) { + Ok(r) => serde_json::to_string(&r).unwrap(), + Err(err) => format!("err: {:?}", err), } } #[cfg(feature = "ts-parser")] #[wasm_bindgen] pub fn parse_assets_ts(code: &str) -> String { - if let Ok(r) = windmill_parser_ts::parse_assets(code) { - return serde_json::to_string(&r).unwrap(); - } else { - return "Invalid".to_string(); + match windmill_parser_ts::parse_assets(code) { + Ok(r) => serde_json::to_string(&r).unwrap(), + Err(err) => format!("err: {:?}", err), } } #[cfg(feature = "py-parser")] #[wasm_bindgen] pub fn parse_assets_py(code: &str) -> String { - if let Ok(r) = windmill_parser_py::parse_assets(code) { - return serde_json::to_string(&r).unwrap(); - } else { - return "Invalid".to_string(); + match windmill_parser_py::parse_assets(code) { + Ok(r) => serde_json::to_string(&r).unwrap(), + Err(err) => format!("err: {:?}", err), } } #[cfg(feature = "ansible-parser")] #[wasm_bindgen] pub fn parse_assets_ansible(code: &str) -> String { - let o = windmill_parser_yaml::parse_assets(code); - if let Ok(r) = o { - return serde_json::to_string(&r).unwrap(); - } else { - return format!("err: {:?}", o.err().unwrap()); + match windmill_parser_yaml::parse_assets(code) { + Ok(r) => serde_json::to_string(&r).unwrap(), + Err(err) => format!("err: {:?}", err), } } diff --git a/frontend/src/lib/components/ScriptEditor.svelte b/frontend/src/lib/components/ScriptEditor.svelte index d042f3ec03..be6d5cd21b 100644 --- a/frontend/src/lib/components/ScriptEditor.svelte +++ b/frontend/src/lib/components/ScriptEditor.svelte @@ -58,6 +58,7 @@ import { copilotInfo } from '$lib/aiStore' import JsonInputs from '$lib/components/JsonInputs.svelte' import Toggle from './Toggle.svelte' + import { deepEqual } from 'fast-equals' interface Props { // Exported @@ -158,12 +159,14 @@ $effect(() => { ;[lang, code] untrack(() => { - inferAssets(lang, code).then((newAssets: AssetWithAltAccessType[]) => { + inferAssets(lang, code).then((inferAssetsResult) => { + if (inferAssetsResult.status === 'error') return + let newAssets = inferAssetsResult.assets as AssetWithAltAccessType[] for (const asset of newAssets) { const old = assets?.find((a) => assetEq(a, asset)) if (old?.alt_access_type) asset.alt_access_type = old.alt_access_type } - assets = newAssets + if (!deepEqual(assets, newAssets)) assets = newAssets }) if (lang === 'ansible') { diff --git a/frontend/src/lib/components/assets/JobAssetsViewer.svelte b/frontend/src/lib/components/assets/JobAssetsViewer.svelte index 6375783595..22ca628712 100644 --- a/frontend/src/lib/components/assets/JobAssetsViewer.svelte +++ b/frontend/src/lib/components/assets/JobAssetsViewer.svelte @@ -35,10 +35,9 @@ ) } if (job.job_kind === 'script') { - return [ - ...(await inferAssets(job.language!, job.raw_code ?? '')), - ...parseInputArgsAssets(job.args ?? {}) - ] + let inferAssetsResult = await inferAssets(job.language!, job.raw_code ?? '') + let assets = inferAssetsResult.status === 'ok' ? inferAssetsResult.assets : [] + return [...assets, ...parseInputArgsAssets(job.args ?? {})] } return [] } diff --git a/frontend/src/lib/components/flows/FlowAssetsHandler.svelte b/frontend/src/lib/components/flows/FlowAssetsHandler.svelte index cd4ebc98c4..f4e5218237 100644 --- a/frontend/src/lib/components/flows/FlowAssetsHandler.svelte +++ b/frontend/src/lib/components/flows/FlowAssetsHandler.svelte @@ -119,14 +119,14 @@ }) async function parseAndUpdateRawScriptModule(v: RawScript) { - try { - let parsedAssets: AssetWithAltAccessType[] = await inferAssets(v.language, v.content) - for (const asset of parsedAssets) { - const old = v.assets?.find((a) => assetEq(a, asset)) - if (old?.alt_access_type) asset.alt_access_type = old.alt_access_type - } - if (!deepEqual(v.assets, parsedAssets)) v.assets = parsedAssets - } catch (e) {} + let inferAssetsResult = await inferAssets(v.language, v.content) + if (inferAssetsResult.status === 'error') return + let newAssets = inferAssetsResult.assets as AssetWithAltAccessType[] + for (const asset of newAssets) { + const old = v.assets?.find((a) => assetEq(a, asset)) + if (old?.alt_access_type) asset.alt_access_type = old.alt_access_type + } + if (!deepEqual(v.assets, newAssets)) v.assets = newAssets } // Check for raw script modules whose assets were not parsed. Useful for flows created diff --git a/frontend/src/lib/infer.ts b/frontend/src/lib/infer.ts index ffe37a860f..228e68d491 100644 --- a/frontend/src/lib/infer.ts +++ b/frontend/src/lib/infer.ts @@ -89,33 +89,43 @@ async function initWasmRuby() { await initRubyParser(wasmUrlRuby) } +type InferAssetsResult = + | { status: 'ok'; assets: AssetWithAccessType[] } + | { status: 'error'; error: string } + export async function inferAssets( language: SupportedLanguage | undefined, code: string -): Promise { +): Promise { + function wrap(raw_result: string): InferAssetsResult { + if (raw_result.startsWith('err:')) { + return { status: 'error', error: raw_result.slice(4).trim() } + } + return { status: 'ok', assets: JSON.parse(raw_result) as AssetWithAccessType[] } + } + try { if (language === 'duckdb') { await initWasmRegex() - let r = JSON.parse(parse_assets_sql(code)) - return r + return wrap(parse_assets_sql(code)) } if (language === 'deno' || language === 'nativets' || language === 'bun') { await initWasmTs() - return JSON.parse(parse_assets_ts(code)) + return wrap(parse_assets_ts(code)) } if (language === 'python3') { await initWasmPython() - return JSON.parse(parse_assets_py(code)) + return wrap(parse_assets_py(code)) } if (language === 'ansible') { await initWasmYaml() - return JSON.parse(parse_assets_ansible(code)) + return wrap(parse_assets_ansible(code)) } } catch (e) { - console.error('error parsing assets', e) - return [] + return { status: 'error', error: (e as Error)?.message || JSON.stringify(e) } } - return [] + + return { status: 'ok', assets: [] } } export async function inferAnsibleExecutionMode(code: string): Promise {