From c9414a1165e142973397d856080c116e73d062f3 Mon Sep 17 00:00:00 2001
From: Mees Delzenne <DelSkayn@users.noreply.github.com>
Date: Mon, 17 Jul 2023 15:40:58 +0200
Subject: [PATCH] Update rquickjs, attempt two. (#2275)

---
 Cargo.lock                                    | 265 ++++-----
 lib/Cargo.toml                                |   2 +-
 lib/src/fnc/script/classes/duration.rs        |  81 ++-
 lib/src/fnc/script/classes/mod.rs             |  10 +-
 lib/src/fnc/script/classes/record.rs          |  94 ++-
 lib/src/fnc/script/classes/uuid.rs            |  81 ++-
 lib/src/fnc/script/fetch/body.rs              |  10 +-
 lib/src/fnc/script/fetch/classes/blob.rs      | 251 ++++----
 lib/src/fnc/script/fetch/classes/form_data.rs | 217 ++++---
 lib/src/fnc/script/fetch/classes/headers.rs   | 484 ++++++++-------
 lib/src/fnc/script/fetch/classes/request.rs   | 387 ++++++------
 .../fnc/script/fetch/classes/response/init.rs |  34 +-
 .../fnc/script/fetch/classes/response/mod.rs  | 555 +++++++++---------
 lib/src/fnc/script/fetch/func.rs              |  32 +-
 lib/src/fnc/script/fetch/mod.rs               |  25 +-
 lib/src/fnc/script/fetch_stub/mod.rs          |  64 +-
 lib/src/fnc/script/from.rs                    |  26 +-
 lib/src/fnc/script/globals/console.rs         |  74 ++-
 lib/src/fnc/script/into.rs                    |  40 +-
 lib/src/fnc/script/main.rs                    |  15 +-
 lib/src/fnc/script/modules/mod.rs             |   4 +-
 lib/src/fnc/script/modules/os.rs              |  38 +-
 lib/src/fnc/script/modules/surrealdb/mod.rs   |   4 +-
 23 files changed, 1320 insertions(+), 1473 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 430562da..8a0260de 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -240,15 +240,6 @@ dependencies = [
  "version_check",
 ]
 
-[[package]]
-name = "aho-corasick"
-version = "0.7.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
-dependencies = [
- "memchr",
-]
-
 [[package]]
 name = "aho-corasick"
 version = "1.0.2"
@@ -330,7 +321,7 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
 dependencies = [
- "windows-sys 0.48.0",
+ "windows-sys",
 ]
 
 [[package]]
@@ -340,7 +331,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
 dependencies = [
  "anstyle",
- "windows-sys 0.48.0",
+ "windows-sys",
 ]
 
 [[package]]
@@ -351,9 +342,9 @@ checksum = "ea50b14b7a4b9343f8c627a7a53c52076482bd4bdad0a24fd3ec533ed616cc2c"
 
 [[package]]
 name = "anyhow"
-version = "1.0.71"
+version = "1.0.72"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
+checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854"
 
 [[package]]
 name = "approx"
@@ -492,9 +483,9 @@ checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae"
 
 [[package]]
 name = "async-trait"
-version = "0.1.69"
+version = "0.1.71"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b2d0f03b3640e3a630367e40c468cb7f309529c708ed1d88597047b0e7c6ef7"
+checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -546,9 +537,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
 [[package]]
 name = "axum"
-version = "0.6.18"
+version = "0.6.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8175979259124331c1d7bf6586ee7e0da434155e4b2d48ec2c8386281d8df39"
+checksum = "a6a1de45611fdb535bfde7b7de4fd54f4fd2b17b1737c0a59b69bf9b92074b8c"
 dependencies = [
  "async-trait",
  "axum-core",
@@ -711,7 +702,7 @@ dependencies = [
  "lazycell",
  "log",
  "peeking_take_while",
- "prettyplease 0.2.9",
+ "prettyplease 0.2.10",
  "proc-macro2",
  "quote",
  "regex",
@@ -1312,9 +1303,9 @@ dependencies = [
 
 [[package]]
 name = "darling"
-version = "0.14.4"
+version = "0.20.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850"
+checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e"
 dependencies = [
  "darling_core",
  "darling_macro",
@@ -1322,37 +1313,37 @@ dependencies = [
 
 [[package]]
 name = "darling_core"
-version = "0.14.4"
+version = "0.20.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0"
+checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621"
 dependencies = [
  "fnv",
  "ident_case",
  "proc-macro2",
  "quote",
  "strsim",
- "syn 1.0.109",
+ "syn 2.0.26",
 ]
 
 [[package]]
 name = "darling_macro"
-version = "0.14.4"
+version = "0.20.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e"
+checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
 dependencies = [
  "darling_core",
  "quote",
- "syn 1.0.109",
+ "syn 2.0.26",
 ]
 
 [[package]]
 name = "dashmap"
-version = "5.4.0"
+version = "5.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc"
+checksum = "6943ae99c34386c84a470c499d3414f66502a41340aa895406e0d2e4a207b91d"
 dependencies = [
  "cfg-if",
- "hashbrown 0.12.3",
+ "hashbrown 0.14.0",
  "lock_api",
  "once_cell",
  "parking_lot_core 0.9.8",
@@ -1508,9 +1499,9 @@ dependencies = [
 
 [[package]]
 name = "equivalent"
-version = "1.0.0"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
 
 [[package]]
 name = "errno"
@@ -1520,7 +1511,7 @@ checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
 dependencies = [
  "errno-dragonfly",
  "libc",
- "windows-sys 0.48.0",
+ "windows-sys",
 ]
 
 [[package]]
@@ -1577,7 +1568,7 @@ checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5"
 dependencies = [
  "cfg-if",
  "rustix 0.38.4",
- "windows-sys 0.48.0",
+ "windows-sys",
 ]
 
 [[package]]
@@ -1892,9 +1883,9 @@ dependencies = [
 
 [[package]]
 name = "geo-types"
-version = "0.7.10"
+version = "0.7.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1019f6d372c5b53143f08deee4168d05c22920fe5e0f51f0dfb0e8ffb67ec11e"
+checksum = "9705398c5c7b26132e74513f4ee7c1d7dafd786004991b375c172be2be0eecaa"
 dependencies = [
  "approx",
  "num-traits",
@@ -1949,11 +1940,11 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
 
 [[package]]
 name = "globset"
-version = "0.4.10"
+version = "0.4.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc"
+checksum = "1391ab1f92ffcc08911957149833e682aa3fe252b9f45f966d2ef972274c97df"
 dependencies = [
- "aho-corasick 0.7.20",
+ "aho-corasick",
  "bstr",
  "fnv",
  "log",
@@ -2223,13 +2214,14 @@ dependencies = [
 
 [[package]]
 name = "hyper-rustls"
-version = "0.24.0"
+version = "0.24.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0646026eb1b3eea4cd9ba47912ea5ce9cc07713d105b1a14698f4e6433d348b7"
+checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97"
 dependencies = [
+ "futures-util",
  "http",
  "hyper",
- "rustls 0.21.2",
+ "rustls 0.21.5",
  "tokio",
  "tokio-rustls 0.24.1",
 ]
@@ -2415,7 +2407,7 @@ checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
 dependencies = [
  "hermit-abi 0.3.2",
  "libc",
- "windows-sys 0.48.0",
+ "windows-sys",
 ]
 
 [[package]]
@@ -2432,7 +2424,7 @@ checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
 dependencies = [
  "hermit-abi 0.3.2",
  "rustix 0.38.4",
- "windows-sys 0.48.0",
+ "windows-sys",
 ]
 
 [[package]]
@@ -2455,9 +2447,9 @@ dependencies = [
 
 [[package]]
 name = "itoa"
-version = "1.0.8"
+version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
 
 [[package]]
 name = "jobserver"
@@ -2746,7 +2738,7 @@ dependencies = [
  "libc",
  "log",
  "wasi 0.11.0+wasi-snapshot-preview1",
- "windows-sys 0.48.0",
+ "windows-sys",
 ]
 
 [[package]]
@@ -3147,9 +3139,9 @@ dependencies = [
 
 [[package]]
 name = "paste"
-version = "1.0.12"
+version = "1.0.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79"
+checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
 
 [[package]]
 name = "pbkdf2"
@@ -3348,9 +3340,9 @@ dependencies = [
 
 [[package]]
 name = "prettyplease"
-version = "0.2.9"
+version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9825a04601d60621feed79c4e6b56d65db77cdca55cef43b46b0de1096d1c282"
+checksum = "92139198957b410250d43fad93e630d956499a625c527eda65175c8680f83387"
 dependencies = [
  "proc-macro2",
  "syn 2.0.26",
@@ -3401,9 +3393,9 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.64"
+version = "1.0.66"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da"
+checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
 dependencies = [
  "unicode-ident",
 ]
@@ -3602,9 +3594,9 @@ dependencies = [
 
 [[package]]
 name = "quote"
-version = "1.0.29"
+version = "1.0.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105"
+checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0"
 dependencies = [
  "proc-macro2",
 ]
@@ -3775,7 +3767,7 @@ version = "1.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575"
 dependencies = [
- "aho-corasick 1.0.2",
+ "aho-corasick",
  "memchr",
  "regex-automata 0.3.3",
  "regex-syntax 0.7.4",
@@ -3796,7 +3788,7 @@ version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310"
 dependencies = [
- "aho-corasick 1.0.2",
+ "aho-corasick",
  "memchr",
  "regex-syntax 0.7.4",
 ]
@@ -3854,7 +3846,7 @@ dependencies = [
  "once_cell",
  "percent-encoding",
  "pin-project-lite",
- "rustls 0.21.2",
+ "rustls 0.21.5",
  "rustls-pemfile",
  "serde",
  "serde_json",
@@ -3998,9 +3990,9 @@ dependencies = [
 
 [[package]]
 name = "rquickjs"
-version = "0.3.1"
+version = "0.4.0-beta.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6db7788c2818f4546daabe9ae2d1ee2f4db61ab1998d4b483494c4193cc38dab"
+checksum = "ccd00408a820b4b0986ef746b9d614a248930b05059a64f621fe7f991bfff3c0"
 dependencies = [
  "rquickjs-core",
  "rquickjs-macro",
@@ -4008,9 +4000,9 @@ dependencies = [
 
 [[package]]
 name = "rquickjs-core"
-version = "0.3.1"
+version = "0.4.0-beta.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b12cf8646fe0af5bcff2822ccd162990f0679a1f9287c7257f4f4193a9d31ea9"
+checksum = "73d8e512a3ecb683bf3ae03346ee60450790592ca179a9613491ae0a141c42a9"
 dependencies = [
  "async-lock",
  "relative-path",
@@ -4019,9 +4011,9 @@ dependencies = [
 
 [[package]]
 name = "rquickjs-macro"
-version = "0.3.1"
+version = "0.4.0-beta.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "80564583a91b0ae6b2d6b9b3d0f8ffd69a4b17202cc63a12df78dfa8983885fc"
+checksum = "254f40a1a45a65e2fed556ee5f2d54eba1ba537e85109e3ac7e11fca759e9c0d"
 dependencies = [
  "darling",
  "fnv",
@@ -4032,14 +4024,14 @@ dependencies = [
  "proc-macro2",
  "quote",
  "rquickjs-core",
- "syn 1.0.109",
+ "syn 2.0.26",
 ]
 
 [[package]]
 name = "rquickjs-sys"
-version = "0.3.1"
+version = "0.4.0-beta.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b747058afd4d988d056e4972ec8516a5a86fdfc103c1c1485bfee8966a0743ae"
+checksum = "351f5b86c2588d2f5eb7c50df37adc4dedff944d0f6cd7913dd6e3df1636ec6e"
 dependencies = [
  "bindgen 0.65.1",
  "cc",
@@ -4116,7 +4108,7 @@ dependencies = [
  "io-lifetimes",
  "libc",
  "linux-raw-sys 0.3.8",
- "windows-sys 0.48.0",
+ "windows-sys",
 ]
 
 [[package]]
@@ -4129,7 +4121,7 @@ dependencies = [
  "errno",
  "libc",
  "linux-raw-sys 0.4.3",
- "windows-sys 0.48.0",
+ "windows-sys",
 ]
 
 [[package]]
@@ -4146,9 +4138,9 @@ dependencies = [
 
 [[package]]
 name = "rustls"
-version = "0.21.2"
+version = "0.21.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e32ca28af694bc1bbf399c33a516dbdf1c90090b8ab23c2bc24f834aa2247f5f"
+checksum = "79ea77c539259495ce8ca47f53e66ae0330a8819f67e23ac96ca02f50e7b7d36"
 dependencies = [
  "log",
  "ring",
@@ -4167,9 +4159,9 @@ dependencies = [
 
 [[package]]
 name = "rustls-webpki"
-version = "0.100.1"
+version = "0.101.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b"
+checksum = "15f36a6828982f422756984e47912a7a51dcbc2a197aa791158f8ca61cd8204e"
 dependencies = [
  "ring",
  "untrusted",
@@ -4177,9 +4169,9 @@ dependencies = [
 
 [[package]]
 name = "rustversion"
-version = "1.0.12"
+version = "1.0.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06"
+checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
 
 [[package]]
 name = "rustyline"
@@ -4218,9 +4210,9 @@ dependencies = [
 
 [[package]]
 name = "ryu"
-version = "1.0.14"
+version = "1.0.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
 
 [[package]]
 name = "salsa20"
@@ -4242,11 +4234,11 @@ dependencies = [
 
 [[package]]
 name = "schannel"
-version = "0.1.21"
+version = "0.1.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3"
+checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
 dependencies = [
- "windows-sys 0.42.0",
+ "windows-sys",
 ]
 
 [[package]]
@@ -4314,9 +4306,9 @@ dependencies = [
 
 [[package]]
 name = "semver"
-version = "1.0.17"
+version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
+checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
 dependencies = [
  "serde",
 ]
@@ -4338,9 +4330,9 @@ dependencies = [
 
 [[package]]
 name = "serde_bytes"
-version = "0.11.9"
+version = "0.11.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294"
+checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff"
 dependencies = [
  "serde",
 ]
@@ -4368,9 +4360,9 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.102"
+version = "1.0.103"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5062a995d481b2308b6064e9af76011f2921c35f97b0468811ed9f6cd91dfed"
+checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b"
 dependencies = [
  "itoa",
  "ryu",
@@ -4379,9 +4371,9 @@ dependencies = [
 
 [[package]]
 name = "serde_path_to_error"
-version = "0.1.12"
+version = "0.1.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b1b6471d7496b051e03f1958802a73f88b947866f5146f329e47e36554f4e55"
+checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335"
 dependencies = [
  "itoa",
  "serde",
@@ -4516,9 +4508,9 @@ dependencies = [
 
 [[package]]
 name = "smallvec"
-version = "1.10.0"
+version = "1.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
+checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
 
 [[package]]
 name = "snap"
@@ -4915,7 +4907,7 @@ dependencies = [
  "fastrand",
  "redox_syscall 0.3.5",
  "rustix 0.37.23",
- "windows-sys 0.48.0",
+ "windows-sys",
 ]
 
 [[package]]
@@ -4934,7 +4926,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237"
 dependencies = [
  "rustix 0.37.23",
- "windows-sys 0.48.0",
+ "windows-sys",
 ]
 
 [[package]]
@@ -5070,7 +5062,7 @@ dependencies = [
  "signal-hook-registry",
  "socket2",
  "tokio-macros",
- "windows-sys 0.48.0",
+ "windows-sys",
 ]
 
 [[package]]
@@ -5121,7 +5113,7 @@ version = "0.24.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
 dependencies = [
- "rustls 0.21.2",
+ "rustls 0.21.5",
  "tokio",
 ]
 
@@ -5186,9 +5178,9 @@ checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
 
 [[package]]
 name = "toml_edit"
-version = "0.19.11"
+version = "0.19.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "266f016b7f039eec8a1a80dfe6156b633d208b9fccca5e4db1d6775b0c4e34a7"
+checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a"
 dependencies = [
  "indexmap 2.0.0",
  "toml_datetime",
@@ -5437,9 +5429,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
 
 [[package]]
 name = "unicode-ident"
-version = "1.0.10"
+version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73"
+checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
 
 [[package]]
 name = "unicode-normalization"
@@ -5499,9 +5491,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
 
 [[package]]
 name = "uuid"
-version = "1.4.0"
+version = "1.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be"
+checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d"
 dependencies = [
  "atomic",
  "getrandom 0.2.10",
@@ -5777,21 +5769,6 @@ dependencies = [
  "windows-targets",
 ]
 
-[[package]]
-name = "windows-sys"
-version = "0.42.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
-dependencies = [
- "windows_aarch64_gnullvm 0.42.2",
- "windows_aarch64_msvc 0.42.2",
- "windows_i686_gnu 0.42.2",
- "windows_i686_msvc 0.42.2",
- "windows_x86_64_gnu 0.42.2",
- "windows_x86_64_gnullvm 0.42.2",
- "windows_x86_64_msvc 0.42.2",
-]
-
 [[package]]
 name = "windows-sys"
 version = "0.48.0"
@@ -5807,93 +5784,51 @@ version = "0.48.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
 dependencies = [
- "windows_aarch64_gnullvm 0.48.0",
- "windows_aarch64_msvc 0.48.0",
- "windows_i686_gnu 0.48.0",
- "windows_i686_msvc 0.48.0",
- "windows_x86_64_gnu 0.48.0",
- "windows_x86_64_gnullvm 0.48.0",
- "windows_x86_64_msvc 0.48.0",
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
 ]
 
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
-
 [[package]]
 name = "windows_aarch64_gnullvm"
 version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
 
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
-
 [[package]]
 name = "windows_aarch64_msvc"
 version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
 
-[[package]]
-name = "windows_i686_gnu"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
-
 [[package]]
 name = "windows_i686_gnu"
 version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
 
-[[package]]
-name = "windows_i686_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
-
 [[package]]
 name = "windows_i686_msvc"
 version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
 
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
-
 [[package]]
 name = "windows_x86_64_gnu"
 version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
 
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
-
 [[package]]
 name = "windows_x86_64_gnullvm"
 version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
 
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
-
 [[package]]
 name = "windows_x86_64_msvc"
 version = "0.48.0"
@@ -5902,9 +5837,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
 
 [[package]]
 name = "winnow"
-version = "0.4.7"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448"
+checksum = "81fac9742fd1ad1bd9643b991319f72dd031016d44b77039a26977eb667141e7"
 dependencies = [
  "memchr",
 ]
diff --git a/lib/Cargo.toml b/lib/Cargo.toml
index f3830b5b..db217307 100644
--- a/lib/Cargo.toml
+++ b/lib/Cargo.toml
@@ -75,7 +75,7 @@ fuzzy-matcher = "0.3.7"
 geo = { version = "0.25.1", features = ["use-serde"] }
 indexmap = { version = "1.9.3", features = ["serde"] }
 indxdb = { version = "0.3.0", optional = true }
-js = { version = "0.3.1" , package = "rquickjs", features = ["array-buffer", "bindgen", "classes", "futures", "loader", "macro", "parallel", "properties","rust-alloc"], optional = true }
+js = { version = "0.4.0-beta.2" , package = "rquickjs", features = ["array-buffer", "bindgen", "classes", "futures", "loader", "macro", "parallel", "properties","rust-alloc"], optional = true }
 jsonwebtoken = "8.3.0"
 lexicmp = "0.1.0"
 lru = "0.10.1"
diff --git a/lib/src/fnc/script/classes/duration.rs b/lib/src/fnc/script/classes/duration.rs
index 7631deb3..7b36849b 100644
--- a/lib/src/fnc/script/classes/duration.rs
+++ b/lib/src/fnc/script/classes/duration.rs
@@ -1,51 +1,48 @@
-#[js::bind(object, public)]
-#[quickjs(bare)]
-#[allow(non_snake_case)]
-#[allow(unused_variables)]
-#[allow(clippy::module_inception)]
-pub mod duration {
+use js::class::Trace;
 
-	use crate::sql::duration;
-	use crate::sql::value::Value;
-	use js::{class::Ref, function::Rest};
+use crate::sql::duration;
 
-	#[derive(Clone)]
-	#[quickjs(cloneable)]
-	pub struct Duration {
-		pub(crate) value: Option<duration::Duration>,
+#[derive(Clone, Trace)]
+#[js::class]
+pub struct Duration {
+	#[qjs(skip_trace)]
+	pub(crate) value: Option<duration::Duration>,
+}
+
+#[js::methods]
+impl Duration {
+	#[qjs(constructor)]
+	pub fn new(value: String) -> Self {
+		Self {
+			value: duration::Duration::try_from(value).ok(),
+		}
 	}
 
-	impl Duration {
-		#[quickjs(constructor)]
-		pub fn new(value: String, args: Rest<Value>) -> Self {
-			Self {
-				value: duration::Duration::try_from(value).ok(),
-			}
+	#[qjs(get)]
+	pub fn value(&self) -> String {
+		match &self.value {
+			Some(v) => v.to_raw(),
+			None => String::from("Invalid Duration"),
 		}
-		#[quickjs(get)]
-		pub fn value(&self) -> String {
-			match &self.value {
-				Some(v) => v.to_raw(),
-				None => String::from("Invalid Duration"),
-			}
+	}
+	// Compare two Duration instances
+	pub fn is(a: &Duration, b: &Duration) -> bool {
+		a.value.is_some() && b.value.is_some() && a.value == b.value
+	}
+	/// Convert the object to a string
+	#[qjs(rename = "toString")]
+	pub fn js_to_string(&self) -> String {
+		match &self.value {
+			Some(v) => v.to_raw(),
+			None => String::from("Invalid Duration"),
 		}
-		// Compare two Duration instances
-		pub fn is(a: Ref<Duration>, b: Ref<Duration>, args: Rest<()>) -> bool {
-			a.value.is_some() && b.value.is_some() && a.value == b.value
-		}
-		/// Convert the object to a string
-		pub fn toString(&self, args: Rest<()>) -> String {
-			match &self.value {
-				Some(v) => v.to_raw(),
-				None => String::from("Invalid Duration"),
-			}
-		}
-		/// Convert the object to JSON
-		pub fn toJSON(&self, args: Rest<()>) -> String {
-			match &self.value {
-				Some(v) => v.to_raw(),
-				None => String::from("Invalid Duration"),
-			}
+	}
+	/// Convert the object to JSON
+	#[qjs(rename = "toJSON")]
+	pub fn to_json(&self) -> String {
+		match &self.value {
+			Some(v) => v.to_raw(),
+			None => String::from("Invalid Duration"),
 		}
 	}
 }
diff --git a/lib/src/fnc/script/classes/mod.rs b/lib/src/fnc/script/classes/mod.rs
index 11041b2d..6699419e 100644
--- a/lib/src/fnc/script/classes/mod.rs
+++ b/lib/src/fnc/script/classes/mod.rs
@@ -1,13 +1,13 @@
-use js::{Ctx, Result};
+use js::{Class, Ctx, Result};
 
 pub mod duration;
 pub mod record;
 pub mod uuid;
 
-pub fn init(ctx: Ctx<'_>) -> Result<()> {
+pub fn init(ctx: &Ctx<'_>) -> Result<()> {
 	let globals = ctx.globals();
-	globals.init_def::<duration::Duration>()?;
-	globals.init_def::<record::Record>()?;
-	globals.init_def::<uuid::Uuid>()?;
+	Class::<duration::Duration>::define(&globals)?;
+	Class::<record::Record>::define(&globals)?;
+	Class::<uuid::Uuid>::define(&globals)?;
 	Ok(())
 }
diff --git a/lib/src/fnc/script/classes/record.rs b/lib/src/fnc/script/classes/record.rs
index 0508f8be..f8c27ce8 100644
--- a/lib/src/fnc/script/classes/record.rs
+++ b/lib/src/fnc/script/classes/record.rs
@@ -1,56 +1,52 @@
-#[js::bind(object, public)]
-#[quickjs(bare)]
-#[allow(non_snake_case)]
-#[allow(unused_variables)]
-#[allow(clippy::module_inception)]
-pub mod record {
+use crate::sql::thing;
+use crate::sql::value::Value;
+use js::class::Trace;
 
-	use crate::sql::thing;
-	use crate::sql::value::Value;
-	use js::{class::Ref, function::Rest};
+#[derive(Clone, Trace)]
+#[js::class]
+pub struct Record {
+	#[qjs(skip_trace)]
+	pub(crate) value: thing::Thing,
+}
 
-	#[derive(Clone)]
-	#[quickjs(cloneable)]
-	pub struct Record {
-		pub(crate) value: thing::Thing,
+#[js::methods]
+impl Record {
+	#[qjs(constructor)]
+	pub fn new(tb: String, id: Value) -> Self {
+		Self {
+			value: thing::Thing {
+				tb,
+				id: match id {
+					Value::Array(v) => v.into(),
+					Value::Object(v) => v.into(),
+					Value::Number(v) => v.into(),
+					v => v.as_string().into(),
+				},
+			},
+		}
 	}
 
-	impl Record {
-		#[quickjs(constructor)]
-		pub fn new(tb: String, id: Value, args: Rest<()>) -> Self {
-			Self {
-				value: thing::Thing {
-					tb,
-					id: match id {
-						Value::Array(v) => v.into(),
-						Value::Object(v) => v.into(),
-						Value::Number(v) => v.into(),
-						v => v.as_string().into(),
-					},
-				},
-			}
-		}
+	#[qjs(get)]
+	pub fn tb(&self) -> String {
+		self.value.tb.clone()
+	}
 
-		#[quickjs(get)]
-		pub fn tb(&self) -> String {
-			self.value.tb.clone()
-		}
-
-		#[quickjs(get)]
-		pub fn id(&self) -> String {
-			self.value.id.to_raw()
-		}
-		// Compare two Record instances
-		pub fn is<'js>(a: Ref<'js, Record>, b: Ref<'js, Record>, args: Rest<()>) -> bool {
-			a.value == b.value
-		}
-		/// Convert the object to a string
-		pub fn toString(&self, args: Rest<()>) -> String {
-			self.value.to_raw()
-		}
-		/// Convert the object to JSON
-		pub fn toJSON(&self, args: Rest<()>) -> String {
-			self.value.to_raw()
-		}
+	#[qjs(get)]
+	pub fn id(&self) -> String {
+		self.value.id.to_raw()
+	}
+	// Compare two Record instances
+	pub fn is(a: &Record, b: &Record) -> bool {
+		a.value == b.value
+	}
+	/// Convert the object to a string
+	#[qjs(rename = "toString")]
+	pub fn js_to_string(&self) -> String {
+		self.value.to_raw()
+	}
+	/// Convert the object to JSON
+	#[qjs(rename = "toJSON")]
+	pub fn to_json(&self) -> String {
+		self.value.to_raw()
 	}
 }
diff --git a/lib/src/fnc/script/classes/uuid.rs b/lib/src/fnc/script/classes/uuid.rs
index 4f759ec4..e89e412e 100644
--- a/lib/src/fnc/script/classes/uuid.rs
+++ b/lib/src/fnc/script/classes/uuid.rs
@@ -1,51 +1,46 @@
-#[js::bind(object, public)]
-#[quickjs(bare)]
-#[allow(non_snake_case)]
-#[allow(unused_variables)]
-#[allow(clippy::module_inception)]
-pub mod uuid {
+use crate::sql::uuid;
+use js::class::Trace;
 
-	use crate::sql::uuid;
-	use crate::sql::value::Value;
-	use js::{class::Ref, function::Rest};
+#[derive(Clone, Trace)]
+#[js::class]
+pub struct Uuid {
+	#[qjs(skip_trace)]
+	pub(crate) value: Option<uuid::Uuid>,
+}
 
-	#[derive(Clone)]
-	#[quickjs(cloneable)]
-	pub struct Uuid {
-		pub(crate) value: Option<uuid::Uuid>,
+#[js::methods]
+impl Uuid {
+	#[qjs(constructor)]
+	pub fn new(value: String) -> Self {
+		Self {
+			value: uuid::Uuid::try_from(value).ok(),
+		}
 	}
-
-	impl Uuid {
-		#[quickjs(constructor)]
-		pub fn new(value: String, args: Rest<Value>) -> Self {
-			Self {
-				value: uuid::Uuid::try_from(value).ok(),
-			}
+	#[qjs(get)]
+	pub fn value(&self) -> String {
+		match &self.value {
+			Some(v) => v.to_raw(),
+			None => String::from("Invalid Uuid"),
 		}
-		#[quickjs(get)]
-		pub fn value(&self) -> String {
-			match &self.value {
-				Some(v) => v.to_raw(),
-				None => String::from("Invalid Uuid"),
-			}
+	}
+	// Compare two Uuid instances
+	pub fn is(a: &Uuid, b: &Uuid) -> bool {
+		a.value.is_some() && b.value.is_some() && a.value == b.value
+	}
+	/// Convert the object to a string
+	#[qjs(rename = "toString")]
+	pub fn js_to_string(&self) -> String {
+		match &self.value {
+			Some(v) => v.to_raw(),
+			None => String::from("Invalid Uuid"),
 		}
-		// Compare two Uuid instances
-		pub fn is<'js>(a: Ref<'js, Uuid>, b: Ref<'js, Uuid>, _args: Rest<()>) -> bool {
-			a.value.is_some() && b.value.is_some() && a.value == b.value
-		}
-		/// Convert the object to a string
-		pub fn toString(&self, _args: Rest<()>) -> String {
-			match &self.value {
-				Some(v) => v.to_raw(),
-				None => String::from("Invalid Uuid"),
-			}
-		}
-		/// Convert the object to JSON
-		pub fn toJSON(&self, _args: Rest<()>) -> String {
-			match &self.value {
-				Some(v) => v.to_raw(),
-				None => String::from("Invalid Uuid"),
-			}
+	}
+	/// Convert the object to JSON
+	#[qjs(rename = "toJSON")]
+	pub fn to_json(&self) -> String {
+		match &self.value {
+			Some(v) => v.to_raw(),
+			None => String::from("Invalid Uuid"),
 		}
 	}
 }
diff --git a/lib/src/fnc/script/fetch/body.rs b/lib/src/fnc/script/fetch/body.rs
index 6e7fa863..17cb19b7 100644
--- a/lib/src/fnc/script/fetch/body.rs
+++ b/lib/src/fnc/script/fetch/body.rs
@@ -1,4 +1,4 @@
-use crate::fnc::script::fetch::{classes::BlobClass, stream::ReadableStream, RequestError};
+use crate::fnc::script::fetch::{stream::ReadableStream, RequestError};
 use bytes::{Bytes, BytesMut};
 use futures::{future, Stream, TryStreamExt};
 use js::{ArrayBuffer, Class, Ctx, Error, Exception, FromJs, Result, Type, TypedArray, Value};
@@ -7,6 +7,8 @@ use std::{
 	result::Result as StdResult,
 };
 
+use super::classes::Blob;
+
 pub type StreamItem = StdResult<Bytes, RequestError>;
 
 #[derive(Clone)]
@@ -127,7 +129,7 @@ impl Body {
 }
 
 impl<'js> FromJs<'js> for Body {
-	fn from_js(ctx: Ctx<'js>, value: Value<'js>) -> Result<Self> {
+	fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> {
 		let object = match value.type_of() {
 			Type::String => {
 				let string = value.as_string().unwrap().to_string()?;
@@ -142,7 +144,7 @@ impl<'js> FromJs<'js> for Body {
 				})
 			}
 		};
-		if let Ok(x) = Class::<BlobClass>::from_object(object.clone()) {
+		if let Some(x) = Class::<Blob>::from_object(object.clone()) {
 			let borrow = x.borrow();
 			return Ok(Body::buffer(BodyKind::Blob(borrow.mime.clone()), borrow.data.clone()));
 		}
@@ -194,7 +196,7 @@ impl<'js> FromJs<'js> for Body {
 				.ok_or_else(|| Exception::throw_type(ctx, "Buffer is already detached"))?;
 			return Ok(Body::buffer(BodyKind::Buffer, Bytes::copy_from_slice(bytes)));
 		}
-		if let Ok(x) = ArrayBuffer::from_object(object.clone()) {
+		if let Some(x) = ArrayBuffer::from_object(object.clone()) {
 			let bytes = x
 				.as_bytes()
 				.ok_or_else(|| Exception::throw_type(ctx, "Buffer is already detached"))?;
diff --git a/lib/src/fnc/script/fetch/classes/blob.rs b/lib/src/fnc/script/fetch/classes/blob.rs
index a616b4de..95e18bb0 100644
--- a/lib/src/fnc/script/fetch/classes/blob.rs
+++ b/lib/src/fnc/script/fetch/classes/blob.rs
@@ -1,9 +1,11 @@
 //! Blob class implementation
 
-use bytes::BytesMut;
-use js::{bind, prelude::Coerced, ArrayBuffer, Class, Ctx, Exception, FromJs, Result, Value};
-
-pub use blob::Blob as BlobClass;
+use bytes::{Bytes, BytesMut};
+use js::{
+	class::Trace,
+	prelude::{Coerced, Opt},
+	ArrayBuffer, Class, Ctx, Exception, FromJs, Object, Result, Value,
+};
 
 #[derive(Clone, Copy)]
 pub enum EndingType {
@@ -12,7 +14,7 @@ pub enum EndingType {
 }
 
 fn append_blob_part<'js>(
-	ctx: Ctx<'js>,
+	ctx: &Ctx<'js>,
 	value: Value<'js>,
 	ending: EndingType,
 	data: &mut BytesMut,
@@ -23,11 +25,11 @@ fn append_blob_part<'js>(
 	const LINE_ENDING: &[u8] = b"\n";
 
 	if let Some(object) = value.as_object() {
-		if let Ok(x) = Class::<BlobClass>::from_object(object.clone()) {
+		if let Some(x) = Class::<Blob>::from_object(object.clone()) {
 			data.extend_from_slice(&x.borrow().data);
 			return Ok(());
 		}
-		if let Ok(x) = ArrayBuffer::from_object(object.clone()) {
+		if let Some(x) = ArrayBuffer::from_object(object.clone()) {
 			data.extend_from_slice(x.as_bytes().ok_or_else(|| {
 				Exception::throw_type(ctx, "Tried to construct blob with detached buffer")
 			})?);
@@ -74,144 +76,121 @@ fn normalize_type(mut ty: String) -> String {
 	}
 }
 
-#[bind(object, public)]
-#[quickjs(bare)]
-#[allow(non_snake_case)]
-#[allow(unused_variables)]
-#[allow(clippy::module_inception)]
-mod blob {
-	use super::*;
+#[derive(Clone, Trace)]
+#[js::class]
+pub struct Blob {
+	pub(crate) mime: String,
+	// TODO: make bytes?
+	#[qjs(skip_trace)]
+	pub(crate) data: Bytes,
+}
 
-	use bytes::{Bytes, BytesMut};
-	use js::{
-		function::{Opt, Rest},
-		ArrayBuffer, Ctx, Exception, Object, Result, Value,
-	};
+#[js::methods]
+impl Blob {
+	// ------------------------------
+	// Constructor
+	// ------------------------------
 
-	#[derive(Clone)]
-	#[quickjs(cloneable)]
-	pub struct Blob {
-		pub(crate) mime: String,
-		// TODO: make bytes?
-		pub(crate) data: Bytes,
+	#[qjs(constructor)]
+	pub fn new<'js>(
+		ctx: Ctx<'js>,
+		parts: Opt<Value<'js>>,
+		options: Opt<Object<'js>>,
+	) -> Result<Self> {
+		let mut r#type = String::new();
+		let mut endings = EndingType::Transparent;
+
+		if let Some(obj) = options.into_inner() {
+			if let Some(x) = obj.get::<_, Option<Coerced<String>>>("type")? {
+				r#type = normalize_type(x.to_string());
+			}
+			if let Some(Coerced(x)) = obj.get::<_, Option<Coerced<String>>>("endings")? {
+				if x == "native" {
+					endings = EndingType::Native;
+				} else if x != "transparent" {
+					return Err(Exception::throw_type(
+						&ctx,
+						",expected endings to be either 'transparent' or 'native'",
+					));
+				}
+			}
+		}
+
+		let data = if let Some(parts) = parts.into_inner() {
+			let array = parts
+				.into_array()
+				.ok_or_else(|| Exception::throw_type(&ctx, "Blob parts are not a sequence"))?;
+
+			let mut buffer = BytesMut::new();
+
+			for elem in array.iter::<Value>() {
+				let elem = elem?;
+				append_blob_part(&ctx, elem, endings, &mut buffer)?;
+			}
+			buffer.freeze()
+		} else {
+			Bytes::new()
+		};
+		Ok(Self {
+			mime: r#type,
+			data,
+		})
 	}
 
-	impl Blob {
-		// ------------------------------
-		// Constructor
-		// ------------------------------
+	// ------------------------------
+	// Instance properties
+	// ------------------------------
 
-		#[quickjs(constructor)]
-		pub fn new<'js>(
-			ctx: Ctx<'js>,
-			parts: Opt<Value<'js>>,
-			options: Opt<Object<'js>>,
-			_rest: Rest<()>,
-		) -> Result<Self> {
-			let mut r#type = String::new();
-			let mut endings = EndingType::Transparent;
+	#[qjs(get)]
+	pub fn size(&self) -> usize {
+		self.data.len()
+	}
 
-			if let Some(obj) = options.into_inner() {
-				if let Some(x) = obj.get::<_, Option<Coerced<String>>>("type")? {
-					r#type = normalize_type(x.to_string());
-				}
-				if let Some(Coerced(x)) = obj.get::<_, Option<Coerced<String>>>("endings")? {
-					if x == "native" {
-						endings = EndingType::Native;
-					} else if x != "transparent" {
-						return Err(Exception::throw_type(
-							ctx,
-							",expected endings to be either 'transparent' or 'native'",
-						));
-					}
-				}
-			}
+	#[qjs(get, rename = "type")]
+	pub fn r#type(&self) -> String {
+		self.mime.clone()
+	}
 
-			let data = if let Some(parts) = parts.into_inner() {
-				let array = parts
-					.into_array()
-					.ok_or_else(|| Exception::throw_type(ctx, "Blob parts are not a sequence"))?;
-
-				let mut buffer = BytesMut::new();
-
-				for elem in array.iter::<Value>() {
-					let elem = elem?;
-					append_blob_part(ctx, elem, endings, &mut buffer)?;
-				}
-				buffer.freeze()
-			} else {
-				Bytes::new()
-			};
-			Ok(Self {
-				mime: r#type,
-				data,
-			})
+	pub fn slice(&self, start: Opt<isize>, end: Opt<isize>, content_type: Opt<String>) -> Blob {
+		// see https://w3c.github.io/FileAPI/#slice-blob
+		let start = start.into_inner().unwrap_or_default();
+		let start = if start < 0 {
+			(self.data.len() as isize + start).max(0) as usize
+		} else {
+			start as usize
+		};
+		let end = end.into_inner().unwrap_or_default();
+		let end = if end < 0 {
+			(self.data.len() as isize + end).max(0) as usize
+		} else {
+			end as usize
+		};
+		let data = self.data.slice(start..end);
+		let content_type = content_type.into_inner().map(normalize_type).unwrap_or_default();
+		Blob {
+			mime: content_type,
+			data,
 		}
+	}
 
-		// ------------------------------
-		// Instance properties
-		// ------------------------------
+	pub async fn text(&self) -> Result<String> {
+		let text = String::from_utf8(self.data.to_vec())?;
+		Ok(text)
+	}
 
-		#[quickjs(get)]
-		pub fn size(&self) -> usize {
-			self.data.len()
-		}
+	#[qjs(rename = "arrayBuffer")]
+	pub async fn array_buffer<'js>(&self, ctx: Ctx<'js>) -> Result<ArrayBuffer<'js>> {
+		ArrayBuffer::new(ctx, self.data.to_vec())
+	}
 
-		#[quickjs(get)]
-		#[quickjs(rename = "type")]
-		pub fn r#type(&self) -> String {
-			self.mime.clone()
-		}
+	// ------------------------------
+	// Instance methods
+	// ------------------------------
 
-		pub fn slice(
-			&self,
-			start: Opt<isize>,
-			end: Opt<isize>,
-			content_type: Opt<String>,
-			_rest: Rest<()>,
-		) -> Blob {
-			// see https://w3c.github.io/FileAPI/#slice-blob
-			let start = start.into_inner().unwrap_or_default();
-			let start = if start < 0 {
-				(self.data.len() as isize + start).max(0) as usize
-			} else {
-				start as usize
-			};
-			let end = end.into_inner().unwrap_or_default();
-			let end = if end < 0 {
-				(self.data.len() as isize + end).max(0) as usize
-			} else {
-				end as usize
-			};
-			let data = self.data.slice(start..end);
-			let content_type = content_type.into_inner().map(normalize_type).unwrap_or_default();
-			Blob {
-				mime: content_type,
-				data,
-			}
-		}
-
-		pub async fn text(&self, _rest: Rest<()>) -> Result<String> {
-			let text = String::from_utf8(self.data.to_vec())?;
-			Ok(text)
-		}
-
-		pub async fn arrayBuffer<'js>(
-			&self,
-			ctx: Ctx<'js>,
-			_rest: Rest<()>,
-		) -> Result<ArrayBuffer<'js>> {
-			ArrayBuffer::new(ctx, self.data.to_vec())
-		}
-
-		// ------------------------------
-		// Instance methods
-		// ------------------------------
-
-		// Convert the object to a string
-		pub fn toString(&self, _rest: Rest<()>) -> String {
-			String::from("[object Blob]")
-		}
+	// Convert the object to a string
+	#[qjs(rename = "toString")]
+	pub fn js_to_string(&self) -> String {
+		String::from("[object Blob]")
 	}
 }
 
@@ -242,17 +221,17 @@ mod test {
 
 				blob = new Blob(["\n\r\n \n\r"],{endings: "transparent"});
 				assert.eq(blob.size,6)
-				assert.eq(await blob.text(),"\n\r\n \n\r");
+					assert.eq(await blob.text(),"\n\r\n \n\r");
 				blob = new Blob(["\n\r\n \n\r"],{endings: "native"});
 				// \n \r\n and the \n from \n\r are converted.
 				// the part of the string which isn't converted is the space and the \r
 				assert.eq(await blob.text(),`${NATIVE_LINE_ENDING}${NATIVE_LINE_ENDING} ${NATIVE_LINE_ENDING}\r`);
 				assert.eq(blob.size,NATIVE_LINE_ENDING.length*3 + 2)
 
-				assert.mustThrow(() => new Blob("text"));
+					assert.mustThrow(() => new Blob("text"));
 				assert.mustThrow(() => new Blob(["text"], {endings: "invalid value"}));
 			})()
-			"#).catch(ctx).unwrap().await.catch(ctx).unwrap();
+			"#).catch(&ctx).unwrap().await.catch(&ctx).unwrap();
 		})
 		.await
 	}
diff --git a/lib/src/fnc/script/fetch/classes/form_data.rs b/lib/src/fnc/script/fetch/classes/form_data.rs
index f77cd8f7..a963bc98 100644
--- a/lib/src/fnc/script/fetch/classes/form_data.rs
+++ b/lib/src/fnc/script/fetch/classes/form_data.rs
@@ -1,34 +1,36 @@
 //! FormData class implementation
 
 use js::{
-	bind, function::Opt, prelude::Coerced, Class, Ctx, Exception, FromJs, Persistent, Result,
-	String, Value,
+	class::{Class, Trace},
+	function::{Opt, Rest},
+	prelude::Coerced,
+	Ctx, Exception, FromJs, Result, String, Value,
 };
-use std::string::String as StdString;
+use reqwest::multipart::{Form, Part};
+use std::{collections::HashMap, string::String as StdString};
 
-use crate::fnc::script::fetch::classes::BlobClass;
+use crate::fnc::script::fetch::classes::Blob;
 
 #[derive(Clone)]
-pub enum FormDataValue {
-	String(Persistent<String<'static>>),
+pub enum FormDataValue<'js> {
+	String(String<'js>),
 	Blob {
-		data: Persistent<Class<'static, BlobClass>>,
-		filename: Option<Persistent<String<'static>>>,
+		data: Class<'js, Blob>,
+		filename: Option<String<'js>>,
 	},
 }
 
-impl FormDataValue {
-	fn from_arguments<'js>(
-		ctx: Ctx<'js>,
+impl<'js> FormDataValue<'js> {
+	fn from_arguments(
+		ctx: &Ctx<'js>,
 		value: Value<'js>,
 		filename: Opt<Coerced<String<'js>>>,
 		error: &'static str,
-	) -> Result<FormDataValue> {
+	) -> Result<Self> {
 		if let Some(blob) =
-			value.as_object().and_then(|value| Class::<BlobClass>::from_object(value.clone()).ok())
+			value.as_object().and_then(|value| Class::<Blob>::from_object(value.clone()))
 		{
-			let blob = Persistent::save(ctx, blob);
-			let filename = filename.into_inner().map(|x| Persistent::save(ctx, x.0));
+			let filename = filename.into_inner().map(|x| x.0);
 
 			Ok(FormDataValue::Blob {
 				data: blob,
@@ -38,128 +40,109 @@ impl FormDataValue {
 			return Err(Exception::throw_type(ctx, error));
 		} else {
 			let value = Coerced::<String>::from_js(ctx, value)?;
-			let value = Persistent::save(ctx, value.0);
-			Ok(FormDataValue::String(value))
+			Ok(FormDataValue::String(value.0))
 		}
 	}
 }
 
-pub use form_data::FormData as FormDataClass;
-#[bind(object, public)]
-#[quickjs(bare)]
-#[allow(non_snake_case)]
-#[allow(unused_variables)]
-#[allow(clippy::module_inception)]
-pub mod form_data {
-	use super::*;
-	use std::{cell::RefCell, collections::HashMap};
+#[js::class]
+#[derive(Clone, Trace)]
+pub struct FormData<'js> {
+	#[qjs(skip_trace)]
+	pub(crate) values: HashMap<StdString, Vec<FormDataValue<'js>>>,
+}
 
-	use js::{
-		function::Opt,
-		prelude::{Coerced, Rest},
-		Ctx, Result, String, Value,
-	};
-	use reqwest::multipart::{Form, Part};
+#[js::methods]
+impl<'js> FormData<'js> {
+	// ------------------------------
+	// Constructor
+	// ------------------------------
 
-	#[derive(Clone)]
-	#[quickjs(cloneable)]
-	pub struct FormData {
-		pub(crate) values: RefCell<HashMap<StdString, Vec<FormDataValue>>>,
+	// FormData spec states that FormDa takes two html elements as arguments
+	// which does not make sense implementing fetch outside a browser.
+	// So we ignore those arguments.
+	#[qjs(constructor)]
+	pub fn new(ctx: Ctx<'js>, args: Rest<()>) -> Result<Self> {
+		if args.len() > 0 {
+			return Err(Exception::throw_internal(
+				&ctx,
+				"Cant call FormData with arguments as the dom elements required are not available",
+			));
+		}
+		Ok(FormData {
+			values: HashMap::new(),
+		})
 	}
 
-	impl FormData {
-		// ------------------------------
-		// Constructor
-		// ------------------------------
+	pub fn append(
+		&mut self,
+		ctx: Ctx<'js>,
+		name: Coerced<StdString>,
+		value: Value<'js>,
+		filename: Opt<Coerced<String<'js>>>,
+	) -> Result<()> {
+		let value = FormDataValue::from_arguments(
+			&ctx,
+			value,
+			filename,
+			"Can't call `append` on `FormData` with a filename when value isn't of type `Blob`",
+		)?;
 
-		// FormData spec states that FormDa takes two html elements as arguments
-		// which does not make sense implementing fetch outside a browser.
-		// So we ignore those arguments.
-		#[quickjs(constructor)]
-		pub fn new(ctx: Ctx<'_>, args: Rest<()>) -> Result<FormData> {
-			if args.len() > 0 {
-				return Err(Exception::throw_internal(ctx,"Cant call FormData with arguments as the dom elements required are not available"));
-			}
-			Ok(FormData {
-				values: RefCell::new(HashMap::new()),
-			})
-		}
+		self.values.entry(name.0).or_insert_with(Vec::new).push(value);
 
-		pub fn append<'js>(
-			&self,
-			ctx: Ctx<'js>,
-			name: Coerced<StdString>,
-			value: Value<'js>,
-			filename: Opt<Coerced<String<'js>>>,
-		) -> Result<()> {
-			let value = FormDataValue::from_arguments(
-				ctx,
-				value,
-				filename,
-				"Can't call `append` on `FormData` with a filename when value isn't of type `Blob`",
-			)?;
+		Ok(())
+	}
 
-			self.values.borrow_mut().entry(name.0).or_insert_with(Vec::new).push(value);
+	pub fn set(
+		&mut self,
+		ctx: Ctx<'js>,
+		name: Coerced<StdString>,
+		value: Value<'js>,
+		filename: Opt<Coerced<String<'js>>>,
+	) -> Result<()> {
+		let value = FormDataValue::from_arguments(
+			&ctx,
+			value,
+			filename,
+			"Can't call `set` on `FormData` with a filename when value isn't of type `Blob`",
+		)?;
 
-			Ok(())
-		}
+		self.values.insert(name.0, vec![value]);
 
-		pub fn set<'js>(
-			&self,
-			ctx: Ctx<'js>,
-			name: Coerced<StdString>,
-			value: Value<'js>,
-			filename: Opt<Coerced<String<'js>>>,
-		) -> Result<()> {
-			let value = FormDataValue::from_arguments(
-				ctx,
-				value,
-				filename,
-				"Can't call `set` on `FormData` with a filename when value isn't of type `Blob`",
-			)?;
+		Ok(())
+	}
 
-			self.values.borrow_mut().insert(name.0, vec![value]);
+	pub fn has(&self, name: Coerced<StdString>) -> bool {
+		self.values.contains_key(&name.0)
+	}
 
-			Ok(())
-		}
+	pub fn delete(&mut self, name: Coerced<StdString>) {
+		self.values.remove(&name.0);
+	}
 
-		pub fn has(&self, ctx: Ctx<'_>, name: Coerced<StdString>) -> bool {
-			self.values.borrow().contains_key(&name.0)
-		}
-
-		pub fn delete(&self, ctx: Ctx<'_>, name: Coerced<StdString>) {
-			self.values.borrow_mut().remove(&name.0);
-		}
-
-		#[quickjs(skip)]
-		pub fn to_form(&self, ctx: Ctx<'_>) -> Result<Form> {
-			let lock = self.values.borrow();
-			let mut res = Form::new();
-			for (k, v) in lock.iter() {
-				for v in v {
-					match v {
-						FormDataValue::String(x) => {
-							let x = x.clone().restore(ctx).unwrap();
-							res = res.text(k.clone(), x.to_string()?);
-						}
-						FormDataValue::Blob {
-							data,
-							filename,
-						} => {
-							let mut part = Part::bytes(
-								data.clone().restore(ctx).unwrap().borrow().data.to_vec(),
-							);
-							if let Some(filename) = filename {
-								let filename =
-									filename.clone().restore(ctx).unwrap().to_string()?;
-								part = part.file_name(filename);
-							}
-							res = res.part(k.clone(), part);
+	#[qjs(skip)]
+	pub fn to_form(&self) -> Result<Form> {
+		let mut res = Form::new();
+		for (k, v) in self.values.iter() {
+			for v in v {
+				match v {
+					FormDataValue::String(x) => {
+						res = res.text(k.clone(), x.to_string()?);
+					}
+					FormDataValue::Blob {
+						data,
+						filename,
+					} => {
+						let mut part = Part::bytes(data.borrow().data.to_vec());
+						if let Some(filename) = filename {
+							let filename = filename.to_string()?;
+							part = part.file_name(filename);
 						}
+						res = res.part(k.clone(), part);
 					}
 				}
 			}
-			Ok(res)
 		}
+		Ok(res)
 	}
 }
diff --git a/lib/src/fnc/script/fetch/classes/headers.rs b/lib/src/fnc/script/fetch/classes/headers.rs
index 01e06053..ecb015d0 100644
--- a/lib/src/fnc/script/fetch/classes/headers.rs
+++ b/lib/src/fnc/script/fetch/classes/headers.rs
@@ -1,272 +1,258 @@
 //! Headers class implementation
 
-use js::bind;
+use std::str::FromStr;
 
-pub use headers::Headers as HeadersClass;
+use js::{
+	class::Trace,
+	prelude::{Coerced, List},
+	Array, Ctx, Exception, Result, Value,
+};
+use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
 
-#[bind(object, public)]
-#[quickjs(bare)]
-#[allow(non_snake_case)]
-#[allow(unused_variables)]
-#[allow(clippy::module_inception)]
-mod headers {
-	use std::{cell::RefCell, str::FromStr};
+#[derive(Clone, Trace)]
+#[js::class]
+pub struct Headers {
+	#[qjs(skip_trace)]
+	pub(crate) inner: HeaderMap,
+}
 
-	use js::{function::Rest, prelude::Coerced, Array, Ctx, Exception, Result, Value};
-	use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
+#[js::methods]
+impl Headers {
+	// ------------------------------
+	// Constructor
+	// ------------------------------
 
-	#[derive(Clone)]
-	#[quickjs(cloneable)]
-	#[allow(dead_code)]
-	pub struct Headers {
-		pub(crate) inner: RefCell<HeaderMap>,
+	#[qjs(constructor)]
+	pub fn new<'js>(ctx: Ctx<'js>, init: Value<'js>) -> Result<Self> {
+		Headers::new_inner(&ctx, init)
 	}
 
-	impl Headers {
-		// ------------------------------
-		// Constructor
-		// ------------------------------
+	// ------------------------------
+	// Instance methods
+	// ------------------------------
 
-		#[quickjs(constructor)]
-		pub fn new<'js>(ctx: Ctx<'js>, init: Value<'js>, args: Rest<()>) -> Result<Self> {
-			Headers::new_inner(ctx, init)
-		}
-
-		// ------------------------------
-		// Instance methods
-		// ------------------------------
-
-		// Convert the object to a string
-		pub fn toString(&self, args: Rest<()>) -> String {
-			String::from("[object Header]")
-		}
-
-		// Adds or appends a new value to a header
-		pub fn append(&self, ctx: Ctx<'_>, key: String, val: String, args: Rest<()>) -> Result<()> {
-			self.append_inner(ctx, &key, &val)
-		}
-
-		// Deletes a header from the header set
-		pub fn delete(&self, ctx: Ctx<'_>, key: String, args: Rest<()>) -> Result<()> {
-			// Process and check the header name is valid
-			let key = HeaderName::from_str(&key)
-				.map_err(|e| Exception::throw_type(ctx, &format!("{e}")))?;
-			// Remove the header entry from the map
-			self.inner.borrow_mut().remove(&key);
-			// Everything ok
-			Ok(())
-		}
-
-		// Returns all header entries in the header set
-		pub fn entries(&self, args: Rest<()>) -> Vec<(String, String)> {
-			let lock = self.inner.borrow();
-			let mut res = Vec::<(String, String)>::with_capacity(lock.len());
-
-			for (k, v) in lock.iter() {
-				let k = k.as_str();
-				if Some(k) == res.last().map(|x| x.0.as_str()) {
-					let ent = res.last_mut().unwrap();
-					ent.1.push_str(", ");
-					// Header value came from a string, so it should also be able to be cast back
-					// to a string
-					ent.1.push_str(v.to_str().unwrap());
-				} else {
-					res.push((k.to_owned(), v.to_str().unwrap().to_owned()));
-				}
-			}
-
-			res
-		}
-
-		// Returns all values of a header in the header set
-		pub fn get(&self, ctx: Ctx<'_>, key: String, args: Rest<()>) -> Result<Option<String>> {
-			// Process and check the header name is valid
-			let key = HeaderName::from_str(&key)
-				.map_err(|e| Exception::throw_type(ctx, &format!("{e}")))?;
-			// Convert the header values to strings
-			let lock = self.inner.borrow();
-			let all = lock.get_all(&key);
-
-			// Header value came from a string, so it should also be able to be cast back
-			// to a string
-			let mut res = String::new();
-			for (idx, v) in all.iter().enumerate() {
-				if idx != 0 {
-					res.push_str(", ");
-				}
-				res.push_str(v.to_str().unwrap());
-			}
-
-			if res.is_empty() {
-				return Ok(None);
-			}
-			Ok(Some(res))
-		}
-
-		// Returns all values for the `Set-Cookie` header.
-		#[quickjs(rename = "getSetCookie")]
-		pub fn get_set_cookie(&self, args: Rest<()>) -> Vec<String> {
-			// This should always be a correct cookie;
-			let key = HeaderName::from_str("set-cookie").unwrap();
-			self.inner
-				.borrow()
-				.get_all(key)
-				.iter()
-				.map(|x| x.to_str().unwrap().to_owned())
-				.collect()
-		}
-
-		// Checks to see if the header set contains a header
-		pub fn has(&self, ctx: Ctx<'_>, key: String, args: Rest<()>) -> Result<bool> {
-			// Process and check the header name is valid
-			let key = HeaderName::from_str(&key)
-				.map_err(|e| Exception::throw_type(ctx, &format!("{e}")))?;
-			// Check if the header entry exists
-			Ok(self.inner.borrow().contains_key(&key))
-		}
-
-		// Returns all header keys contained in the header set
-		pub fn keys(&self, args: Rest<()>) -> Vec<String> {
-			// TODO: Incorrect, should return an iterator but iterators are not supported yet by quickjs
-			self.inner.borrow().keys().map(|v| v.as_str().to_owned()).collect::<Vec<String>>()
-		}
-
-		// Sets a new value or adds a header to the header set
-		pub fn set(&self, ctx: Ctx<'_>, key: String, val: String, args: Rest<()>) -> Result<()> {
-			// Process and check the header name is valid
-			let key = HeaderName::from_str(&key)
-				.map_err(|e| Exception::throw_type(ctx, &format!("Invalid header name: {e}")))?;
-			// Process and check the header name is valid
-			let val = HeaderValue::from_str(&val)
-				.map_err(|e| Exception::throw_type(ctx, &format!("Invalid header value: {e}")))?;
-			// Insert and overwrite the header entry
-			self.inner.borrow_mut().insert(key, val);
-			// Everything ok
-			Ok(())
-		}
-
-		// Returns all header values contained in the header set
-		pub fn values(&self, args: Rest<()>) -> Vec<String> {
-			let lock = self.inner.borrow();
-			let mut res = Vec::<String>::with_capacity(lock.len());
-
-			let mut pref = None;
-			for (k, v) in lock.iter() {
-				if Some(k) == pref {
-					let ent = res.last_mut().unwrap();
-					ent.push_str(", ");
-					ent.push_str(v.to_str().unwrap())
-				} else {
-					pref = Some(k);
-					res.push(v.to_str().unwrap().to_owned());
-				}
-			}
-
-			res
-		}
+	// Convert the object to a string
+	#[qjs(rename = "toString")]
+	pub fn js_to_string(&self) -> String {
+		String::from("[object Header]")
 	}
 
-	#[quickjs(skip)]
-	impl Headers {
-		pub fn from_map(map: HeaderMap) -> Self {
-			Self {
-				inner: RefCell::new(map),
-			}
-		}
+	// Adds or appends a new value to a header
+	pub fn append(&mut self, ctx: Ctx<'_>, key: String, val: String) -> Result<()> {
+		self.append_inner(&ctx, &key, &val)
+	}
 
-		pub fn new_empty() -> Self {
-			Self::from_map(HeaderMap::new())
-		}
+	// Deletes a header from the header set
+	pub fn delete(&mut self, ctx: Ctx<'_>, key: String) -> Result<()> {
+		// Process and check the header name is valid
+		let key =
+			HeaderName::from_str(&key).map_err(|e| Exception::throw_type(&ctx, &format!("{e}")))?;
+		// Remove the header entry from the map
+		self.inner.remove(&key);
+		// Everything ok
+		Ok(())
+	}
 
-		pub fn new_inner<'js>(ctx: Ctx<'js>, val: Value<'js>) -> Result<Self> {
-			static INVALID_ERROR: &str = "Headers constructor: init was neither sequence<sequence<ByteString>> or record<ByteString, ByteString>";
-			let res = Self::new_empty();
+	// Returns all header entries in the header set
+	pub fn entries(&self) -> Vec<List<(String, String)>> {
+		let mut res = Vec::<List<(String, String)>>::with_capacity(self.inner.len());
 
-			// TODO Set and Map,
-			if let Some(array) = val.as_array() {
-				// a sequence<sequence<String>>;
-				for v in array.iter::<Array>() {
-					let v = match v {
-						Ok(x) => x,
-						Err(e) => {
-							if e.is_from_js() {
-								return Err(Exception::throw_type(ctx, INVALID_ERROR));
-							}
-							return Err(e);
-						}
-					};
-					let key = match v.get::<Coerced<String>>(0) {
-						Ok(x) => x,
-						Err(e) => {
-							if e.is_from_js() {
-								return Err(Exception::throw_type(ctx, INVALID_ERROR));
-							}
-							return Err(e);
-						}
-					};
-					let value = match v.get::<Coerced<String>>(1) {
-						Ok(x) => x,
-						Err(e) => {
-							if e.is_from_js() {
-								return Err(Exception::throw_type(ctx, INVALID_ERROR));
-							}
-							return Err(e);
-						}
-					};
-					res.append_inner(ctx, &key, &value)?;
-				}
-			} else if let Some(obj) = val.as_object() {
-				// a record<String,String>;
-				for prop in obj.props::<String, Coerced<String>>() {
-					let (key, value) = match prop {
-						Ok(x) => x,
-						Err(e) => {
-							if e.is_from_js() {
-								return Err(Exception::throw_type(ctx, INVALID_ERROR));
-							}
-							return Err(e);
-						}
-					};
-					res.append_inner(ctx, &key, &value.0)?;
-				}
+		for (k, v) in self.inner.iter() {
+			let k = k.as_str();
+			if Some(k) == res.last().map(|x| x.0 .0.as_str()) {
+				let ent = res.last_mut().unwrap();
+				ent.0 .1.push_str(", ");
+				// Header value came from a string, so it should also be able to be cast back
+				// to a string
+				ent.0 .1.push_str(v.to_str().unwrap());
 			} else {
-				return Err(Exception::throw_type(ctx, INVALID_ERROR));
+				res.push(List((k.to_owned(), v.to_str().unwrap().to_owned())));
 			}
-
-			Ok(res)
 		}
 
-		fn append_inner(&self, ctx: Ctx<'_>, key: &str, val: &str) -> Result<()> {
-			// Unsure what to do exactly here.
-			// Spec dictates normalizing string before adding it as a header value, i.e. removing
-			// any leading and trailing whitespace:
-			// [`https://fetch.spec.whatwg.org/#concept-header-value-normalize`]
-			// But non of the platforms I tested, normalize, instead they throw an error
-			// with `Invalid header value`. I'll chose to just do what the platforms do.
+		res
+	}
 
-			let key = match HeaderName::from_bytes(key.as_bytes()) {
-				Ok(x) => x,
-				Err(e) => {
-					return Err(Exception::throw_type(
-						ctx,
-						&format!("invalid header name `{key}`: {e}"),
-					))
-				}
-			};
-			let val = match HeaderValue::from_bytes(val.as_bytes()) {
-				Ok(x) => x,
-				Err(e) => {
-					return Err(Exception::throw_type(
-						ctx,
-						&format!("invalid header value `{val}`: {e}"),
-					))
-				}
-			};
+	// Returns all values of a header in the header set
+	pub fn get(&self, ctx: Ctx<'_>, key: String) -> Result<Option<String>> {
+		// Process and check the header name is valid
+		let key =
+			HeaderName::from_str(&key).map_err(|e| Exception::throw_type(&ctx, &format!("{e}")))?;
+		// Convert the header values to strings
+		let all = self.inner.get_all(&key);
 
-			self.inner.borrow_mut().append(key, val);
-
-			Ok(())
+		// Header value came from a string, so it should also be able to be cast back
+		// to a string
+		let mut res = String::new();
+		for (idx, v) in all.iter().enumerate() {
+			if idx != 0 {
+				res.push_str(", ");
+			}
+			res.push_str(v.to_str().unwrap());
 		}
+
+		if res.is_empty() {
+			return Ok(None);
+		}
+		Ok(Some(res))
+	}
+
+	// Returns all values for the `Set-Cookie` header.
+	#[qjs(rename = "getSetCookie")]
+	pub fn get_set_cookie(&self) -> Vec<String> {
+		// This should always be a correct cookie;
+		let key = HeaderName::from_str("set-cookie").unwrap();
+		self.inner.get_all(key).iter().map(|x| x.to_str().unwrap().to_owned()).collect()
+	}
+
+	// Checks to see if the header set contains a header
+	pub fn has(&self, ctx: Ctx<'_>, key: String) -> Result<bool> {
+		// Process and check the header name is valid
+		let key =
+			HeaderName::from_str(&key).map_err(|e| Exception::throw_type(&ctx, &format!("{e}")))?;
+		// Check if the header entry exists
+		Ok(self.inner.contains_key(&key))
+	}
+
+	// Returns all header keys contained in the header set
+	pub fn keys(&self) -> Vec<String> {
+		// TODO: Incorrect, should return an iterator but iterators are not supported yet by quickjs
+		self.inner.keys().map(|v| v.as_str().to_owned()).collect::<Vec<String>>()
+	}
+
+	// Sets a new value or adds a header to the header set
+	pub fn set(&mut self, ctx: Ctx<'_>, key: String, val: String) -> Result<()> {
+		// Process and check the header name is valid
+		let key = HeaderName::from_str(&key)
+			.map_err(|e| Exception::throw_type(&ctx, &format!("Invalid header name: {e}")))?;
+		// Process and check the header name is valid
+		let val = HeaderValue::from_str(&val)
+			.map_err(|e| Exception::throw_type(&ctx, &format!("Invalid header value: {e}")))?;
+		// Insert and overwrite the header entry
+		self.inner.insert(key, val);
+		// Everything ok
+		Ok(())
+	}
+
+	// Returns all header values contained in the header set
+	pub fn values(&self) -> Vec<String> {
+		let mut res = Vec::<String>::with_capacity(self.inner.len());
+
+		let mut pref = None;
+		for (k, v) in self.inner.iter() {
+			if Some(k) == pref {
+				let ent = res.last_mut().unwrap();
+				ent.push_str(", ");
+				ent.push_str(v.to_str().unwrap())
+			} else {
+				pref = Some(k);
+				res.push(v.to_str().unwrap().to_owned());
+			}
+		}
+
+		res
+	}
+}
+
+impl Headers {
+	pub fn from_map(map: HeaderMap) -> Self {
+		Self {
+			inner: map,
+		}
+	}
+
+	pub fn new_empty() -> Self {
+		Self::from_map(HeaderMap::new())
+	}
+
+	pub fn new_inner<'js>(ctx: &Ctx<'js>, val: Value<'js>) -> Result<Self> {
+		static INVALID_ERROR: &str = "Headers constructor: init was neither sequence<sequence<ByteString>> or record<ByteString, ByteString>";
+		let mut res = Self::new_empty();
+
+		// TODO Set and Map,
+		if let Some(array) = val.as_array() {
+			// a sequence<sequence<String>>;
+			for v in array.iter::<Array>() {
+				let v = match v {
+					Ok(x) => x,
+					Err(e) => {
+						if e.is_from_js() {
+							return Err(Exception::throw_type(ctx, INVALID_ERROR));
+						}
+						return Err(e);
+					}
+				};
+				let key = match v.get::<Coerced<String>>(0) {
+					Ok(x) => x,
+					Err(e) => {
+						if e.is_from_js() {
+							return Err(Exception::throw_type(ctx, INVALID_ERROR));
+						}
+						return Err(e);
+					}
+				};
+				let value = match v.get::<Coerced<String>>(1) {
+					Ok(x) => x,
+					Err(e) => {
+						if e.is_from_js() {
+							return Err(Exception::throw_type(ctx, INVALID_ERROR));
+						}
+						return Err(e);
+					}
+				};
+				res.append_inner(ctx, &key, &value)?;
+			}
+		} else if let Some(obj) = val.as_object() {
+			// a record<String,String>;
+			for prop in obj.props::<String, Coerced<String>>() {
+				let (key, value) = match prop {
+					Ok(x) => x,
+					Err(e) => {
+						if e.is_from_js() {
+							return Err(Exception::throw_type(ctx, INVALID_ERROR));
+						}
+						return Err(e);
+					}
+				};
+				res.append_inner(ctx, &key, &value.0)?;
+			}
+		} else {
+			return Err(Exception::throw_type(ctx, INVALID_ERROR));
+		}
+
+		Ok(res)
+	}
+
+	fn append_inner(&mut self, ctx: &Ctx<'_>, key: &str, val: &str) -> Result<()> {
+		// Unsure what to do exactly here.
+		// Spec dictates normalizing string before adding it as a header value, i.e. removing
+		// any leading and trailing whitespace:
+		// [`https://fetch.spec.whatwg.org/#concept-header-value-normalize`]
+		// But non of the platforms I tested, normalize, instead they throw an error
+		// with `Invalid header value`. I'll chose to just do what the platforms do.
+
+		let key = match HeaderName::from_bytes(key.as_bytes()) {
+			Ok(x) => x,
+			Err(e) => {
+				return Err(Exception::throw_type(
+					ctx,
+					&format!("invalid header name `{key}`: {e}"),
+				))
+			}
+		};
+		let val = match HeaderValue::from_bytes(val.as_bytes()) {
+			Ok(x) => x,
+			Err(e) => {
+				return Err(Exception::throw_type(
+					ctx,
+					&format!("invalid header value `{val}`: {e}"),
+				))
+			}
+		};
+
+		self.inner.append(key, val);
+
+		Ok(())
 	}
 }
 
@@ -327,7 +313,7 @@ mod test {
 				});
 				assert.seq(headers.get("f"), "g");
 				assert.seq(headers.get("h"), "j");
-			"#).catch(ctx).unwrap();
+			"#).catch(&ctx).unwrap();
 		})
 		.await
 	}
diff --git a/lib/src/fnc/script/fetch/classes/request.rs b/lib/src/fnc/script/fetch/classes/request.rs
index f57c982e..aae64746 100644
--- a/lib/src/fnc/script/fetch/classes/request.rs
+++ b/lib/src/fnc/script/fetch/classes/request.rs
@@ -1,18 +1,9 @@
 //! Request class implementation
-//!
-use js::{
-	bind,
-	class::{HasRefs, RefsMarker},
-	prelude::Coerced,
-	Class, Ctx, Exception, FromJs, Object, Persistent, Result, Value,
-};
+
+use js::{class::Trace, prelude::Coerced, Class, Ctx, Exception, FromJs, Object, Result, Value};
 use reqwest::Method;
 
-use crate::fnc::script::fetch::{
-	body::Body,
-	classes::{BlobClass, HeadersClass},
-	RequestError,
-};
+use crate::fnc::script::fetch::{body::Body, RequestError};
 
 #[derive(Clone, Copy, Eq, PartialEq)]
 pub enum RequestMode {
@@ -23,7 +14,7 @@ pub enum RequestMode {
 }
 
 impl<'js> FromJs<'js> for RequestMode {
-	fn from_js(ctx: Ctx<'js>, value: Value<'js>) -> Result<Self> {
+	fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> {
 		let res = if let Some(Coerced(x)) = <Option<Coerced<String>>>::from_js(ctx, value)? {
 			match x.as_str() {
 				"navigate" => RequestMode::Navigate,
@@ -59,7 +50,7 @@ pub enum RequestCredentials {
 }
 
 impl<'js> FromJs<'js> for RequestCredentials {
-	fn from_js(ctx: Ctx<'js>, value: Value<'js>) -> Result<Self> {
+	fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> {
 		let res = if let Some(Coerced(x)) = <Option<Coerced<String>>>::from_js(ctx, value)? {
 			match x.as_str() {
 				"omit" => RequestCredentials::Omit,
@@ -96,7 +87,7 @@ pub enum RequestCache {
 }
 
 impl<'js> FromJs<'js> for RequestCache {
-	fn from_js(ctx: Ctx<'js>, value: Value<'js>) -> Result<Self> {
+	fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> {
 		let res = if let Some(Coerced(x)) = <Option<Coerced<String>>>::from_js(ctx, value)? {
 			match x.as_str() {
 				"default" => RequestCache::Default,
@@ -136,7 +127,7 @@ pub enum RequestRedirect {
 }
 
 impl<'js> FromJs<'js> for RequestRedirect {
-	fn from_js(ctx: Ctx<'js>, value: Value<'js>) -> Result<Self> {
+	fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> {
 		let res = if let Some(Coerced(x)) = <Option<Coerced<String>>>::from_js(ctx, value)? {
 			match x.as_str() {
 				"follow" => RequestRedirect::Follow,
@@ -176,7 +167,7 @@ pub enum ReferrerPolicy {
 }
 
 impl<'js> FromJs<'js> for ReferrerPolicy {
-	fn from_js(ctx: Ctx<'js>, value: Value<'js>) -> Result<Self> {
+	fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> {
 		let res = if let Some(Coerced(x)) = <Option<Coerced<String>>>::from_js(ctx, value)? {
 			match x.as_str() {
 				"" => ReferrerPolicy::Empty,
@@ -213,9 +204,9 @@ impl<'js> FromJs<'js> for ReferrerPolicy {
 	}
 }
 
-pub struct RequestInit {
+pub struct RequestInit<'js> {
 	pub method: Method,
-	pub headers: Persistent<Class<'static, HeadersClass>>,
+	pub headers: Class<'js, Headers>,
 	pub body: Option<Body>,
 	pub referrer: String,
 	pub referrer_policy: ReferrerPolicy,
@@ -227,15 +218,15 @@ pub struct RequestInit {
 	pub keep_alive: bool,
 }
 
-impl HasRefs for RequestInit {
-	fn mark_refs(&self, marker: &RefsMarker) {
-		self.headers.mark_refs(marker);
+impl<'js> Trace<'js> for RequestInit<'js> {
+	fn trace<'a>(&self, tracer: js::class::Tracer<'a, 'js>) {
+		self.headers.trace(tracer);
 	}
 }
 
-impl RequestInit {
-	pub fn default(ctx: Ctx<'_>) -> Result<Self> {
-		let headers = Persistent::save(ctx, Class::instance(ctx, HeadersClass::new_empty())?);
+impl<'js> RequestInit<'js> {
+	pub fn default(ctx: Ctx<'js>) -> Result<Self> {
+		let headers = Class::instance(ctx, Headers::new_empty())?;
 		Ok(RequestInit {
 			method: Method::GET,
 			headers,
@@ -251,9 +242,9 @@ impl RequestInit {
 		})
 	}
 
-	pub fn clone_js(&self, ctx: Ctx<'_>) -> Result<Self> {
-		let headers = self.headers.clone().restore(ctx).unwrap();
-		let headers = Persistent::save(ctx, Class::instance(ctx, headers.borrow().clone())?);
+	pub fn clone_js(&self, ctx: Ctx<'js>) -> Result<Self> {
+		let headers = self.headers.clone();
+		let headers = Class::instance(ctx.clone(), headers.borrow().clone())?;
 
 		let body = self.body.as_ref().map(|x| x.clone_js(ctx));
 
@@ -274,7 +265,7 @@ impl RequestInit {
 }
 
 // Normalize method string according to spec.
-fn normalize_method(ctx: Ctx<'_>, m: String) -> Result<Method> {
+fn normalize_method(ctx: &Ctx<'_>, m: String) -> Result<Method> {
 	if m.as_bytes().eq_ignore_ascii_case(b"CONNECT")
 		|| m.as_bytes().eq_ignore_ascii_case(b"TRACE")
 		|| m.as_bytes().eq_ignore_ascii_case(b"TRACK")
@@ -309,8 +300,8 @@ fn normalize_method(ctx: Ctx<'_>, m: String) -> Result<Method> {
 	}
 }
 
-impl<'js> FromJs<'js> for RequestInit {
-	fn from_js(ctx: Ctx<'js>, value: Value<'js>) -> Result<Self> {
+impl<'js> FromJs<'js> for RequestInit<'js> {
+	fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> {
 		let object = Object::from_js(ctx, value)?;
 
 		let referrer = object
@@ -346,15 +337,14 @@ impl<'js> FromJs<'js> for RequestInit {
 		}
 
 		let headers = if let Some(hdrs) = object.get::<_, Option<Object>>("headers")? {
-			if let Ok(cls) = Class::<HeadersClass>::from_object(hdrs.clone()) {
+			if let Some(cls) = Class::<Headers>::from_object(hdrs.clone()) {
 				cls
 			} else {
-				Class::instance(ctx, HeadersClass::new_inner(ctx, hdrs.into_value())?)?
+				Class::instance(ctx.clone(), Headers::new_inner(ctx, hdrs.into_value())?)?
 			}
 		} else {
-			Class::instance(ctx, HeadersClass::new_empty())?
+			Class::instance(ctx.clone(), Headers::new_empty())?
 		};
-		let headers = Persistent::save(ctx, headers);
 
 		let body = object.get::<_, Option<Body>>("body")?;
 
@@ -376,192 +366,173 @@ impl<'js> FromJs<'js> for RequestInit {
 
 pub use request::Request as RequestClass;
 
-#[bind(object, public)]
-#[quickjs(bare)]
-#[allow(non_snake_case)]
-#[allow(unused_variables)]
-#[allow(clippy::module_inception)]
-mod request {
+pub use super::*;
 
-	pub use super::*;
+use bytes::Bytes;
+use js::function::Opt;
+// TODO: change implementation based on features.
+use reqwest::{header::HeaderName, Url};
 
-	use bytes::Bytes;
-	use js::{
-		function::{Opt, Rest},
-		Class, Ctx, Exception, HasRefs, Result, Value,
-	};
-	// TODO: change implementation based on features.
-	use reqwest::{header::HeaderName, Url};
+#[allow(dead_code)]
+#[js::class]
+#[derive(Trace)]
+pub struct Request<'js> {
+	#[qjs(skip_trace)]
+	pub(crate) url: Url,
+	pub(crate) init: RequestInit<'js>,
+}
 
-	#[allow(dead_code)]
-	#[derive(HasRefs)]
-	#[quickjs(has_refs)]
-	pub struct Request {
-		pub(crate) url: Url,
-		#[quickjs(has_refs)]
-		pub(crate) init: RequestInit,
+#[js::methods]
+impl<'js> Request<'js> {
+	// ------------------------------
+	// Constructor
+	// ------------------------------
+
+	#[qjs(constructor)]
+	pub fn new(ctx: Ctx<'js>, input: Value<'js>, init: Opt<RequestInit<'js>>) -> Result<Self> {
+		if let Some(url) = input.as_string() {
+			// url string
+			let url_str = url.to_string()?;
+			let url = Url::parse(&url_str)
+				.map_err(|e| Exception::throw_type(&ctx, &format!("failed to parse url: {e}")))?;
+			if !url.username().is_empty() || !url.password().map(str::is_empty).unwrap_or(true) {
+				// url cannot contain non empty username and passwords
+				return Err(Exception::throw_type(&ctx, "Url contained credentials."));
+			}
+			let init = init.into_inner().map_or_else(|| RequestInit::default(ctx.clone()), Ok)?;
+			// HEAD and GET methods can't have a body
+			if init.body.is_some() && init.method == Method::GET || init.method == Method::HEAD {
+				return Err(Exception::throw_type(
+					&ctx,
+					&format!("Request with method `{}` cannot have a body", init.method),
+				));
+			}
+
+			Ok(Self {
+				url,
+				init,
+			})
+		} else if let Some(request) = input.into_object().and_then(Class::<Self>::from_object) {
+			// existing request object, just return it
+			request.try_borrow()?.clone_js(ctx.clone())
+		} else {
+			Err(Exception::throw_type(
+				&ctx,
+				"request `init` paramater must either be a request object or a string",
+			))
+		}
 	}
 
-	impl Request {
-		// ------------------------------
-		// Constructor
-		// ------------------------------
+	/// Clone the response, teeing any possible underlying streams.
+	#[qjs(rename = "clone")]
+	pub fn clone_js(&self, ctx: Ctx<'js>) -> Result<Self> {
+		Ok(Self {
+			url: self.url.clone(),
+			init: self.init.clone_js(ctx)?,
+		})
+	}
 
-		#[quickjs(constructor)]
-		pub fn new<'js>(
-			ctx: Ctx<'js>,
-			input: Value<'js>,
-			init: Opt<RequestInit>,
-			args: Rest<()>,
-		) -> Result<Self> {
-			if let Some(url) = input.as_string() {
-				// url string
-				let url_str = url.to_string()?;
-				let url = Url::parse(&url_str).map_err(|e| {
-					Exception::throw_type(ctx, &format!("failed to parse url: {e}"))
-				})?;
-				if !url.username().is_empty() || !url.password().map(str::is_empty).unwrap_or(true)
-				{
-					// url cannot contain non empty username and passwords
-					return Err(Exception::throw_type(ctx, "Url contained credentials."));
+	// ------------------------------
+	// Instance properties
+	// ------------------------------
+	#[qjs(get, rename = "body_used")]
+	pub fn body_used(&self) -> bool {
+		self.init.body.as_ref().map(Body::used).unwrap_or(true)
+	}
+
+	#[qjs(get)]
+	pub fn method(&self) -> String {
+		self.init.method.to_string()
+	}
+
+	#[qjs(get)]
+	pub fn url(&self) -> String {
+		self.url.to_string()
+	}
+
+	#[qjs(get)]
+	pub fn headers(&self) -> Class<'js, Headers> {
+		self.init.headers.clone()
+	}
+
+	#[qjs(get)]
+	pub fn referrer(&self) -> String {
+		self.init.referrer.clone()
+	}
+	// TODO
+
+	// ------------------------------
+	// Instance methods
+	// ------------------------------
+
+	// Convert the object to a string
+	#[qjs(rename = "toString")]
+	pub fn js_to_string(&self) -> String {
+		String::from("[object Request]")
+	}
+
+	/// Takes the buffer from the body leaving it used.
+	#[qjs(skip)]
+	async fn take_buffer(&self, ctx: &Ctx<'js>) -> Result<Bytes> {
+		let Some(body) = self.init.body.as_ref() else {
+			return Ok(Bytes::new());
+		};
+		match body.to_buffer().await {
+			Ok(Some(x)) => Ok(x),
+			Ok(None) => Err(Exception::throw_type(ctx, "Body unusable")),
+			Err(e) => match e {
+				RequestError::Reqwest(e) => {
+					Err(Exception::throw_type(ctx, &format!("stream failed: {e}")))
 				}
-				let init = init.into_inner().map_or_else(|| RequestInit::default(ctx), Ok)?;
-				// HEAD and GET methods can't have a body
-				if init.body.is_some() && init.method == Method::GET || init.method == Method::HEAD
-				{
-					return Err(Exception::throw_type(
-						ctx,
-						&format!("Request with method `{}` cannot have a body", init.method),
-					));
-				}
-
-				Ok(Self {
-					url,
-					init,
-				})
-			} else if let Some(request) = input
-				.into_object()
-				.and_then(|obj| Class::<Self>::from_object(obj).ok().map(|x| x.borrow()))
-			{
-				// existing request object, just return it
-				request.clone_js(ctx, Default::default())
-			} else {
-				Err(Exception::throw_type(
-					ctx,
-					"request `init` paramater must either be a request object or a string",
-				))
-			}
+			},
 		}
+	}
 
-		/// Clone the response, teeing any possible underlying streams.
-		#[quickjs(rename = "clone")]
-		pub fn clone_js(&self, ctx: Ctx<'_>, _rest: Rest<()>) -> Result<Self> {
-			Ok(Self {
-				url: self.url.clone(),
-				init: self.init.clone_js(ctx)?,
-			})
-		}
+	// Returns a promise with the request body as a Blob
+	pub async fn blob(&self, ctx: Ctx<'js>) -> Result<Blob> {
+		let headers = self.init.headers.clone();
+		let mime = {
+			let headers = headers.borrow();
+			let headers = &headers.inner;
+			let key = HeaderName::from_static("content-type");
+			let types = headers.get_all(key);
+			// TODO: This is not according to spec.
+			types
+				.iter()
+				.next()
+				.map(|x| x.to_str().unwrap_or("text/html"))
+				.unwrap_or("text/html")
+				.to_owned()
+		};
 
-		// ------------------------------
-		// Instance properties
-		// ------------------------------
-		#[quickjs(get)]
-		pub fn bodyUsed(&self) -> bool {
-			self.init.body.as_ref().map(Body::used).unwrap_or(true)
-		}
+		let data = self.take_buffer(&ctx).await?;
+		Ok(Blob {
+			mime,
+			data,
+		})
+	}
 
-		#[quickjs(get)]
-		pub fn method(&self) -> String {
-			self.init.method.to_string()
-		}
+	// Returns a promise with the request body as FormData
+	#[qjs(rename = "formData")]
+	pub async fn form_data(&self, ctx: Ctx<'js>) -> Result<Value<'js>> {
+		Err(Exception::throw_internal(&ctx, "Not yet implemented"))
+	}
 
-		#[quickjs(get)]
-		pub fn url(&self) -> String {
-			self.url.to_string()
-		}
+	// Returns a promise with the request body as JSON
+	pub async fn json(&self, ctx: Ctx<'js>) -> Result<Value<'js>> {
+		let text = self.text(ctx.clone()).await?;
+		ctx.json_parse(text)
+	}
 
-		#[quickjs(get)]
-		pub fn headers<'js>(&self, ctx: Ctx<'js>) -> Class<'js, HeadersClass> {
-			self.init.headers.clone().restore(ctx).unwrap()
-		}
+	// Returns a promise with the request body as text
+	pub async fn text(&self, ctx: Ctx<'js>) -> Result<String> {
+		let data = self.take_buffer(&ctx).await?;
 
-		#[quickjs(get)]
-		pub fn referrer(&self, ctx: Ctx<'_>) -> String {
-			self.init.referrer.clone()
-		}
-		// TODO
-
-		// ------------------------------
-		// Instance methods
-		// ------------------------------
-
-		// Convert the object to a string
-		pub fn toString(&self) -> String {
-			String::from("[object Request]")
-		}
-
-		/// Takes the buffer from the body leaving it used.
-		#[quickjs(skip)]
-		async fn take_buffer<'js>(&self, ctx: Ctx<'js>) -> Result<Bytes> {
-			let Some(body) = self.init.body.as_ref() else {
-				return Ok(Bytes::new())
-			};
-			match body.to_buffer().await {
-				Ok(Some(x)) => Ok(x),
-				Ok(None) => Err(Exception::throw_type(ctx, "Body unusable")),
-				Err(e) => match e {
-					RequestError::Reqwest(e) => {
-						Err(Exception::throw_type(ctx, &format!("stream failed: {e}")))
-					}
-				},
-			}
-		}
-
-		// Returns a promise with the request body as a Blob
-		pub async fn blob(&self, ctx: Ctx<'_>, args: Rest<()>) -> Result<BlobClass> {
-			let headers = self.init.headers.clone().restore(ctx).unwrap();
-			let mime = {
-				let headers = headers.borrow();
-				let headers = headers.inner.borrow();
-				let key = HeaderName::from_static("content-type");
-				let types = headers.get_all(key);
-				// TODO: This is not according to spec.
-				types
-					.iter()
-					.next()
-					.map(|x| x.to_str().unwrap_or("text/html"))
-					.unwrap_or("text/html")
-					.to_owned()
-			};
-
-			let data = self.take_buffer(ctx).await?;
-			Ok(BlobClass {
-				mime,
-				data,
-			})
-		}
-
-		// Returns a promise with the request body as FormData
-		pub async fn formData<'js>(&self, ctx: Ctx<'js>, args: Rest<()>) -> Result<Value<'js>> {
-			Err(Exception::throw_internal(ctx, "Not yet implemented"))
-		}
-
-		// Returns a promise with the request body as JSON
-		pub async fn json<'js>(&self, ctx: Ctx<'js>, args: Rest<()>) -> Result<Value<'js>> {
-			let text = self.text(ctx, args).await?;
-			ctx.json_parse(text)
-		}
-
-		// Returns a promise with the request body as text
-		pub async fn text<'js>(&self, ctx: Ctx<'js>, args: Rest<()>) -> Result<String> {
-			let data = self.take_buffer(ctx).await?;
-
-			// Skip UTF-BOM
-			if data.starts_with(&[0xEF, 0xBB, 0xBF]) {
-				Ok(String::from_utf8_lossy(&data[3..]).into_owned())
-			} else {
-				Ok(String::from_utf8_lossy(&data).into_owned())
-			}
+		// Skip UTF-BOM
+		if data.starts_with(&[0xEF, 0xBB, 0xBF]) {
+			Ok(String::from_utf8_lossy(&data[3..]).into_owned())
+		} else {
+			Ok(String::from_utf8_lossy(&data).into_owned())
 		}
 	}
 }
@@ -644,7 +615,7 @@ mod test {
 					assert.seq(await req_2.text(),"some text");
 
 				})()
-			"#).catch(ctx).unwrap().await.catch(ctx).unwrap();
+			"#).catch(&ctx).unwrap().await.catch(&ctx).unwrap();
 		})
 		.await;
 	}
diff --git a/lib/src/fnc/script/fetch/classes/response/init.rs b/lib/src/fnc/script/fetch/classes/response/init.rs
index 53cbcb78..04c25394 100644
--- a/lib/src/fnc/script/fetch/classes/response/init.rs
+++ b/lib/src/fnc/script/fetch/classes/response/init.rs
@@ -1,34 +1,33 @@
 use std::string::String as StdString;
 
 use js::{
-	class::{HasRefs, RefsMarker},
+	class::{Trace, Tracer},
 	prelude::*,
-	Class, Ctx, Exception, FromJs, Object, Persistent, Result, Value,
+	Class, Ctx, Exception, FromJs, Object, Result, Value,
 };
 
-use crate::fnc::script::fetch::{classes::HeadersClass, util};
+use crate::fnc::script::fetch::{classes::Headers, util};
 
 /// Struct containing data from the init argument from the Response constructor.
 #[derive(Clone)]
-pub struct ResponseInit {
+pub struct ResponseInit<'js> {
 	// u16 instead of reqwest::StatusCode since javascript allows non valid status codes in some
 	// circumstances.
 	pub status: u16,
 	pub status_text: StdString,
-	pub headers: Persistent<Class<'static, HeadersClass>>,
+	pub headers: Class<'js, Headers>,
 }
 
-impl HasRefs for ResponseInit {
-	fn mark_refs(&self, marker: &RefsMarker) {
-		self.headers.mark_refs(marker);
+impl<'js> Trace<'js> for ResponseInit<'js> {
+	fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
+		self.headers.trace(tracer);
 	}
 }
 
-impl ResponseInit {
+impl<'js> ResponseInit<'js> {
 	/// Returns a ResponseInit object with all values as the default value.
-	pub fn default(ctx: Ctx<'_>) -> Result<ResponseInit> {
-		let headers = Class::instance(ctx, HeadersClass::new_empty())?;
-		let headers = Persistent::save(ctx, headers);
+	pub fn default(ctx: Ctx<'js>) -> Result<Self> {
+		let headers = Class::instance(ctx, Headers::new_empty())?;
 		Ok(ResponseInit {
 			status: 200,
 			status_text: StdString::new(),
@@ -37,8 +36,8 @@ impl ResponseInit {
 	}
 }
 
-impl<'js> FromJs<'js> for ResponseInit {
-	fn from_js(ctx: Ctx<'js>, value: Value<'js>) -> Result<Self> {
+impl<'js> FromJs<'js> for ResponseInit<'js> {
+	fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> {
 		let object = Object::from_js(ctx, value)?;
 
 		// Extract status.
@@ -66,12 +65,11 @@ impl<'js> FromJs<'js> for ResponseInit {
 
 		// Extract headers.
 		let headers = if let Some(headers) = object.get::<_, Option<Value>>("headers")? {
-			let headers = HeadersClass::new_inner(ctx, headers)?;
-			Class::instance(ctx, headers)?
+			let headers = Headers::new_inner(ctx, headers)?;
+			Class::instance(ctx.clone(), headers)?
 		} else {
-			Class::instance(ctx, HeadersClass::new_empty())?
+			Class::instance(ctx.clone(), Headers::new_empty())?
 		};
-		let headers = Persistent::save(ctx, headers);
 
 		Ok(ResponseInit {
 			status,
diff --git a/lib/src/fnc/script/fetch/classes/response/mod.rs b/lib/src/fnc/script/fetch/classes/response/mod.rs
index 87a0223b..4791900c 100644
--- a/lib/src/fnc/script/fetch/classes/response/mod.rs
+++ b/lib/src/fnc/script/fetch/classes/response/mod.rs
@@ -1,11 +1,10 @@
 //! Response class implementation
 
-use js::bind;
-
 mod init;
+use bytes::Bytes;
 pub use init::ResponseInit;
 
-pub use response::Response as ResponseClass;
+use js::{class::Trace, prelude::Opt, ArrayBuffer, Class, Ctx, Exception, Result, Value};
 
 #[allow(dead_code)]
 #[derive(Clone, Copy)]
@@ -18,304 +17,284 @@ pub enum ResponseType {
 	OpaqueRedirect,
 }
 
-#[bind(object, public)]
-#[quickjs(bare)]
-#[allow(non_snake_case)]
-#[allow(unused_variables)]
-#[allow(clippy::module_inception)]
-pub mod response {
+use reqwest::Url;
 
-	use crate::fnc::script::fetch::{
-		body::{Body, BodyKind},
-		classes::{BlobClass, HeadersClass},
-		util, RequestError,
-	};
+use crate::fnc::script::fetch::{
+	body::{Body, BodyKind},
+	util, RequestError,
+};
 
-	use super::{ResponseInit, ResponseType};
-	use bytes::Bytes;
-	use js::{
-		function::{Opt, Rest},
-		ArrayBuffer, Class, Ctx, Exception, HasRefs, Persistent, Result, Value,
-	};
-	use reqwest::Url;
+use super::{Blob, Headers};
 
-	#[derive(HasRefs)]
-	#[allow(dead_code)]
-	#[quickjs(has_refs)]
-	pub struct Response {
-		#[quickjs(has_refs)]
-		pub(crate) body: Body,
-		#[quickjs(has_refs)]
-		pub(crate) init: ResponseInit,
-		pub(crate) url: Option<Url>,
-		pub(crate) r#type: ResponseType,
-		pub(crate) was_redirected: bool,
+#[allow(dead_code)]
+#[derive(Trace)]
+#[js::class]
+pub struct Response<'js> {
+	#[qjs(skip_trace)]
+	pub(crate) body: Body,
+	pub(crate) init: ResponseInit<'js>,
+	#[qjs(skip_trace)]
+	pub(crate) url: Option<Url>,
+	#[qjs(skip_trace)]
+	pub(crate) r#type: ResponseType,
+	pub(crate) was_redirected: bool,
+}
+
+#[js::methods]
+impl<'js> Response<'js> {
+	// ------------------------------
+	// Constructor
+	// ------------------------------
+
+	#[qjs(constructor)]
+	pub fn new(
+		ctx: Ctx<'js>,
+		body: Opt<Option<Body>>,
+		init: Opt<ResponseInit<'js>>,
+	) -> Result<Self> {
+		let init = match init.into_inner() {
+			Some(x) => x,
+			None => ResponseInit::default(ctx.clone())?,
+		};
+		let body = body.into_inner().and_then(|x| x);
+		if body.is_some() && util::is_null_body_status(init.status) {
+			// Null body statuses are not allowed to have a body.
+			return Err(Exception::throw_type(
+				&ctx,
+				&format!("Response with status `{}` is not allowed to have a body", init.status),
+			));
+		}
+		let body = body.unwrap_or_default();
+
+		Ok(Response {
+			body,
+			init,
+			url: None,
+			r#type: ResponseType::Default,
+			was_redirected: false,
+		})
 	}
 
-	impl Response {
-		// ------------------------------
-		// Constructor
-		// ------------------------------
+	// ------------------------------
+	// Instance properties
+	// ------------------------------
 
-		#[quickjs(constructor)]
-		pub fn new(
-			ctx: Ctx<'_>,
-			body: Opt<Option<Body>>,
-			init: Opt<ResponseInit>,
-			args: Rest<()>,
-		) -> Result<Self> {
-			let init = match init.into_inner() {
-				Some(x) => x,
-				None => ResponseInit::default(ctx)?,
-			};
-			let body = body.into_inner().and_then(|x| x);
-			if body.is_some() && util::is_null_body_status(init.status) {
-				// Null body statuses are not allowed to have a body.
-				return Err(Exception::throw_type(
-					ctx,
-					&format!(
-						"Response with status `{}` is not allowed to have a body",
-						init.status
-					),
-				));
+	#[qjs(get, rename = "bodyUsed")]
+	pub fn body_used(&self) -> bool {
+		self.body.used()
+	}
+
+	#[qjs(get)]
+	pub fn status(&self) -> u16 {
+		self.init.status
+	}
+
+	#[qjs(get)]
+	pub fn ok(&self) -> bool {
+		util::is_ok_status(self.init.status)
+	}
+
+	#[qjs(get)]
+	pub fn redirected(&self) -> bool {
+		self.was_redirected
+	}
+
+	#[qjs(get, rename = "statusText")]
+	pub fn status_text(&self) -> String {
+		self.init.status_text.clone()
+	}
+
+	#[qjs(get, rename = "type")]
+	pub fn r#type(&self) -> &'static str {
+		match self.r#type {
+			ResponseType::Basic => "basic",
+			ResponseType::Cors => "cors",
+			ResponseType::Default => "default",
+			ResponseType::Error => "error",
+			ResponseType::Opaque => "opaque",
+			ResponseType::OpaqueRedirect => "opaqueredirect",
+		}
+	}
+
+	#[qjs(get)]
+	pub fn headers(&self) -> Class<'js, Headers> {
+		self.init.headers.clone()
+	}
+
+	#[qjs(get)]
+	pub fn url(&self) -> Option<String> {
+		self.url.as_ref().map(|x| {
+			if x.fragment().is_some() {
+				let mut res = x.clone();
+				res.set_fragment(None);
+				res.to_string()
+			} else {
+				x.to_string()
 			}
-			let body = body.unwrap_or_default();
+		})
+	}
 
-			Ok(Response {
-				body,
-				init,
-				url: None,
-				r#type: ResponseType::Default,
-				was_redirected: false,
-			})
+	// ------------------------------
+	// Instance methods
+	// ------------------------------
+
+	// Convert the object to a string
+	#[qjs(rename = "toString")]
+	pub fn js_to_string(&self) -> String {
+		String::from("[object Response]")
+	}
+
+	// Creates a copy of the request object
+	#[qjs(rename = "clone")]
+	pub fn clone_js(&self, ctx: Ctx<'js>) -> Self {
+		Response {
+			body: self.body.clone_js(ctx),
+			init: self.init.clone(),
+			url: self.url.clone(),
+			r#type: self.r#type,
+			was_redirected: self.was_redirected,
 		}
+	}
 
-		// ------------------------------
-		// Instance properties
-		// ------------------------------
-
-		#[quickjs(get)]
-		pub fn bodyUsed(&self) -> bool {
-			self.body.used()
-		}
-
-		#[quickjs(get)]
-		pub fn status(&self) -> u16 {
-			self.init.status
-		}
-
-		#[quickjs(get)]
-		pub fn ok(&self) -> bool {
-			util::is_ok_status(self.init.status)
-		}
-
-		#[quickjs(get)]
-		pub fn redirected(&self) -> bool {
-			self.was_redirected
-		}
-
-		#[quickjs(get)]
-		pub fn statusText(&self) -> String {
-			self.init.status_text.clone()
-		}
-
-		#[quickjs(get)]
-		pub fn r#type(&self) -> &'static str {
-			match self.r#type {
-				ResponseType::Basic => "basic",
-				ResponseType::Cors => "cors",
-				ResponseType::Default => "default",
-				ResponseType::Error => "error",
-				ResponseType::Opaque => "opaque",
-				ResponseType::OpaqueRedirect => "opaqueredirect",
-			}
-		}
-
-		#[quickjs(get)]
-		pub fn headers<'js>(&self, ctx: Ctx<'js>) -> Class<'js, HeadersClass> {
-			self.init.headers.clone().restore(ctx).unwrap()
-		}
-
-		#[quickjs(get)]
-		pub fn url(&self) -> Option<String> {
-			self.url.as_ref().map(|x| {
-				if x.fragment().is_some() {
-					let mut res = x.clone();
-					res.set_fragment(None);
-					res.to_string()
-				} else {
-					x.to_string()
+	#[qjs(skip)]
+	async fn take_buffer(&self, ctx: &Ctx<'js>) -> Result<Bytes> {
+		match self.body.to_buffer().await {
+			Ok(Some(x)) => Ok(x),
+			Ok(None) => Err(Exception::throw_type(ctx, "Body unusable")),
+			Err(e) => match e {
+				RequestError::Reqwest(e) => {
+					Err(Exception::throw_type(ctx, &format!("stream failed: {e}")))
 				}
-			})
+			},
+		}
+	}
+
+	// Returns a promise with the response body as a Blob
+	pub async fn blob(&self, ctx: Ctx<'js>) -> Result<Blob> {
+		let headers = self.init.headers.clone();
+		let mime = {
+			let headers = headers.borrow();
+			let headers = &headers.inner;
+			let types = headers.get_all(reqwest::header::CONTENT_TYPE);
+			// TODO: This is not according to spec.
+			types
+				.iter()
+				.next()
+				.map(|x| x.to_str().unwrap_or("text/html"))
+				.unwrap_or("text/html")
+				.to_owned()
+		};
+
+		let data = self.take_buffer(&ctx).await?;
+		Ok(Blob {
+			mime,
+			data,
+		})
+	}
+
+	// Returns a promise with the response body as FormData
+	#[qjs(rename = "formData")]
+	pub async fn form_data(&self, ctx: Ctx<'js>) -> Result<Value<'js>> {
+		Err(Exception::throw_internal(&ctx, "Not yet implemented"))
+	}
+
+	// Returns a promise with the response body as JSON
+	pub async fn json(&self, ctx: Ctx<'js>) -> Result<Value<'js>> {
+		let text = self.text(ctx.clone()).await?;
+		ctx.json_parse(text)
+	}
+
+	// Returns a promise with the response body as text
+	pub async fn text(&self, ctx: Ctx<'js>) -> Result<String> {
+		let data = self.take_buffer(&ctx).await?;
+
+		// Skip UTF-BOM
+		if data.starts_with(&[0xEF, 0xBB, 0xBF]) {
+			Ok(String::from_utf8_lossy(&data[3..]).into_owned())
+		} else {
+			Ok(String::from_utf8_lossy(&data).into_owned())
+		}
+	}
+
+	// Returns a promise with the response body as text
+	#[qjs(rename = "arrayBuffer")]
+	pub async fn array_buffer(&self, ctx: Ctx<'js>) -> Result<ArrayBuffer<'js>> {
+		let data = self.take_buffer(&ctx).await?;
+		ArrayBuffer::new(ctx, data)
+	}
+
+	// ------------------------------
+	// Static methods
+	// ------------------------------
+
+	#[qjs(r#static, rename = "json")]
+	pub fn static_json(
+		ctx: Ctx<'js>,
+		data: Value<'js>,
+		init: Opt<ResponseInit<'js>>,
+	) -> Result<Self> {
+		let json = ctx.json_stringify(data)?;
+		let json =
+			json.ok_or_else(|| Exception::throw_type(&ctx, "Value is not JSON serializable"))?;
+		let json = json.to_string()?;
+
+		let init = if let Some(init) = init.into_inner() {
+			init
+		} else {
+			ResponseInit::default(ctx)?
+		};
+
+		Ok(Response {
+			url: None,
+			body: Body::buffer(BodyKind::Buffer, json),
+			init,
+			r#type: ResponseType::Default,
+			was_redirected: false,
+		})
+	}
+
+	// Returns a new response representing a network error
+	#[qjs(r#static)]
+	pub fn error(ctx: Ctx<'js>) -> Result<Self> {
+		let headers = Class::instance(ctx, Headers::new_empty())?;
+		Ok(Response {
+			url: None,
+			body: Body::new(),
+			init: ResponseInit {
+				status: 0,
+				status_text: String::new(),
+				headers,
+			},
+			r#type: ResponseType::Error,
+			was_redirected: false,
+		})
+	}
+
+	// Creates a new response with a different URL
+	#[qjs(r#static)]
+	pub fn redirect(ctx: Ctx<'_>, url: String, status: Opt<u32>) -> Result<Response> {
+		let url = url
+			.parse::<Url>()
+			.map_err(|e| Exception::throw_type(&ctx, &format!("Invalid url: {e}")))?;
+
+		let status = status.into_inner().unwrap_or(302) as u16;
+		if !util::is_redirect_status(status) {
+			return Err(Exception::throw_range(&ctx, "Status code is not a redirect status"));
 		}
 
-		// ------------------------------
-		// Instance methods
-		// ------------------------------
+		let headers = Class::instance(ctx, Headers::new_empty())?;
 
-		// Convert the object to a string
-		pub fn toString(&self, args: Rest<()>) -> String {
-			String::from("[object Response]")
-		}
-
-		// Creates a copy of the request object
-		#[quickjs(rename = "clone")]
-		pub fn clone_js(&self, ctx: Ctx<'_>, args: Rest<()>) -> Response {
-			Response {
-				body: self.body.clone_js(ctx),
-				init: self.init.clone(),
-				url: self.url.clone(),
-				r#type: self.r#type,
-				was_redirected: self.was_redirected,
-			}
-		}
-
-		#[quickjs(skip)]
-		async fn take_buffer<'js>(&self, ctx: Ctx<'js>) -> Result<Bytes> {
-			match self.body.to_buffer().await {
-				Ok(Some(x)) => Ok(x),
-				Ok(None) => Err(Exception::throw_type(ctx, "Body unusable")),
-				Err(e) => match e {
-					RequestError::Reqwest(e) => {
-						Err(Exception::throw_type(ctx, &format!("stream failed: {e}")))
-					}
-				},
-			}
-		}
-
-		// Returns a promise with the response body as a Blob
-		pub async fn blob<'js>(&self, ctx: Ctx<'js>, args: Rest<()>) -> Result<BlobClass> {
-			let headers = self.init.headers.clone().restore(ctx).unwrap();
-			let mime = {
-				let headers = headers.borrow();
-				let headers = headers.inner.borrow();
-				let types = headers.get_all(reqwest::header::CONTENT_TYPE);
-				// TODO: This is not according to spec.
-				types
-					.iter()
-					.next()
-					.map(|x| x.to_str().unwrap_or("text/html"))
-					.unwrap_or("text/html")
-					.to_owned()
-			};
-
-			let data = self.take_buffer(ctx).await?;
-			Ok(BlobClass {
-				mime,
-				data,
-			})
-		}
-
-		// Returns a promise with the response body as FormData
-		pub async fn formData<'js>(&self, ctx: Ctx<'js>, args: Rest<()>) -> Result<Value<'js>> {
-			Err(Exception::throw_internal(ctx, "Not yet implemented"))
-		}
-
-		// Returns a promise with the response body as JSON
-		pub async fn json<'js>(&self, ctx: Ctx<'js>, args: Rest<()>) -> Result<Value<'js>> {
-			let text = self.text(ctx, args).await?;
-			ctx.json_parse(text)
-		}
-
-		// Returns a promise with the response body as text
-		pub async fn text<'js>(&self, ctx: Ctx<'js>, args: Rest<()>) -> Result<String> {
-			let data = self.take_buffer(ctx).await?;
-
-			// Skip UTF-BOM
-			if data.starts_with(&[0xEF, 0xBB, 0xBF]) {
-				Ok(String::from_utf8_lossy(&data[3..]).into_owned())
-			} else {
-				Ok(String::from_utf8_lossy(&data).into_owned())
-			}
-		}
-
-		// Returns a promise with the response body as text
-		pub async fn arrayBuffer<'js>(
-			&self,
-			ctx: Ctx<'js>,
-			args: Rest<()>,
-		) -> Result<ArrayBuffer<'js>> {
-			let data = self.take_buffer(ctx).await?;
-			ArrayBuffer::new(ctx, data)
-		}
-
-		// ------------------------------
-		// Static methods
-		// ------------------------------
-
-		#[quickjs(rename = "json")]
-		pub fn static_json<'js>(
-			ctx: Ctx<'js>,
-			data: Value<'js>,
-			init: Opt<ResponseInit>,
-			args: Rest<()>,
-		) -> Result<Self> {
-			let json = ctx.json_stringify(data)?;
-			let json =
-				json.ok_or_else(|| Exception::throw_type(ctx, "Value is not JSON serializable"))?;
-			let json = json.to_string()?;
-
-			let init = if let Some(init) = init.into_inner() {
-				init
-			} else {
-				ResponseInit::default(ctx)?
-			};
-
-			Ok(Response {
-				url: None,
-				body: Body::buffer(BodyKind::Buffer, json),
-				init,
-				r#type: ResponseType::Default,
-				was_redirected: false,
-			})
-		}
-
-		// Returns a new response representing a network error
-		pub fn error(ctx: Ctx<'_>, args: Rest<()>) -> Result<Self> {
-			let headers = Persistent::save(ctx, Class::instance(ctx, HeadersClass::new_empty())?);
-			Ok(Response {
-				url: None,
-				body: Body::new(),
-				init: ResponseInit {
-					status: 0,
-					status_text: String::new(),
-					headers,
-				},
-				r#type: ResponseType::Error,
-				was_redirected: false,
-			})
-		}
-
-		// Creates a new response with a different URL
-		pub fn redirect(
-			ctx: Ctx<'_>,
-			url: String,
-			status: Opt<u32>,
-			args: Rest<()>,
-		) -> Result<Response> {
-			let url = url
-				.parse::<Url>()
-				.map_err(|e| Exception::throw_type(ctx, &format!("Invalid url: {e}")))?;
-
-			let status = status.into_inner().unwrap_or(302) as u16;
-			if !util::is_redirect_status(status) {
-				return Err(Exception::throw_range(ctx, "Status code is not a redirect status"));
-			}
-
-			let headers = Persistent::save(ctx, Class::instance(ctx, HeadersClass::new_empty())?);
-
-			Ok(Response {
-				url: Some(url),
-				body: Body::new(),
-				init: ResponseInit {
-					status,
-					status_text: String::new(),
-					headers,
-				},
-				r#type: ResponseType::Default,
-				was_redirected: false,
-			})
-		}
+		Ok(Response {
+			url: Some(url),
+			body: Body::new(),
+			init: ResponseInit {
+				status,
+				status_text: String::new(),
+				headers,
+			},
+			r#type: ResponseType::Default,
+			was_redirected: false,
+		})
 	}
 }
 
@@ -388,7 +367,7 @@ mod test {
 
 
 				})()
-			"#).catch(ctx).unwrap().await.catch(ctx).unwrap();
+			"#).catch(&ctx).unwrap().await.catch(&ctx).unwrap();
 		})
 		.await;
 	}
diff --git a/lib/src/fnc/script/fetch/func.rs b/lib/src/fnc/script/fetch/func.rs
index cc2f6241..40dc9fac 100644
--- a/lib/src/fnc/script/fetch/func.rs
+++ b/lib/src/fnc/script/fetch/func.rs
@@ -2,29 +2,28 @@
 
 use crate::fnc::script::fetch::{
 	body::{Body, BodyData, BodyKind},
-	classes::{
-		self, HeadersClass, RequestClass, RequestInit, ResponseClass, ResponseInit, ResponseType,
-	},
+	classes::{self, Request, RequestInit, Response, ResponseInit, ResponseType},
 	RequestError,
 };
 use futures::TryStreamExt;
-use js::{bind, function::Opt, prelude::*, Class, Ctx, Exception, Persistent, Result, Value};
+use js::{function::Opt, Class, Ctx, Exception, Result, Value};
 use reqwest::{
 	header::{HeaderValue, CONTENT_TYPE},
 	redirect, Body as ReqBody,
 };
 use std::sync::Arc;
 
-#[bind(object, public)]
+use super::classes::Headers;
+
+#[js::function]
 #[allow(unused_variables)]
 pub async fn fetch<'js>(
 	ctx: Ctx<'js>,
 	input: Value<'js>,
-	init: Opt<RequestInit>,
-	args: Rest<()>,
-) -> Result<ResponseClass> {
+	init: Opt<RequestInit<'js>>,
+) -> Result<Response<'js>> {
 	// Create a request from the input.
-	let js_req = RequestClass::new(ctx, input, init, args)?;
+	let js_req = Request::new(ctx.clone(), input, init)?;
 
 	let url = js_req.url;
 
@@ -32,9 +31,9 @@ pub async fn fetch<'js>(
 
 	// SurrealDB Implementation keeps all javascript parts inside the context::with scope so this
 	// unwrap should never panic.
-	let headers = js_req.init.headers.restore(ctx).unwrap();
+	let headers = js_req.init.headers;
 	let headers = headers.borrow();
-	let mut headers = headers.inner.borrow().clone();
+	let mut headers = headers.inner.clone();
 
 	let redirect = js_req.init.request_redirect;
 
@@ -55,7 +54,7 @@ pub async fn fetch<'js>(
 	});
 
 	let client = reqwest::Client::builder().redirect(policy).build().map_err(|e| {
-		Exception::throw_internal(ctx, &format!("Could not initialize http client: {e}"))
+		Exception::throw_internal(&ctx, &format!("Could not initialize http client: {e}"))
 	})?;
 
 	// Set the body for the request.
@@ -70,7 +69,7 @@ pub async fn fetch<'js>(
 				let body = ReqBody::from(x);
 				req_builder = req_builder.body(body);
 			}
-			BodyData::Used => return Err(Exception::throw_type(ctx, "Body unusable")),
+			BodyData::Used => return Err(Exception::throw_type(&ctx, "Body unusable")),
 		};
 		match body.kind {
 			BodyKind::Buffer => {}
@@ -94,12 +93,11 @@ pub async fn fetch<'js>(
 		.headers(headers)
 		.send()
 		.await
-		.map_err(|e| Exception::throw_type(ctx, &e.to_string()))?;
+		.map_err(|e| Exception::throw_type(&ctx, &e.to_string()))?;
 
 	// Extract the headers
-	let headers = HeadersClass::from_map(response.headers().clone());
+	let headers = Headers::from_map(response.headers().clone());
 	let headers = Class::instance(ctx, headers)?;
-	let headers = Persistent::save(ctx, headers);
 	let init = ResponseInit {
 		headers,
 		status: response.status().as_u16(),
@@ -111,7 +109,7 @@ pub async fn fetch<'js>(
 		BodyKind::Buffer,
 		response.bytes_stream().map_err(Arc::new).map_err(RequestError::Reqwest),
 	);
-	let response = ResponseClass {
+	let response = Response {
 		body,
 		init,
 		url: Some(url),
diff --git a/lib/src/fnc/script/fetch/mod.rs b/lib/src/fnc/script/fetch/mod.rs
index 5636df74..40f831e9 100644
--- a/lib/src/fnc/script/fetch/mod.rs
+++ b/lib/src/fnc/script/fetch/mod.rs
@@ -1,6 +1,6 @@
 use std::{error::Error, fmt, sync::Arc};
 
-use js::{Ctx, Result};
+use js::{Class, Ctx, Result};
 
 mod body;
 mod classes;
@@ -9,7 +9,7 @@ mod stream;
 mod util;
 
 use classes::{Blob, FormData, Headers, Request, Response};
-use func::Fetch;
+use func::js_fetch;
 
 // Anoyingly errors aren't clone,
 // But with how we implement streams RequestError must be clone.
@@ -30,15 +30,14 @@ impl fmt::Display for RequestError {
 impl Error for RequestError {}
 
 /// Register the fetch types in the context.
-pub fn register(ctx: Ctx<'_>) -> Result<()> {
+pub fn register(ctx: &Ctx<'_>) -> Result<()> {
 	let globals = ctx.globals();
-	globals.init_def::<Fetch>()?;
-
-	globals.init_def::<Response>()?;
-	globals.init_def::<Request>()?;
-	globals.init_def::<Blob>()?;
-	globals.init_def::<FormData>()?;
-	globals.init_def::<Headers>()?;
+	globals.set("fetch", js_fetch)?;
+	Class::<Response>::define(&globals)?;
+	Class::<Request>::define(&globals)?;
+	Class::<Blob>::define(&globals)?;
+	Class::<FormData>::define(&globals)?;
+	Class::<Headers>::define(&globals)?;
 
 	Ok(())
 }
@@ -55,9 +54,9 @@ mod test {
 				let ctx = js::AsyncContext::full(&rt).await.unwrap();
 
 				js::async_with!(ctx => |$ctx|{
-					crate::fnc::script::fetch::register($ctx).unwrap();
+					crate::fnc::script::fetch::register(&$ctx).unwrap();
 
-					$ctx.eval::<(),_>(r#"
+					$ctx.eval::<(),_>(r"
 					globalThis.assert = (...arg) => {
 						arg.forEach(x => {
 							if (!x) {
@@ -91,7 +90,7 @@ mod test {
 						}
 						throw new Error(`Code which should throw, didnt: \n${cb}`)
 					}
-					"#).unwrap();
+					").unwrap();
 
 					$($t)*
 				}).await;
diff --git a/lib/src/fnc/script/fetch_stub/mod.rs b/lib/src/fnc/script/fetch_stub/mod.rs
index 9f050826..e497e09a 100644
--- a/lib/src/fnc/script/fetch_stub/mod.rs
+++ b/lib/src/fnc/script/fetch_stub/mod.rs
@@ -1,27 +1,25 @@
-/// The stub implementations for the fetch API when `http` is not enabled.
-use js::{bind, prelude::*, Ctx, Exception, Result};
+//! stub implementations for the fetch API when `http` is not enabled.
+
+use js::{class::Trace, Class, Ctx, Exception, Result};
 
 #[cfg(test)]
 mod test;
 
 /// Register the fetch types in the context.
-pub fn register(ctx: Ctx<'_>) -> Result<()> {
+pub fn register(ctx: &Ctx<'_>) -> Result<()> {
 	let globals = ctx.globals();
-	globals.init_def::<Fetch>()?;
-
-	globals.init_def::<Response>()?;
-	globals.init_def::<Request>()?;
-	globals.init_def::<Blob>()?;
-	globals.init_def::<FormData>()?;
-	globals.init_def::<Headers>()?;
-
+	Class::<response::Response>::define(&globals)?;
+	Class::<request::Request>::define(&globals)?;
+	Class::<blob::Blob>::define(&globals)?;
+	Class::<form_data::FormData>::define(&globals)?;
+	Class::<headers::Headers>::define(&globals)?;
+	globals.set("fetch", Function::new(ctx, js_fetch).with_name("fetch"));
 	Ok(())
 }
 
-#[bind(object, public)]
-#[allow(unused_variables)]
-fn fetch<'js>(ctx: Ctx<'js>, args: Rest<()>) -> Result<()> {
-	Err(Exception::throw_internal(ctx,"The 'fetch' function is not available in this build of SurrealDB. In order to use 'fetch', enable the 'http' feature."))
+#[js::function]
+fn fetch<'js>(ctx: Ctx<'js>) -> Result<()> {
+	Err(Exception::throw_internal(&ctx,"The 'fetch' function is not available in this build of SurrealDB. In order to use 'fetch', enable the 'http' feature."))
 }
 
 macro_rules! impl_stub_class {
@@ -29,31 +27,29 @@ macro_rules! impl_stub_class {
 
 		$(
 
-			#[js::bind(object, public)]
-			#[quickjs(bare)]
-			#[allow(non_snake_case)]
-			#[allow(unused_variables)]
-			#[allow(clippy::module_inception)]
-			pub mod $module{
-				use js::{function::Rest, Ctx, Exception, Result};
+			mod $module{
+				use super::*;
 
+				#[js::class]
+				#[derive(Trace)]
 				pub struct $name;
+
+				#[js::methods]
 				impl $name {
-					#[quickjs(constructor)]
-					pub fn new(ctx: Ctx, _arg: Rest<()>) -> Result<Self> {
+					#[qjs(constructor)]
+					pub fn new(ctx: Ctx<'_>) -> Result<Self> {
 						Err(Exception::throw_internal(
-								ctx,
-								concat!(
-									"The '",
-									stringify!($name),
-									"' class is not available in this build of SurrealDB. In order to use '",
-									stringify!($name),
-									"', enable the 'http' feature."
-								),
-								))
+							&ctx,
+							concat!(
+								"The '",
+								stringify!($name),
+								"' class is not available in this build of SurrealDB. In order to use '",
+								stringify!($name),
+								"', enable the 'http' feature."
+							),
+						))
 					}
 				}
-
 			}
 		)*
 	};
diff --git a/lib/src/fnc/script/from.rs b/lib/src/fnc/script/from.rs
index 55a5363c..10fc6a00 100644
--- a/lib/src/fnc/script/from.rs
+++ b/lib/src/fnc/script/from.rs
@@ -5,6 +5,7 @@ use crate::sql::object::Object;
 use crate::sql::value::Value;
 use crate::sql::Id;
 use chrono::{TimeZone, Utc};
+use js::prelude::This;
 use js::Ctx;
 use js::Error;
 use js::Exception;
@@ -20,7 +21,7 @@ fn check_nul(s: &str) -> Result<(), Error> {
 }
 
 impl<'js> FromJs<'js> for Value {
-	fn from_js(ctx: Ctx<'js>, val: js::Value<'js>) -> Result<Self, Error> {
+	fn from_js(ctx: &Ctx<'js>, val: js::Value<'js>) -> Result<Self, Error> {
 		match val {
 			val if val.type_name() == "null" => Ok(Value::Null),
 			val if val.type_name() == "undefined" => Ok(Value::None),
@@ -49,14 +50,15 @@ impl<'js> FromJs<'js> for Value {
 				// Check to see if this object is an error
 				if v.is_error() {
 					let e: String = v.get("message")?;
-					let (Ok(e) | Err(e)) = Exception::from_message(ctx, &e).map(|x| x.throw());
+					let (Ok(e) | Err(e)) =
+						Exception::from_message(ctx.clone(), &e).map(|x| x.throw());
 					return Err(e);
 				}
 				// Check to see if this object is a record
-				if (v).instance_of::<classes::record::record::Record>() {
-					let v = v.into_instance::<classes::record::record::Record>().unwrap();
+				if (v).instance_of::<classes::record::Record>() {
+					let v = v.into_class::<classes::record::Record>().unwrap();
 					let borrow = v.borrow();
-					let v: &classes::record::record::Record = &borrow;
+					let v: &classes::record::Record = &borrow;
 					check_nul(&v.value.tb)?;
 					if let Id::String(s) = &v.value.id {
 						check_nul(s)?;
@@ -64,20 +66,20 @@ impl<'js> FromJs<'js> for Value {
 					return Ok(v.value.clone().into());
 				}
 				// Check to see if this object is a duration
-				if (v).instance_of::<classes::duration::duration::Duration>() {
-					let v = v.into_instance::<classes::duration::duration::Duration>().unwrap();
+				if (v).instance_of::<classes::duration::Duration>() {
+					let v = v.into_class::<classes::duration::Duration>().unwrap();
 					let borrow = v.borrow();
-					let v: &classes::duration::duration::Duration = &borrow;
+					let v: &classes::duration::Duration = &borrow;
 					return match &v.value {
 						Some(v) => Ok(v.clone().into()),
 						None => Ok(Value::None),
 					};
 				}
 				// Check to see if this object is a uuid
-				if (v).instance_of::<classes::uuid::uuid::Uuid>() {
-					let v = v.into_instance::<classes::uuid::uuid::Uuid>().unwrap();
+				if (v).instance_of::<classes::uuid::Uuid>() {
+					let v = v.into_class::<classes::uuid::Uuid>().unwrap();
 					let borrow = v.borrow();
-					let v: &classes::uuid::uuid::Uuid = &borrow;
+					let v: &classes::uuid::Uuid = &borrow;
 					return match &v.value {
 						Some(v) => Ok(v.clone().into()),
 						None => Ok(Value::None),
@@ -87,7 +89,7 @@ impl<'js> FromJs<'js> for Value {
 				let date: js::Object = ctx.globals().get("Date")?;
 				if (v).is_instance_of(&date) {
 					let f: js::Function = v.get("getTime")?;
-					let m: i64 = f.call((js::prelude::This(v),))?;
+					let m: i64 = f.call((This(v),))?;
 					let d = Utc.timestamp_millis_opt(m).unwrap();
 					return Ok(Datetime::from(d).into());
 				}
diff --git a/lib/src/fnc/script/globals/console.rs b/lib/src/fnc/script/globals/console.rs
index 7ec7fda5..b681ea78 100644
--- a/lib/src/fnc/script/globals/console.rs
+++ b/lib/src/fnc/script/globals/console.rs
@@ -1,32 +1,44 @@
-#[js::bind(object, public)]
-#[quickjs(rename = "console")]
-#[allow(clippy::module_inception)]
-pub mod console {
-	// Specify the imports
-	use crate::sql::value::Value;
-	use js::prelude::Rest;
-	/// Log the input values as INFO
-	pub fn log(args: Rest<Value>) {
-		info!("{}", args.iter().map(|v| v.to_raw_string()).collect::<Vec<String>>().join(" "));
-	}
-	/// Log the input values as INFO
-	pub fn info(args: Rest<Value>) {
-		info!("{}", args.iter().map(|v| v.to_raw_string()).collect::<Vec<String>>().join(" "));
-	}
-	/// Log the input values as WARN
-	pub fn warn(args: Rest<Value>) {
-		warn!("{}", args.iter().map(|v| v.to_raw_string()).collect::<Vec<String>>().join(" "));
-	}
-	/// Log the input values as ERROR
-	pub fn error(args: Rest<Value>) {
-		error!("{}", args.iter().map(|v| v.to_raw_string()).collect::<Vec<String>>().join(" "));
-	}
-	/// Log the input values as DEBUG
-	pub fn debug(args: Rest<Value>) {
-		debug!("{}", args.iter().map(|v| v.to_raw_string()).collect::<Vec<String>>().join(" "));
-	}
-	/// Log the input values as TRACE
-	pub fn trace(args: Rest<Value>) {
-		trace!("{}", args.iter().map(|v| v.to_raw_string()).collect::<Vec<String>>().join(" "));
-	}
+// Specify the imports
+use crate::sql::value::Value;
+use js::{prelude::Rest, Ctx, Object, Result};
+/// Log the input values as INFO
+#[js::function]
+pub fn log(args: Rest<Value>) {
+	info!("{}", args.iter().map(|v| v.to_raw_string()).collect::<Vec<String>>().join(" "));
+}
+/// Log the input values as INFO
+#[js::function]
+pub fn info(args: Rest<Value>) {
+	info!("{}", args.iter().map(|v| v.to_raw_string()).collect::<Vec<String>>().join(" "));
+}
+/// Log the input values as WARN
+#[js::function]
+pub fn warn(args: Rest<Value>) {
+	warn!("{}", args.iter().map(|v| v.to_raw_string()).collect::<Vec<String>>().join(" "));
+}
+/// Log the input values as ERROR
+#[js::function]
+pub fn error(args: Rest<Value>) {
+	error!("{}", args.iter().map(|v| v.to_raw_string()).collect::<Vec<String>>().join(" "));
+}
+/// Log the input values as DEBUG
+#[js::function]
+pub fn debug(args: Rest<Value>) {
+	debug!("{}", args.iter().map(|v| v.to_raw_string()).collect::<Vec<String>>().join(" "));
+}
+/// Log the input values as TRACE
+#[js::function]
+pub fn trace(args: Rest<Value>) {
+	trace!("{}", args.iter().map(|v| v.to_raw_string()).collect::<Vec<String>>().join(" "));
+}
+
+pub fn console<'js>(ctx: &Ctx<'js>) -> Result<Object<'js>> {
+	let console = Object::new(ctx.clone())?;
+	console.set("log", js_log)?;
+	console.set("info", js_info)?;
+	console.set("warn", js_warn)?;
+	console.set("error", js_error)?;
+	console.set("debug", js_debug)?;
+	console.set("trace", js_trace)?;
+	Ok(console)
 }
diff --git a/lib/src/fnc/script/into.rs b/lib/src/fnc/script/into.rs
index 1b999c0b..495df24f 100644
--- a/lib/src/fnc/script/into.rs
+++ b/lib/src/fnc/script/into.rs
@@ -12,58 +12,58 @@ use js::Object;
 use js::Undefined;
 
 impl<'js> IntoJs<'js> for Value {
-	fn into_js(self, ctx: Ctx<'js>) -> Result<js::Value<'js>, Error> {
+	fn into_js(self, ctx: &Ctx<'js>) -> Result<js::Value<'js>, Error> {
 		(&self).into_js(ctx)
 	}
 }
 
 impl<'js> IntoJs<'js> for &Value {
-	fn into_js(self, ctx: Ctx<'js>) -> Result<js::Value<'js>, Error> {
+	fn into_js(self, ctx: &Ctx<'js>) -> Result<js::Value<'js>, Error> {
 		match self {
 			Value::Null => Null.into_js(ctx),
 			Value::None => Undefined.into_js(ctx),
-			Value::Bool(boolean) => Ok(js::Value::new_bool(ctx, *boolean)),
-			Value::Strand(v) => js::String::from_str(ctx, v)?.into_js(ctx),
-			Value::Number(Number::Int(v)) => Ok(js::Value::new_int(ctx, *v as i32)),
-			Value::Number(Number::Float(v)) => Ok(js::Value::new_float(ctx, *v)),
+			Value::Bool(boolean) => Ok(js::Value::new_bool(ctx.clone(), *boolean)),
+			Value::Strand(v) => js::String::from_str(ctx.clone(), v)?.into_js(ctx),
+			Value::Number(Number::Int(v)) => Ok(js::Value::new_int(ctx.clone(), *v as i32)),
+			Value::Number(Number::Float(v)) => Ok(js::Value::new_float(ctx.clone(), *v)),
 			&Value::Number(Number::Decimal(v)) => match decimal_is_integer(&v) {
-				true => Ok(js::Value::new_int(ctx, v.try_into().unwrap_or_default())),
-				false => Ok(js::Value::new_float(ctx, v.try_into().unwrap_or_default())),
+				true => Ok(js::Value::new_int(ctx.clone(), v.try_into().unwrap_or_default())),
+				false => Ok(js::Value::new_float(ctx.clone(), v.try_into().unwrap_or_default())),
 			},
 			Value::Datetime(v) => {
-				let date: js::Function = ctx.globals().get("Date")?;
+				let date: js::function::Constructor = ctx.globals().get("Date")?;
 				date.construct((v.0.timestamp_millis(),))
 			}
-			Value::Thing(v) => Ok(Class::<classes::record::record::Record>::instance(
-				ctx,
-				classes::record::record::Record {
+			Value::Thing(v) => Ok(Class::<classes::record::Record>::instance(
+				ctx.clone(),
+				classes::record::Record {
 					value: v.to_owned(),
 				},
 			)?
 			.into_value()),
-			Value::Duration(v) => Ok(Class::<classes::duration::duration::Duration>::instance(
-				ctx,
-				classes::duration::duration::Duration {
+			Value::Duration(v) => Ok(Class::<classes::duration::Duration>::instance(
+				ctx.clone(),
+				classes::duration::Duration {
 					value: Some(v.to_owned()),
 				},
 			)?
 			.into_value()),
-			Value::Uuid(v) => Ok(Class::<classes::uuid::uuid::Uuid>::instance(
-				ctx,
-				classes::uuid::uuid::Uuid {
+			Value::Uuid(v) => Ok(Class::<classes::uuid::Uuid>::instance(
+				ctx.clone(),
+				classes::uuid::Uuid {
 					value: Some(v.to_owned()),
 				},
 			)?
 			.into_value()),
 			Value::Array(v) => {
-				let x = Array::new(ctx)?;
+				let x = Array::new(ctx.clone())?;
 				for (i, v) in v.iter().enumerate() {
 					x.set(i, v)?;
 				}
 				x.into_js(ctx)
 			}
 			Value::Object(v) => {
-				let x = Object::new(ctx)?;
+				let x = Object::new(ctx.clone())?;
 				for (k, v) in v.iter() {
 					x.set(k, v)?;
 				}
diff --git a/lib/src/fnc/script/main.rs b/lib/src/fnc/script/main.rs
index 6f837d94..08ec697f 100644
--- a/lib/src/fnc/script/main.rs
+++ b/lib/src/fnc/script/main.rs
@@ -50,23 +50,24 @@ pub async fn run(
 
 	// Attempt to execute the script
 	async_with!(ctx => |ctx|{
-		let res = async move {
+		let res = async{
 			// register all classes to the runtime.
 			// Get the context global object
 			let global = ctx.globals();
 			// Register the surrealdb module as a global object
 			global.set(
 				"surrealdb",
-				Module::evaluate_def::<modules::surrealdb::Package, _>(ctx, "surrealdb")?
+				Module::evaluate_def::<modules::surrealdb::Package, _>(ctx.clone(), "surrealdb")?
 					.get::<_, js::Value>("default")?,
 			)?;
-			fetch::register(ctx)?;
+			fetch::register(&ctx)?;
+			let console = globals::console::console(&ctx)?;
 			// Register the console function to the globals
-			global.init_def::<globals::console::Console>()?;
+			global.set("console",console)?;
 			// Register the special SurrealDB types as classes
-			classes::init(ctx)?;
+			classes::init(&ctx)?;
 			// Attempt to compile the script
-			let res = ctx.compile("script", src)?;
+			let res = ctx.clone().compile("script", src)?;
 			// Attempt to fetch the main export
 			let fnc = res.get::<_, Function>("default")?;
 			// Extract the doc if any
@@ -76,7 +77,7 @@ pub async fn run(
 			promise.await
 		}.await;
 
-		res.catch(ctx).map_err(Error::from)
+		res.catch(&ctx).map_err(Error::from)
 	})
 	.await
 }
diff --git a/lib/src/fnc/script/modules/mod.rs b/lib/src/fnc/script/modules/mod.rs
index 4e37c88a..879241f7 100644
--- a/lib/src/fnc/script/modules/mod.rs
+++ b/lib/src/fnc/script/modules/mod.rs
@@ -54,8 +54,8 @@ macro_rules! impl_module_def {
 				Ok(())
 			}
 
-			fn evaluate<'js>(ctx: js::Ctx<'js>, exports: &mut js::module::Exports<'js>) -> js::Result<()> {
-				let default = js::Object::new(ctx)?;
+			fn evaluate<'js>(ctx: &js::Ctx<'js>, exports: &mut js::module::Exports<'js>) -> js::Result<()> {
+				let default = js::Object::new(ctx.clone())?;
 				$(
 					exports.export($name, crate::fnc::script::modules::impl_module_def!(ctx, $path, $name, $action, $($wrapper)?))?;
 					default.set($name, crate::fnc::script::modules::impl_module_def!(ctx, $path, $name, $action, $($wrapper)?))?;
diff --git a/lib/src/fnc/script/modules/os.rs b/lib/src/fnc/script/modules/os.rs
index 49162f66..1e6b4b9b 100644
--- a/lib/src/fnc/script/modules/os.rs
+++ b/lib/src/fnc/script/modules/os.rs
@@ -1,13 +1,31 @@
-#[js::bind(module, public)]
-#[quickjs(bare)]
-#[allow(non_upper_case_globals)]
-pub mod package {
-	/// Get the target system architecture
-	pub fn arch() -> &'static str {
-		crate::env::arch()
+use js::{
+	module::{Declarations, Exports, ModuleDef},
+	Result,
+};
+
+/// Get the target system architecture
+#[js::function]
+pub fn arch() -> &'static str {
+	crate::env::arch()
+}
+/// Get the target operating system
+#[js::function]
+pub fn platform() -> &'static str {
+	crate::env::os()
+}
+
+pub struct Package;
+
+impl ModuleDef for Package {
+	fn declare(declare: &mut Declarations) -> Result<()> {
+		declare.declare("arch")?;
+		declare.declare("platform")?;
+		Ok(())
 	}
-	/// Get the target operating system
-	pub fn platform() -> &'static str {
-		crate::env::os()
+
+	fn evaluate<'js>(_ctx: &js::Ctx<'js>, exports: &mut Exports<'js>) -> Result<()> {
+		exports.export("arch", js_arch)?;
+		exports.export("platform", js_platform)?;
+		Ok(())
 	}
 }
diff --git a/lib/src/fnc/script/modules/surrealdb/mod.rs b/lib/src/fnc/script/modules/surrealdb/mod.rs
index 7e38b6b2..26fb2796 100644
--- a/lib/src/fnc/script/modules/surrealdb/mod.rs
+++ b/lib/src/fnc/script/modules/surrealdb/mod.rs
@@ -12,9 +12,9 @@ impl_module_def!(
 	"version" => (env!("CARGO_PKG_VERSION"))
 );
 
-fn pkg<'js, D>(ctx: Ctx<'js>, name: &str) -> Result<Value<'js>>
+fn pkg<'js, D>(ctx: &Ctx<'js>, name: &str) -> Result<Value<'js>>
 where
 	D: ModuleDef,
 {
-	Module::evaluate_def::<D, _>(ctx, name)?.get::<_, js::Value>("default")
+	Module::evaluate_def::<D, _>(ctx.clone(), name)?.get::<_, js::Value>("default")
 }