diff --git a/Cargo.lock b/Cargo.lock index 0d07126..e68bd3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,6 +27,20 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "ahash" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "serde", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.2" @@ -130,6 +144,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + [[package]] name = "better_scoped_tls" version = "0.1.1" @@ -139,6 +159,21 @@ dependencies = [ "scoped-tls", ] +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitflags" version = "1.3.2" @@ -166,6 +201,12 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +[[package]] +name = "bytecount" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" + [[package]] name = "bytes" version = "1.5.0" @@ -239,6 +280,22 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + [[package]] name = "cpufeatures" version = "0.2.11" @@ -310,7 +367,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8cdc4db9f967c84d8d122892e448e146a4e92987830dd94dbcee31896938dad" dependencies = [ "anyhow", - "base64", + "base64 0.13.1", "deno_media_type", "dprint-swc-ext", "serde", @@ -399,6 +456,15 @@ dependencies = [ "tokio", ] +[[package]] +name = "deranged" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" +dependencies = [ + "powerfmt", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -444,6 +510,15 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + [[package]] name = "env_logger" version = "0.10.0" @@ -473,6 +548,22 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "fancy-regex" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2" +dependencies = [ + "bit-set", + "regex", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "form_urlencoded" version = "1.2.0" @@ -482,6 +573,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fraction" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3027ae1df8d41b4bed2241c8fdad4acc1e7af60c8e17743534b545e77182d678" +dependencies = [ + "lazy_static", + "num", +] + [[package]] name = "from_variant" version = "0.1.6" @@ -610,8 +711,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -620,6 +723,25 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +[[package]] +name = "h2" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 2.1.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "handlebars" version = "4.5.0" @@ -667,12 +789,70 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "http" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "hyper" +version = "0.14.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.4.10", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "idna" version = "0.4.0" @@ -709,6 +889,12 @@ dependencies = [ "hashbrown 0.14.2", ] +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + [[package]] name = "is-macro" version = "0.3.0" @@ -733,12 +919,60 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "iso8601" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924e5d73ea28f59011fec52a0d12185d496a9b075d360657aed2a5707f701153" +dependencies = [ + "nom", +] + [[package]] name = "itoa" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +[[package]] +name = "js-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonschema" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a071f4f7efc9a9118dfb627a0a94ef247986e1ab8606a4c806ae2b3aa3b6978" +dependencies = [ + "ahash", + "anyhow", + "base64 0.21.5", + "bytecount", + "clap", + "fancy-regex", + "fraction", + "getrandom", + "iso8601", + "itoa", + "memchr", + "num-cmp", + "once_cell", + "parking_lot", + "percent-encoding", + "regex", + "reqwest", + "serde", + "serde_json", + "time", + "url", + "uuid", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -779,6 +1013,18 @@ version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -816,6 +1062,30 @@ dependencies = [ "libc", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.4" @@ -829,6 +1099,21 @@ dependencies = [ "serde", ] +[[package]] +name = "num-cmp" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63335b2e2c34fae2fb0aa2cecfd9f0832a1e24b3b32ecec612c3426d46dc8aaa" + +[[package]] +name = "num-complex" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +dependencies = [ + "num-traits", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -839,6 +1124,29 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.17" @@ -1046,6 +1354,12 @@ dependencies = [ "syn 2.0.38", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1182,6 +1496,41 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "reqwest" +version = "0.11.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +dependencies = [ + "base64 0.21.5", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "system-configuration", + "tokio", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -1296,10 +1645,12 @@ dependencies = [ "deno_core", "env_logger", "handlebars", + "jsonschema", "lazy_static", "log", "path-clean", "regex", + "serde_json", "serde_yaml", "tokio", "uuid", @@ -1338,6 +1689,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_v8" version = "0.142.0" @@ -1429,6 +1792,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "socket2" version = "0.5.5" @@ -1787,7 +2160,7 @@ version = "0.180.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2aa0a0c071551b412034c9c3d8164f8aeae3ade2cf673877f8b9176c0b7c66ed" dependencies = [ - "base64", + "base64 0.13.1", "dashmap", "indexmap 1.9.3", "once_cell", @@ -1924,6 +2297,27 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "termcolor" version = "1.3.0" @@ -1962,6 +2356,34 @@ dependencies = [ "syn 2.0.38", ] +[[package]] +name = "time" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +dependencies = [ + "deranged", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +dependencies = [ + "time-core", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -1991,7 +2413,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.5.5", "tokio-macros", "windows-sys", ] @@ -2007,6 +2429,26 @@ dependencies = [ "syn 2.0.38", ] +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + [[package]] name = "tracing" version = "0.1.40" @@ -2048,6 +2490,12 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + [[package]] name = "typed-arena" version = "2.0.2" @@ -2160,12 +2608,97 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.38", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" + +[[package]] +name = "web-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "which" version = "4.4.2" @@ -2274,3 +2807,33 @@ name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys", +] + +[[package]] +name = "zerocopy" +version = "0.7.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d075cf85bbb114e933343e087b92f2146bac0d55b534cbb8188becf0039948e" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86cd5ca076997b97ef09d3ad65efe811fa68c9e874cb636ccb211223a813b0c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] diff --git a/Cargo.toml b/Cargo.toml index 7e2cd3a..986c5ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,10 +19,12 @@ deno_ast = { version = "0.31.3", features = ["transpiling"] } deno_core = "0.233.0" env_logger = "0.10.0" handlebars = "4.5.0" +jsonschema = { version = "0.17.1", features = [ "draft202012" ] } lazy_static = "1.4.0" log = "0.4.20" path-clean = "1.0.1" regex = "1.10.2" +serde_json = "1.0.108" serde_yaml = "0.9.27" tokio = { version = "1.33.0", features = ["full"] } uuid = { version = "1.5.0", features = ["v4"] } diff --git a/packages/types/globals.d.ts b/packages/types/globals.d.ts index 96bf49a..4ee41d9 100644 --- a/packages/types/globals.d.ts +++ b/packages/types/globals.d.ts @@ -119,6 +119,7 @@ declare namespace senc { out_ext?: string; out_type: "yaml" | "json"; out_prefix?: string; + schema_path?: string; data: any; }); diff --git a/packages/types/package.json b/packages/types/package.json index c6642a7..0daa1da 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@fensak-io/senc-types", - "version": "0.0.4", + "version": "0.0.5", "author": "Fensak, LLC (https://fensak.io)", "license": "MPL-2.0", "description": "Core type definitions for senc.", diff --git a/src/builtins/senc.js b/src/builtins/senc.js index 4dee50e..3e981f4 100644 --- a/src/builtins/senc.js +++ b/src/builtins/senc.js @@ -25,10 +25,12 @@ */ class OutData { constructor(attrs) { + this.out_type = attrs.out_type || ""; + this.out_path = attrs.out_path; this.out_ext = attrs.out_ext; - this.out_type = attrs.out_type; this.out_prefix = attrs.out_prefix + this.schema_path = attrs.schema_path this.data = attrs.data; } diff --git a/src/engine.rs b/src/engine.rs index d992af9..571d1e4 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -16,6 +16,8 @@ use deno_core::*; use crate::files; use crate::module_loader; use crate::ops; +use crate::validator; +use crate::validator::DataSchema; // Load and embed the runtime snapshot built from the build script. static RUNTIME_SNAPSHOT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/SENC_SNAPSHOT.bin")); @@ -96,11 +98,14 @@ pub async fn run_js_and_write(ctx: &Context, req: &RunRequest) -> Result<()> { // Run the javascript or typescript file available at the given file path through the Deno runtime. async fn run_js(ctx: &Context, req: &RunRequest) -> Result> { + let script_path = path::Path::new(&req.in_file); + let script_dir = script_path.parent().unwrap(); + let mut js_runtime = new_runtime(ctx, req)?; let mod_id = load_main_module(&mut js_runtime, &req.in_file).await?; let main_fn = load_main_fn(&mut js_runtime, mod_id)?; let result = call_main_fn(ctx, &mut js_runtime, main_fn).await?; - return load_result(&mut js_runtime, result); + return load_result(&script_dir, &mut js_runtime, result); } // Initialize a new JsRuntime object (which represents an Isolate) with all the extensions loaded. @@ -189,6 +194,7 @@ async fn call_main_fn( // Load the result from the main function as a vector of OutData that can be outputed to disk. Each // OutData represents a single file that should be outputed. fn load_result( + script_dir: &path::Path, js_runtime: &mut JsRuntime, result: v8::Global, ) -> Result> { @@ -205,11 +211,11 @@ fn load_result( let sz = result_arr_raw.length(); for i in 0..sz { let item = result_arr_raw.get_index(&mut scope, i).unwrap(); - let single_out = load_one_result(&mut scope, item)?; + let single_out = load_one_result(script_dir, &mut scope, item)?; out.push(single_out); } } else { - let single_out = load_one_result(&mut scope, result_local)?; + let single_out = load_one_result(script_dir, &mut scope, result_local)?; out.push(single_out); } @@ -221,6 +227,7 @@ fn load_result( // allows customization of the output behavior on a file by file basis. // - Anything else would be treated as raw object to be serialized to JSON. fn load_one_result<'a>( + script_dir: &path::Path, scope: &mut v8::HandleScope<'a>, orig_result_local: v8::Local<'a, v8::Value>, ) -> Result { @@ -231,19 +238,23 @@ fn load_one_result<'a>( let mut out_ext = Some(String::from(".json")); let mut out_type = OutputType::JSON; let mut out_prefix: Option = None; + let mut schema_path: Option = None; // Determine if the raw JS object from the runtime is an out data object, and if it is, process // it. if result_is_sencjs_out_data(scope, result_local)? { - let (op, oe, ot, opre, rs) = load_one_sencjs_out_data_result(scope, result_local)?; + let (op, oe, ot, opre, sp, rs) = load_one_sencjs_out_data_result(scope, result_local)?; out_path = op; out_ext = oe; out_type = ot; out_prefix = opre; + schema_path = sp; result_local = rs; } let deserialized_result = serde_v8::from_v8::(scope, result_local)?; + validate_result(script_dir, schema_path, &deserialized_result)?; + let data = match out_type { // NOTE // Both serde_json and serde_yaml have consistent outputs, so we don't need to do anything @@ -273,13 +284,16 @@ fn load_one_sencjs_out_data_result<'a>( OutputType, // out_prefix Option, + // schema_path + Option, // result_local v8::Local<'a, v8::Value>, )> { let mut out_path: Option = None; - let mut out_ext: Option = None; + let mut out_ext: Option = Some(String::from(".json")); let mut out_type = OutputType::JSON; let mut out_prefix: Option = None; + let mut schema_path: Option = None; let result_obj: v8::Local = result_local.try_into()?; let out_type_key: v8::Local = v8::String::new(scope, "out_type").unwrap().into(); @@ -297,9 +311,12 @@ fn load_one_sencjs_out_data_result<'a>( let out_path_key: v8::Local = v8::String::new(scope, "out_path").unwrap().into(); let out_ext_key: v8::Local = v8::String::new(scope, "out_ext").unwrap().into(); let out_prefix_key: v8::Local = v8::String::new(scope, "out_prefix").unwrap().into(); + let schema_path_key: v8::Local = + v8::String::new(scope, "schema_path").unwrap().into(); let maybe_out_path: v8::Local = result_obj.get(scope, out_path_key).unwrap(); let maybe_out_ext: v8::Local = result_obj.get(scope, out_ext_key).unwrap(); let maybe_out_prefix: v8::Local = result_obj.get(scope, out_prefix_key).unwrap(); + let maybe_schema_path: v8::Local = result_obj.get(scope, schema_path_key).unwrap(); if maybe_out_path.is_string() && maybe_out_ext.is_string() { return Err(anyhow!( @@ -320,12 +337,18 @@ fn load_one_sencjs_out_data_result<'a>( out_prefix = Some(out_prefix_local.to_rust_string_lossy(scope)); } + if maybe_schema_path.is_string() { + let schema_path_local: v8::Local = maybe_schema_path.try_into()?; + schema_path = Some(schema_path_local.to_rust_string_lossy(scope)); + } + let out_data_key: v8::Local = v8::String::new(scope, "data").unwrap().into(); Ok(( out_path, out_ext, out_type, out_prefix, + schema_path, result_obj.get(scope, out_data_key).unwrap().try_into()?, )) } @@ -464,6 +487,26 @@ fn load_templated_builtins(ctx: &Context, req: &RunRequest) -> Result Ok(ext) } +// Validate the result data against a specified schema. If no schema is specified, this function +// does nothing. +fn validate_result( + script_dir: &path::Path, + maybe_schema_path: Option, + result: &serde_json::Value, +) -> Result<()> { + let schema_path_str = match maybe_schema_path { + None => { + return Ok(()); + } + Some(d) => d, + }; + let mut schema_path = path::PathBuf::from(script_dir); + schema_path.push(schema_path_str); + let schema_path_abs = fs::canonicalize(schema_path)?; + let schema = validator::new_from_path(schema_path_abs.as_path())?; + return schema.validate(result); +} + // Test cases #[cfg(test)] @@ -479,6 +522,7 @@ mod tests { static EXPECTED_IMPORT_CONFIG_OUTPUT_JSON: &str = "{\"msg\":\"hello world\"}"; static EXPECTED_ARGS_OUTPUT_JSON: &str = "{\"arg1\":[\"hello world\"],\"arg2\":{\"msg\":\"hello world\"}}"; + static EXPECTED_JSONSCHEMA_OUTPUT_JSON: &str = "{\"productId\":5}"; #[tokio::test] async fn test_engine_runs_js() { @@ -510,6 +554,22 @@ mod tests { check_single_json_output(EXPECTED_RELPATH_OUTPUT_JSON, "aws/us-east-1/vpc/main.js").await; } + #[tokio::test] + async fn test_engine_checks_schema_pass() { + check_single_json_output(EXPECTED_JSONSCHEMA_OUTPUT_JSON, "jsonschema/pass.js").await; + } + + #[tokio::test] + async fn test_engine_checks_schema_fail() { + let p = get_fixture_path("jsonschema/fail.js"); + let req = RunRequest { + in_file: String::from(p.as_path().to_string_lossy()), + out_file_stem: String::from(""), + }; + let result = run_js(&get_context(&[]), &req).await; + assert!(result.is_err()); + } + #[tokio::test] async fn test_engine_runs_js_with_args() { let arg1 = "[\"hello world\"]"; diff --git a/src/main.rs b/src/main.rs index d22a87d..25fd5e1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ mod logger; mod module_loader; mod ops; mod threadpool; +mod validator; use std::fs; use std::path; diff --git a/src/validator.rs b/src/validator.rs new file mode 100644 index 0000000..b699334 --- /dev/null +++ b/src/validator.rs @@ -0,0 +1,60 @@ +// Copyright (c) Fensak, LLC. +// SPDX-License-Identifier: MPL-2.0 + +use std::fs; +use std::io; +use std::path; + +use anyhow::{anyhow, Result}; +use jsonschema::{Draft, JSONSchema}; + +pub trait DataSchema { + fn validate(&self, data: &serde_json::Value) -> Result<()>; +} + +pub struct DataJSONSchema { + schema: JSONSchema, +} + +impl DataSchema for DataJSONSchema { + fn validate(&self, data: &serde_json::Value) -> Result<()> { + match self.schema.validate(data) { + Err(errs) => { + let mut err_strs = Vec::new(); + for err in errs { + let instance_path_str = err.instance_path.to_string(); + let err_str = if instance_path_str == "" { + format!("[.] {}", err).to_string() + } else { + format!("[{}] {}\n", instance_path_str, err).to_string() + }; + err_strs.push(err_str); + } + Err(anyhow!(err_strs.join("\n"))) + } + Ok(result) => Ok(result), + } + } +} + +pub fn new_from_path(schema_path: &path::Path) -> Result { + let schema_file = fs::File::open(schema_path)?; + let schema_reader = io::BufReader::new(schema_file); + let raw_schema: serde_json::Value = serde_json::from_reader(schema_reader)?; + + let maybe_jsonschema: Result = JSONSchema::options() + .with_draft(Draft::Draft202012) + .compile(&raw_schema); + match maybe_jsonschema { + Ok(jsonschema) => { + return Ok(DataJSONSchema { schema: jsonschema }); + } + Err(err) => { + return Err(anyhow!( + "Could not load schema {}: {}", + schema_path.to_string_lossy(), + err + )); + } + }; +} diff --git a/tests/fixtures/jsonschema/fail.js b/tests/fixtures/jsonschema/fail.js new file mode 100644 index 0000000..484e115 --- /dev/null +++ b/tests/fixtures/jsonschema/fail.js @@ -0,0 +1,12 @@ +// Copyright (c) Fensak, LLC. +// SPDX-License-Identifier: MPL-2.0 + +export function main() { + return new senc.OutData({ + schema_path: "schema.json", + data: { + productId: 5, + shouldNotHave: true, + }, + }); +} diff --git a/tests/fixtures/jsonschema/pass.js b/tests/fixtures/jsonschema/pass.js new file mode 100644 index 0000000..447cb36 --- /dev/null +++ b/tests/fixtures/jsonschema/pass.js @@ -0,0 +1,9 @@ +// Copyright (c) Fensak, LLC. +// SPDX-License-Identifier: MPL-2.0 + +export function main() { + return new senc.OutData({ + schema_path: "schema.json", + data: { productId: 5 }, + }); +} diff --git a/tests/fixtures/jsonschema/schema.json b/tests/fixtures/jsonschema/schema.json new file mode 100644 index 0000000..13f2138 --- /dev/null +++ b/tests/fixtures/jsonschema/schema.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://example.com/product.schema.json", + "title": "Product", + "description": "A product in the catalog", + "type": "object", + "properties": { + "productId": { + "description": "The unique identifier for a product", + "type": "integer" + } + }, + "additionalProperties": false +}