From 10b279777d34de342103380b877e74e97fc1a20f Mon Sep 17 00:00:00 2001 From: Mayant Mukul Date: Sat, 4 May 2024 05:40:08 -0700 Subject: [PATCH] fix(js): replace `deno_core` with `rquickjs` (#1713) Co-authored-by: amit Co-authored-by: Tushar Mathur --- Cargo.lock | 594 +++++++++++---------------- Cargo.toml | 8 +- src/cli/javascript/js_request.rs | 360 +++++++++++----- src/cli/javascript/js_response.rs | 175 +++++--- src/cli/javascript/request_filter.rs | 107 ++++- src/cli/javascript/runtime.rs | 103 +++-- src/cli/javascript/shim/console.js | 10 +- tailcall-cloudflare/src/handle.rs | 3 +- 8 files changed, 790 insertions(+), 570 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0c324a0693..a021f17da6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -183,7 +183,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" dependencies = [ - "quote", + "quote 1.0.36", "syn 1.0.109", ] @@ -288,8 +288,8 @@ dependencies = [ "async-graphql-parser", "darling 0.20.8", "proc-macro-crate", - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "strum 0.26.2", "syn 2.0.60", "thiserror", @@ -443,8 +443,8 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 2.0.60", ] @@ -511,8 +511,8 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 2.0.60", ] @@ -528,8 +528,8 @@ version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 2.0.60", ] @@ -633,15 +633,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "base64-simd" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "781dd20c3aff0bd194fe7d2a977dd92f21c173891f3a03b677359e5fa457e5d5" -dependencies = [ - "simd-abstraction", -] - [[package]] name = "basic-cookies" version = "0.1.5" @@ -708,18 +699,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - [[package]] name = "block-buffer" version = "0.9.0" @@ -915,8 +894,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ "heck 0.5.0", - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 2.0.60", ] @@ -982,12 +961,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "cooked-waker" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147be55d677052dabc6b22252d5dd0fd4c29c8c27aa4f2fbef0f94aa003b406f" - [[package]] name = "core-foundation" version = "0.9.4" @@ -1180,8 +1153,8 @@ checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "strsim 0.10.0", "syn 1.0.109", ] @@ -1194,8 +1167,8 @@ checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" dependencies = [ "fnv", "ident_case", - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "strsim 0.10.0", "syn 2.0.60", ] @@ -1207,7 +1180,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ "darling_core 0.14.4", - "quote", + "quote 1.0.36", "syn 1.0.109", ] @@ -1218,7 +1191,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core 0.20.8", - "quote", + "quote 1.0.36", "syn 2.0.60", ] @@ -1241,12 +1214,6 @@ dependencies = [ "parking_lot_core", ] -[[package]] -name = "data-encoding" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" - [[package]] name = "datatest-stable" version = "0.2.9" @@ -1259,70 +1226,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "debugid" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" -dependencies = [ - "serde", - "uuid", -] - -[[package]] -name = "deno_core" -version = "0.278.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353cd08b2c60ee71e3a3215f111bf273dc04be455bb084873d79723a941c57ec" -dependencies = [ - "anyhow", - "bincode", - "bit-set", - "bit-vec", - "bytes", - "cooked-waker", - "deno_ops", - "deno_unsync", - "futures", - "libc", - "memoffset", - "parking_lot", - "pin-project", - "serde", - "serde_json", - "serde_v8", - "smallvec", - "sourcemap", - "static_assertions", - "tokio", - "url", - "v8", -] - -[[package]] -name = "deno_ops" -version = "0.154.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5402d2a4027026c876ab15b4bc46c60252c4a8704e80739ab62900af2d60e1f" -dependencies = [ - "proc-macro-rules", - "proc-macro2", - "quote", - "strum 0.25.0", - "strum_macros 0.25.3", - "syn 2.0.60", - "thiserror", -] - -[[package]] -name = "deno_unsync" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30dff7e03584dbae188dae96a0f1876740054809b2ad0cf7c9fc5d361f20e739" -dependencies = [ - "tokio", -] - [[package]] name = "deranged" version = "0.3.11" @@ -1348,8 +1251,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4ec317cc3e7ef0928b0ca6e4a634a4d6c001672ae210438cf114a83e56b018d" dependencies = [ "darling 0.14.4", - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 1.0.109", ] @@ -1370,8 +1273,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e8ef033054e131169b8f0f9a7af8f5533a9436fadf3c500ed547f730f07090d" dependencies = [ "darling 0.20.8", - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 2.0.60", ] @@ -1442,6 +1345,29 @@ dependencies = [ "winapi", ] +[[package]] +name = "dlopen" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e80ad39f814a9abe68583cd50a2d45c8a67561c3361ab8da240587dda80937" +dependencies = [ + "dlopen_derive", + "lazy_static", + "libc", + "winapi", +] + +[[package]] +name = "dlopen_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f236d9e1b1fbd81cea0f9cbdc8dcc7e8ebcd80e6659cd7cb2ad5f6c05946c581" +dependencies = [ + "libc", + "quote 0.6.13", + "syn 0.15.44", +] + [[package]] name = "dotenvy" version = "0.15.7" @@ -1640,22 +1566,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fslock" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - [[package]] name = "futures" version = "0.3.30" @@ -1749,8 +1659,8 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 2.0.60", ] @@ -2015,15 +1925,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "gzip-header" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95cc527b92e6029a62960ad99aa8a6660faa4555fe5f731aab13aa6a921795a2" -dependencies = [ - "crc32fast", -] - [[package]] name = "h2" version = "0.3.26" @@ -2475,12 +2376,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "if_chain" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" - [[package]] name = "indenter" version = "0.3.3" @@ -2659,7 +2554,7 @@ dependencies = [ "string_cache", "term", "tiny-keccak", - "unicode-xid", + "unicode-xid 0.2.4", "walkdir", ] @@ -2895,8 +2790,8 @@ checksum = "dc487311295e0002e452025d6b580b77bb17286de87b57138f3b5db711cded68" dependencies = [ "beef", "fnv", - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "regex-syntax 0.6.29", "syn 2.0.60", ] @@ -2910,8 +2805,8 @@ dependencies = [ "beef", "fnv", "lazy_static", - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "regex-syntax 0.8.3", "syn 2.0.60", ] @@ -3018,15 +2913,6 @@ version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - [[package]] name = "miette" version = "7.2.0" @@ -3045,8 +2931,8 @@ version = "7.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 2.0.60", ] @@ -3118,7 +3004,7 @@ dependencies = [ "once_cell", "parking_lot", "quanta", - "rustc_version 0.4.0", + "rustc_version", "smallvec", "tagptr", "thiserror", @@ -3203,7 +3089,6 @@ dependencies = [ "autocfg", "num-integer", "num-traits", - "rand", ] [[package]] @@ -3488,12 +3373,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "outref" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f222829ae9293e33a9f5e9f440c6760a3d450a64affe1846486b140db81c1f4" - [[package]] name = "overload" version = "0.1.1" @@ -3574,8 +3453,8 @@ checksum = "c35eeed0a3fab112f75165fdc026b3913f4183133f19b49be773ac9ea966e8bd" dependencies = [ "pest", "pest_meta", - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 2.0.60", ] @@ -3600,6 +3479,25 @@ dependencies = [ "indexmap 2.2.6", ] +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_shared 0.11.2", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared 0.11.2", + "rand", +] + [[package]] name = "phf_shared" version = "0.10.0" @@ -3609,6 +3507,15 @@ dependencies = [ "siphasher", ] +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + [[package]] name = "phonenumber" version = "0.3.4+8.13.34" @@ -3651,8 +3558,8 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 2.0.60", ] @@ -3772,7 +3679,7 @@ version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ac2cf0f2e4f42b49f5ffd07dae8d746508ef7526c13940e5f524012ae6c6550" dependencies = [ - "proc-macro2", + "proc-macro2 1.0.81", "syn 2.0.60", ] @@ -3787,26 +3694,36 @@ dependencies = [ ] [[package]] -name = "proc-macro-rules" -version = "0.4.0" +name = "proc-macro-error" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ - "proc-macro-rules-macros", - "proc-macro2", - "syn 2.0.60", + "proc-macro-error-attr", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 1.0.109", + "version_check", ] [[package]] -name = "proc-macro-rules-macros" -version = "0.4.0" +name = "proc-macro-error-attr" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "207fffb0fe655d1d47f6af98cc2793405e85929bdbc420d685554ff07be27ac7" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.60", + "proc-macro2 1.0.81", + "quote 1.0.36", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +dependencies = [ + "unicode-xid 0.1.0", ] [[package]] @@ -3872,8 +3789,8 @@ checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" dependencies = [ "anyhow", "itertools 0.11.0", - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 2.0.60", ] @@ -4088,18 +4005,21 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" dependencies = [ - "proc-macro2", + "proc-macro2 0.4.30", ] [[package]] -name = "radium" -version = "0.7.0" +name = "quote" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2 1.0.81", +] [[package]] name = "rand" @@ -4236,6 +4156,12 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +[[package]] +name = "relative-path" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" + [[package]] name = "reqwest" version = "0.11.27" @@ -4321,33 +4247,74 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" [[package]] -name = "rustc-demangle" -version = "0.1.23" +name = "rquickjs" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "ad7f63201fa6f2ff8173e4758ea552549d687d8f63003361a8b5c50f7c446ded" +dependencies = [ + "either", + "indexmap 2.2.6", + "rquickjs-core", + "rquickjs-macro", +] [[package]] -name = "rustc-hash" -version = "1.1.0" +name = "rquickjs-core" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +checksum = "cad00eeddc0f88af54ee202c8385fb214fe0423897c056a7df8369fb482e3695" +dependencies = [ + "chrono", + "dlopen", + "either", + "indexmap 2.2.6", + "phf", + "relative-path", + "rquickjs-sys", +] [[package]] -name = "rustc_version" -version = "0.2.3" +name = "rquickjs-macro" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +checksum = "f27b39e889cc951e3e5f6b74012f943e642fa0fac51a8552948751f19a9b62f8" dependencies = [ - "semver 0.9.0", + "convert_case", + "fnv", + "ident_case", + "indexmap 2.2.6", + "phf_generator", + "phf_shared 0.11.2", + "proc-macro-crate", + "proc-macro-error", + "proc-macro2 1.0.81", + "quote 1.0.36", + "rquickjs-core", + "syn 2.0.60", +] + +[[package]] +name = "rquickjs-sys" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120dbbc3296de9b96de8890091635d46f3506cd38b4e8f21800c386c035d64fa" +dependencies = [ + "cc", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.22", + "semver", ] [[package]] @@ -4523,8 +4490,8 @@ version = "0.8.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83263746fe5e32097f06356968a077f96089739c927a61450efa069905eec108" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "serde_derive_internals", "syn 2.0.60", ] @@ -4568,27 +4535,12 @@ dependencies = [ "libc", ] -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - [[package]] name = "semver" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "send_wrapper" version = "0.4.0" @@ -4651,8 +4603,8 @@ version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 2.0.60", ] @@ -4662,8 +4614,8 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 2.0.60", ] @@ -4722,20 +4674,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_v8" -version = "0.187.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572631c1ad3d7b304593d7afeb8b28805c00f8e15135db15200f59676d618fe2" -dependencies = [ - "bytes", - "num-bigint", - "serde", - "smallvec", - "thiserror", - "v8", -] - [[package]] name = "serde_yaml" version = "0.9.34+deprecated" @@ -4836,15 +4774,6 @@ dependencies = [ "libc", ] -[[package]] -name = "simd-abstraction" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cadb29c57caadc51ff8346233b5cec1d240b68ce55cf1afc764818791876987" -dependencies = [ - "outref", -] - [[package]] name = "similar" version = "2.5.0" @@ -4904,25 +4833,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "sourcemap" -version = "8.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "208d40b9e8cad9f93613778ea295ed8f3c2b1824217c6cfc7219d3f6f45b96d4" -dependencies = [ - "base64-simd", - "bitvec", - "data-encoding", - "debugid", - "if_chain", - "rustc-hash", - "rustc_version 0.2.3", - "serde", - "serde_json", - "unicode-id-start", - "url", -] - [[package]] name = "spin" version = "0.9.8" @@ -4950,7 +4860,7 @@ dependencies = [ "new_debug_unreachable", "once_cell", "parking_lot", - "phf_shared", + "phf_shared 0.10.0", "precomputed-hash", ] @@ -4997,8 +4907,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck 0.4.1", - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "rustversion", "syn 2.0.60", ] @@ -5010,8 +4920,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" dependencies = [ "heck 0.4.1", - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "rustversion", "syn 2.0.60", ] @@ -5022,14 +4932,25 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +[[package]] +name = "syn" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "unicode-xid 0.1.0", +] + [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "unicode-ident", ] @@ -5039,8 +4960,8 @@ version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "unicode-ident", ] @@ -5111,7 +5032,6 @@ dependencies = [ "convert_case", "criterion", "datatest-stable", - "deno_core", "derive_setters", "dotenvy", "exitcode", @@ -5161,6 +5081,7 @@ dependencies = [ "reqwest", "reqwest-middleware", "resource", + "rquickjs", "rustls 0.23.5", "rustls-pemfile 1.0.4", "rustls-pki-types", @@ -5258,8 +5179,8 @@ dependencies = [ name = "tailcall-macros" version = "0.1.0" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 2.0.60", ] @@ -5319,12 +5240,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - [[package]] name = "task-local-extensions" version = "0.1.4" @@ -5390,8 +5305,8 @@ version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 2.0.60", ] @@ -5514,8 +5429,8 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 2.0.60", ] @@ -5633,9 +5548,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d021fc044c18582b9a2408cd0dd05b1596e3ecdb5c4df822bb0183545683889" dependencies = [ "prettyplease", - "proc-macro2", + "proc-macro2 1.0.81", "prost-build", - "quote", + "quote 1.0.36", "syn 2.0.60", ] @@ -5646,9 +5561,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4ef6dd70a610078cb4e338a0f79d06bc759ff1b22d2120c2ff02ae264ba9c2" dependencies = [ "prettyplease", - "proc-macro2", + "proc-macro2 1.0.81", "prost-build", - "quote", + "quote 1.0.36", "syn 2.0.60", ] @@ -5745,8 +5660,8 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 2.0.60", ] @@ -5894,12 +5809,6 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1b6def86329695390197b82c1e244a54a131ceb66c996f2088a3876e2ae083f" -[[package]] -name = "unicode-id-start" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8f73150333cb58412db36f2aca8f2875b013049705cc77b94ded70a1ab1f5da" - [[package]] name = "unicode-ident" version = "1.0.12" @@ -5927,6 +5836,12 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + [[package]] name = "unicode-xid" version = "0.2.4" @@ -5953,7 +5868,7 @@ checksum = "2f8811797a24ff123db3c6e1087aa42551d03d772b3724be421ad063da1f5f3f" dependencies = [ "directories", "reqwest", - "semver 1.0.22", + "semver", "serde", "serde_json", ] @@ -5992,21 +5907,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "v8" -version = "0.91.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03bdee44e85d6235cff99e1ed5b1016c53822c70d1cce3d51f421b27a125a1e8" -dependencies = [ - "bitflags 2.5.0", - "fslock", - "gzip-header", - "home", - "miniz_oxide", - "once_cell", - "which 5.0.0", -] - [[package]] name = "valuable" version = "0.1.0" @@ -6075,8 +5975,8 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 2.0.60", "wasm-bindgen-shared", ] @@ -6099,7 +5999,7 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ - "quote", + "quote 1.0.36", "wasm-bindgen-macro-support", ] @@ -6109,8 +6009,8 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 2.0.60", "wasm-bindgen-backend", "wasm-bindgen-shared", @@ -6173,19 +6073,6 @@ dependencies = [ "rustix 0.38.32", ] -[[package]] -name = "which" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bf3ea8596f3a0dd5980b46430f2058dfe2c36a27ccfbb1845d6fbfcd9ba6e14" -dependencies = [ - "either", - "home", - "once_cell", - "rustix 0.38.32", - "windows-sys 0.48.0", -] - [[package]] name = "which" version = "6.0.1" @@ -6455,8 +6342,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a0f7f15151a77dca96813d0eff10ab9b29114533fae0267d00c466c13081e69" dependencies = [ "async-trait", - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 2.0.60", "wasm-bindgen", "wasm-bindgen-futures", @@ -6483,20 +6370,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a76ff259533532054cfbaefb115c613203c73707017459206380f03b3b3f266e" dependencies = [ "darling 0.20.8", - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 2.0.60", ] -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - [[package]] name = "yansi" version = "0.5.1" @@ -6518,8 +6396,8 @@ version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.81", + "quote 1.0.36", "syn 2.0.60", ] diff --git a/Cargo.toml b/Cargo.toml index b50107097a..29bef4bfb2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -96,9 +96,7 @@ lazy_static = "1.4.0" which = { version = "6.0.1", optional = true } async-recursion = "1.1.1" tempfile = "3.10.1" -deno_core = { version = "0.278.0", optional = true, features = [ - "v8_use_custom_libcxx", -], default-features = false } +rquickjs = { "version" = "0.5.1", optional = true, features = ["full"] } strum_macros = "0.26.2" # TODO: disable some levels with features? tracing = "0.1.40" @@ -167,7 +165,7 @@ tailcall-fixtures = { path = "./tailcall-fixtures" } # Feature Flag to enable V8. # V8 currently is not support on all platforms so, we control it via this feature flag. -js = ["dep:deno_core"] +js = ["dep:rquickjs"] # Feature Flag to core CLI features. # This is created to control what we expose for WASM. @@ -233,4 +231,4 @@ harness = false [[test]] name = "execution_spec" -harness = false \ No newline at end of file +harness = false diff --git a/src/cli/javascript/js_request.rs b/src/cli/javascript/js_request.rs index ad46afff4f..c2601ed326 100644 --- a/src/cli/javascript/js_request.rs +++ b/src/cli/javascript/js_request.rs @@ -1,19 +1,127 @@ use std::collections::BTreeMap; use std::fmt::Display; +use std::str::FromStr; +use headers::HeaderValue; +use reqwest::header::HeaderName; +use reqwest::Request; +use rquickjs::{FromJs, IntoJs}; use serde::{Deserialize, Serialize}; -use super::create_header_map; use crate::is_default; -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct JsRequest { - uri: Uri, - method: String, - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - headers: BTreeMap, - #[serde(default, skip_serializing_if = "is_default")] - body: Option, + +#[derive(Debug)] +pub struct JsRequest(reqwest::Request); + +impl JsRequest { + fn uri(&self) -> Uri { + self.0.url().into() + } + + fn method(&self) -> String { + self.0.method().to_string() + } + + fn headers(&self) -> anyhow::Result> { + let headers = self.0.headers(); + let mut map = BTreeMap::new(); + for (k, v) in headers.iter() { + map.insert(k.to_string(), v.to_str()?.to_string()); + } + Ok(map) + } + + fn body(&self) -> Option { + if let Some(body) = self.0.body() { + let bytes = body.as_bytes()?; + Some(String::from_utf8_lossy(bytes).to_string()) + } else { + None + } + } +} + +impl TryFrom<&reqwest::Request> for JsRequest { + type Error = anyhow::Error; + + fn try_from(value: &Request) -> Result { + let request = value + .try_clone() + .ok_or(anyhow::anyhow!("unable to clone request"))?; + Ok(JsRequest(request)) + } +} + +impl From for reqwest::Request { + fn from(val: JsRequest) -> Self { + val.0 + } +} + +impl<'js> IntoJs<'js> for JsRequest { + fn into_js(self, ctx: &rquickjs::Ctx<'js>) -> rquickjs::Result> { + let object = rquickjs::Object::new(ctx.clone())?; + object.set("uri", self.uri())?; + object.set("method", self.method())?; + object.set( + "headers", + self.headers().map_err(|e| rquickjs::Error::FromJs { + from: "HeaderMap", + to: "BTreeMap", + message: Some(e.to_string()), + })?, + )?; + object.set("body", self.body())?; + Ok(object.into_value()) + } +} + +impl<'js> FromJs<'js> for JsRequest { + fn from_js(_: &rquickjs::Ctx<'js>, value: rquickjs::Value<'js>) -> rquickjs::Result { + let object = value.as_object().ok_or(rquickjs::Error::FromJs { + from: value.type_name(), + to: "rquickjs::Object", + message: Some("unable to cast JS Value as object".to_string()), + })?; + let uri = object.get::<&str, Uri>("uri")?; + let method = object.get::<&str, String>("method")?; + let headers = object.get::<&str, BTreeMap>("headers")?; + let body = object.get::<&str, Option>("body")?; + let mut request = reqwest::Request::new( + reqwest::Method::from_bytes(method.as_bytes()).map_err(|e| { + rquickjs::Error::FromJs { + from: "string", + to: "Method", + message: Some(e.to_string()), + } + })?, + uri.to_string() + .parse() + .map_err(|_| rquickjs::Error::FromJs { + from: "string", + to: "Url", + message: Some("unable to parse URL".to_string()), + })?, + ); + for (k, v) in headers { + request.headers_mut().insert( + HeaderName::from_str(&k).map_err(|e| rquickjs::Error::FromJs { + from: "string", + to: "HeaderName", + message: Some(e.to_string()), + })?, + HeaderValue::from_str(v.as_str()).map_err(|e| rquickjs::Error::FromJs { + from: "string", + to: "reqwest::header::HeaderValue", + message: Some(e.to_string()), + })?, + ); + } + if let Some(body) = body { + let _ = request.body_mut().insert(reqwest::Body::from(body)); + } + Ok(JsRequest(request)) + } } #[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq)] @@ -23,6 +131,38 @@ pub enum Scheme { Https, } +impl<'js> IntoJs<'js> for Scheme { + fn into_js(self, ctx: &rquickjs::Ctx<'js>) -> rquickjs::Result> { + match self { + Scheme::Http => Ok(rquickjs::String::from_str(ctx.clone(), "http")?.into_value()), + Scheme::Https => Ok(rquickjs::String::from_str(ctx.clone(), "https")?.into_value()), + } + } +} + +impl<'js> FromJs<'js> for Scheme { + fn from_js(_: &rquickjs::Ctx<'js>, value: rquickjs::Value<'js>) -> rquickjs::Result { + let as_string = value.as_string().ok_or(rquickjs::Error::FromJs { + from: value.type_name(), + to: "rquickjs::String", + message: Some("unable to cast JS Value as string".to_string()), + })?; + + let rs_string = as_string.to_string()?; + if rs_string == "https" { + Ok(Scheme::Https) + } else if rs_string == "http" { + Ok(Scheme::Http) + } else { + Err(rquickjs::Error::FromJs { + from: "string", + to: "tailcall::cli::javascript::js_request::Scheme", + message: Some("scheme must be `http` or `https`".to_string()), + }) + } + } +} + #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Uri { @@ -37,6 +177,35 @@ pub struct Uri { port: Option, } +impl<'js> IntoJs<'js> for Uri { + fn into_js(self, ctx: &rquickjs::Ctx<'js>) -> rquickjs::Result> { + let object = rquickjs::Object::new(ctx.clone())?; + object.set("path", self.path)?; + object.set("query", self.query)?; + object.set("scheme", self.scheme)?; + object.set("host", self.host)?; + object.set("port", self.port)?; + Ok(object.into_value()) + } +} + +impl<'js> FromJs<'js> for Uri { + fn from_js(_: &rquickjs::Ctx<'js>, value: rquickjs::Value<'js>) -> rquickjs::Result { + let object = value.as_object().ok_or(rquickjs::Error::FromJs { + from: value.type_name(), + to: "rquickjs::Object", + message: Some("unable to cast JS Value as object".to_string()), + })?; + let path = object.get::<&str, String>("path")?; + let query = object.get::<&str, BTreeMap>("query")?; + let scheme = object.get::<&str, Scheme>("scheme")?; + let host = object.get::<&str, Option>("host")?; + let port = object.get::<&str, Option>("port")?; + + Ok(Uri { path, query, scheme, host, port }) + } +} + impl From<&reqwest::Url> for Uri { fn from(value: &reqwest::Url) -> Self { Self { @@ -78,101 +247,12 @@ impl Display for Uri { } } -impl TryInto for JsRequest { - type Error = anyhow::Error; - - fn try_into(self) -> Result { - let mut request = reqwest::Request::new( - reqwest::Method::from_bytes(self.method.as_bytes())?, - self.uri.to_string().parse()?, - ); - let headers = create_header_map(self.headers)?; - request.headers_mut().extend(headers); - if let Some(bytes) = self.body { - let _ = request.body_mut().insert(reqwest::Body::from(bytes)); - } - - Ok(request) - } -} - -impl TryFrom<&reqwest::Request> for JsRequest { - type Error = anyhow::Error; - - fn try_from(req: &reqwest::Request) -> Result { - let url = Uri::from(req.url()); - let method = req.method().as_str().to_string(); - let headers = req - .headers() - .iter() - .map(|(key, value)| { - ( - key.to_string(), - value.to_str().unwrap_or_default().to_string(), - ) - }) - .collect::>(); - - // NOTE: We don't pass body to worker for performance reasons - Ok(JsRequest { uri: url, method, headers, body: None }) - } -} - #[cfg(test)] mod tests { - use hyper::HeaderMap; use pretty_assertions::assert_eq; + use rquickjs::{Context, Runtime}; use super::*; - impl Uri { - pub fn parse(input: &str) -> anyhow::Result { - Ok(Self::from(&reqwest::Url::parse(input)?)) - } - } - - #[test] - fn test_js_request_to_reqwest_request() { - let body = "Hello, World!"; - let mut headers = BTreeMap::new(); - headers.insert("x-unusual-header".to_string(), "🚀".to_string()); - - let js_request = JsRequest { - uri: Uri::parse("http://example.com/").unwrap(), - method: "GET".to_string(), - headers, - body: Some(body.to_string()), - }; - let reqwest_request: reqwest::Request = js_request.try_into().unwrap(); - assert_eq!(reqwest_request.method(), reqwest::Method::GET); - assert_eq!(reqwest_request.url().as_str(), "http://example.com/"); - assert_eq!( - reqwest_request.headers().get("x-unusual-header").unwrap(), - "🚀" - ); - let body_out = reqwest_request - .body() - .as_ref() - .and_then(|body| body.as_bytes()) - .map(|a| String::from_utf8_lossy(a).to_string()); - assert_eq!(body_out, Some(body.to_string())); - } - - #[test] - fn test_js_request_to_reqwest_request_with_port_and_query() { - let js_request = JsRequest { - uri: Uri::parse("http://localhost:3000/?test=abc").unwrap(), - method: "GET".to_string(), - headers: BTreeMap::default(), - body: None, - }; - let reqwest_request: reqwest::Request = js_request.try_into().unwrap(); - assert_eq!(reqwest_request.method(), reqwest::Method::GET); - assert_eq!( - reqwest_request.url().as_str(), - "http://localhost:3000/?test=abc" - ); - assert_eq!(reqwest_request.headers(), &HeaderMap::default()); - } #[test] fn test_reqwest_request_to_js_request() { @@ -182,9 +262,81 @@ mod tests { .body_mut() .insert(reqwest::Body::from("Hello, World!")); let js_request: JsRequest = (&reqwest_request).try_into().unwrap(); - assert_eq!(js_request.method, "GET"); - assert_eq!(js_request.uri.to_string(), "http://example.com/"); - let body_out = js_request.body; - assert_eq!(body_out, None); + assert_eq!(js_request.method(), "GET"); + assert_eq!(js_request.uri().to_string(), "http://example.com/"); + let body_out = js_request.body(); + assert_eq!(body_out, Some("Hello, World!".to_string())); + } + + #[test] + fn test_js_request_into_js() { + let runtime = Runtime::new().unwrap(); + let context = Context::base(&runtime).unwrap(); + context.with(|ctx| { + let mut headers = BTreeMap::new(); + headers.insert("content-type".to_string(), "application/json".to_string()); + + let mut request = + Request::new(reqwest::Method::GET, "http://example.com/".parse().unwrap()); + let _ = request + .body_mut() + .insert(reqwest::Body::from("Hello, World!")); + request.headers_mut().insert( + reqwest::header::CONTENT_TYPE, + HeaderValue::from_str("application/json").unwrap(), + ); + + let js_request: JsRequest = (&request).try_into().unwrap(); + let value = js_request.into_js(&ctx).unwrap(); + let object = value.as_object().unwrap(); + + let uri = object.get::<&str, Uri>("uri").unwrap(); + let method = object.get::<&str, String>("method").unwrap(); + let body = object.get::<&str, Option>("body").unwrap(); + let js_headers = object + .get::<&str, BTreeMap>("headers") + .unwrap(); + + assert_eq!(uri.to_string(), "http://example.com/"); + assert_eq!(method, "GET"); + assert_eq!(body, Some("Hello, World!".to_string())); + assert_eq!( + js_headers.get("content-type"), + Some(&"application/json".to_string()) + ); + }); + } + + #[test] + fn test_js_request_from_js() { + let runtime = Runtime::new().unwrap(); + let context = Context::base(&runtime).unwrap(); + context.with(|ctx| { + let mut headers = BTreeMap::new(); + headers.insert("content-type".to_string(), "application/json".to_string()); + + let mut request = + Request::new(reqwest::Method::GET, "http://example.com/".parse().unwrap()); + let _ = request + .body_mut() + .insert(reqwest::Body::from("Hello, World!")); + request.headers_mut().insert( + reqwest::header::CONTENT_TYPE, + HeaderValue::from_str("application/json").unwrap(), + ); + + let js_request: JsRequest = (&request).try_into().unwrap(); + let value = js_request.into_js(&ctx).unwrap(); + + let js_request = JsRequest::from_js(&ctx, value).unwrap(); + + assert_eq!(js_request.uri().to_string(), "http://example.com/"); + assert_eq!(js_request.method(), "GET"); + assert_eq!(js_request.body(), Some("Hello, World!".to_string())); + assert_eq!( + js_request.headers().unwrap().get("content-type"), + Some(&"application/json".to_string()) + ); + }); } } diff --git a/src/cli/javascript/js_response.rs b/src/cli/javascript/js_response.rs index 55cdedc374..9fd71f0797 100644 --- a/src/cli/javascript/js_response.rs +++ b/src/cli/javascript/js_response.rs @@ -1,31 +1,80 @@ use std::collections::BTreeMap; use hyper::body::Bytes; -use nom::AsBytes; -use serde::{Deserialize, Serialize}; +use rquickjs::{FromJs, IntoJs}; use super::create_header_map; use crate::http::Response; -use crate::is_default; - -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct JsResponse { - pub status: u16, - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub headers: BTreeMap, - #[serde(default, skip_serializing_if = "is_default")] - pub body: Option, + +#[derive(Debug)] +pub struct JsResponse(Response); + +impl JsResponse { + pub fn status(&self) -> u16 { + self.0.status.as_u16() + } + + pub fn headers(&self) -> BTreeMap { + let mut headers = BTreeMap::new(); + for (key, value) in self.0.headers.iter() { + headers.insert(key.to_string(), value.to_str().unwrap().to_string()); + } + headers + } + + pub fn body(&self) -> Option { + let b = self.0.body.as_bytes(); + Some(String::from_utf8_lossy(b).to_string()) + } +} + +impl<'js> IntoJs<'js> for JsResponse { + fn into_js(self, ctx: &rquickjs::Ctx<'js>) -> rquickjs::Result> { + let object = rquickjs::Object::new(ctx.clone())?; + object.set("status", self.status())?; + object.set("headers", self.headers())?; + object.set("body", self.body())?; + Ok(object.into_value()) + } +} + +impl<'js> FromJs<'js> for JsResponse { + fn from_js(_: &rquickjs::Ctx<'js>, value: rquickjs::Value<'js>) -> rquickjs::Result { + let object = value.as_object().ok_or(rquickjs::Error::FromJs { + from: value.type_name(), + to: "rquickjs::Object", + message: Some("unable to cast JS Value as object".to_string()), + })?; + let status = object.get::<&str, u16>("status")?; + let headers = object.get::<&str, BTreeMap>("headers")?; + let body = object.get::<&str, Option>("body")?; + let response = Response { + status: reqwest::StatusCode::from_u16(status).map_err(|_| rquickjs::Error::FromJs { + from: "u16", + to: "reqwest::StatusCode", + message: Some("invalid status code".to_string()), + })?, + headers: create_header_map(headers).map_err(|e| rquickjs::Error::FromJs { + from: "BTreeMap", + to: "reqwest::header::HeaderMap", + message: Some(e.to_string()), + })?, + body: body.unwrap_or_default(), + }; + Ok(JsResponse(response)) + } } impl TryFrom for Response { type Error = anyhow::Error; fn try_from(res: JsResponse) -> Result { - let status = reqwest::StatusCode::from_u16(res.status)?; - let headers = create_header_map(res.headers)?; - let body = res.body.unwrap_or_default(); - Ok(Response { status, headers, body: Bytes::from(body) }) + let res = res.0; + Ok(Response { + status: res.status, + headers: res.headers, + body: Bytes::from(res.body.as_bytes().to_vec()), + }) } } @@ -33,16 +82,12 @@ impl TryFrom> for JsResponse { type Error = anyhow::Error; fn try_from(res: Response) -> Result { - let status = res.status.as_u16(); - let mut headers = BTreeMap::new(); - for (key, value) in res.headers.iter() { - let key = key.to_string(); - let value = value.to_str()?.to_string(); - headers.insert(key, value); - } - - let body = Some(std::str::from_utf8(res.body.as_bytes())?.to_owned()); - Ok(JsResponse { status, headers, body }) + let body = String::from_utf8_lossy(res.body.as_ref()).to_string(); + Ok(JsResponse(Response { + status: res.status, + headers: res.headers, + body, + })) } } @@ -51,9 +96,11 @@ mod test { use std::collections::BTreeMap; use anyhow::Result; + use headers::{HeaderName, HeaderValue}; use hyper::body::Bytes; use pretty_assertions::assert_eq; use reqwest::header::HeaderMap; + use rquickjs::{Context, FromJs, IntoJs, Runtime}; use super::JsResponse; @@ -68,17 +115,18 @@ mod test { let js_response: Result = response.try_into(); js_response } + #[test] fn test_to_js_response() { let js_response = create_test_response(); assert!(js_response.is_ok()); let js_response = js_response.unwrap(); - assert_eq!(js_response.status, 200); + assert_eq!(js_response.status(), 200); assert_eq!( - js_response.headers.get("content-type").unwrap(), + js_response.headers().get("content-type").unwrap(), "application/json" ); - assert_eq!(js_response.body, Some("Hello, World!".into())); + assert_eq!(js_response.body(), Some("Hello, World!".into())); } #[test] @@ -94,30 +142,18 @@ mod test { ); assert_eq!(response.body, Bytes::from("Hello, World!")); } - #[test] - fn test_js_response_with_defaults() { - let js_response = JsResponse { - status: 200, - headers: BTreeMap::new(), // Empty headers - body: None, // No body - }; - - let response: Result, _> = js_response.try_into(); - assert!(response.is_ok()); - let response = response.unwrap(); - assert!(response.headers.is_empty()); - assert_eq!(response.body, Bytes::new()); // Assuming `Bytes::new()` is - // the expected result for no - // body - } #[test] fn test_unusual_headers() { let body = "a"; - let mut headers = BTreeMap::new(); - headers.insert("x-unusual-header".to_string(), "🚀".to_string()); - - let js_response = JsResponse { status: 200, headers, body: Some(body.into()) }; + let mut headers = HeaderMap::new(); + headers.insert( + HeaderName::from_static("x-unusual-header"), + HeaderValue::from_str("🚀").unwrap(), + ); + let response = + crate::http::Response { status: reqwest::StatusCode::OK, headers, body: body.into() }; + let js_response = JsResponse(response); let response: Result, _> = js_response.try_into(); assert!(response.is_ok()); @@ -125,4 +161,45 @@ mod test { assert_eq!(response.headers.get("x-unusual-header").unwrap(), "🚀"); assert_eq!(response.body, Bytes::from(body)); } + + #[test] + fn test_response_into_js() { + let runtime = Runtime::new().unwrap(); + let context = Context::base(&runtime).unwrap(); + context.with(|ctx| { + let value = create_test_response().unwrap().into_js(&ctx).unwrap(); + let object = value.as_object().unwrap(); + + let status = object.get::<&str, u16>("status").unwrap(); + let headers = object + .get::<&str, BTreeMap>("headers") + .unwrap(); + let body = object.get::<&str, Option>("body").unwrap(); + + assert_eq!(status, reqwest::StatusCode::OK); + assert_eq!(body, Some("Hello, World!".to_owned())); + assert!(headers.contains_key("content-type")); + assert_eq!( + headers.get("content-type"), + Some(&"application/json".to_owned()) + ); + }); + } + + #[test] + fn test_response_from_js() { + let runtime = Runtime::new().unwrap(); + let context = Context::base(&runtime).unwrap(); + context.with(|ctx| { + let js_response = create_test_response().unwrap().into_js(&ctx).unwrap(); + let response = JsResponse::from_js(&ctx, js_response).unwrap(); + + assert_eq!(response.status(), reqwest::StatusCode::OK.as_u16()); + assert_eq!(response.body(), Some("Hello, World!".to_owned())); + assert_eq!( + response.headers().get("content-type"), + Some(&"application/json".to_owned()) + ); + }); + } } diff --git a/src/cli/javascript/request_filter.rs b/src/cli/javascript/request_filter.rs index 2c223aa742..3afe7f93d9 100644 --- a/src/cli/javascript/request_filter.rs +++ b/src/cli/javascript/request_filter.rs @@ -1,25 +1,51 @@ use std::sync::Arc; use hyper::body::Bytes; -use serde::{Deserialize, Serialize}; +use rquickjs::FromJs; use super::{JsRequest, JsResponse}; use crate::http::Response; use crate::{HttpIO, WorkerIO}; -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] +#[derive(Debug)] pub enum Event { Request(JsRequest), } -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] +#[derive(Debug)] pub enum Command { Request(JsRequest), Response(JsResponse), } +impl<'js> FromJs<'js> for Command { + fn from_js(ctx: &rquickjs::Ctx<'js>, value: rquickjs::Value<'js>) -> rquickjs::Result { + let object = value.as_object().ok_or(rquickjs::Error::FromJs { + from: value.type_name(), + to: "rquickjs::Object", + message: Some("unable to cast JS Value as object".to_string()), + })?; + + if object.contains_key("request")? { + Ok(Command::Request(JsRequest::from_js( + ctx, + object.get("request")?, + )?)) + } else if object.contains_key("response")? { + Ok(Command::Response(JsResponse::from_js( + ctx, + object.get("response")?, + )?)) + } else { + Err(rquickjs::Error::FromJs { + from: "object", + to: "tailcall::cli::javascript::request_filter::Command", + message: Some("object must contain either request or response".to_string()), + }) + } + } +} + pub struct RequestFilter { worker: Arc>, client: Arc, @@ -41,17 +67,17 @@ impl RequestFilter { match command { Some(command) => match command { Command::Request(js_request) => { - let response = self.client.execute(js_request.try_into()?).await?; + let response = self.client.execute(js_request.into()).await?; Ok(response) } Command::Response(js_response) => { // Check if the response is a redirect - if (js_response.status == 301 || js_response.status == 302) - && js_response.headers.contains_key("location") + if (js_response.status() == 301 || js_response.status() == 302) + && js_response.headers().contains_key("location") { request .url_mut() - .set_path(js_response.headers["location"].as_str()); + .set_path(js_response.headers()["location"].as_str()); self.on_request(request).await } else { Ok(js_response.try_into()?) @@ -72,3 +98,66 @@ impl HttpIO for RequestFilter { self.on_request(request).await } } + +#[cfg(test)] +mod tests { + use hyper::body::Bytes; + use rquickjs::{Context, FromJs, IntoJs, Object, Runtime, String as JsString}; + + use crate::cli::javascript::request_filter::Command; + use crate::cli::javascript::{JsRequest, JsResponse}; + use crate::http::Response; + + #[test] + fn test_command_from_invalid_object() { + let runtime = Runtime::new().unwrap(); + let context = Context::base(&runtime).unwrap(); + context.with(|ctx| { + let value = JsString::from_str(ctx.clone(), "invalid") + .unwrap() + .into_value(); + assert!(Command::from_js(&ctx, value).is_err()); + }); + } + + #[test] + fn test_command_from_request() { + let runtime = Runtime::new().unwrap(); + let context = Context::base(&runtime).unwrap(); + context.with(|ctx| { + let request = + reqwest::Request::new(reqwest::Method::GET, "http://example.com/".parse().unwrap()); + let js_request: JsRequest = (&request).try_into().unwrap(); + let value = Object::new(ctx.clone()).unwrap(); + value.set("request", js_request.into_js(&ctx)).unwrap(); + assert!(Command::from_js(&ctx, value.into_value()).is_ok()); + }); + } + + #[test] + fn test_command_from_response() { + let runtime = Runtime::new().unwrap(); + let context = Context::base(&runtime).unwrap(); + context.with(|ctx| { + let js_response = JsResponse::try_from(Response { + status: reqwest::StatusCode::OK, + headers: reqwest::header::HeaderMap::default(), + body: Bytes::new(), + }) + .unwrap(); + let value = Object::new(ctx.clone()).unwrap(); + value.set("response", js_response).unwrap(); + assert!(Command::from_js(&ctx, value.into_value()).is_ok()); + }); + } + + #[test] + fn test_command_from_arbitrary_object() { + let runtime = Runtime::new().unwrap(); + let context = Context::base(&runtime).unwrap(); + context.with(|ctx| { + let value = Object::new(ctx.clone()).unwrap(); + assert!(Command::from_js(&ctx, value.into_value()).is_err()); + }); + } +} diff --git a/src/cli/javascript/runtime.rs b/src/cli/javascript/runtime.rs index 0bd4e51052..a7211d3d5b 100644 --- a/src/cli/javascript/runtime.rs +++ b/src/cli/javascript/runtime.rs @@ -1,15 +1,13 @@ use std::cell::{OnceCell, RefCell}; use std::thread; -use deno_core::{extension, serde_v8, v8, FastString, JsRuntime, RuntimeOptions}; +use rquickjs::{Context, Ctx, FromJs, Function, IntoJs, Value}; use super::request_filter::{Command, Event}; +use super::JsRequest; use crate::{blueprint, WorkerIO}; -struct LocalRuntime { - js_runtime: JsRuntime, - global: v8::Global, -} +struct LocalRuntime(Context); thread_local! { // Practically only one JS runtime is created because CHANNEL_RUNTIME is single threaded. @@ -26,19 +24,34 @@ lazy_static::lazy_static! { .expect("JS runtime not initialized"); } +#[rquickjs::function] +fn qjs_print(msg: String, is_err: bool) { + if is_err { + tracing::error!("{msg}"); + } else { + tracing::info!("{msg}"); + } +} + +fn setup_builtins(ctx: &Ctx<'_>) -> rquickjs::Result<()> { + ctx.globals().set("__qjs_print", js_qjs_print)?; + let _: Value = ctx.eval_file("src/cli/javascript/shim/console.js")?; + + Ok(()) +} + impl LocalRuntime { fn try_new(script: blueprint::Script) -> anyhow::Result { let source = script.source; - extension!(console, js = ["src/cli/javascript/shim/console.js",]); - let mut js_runtime = JsRuntime::new(RuntimeOptions { - extensions: vec![console::init_ops_and_esm()], - ..Default::default() - }); - let global = js_runtime.execute_script("", FastString::from_static("globalThis"))?; - js_runtime.execute_script("", FastString::from(source))?; - tracing::debug!("JS Runtime created: {:?}", thread::current().name()); + let js_runtime = rquickjs::Runtime::new()?; + let context = Context::full(&js_runtime)?; + context.with(|ctx| { + setup_builtins(&ctx)?; + ctx.eval(source) + })?; - Ok(Self { js_runtime, global }) + tracing::debug!("JS Runtime created: {:?}", thread::current().name()); + Ok(Self(context)) } } @@ -58,10 +71,21 @@ impl WorkerIO for Runtime { let script = self.script.clone(); CHANNEL_RUNTIME .spawn(async move { + // initialize runtime if this is the first call + // exit if failed to initialize LOCAL_RUNTIME.with(move |cell| { - cell.borrow() - .get_or_init(|| LocalRuntime::try_new(script).unwrap()); - }); + if cell.borrow().get().is_none() { + LocalRuntime::try_new(script).and_then(|runtime| { + cell.borrow().set(runtime).map_err(|_| { + anyhow::anyhow!( + "trying to reinitialize an already initialized QuickJS runtime" + ) + }) + }) + } else { + Ok(()) + } + })?; call(name, event) }) @@ -69,30 +93,35 @@ impl WorkerIO for Runtime { } } +fn prepare_args<'js>(ctx: &Ctx<'js>, req: JsRequest) -> rquickjs::Result<(Value<'js>,)> { + let object = rquickjs::Object::new(ctx.clone())?; + object.set("request", req.into_js(ctx)?)?; + Ok((object.into_value(),)) +} + fn call(name: String, event: Event) -> anyhow::Result> { LOCAL_RUNTIME.with_borrow_mut(|cell| { let runtime = cell .get_mut() .ok_or(anyhow::anyhow!("JS runtime not initialized"))?; - let js_runtime = &mut runtime.js_runtime; - let scope = &mut js_runtime.handle_scope(); - let global = v8::Local::::try_from(v8::Local::new(scope, &runtime.global))?; - let args = serde_v8::to_v8(scope, event)?; - - // NOTE: unwrap is safe here - // We receive a `None` only if the name of the function is more than the set - // kMaxLength. kMaxLength is set to a very high value ~ 1 Billion, so we - // don't expect to hit this limit. - let fn_server_emit = v8::String::new(scope, name.as_str()).unwrap(); - let fn_server_emit = global - .get(scope, fn_server_emit.into()) - .ok_or(anyhow::anyhow!("globalThis not initialized"))?; - - let fn_server_emit = v8::Local::::try_from(fn_server_emit)?; - let command = fn_server_emit.call(scope, global.into(), &[args]); - - command - .map(|output| Ok(serde_v8::from_v8(scope, output)?)) - .transpose() + runtime.0.with(|ctx| match event { + Event::Request(req) => { + let fn_as_value = ctx + .globals() + .get::<&str, Function>(name.as_str()) + .map_err(|_| anyhow::anyhow!("globalThis not initialized"))?; + + let function = fn_as_value + .as_function() + .ok_or(anyhow::anyhow!("`{name}` is not a function"))?; + + let args = prepare_args(&ctx, req)?; + let command: Option = function.call(args).ok(); + command + .map(|output| Command::from_js(&ctx, output)) + .transpose() + .map_err(|e| anyhow::anyhow!("deserialize failed: {e}")) + } + }) }) } diff --git a/src/cli/javascript/shim/console.js b/src/cli/javascript/shim/console.js index 5c59a344f7..f6c6cb6d03 100644 --- a/src/cli/javascript/shim/console.js +++ b/src/cli/javascript/shim/console.js @@ -1,15 +1,13 @@ -const {core} = Deno - function argsToMessage(...args) { return args.map((arg) => JSON.stringify(arg)).join(" ") } const console = { - log: (...args) => { - core.print(`${argsToMessage(...args)}\n`, false) + log(...args) { + globalThis.__qjs_print(`${argsToMessage(...args)}\n`, false) }, - error: (...args) => { - core.print(`[err]: ${argsToMessage(...args)}\n`, true) + error(...args) { + globalThis.__qjs_print(`${argsToMessage(...args)}\n`, true) }, } diff --git a/tailcall-cloudflare/src/handle.rs b/tailcall-cloudflare/src/handle.rs index 4dd2123d22..94639905db 100644 --- a/tailcall-cloudflare/src/handle.rs +++ b/tailcall-cloudflare/src/handle.rs @@ -1,4 +1,3 @@ -use std::borrow::Borrow; use std::collections::HashMap; use std::rc::Rc; use std::sync::{Arc, RwLock}; @@ -53,7 +52,7 @@ async fn get_app_ctx( if let Some(file_path) = &file_path { if let Some(app_ctx) = read_app_ctx() { - if app_ctx.0 == file_path.borrow() { + if app_ctx.0.eq(file_path) { tracing::info!("Using cached application context"); return Ok(Ok(app_ctx.clone().1)); }