feat: bun support (#1800)

* feat: bun support

* feat: bun support

* add bun support

* add bun support

* add bun support

* add bun
This commit is contained in:
Ruben Fiszel
2023-07-06 21:19:07 +02:00
committed by GitHub
parent d2aeaa6661
commit 4317f065f5
21 changed files with 1005 additions and 245 deletions

View File

@@ -130,6 +130,8 @@ COPY --from=builder /windmill/target/release/windmill ${APP}/windmill
COPY --from=nsjail /nsjail/nsjail /bin/nsjail
COPY --from=denoland/deno:1.35.0 /usr/bin/deno /usr/bin/deno
COPY --from=oven/bun:0.6.13 /usr/bin/bun /usr/bin/bun
# docker does not support conditional COPY and we want to use the same Dockerfile for both amd64 and arm64 and privilege the official image
COPY --from=lukechannings/deno:v1.35.0 /usr/bin/deno /usr/bin/deno-arm

View File

@@ -22,8 +22,9 @@ use tokio::{
use windmill_api::{LICENSE_KEY, OAUTH_CLIENTS, SMTP_CLIENT};
use windmill_common::{utils::rd_string, METRICS_ADDR};
use windmill_worker::{
DENO_CACHE_DIR, DENO_TMP_CACHE_DIR, GO_CACHE_DIR, GO_TMP_CACHE_DIR, HUB_CACHE_DIR,
HUB_TMP_CACHE_DIR, PIP_CACHE_DIR, ROOT_TMP_CACHE_DIR, TAR_PIP_TMP_CACHE_DIR,
BUN_CACHE_DIR, BUN_TMP_CACHE_DIR, DENO_CACHE_DIR, DENO_TMP_CACHE_DIR, GO_CACHE_DIR,
GO_TMP_CACHE_DIR, HUB_CACHE_DIR, HUB_TMP_CACHE_DIR, PIP_CACHE_DIR, ROOT_TMP_CACHE_DIR,
TAR_PIP_TMP_CACHE_DIR,
};
const GIT_VERSION: &str = git_version!(args = ["--tag", "--always"], fallback = "unknown-version");
@@ -334,10 +335,12 @@ pub async fn run_workers<R: rsmq_async::RsmqConnection + Send + Sync + Clone + '
for x in [
PIP_CACHE_DIR,
DENO_CACHE_DIR,
BUN_CACHE_DIR,
GO_CACHE_DIR,
HUB_CACHE_DIR,
TAR_PIP_TMP_CACHE_DIR,
DENO_TMP_CACHE_DIR,
BUN_TMP_CACHE_DIR,
GO_TMP_CACHE_DIR,
HUB_TMP_CACHE_DIR,
] {

View File

@@ -5109,7 +5109,7 @@ paths:
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/RunnableId"
- $ref: "#/components/parameters/RunnableType"
- $ref: "#/components/parameters/RunnableTypeQuery"
- $ref: "#/components/parameters/Page"
- $ref: "#/components/parameters/PerPage"
responses:
@@ -5131,7 +5131,7 @@ paths:
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/RunnableId"
- $ref: "#/components/parameters/RunnableType"
- $ref: "#/components/parameters/RunnableTypeQuery"
- $ref: "#/components/parameters/Page"
- $ref: "#/components/parameters/PerPage"
responses:
@@ -5153,7 +5153,7 @@ paths:
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/RunnableId"
- $ref: "#/components/parameters/RunnableType"
- $ref: "#/components/parameters/RunnableTypeQuery"
requestBody:
description: Input
required: true
@@ -5491,7 +5491,7 @@ components:
in: query
schema:
type: string
RunnableType:
RunnableTypeQuery:
name: runnable_type
in: query
schema:

View File

@@ -4,10 +4,7 @@ mode: ONCE
hostname: "bash"
log_level: ERROR
rlimit_as: 4096
rlimit_cpu: 1000
rlimit_fsize: 1000
rlimit_nofile: 10000
disable_rl: true
cwd: "/tmp"

View File

@@ -0,0 +1,121 @@
name: "bun run script"
mode: ONCE
hostname: "bun"
log_level: ERROR
disable_rl: true
mount_proc: true
cwd: "/tmp/bun"
clone_newnet: false
clone_newuser: {CLONE_NEWUSER}
keep_caps: false
keep_env: true
mount {
src: "/bin"
dst: "/bin"
is_bind: true
}
mount {
src: "/lib"
dst: "/lib"
is_bind: true
}
mount {
src: "/lib64"
dst: "/lib64"
is_bind: true
}
mount {
src: "/usr"
dst: "/usr"
is_bind: true
}
mount {
src: "/dev/null"
dst: "/dev/null"
is_bind: true
rw: true
}
mount {
dst: "/tmp"
fstype: "tmpfs"
rw: true
options: "size=500000000"
}
mount {
src: "{JOB_DIR}/wrapper.ts"
dst: "/tmp/bun/wrapper.ts"
is_bind: true
mandatory: false
}
mount {
src: "{JOB_DIR}/main.ts"
dst: "/tmp/bun/main.ts"
is_bind: true
mandatory: false
}
mount {
src: "{JOB_DIR}/args.json"
dst: "/tmp/bun/args.json"
is_bind: true
}
mount {
src: "{JOB_DIR}/result.json"
dst: "/tmp/bun/result.json"
rw: true
is_bind: true
}
mount {
src: "/etc"
dst: "/etc"
is_bind: true
}
mount {
src: "/dev/random"
dst: "/dev/random"
is_bind: true
}
mount {
src: "/dev/urandom"
dst: "/dev/urandom"
is_bind: true
}
iface_no_lo: true
mount {
src: "{CACHE_DIR}"
dst: "/tmp/windmill/cache/bun"
is_bind: true
rw: true
mandatory: false
}
{SHARED_MOUNT}
envar: "HOME=/tmp/bun"

View File

@@ -4,10 +4,7 @@ mode: ONCE
hostname: "go"
log_level: ERROR
rlimit_as: 4096
rlimit_cpu: 1000
rlimit_fsize: 1000
rlimit_nofile: 10000
disable_rl: true
cwd: "/tmp/go"

View File

@@ -153,6 +153,8 @@ pub async fn copy_cache_from_bucket(bucket: &str, tx: Sender<()>) -> error::Resu
"--filter",
"+ deno/**",
"--filter",
"+ bun/**",
"--filter",
"+ go/**",
"--filter",
"+ tar/**",
@@ -195,6 +197,8 @@ pub async fn copy_cache_to_bucket(bucket: &str) -> error::Result<()> {
"--filter",
"+ deno/**",
"--filter",
"+ bun/**",
"--filter",
"+ go/**",
"--filter",
"- *",
@@ -226,6 +230,7 @@ pub async fn copy_cache_to_bucket_as_tar(bucket: &str) {
&format!("{ROOT_TMP_CACHE_DIR}{TAR_CACHE_FILENAME}"),
"go",
"deno",
"bun",
],
)
.await
@@ -277,7 +282,7 @@ pub async fn copy_cache_to_bucket_as_tar(bucket: &str) {
pub async fn copy_denogo_cache_from_bucket_as_tar(bucket: &str) {
use tokio::fs::metadata;
tracing::info!("Copying denogo cache from bucket {bucket} as tar");
tracing::info!("Copying deno,go,bun cache from bucket {bucket} as tar");
let start: Instant = Instant::now();
@@ -295,7 +300,7 @@ pub async fn copy_denogo_cache_from_bucket_as_tar(bucket: &str) {
)
.await
{
tracing::info!("Failed copying denogo tar from cache. Error: {:?}", e);
tracing::info!("Failed copying deno,go,bun tar from cache. Error: {:?}", e);
return;
}
@@ -309,7 +314,7 @@ pub async fn copy_denogo_cache_from_bucket_as_tar(bucket: &str) {
)
.await
{
tracing::info!("Failed to untar denogo. Error: {:?}", e);
tracing::info!("Failed to untar denogobun tar. Error: {:?}", e);
return;
}
@@ -321,12 +326,12 @@ pub async fn copy_denogo_cache_from_bucket_as_tar(bucket: &str) {
if let Err(e) =
tokio::fs::remove_file(format!("{ROOT_TMP_CACHE_DIR}{TAR_CACHE_FILENAME}")).await
{
tracing::info!("Failed to remove denotar cache. Error: {:?}", e);
tracing::info!("Failed to remove denogobuntar cache. Error: {:?}", e);
return;
};
tracing::info!(
"Finished copying denogotar from bucket {bucket} as tar, took: {:?}s",
"Finished copying denogobuntar from bucket {bucket} as tar, took: {:?}s",
start.elapsed().as_secs()
);
}
@@ -390,6 +395,8 @@ pub async fn copy_tmp_cache_to_cache() -> error::Result<()> {
"--filter",
"+ deno/**",
"--filter",
"+ bun/**",
"--filter",
"+ go/**",
"--filter",
"- *",
@@ -480,6 +487,8 @@ pub async fn copy_cache_to_tmp_cache() -> error::Result<()> {
"--filter",
"+ deno/**",
"--filter",
"+ bun/**",
"--filter",
"+ go/**",
"--filter",
"- *",

View File

@@ -197,6 +197,7 @@ func Run(req Req) (interface{{}}, error){{
Command::new(NSJAIL_PATH.as_str())
.current_dir(job_dir)
.env_clear()
.envs(envs)
.envs(reserved_variables)
.env("PATH", PATH_ENV.as_str())
.env("BASE_INTERNAL_URL", base_internal_url)

View File

@@ -136,10 +136,12 @@ pub const ROOT_TMP_CACHE_DIR: &str = "/tmp/windmill/tmpcache/";
pub const PIP_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "pip");
pub const DENO_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "deno");
pub const GO_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "go");
pub const BUN_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "bun");
pub const HUB_CACHE_DIR: &str = concatcp!(ROOT_CACHE_DIR, "hub");
pub const TAR_PIP_TMP_CACHE_DIR: &str = concatcp!(ROOT_TMP_CACHE_DIR, "tar/pip");
pub const DENO_TMP_CACHE_DIR: &str = concatcp!(ROOT_TMP_CACHE_DIR, "deno");
pub const BUN_TMP_CACHE_DIR: &str = concatcp!(ROOT_TMP_CACHE_DIR, "bun");
pub const GO_TMP_CACHE_DIR: &str = concatcp!(ROOT_TMP_CACHE_DIR, "go");
pub const HUB_TMP_CACHE_DIR: &str = concatcp!(ROOT_TMP_CACHE_DIR, "hub");
@@ -148,6 +150,7 @@ const NUM_SECS_PING: u64 = 5;
const INCLUDE_DEPS_PY_SH_CONTENT: &str = include_str!("../nsjail/download_deps.py.sh");
const NSJAIL_CONFIG_RUN_BASH_CONTENT: &str = include_str!("../nsjail/run.bash.config.proto");
const NSJAIL_CONFIG_RUN_BUN_CONTENT: &str = include_str!("../nsjail/run.bun.config.proto");
pub const DEFAULT_TIMEOUT: u64 = 900;
@@ -187,6 +190,7 @@ lazy_static::lazy_static! {
pub static ref HTTP_PROXY: Option<String> = std::env::var("http_proxy").ok().or(std::env::var("HTTP_PROXY").ok());
pub static ref HTTPS_PROXY: Option<String> = std::env::var("https_proxy").ok().or(std::env::var("HTTPS_PROXY").ok());
pub static ref DENO_PATH: String = std::env::var("DENO_PATH").unwrap_or_else(|_| "/usr/bin/deno".to_string());
pub static ref BUN_PATH: String = std::env::var("BUN_PATH").unwrap_or_else(|_| "/usr/bin/bun".to_string());
pub static ref NSJAIL_PATH: String = std::env::var("NSJAIL_PATH").unwrap_or_else(|_| "nsjail".to_string());
pub static ref PATH_ENV: String = std::env::var("PATH").unwrap_or_else(|_| String::new());
pub static ref HOME_ENV: String = std::env::var("HOME").unwrap_or_else(|_| String::new());
@@ -1532,7 +1536,8 @@ mount {{
&inner_content,
base_internal_url,
worker_name,
envs
envs,
&shared_mount
)
.await
}
@@ -1688,6 +1693,21 @@ fn get_common_deno_proc_envs(token: &str, base_internal_url: &str) -> HashMap<St
return deno_envs;
}
fn get_common_bun_proc_envs(base_internal_url: &str) -> HashMap<String, String> {
let mut deno_envs: HashMap<String, String> = HashMap::from([
(String::from("PATH"), PATH_ENV.clone()),
(String::from("DO_NOT_TRACK"), "1".to_string()),
(String::from("BASE_INTERNAL_URL"), base_internal_url.to_string()),
(String::from("BUN_INSTALL_CACHE_DIR"), BUN_CACHE_DIR.to_string()),
]);
if let Some(ref s) = *NPM_CONFIG_REGISTRY {
deno_envs.insert(String::from("NPM_CONFIG_REGISTRY"), s.clone());
}
return deno_envs;
}
#[tracing::instrument(level = "trace", skip_all)]
async fn handle_deno_job(
logs: &mut String,
@@ -1811,7 +1831,7 @@ run().catch(async (e) => {{
//do not cache local dependencies
let reload = format!("--reload={base_internal_url}");
let child = async {
let child = {
let script_path = format!("{job_dir}/wrapper.ts");
let import_map_path = format!("{job_dir}/import_map.json");
let mut args = Vec::with_capacity(12);
@@ -1844,9 +1864,8 @@ run().catch(async (e) => {{
.args(args)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
}
.await?;
.spawn()?
};
// logs.push_str(format!("prepare: {:?}\n", start.elapsed().as_micros()).as_str());
// start = Instant::now();
handle_child(&job.id, db, logs, child, false, worker_name, &job.workspace_id, "deno run").await?;
@@ -1868,10 +1887,11 @@ async fn handle_bun_job(
base_internal_url: &str,
worker_name: &str,
envs: HashMap<String, String>,
shared_mount: &str,
) -> error::Result<serde_json::Value> {
// let mut start = Instant::now();
logs.push_str("\n\n--- DENO CODE EXECUTION ---\n");
logs.push_str("\n\n--- BUN CODE EXECUTION ---\n");
let logs_to_set = logs.clone();
let id = job.id.clone();
@@ -1901,8 +1921,8 @@ async fn handle_bun_job(
r#"
import {{ main }} from "./main.ts";
const args = await Deno.readTextFile("args.json")
.then(JSON.parse)
const args = await Bun.file("args.json").json()
.then(({{ {spread} }}) => [ {spread} ])
BigInt.prototype.toJSON = function () {{
@@ -1913,12 +1933,12 @@ BigInt.prototype.toJSON = function () {{
async function run() {{
let res: any = await main(...args);
const res_json = JSON.stringify(res ?? null, (key, value) => typeof value === 'undefined' ? null : value);
await Deno.writeTextFile("result.json", res_json);
Deno.exit(0);
await Bun.write("result.json", res_json);
process.exit(0);
}}
run().catch(async (e) => {{
await Deno.writeTextFile("result.json", JSON.stringify({{ message: e.message, name: e.name, stack: e.stack }}));
Deno.exit(1);
await Bun.write("result.json", JSON.stringify({{ message: e.message, name: e.name, stack: e.stack }}));
process.exit(1);
}});
"#,
);
@@ -1926,33 +1946,6 @@ run().catch(async (e) => {{
Ok(()) as error::Result<()>
};
let write_import_map_f = async {
let w_id = job.workspace_id.clone();
let script_path_split = job.script_path().split("/");
let script_path_parts_len = script_path_split.clone().count();
let mut relative_mounts = "".to_string();
for c in 0..script_path_parts_len {
relative_mounts += ",\n ";
relative_mounts += &format!("\"./{}\": \"{base_internal_url}/api/w/{w_id}/scripts/raw/p/{}{}\"",
(0..c).map(|_| "../").join(""),
&script_path_split.clone().take(script_path_parts_len - c - 1).join("/"),
if c == script_path_parts_len - 1 { "" } else { "/" },
);
}
let import_map = format!(
r#"{{
"imports": {{
"{base_internal_url}/api/w/{w_id}/scripts/raw/p/": "{base_internal_url}/api/w/{w_id}/scripts/raw/p/",
"{base_internal_url}": "{base_internal_url}/api/w/{w_id}/scripts/raw/p/",
"/": "{base_internal_url}/api/w/{w_id}/scripts/raw/p/",
"./wrapper.ts": "./wrapper.ts",
"./main.ts": "./main.ts"{relative_mounts}
}}
}}"#,
);
write_file(job_dir, "import_map.json", &import_map).await?;
Ok(()) as error::Result<()>
};
let reserved_variables_args_out_f = async {
let client = client.get_authed().await;
@@ -1961,68 +1954,68 @@ run().catch(async (e) => {{
Ok(()) as Result<()>
};
let reserved_variables_f = async {
let mut vars = get_reserved_variables(job, &client.token, db).await?;
vars.insert("RUST_LOG".to_string(), "info".to_string());
let vars = get_reserved_variables(job, &client.token, db).await?;
Ok(vars) as Result<HashMap<String, String>>
};
let (_, reserved_variables) = tokio::try_join!(args_and_out_f, reserved_variables_f)?;
Ok((reserved_variables, client.token)) as error::Result<(HashMap<String, String>, String)>
Ok(reserved_variables) as error::Result<HashMap<String, String>>
};
let (_, (reserved_variables, token), _, _, _) = tokio::try_join!(
let (_, reserved_variables, _, _) = tokio::try_join!(
set_logs_f,
reserved_variables_args_out_f,
write_main_f,
write_wrapper_f,
write_import_map_f)?;
write_wrapper_f)?;
let common_bun_proc_envs = get_common_bun_proc_envs(&base_internal_url);
let common_deno_proc_envs = get_common_deno_proc_envs(&token, base_internal_url);
//do not cache local dependencies
let reload = format!("--reload={base_internal_url}");
let child = async {
let child = if !*DISABLE_NSJAIL {
let _ = write_file(
job_dir,
"run.config.proto",
&NSJAIL_CONFIG_RUN_BUN_CONTENT
.replace("{JOB_DIR}", job_dir)
.replace("{CACHE_DIR}", BUN_CACHE_DIR)
.replace("{CLONE_NEWUSER}", &(!*DISABLE_NUSER).to_string())
.replace("{SHARED_MOUNT}", shared_mount),
)
.await?;
Command::new(NSJAIL_PATH.as_str())
.current_dir(job_dir)
.env_clear()
.envs(envs)
.envs(reserved_variables)
.envs(common_bun_proc_envs)
.env("PATH", PATH_ENV.as_str())
.env("BASE_INTERNAL_URL", base_internal_url)
.args(vec!["--config", "run.config.proto", "--", &BUN_PATH, "run", "/tmp/bun/wrapper.ts", "--prefer-offline"])
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?
} else {
let script_path = format!("{job_dir}/wrapper.ts");
let import_map_path = format!("{job_dir}/import_map.json");
let mut args = Vec::with_capacity(12);
args.push("run");
args.push("--no-check");
args.push("--import-map");
args.push(&import_map_path);
args.push(&reload);
args.push("--unstable");
if let Some(deno_flags) = DENO_FLAGS.as_ref() {
for flag in deno_flags {
args.push(flag);
}
} else if !*DISABLE_NSJAIL {
args.push("--allow-net");
args.push("--allow-read=./");
args.push("--allow-write=./");
args.push("--allow-env");
} else {
args.push("-A");
}
let mut args = vec!["run", &script_path, "--prefer-offline"];
args.push(&script_path);
Command::new(DENO_PATH.as_str())
Command::new(&*BUN_PATH)
.current_dir(job_dir)
.env_clear()
.envs(envs)
.envs(reserved_variables)
.envs(common_deno_proc_envs)
.env("DENO_DIR", DENO_CACHE_DIR)
.envs(common_bun_proc_envs)
.args(args)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
}
.await?;
.spawn()?
};
// logs.push_str(format!("prepare: {:?}\n", start.elapsed().as_micros()).as_str());
// start = Instant::now();
handle_child(&job.id, db, logs, child, false, worker_name, &job.workspace_id, "deno run").await?;
handle_child(&job.id, db, logs, child, false, worker_name, &job.workspace_id, "bun run").await?;
// logs.push_str(format!("execute: {:?}\n", start.elapsed().as_millis()).as_str());
if let Err(e) = tokio::fs::remove_dir_all(format!("{DENO_CACHE_DIR}/gen/file/{job_dir}")).await {
tracing::error!("failed to remove deno gen tmp cache dir: {}", e);
}
read_result(job_dir).await
}

View File

@@ -21,13 +21,6 @@ export {
WorkspaceService,
} from "./windmill-api/index.ts";
// @ts-ignore: Otherwise BigInt is not supported for export
BigInt.prototype.toJSON = function () {
return this.toString();
};
export { pgSql, pgClient } from "./pg.ts";
export type Sql = string;
export type Email = string;
export type Base64 = string;

View File

@@ -1,74 +0,0 @@
import { createConnection } from "https://deno.land/x/mysql2@v1.0.6/mod.ts";
import { type Resource } from "./mod.ts";
/**
* Establish MySQL connection using MySQL client for Deno:
* https://deno.land/x/mysql2@v1.0.6/mod.ts
*
* IMPORTANT: make sure to close the connection with `.end()`
*
* @param db the MySQL resource to establish the connection for
*
* @returns MySQL database connection
*
* @example
* ```ts
* const conn = await mysqlClient(db);
* await conn.execute('CREATE TABLE IF NOT EXISTS pets (name varchar(255), kind varchar(255))');
* await conn.execute('INSERT INTO pets VALUES (?, ?)', ['behemot','cat']);
* const [rows] = await conn.execute('SELECT * from pets');
* conn.end();
* console.log(rows);
* ```
*/
export async function mysqlClient(
db: Resource<"mysql">
) {
const conn = await createConnection(db);
return conn;
}
/**
* Execute SQL query. For more info check:
* https://deno.land/x/mysql2@v1.0.6/mod.ts
*
* @param db the MySQL resource to run sql query for
*
* @returns array with two items: rows and fields
*
* @example
* ```ts
* const kind = 'cat';
* const { rows } = await mySql(db)`SELECT * from pets WHERE kind = ${kind}`;
* console.log(rows);
* ```
*/
export function mySql(
db: Resource<"mysql">,
asObjects = false
) {
return async function execute(
query: TemplateStringsArray,
...args: unknown[],
) {
const conn = await mysqlClient(db);
const adapter = getQueryAdapter(query, args);
const [rows, fields] = await conn.execute(...adapter);
conn.end();
return { rows: asObjects ? rows : getRowsAdapter(rows), fields };
}
}
function getQueryAdapter(template: TemplateStringsArray, args: unknown[]) {
const text = template.reduce((curr, next) => {
return `${curr}?${next}`;
});
return [text, args];
}
function getRowsAdapter(rows: object[] | object) {
if (!Array.isArray(rows)) {
return rows;
}
return rows.map((r) => Object.values(r))
}

View File

@@ -1,57 +0,0 @@
import { Client } from "https://deno.land/x/postgres@v0.17.0/mod.ts"
import { type Resource } from "./mod.ts"
/**
* deno-postgres client API is very flexible:
* https://deno.land/x/postgres@v0.17.0/mod.ts?s=QueryClient
*
* @param db the PostgreSQL resource to generate the client for
*
* @returns the client for the resource
*
* @example
* // Static query
* ```ts
* const { rows } = await pgClient(db).queryObject(
* "SELECT ID, NAME FROM CLIENTS"
* );
* ```
*
* // Prepared Statements
* ```ts
* const { rows } = await pgClient(db).queryObject`SELECT ID, NAME FROM CLIENTS WHERE ID = ${id}`;
* ```
*/
export function pgClient(
db: Resource<"postgresql">
) {
const databaseUrl = 'postgresql://' + db.user + ':' + db.password + '@' + db.host + ':' + db.port + '/' + db.dbname + '?sslmode=' + db.sslmode
return new Client(databaseUrl)
}
/**
* deno-postgres client API is very flexible:
* https://deno.land/x/postgres@v0.17.0/mod.ts?s=QueryClient
*
* @param db the PostgreSQL resource to run sql query for
*
* @returns the rows corresponding to the returned objetcs
*
* @example
* // Prepared Statements
* ```ts
* const { rows } = await pgSql(db)`SELECT ID, NAME FROM CLIENTS WHERE ID = ${id}`;
* ```
*/
export function pgSql(
db: Resource<"postgresql">,
asObjects = false
) {
return async function queryObject(
query: TemplateStringsArray,
...args: unknown[]
) {
const client = pgClient(db)
return asObjects ? await client.queryObject(query, ...args) : await client.queryArray(query, ...args)
}
}

View File

@@ -67,7 +67,6 @@
if (SCRIPT_SHOW_BASH) {
langs.push(['Bash', Script.language.BASH])
}
// langs.push(['Typescript (Bun experimental)', Script.language.BUN])
langs.push(['PostgreSQL', Script.language.POSTGRESQL])
langs.push(['REST', Script.language.NATIVETS])
const scriptKindOptions: {
@@ -331,6 +330,19 @@
>
<LanguageIcon lang="docker" /><span class="ml-2 py-2">Docker</span>
</Button>
<Button
size="xs"
variant="border"
color={script.language == 'bun' ? 'blue' : 'dark'}
btnClasses={script.language == 'bun' ? '!border-2 !bg-blue-50/75' : 'm-[1px]'}
disabled={lockedLanguage}
on:click={() => {
initContent(Script.language.BUN, script.kind, template)
script.language = Script.language.BUN
}}
>
<LanguageIcon lang="bun" /><span class="ml-2 py-2">Typescript (Bun, experimental)</span>
</Button>
<!-- <Button
size="sm"

View File

@@ -45,7 +45,7 @@ export async function main(
`
export const BUN_INIT_CODE = `// import { toWords } from "number-to-words@1"
// import { VariableService } from "windmill-client"
import { setClient, getVariable } from "windmill-client@0.3.14"
// fill the type, or use the +Resource type to get a type-safe reference to a resource
// type Postgresql = object
@@ -57,7 +57,8 @@ export async function main(
d = "inferred type string from default arg",
e = { nested: "object" },
) {
// let x = await wmill.getVariable('u/user/foo')
// setClient()
// let x = await getVariable('u/user/foo')
return { foo: a };
}
`

View File

@@ -0,0 +1,2 @@
src/
*.sh

View File

@@ -4,3 +4,6 @@ set -e
npx --yes openapi-typescript-codegen --input ../backend/windmill-api/openapi.yaml \
--output ./src --useOptions \
&& sed -i '213 i \\ request.referrerPolicy = \"no-referrer\"\n' src/core/request.ts
cp client.ts src/
echo 'export { setClient, getVariable, setVariable, getResource, setResource } from "./client";' >> src/index.ts

364
typescript-client/client.ts Normal file
View File

@@ -0,0 +1,364 @@
import { JobService, ResourceService, VariableService } from "./index";
import { OpenAPI } from "./index";
export {
AdminService,
AuditService,
FlowService,
GranularAclService,
GroupService,
JobService,
ResourceService,
VariableService,
ScriptService,
ScheduleService,
SettingsService,
UserService,
WorkspaceService,
} from "./index";
export type Sql = string;
export type Email = string;
export type Base64 = string;
export type Resource<S extends string> = any;
export const SHARED_FOLDER = "/shared";
export function setClient(token?: string, baseUrl?: string) {
if (baseUrl === undefined) {
baseUrl =
getEnv("BASE_INTERNAL_URL") ??
getEnv("BASE_URL") ??
"http://localhost:8000";
}
if (token === undefined) {
token = getEnv("WM_TOKEN") ?? "no_token";
}
OpenAPI.WITH_CREDENTIALS = true;
OpenAPI.TOKEN = token;
OpenAPI.BASE = baseUrl + "/api";
}
const getEnv = (key: string) => {
if (typeof window === "undefined") {
// node
return process.env[key];
}
// browser
return window.process.env[key];
};
/**
* Create a client configuration from env variables
* @returns client configuration
*/
export function getWorkspace(): string {
return getEnv("WM_WORKSPACE") ?? "no_workspace";
}
/**
* Get a resource value by path
* @param path path of the resource, default to internal state path
* @param undefinedIfEmpty if the resource does not exist, return undefined instead of throwing an error
* @returns resource value
*/
export async function getResource(
path?: string,
undefinedIfEmpty?: boolean
): Promise<any> {
const workspace = getWorkspace();
path = path ?? getStatePath();
try {
const resource = await ResourceService.getResource({ workspace, path });
return await _transformLeaf(resource.value);
} catch (e: any) {
if (undefinedIfEmpty && e.status === 404) {
return undefined;
} else {
throw Error(`Resource not found at ${path} or not visible to you`);
}
}
}
/**
* Resolve a resource value in case the default value was picked because the input payload was undefined
* @param obj resource value or path of the resource under the format `$res:path`
* @returns resource value
*/
export async function resolveDefaultResource(obj: any): Promise<any> {
if (typeof obj === "string" && obj.startsWith("$res:")) {
return await getResource(obj.substring(5), true);
} else {
return obj;
}
}
/**
* Get the full resource value by path
* @param path path of the resource, default to internal state path
* @param undefinedIfEmpty if the resource does not exist, return undefined instead of throwing an error
* @returns full resource
*/
export async function getFullResource(
path?: string,
undefinedIfEmpty?: boolean
): Promise<any> {
const workspace = getWorkspace();
path = path ?? getStatePath();
try {
const resource = await ResourceService.getResource({ workspace, path });
const value = await _transformLeaf(resource.value);
return { ...resource, value };
} catch (e: any) {
if (undefinedIfEmpty && e.status === 404) {
return undefined;
} else {
throw Error(`Resource not found at ${path} or not visible to you`);
}
}
}
export function getStatePath(): string {
const state_path = getEnv("WM_STATE_PATH");
if (state_path === undefined) {
throw Error("State path not set");
}
return state_path;
}
/**
* Set a resource value by path
* @param path path of the resource to set, default to state path
* @param value new value of the resource to set
* @param initializeToTypeIfNotExist if the resource does not exist, initialize it with this type
*/
export async function setResource(
value: any,
path?: string,
initializeToTypeIfNotExist?: string
): Promise<void> {
path = path ?? getStatePath();
const workspace = getWorkspace();
if (await ResourceService.existsResource({ workspace, path })) {
await ResourceService.updateResourceValue({
workspace,
path,
requestBody: { value },
});
} else if (initializeToTypeIfNotExist) {
await ResourceService.createResource({
workspace,
requestBody: { path, value, resource_type: initializeToTypeIfNotExist },
});
} else {
throw Error(
`Resource at path ${path} does not exist and no type was provided to initialize it`
);
}
}
/**
* Set the state
* @param state state to set
* @deprecated use setState instead
*/
export async function setInternalState(state: any): Promise<void> {
await setResource(state, undefined, "state");
}
/**
* Set the state
* @param state state to set
*/
export async function setState(state: any): Promise<void> {
await setResource(state, undefined, "state");
}
// /**
// * Set the shared state
// * @param state state to set
// */
// export async function setSharedState(
// state: any,
// path = "state.json"
// ): Promise<void> {
// await Deno.writeTextFile(SHARED_FOLDER + "/" + path, JSON.stringify(state));
// }
// /**
// * Get the shared state
// * @param state state to set
// */
// export async function getSharedState(path = "state.json"): Promise<any> {
// return JSON.parse(await Deno.readTextFile(SHARED_FOLDER + "/" + path));
// }
/**
* Get the internal state
* @deprecated use getState instead
*/
export async function getInternalState(): Promise<any> {
return await getResource(getStatePath(), true);
}
/**
* Get the state shared across executions
*/
export async function getState(): Promise<any> {
return await getResource(getStatePath(), true);
}
/**
* Get a variable by path
* @param path path of the variable
* @returns variable value
*/
export async function getVariable(path: string): Promise<string | undefined> {
const workspace = getWorkspace();
try {
const variable = await VariableService.getVariable({ workspace, path });
return variable.value;
} catch (e: any) {
throw Error(`Variable not found at ${path} or not visible to you`);
}
}
/**
* Set a variable by path, create if not exist
* @param path path of the variable
* @param value value of the variable
* @param isSecretIfNotExist if the variable does not exist, create it as secret or not (default: false)
* @param descriptionIfNotExist if the variable does not exist, create it with this description (default: "")
*/
export async function setVariable(
path: string,
value: string,
isSecretIfNotExist?: boolean,
descriptionIfNotExist?: string
): Promise<void> {
const workspace = getWorkspace();
if (await VariableService.existsVariable({ workspace, path })) {
await VariableService.updateVariable({
workspace,
path,
requestBody: { value },
});
} else {
await VariableService.createVariable({
workspace,
requestBody: {
path,
value,
is_secret: isSecretIfNotExist ?? false,
description: descriptionIfNotExist ?? "",
},
});
}
}
async function transformLeaves(d: {
[key: string]: any;
}): Promise<{ [key: string]: any }> {
for (const k in d) {
d[k] = await _transformLeaf(d[k]);
}
return d;
}
const VAR_RESOURCE_PREFIX = "$var:";
const RES_RESOURCE_PREFIX = "$res:";
async function _transformLeaf(v: any): Promise<any> {
if (typeof v === "object") {
return transformLeaves(v);
} else if (typeof v === "string" && v.startsWith(VAR_RESOURCE_PREFIX)) {
const varName = v.substring(VAR_RESOURCE_PREFIX.length);
return await getVariable(varName);
} else if (typeof v === "string" && v.startsWith(RES_RESOURCE_PREFIX)) {
const resName = v.substring(RES_RESOURCE_PREFIX.length);
return await getResource(resName);
} else {
return v;
}
}
export async function databaseUrlFromResource(path: string): Promise<string> {
const resource = await getResource(path);
return `postgresql://${resource.user}:${resource.password}@${resource.host}:${resource.port}/${resource.dbname}?sslmode=${resource.sslmode}`;
}
// /**
// * Get URLs needed for resuming a flow after this step
// * @param approver approver name
// * @returns approval page UI URL, resume and cancel API URLs for resumeing the flow
// */
// export async function getResumeUrls(approver?: string): Promise<{
// approvalPage: string;
// resume: string;
// cancel: string;
// }> {
// const nonce = Math.floor(Math.random() * 4294967295);
// const workspace = getWorkspace();
// return await JobService.getResumeUrls({
// workspace,
// resumeId: nonce,
// approver,
// id: process.env.get("WM_JOB_ID") ?? "NO_JOB_ID",
// });
// }
export function base64ToUint8Array(data: string): Uint8Array {
return Uint8Array.from(atob(data), (c) => c.charCodeAt(0));
}
export function uint8ArrayToBase64(arrayBuffer: Uint8Array): string {
let base64 = "";
const encodings =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const bytes = new Uint8Array(arrayBuffer);
const byteLength = bytes.byteLength;
const byteRemainder = byteLength % 3;
const mainLength = byteLength - byteRemainder;
let a, b, c, d;
let chunk;
// Main loop deals with bytes in chunks of 3
for (let i = 0; i < mainLength; i = i + 3) {
// Combine the three bytes into a single integer
chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
// Use bitmasks to extract 6-bit segments from the triplet
a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18
b = (chunk & 258048) >> 12; // 258048 = (2^6 - 1) << 12
c = (chunk & 4032) >> 6; // 4032 = (2^6 - 1) << 6
d = chunk & 63; // 63 = 2^6 - 1
// Convert the raw binary segments to the appropriate ASCII encoding
base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d];
}
// Deal with the remaining bytes and padding
if (byteRemainder == 1) {
chunk = bytes[mainLength];
a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2
// Set the 4 least significant bits to zero
b = (chunk & 3) << 4; // 3 = 2^2 - 1
base64 += encodings[a] + encodings[b] + "==";
} else if (byteRemainder == 2) {
chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1];
a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10
b = (chunk & 1008) >> 4; // 1008 = (2^6 - 1) << 4
// Set the 2 least significant bits to zero
c = (chunk & 15) << 2; // 15 = 2^4 - 1
base64 += encodings[a] + encodings[b] + encodings[c] + "=";
}
return base64;
}

385
typescript-client/package-lock.json generated Normal file
View File

@@ -0,0 +1,385 @@
{
"name": "windmill-client",
"version": "0.3.11",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "windmill-client",
"version": "0.3.11",
"license": "Apache 2.0",
"devDependencies": {
"@digitak/tsc-esm": "^3.1.4",
"@types/node": "^20.4.0",
"tsc": "^2.0.4",
"typescript": "^5.1.6"
}
},
"node_modules/@digitak/grubber": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/@digitak/grubber/-/grubber-3.1.4.tgz",
"integrity": "sha512-pqsnp2BUYlDoTXWG34HWgEJse/Eo1okRgNex8IG84wHrJp8h3SakeR5WhB4VxSA2+/D+frNYJ0ch3yXzsfNDoA==",
"dev": true
},
"node_modules/@digitak/tsc-esm": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/@digitak/tsc-esm/-/tsc-esm-3.1.4.tgz",
"integrity": "sha512-D3AbtMYfh/BarDe0HS3nPh93zavleQH+UfeoWosz6p14YYG+zXPYqbPt9zwXJXrE9C3/g6DRLvBTK/l/qW2WlA==",
"dev": true,
"dependencies": {
"@digitak/grubber": "^3.1.3",
"fast-glob": "^3.2.5",
"relaxed-json": "^1.0.3"
},
"bin": {
"tsc-esm": "binary/tsc-esm.js"
}
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
"dev": true,
"dependencies": {
"@nodelib/fs.stat": "2.0.5",
"run-parallel": "^1.1.9"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/@nodelib/fs.stat": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
"dev": true,
"engines": {
"node": ">= 8"
}
},
"node_modules/@nodelib/fs.walk": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
"dev": true,
"dependencies": {
"@nodelib/fs.scandir": "2.1.5",
"fastq": "^1.6.0"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/@types/node": {
"version": "20.4.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.0.tgz",
"integrity": "sha512-jfT7iTf/4kOQ9S7CHV9BIyRaQqHu67mOjsIQBC3BKZvzvUB6zLxEwJ6sBE3ozcvP8kF6Uk5PXN0Q+c0dfhGX0g==",
"dev": true
},
"node_modules/ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"dependencies": {
"color-convert": "^1.9.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dev": true,
"dependencies": {
"fill-range": "^7.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"dependencies": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"dependencies": {
"color-name": "1.1.3"
}
},
"node_modules/color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
"dev": true
},
"node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true
},
"node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"dev": true,
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/fast-glob": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz",
"integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==",
"dev": true,
"dependencies": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
"glob-parent": "^5.1.2",
"merge2": "^1.3.0",
"micromatch": "^4.0.4"
},
"engines": {
"node": ">=8.6.0"
}
},
"node_modules/fastq": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
"integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
"dev": true,
"dependencies": {
"reusify": "^1.0.4"
}
},
"node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dev": true,
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"dev": true,
"engines": {
"node": ">=4"
}
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"dependencies": {
"is-extglob": "^2.1.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
"dev": true,
"engines": {
"node": ">= 8"
}
},
"node_modules/micromatch": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
"dev": true,
"dependencies": {
"braces": "^3.0.2",
"picomatch": "^2.3.1"
},
"engines": {
"node": ">=8.6"
}
},
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/relaxed-json": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/relaxed-json/-/relaxed-json-1.0.3.tgz",
"integrity": "sha512-b7wGPo7o2KE/g7SqkJDDbav6zmrEeP4TK2VpITU72J/M949TLe/23y/ZHJo+pskcGM52xIfFoT9hydwmgr1AEg==",
"dev": true,
"dependencies": {
"chalk": "^2.4.2",
"commander": "^2.6.0"
},
"bin": {
"rjson": "bin/rjson.js"
},
"engines": {
"node": ">= 0.10.0"
}
},
"node_modules/reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
"dev": true,
"engines": {
"iojs": ">=1.0.0",
"node": ">=0.10.0"
}
},
"node_modules/run-parallel": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"dependencies": {
"queue-microtask": "^1.2.2"
}
},
"node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"dependencies": {
"has-flag": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/tsc": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/tsc/-/tsc-2.0.4.tgz",
"integrity": "sha512-fzoSieZI5KKJVBYGvwbVZs/J5za84f2lSTLPYf6AGiIf43tZ3GNrI1QzTLcjtyDDP4aLxd46RTZq1nQxe7+k5Q==",
"dev": true,
"bin": {
"tsc": "bin/tsc"
}
},
"node_modules/typescript": {
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz",
"integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
}
}
}

View File

@@ -1,12 +1,19 @@
{
"name": "windmill-client",
"description": "Windmill SDK client for browsers and Node.js",
"version": "0.3.1",
"version": "0.3.15",
"author": "Ruben Fiszel",
"license": "Apache 2.0",
"dependencies": {},
"devDependencies": {},
"devDependencies": {
"@types/node": "^20.4.0"
},
"main": "dist/index.js",
"types": "dist/index.d.ts",
"prepublish": "tsc"
"prepublish": "tsc",
"files": [
"dist",
"LICENSE",
"README.md",
"package.json"
]
}

View File

@@ -1,3 +1,5 @@
./build.sh
rm client.ts
tsc
cp src/client.ts .
npm publish

View File

@@ -1,7 +1,6 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
"declaration": true,
"outDir": "dist",
/* Projects */
@@ -13,7 +12,7 @@
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2018" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
"target": "ES6" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
@@ -27,7 +26,7 @@
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "commonjs" /* Specify what module code is generated. */,
"module": "CommonJS" /* Specify what module code is generated. */,
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */