From 715c87fac5f8e46185d1fb56177cf636fcf48eee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Fern=C3=A1ndez?= Date: Wed, 29 Nov 2023 14:21:17 +0100 Subject: [PATCH] Fix panic kills running engine in query-engine-tests (#4499) * I didn't have wasm-pack installed, let's fix that * Update wasm-bindgen-futures to 0.4.39 This includes https://github.com/rustwasm/wasm-bindgen/issues/3203 that use queueMicrotask to transalate spawn_local rust code. This has fixed https://github.com/rustwasm/wasm-bindgen/issues/2392 which was an issue about not being able to catch async wasm traps. This might (or not) have an effect on the issue we are trying to solve in here. * Revert "Update wasm-bindgen-futures to 0.4.39" This reverts commit 9a494dc1f5f2250ddf2c3f71b52ec2221e6c93cb. * Restart executor when it dies * Document Restartable * Remove async_panic_to_js_error in WASM query engine * Rename p -> process * Use tokio::sync::RwLock rather than futures::lock::Mutex * Better error messaging * Fixing clippy * Exclude unit tests for wasm32 when compiling the binary for other architectures --- libs/crosstarget-utils/src/common.rs | 16 +- libs/crosstarget-utils/src/native/spawn.rs | 2 +- libs/crosstarget-utils/src/native/time.rs | 4 +- libs/crosstarget-utils/src/wasm/time.rs | 2 +- .../query-tests-setup/src/connector_tag/js.rs | 2 +- .../src/connector_tag/js/external_process.rs | 89 +++++- .../query-tests-setup/src/lib.rs | 4 +- query-engine/driver-adapters/tests/wasm.rs | 2 +- query-engine/query-engine-wasm/Cargo.toml | 2 +- query-engine/query-engine-wasm/build.sh | 7 + .../query-engine-wasm/src/wasm/engine.rs | 293 ++++++++---------- 11 files changed, 218 insertions(+), 205 deletions(-) diff --git a/libs/crosstarget-utils/src/common.rs b/libs/crosstarget-utils/src/common.rs index 3afce64c6714..92a1d5094e89 100644 --- a/libs/crosstarget-utils/src/common.rs +++ b/libs/crosstarget-utils/src/common.rs @@ -1,13 +1,7 @@ use std::fmt::Display; #[derive(Debug)] -pub struct SpawnError {} - -impl SpawnError { - pub fn new() -> Self { - SpawnError {} - } -} +pub struct SpawnError; impl Display for SpawnError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -18,13 +12,7 @@ impl Display for SpawnError { impl std::error::Error for SpawnError {} #[derive(Debug)] -pub struct TimeoutError {} - -impl TimeoutError { - pub fn new() -> Self { - TimeoutError {} - } -} +pub struct TimeoutError; impl Display for TimeoutError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/libs/crosstarget-utils/src/native/spawn.rs b/libs/crosstarget-utils/src/native/spawn.rs index b0d541258c2a..8a8360c580fa 100644 --- a/libs/crosstarget-utils/src/native/spawn.rs +++ b/libs/crosstarget-utils/src/native/spawn.rs @@ -7,5 +7,5 @@ where F: Future + 'static + Send, F::Output: Send + 'static, { - tokio::spawn(future).await.map_err(|_| SpawnError::new()) + tokio::spawn(future).await.map_err(|_| SpawnError) } diff --git a/libs/crosstarget-utils/src/native/time.rs b/libs/crosstarget-utils/src/native/time.rs index e222e08cf628..3b154a27565c 100644 --- a/libs/crosstarget-utils/src/native/time.rs +++ b/libs/crosstarget-utils/src/native/time.rs @@ -21,7 +21,7 @@ impl ElapsedTimeCounter { } } -pub async fn sleep(duration: Duration) -> () { +pub async fn sleep(duration: Duration) { tokio::time::sleep(duration).await } @@ -31,5 +31,5 @@ where { let result = tokio::time::timeout(duration, future).await; - result.map_err(|_| TimeoutError::new()) + result.map_err(|_| TimeoutError) } diff --git a/libs/crosstarget-utils/src/wasm/time.rs b/libs/crosstarget-utils/src/wasm/time.rs index e983aa5678a6..6f14ac001ee8 100644 --- a/libs/crosstarget-utils/src/wasm/time.rs +++ b/libs/crosstarget-utils/src/wasm/time.rs @@ -50,7 +50,7 @@ where { tokio::select! { result = future => Ok(result), - _ = sleep(duration) => Err(TimeoutError::new()) + _ = sleep(duration) => Err(TimeoutError) } } diff --git a/query-engine/connector-test-kit-rs/query-tests-setup/src/connector_tag/js.rs b/query-engine/connector-test-kit-rs/query-tests-setup/src/connector_tag/js.rs index 2ec8513baeda..c852924bbf69 100644 --- a/query-engine/connector-test-kit-rs/query-tests-setup/src/connector_tag/js.rs +++ b/query-engine/connector-test-kit-rs/query-tests-setup/src/connector_tag/js.rs @@ -3,7 +3,7 @@ mod external_process; use super::*; use external_process::*; use serde::de::DeserializeOwned; -use std::{collections::HashMap, sync::atomic::AtomicU64}; +use std::sync::atomic::AtomicU64; use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; pub(crate) async fn executor_process_request( diff --git a/query-engine/connector-test-kit-rs/query-tests-setup/src/connector_tag/js/external_process.rs b/query-engine/connector-test-kit-rs/query-tests-setup/src/connector_tag/js/external_process.rs index 1abfedbaf8ee..912a5e6d8abf 100644 --- a/query-engine/connector-test-kit-rs/query-tests-setup/src/connector_tag/js/external_process.rs +++ b/query-engine/connector-test-kit-rs/query-tests-setup/src/connector_tag/js/external_process.rs @@ -1,8 +1,13 @@ use super::*; use once_cell::sync::Lazy; use serde::de::DeserializeOwned; -use std::{fmt::Display, io::Write as _, sync::atomic::Ordering}; -use tokio::sync::{mpsc, oneshot}; +use std::{ + error::Error as StdError, + fmt::Display, + io::Write as _, + sync::{atomic::Ordering, Arc}, +}; +use tokio::sync::{mpsc, oneshot, RwLock}; type Result = std::result::Result>; @@ -29,6 +34,17 @@ fn exit_with_message(status_code: i32, message: &str) -> ! { } impl ExecutorProcess { + fn spawn() -> ExecutorProcess { + match std::thread::spawn(ExecutorProcess::new).join() { + Ok(Ok(process)) => process, + Ok(Err(err)) => exit_with_message(1, &format!("Failed to start node process. Details: {err}")), + Err(err) => { + let err = err.downcast_ref::().map(ToOwned::to_owned).unwrap_or_default(); + exit_with_message(1, &format!("Panic while trying to start node process.\nDetails: {err}")) + } + } + } + fn new() -> Result { let (sender, receiver) = mpsc::channel::(300); @@ -81,15 +97,50 @@ impl ExecutorProcess { } } -pub(super) static EXTERNAL_PROCESS: Lazy = - Lazy::new(|| match std::thread::spawn(ExecutorProcess::new).join() { - Ok(Ok(process)) => process, - Ok(Err(err)) => exit_with_message(1, &format!("Failed to start node process. Details: {err}")), - Err(err) => { - let err = err.downcast_ref::().map(ToOwned::to_owned).unwrap_or_default(); - exit_with_message(1, &format!("Panic while trying to start node process.\nDetails: {err}")) +/// Wraps an ExecutorProcess allowing for restarting it. +/// +/// A node process can die for a number of reasons, being one that any `panic!` occurring in Rust +/// asynchronous code are translated to an abort trap by wasm-bindgen, which kills the node process. +#[derive(Clone)] +pub(crate) struct RestartableExecutorProcess { + process: Arc>, +} + +impl RestartableExecutorProcess { + fn new() -> Self { + Self { + process: Arc::new(RwLock::new(ExecutorProcess::spawn())), } - }); + } + + async fn restart(&self) { + let mut process = self.process.write().await; + *process = ExecutorProcess::spawn(); + } + + pub(crate) async fn request(&self, method: &str, params: serde_json::Value) -> Result { + let p = self.process.read().await; + p.request(method, params).await + } +} + +struct ExecutorProcessDiedError; + +impl fmt::Debug for ExecutorProcessDiedError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "The external test executor process died") + } +} + +impl Display for ExecutorProcessDiedError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self, f) + } +} + +impl StdError for ExecutorProcessDiedError {} + +pub(super) static EXTERNAL_PROCESS: Lazy = Lazy::new(RestartableExecutorProcess::new); type ReqImpl = ( jsonrpc_core::MethodCall, @@ -122,8 +173,7 @@ fn start_rpc_thread(mut receiver: mpsc::Receiver) -> Result<()> { let mut stdout = BufReader::new(process.stdout.unwrap()).lines(); let mut stdin = process.stdin.unwrap(); - let mut pending_requests: HashMap>> = - HashMap::new(); + let mut last_pending_request: Option<(jsonrpc_core::Id, oneshot::Sender>)> = None; loop { tokio::select! { @@ -137,7 +187,11 @@ fn start_rpc_thread(mut receiver: mpsc::Receiver) -> Result<()> { { match serde_json::from_str::(&line) { Ok(response) => { - let sender = pending_requests.remove(response.id()).unwrap(); + let (id, sender) = last_pending_request.take().expect("got a response from the external process, but there was no pending request"); + if &id != response.id() { + unreachable!("got a response from the external process, but the id didn't match. Are you running with cargo tests with `--test-threads=1`"); + } + match response { jsonrpc_core::Output::Success(success) => { // The other end may be dropped if the whole @@ -159,7 +213,12 @@ fn start_rpc_thread(mut receiver: mpsc::Receiver) -> Result<()> { } Ok(None) => // end of the stream { - exit_with_message(1, "child node process stdout closed") + tracing::error!("Error when reading from child node process. Process might have exited. Restarting..."); + if let Some((_, sender)) = last_pending_request.take() { + sender.send(Err(Box::new(ExecutorProcessDiedError))).unwrap(); + } + EXTERNAL_PROCESS.restart().await; + break; } Err(err) => // log it { @@ -174,7 +233,7 @@ fn start_rpc_thread(mut receiver: mpsc::Receiver) -> Result<()> { exit_with_message(1, "The json-rpc client channel was closed"); } Some((request, response_sender)) => { - pending_requests.insert(request.id.clone(), response_sender); + last_pending_request = Some((request.id.clone(), response_sender)); let mut req = serde_json::to_vec(&request).unwrap(); req.push(b'\n'); stdin.write_all(&req).await.unwrap(); diff --git a/query-engine/connector-test-kit-rs/query-tests-setup/src/lib.rs b/query-engine/connector-test-kit-rs/query-tests-setup/src/lib.rs index b216e44b9d12..d7dbd0f53897 100644 --- a/query-engine/connector-test-kit-rs/query-tests-setup/src/lib.rs +++ b/query-engine/connector-test-kit-rs/query-tests-setup/src/lib.rs @@ -289,7 +289,9 @@ fn run_connector_test_impl( .unwrap(); let schema_id = runner.schema_id(); - test_fn(runner).await.unwrap(); + if let Err(err) = test_fn(runner).await { + panic!("💥 Test failed due to an error: {err:?}"); + } crate::teardown_project(&datamodel, db_schemas, schema_id) .await diff --git a/query-engine/driver-adapters/tests/wasm.rs b/query-engine/driver-adapters/tests/wasm.rs index c40529978c9f..8f3aa30f7335 100644 --- a/query-engine/driver-adapters/tests/wasm.rs +++ b/query-engine/driver-adapters/tests/wasm.rs @@ -1,6 +1,6 @@ +#![cfg(target_os = "wasm32")] use wasm_bindgen_test::*; -// use driver_adapters::types::ColumnType; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; use tsify::Tsify; diff --git a/query-engine/query-engine-wasm/Cargo.toml b/query-engine/query-engine-wasm/Cargo.toml index 06738c456709..175f47a01ae5 100644 --- a/query-engine/query-engine-wasm/Cargo.toml +++ b/query-engine/query-engine-wasm/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [lib] doc = false crate-type = ["cdylib"] -name = "query_engine_wasm" +name = "query_engine" [dependencies] diff --git a/query-engine/query-engine-wasm/build.sh b/query-engine/query-engine-wasm/build.sh index 13a7c13e89ec..10e3008912f0 100755 --- a/query-engine/query-engine-wasm/build.sh +++ b/query-engine/query-engine-wasm/build.sh @@ -21,6 +21,13 @@ else BUILD_PROFILE="--dev" fi +# Check if wasm-pack is installed +if ! command -v wasm-pack &> /dev/null +then + echo "wasm-pack could not be found, installing now..." + # Install wasm-pack + curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh +fi wasm-pack build $BUILD_PROFILE --target $OUT_TARGET sed -i '' 's/name = "query_engine"/name = "query_engine_wasm"/g' Cargo.toml diff --git a/query-engine/query-engine-wasm/src/wasm/engine.rs b/query-engine/query-engine-wasm/src/wasm/engine.rs index 1b3b51653c7a..9a326e6547a9 100644 --- a/query-engine/query-engine-wasm/src/wasm/engine.rs +++ b/query-engine/query-engine-wasm/src/wasm/engine.rs @@ -6,7 +6,6 @@ use crate::{ logger::{LogCallback, Logger}, }; use driver_adapters::JsObject; -use futures::FutureExt; use js_sys::Function as JsFunction; use query_core::{ protocol::EngineProtocol, @@ -19,8 +18,6 @@ use serde::{Deserialize, Serialize}; use serde_json::json; use std::{ collections::{BTreeMap, HashMap}, - future::Future, - panic::AssertUnwindSafe, path::PathBuf, sync::Arc, }; @@ -28,7 +25,6 @@ use tokio::sync::RwLock; use tracing::{field, Instrument, Span}; use tracing_subscriber::filter::LevelFilter; use tsify::Tsify; -use user_facing_errors::Error; use wasm_bindgen::prelude::wasm_bindgen; /// The main query engine used by JS #[wasm_bindgen] @@ -210,95 +206,87 @@ impl QueryEngine { /// Connect to the database, allow queries to be run. #[wasm_bindgen] pub async fn connect(&self, trace: String) -> Result<(), wasm_bindgen::JsError> { - async_panic_to_js_error(async { - let span = tracing::info_span!("prisma:engine:connect"); - - let mut inner = self.inner.write().await; - let builder = inner.as_builder()?; - let arced_schema = Arc::clone(&builder.schema); - let arced_schema_2 = Arc::clone(&builder.schema); - - let url = { - let data_source = builder - .schema - .configuration - .datasources - .first() - .ok_or_else(|| ApiError::configuration("No valid data source found"))?; - data_source - .load_url_with_config_dir(&builder.config_dir, |key| builder.env.get(key).map(ToString::to_string)) - .map_err(|err| crate::error::ApiError::Conversion(err, builder.schema.db.source().to_owned()))? - }; - - let engine = async move { - // We only support one data source & generator at the moment, so take the first one (default not exposed yet). - let data_source = arced_schema - .configuration - .datasources - .first() - .ok_or_else(|| ApiError::configuration("No valid data source found"))?; - - let preview_features = arced_schema.configuration.preview_features(); - - let executor = load_executor(self.connector_mode, data_source, preview_features, &url).await?; - let connector = executor.primary_connector(); - - let conn_span = tracing::info_span!( - "prisma:engine:connection", - user_facing = true, - "db.type" = connector.name(), - ); - - connector.get_connection().instrument(conn_span).await?; - - let query_schema_span = tracing::info_span!("prisma:engine:schema"); - let query_schema = query_schema_span.in_scope(|| schema::build(arced_schema_2, true)); - - Ok(ConnectedEngine { - schema: builder.schema.clone(), - query_schema: Arc::new(query_schema), - executor, - config_dir: builder.config_dir.clone(), - env: builder.env.clone(), - engine_protocol: builder.engine_protocol, - }) as crate::Result - } - .instrument(span) - .await?; - - *inner = Inner::Connected(engine); + let span = tracing::info_span!("prisma:engine:connect"); + + let mut inner = self.inner.write().await; + let builder = inner.as_builder()?; + let arced_schema = Arc::clone(&builder.schema); + let arced_schema_2 = Arc::clone(&builder.schema); + + let url = { + let data_source = builder + .schema + .configuration + .datasources + .first() + .ok_or_else(|| ApiError::configuration("No valid data source found"))?; + data_source + .load_url_with_config_dir(&builder.config_dir, |key| builder.env.get(key).map(ToString::to_string)) + .map_err(|err| crate::error::ApiError::Conversion(err, builder.schema.db.source().to_owned()))? + }; - Ok(()) - }) + let engine = async move { + // We only support one data source & generator at the moment, so take the first one (default not exposed yet). + let data_source = arced_schema + .configuration + .datasources + .first() + .ok_or_else(|| ApiError::configuration("No valid data source found"))?; + + let preview_features = arced_schema.configuration.preview_features(); + + let executor = load_executor(self.connector_mode, data_source, preview_features, &url).await?; + let connector = executor.primary_connector(); + + let conn_span = tracing::info_span!( + "prisma:engine:connection", + user_facing = true, + "db.type" = connector.name(), + ); + + connector.get_connection().instrument(conn_span).await?; + + let query_schema_span = tracing::info_span!("prisma:engine:schema"); + let query_schema = query_schema_span.in_scope(|| schema::build(arced_schema_2, true)); + + Ok(ConnectedEngine { + schema: builder.schema.clone(), + query_schema: Arc::new(query_schema), + executor, + config_dir: builder.config_dir.clone(), + env: builder.env.clone(), + engine_protocol: builder.engine_protocol, + }) as crate::Result + } + .instrument(span) .await?; + *inner = Inner::Connected(engine); + Ok(()) } /// Disconnect and drop the core. Can be reconnected later with `#connect`. #[wasm_bindgen] pub async fn disconnect(&self, trace: String) -> Result<(), wasm_bindgen::JsError> { - async_panic_to_js_error(async { - let span = tracing::info_span!("prisma:engine:disconnect"); + let span = tracing::info_span!("prisma:engine:disconnect"); - async { - let mut inner = self.inner.write().await; - let engine = inner.as_engine()?; + async { + let mut inner = self.inner.write().await; + let engine = inner.as_engine()?; - let builder = EngineBuilder { - schema: engine.schema.clone(), - config_dir: engine.config_dir.clone(), - env: engine.env.clone(), - engine_protocol: engine.engine_protocol(), - }; + let builder = EngineBuilder { + schema: engine.schema.clone(), + config_dir: engine.config_dir.clone(), + env: engine.env.clone(), + engine_protocol: engine.engine_protocol(), + }; - *inner = Inner::Builder(builder); + *inner = Inner::Builder(builder); - Ok(()) - } - .instrument(span) - .await - }) + Ok(()) + } + .instrument(span) .await } @@ -310,122 +298,104 @@ impl QueryEngine { trace: String, tx_id: Option, ) -> Result { - async_panic_to_js_error(async { - let inner = self.inner.read().await; - let engine = inner.as_engine()?; + let inner = self.inner.read().await; + let engine = inner.as_engine()?; - let query = RequestBody::try_from_str(&body, engine.engine_protocol())?; + let query = RequestBody::try_from_str(&body, engine.engine_protocol())?; - async move { - let span = if tx_id.is_none() { - tracing::info_span!("prisma:engine", user_facing = true) - } else { - Span::none() - }; + async move { + let span = if tx_id.is_none() { + tracing::info_span!("prisma:engine", user_facing = true) + } else { + Span::none() + }; - let handler = RequestHandler::new(engine.executor(), engine.query_schema(), engine.engine_protocol()); - let response = handler - .handle(query, tx_id.map(TxId::from), None) - .instrument(span) - .await; + let handler = RequestHandler::new(engine.executor(), engine.query_schema(), engine.engine_protocol()); + let response = handler + .handle(query, tx_id.map(TxId::from), None) + .instrument(span) + .await; - Ok(serde_json::to_string(&response)?) - } - .await - }) + Ok(serde_json::to_string(&response)?) + } .await } /// If connected, attempts to start a transaction in the core and returns its ID. #[wasm_bindgen(js_name = startTransaction)] pub async fn start_transaction(&self, input: String, trace: String) -> Result { - async_panic_to_js_error(async { - let inner = self.inner.read().await; - let engine = inner.as_engine()?; - - async move { - let span = tracing::info_span!("prisma:engine:itx_runner", user_facing = true, itx_id = field::Empty); - - let tx_opts: TransactionOptions = serde_json::from_str(&input)?; - match engine - .executor() - .start_tx(engine.query_schema().clone(), engine.engine_protocol(), tx_opts) - .instrument(span) - .await - { - Ok(tx_id) => Ok(json!({ "id": tx_id.to_string() }).to_string()), - Err(err) => Ok(map_known_error(err)?), - } + let inner = self.inner.read().await; + let engine = inner.as_engine()?; + + async move { + let span = tracing::info_span!("prisma:engine:itx_runner", user_facing = true, itx_id = field::Empty); + + let tx_opts: TransactionOptions = serde_json::from_str(&input)?; + match engine + .executor() + .start_tx(engine.query_schema().clone(), engine.engine_protocol(), tx_opts) + .instrument(span) + .await + { + Ok(tx_id) => Ok(json!({ "id": tx_id.to_string() }).to_string()), + Err(err) => Ok(map_known_error(err)?), } - .await - }) + } .await } /// If connected, attempts to commit a transaction with id `tx_id` in the core. #[wasm_bindgen(js_name = commitTransaction)] pub async fn commit_transaction(&self, tx_id: String, trace: String) -> Result { - async_panic_to_js_error(async { - let inner = self.inner.read().await; - let engine = inner.as_engine()?; + let inner = self.inner.read().await; + let engine = inner.as_engine()?; - async move { - match engine.executor().commit_tx(TxId::from(tx_id)).await { - Ok(_) => Ok("{}".to_string()), - Err(err) => Ok(map_known_error(err)?), - } + async move { + match engine.executor().commit_tx(TxId::from(tx_id)).await { + Ok(_) => Ok("{}".to_string()), + Err(err) => Ok(map_known_error(err)?), } - .await - }) + } .await } #[wasm_bindgen] pub async fn dmmf(&self, trace: String) -> Result { - async_panic_to_js_error(async { - let inner = self.inner.read().await; - let engine = inner.as_engine()?; + let inner = self.inner.read().await; + let engine = inner.as_engine()?; - let dmmf = dmmf::render_dmmf(&engine.query_schema); + let dmmf = dmmf::render_dmmf(&engine.query_schema); - let json = { - let _span = tracing::info_span!("prisma:engine:dmmf_to_json").entered(); - serde_json::to_string(&dmmf)? - }; + let json = { + let _span = tracing::info_span!("prisma:engine:dmmf_to_json").entered(); + serde_json::to_string(&dmmf)? + }; - Ok(json) - }) - .await + Ok(json) } /// If connected, attempts to roll back a transaction with id `tx_id` in the core. #[wasm_bindgen(js_name = rollbackTransaction)] pub async fn rollback_transaction(&self, tx_id: String, trace: String) -> Result { - async_panic_to_js_error(async { - let inner = self.inner.read().await; - let engine = inner.as_engine()?; + let inner = self.inner.read().await; + let engine = inner.as_engine()?; - async move { - match engine.executor().rollback_tx(TxId::from(tx_id)).await { - Ok(_) => Ok("{}".to_string()), - Err(err) => Ok(map_known_error(err)?), - } + async move { + match engine.executor().rollback_tx(TxId::from(tx_id)).await { + Ok(_) => Ok("{}".to_string()), + Err(err) => Ok(map_known_error(err)?), } - .await - }) + } .await } /// Loads the query schema. Only available when connected. #[wasm_bindgen(js_name = sdlSchema)] pub async fn sdl_schema(&self) -> Result { - async_panic_to_js_error(async move { - let inner = self.inner.read().await; - let engine = inner.as_engine()?; + let inner = self.inner.read().await; + let engine = inner.as_engine()?; - Ok(render_graphql_schema(engine.query_schema())) - }) - .await + Ok(render_graphql_schema(engine.query_schema())) } #[wasm_bindgen] @@ -472,16 +442,3 @@ fn stringify_env_values(origin: serde_json::Value) -> crate::Result(fut: F) -> Result -where - F: Future>, -{ - match AssertUnwindSafe(fut).catch_unwind().await { - Ok(result) => result, - Err(err) => match Error::extract_panic_message(err) { - Some(message) => Err(wasm_bindgen::JsError::new(&format!("PANIC: {message}"))), - None => Err(wasm_bindgen::JsError::new("PANIC: unknown panic")), - }, - } -}