diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 5d4db47490..a533cd929d 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -3752,9 +3752,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.67" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" dependencies = [ "wasm-bindgen", ] @@ -9044,9 +9044,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.90" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -9054,9 +9054,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.90" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" dependencies = [ "bumpalo", "log", @@ -9069,9 +9069,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.40" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" dependencies = [ "cfg-if", "js-sys", @@ -9081,9 +9081,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.90" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -9091,9 +9091,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.90" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", @@ -9104,15 +9104,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.90" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] name = "wasm-bindgen-test" -version = "0.3.40" +version = "0.3.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139bd73305d50e1c1c4333210c0db43d989395b64a237bd35c10ef3832a7f70c" +checksum = "2cf9242c0d27999b831eae4767b2a146feb0b27d332d553e605864acd2afd403" dependencies = [ "console_error_panic_hook", "js-sys", @@ -9124,9 +9124,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.40" +version = "0.3.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70072aebfe5da66d2716002c729a14e4aec4da0e23cc2ea66323dac541c93928" +checksum = "794645f5408c9a039fd09f4d113cdfb2e7eba5ff1956b07bcf701cf4b394fe89" dependencies = [ "proc-macro2", "quote", @@ -9157,9 +9157,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.67" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index fb6b9f3f49..63638c6423 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -189,7 +189,7 @@ rsmq_async = { version = "5.1.5" } gosyn = "0.2.6" bytes = "1.4.0" gethostname = "0.4.3" -wasm-bindgen = "0.2" +wasm-bindgen = "=0.2.89" serde-wasm-bindgen = "0.4" wasm-bindgen-test = "0.3.0" convert_case = "0.6.0" diff --git a/backend/parsers/windmill-parser-wasm/build.sh b/backend/parsers/windmill-parser-wasm/build.sh new file mode 100644 index 0000000000..0ac5a6fde0 --- /dev/null +++ b/backend/parsers/windmill-parser-wasm/build.sh @@ -0,0 +1,4 @@ +#!/bin/bash +set -eou pipefail + +deno task wasmbuild --out ../../../cli/wasm/ diff --git a/backend/parsers/windmill-parser-wasm/deno.json b/backend/parsers/windmill-parser-wasm/deno.json new file mode 100644 index 0000000000..0764e447d0 --- /dev/null +++ b/backend/parsers/windmill-parser-wasm/deno.json @@ -0,0 +1,5 @@ +{ + "tasks": { + "wasmbuild": "deno run -A https://deno.land/x/wasmbuild@0.15.4/main.ts" + } + } \ No newline at end of file diff --git a/backend/windmill-api/openapi-deref.yaml b/backend/windmill-api/openapi-deref.yaml index af1cf46a31..ecb798f56e 100644 --- a/backend/windmill-api/openapi-deref.yaml +++ b/backend/windmill-api/openapi-deref.yaml @@ -1,6 +1,6 @@ openapi: 3.0.3 info: - version: 1.242.0 + version: 1.244.2 title: Windmill API contact: name: Windmill Team @@ -2530,13 +2530,10 @@ paths: type: string expires_in: type: integer - owner: - type: string client: type: string required: - expires_in - - owner - client responses: '200': @@ -3358,32 +3355,32 @@ paths: id: type: string value: - oneOf: &ref_204 + oneOf: &ref_206 - type: object - properties: &ref_188 + properties: &ref_190 input_transforms: type: object additionalProperties: oneOf: &ref_26 - type: object - properties: &ref_184 + properties: &ref_186 value: {} type: type: string enum: - javascript - required: &ref_185 + required: &ref_187 - expr - type - type: object - properties: &ref_186 + properties: &ref_188 expr: type: string type: type: string enum: - javascript - required: &ref_187 + required: &ref_189 - expr - type discriminator: &ref_27 @@ -3423,13 +3420,13 @@ paths: type: number concurrency_time_window_s: type: number - required: &ref_189 + required: &ref_191 - type - content - language - input_transforms - type: object - properties: &ref_190 + properties: &ref_192 input_transforms: type: object additionalProperties: @@ -3443,12 +3440,12 @@ paths: type: string enum: - script - required: &ref_191 + required: &ref_193 - type - path - input_transforms - type: object - properties: &ref_192 + properties: &ref_194 input_transforms: type: object additionalProperties: @@ -3460,12 +3457,12 @@ paths: type: string enum: - flow - required: &ref_193 + required: &ref_195 - type - path - input_transforms - type: object - properties: &ref_194 + properties: &ref_196 modules: type: array items: @@ -3487,13 +3484,13 @@ paths: type: boolean parallelism: type: integer - required: &ref_195 + required: &ref_197 - modules - iterator - skip_failures - type - type: object - properties: &ref_196 + properties: &ref_198 branches: type: array items: @@ -3524,12 +3521,12 @@ paths: type: string enum: - branchone - required: &ref_197 + required: &ref_199 - branches - default - type - type: object - properties: &ref_198 + properties: &ref_200 branches: type: array items: @@ -3554,28 +3551,28 @@ paths: - branchall parallel: type: boolean - required: &ref_199 - - branches - - type - - type: object - properties: &ref_200 - type: - type: string - enum: - - identity - flow: - type: boolean required: &ref_201 + - branches - type - type: object properties: &ref_202 type: type: string enum: - - graphql + - identity + flow: + type: boolean required: &ref_203 - type - discriminator: &ref_205 + - type: object + properties: &ref_204 + type: + type: string + enum: + - graphql + required: &ref_205 + - type + discriminator: &ref_207 propertyName: type mapping: rawscript: '#/components/schemas/RawScript' @@ -6746,6 +6743,71 @@ paths: schema: type: string format: uuid + /w/{workspace}/jobs/run/dependencies: + post: + summary: run a one-off dependencies job + operationId: runRawScriptDependencies + tags: + - job + parameters: + - name: workspace + in: path + required: true + schema: *ref_0 + requestBody: + description: raw script content + required: true + content: + application/json: + schema: + type: object + properties: + raw_scripts: + type: array + items: + type: object + properties: &ref_184 + content: + type: string + path: + type: string + language: + type: string + enum: + - python3 + - deno + - go + - bash + - powershell + - postgresql + - mysql + - bigquery + - snowflake + - mssql + - graphql + - nativets + - bun + required: &ref_185 + - content + - path + - language + entrypoint: + type: string + required: + - entrypoint + - raw_scripts + responses: + '201': + description: dependency job result + content: + application/json: + schema: + type: object + properties: + lock: + type: string + required: + - lock /w/{workspace}/jobs/run/preview_flow: post: summary: run flow preview @@ -11224,56 +11286,60 @@ components: type: object properties: *ref_182 required: *ref_183 - StaticTransform: + RawScriptForDependencies: type: object properties: *ref_184 required: *ref_185 - JavascriptTransform: + StaticTransform: type: object properties: *ref_186 required: *ref_187 + JavascriptTransform: + type: object + properties: *ref_188 + required: *ref_189 InputTransform: oneOf: *ref_26 discriminator: *ref_27 RawScript: - type: object - properties: *ref_188 - required: *ref_189 - PathScript: type: object properties: *ref_190 required: *ref_191 - PathFlow: + PathScript: type: object properties: *ref_192 required: *ref_193 + PathFlow: + type: object + properties: *ref_194 + required: *ref_195 FlowModule: type: object properties: *ref_28 required: *ref_29 ForloopFlow: - type: object - properties: *ref_194 - required: *ref_195 - BranchOne: type: object properties: *ref_196 required: *ref_197 - BranchAll: + BranchOne: type: object properties: *ref_198 required: *ref_199 - Identity: + BranchAll: type: object properties: *ref_200 required: *ref_201 - Graphql: + Identity: type: object properties: *ref_202 required: *ref_203 + Graphql: + type: object + properties: *ref_204 + required: *ref_205 FlowModuleValue: - oneOf: *ref_204 - discriminator: *ref_205 + oneOf: *ref_206 + discriminator: *ref_207 Retry: type: object properties: *ref_78 diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 807e305725..e0af8fa6e2 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -4617,6 +4617,45 @@ paths: type: string format: uuid + /w/{workspace}/jobs/run/dependencies: + post: + summary: run a one-off dependencies job + operationId: runRawScriptDependencies + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + + requestBody: + description: raw script content + required: true + content: + application/json: + schema: + type: object + properties: + raw_scripts: + type: array + items: + $ref: "#/components/schemas/RawScriptForDependencies" + entrypoint: + type: string + required: + - entrypoint + - raw_scripts + responses: + "201": + description: dependency job result + content: + application/json: + schema: + type: object + properties: + lock: + type: string + required: + - lock + /w/{workspace}/jobs/run/preview_flow: post: summary: run flow preview @@ -9313,3 +9352,33 @@ components: required: - timestamp - value + + RawScriptForDependencies: + type: object + properties: + content: + type: string + path: + type: string + language: + type: string + enum: + [ + python3, + deno, + go, + bash, + powershell, + postgresql, + mysql, + bigquery, + snowflake, + mssql, + graphql, + nativets, + bun, + ] + required: + - content + - path + - language diff --git a/backend/windmill-api/src/jobs.rs b/backend/windmill-api/src/jobs.rs index 602afbb8ea..7439dd005e 100644 --- a/backend/windmill-api/src/jobs.rs +++ b/backend/windmill-api/src/jobs.rs @@ -152,6 +152,7 @@ pub fn workspaced_service() -> Router { "/result_by_id/:job_id/:node_id", get(get_result_by_id).layer(cors.clone()), ) + .route("/run/dependencies", post(run_dependencies_job)) } pub fn global_service() -> Router { @@ -1945,10 +1946,10 @@ pub struct WindmillCompositeResult { windmill_content_type: Option, result: Option>, } -async fn run_wait_result( +async fn run_wait_result( db: &DB, uuid: Uuid, - Path((w_id, _)): Path<(String, T)>, + w_id: String, node_id_for_empty_return: Option, ) -> error::Result { let mut result; @@ -2183,7 +2184,7 @@ pub async fn run_wait_result_job_by_path_get( .await?; tx.commit().await?; - let wait_result = run_wait_result(&db, uuid, Path((w_id, script_path)), None).await; + let wait_result = run_wait_result(&db, uuid, w_id, None).await; if delete_after_use.unwrap_or(false) { delete_job_metadata_after_use(&db, uuid).await?; } @@ -2302,7 +2303,7 @@ async fn run_wait_result_script_by_path_internal( .await?; tx.commit().await?; - let wait_result = run_wait_result(&db, uuid, Path((w_id, script_path)), None).await; + let wait_result = run_wait_result(&db, uuid, w_id, None).await; if delete_after_use.unwrap_or(false) { delete_job_metadata_after_use(&db, uuid).await?; } @@ -2377,7 +2378,7 @@ pub async fn run_wait_result_script_by_hash( .await?; tx.commit().await?; - let wait_result = run_wait_result(&db, uuid, Path((w_id, script_hash)), None).await; + let wait_result = run_wait_result(&db, uuid, w_id, None).await; if delete_after_use.unwrap_or(false) { delete_job_metadata_after_use(&db, uuid).await?; } @@ -2459,7 +2460,7 @@ async fn run_wait_result_flow_by_path_internal( .await?; tx.commit().await?; - run_wait_result(&db, uuid, Path((w_id, flow_path)), early_return).await + run_wait_result(&db, uuid, w_id, early_return).await } async fn run_preview_job( @@ -2527,6 +2528,82 @@ async fn run_preview_job( Ok((StatusCode::CREATED, uuid.to_string())) } +#[derive(Deserialize)] +pub struct RunDependenciesRequest { + pub raw_scripts: Vec, + pub entrypoint: String, +} + +#[derive(Deserialize, Clone)] +pub struct RawScriptForDependencies { + pub script_path: String, + pub raw_code: String, + pub language: ScriptLang, +} + +#[derive(Serialize)] +pub struct RunDependenciesResponse { + pub dependencies: String, +} + +pub async fn run_dependencies_job( + authed: ApiAuthed, + Extension(db): Extension, + Extension(rsmq): Extension>, + Path(w_id): Path, + Json(req): Json, +) -> error::Result { + check_scopes(&authed, || format!("runscript"))?; + if authed.is_operator { + return Err(error::Error::NotAuthorized( + "Operators cannot run dependencies jobs for security reasons".to_string(), + )); + } + + if req.raw_scripts.len() != 1 || req.raw_scripts[0].script_path != req.entrypoint { + return Err(error::Error::InternalErr( + "For now only a single raw script can be passed to this endpoint, and the entrypoint should be set to the script path".to_string(), + )); + } + let raw_script = req.raw_scripts[0].clone(); + let script_path = raw_script.script_path; + let raw_code = raw_script.raw_code; + let language = raw_script.language; + + let (uuid, tx) = push( + &db, + PushIsolationLevel::IsolatedRoot(db.clone(), rsmq), + &w_id, + JobPayload::RawScriptDependencies { + script_path: script_path, + content: raw_code, + language: language, + }, + PushArgs::empty(), + &authed.username, + &authed.email, + username_to_permissioned_as(&authed.username), + None, + None, + None, + None, + None, + false, + false, + None, + true, + None, + None, + None, + None, + ) + .await?; + tx.commit().await?; + + let wait_result = run_wait_result(&db, uuid, w_id, None).await; + wait_result +} + #[derive(Deserialize)] struct BatchInfo { kind: String, diff --git a/backend/windmill-common/src/jobs.rs b/backend/windmill-common/src/jobs.rs index 77bf119fea..7f1ad5b8f1 100644 --- a/backend/windmill-common/src/jobs.rs +++ b/backend/windmill-common/src/jobs.rs @@ -294,6 +294,11 @@ pub enum JobPayload { path: String, version: i64, }, + RawScriptDependencies { + script_path: String, + content: String, + language: ScriptLang, + }, Flow { path: String, dedicated_worker: Option, diff --git a/backend/windmill-queue/src/jobs.rs b/backend/windmill-queue/src/jobs.rs index 22963eb784..e9ea7df117 100644 --- a/backend/windmill-queue/src/jobs.rs +++ b/backend/windmill-queue/src/jobs.rs @@ -2516,6 +2516,20 @@ pub async fn push<'c, T: Serialize + Send + Sync, R: rsmq_async::RsmqConnection dedicated_worker, None, ), + JobPayload::RawScriptDependencies { script_path, content, language } => ( + None, + Some(script_path), + Some((content, None)), + JobKind::Dependencies, + None, + None, + Some(language), + None, + None, + None, + None, + None, + ), JobPayload::FlowDependencies { path, dedicated_worker } => { let value_json = fetch_scalar_isolated!( sqlx::query_scalar!( diff --git a/backend/windmill-worker/src/worker.rs b/backend/windmill-worker/src/worker.rs index 30593ea1ff..5dae852ff8 100644 --- a/backend/windmill-worker/src/worker.rs +++ b/backend/windmill-worker/src/worker.rs @@ -3173,14 +3173,18 @@ async fn handle_dependency_job, ) -> error::Result> { - let raw_code = sqlx::query_scalar!( - "SELECT content FROM script WHERE hash = $1 AND workspace_id = $2", - &job.script_hash.unwrap_or(ScriptHash(0)).0, - &job.workspace_id - ) - .fetch_optional(db) - .await? - .unwrap_or_else(|| "No script found at this hash".to_string()); + let raw_code = match job.raw_code { + Some(ref code) => code.to_owned(), + None => sqlx::query_scalar!( + "SELECT content FROM script WHERE hash = $1 AND workspace_id = $2", + &job.script_hash.unwrap_or(ScriptHash(0)).0, + &job.workspace_id + ) + .fetch_optional(db) + .await? + .unwrap_or_else(|| "No script found at this hash".to_string()), + }; + let script_path = job.script_path(); let content = capture_dependency_job( &job.id, @@ -3205,6 +3209,13 @@ async fn handle_dependency_job { + if job.script_hash.is_none() { + // it a one-off raw script dependency job, no need to update the db + return Ok(to_raw_value_owned( + json!({ "success": "Successful lock file generation", "lock": content }), + )); + } + let hash = job.script_hash.unwrap_or(ScriptHash(0)); let w_id = &job.workspace_id; sqlx::query!( diff --git a/cli/bootstrap/common.ts b/cli/bootstrap/common.ts new file mode 100644 index 0000000000..782eb9c8b5 --- /dev/null +++ b/cli/bootstrap/common.ts @@ -0,0 +1,24 @@ + +export interface SchemaProperty { + type: string | undefined + description?: string + pattern?: string + default?: any + enum?: string[] + contentEncoding?: 'base64' | 'binary' + format?: string + items?: { + type?: 'string' | 'number' | 'bytes' | 'object' + contentEncoding?: 'base64' + enum?: string[] + } + min?: number + max?: number + currency?: string + currencyLocale?: string + multiselect?: boolean + customErrorMessage?: string + properties?: { [name: string]: SchemaProperty } + required?: string[] + showExpr?: string +} diff --git a/cli/bootstrap/flow_bootstrap.ts b/cli/bootstrap/flow_bootstrap.ts new file mode 100644 index 0000000000..8bae373b17 --- /dev/null +++ b/cli/bootstrap/flow_bootstrap.ts @@ -0,0 +1,35 @@ +import { SchemaProperty } from "./common.ts"; + +export interface FlowDefinition { + summary: string, + description: string, + value: { + modules: any[] + }, + schema: { + $schema: string, + type: string, + order: string[], + properties: { [name: string]: SchemaProperty}, + required: string[] + } + ws_error_handler_muted: false +} + +export function defaultFlowDefinition(): FlowDefinition { + return { + summary: '', + description: '', + value: { + modules: [] + }, + schema: { + $schema: 'https://json-schema.org/draft/2020-12/schema', + type: 'object', + order: [], + properties: {}, + required: [] + }, + ws_error_handler_muted: false, + } +} diff --git a/cli/bootstrap/script_bootstrap.ts b/cli/bootstrap/script_bootstrap.ts new file mode 100644 index 0000000000..cbc8210e21 --- /dev/null +++ b/cli/bootstrap/script_bootstrap.ts @@ -0,0 +1,83 @@ +import { SchemaProperty } from "./common.ts"; + +export interface ScriptMetadata { + summary: string, + description: string, + lock: string[], + is_template: boolean, + kind: string, + schema: { + $schema: string, + type: string, + properties: { [name: string]: SchemaProperty}, + required: string[] + } +} + +export function defaultScriptMetadata(): ScriptMetadata { + return { + summary: '', + description: '', + lock: [], + is_template: false, + kind: 'script', + schema: { + $schema: 'https://json-schema.org/draft/2020-12/schema', + type: 'object', + properties: {}, + required: [] + } + } +} + +export const scriptBootstrapCode = { + python3: `def main(): + return "Hello world" +`, + + nativets: `export async function main() { + return "Hello world"; +} +`, + + bun: `export async function main() { + return "Hello world"; +} +`, + + deno: `export async function main() { + return "Hello world"; +} +`, + + go: `package inner +func main() (interface{}, error) { + return "Hello world", nil +} +`, + + mysql: `SELECT 'Hello world' AS message +`, + + bigquery: `SELECT 'Hello world' AS message +`, + + snowflake: `SELECT 'Hello world' AS message +`, + + mssql: `SELECT 'Hello world' AS message +`, + + graphql: `query() { + demo() {} +}`, + + postgresql: `SELECT 'Hello world' AS message +`, + + bash: `echo "Hello world" +`, + + powershell: `Write-Output "Hello world"`, + +} \ No newline at end of file diff --git a/cli/flow.ts b/cli/flow.ts index 162587f55f..a670f68c3d 100644 --- a/cli/flow.ts +++ b/cli/flow.ts @@ -1,6 +1,6 @@ // deno-lint-ignore-file no-explicit-any import { GlobalOptions, isSuperset } from "./types.ts"; -import { SEP, log } from "./deps.ts"; +import { SEP, log, yamlStringify } from "./deps.ts"; import { colors, Command, @@ -13,6 +13,7 @@ import { } from "./deps.ts"; import { requireLogin, resolveWorkspace, validatePath } from "./context.ts"; import { resolve, track_job } from "./script.ts"; +import { defaultFlowDefinition } from "./bootstrap/flow_bootstrap.ts"; export interface FlowFile { summary: string; @@ -193,6 +194,31 @@ async function run( log.info(jobInfo.result ?? {}); } +export function bootstrap(opts: GlobalOptions & {summary: string, description: string}, flowPath: string) { + if (!validatePath(flowPath)) { + return; + } + + const flowDirFullPath = `${flowPath}.flow` + Deno.mkdirSync(flowDirFullPath, { recursive: false }); + + const newFlowDefinition = defaultFlowDefinition(); + if (opts.summary !== undefined) { + newFlowDefinition.summary = opts.summary; + } + if (opts.description !== undefined) { + newFlowDefinition.description = opts.description; + } + + const newFlowDefinitionYaml = yamlStringify(newFlowDefinition as Record); + + const flowYamlPath = `${flowDirFullPath}/flow.yaml`; + Deno.writeTextFile( + flowYamlPath, + newFlowDefinitionYaml, + { createNew: true }); +} + const command = new Command() .description("flow related commands") .option("--show-archived", "Enable archived scripts in output") @@ -213,6 +239,11 @@ const command = new Command() "-s --silent", "Do not ouput anything other then the final output. Useful for scripting." ) - .action(run as any); + .action(run as any) + .command("bootstrap", "create a new empty flow") + .arguments("") + .option("--summary ", "script summary") + .option("--description ", "script description") + .action(bootstrap as any); export default command; diff --git a/cli/script.ts b/cli/script.ts index e1f4c8679e..244b36c0ae 100644 --- a/cli/script.ts +++ b/cli/script.ts @@ -13,8 +13,13 @@ import { Table, writeAllSync, yamlParse, + yamlStringify, } from "./deps.ts"; import { deepEqual } from "./utils.ts"; +import { defaultScriptMetadata, scriptBootstrapCode } from "./bootstrap/script_bootstrap.ts"; +import { instantiate as instantiateWasm, parse_bash, parse_bigquery, parse_deno, parse_go, parse_graphql, parse_mssql, parse_mysql, parse_powershell, parse_python, parse_snowflake, parse_sql } from "./wasm/windmill_parser_wasm.generated.js" +import { Workspace } from "./workspace.ts"; +import { SchemaProperty } from "./bootstrap/common.ts"; export interface ScriptFile { parent_hash?: string; @@ -63,6 +68,29 @@ export async function handleScriptMetadata( } } +async function parseMetadataFile(scriptPath: string): Promise<{isJson: boolean, payload: any} | undefined> { + let metadataFilePath = scriptPath + ".script.json"; + try { + await Deno.stat(metadataFilePath); + return { + payload: JSON.parse(await Deno.readTextFile(metadataFilePath)), + isJson: true, + } + } catch { + try { + metadataFilePath = scriptPath + ".script.yaml"; + await Deno.stat(metadataFilePath); + return { + payload: yamlParse(await Deno.readTextFile(metadataFilePath)), + isJson: false, + } + } catch { + // no meta file + } + } + return undefined; +} + export async function handleFile( path: string, workspace: string, @@ -88,21 +116,7 @@ export async function handleFile( const remotePath = path .substring(0, path.indexOf(".")) .replaceAll("\\", "/"); - const metaPath = remotePath + ".script.json"; - let typed = undefined; - try { - await Deno.stat(metaPath); - typed = JSON.parse(await Deno.readTextFile(metaPath)); - } catch { - const metaPath = remotePath + ".script.yaml"; - try { - await Deno.stat(metaPath); - typed = yamlParse(await Deno.readTextFile(metaPath)); - } catch { - // no meta file - } - } - + const typed = (await parseMetadataFile(path))?.payload; const language = inferContentTypeFromFilePath(path); let remote = undefined; @@ -256,22 +270,45 @@ export async function findContentFile(filePath: string) { return validCandidates[0]; } +type ScriptLanguage = "python3" | "deno" | "bun" | "nativets" | "go" | "bash" | "powershell" | "postgresql" | "mysql" | "bigquery" | "snowflake" | "mssql" | "graphql"; + +export function filePathExtensionFromContentType(language: ScriptLanguage): string { + if (language === "python3") { + return ".py"; + } else if (language === "nativets") { + return ".fetch.ts"; + } else if (language === "bun") { + return ".bun.ts"; + } else if (language === "deno") { + return ".ts"; + } else if (language === "go") { + return ".go"; + } else if (language === "mysql") { + return ".my.sql"; + } else if (language === "bigquery") { + return ".bq.sql"; + } else if (language === "snowflake") { + return ".sf.sql"; + } else if (language === "mssql") { + return ".ms.sql"; + } else if (language === "postgresql") { + return ".pg.sql"; + } else if (language === "graphql") { + return ".gql"; + } else if (language === "bash") { + return ".sh"; + } else if (language === "powershell") { + return ".ps1"; + } else { + throw new Error( + "Invalid language: " + language + ); + } +} + export function inferContentTypeFromFilePath( contentPath: string -): - | "python3" - | "deno" - | "bun" - | "nativets" - | "go" - | "bash" - | "powershell" - | "postgresql" - | "mysql" - | "bigquery" - | "snowflake" - | "mssql" - | "graphql" { +): ScriptLanguage { if (contentPath.endsWith(".py")) { return "python3"; } else if (contentPath.endsWith("fetch.ts")) { @@ -305,6 +342,189 @@ export function inferContentTypeFromFilePath( } } +//////////////////////////////////////////////////////////////////////////////////////////// +// below functions copied from Windmill's FE inferArgs function. TODO: refactor // +//////////////////////////////////////////////////////////////////////////////////////////// +export function inferSchema(language: ScriptLanguage, content: string, currentSchema: any) { + let inferedSchema: any; + if (language === "python3") { + inferedSchema = JSON.parse(parse_python(content)); + } else if (language === "nativets") { + inferedSchema = JSON.parse(parse_deno(content)); + } else if (language === "bun") { + inferedSchema = JSON.parse(parse_deno(content)); + } else if (language === "deno") { + inferedSchema = JSON.parse(parse_deno(content)); + } else if (language === "go") { + inferedSchema = JSON.parse(parse_go(content)); + } else if (language === "mysql") { + inferedSchema = JSON.parse(parse_mysql(content)); + inferedSchema.args = [ + { name: 'database', typ: { resource: 'mysql' } }, + ...inferedSchema.args + ] + } else if (language === "bigquery") { + inferedSchema = JSON.parse(parse_bigquery(content)); + inferedSchema.args = [ + { name: 'database', typ: { resource: 'bigquery' } }, + ...inferedSchema.args + ] + } else if (language === "snowflake") { + inferedSchema = JSON.parse(parse_snowflake(content)); + inferedSchema.args = [ + { name: 'database', typ: { resource: 'snowflake' } }, + ...inferedSchema.args + ] + } else if (language === "mssql") { + inferedSchema = JSON.parse(parse_mssql(content)); + inferedSchema.args = [ + { name: 'database', typ: { resource: 'ms_sql_server' } }, + ...inferedSchema.args + ] + } else if (language === "postgresql") { + inferedSchema = JSON.parse(parse_sql(content)); + inferedSchema.args = [ + { name: 'database', typ: { resource: 'postgresql' } }, + ...inferedSchema.args + ] + } else if (language === "graphql") { + inferedSchema = JSON.parse(parse_graphql(content)); + inferedSchema.args = [ + { name: 'api', typ: { resource: 'graphql' } }, + ...inferedSchema.args + ] + } else if (language === "bash") { + inferedSchema = JSON.parse(parse_bash(content)); + } else if (language === "powershell") { + inferedSchema = JSON.parse(parse_powershell(content)); + } else { + throw new Error( + "Invalid language: " + language + ); + } + if (inferedSchema.type == 'Invalid') { + throw new Error(`Script invalid, it cannot be parsed to infer schema. Error was: ${inferedSchema.error}`) + } + + currentSchema.required = [] + const oldProperties = JSON.parse(JSON.stringify(currentSchema.properties)) + currentSchema.properties = {} + + for (const arg of inferedSchema.args) { + if (!(arg.name in oldProperties)) { + currentSchema.properties[arg.name] = { description: '', type: '' } + } else { + currentSchema.properties[arg.name] = oldProperties[arg.name] + } + currentSchema.properties[arg.name] = sortObject(currentSchema.properties[arg.name]) + + argSigToJsonSchemaType(arg.typ, currentSchema.properties[arg.name]) + + currentSchema.properties[arg.name].default = arg.default + + if (!arg.has_default && !currentSchema.required.includes(arg.name)) { + currentSchema.required.push(arg.name) + } + } + + return currentSchema; +} + +function sortObject(obj: any): any { + return Object.keys(obj) + .sort() + .reduce((acc, key) => ({ + ...acc, [key]: obj[key] + }), {}) +} + +function argSigToJsonSchemaType( + typ: + | string + | { resource: string | null } + | { list: string | { str: any } | { object: { key: string; typ: any }[] } | null } + | { str: string[] | null } + | { object: { key: string; typ: any }[] }, + oldS: SchemaProperty +): void { + const newS: SchemaProperty = { type: '' } + if (typ === 'int') { + newS.type = 'integer' + } else if (typ === 'float') { + newS.type = 'number' + } else if (typ === 'bool') { + newS.type = 'boolean' + } else if (typ === 'email') { + newS.type = 'string' + newS.format = 'email' + } else if (typ === 'sql') { + newS.type = 'string' + newS.format = 'sql' + } else if (typ === 'yaml') { + newS.type = 'string' + newS.format = 'yaml' + } else if (typ === 'bytes') { + newS.type = 'string' + newS.contentEncoding = 'base64' + } else if (typ === 'datetime') { + newS.type = 'string' + newS.format = 'date-time' + } else if (typeof typ !== 'string' && `object` in typ) { + newS.type = 'object' + if (typ.object) { + const properties: Record = {}; + for (const prop of typ.object) { + properties[prop.key] = {type: undefined} + argSigToJsonSchemaType(prop.typ, properties[prop.key]) + } + newS.properties = properties + } + } else if (typeof typ !== 'string' && `str` in typ) { + newS.type = 'string' + if (typ.str) { + newS.enum = typ.str + } + } else if (typeof typ !== 'string' && `resource` in typ) { + newS.type = 'object' + newS.format = `resource-${typ.resource}` + } else if (typeof typ !== 'string' && `list` in typ) { + newS.type = 'array' + if (typ.list === 'int' || typ.list === 'float') { + newS.items = { type: 'number' } + } else if (typ.list === 'bytes') { + newS.items = { type: 'string', contentEncoding: 'base64' } + } else if (typ.list == 'string') { + newS.items = { type: 'string' } + } else if (typ.list && typeof typ.list == 'object' && 'str' in typ.list) { + newS.items = { type: 'string', enum: typ.list.str } + } else { + newS.items = { type: 'object' } + } + } else { + newS.type = 'object' + } + + if (oldS.type != newS.type) { + for (const prop of Object.getOwnPropertyNames(newS)) { + if (prop != 'description') { + delete oldS[prop] + } + } + } else if (oldS.format == 'date-time' && newS.format != 'date-time') { + delete oldS.format + } else if (oldS.items?.type != newS.items?.type) { + delete oldS.items + } + + Object.assign(oldS, newS) + if (oldS.format?.startsWith('resource-') && newS.type != 'object') { + oldS.format = undefined + } +} +//////////////////////////////////////////////////////////////////////////////////////////// +// end of refactoring TODO // +//////////////////////////////////////////////////////////////////////////////////////////// + async function list(opts: GlobalOptions & { showArchived?: boolean }) { const workspace = await resolveWorkspace(opts); await requireLogin(opts); @@ -484,6 +704,129 @@ async function show(opts: GlobalOptions, path: string) { log.info(s.content); } +async function bootstrap(opts: GlobalOptions & {summary: string, description: string}, scriptPath: string, language: ScriptLanguage) { + if (!validatePath(scriptPath)) { + return; + } + + const scriptInitialCode = scriptBootstrapCode[language]; + if (scriptInitialCode === undefined) { + throw new Error("Language unknown"); + } + + const extension = filePathExtensionFromContentType(language); + const scriptCodeFileFullPath = scriptPath + extension; + const scriptMetadataFileFullPath = scriptPath + ".script.yaml"; + + try { + await Deno.stat(scriptCodeFileFullPath); + await Deno.stat(scriptMetadataFileFullPath); + throw new Error("File already exists in repository"); + } catch { + // file does not exist, we can continue + } + + const scriptMetadata = defaultScriptMetadata(); + if (opts.summary !== undefined) { + scriptMetadata.summary = opts.summary; + } + if (opts.description !== undefined) { + scriptMetadata.description = opts.description; + } + + const scriptInitialMetadataYaml = yamlStringify(scriptMetadata as Record); + + Deno.writeTextFile( + scriptCodeFileFullPath, + scriptInitialCode, + { createNew: true }); + Deno.writeTextFile( + scriptMetadataFileFullPath, + scriptInitialMetadataYaml, + { createNew: true }); +} + +async function generateMetadata(opts: GlobalOptions & {lockOnly: boolean, schemaOnly: boolean}, scriptPath: string) { + if (!validatePath(scriptPath)) { + return; + } + + const workspace = await resolveWorkspace(opts); + await requireLogin(opts); + + const language = inferContentTypeFromFilePath(scriptPath); + + // read script metadata file + const remotePath = scriptPath + .substring(0, scriptPath.indexOf(".")) + .replaceAll("\\", "/"); + const metadataWithType = await parseMetadataFile(remotePath); + if (metadataWithType === undefined) { + throw new Error("Script metadata file does not exist at this path"); + } + + // read script content + const scriptContent = await Deno.readTextFile(scriptPath); + + const metadataParsedContent = metadataWithType?.payload as Record; + + if (!opts.lockOnly) { + await updateScriptSchema(scriptContent, language, metadataParsedContent); + } + + if (!opts.schemaOnly) { + await updateScriptLock(workspace, scriptContent, language, remotePath, metadataParsedContent); + } + + let metaPath = remotePath + ".script.yaml"; + let newMetadataContent = yamlStringify(metadataParsedContent); + if (metadataWithType.isJson) { + metaPath = remotePath + ".script.json" + newMetadataContent = JSON.stringify(metadataParsedContent); + } + Deno.writeTextFile( + metaPath, + newMetadataContent); +} + +async function updateScriptSchema(scriptContent: string, language: ScriptLanguage, metadataContent: Record): Promise { + // infer schema from script content and update it inplace + await instantiateWasm(); + const newSchema = inferSchema(language, scriptContent, metadataContent.schema); + metadataContent.schema = newSchema; +} + +async function updateScriptLock(workspace: Workspace, scriptContent: string, language: ScriptLanguage, remotePath: string, metadataContent: Record): Promise { + // generate the script lock running a dependency job in Windmill and update it inplace + // TODO: update this once the client is released + const rawResponse = await fetch(`${workspace.remote}api/w/${workspace.workspaceId}/jobs/run/dependencies`, { + method: "POST", + headers: { + "Cookie": `token=${workspace.token}`, + "Content-Type": "application/json" + }, + body: JSON.stringify({ + "raw_scripts": [{ + "raw_code": scriptContent, + "language": language, + "script_path": remotePath, + }], + "entrypoint": remotePath, + }) + }); + + try { + const response = await rawResponse.json(); + const lock = response.lock; + if (lock === undefined) { + throw new Error(`Failed to generate lockfile. Full response was: ${JSON.stringify(response)}`); + } + metadataContent.lock = lock.split("\n"); + } catch { + throw new Error(`Failed to generate lockfile. Status was: ${rawResponse.statusText}`); + } +} + const command = new Command() .description("script related commands") .option("--show-archived", "Enable archived scripts in output") @@ -505,8 +848,18 @@ const command = new Command() ) .option( "-s --silent", - "Do not ouput anything other then the final output. Useful for scripting." + "Do not output anything other then the final output. Useful for scripting." ) - .action(run as any); + .action(run as any) + .command("bootstrap", "create a new script") + .arguments(" ") + .option("--summary ", "script summary") + .option("--description ", "script description") + .action(bootstrap as any) + .command("generate-metadata", "re-generate the metadata file updating the lock and the script schema") + .arguments("") + .option("--lock-only", "re-generate only the lock") + .option("--schema-only", "re-generate only script schema") + .action(generateMetadata as any); export default command; diff --git a/cli/wasm/windmill_parser_wasm.generated.js b/cli/wasm/windmill_parser_wasm.generated.js new file mode 100644 index 0000000000..a233901797 --- /dev/null +++ b/cli/wasm/windmill_parser_wasm.generated.js @@ -0,0 +1,832 @@ +// @generated file from wasmbuild -- do not edit +// deno-lint-ignore-file +// deno-fmt-ignore-file +// source-hash: f38890e80d35ec2affe2a260e12308f96bcbf771 +let wasm; + +const heap = new Array(128).fill(undefined); + +heap.push(undefined, null, true, false); + +function getObject(idx) { + return heap[idx]; +} + +let heap_next = heap.length; + +function dropObject(idx) { + if (idx < 132) return; + heap[idx] = heap_next; + heap_next = idx; +} + +function takeObject(idx) { + const ret = getObject(idx); + dropObject(idx); + return ret; +} + +let WASM_VECTOR_LEN = 0; + +let cachedUint8Memory0 = null; + +function getUint8Memory0() { + if (cachedUint8Memory0 === null || cachedUint8Memory0.byteLength === 0) { + cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); + } + return cachedUint8Memory0; +} + +const cachedTextEncoder = typeof TextEncoder !== "undefined" + ? new TextEncoder("utf-8") + : { + encode: () => { + throw Error("TextEncoder not available"); + }, + }; + +const encodeString = function (arg, view) { + return cachedTextEncoder.encodeInto(arg, view); +}; + +function passStringToWasm0(arg, malloc, realloc) { + if (realloc === undefined) { + const buf = cachedTextEncoder.encode(arg); + const ptr = malloc(buf.length, 1) >>> 0; + getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf); + WASM_VECTOR_LEN = buf.length; + return ptr; + } + + let len = arg.length; + let ptr = malloc(len, 1) >>> 0; + + const mem = getUint8Memory0(); + + let offset = 0; + + for (; offset < len; offset++) { + const code = arg.charCodeAt(offset); + if (code > 0x7F) break; + mem[ptr + offset] = code; + } + + if (offset !== len) { + if (offset !== 0) { + arg = arg.slice(offset); + } + ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; + const view = getUint8Memory0().subarray(ptr + offset, ptr + len); + const ret = encodeString(arg, view); + + offset += ret.written; + } + + WASM_VECTOR_LEN = offset; + return ptr; +} + +function isLikeNone(x) { + return x === undefined || x === null; +} + +let cachedInt32Memory0 = null; + +function getInt32Memory0() { + if (cachedInt32Memory0 === null || cachedInt32Memory0.byteLength === 0) { + cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); + } + return cachedInt32Memory0; +} + +const cachedTextDecoder = typeof TextDecoder !== "undefined" + ? new TextDecoder("utf-8", { ignoreBOM: true, fatal: true }) + : { + decode: () => { + throw Error("TextDecoder not available"); + }, + }; + +if (typeof TextDecoder !== "undefined") cachedTextDecoder.decode(); + +function getStringFromWasm0(ptr, len) { + ptr = ptr >>> 0; + return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); +} + +function addHeapObject(obj) { + if (heap_next === heap.length) heap.push(heap.length + 1); + const idx = heap_next; + heap_next = heap[idx]; + + heap[idx] = obj; + return idx; +} + +let cachedFloat64Memory0 = null; + +function getFloat64Memory0() { + if (cachedFloat64Memory0 === null || cachedFloat64Memory0.byteLength === 0) { + cachedFloat64Memory0 = new Float64Array(wasm.memory.buffer); + } + return cachedFloat64Memory0; +} + +let cachedBigInt64Memory0 = null; + +function getBigInt64Memory0() { + if ( + cachedBigInt64Memory0 === null || cachedBigInt64Memory0.byteLength === 0 + ) { + cachedBigInt64Memory0 = new BigInt64Array(wasm.memory.buffer); + } + return cachedBigInt64Memory0; +} + +function debugString(val) { + // primitive types + const type = typeof val; + if (type == "number" || type == "boolean" || val == null) { + return `${val}`; + } + if (type == "string") { + return `"${val}"`; + } + if (type == "symbol") { + const description = val.description; + if (description == null) { + return "Symbol"; + } else { + return `Symbol(${description})`; + } + } + if (type == "function") { + const name = val.name; + if (typeof name == "string" && name.length > 0) { + return `Function(${name})`; + } else { + return "Function"; + } + } + // objects + if (Array.isArray(val)) { + const length = val.length; + let debug = "["; + if (length > 0) { + debug += debugString(val[0]); + } + for (let i = 1; i < length; i++) { + debug += ", " + debugString(val[i]); + } + debug += "]"; + return debug; + } + // Test for built-in + const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val)); + let className; + if (builtInMatches.length > 1) { + className = builtInMatches[1]; + } else { + // Failed to match the standard '[object ClassName]' + return toString.call(val); + } + if (className == "Object") { + // we're a user defined class or Object + // JSON.stringify avoids problems with cycles, and is generally much + // easier than looping through ownProperties of `val`. + try { + return "Object(" + JSON.stringify(val) + ")"; + } catch (_) { + return "Object"; + } + } + // errors + if (val instanceof Error) { + return `${val.name}: ${val.message}\n${val.stack}`; + } + // TODO we could test for more things here, like `Set`s and `Map`s. + return className; +} +/** + * @param {string} code + * @returns {string} + */ +export function parse_deno(code) { + let deferred2_0; + let deferred2_1; + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0( + code, + wasm.__wbindgen_malloc, + wasm.__wbindgen_realloc, + ); + const len0 = WASM_VECTOR_LEN; + wasm.parse_deno(retptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + deferred2_0 = r0; + deferred2_1 = r1; + return getStringFromWasm0(r0, r1); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + wasm.__wbindgen_free(deferred2_0, deferred2_1, 1); + } +} + +/** + * @param {string} code + * @returns {string} + */ +export function parse_outputs(code) { + let deferred2_0; + let deferred2_1; + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0( + code, + wasm.__wbindgen_malloc, + wasm.__wbindgen_realloc, + ); + const len0 = WASM_VECTOR_LEN; + wasm.parse_outputs(retptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + deferred2_0 = r0; + deferred2_1 = r1; + return getStringFromWasm0(r0, r1); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + wasm.__wbindgen_free(deferred2_0, deferred2_1, 1); + } +} + +/** + * @param {string} code + * @returns {string} + */ +export function parse_ts_imports(code) { + let deferred2_0; + let deferred2_1; + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0( + code, + wasm.__wbindgen_malloc, + wasm.__wbindgen_realloc, + ); + const len0 = WASM_VECTOR_LEN; + wasm.parse_ts_imports(retptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + deferred2_0 = r0; + deferred2_1 = r1; + return getStringFromWasm0(r0, r1); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + wasm.__wbindgen_free(deferred2_0, deferred2_1, 1); + } +} + +/** + * @param {string} code + * @returns {string} + */ +export function parse_bash(code) { + let deferred2_0; + let deferred2_1; + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0( + code, + wasm.__wbindgen_malloc, + wasm.__wbindgen_realloc, + ); + const len0 = WASM_VECTOR_LEN; + wasm.parse_bash(retptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + deferred2_0 = r0; + deferred2_1 = r1; + return getStringFromWasm0(r0, r1); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + wasm.__wbindgen_free(deferred2_0, deferred2_1, 1); + } +} + +/** + * @param {string} code + * @returns {string} + */ +export function parse_powershell(code) { + let deferred2_0; + let deferred2_1; + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0( + code, + wasm.__wbindgen_malloc, + wasm.__wbindgen_realloc, + ); + const len0 = WASM_VECTOR_LEN; + wasm.parse_powershell(retptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + deferred2_0 = r0; + deferred2_1 = r1; + return getStringFromWasm0(r0, r1); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + wasm.__wbindgen_free(deferred2_0, deferred2_1, 1); + } +} + +/** + * @param {string} code + * @returns {string} + */ +export function parse_go(code) { + let deferred2_0; + let deferred2_1; + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0( + code, + wasm.__wbindgen_malloc, + wasm.__wbindgen_realloc, + ); + const len0 = WASM_VECTOR_LEN; + wasm.parse_go(retptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + deferred2_0 = r0; + deferred2_1 = r1; + return getStringFromWasm0(r0, r1); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + wasm.__wbindgen_free(deferred2_0, deferred2_1, 1); + } +} + +/** + * @param {string} code + * @returns {string} + */ +export function parse_python(code) { + let deferred2_0; + let deferred2_1; + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0( + code, + wasm.__wbindgen_malloc, + wasm.__wbindgen_realloc, + ); + const len0 = WASM_VECTOR_LEN; + wasm.parse_python(retptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + deferred2_0 = r0; + deferred2_1 = r1; + return getStringFromWasm0(r0, r1); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + wasm.__wbindgen_free(deferred2_0, deferred2_1, 1); + } +} + +/** + * @param {string} code + * @returns {string} + */ +export function parse_sql(code) { + let deferred2_0; + let deferred2_1; + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0( + code, + wasm.__wbindgen_malloc, + wasm.__wbindgen_realloc, + ); + const len0 = WASM_VECTOR_LEN; + wasm.parse_sql(retptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + deferred2_0 = r0; + deferred2_1 = r1; + return getStringFromWasm0(r0, r1); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + wasm.__wbindgen_free(deferred2_0, deferred2_1, 1); + } +} + +/** + * @param {string} code + * @returns {string} + */ +export function parse_mysql(code) { + let deferred2_0; + let deferred2_1; + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0( + code, + wasm.__wbindgen_malloc, + wasm.__wbindgen_realloc, + ); + const len0 = WASM_VECTOR_LEN; + wasm.parse_mysql(retptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + deferred2_0 = r0; + deferred2_1 = r1; + return getStringFromWasm0(r0, r1); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + wasm.__wbindgen_free(deferred2_0, deferred2_1, 1); + } +} + +/** + * @param {string} code + * @returns {string} + */ +export function parse_bigquery(code) { + let deferred2_0; + let deferred2_1; + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0( + code, + wasm.__wbindgen_malloc, + wasm.__wbindgen_realloc, + ); + const len0 = WASM_VECTOR_LEN; + wasm.parse_bigquery(retptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + deferred2_0 = r0; + deferred2_1 = r1; + return getStringFromWasm0(r0, r1); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + wasm.__wbindgen_free(deferred2_0, deferred2_1, 1); + } +} + +/** + * @param {string} code + * @returns {string} + */ +export function parse_snowflake(code) { + let deferred2_0; + let deferred2_1; + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0( + code, + wasm.__wbindgen_malloc, + wasm.__wbindgen_realloc, + ); + const len0 = WASM_VECTOR_LEN; + wasm.parse_snowflake(retptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + deferred2_0 = r0; + deferred2_1 = r1; + return getStringFromWasm0(r0, r1); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + wasm.__wbindgen_free(deferred2_0, deferred2_1, 1); + } +} + +/** + * @param {string} code + * @returns {string} + */ +export function parse_mssql(code) { + let deferred2_0; + let deferred2_1; + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0( + code, + wasm.__wbindgen_malloc, + wasm.__wbindgen_realloc, + ); + const len0 = WASM_VECTOR_LEN; + wasm.parse_mssql(retptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + deferred2_0 = r0; + deferred2_1 = r1; + return getStringFromWasm0(r0, r1); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + wasm.__wbindgen_free(deferred2_0, deferred2_1, 1); + } +} + +/** + * @param {string} code + * @returns {string} + */ +export function parse_graphql(code) { + let deferred2_0; + let deferred2_1; + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passStringToWasm0( + code, + wasm.__wbindgen_malloc, + wasm.__wbindgen_realloc, + ); + const len0 = WASM_VECTOR_LEN; + wasm.parse_graphql(retptr, ptr0, len0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + deferred2_0 = r0; + deferred2_1 = r1; + return getStringFromWasm0(r0, r1); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + wasm.__wbindgen_free(deferred2_0, deferred2_1, 1); + } +} + +function handleError(f, args) { + try { + return f.apply(this, args); + } catch (e) { + wasm.__wbindgen_exn_store(addHeapObject(e)); + } +} + +const imports = { + __wbindgen_placeholder__: { + __wbindgen_object_drop_ref: function (arg0) { + takeObject(arg0); + }, + __wbindgen_string_get: function (arg0, arg1) { + const obj = getObject(arg1); + const ret = typeof obj === "string" ? obj : undefined; + var ptr1 = isLikeNone(ret) + ? 0 + : passStringToWasm0( + ret, + wasm.__wbindgen_malloc, + wasm.__wbindgen_realloc, + ); + var len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }, + __wbindgen_error_new: function (arg0, arg1) { + const ret = new Error(getStringFromWasm0(arg0, arg1)); + return addHeapObject(ret); + }, + __wbindgen_boolean_get: function (arg0) { + const v = getObject(arg0); + const ret = typeof v === "boolean" ? (v ? 1 : 0) : 2; + return ret; + }, + __wbindgen_is_bigint: function (arg0) { + const ret = typeof (getObject(arg0)) === "bigint"; + return ret; + }, + __wbindgen_bigint_from_i64: function (arg0) { + const ret = arg0; + return addHeapObject(ret); + }, + __wbindgen_jsval_eq: function (arg0, arg1) { + const ret = getObject(arg0) === getObject(arg1); + return ret; + }, + __wbindgen_bigint_from_u64: function (arg0) { + const ret = BigInt.asUintN(64, arg0); + return addHeapObject(ret); + }, + __wbindgen_number_get: function (arg0, arg1) { + const obj = getObject(arg1); + const ret = typeof obj === "number" ? obj : undefined; + getFloat64Memory0()[arg0 / 8 + 1] = isLikeNone(ret) ? 0 : ret; + getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(ret); + }, + __wbindgen_is_object: function (arg0) { + const val = getObject(arg0); + const ret = typeof val === "object" && val !== null; + return ret; + }, + __wbindgen_in: function (arg0, arg1) { + const ret = getObject(arg0) in getObject(arg1); + return ret; + }, + __wbg_eval_c330e1bec5b1705c: function (arg0, arg1) { + const ret = eval(getStringFromWasm0(arg0, arg1)); + return addHeapObject(ret); + }, + __wbindgen_jsval_loose_eq: function (arg0, arg1) { + const ret = getObject(arg0) == getObject(arg1); + return ret; + }, + __wbg_get_f01601b5a68d10e3: function (arg0, arg1) { + const ret = getObject(arg0)[arg1 >>> 0]; + return addHeapObject(ret); + }, + __wbg_length_1009b1af0c481d7b: function (arg0) { + const ret = getObject(arg0).length; + return ret; + }, + __wbindgen_is_function: function (arg0) { + const ret = typeof (getObject(arg0)) === "function"; + return ret; + }, + __wbg_next_9b877f231f476d01: function (arg0) { + const ret = getObject(arg0).next; + return addHeapObject(ret); + }, + __wbg_next_6529ee0cca8d57ed: function () { + return handleError(function (arg0) { + const ret = getObject(arg0).next(); + return addHeapObject(ret); + }, arguments); + }, + __wbg_done_5fe336b092d60cf2: function (arg0) { + const ret = getObject(arg0).done; + return ret; + }, + __wbg_value_0c248a78fdc8e19f: function (arg0) { + const ret = getObject(arg0).value; + return addHeapObject(ret); + }, + __wbg_iterator_db7ca081358d4fb2: function () { + const ret = Symbol.iterator; + return addHeapObject(ret); + }, + __wbg_get_7b48513de5dc5ea4: function () { + return handleError(function (arg0, arg1) { + const ret = Reflect.get(getObject(arg0), getObject(arg1)); + return addHeapObject(ret); + }, arguments); + }, + __wbg_call_90c26b09837aba1c: function () { + return handleError(function (arg0, arg1) { + const ret = getObject(arg0).call(getObject(arg1)); + return addHeapObject(ret); + }, arguments); + }, + __wbg_isArray_74fb723e24f76012: function (arg0) { + const ret = Array.isArray(getObject(arg0)); + return ret; + }, + __wbg_instanceof_ArrayBuffer_e7d53d51371448e2: function (arg0) { + let result; + try { + result = getObject(arg0) instanceof ArrayBuffer; + } catch (_) { + result = false; + } + const ret = result; + return ret; + }, + __wbg_isSafeInteger_f93fde0dca9820f8: function (arg0) { + const ret = Number.isSafeInteger(getObject(arg0)); + return ret; + }, + __wbg_entries_9e2e2aa45aa5094a: function (arg0) { + const ret = Object.entries(getObject(arg0)); + return addHeapObject(ret); + }, + __wbg_buffer_a448f833075b71ba: function (arg0) { + const ret = getObject(arg0).buffer; + return addHeapObject(ret); + }, + __wbg_new_8f67e318f15d7254: function (arg0) { + const ret = new Uint8Array(getObject(arg0)); + return addHeapObject(ret); + }, + __wbg_set_2357bf09366ee480: function (arg0, arg1, arg2) { + getObject(arg0).set(getObject(arg1), arg2 >>> 0); + }, + __wbg_length_1d25fa9e4ac21ce7: function (arg0) { + const ret = getObject(arg0).length; + return ret; + }, + __wbg_instanceof_Uint8Array_bced6f43aed8c1aa: function (arg0) { + let result; + try { + result = getObject(arg0) instanceof Uint8Array; + } catch (_) { + result = false; + } + const ret = result; + return ret; + }, + __wbindgen_bigint_get_as_i64: function (arg0, arg1) { + const v = getObject(arg1); + const ret = typeof v === "bigint" ? v : undefined; + getBigInt64Memory0()[arg0 / 8 + 1] = isLikeNone(ret) ? BigInt(0) : ret; + getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(ret); + }, + __wbindgen_debug_string: function (arg0, arg1) { + const ret = debugString(getObject(arg1)); + const ptr1 = passStringToWasm0( + ret, + wasm.__wbindgen_malloc, + wasm.__wbindgen_realloc, + ); + const len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }, + __wbindgen_throw: function (arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); + }, + __wbindgen_memory: function () { + const ret = wasm.memory; + return addHeapObject(ret); + }, + }, +}; + +import { Loader } from "https://deno.land/x/wasmbuild@0.15.4/loader.ts"; +import { cacheToLocalDir } from "https://deno.land/x/wasmbuild@0.15.4/cache.ts"; + +const loader = new Loader({ + imports, + cache: cacheToLocalDir, +}); +/** + * Decompression callback + * + * @callback DecompressCallback + * @param {Uint8Array} compressed + * @return {Uint8Array} decompressed + */ + +/** + * Options for instantiating a Wasm instance. + * @typedef {Object} InstantiateOptions + * @property {URL=} url - Optional url to the Wasm file to instantiate. + * @property {DecompressCallback=} decompress - Callback to decompress the + * raw Wasm file bytes before instantiating. + */ + +/** Instantiates an instance of the Wasm module returning its functions. + * @remarks It is safe to call this multiple times and once successfully + * loaded it will always return a reference to the same object. + * @param {InstantiateOptions=} opts + */ +export async function instantiate(opts) { + return (await instantiateWithInstance(opts)).exports; +} + +/** Instantiates an instance of the Wasm module along with its exports. + * @remarks It is safe to call this multiple times and once successfully + * loaded it will always return a reference to the same object. + * @param {InstantiateOptions=} opts + * @returns {Promise<{ + * instance: WebAssembly.Instance; + * exports: { parse_deno: typeof parse_deno; parse_outputs: typeof parse_outputs; parse_ts_imports: typeof parse_ts_imports; parse_bash: typeof parse_bash; parse_powershell: typeof parse_powershell; parse_go: typeof parse_go; parse_python: typeof parse_python; parse_sql: typeof parse_sql; parse_mysql: typeof parse_mysql; parse_bigquery: typeof parse_bigquery; parse_snowflake: typeof parse_snowflake; parse_mssql: typeof parse_mssql; parse_graphql: typeof parse_graphql } + * }>} + */ +export async function instantiateWithInstance(opts) { + const { instance } = await loader.load( + opts?.url ?? new URL("windmill_parser_wasm_bg.wasm", import.meta.url), + opts?.decompress, + ); + wasm = wasm ?? instance.exports; + cachedInt32Memory0 = cachedInt32Memory0 ?? new Int32Array(wasm.memory.buffer); + cachedUint8Memory0 = cachedUint8Memory0 ?? new Uint8Array(wasm.memory.buffer); + return { + instance, + exports: getWasmInstanceExports(), + }; +} + +function getWasmInstanceExports() { + return { + parse_deno, + parse_outputs, + parse_ts_imports, + parse_bash, + parse_powershell, + parse_go, + parse_python, + parse_sql, + parse_mysql, + parse_bigquery, + parse_snowflake, + parse_mssql, + parse_graphql, + }; +} + +/** Gets if the Wasm module has been instantiated. */ +export function isInstantiated() { + return loader.instance != null; +} diff --git a/cli/wasm/windmill_parser_wasm_bg.wasm b/cli/wasm/windmill_parser_wasm_bg.wasm new file mode 100644 index 0000000000..3b447b45aa Binary files /dev/null and b/cli/wasm/windmill_parser_wasm_bg.wasm differ