diff --git a/JS/wasm/assets/wasmjs/wit/http.wit b/JS/wasm/assets/wasmjs/wit/http.wit index df2a5f05b..78b4a54ec 100644 --- a/JS/wasm/assets/wasmjs/wit/http.wit +++ b/JS/wasm/assets/wasmjs/wit/http.wit @@ -1,4 +1,6 @@ use * from http-types send-http-request: func(request: http-request) -> expected -parse-jsonnet: func(file: string) -> expected +jsonnet: func(file: string) -> expected +jsonnet-ext-var: func(name: string, value: string) -> expected + read-bytes: func(file: string) -> expected diff --git a/JS/wasm/crates/wasmjs-engine/shims/src/arakoo-jsonnet.js b/JS/wasm/crates/wasmjs-engine/shims/src/arakoo-jsonnet.js index b64160d75..30a29b98f 100644 --- a/JS/wasm/crates/wasmjs-engine/shims/src/arakoo-jsonnet.js +++ b/JS/wasm/crates/wasmjs-engine/shims/src/arakoo-jsonnet.js @@ -1,3 +1,3 @@ -const parseJsonnet = globalThis.parseJsonnet; - -export { parseJsonnet }; +const jsonnet = globalThis.jsonnet; +const jsonnetExtVars = globalThis.jsonnetExtVars; +export { jsonnet, jsonnetExtVars }; diff --git a/JS/wasm/crates/wasmjs-engine/src/main.rs b/JS/wasm/crates/wasmjs-engine/src/main.rs index 6ccb308f0..92861f98e 100644 --- a/JS/wasm/crates/wasmjs-engine/src/main.rs +++ b/JS/wasm/crates/wasmjs-engine/src/main.rs @@ -162,11 +162,11 @@ pub fn load_bindings(context: &JSContextRef, global: JSValueRef) -> Result<(), R global .set_property( - "parseJsonnet", + "jsonnet", context .wrap_callback(|_ctx, _this_arg, args| { let path = args[0].to_string(); - match parse_jsonnet(path.as_str()) { + match jsonnet(path.as_str()) { Ok(result) => Ok(JSValue::String(result)), Err(err) => { let kind = match err { @@ -188,6 +188,36 @@ pub fn load_bindings(context: &JSContextRef, global: JSValueRef) -> Result<(), R .map_err(|_| RuntimeError::InvalidBinding { invalid_export: "parseJsonnet".to_string(), })?; + + global + .set_property( + "jsonnetExtVars", + context + .wrap_callback(|_ctx, _this_arg, args| { + let path = args[0].to_string(); + let ext_var = args[1].to_string(); + match jsonnet_ext_var(path.as_str(), ext_var.as_str()) { + Ok(result) => Ok(JSValue::String(result)), + Err(err) => { + let kind = match err { + FileError::NotFound => "File not found".to_string(), + FileError::InvalidPath => "Not allowed".to_string(), + }; + Ok(JSValue::from_hashmap(HashMap::from([ + ("error", JSValue::Bool(true)), + ("type", JSValue::String(kind)), + ]))) + } + } + }) + .map_err(|_| RuntimeError::InvalidBinding { + invalid_export: "parseJsonnet".to_string(), + })?, + ) + .map_err(|_| RuntimeError::InvalidBinding { + invalid_export: "parseJsonnet".to_string(), + })?; + Ok(()) } @@ -227,19 +257,19 @@ fn main() { let global = context.global_object().unwrap(); match load_bindings(context, global) { Ok(_) => {} - Err(e) => { - match e { - RuntimeError::InvalidBinding { invalid_export } => { - eprintln!("There was an error adding the '{invalid_export}' binding"); - } + Err(e) => match e { + RuntimeError::InvalidBinding { invalid_export } => { + eprintln!("There was an error adding the '{invalid_export}' binding"); } - } + }, } context.eval_module("buffer", &buffer).unwrap(); context.eval_module("crypto", &crypto).unwrap(); - context.eval_module("arakoo-jsonnet", &ARAKOOJSONNET).unwrap(); - + context + .eval_module("arakoo-jsonnet", &ARAKOOJSONNET) + .unwrap(); + match context.eval_module("path", &path) { Ok(_) => {} Err(err) => eprintln!("Error loading the path shim: {err}"), diff --git a/JS/wasm/crates/wasmjs-engine/wasmjs-engine.wasm b/JS/wasm/crates/wasmjs-engine/wasmjs-engine.wasm index 0dc549c56..fa3bdb0e0 100755 Binary files a/JS/wasm/crates/wasmjs-engine/wasmjs-engine.wasm and b/JS/wasm/crates/wasmjs-engine/wasmjs-engine.wasm differ diff --git a/JS/wasm/crates/wasmjs-runtime/src/bindings.rs b/JS/wasm/crates/wasmjs-runtime/src/bindings.rs index 591fcbd0c..4de98ca39 100644 --- a/JS/wasm/crates/wasmjs-runtime/src/bindings.rs +++ b/JS/wasm/crates/wasmjs-runtime/src/bindings.rs @@ -1,10 +1,13 @@ +use std::collections::HashMap; + +use crate::error::Error; use actix_web::http::Uri; use jsonnet::JsonnetVm; use reqwest::Method; use serde::Deserialize; +use serde_json::Value; use tokio::runtime::Builder; use wiggle::GuestErrorType; -use crate::error::Error; wit_bindgen_wasmtime::export!({paths: ["../../assets/wasmjs/wit/http.wit",], async:[] @@ -16,13 +19,16 @@ wiggle::from_witx!({ errors: { arakoo_status => Error }, }); -impl GuestErrorType for ArakooStatus { +impl GuestErrorType for ArakooStatus { fn success() -> Self { ArakooStatus::Ok } } -use self::{http::{Http, HttpRequest, HttpRequestError, HttpResponse, HttpError, HttpMethod, FileError}, types::ArakooStatus}; +use self::{ + http::{FileError, Http, HttpError, HttpMethod, HttpRequest, HttpRequestError, HttpResponse}, + types::ArakooStatus, +}; #[derive(Deserialize, Clone)] #[serde(default)] @@ -205,15 +211,12 @@ impl Http for HttpBindings { .block_on(async { let bytes = tokio::fs::read(path).await; match bytes { - Ok(bytes) => - Ok(std::str::from_utf8(&bytes).unwrap().to_string()), - Err(_) => { - Err(FileError::NotFound) - } + Ok(bytes) => Ok(std::str::from_utf8(&bytes).unwrap().to_string()), + Err(_) => Err(FileError::NotFound), } }) }) - .join(); + .join(); match thread_result { Ok(res) => match res { @@ -224,14 +227,47 @@ impl Http for HttpBindings { } } + fn jsonnet(&mut self, file: &str) -> Result { + jsonnet(file) + } - fn parse_jsonnet(&mut self, file: &str) -> Result { - parse_jsonnet(file) + fn jsonnet_ext_var(&mut self, file: &str, ext_var: &str) -> Result { + let file = file.to_owned(); + let ext_var = ext_var.to_owned(); + let thread_result = std::thread::spawn(move || { + Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + .block_on(async { + let mut vm = JsonnetVm::new(); + let ext_var_str = ext_var.as_str(); + let ext_vars: HashMap<&str, Value> = serde_json::from_str(ext_var_str).unwrap(); + for (key, value) in ext_vars { + vm.ext_var(key, value.as_str().unwrap()); + } + let json = vm.evaluate_file(&file); + match json { + Ok(json) => Ok(json.to_string()), + Err(e) => { + println!("Error: {}", e); + Err(FileError::NotFound) + } + } + }) + }) + .join(); + match thread_result { + Ok(res) => match res { + Ok(res) => Ok(res), + Err(err) => Err(err), + }, + Err(_) => Err(FileError::NotFound), + } } } - -pub fn parse_jsonnet(file: &str) -> Result { +pub fn jsonnet(file: &str) -> Result { let file = file.to_owned(); let thread_result = std::thread::spawn(move || { Builder::new_current_thread() @@ -247,11 +283,11 @@ pub fn parse_jsonnet(file: &str) -> Result { Err(e) => { println!("Error: {}", e); Err(FileError::NotFound) - }, + } } }) }) - .join(); + .join(); match thread_result { Ok(res) => match res { diff --git a/JS/wasm/crates/wasmjs-runtime/test-vars.jsonnet b/JS/wasm/crates/wasmjs-runtime/test-vars.jsonnet new file mode 100644 index 000000000..b856b4a6a --- /dev/null +++ b/JS/wasm/crates/wasmjs-runtime/test-vars.jsonnet @@ -0,0 +1,6 @@ +local var1 = std.extVar("var1"); + +{ + var: var1, +} + diff --git a/JS/wasm/examples/ec-wasmjs-hono/src/index.js b/JS/wasm/examples/ec-wasmjs-hono/src/index.js index 583cabd8d..fff2d6b99 100644 --- a/JS/wasm/examples/ec-wasmjs-hono/src/index.js +++ b/JS/wasm/examples/ec-wasmjs-hono/src/index.js @@ -1,44 +1,62 @@ import { Hono } from "hono"; import { connect } from "@planetscale/database"; -import { parseJsonnet } from "arakoo-jsonnet" +import { jsonnet, jsonnetExtVars } from "arakoo-jsonnet"; const app = new Hono(); app.get("/", (c) => { - const geo = c.req.raw.geo; - return c.text(`Your from ${geo.city}, ${geo.country_name}!`); + const geo = c.req.raw.geo; + return c.text(`Your from ${geo.city}, ${geo.country_name}!`); }); app.get("/jsonnet", async (c) => { - const jsonnet = await parseJsonnet('test.jsonnet') - return c.json(JSON.parse(jsonnet)); + try { + const result = await jsonnet("test.jsonnet"); + return c.json(JSON.parse(result)); + } catch (error) { + console.log(JSON.stringify(error)); + c.text(error); + } +}); + +app.get("/vars", async (c) => { + try { + const extVars = JSON.stringify({ + var1: "value1", + }); + const result = await jsonnetExtVars("test-vars.jsonnet", extVars); + return c.json(JSON.parse(result)); + } catch (error) { + console.log(JSON.stringify(error)); + c.text(error); + } }); app.get("/hello/:name", async (c) => { - const name = c.req.param("name"); - return c.text(`Async Hello ${name}!`); + const name = c.req.param("name"); + return c.text(`Async Hello ${name}!`); }); app.get("/env/:key", async (c) => { - const key = c.req.param("key"); - return c.text(env[key]); + const key = c.req.param("key"); + return c.text(env[key]); }); const config = { - host: env["PLANETSCALE_HOST"], - username: env["PLANETSCALE_USERNAME"], - password: env["PLANETSCALE_PASSWORD"], + host: env["PLANETSCALE_HOST"], + username: env["PLANETSCALE_USERNAME"], + password: env["PLANETSCALE_PASSWORD"], }; const conn = connect(config); app.get("/db", async (c) => { - const result = await conn.execute("SHOW TABLES"); + const result = await conn.execute("SHOW TABLES"); - return c.json(result); + return c.json(result); }); app.notFound((c) => { - return c.text("404 not found", 404); + return c.text("404 not found", 404); }); export default app; diff --git a/JS/wasm/types/jsonnet/.gitignore b/JS/wasm/types/jsonnet/.gitignore index 8ea5b111e..05dd501fb 100644 --- a/JS/wasm/types/jsonnet/.gitignore +++ b/JS/wasm/types/jsonnet/.gitignore @@ -1,5 +1,4 @@ target -index.node **/node_modules **/.DS_Store npm-debug.log* diff --git a/JS/wasm/types/jsonnet/Cargo.lock b/JS/wasm/types/jsonnet/Cargo.lock index bd343e56a..6f64a6f41 100644 --- a/JS/wasm/types/jsonnet/Cargo.lock +++ b/JS/wasm/types/jsonnet/Cargo.lock @@ -8,6 +8,7 @@ version = "0.1.0" dependencies = [ "jsonnet-rs", "neon", + "serde_json", ] [[package]] @@ -25,6 +26,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + [[package]] name = "jsonnet-rs" version = "0.17.0" @@ -87,7 +94,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7288eac8b54af7913c60e0eb0e2a7683020dffa342ab3fd15e28f035ba897cf" dependencies = [ "quote", - "syn", + "syn 1.0.109", "syn-mid", ] @@ -104,22 +111,28 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.71" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + [[package]] name = "semver" version = "0.9.0" @@ -135,6 +148,37 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "serde" +version = "1.0.195" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.195" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "serde_json" +version = "1.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "smallvec" version = "1.11.2" @@ -152,6 +196,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn-mid" version = "0.5.4" @@ -160,7 +215,7 @@ checksum = "fea305d57546cc8cd04feb14b62ec84bf17f50e3f7b12560d7bfa9265f39d9ed" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] diff --git a/JS/wasm/types/jsonnet/Cargo.toml b/JS/wasm/types/jsonnet/Cargo.toml index 0b5163643..ea90e9942 100644 --- a/JS/wasm/types/jsonnet/Cargo.toml +++ b/JS/wasm/types/jsonnet/Cargo.toml @@ -12,6 +12,7 @@ path = "src/lib.rs" [dependencies] jsonnet-rs = "0.17" +serde_json = "1.0.111" [dependencies.neon] version = "0.10" diff --git a/JS/wasm/types/jsonnet/index.node b/JS/wasm/types/jsonnet/index.node new file mode 100755 index 000000000..e8b213e8b Binary files /dev/null and b/JS/wasm/types/jsonnet/index.node differ diff --git a/JS/wasm/types/jsonnet/package-lock.json b/JS/wasm/types/jsonnet/package-lock.json index a8728d922..369f0ebff 100644 --- a/JS/wasm/types/jsonnet/package-lock.json +++ b/JS/wasm/types/jsonnet/package-lock.json @@ -1,26 +1,26 @@ { - "name": "arakoo-jsonnet", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "arakoo-jsonnet", - "version": "0.1.0", - "hasInstallScript": true, - "license": "ISC", - "devDependencies": { - "cargo-cp-artifact": "^0.1" - } - }, - "node_modules/cargo-cp-artifact": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/cargo-cp-artifact/-/cargo-cp-artifact-0.1.8.tgz", - "integrity": "sha512-3j4DaoTrsCD1MRkTF2Soacii0Nx7UHCce0EwUf4fHnggwiE4fbmF2AbnfzayR36DF8KGadfh7M/Yfy625kgPlA==", - "dev": true, - "bin": { - "cargo-cp-artifact": "bin/cargo-cp-artifact.js" - } - } + "name": "arakoo-jsonnet", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "arakoo-jsonnet", + "version": "0.1.0", + "hasInstallScript": true, + "license": "ISC", + "devDependencies": { + "cargo-cp-artifact": "^0.1" + } + }, + "node_modules/cargo-cp-artifact": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/cargo-cp-artifact/-/cargo-cp-artifact-0.1.8.tgz", + "integrity": "sha512-3j4DaoTrsCD1MRkTF2Soacii0Nx7UHCce0EwUf4fHnggwiE4fbmF2AbnfzayR36DF8KGadfh7M/Yfy625kgPlA==", + "dev": true, + "bin": { + "cargo-cp-artifact": "bin/cargo-cp-artifact.js" + } } + } } diff --git a/JS/wasm/types/jsonnet/package.json b/JS/wasm/types/jsonnet/package.json index 20fade37d..d7b36184a 100644 --- a/JS/wasm/types/jsonnet/package.json +++ b/JS/wasm/types/jsonnet/package.json @@ -1,25 +1,25 @@ { - "name": "arakoo-jsonnet", - "version": "0.1.0", - "description": "", - "main": "index.node", - "scripts": { - "build": "cargo-cp-artifact -nc index.node -- cargo build --message-format=json-render-diagnostics", - "build-debug": "npm run build --", - "build-release": "npm run build -- --release", - "install": "npm run build-release", - "test": "cargo test", - "preinstall": "npm install cargo-cp-artifact --no-save" - }, - "author": "", - "exports": { - ".": { - "require": "./src/index.js", - "default": "./src/module.mjs" - } - }, - "license": "ISC", - "devDependencies": { - "cargo-cp-artifact": "^0.1" + "name": "arakoo-jsonnet", + "version": "0.1.0", + "description": "", + "main": "index.node", + "scripts": { + "build": "cargo-cp-artifact -nc index.node -- cargo build --message-format=json-render-diagnostics", + "build-debug": "npm run build --", + "build-release": "npm run build -- --release", + "test": "cargo test", + "preinstall": "npm install cargo-cp-artifact --no-save" + }, + "types": "src/index.d.ts", + "author": "", + "exports": { + ".": { + "require": "./src/index.js", + "default": "./src/module.mjs" } + }, + "license": "ISC", + "devDependencies": { + "cargo-cp-artifact": "^0.1" + } } diff --git a/JS/wasm/types/jsonnet/src/index.d.ts b/JS/wasm/types/jsonnet/src/index.d.ts index c08e25f03..18ea37599 100644 --- a/JS/wasm/types/jsonnet/src/index.d.ts +++ b/JS/wasm/types/jsonnet/src/index.d.ts @@ -1 +1,14 @@ -export function parseJsonnet(filename: string): Promise; +export function jsonnet(filename: string): Promise; + +/** + * useage: + * const extVars = JSON.stringify({ + * a: 1, + * b: 2, + * }); + * await jsonnetExtVars(filename, extVars); + */ +export function jsonnetExtVars( + filename: string, + extVars: string, +): Promise; diff --git a/JS/wasm/types/jsonnet/src/index.js b/JS/wasm/types/jsonnet/src/index.js index 66874ec6c..97606b1c9 100644 --- a/JS/wasm/types/jsonnet/src/index.js +++ b/JS/wasm/types/jsonnet/src/index.js @@ -1,5 +1,6 @@ -const { parseJsonnet } = require("../index.node"); +const { jsonnet, jsonnetExtVars } = require("../index.node"); module.exports = { - parseJsonnet, + jsonnet, + jsonnetExtVars, }; diff --git a/JS/wasm/types/jsonnet/src/lib.rs b/JS/wasm/types/jsonnet/src/lib.rs index 70aa202fc..03867fd06 100644 --- a/JS/wasm/types/jsonnet/src/lib.rs +++ b/JS/wasm/types/jsonnet/src/lib.rs @@ -1,12 +1,10 @@ -use std::fs; - -use neon::prelude::*; +use std::{collections::HashMap, fs}; use jsonnet::JsonnetVm; +use neon::prelude::*; +use serde_json::Value; -pub fn parse_jsonnet( - mut cx: FunctionContext, -) -> JsResult { +pub fn jsonnet(mut cx: FunctionContext) -> JsResult { let path: String = cx.argument::(0)?.value(&mut cx); let mut vm = JsonnetVm::new(); let snippet = fs::read_to_string(path).unwrap(); @@ -20,8 +18,31 @@ pub fn parse_jsonnet( Ok(cx.string(output.to_string())) } +pub fn jsonnet_ext_vars(mut cx: FunctionContext) -> JsResult { + let path: String = cx.argument::(0)?.value(&mut cx); + let ext_var: String = cx.argument::(1)?.value(&mut cx); + let mut vm = JsonnetVm::new(); + let ext_var_str = ext_var.as_str(); + let ext_vars: HashMap<&str, Value> = serde_json::from_str(ext_var_str).unwrap(); + for (key, value) in ext_vars { + vm.ext_var(key, value.as_str().unwrap()); + } + + let snippet = fs::read_to_string(path).unwrap(); + + let output = vm.evaluate_snippet("snippet", &snippet); + let output = match output { + Ok(output) => output, + Err(e) => { + return cx.throw_error(format!("{}", e)); + } + }; + Ok(cx.string(output.to_string())) +} + #[neon::main] fn main(mut cx: ModuleContext) -> NeonResult<()> { - cx.export_function("parseJsonnet", parse_jsonnet)?; + cx.export_function("jsonnet", jsonnet)?; + cx.export_function("jsonnetExtVars", jsonnet_ext_vars)?; Ok(()) } diff --git a/JS/wasm/types/jsonnet/src/module.mjs b/JS/wasm/types/jsonnet/src/module.mjs index 8efe8310e..962aaa90a 100644 --- a/JS/wasm/types/jsonnet/src/module.mjs +++ b/JS/wasm/types/jsonnet/src/module.mjs @@ -1,5 +1,5 @@ import parseJson from "./index.js"; -const { parseJsonnet } = parseJson; +const { jsonnet, jsonnetExtVars } = parseJson; -export { parseJsonnet }; +export { jsonnet, jsonnetExtVars };