app_bridge 4.1.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,8 @@
1
1
  use std::collections::HashMap;
2
2
  use std::result::Result::Ok;
3
- use wasmtime::component::{Component, Linker};
3
+ use wasmtime::component::{Component, HasSelf, Linker};
4
4
  use wasmtime::{Engine, Result, Store};
5
- use wasmtime_wasi::p2::WasiCtxBuilder;
5
+ use wasmtime_wasi::WasiCtxBuilder;
6
6
 
7
7
  use crate::app_state::AppState;
8
8
  use crate::types::{
@@ -42,6 +42,13 @@ pub mod v4_1 {
42
42
  });
43
43
  }
44
44
 
45
+ pub mod v5 {
46
+ wasmtime::component::bindgen!({
47
+ path: "./wit/v5",
48
+ world: "bridge",
49
+ });
50
+ }
51
+
45
52
  // ============================================================================
46
53
  // Version conversion macro - generates From impls for a version module
47
54
  // ============================================================================
@@ -174,6 +181,15 @@ impl From<v4_1::standout::app::types::ReferenceObject> for ReferenceObject {
174
181
  }
175
182
  }
176
183
 
184
+ impl From<v5::standout::app::types::ReferenceObject> for ReferenceObject {
185
+ fn from(r: v5::standout::app::types::ReferenceObject) -> Self {
186
+ Self {
187
+ reference: r.reference,
188
+ status: r.status,
189
+ }
190
+ }
191
+ }
192
+
177
193
  impl From<&ReferenceObject> for v4_1::standout::app::types::ReferenceObject {
178
194
  fn from(r: &ReferenceObject) -> Self {
179
195
  Self {
@@ -183,6 +199,15 @@ impl From<&ReferenceObject> for v4_1::standout::app::types::ReferenceObject {
183
199
  }
184
200
  }
185
201
 
202
+ impl From<&ReferenceObject> for v5::standout::app::types::ReferenceObject {
203
+ fn from(r: &ReferenceObject) -> Self {
204
+ Self {
205
+ reference: r.reference.clone(),
206
+ status: r.status.clone(),
207
+ }
208
+ }
209
+ }
210
+
186
211
  // Generate conversions for all supported versions
187
212
  impl_error_code_conversion!(v3,);
188
213
  impl_conversions!(v3);
@@ -199,6 +224,13 @@ impl_error_code_conversion!(
199
224
  impl_conversions!(v4_1);
200
225
  impl_app_error_conversion!(v4_1);
201
226
  impl_action_context_conversion_retry!(v4_1);
227
+ impl_error_code_conversion!(
228
+ v5,
229
+ V::RetryWithReference(r) => Self::RetryWithReference(r.into()),
230
+ );
231
+ impl_conversions!(v5);
232
+ impl_app_error_conversion!(v5);
233
+ impl_action_context_conversion_retry!(v5);
202
234
 
203
235
  // ============================================================================
204
236
  // BridgeWrapper - unified interface for all component versions
@@ -210,6 +242,7 @@ pub enum BridgeWrapper {
210
242
  V3(v3::Bridge),
211
243
  V4(v4::Bridge),
212
244
  V4_1(v4_1::Bridge),
245
+ V5(v5::Bridge),
213
246
  }
214
247
 
215
248
  impl BridgeWrapper {
@@ -219,8 +252,13 @@ impl BridgeWrapper {
219
252
  BridgeWrapper::V3(_) => "3.0.0",
220
253
  BridgeWrapper::V4(_) => "4.0.0",
221
254
  BridgeWrapper::V4_1(_) => "4.1.0",
255
+ BridgeWrapper::V5(_) => "5.0.0",
222
256
  }
223
257
  }
258
+
259
+ pub fn connections_supported(&self) -> bool {
260
+ matches!(self, BridgeWrapper::V5(_))
261
+ }
224
262
  }
225
263
 
226
264
  /// Macro to implement a bridge method that works across all versions.
@@ -242,6 +280,10 @@ macro_rules! bridge_method {
242
280
  let r = b.$interface().$method(store)?;
243
281
  Ok(r.map_err(Into::into))
244
282
  }
283
+ BridgeWrapper::V5(b) => {
284
+ let r = b.$interface().$method(store)?;
285
+ Ok(r.map_err(Into::into))
286
+ }
245
287
  }
246
288
  }
247
289
  };
@@ -261,6 +303,10 @@ macro_rules! bridge_method {
261
303
  let r = b.$interface().$method(store, &ctx.into())?;
262
304
  Ok(r.map(Into::into).map_err(Into::into))
263
305
  }
306
+ BridgeWrapper::V5(b) => {
307
+ let r = b.$interface().$method(store, &ctx.into())?;
308
+ Ok(r.map(Into::into).map_err(Into::into))
309
+ }
264
310
  }
265
311
  }
266
312
  };
@@ -280,6 +326,10 @@ macro_rules! bridge_method {
280
326
  let r = b.$interface().$method(store, &ctx.into())?;
281
327
  Ok(r.map(Into::into).map_err(Into::into))
282
328
  }
329
+ BridgeWrapper::V5(b) => {
330
+ let r = b.$interface().$method(store, &ctx.into())?;
331
+ Ok(r.map(Into::into).map_err(Into::into))
332
+ }
283
333
  }
284
334
  }
285
335
  };
@@ -297,6 +347,18 @@ impl BridgeWrapper {
297
347
  bridge_method!(fn call_action_input_schema(&ActionContext) -> Result<String> via standout_app_actions . call_input_schema);
298
348
  bridge_method!(fn call_action_output_schema(&ActionContext) -> Result<String> via standout_app_actions . call_output_schema);
299
349
  bridge_method!(fn call_execute(&ActionContext) -> Result<ActionResponse> via standout_app_actions . call_execute);
350
+
351
+ pub fn call_connection_config(
352
+ &self,
353
+ store: &mut Store<AppState>,
354
+ ) -> Result<String> {
355
+ match self {
356
+ BridgeWrapper::V5(b) => {
357
+ b.standout_app_connections().call_connection_config(store)
358
+ }
359
+ _ => unreachable!("call_connection_config requires connections_supported"),
360
+ }
361
+ }
300
362
  }
301
363
 
302
364
  // ============================================================================
@@ -315,24 +377,23 @@ pub fn build_linker(engine: &Engine) -> Result<Linker<AppState>> {
315
377
 
316
378
  // ---- Version-specific interfaces ----
317
379
  // v3: http + environment
318
- v3::standout::app::http::add_to_linker(&mut linker, |s| s)?;
319
- v3::standout::app::environment::add_to_linker(&mut linker, |s| s)?;
380
+ v3::standout::app::http::add_to_linker::<AppState, HasSelf<AppState>>(&mut linker, |s| s)?;
381
+ v3::standout::app::environment::add_to_linker::<AppState, HasSelf<AppState>>(&mut linker, |s| s)?;
320
382
 
321
383
  // v4: http + environment + file
322
- v4::standout::app::http::add_to_linker(&mut linker, |s| s)?;
323
- v4::standout::app::environment::add_to_linker(&mut linker, |s| s)?;
324
- v4::standout::app::file::add_to_linker(&mut linker, |s| s)?;
384
+ v4::standout::app::http::add_to_linker::<AppState, HasSelf<AppState>>(&mut linker, |s| s)?;
385
+ v4::standout::app::environment::add_to_linker::<AppState, HasSelf<AppState>>(&mut linker, |s| s)?;
386
+ v4::standout::app::file::add_to_linker::<AppState, HasSelf<AppState>>(&mut linker, |s| s)?;
325
387
 
326
388
  // v4.1: http + environment + file
327
- v4_1::standout::app::http::add_to_linker(&mut linker, |s| s)?;
328
- v4_1::standout::app::environment::add_to_linker(&mut linker, |s| s)?;
329
- v4_1::standout::app::file::add_to_linker(&mut linker, |s| s)?;
389
+ v4_1::standout::app::http::add_to_linker::<AppState, HasSelf<AppState>>(&mut linker, |s| s)?;
390
+ v4_1::standout::app::environment::add_to_linker::<AppState, HasSelf<AppState>>(&mut linker, |s| s)?;
391
+ v4_1::standout::app::file::add_to_linker::<AppState, HasSelf<AppState>>(&mut linker, |s| s)?;
330
392
 
331
- // Add new versions here:
332
- // v5::standout::app::http::add_to_linker(&mut linker, |s| s)?;
333
- // v5::standout::app::environment::add_to_linker(&mut linker, |s| s)?;
334
- // v5::standout::app::file::add_to_linker(&mut linker, |s| s)?;
335
- // v5::standout::app::new_feature::add_to_linker(&mut linker, |s| s)?;
393
+ // v5: http + environment + file
394
+ v5::standout::app::http::add_to_linker::<AppState, HasSelf<AppState>>(&mut linker, |s| s)?;
395
+ v5::standout::app::environment::add_to_linker::<AppState, HasSelf<AppState>>(&mut linker, |s| s)?;
396
+ v5::standout::app::file::add_to_linker::<AppState, HasSelf<AppState>>(&mut linker, |s| s)?;
336
397
 
337
398
  Ok(linker)
338
399
  }
@@ -360,7 +421,12 @@ pub fn app(
360
421
  let component = Component::from_file(&engine, &file_path)?;
361
422
 
362
423
  // Try versions newest-first. When adding vN, insert at the top.
363
- // v4.1 (current - has file interface)
424
+ // v5 (current - has connections interface)
425
+ if let Ok(instance) = v5::Bridge::instantiate(&mut *store, &component, &linker) {
426
+ return Ok(BridgeWrapper::V5(instance));
427
+ }
428
+
429
+ // v4.1 (has file interface + retry-with-reference)
364
430
  if let Ok(instance) = v4_1::Bridge::instantiate(&mut *store, &component, &linker) {
365
431
  return Ok(BridgeWrapper::V4_1(instance));
366
432
  }
@@ -376,6 +442,6 @@ pub fn app(
376
442
  }
377
443
 
378
444
  Err(wasmtime::Error::msg(
379
- "Failed to instantiate component: no compatible WIT version found (tried v4.1, v4, v3)",
445
+ "Failed to instantiate component: no compatible WIT version found (tried v5, v4.1, v4, v3)",
380
446
  ))
381
447
  }
@@ -2,6 +2,11 @@ use crate::types::{AppError, ErrorCode};
2
2
  use magnus::prelude::*;
3
3
  use magnus::{Error, ExceptionClass, RObject, Ruby};
4
4
 
5
+ pub fn runtime_error(message: impl Into<String>) -> Error {
6
+ let ruby = Ruby::get().unwrap();
7
+ Error::new(ruby.exception_runtime_error(), message.into())
8
+ }
9
+
5
10
  impl From<ErrorCode> for ExceptionClass {
6
11
  fn from(value: ErrorCode) -> Self {
7
12
  fn get_class(name: &str) -> ExceptionClass {
@@ -1,5 +1,5 @@
1
1
  use crate::app_state::AppState;
2
- use crate::component::{v4, v4_1};
2
+ use crate::component::{v4, v4_1, v5};
3
3
  use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
4
4
 
5
5
  /// Detects the type of input string
@@ -257,6 +257,7 @@ macro_rules! impl_file_host {
257
257
  // When adding v5, add: impl_file_host!(v5);
258
258
  impl_file_host!(v4);
259
259
  impl_file_host!(v4_1);
260
+ impl_file_host!(v5);
260
261
 
261
262
  #[cfg(test)]
262
263
  mod tests {
@@ -90,6 +90,7 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
90
90
  let app_class = module.define_class("App", ruby.class_object())?;
91
91
  app_class.define_alloc_func::<MutRApp>();
92
92
  app_class.define_method("wit_version", method!(MutRApp::wit_version, 0))?;
93
+ app_class.define_method("connections_supported?", method!(MutRApp::connections_supported, 0))?;
93
94
  app_class.define_method("trigger_ids", method!(MutRApp::trigger_ids, 0))?;
94
95
  app_class.define_method("action_ids", method!(MutRApp::action_ids, 0))?;
95
96
  app_class.define_method("action_input_schema", method!(MutRApp::action_input_schema, 1))?;
@@ -97,6 +98,8 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
97
98
  app_class.define_method("trigger_input_schema", method!(MutRApp::trigger_input_schema, 1))?;
98
99
  app_class.define_method("trigger_output_schema", method!(MutRApp::trigger_output_schema, 1))?;
99
100
  app_class.define_private_method("_rust_initialize", method!(MutRApp::initialize, 2))?;
101
+ app_class.define_private_method("_rust_connections_supported?", method!(MutRApp::connections_supported, 0))?;
102
+ app_class.define_private_method("_rust_connection_config", method!(MutRApp::connection_config, 0))?;
100
103
  app_class.define_private_method("_rust_fetch_events", method!(MutRApp::rb_fetch_events, 1))?;
101
104
  app_class.define_private_method("_rust_execute_action", method!(MutRApp::rb_execute_action, 1))?;
102
105
 
@@ -1,6 +1,6 @@
1
1
  use crate::app_state::AppState;
2
- use crate::component::{v3, v4, v4_1};
3
- use crate::component::v4::standout::app::http::{Method, Request, RequestError, Response};
2
+ use crate::component::{v3, v4, v4_1, v5};
3
+ use crate::component::v4::standout::app::http::{Method, Request, RequestError};
4
4
  use reqwest::Method as ReqwestMethod;
5
5
  use std::result::Result::Ok;
6
6
  use wasmtime::component::Resource;
@@ -212,16 +212,6 @@ macro_rules! impl_http_type_conversions {
212
212
  }
213
213
  }
214
214
 
215
- impl From<Response> for $v::standout::app::http::Response {
216
- fn from(r: Response) -> Self {
217
- Self {
218
- status: r.status,
219
- headers: r.headers,
220
- body: r.body,
221
- }
222
- }
223
- }
224
-
225
215
  impl From<Request> for $v::standout::app::http::Request {
226
216
  fn from(r: Request) -> Self {
227
217
  Self {
@@ -243,6 +233,32 @@ macro_rules! impl_http_type_conversions {
243
233
  };
244
234
  }
245
235
 
236
+ macro_rules! impl_http_response_conversion {
237
+ ($v:ident, with_bytes) => {
238
+ impl From<Response> for $v::standout::app::http::Response {
239
+ fn from(r: Response) -> Self {
240
+ Self {
241
+ status: r.status,
242
+ headers: r.headers,
243
+ body: r.body,
244
+ body_bytes: r.body_bytes,
245
+ }
246
+ }
247
+ }
248
+ };
249
+ ($v:ident, no_bytes) => {
250
+ impl From<Response> for $v::standout::app::http::Response {
251
+ fn from(r: Response) -> Self {
252
+ Self {
253
+ status: r.status,
254
+ headers: r.headers,
255
+ body: r.body,
256
+ }
257
+ }
258
+ }
259
+ };
260
+ }
261
+
246
262
  // ============================================================================
247
263
  // Generate implementations for all supported versions
248
264
  // When adding v5, just add:
@@ -253,15 +269,28 @@ macro_rules! impl_http_type_conversions {
253
269
  impl_host_request_builder!(v3, no);
254
270
  impl_host_request_builder!(v4, no);
255
271
  impl_host_request_builder!(v4_1, yes);
272
+ impl_host_request_builder!(v5, yes);
256
273
 
257
274
  impl_http_type_conversions!(v3);
258
- // Note: v4 doesn't need conversions since we use v4 types as the canonical internal types
259
275
  impl_http_type_conversions!(v4_1);
276
+ impl_http_type_conversions!(v5);
277
+ impl_http_response_conversion!(v3, no_bytes);
278
+ impl_http_response_conversion!(v4, no_bytes);
279
+ impl_http_response_conversion!(v4_1, with_bytes);
280
+ impl_http_response_conversion!(v5, with_bytes);
260
281
 
261
282
  // ============================================================================
262
283
  // Shared request sending logic
263
284
  // ============================================================================
264
285
 
286
+ #[derive(Debug, Clone)]
287
+ struct Response {
288
+ status: u16,
289
+ headers: Vec<(String, String)>,
290
+ body: String,
291
+ body_bytes: Option<Vec<u8>>,
292
+ }
293
+
265
294
  fn send_request(
266
295
  client: &std::sync::Arc<std::sync::Mutex<reqwest::blocking::Client>>,
267
296
  request: &Request,
@@ -281,6 +310,7 @@ fn send_request(
281
310
 
282
311
  match builder.send() {
283
312
  Ok(resp) => {
313
+ let status = resp.status().as_u16();
284
314
  let headers = resp
285
315
  .headers()
286
316
  .iter()
@@ -292,10 +322,14 @@ fn send_request(
292
322
  })
293
323
  .collect();
294
324
 
325
+ let bytes = resp.bytes().unwrap_or_default().to_vec();
326
+ let body = String::from_utf8(bytes.clone()).unwrap_or_default();
327
+
295
328
  Ok(Response {
296
- status: resp.status().as_u16(),
329
+ status,
297
330
  headers,
298
- body: resp.text().unwrap_or_default(),
331
+ body,
332
+ body_bytes: Some(bytes),
299
333
  })
300
334
  }
301
335
  Err(error) => Err(RequestError::Other(format!(
@@ -343,6 +377,7 @@ impl Default for Response {
343
377
  status: 0,
344
378
  headers: Vec::new(),
345
379
  body: String::new(),
380
+ body_bytes: None,
346
381
  }
347
382
  }
348
383
  }
@@ -422,4 +457,30 @@ mod tests {
422
457
  assert_eq!(response.status, 200);
423
458
  mock.assert();
424
459
  }
460
+
461
+ #[test]
462
+ fn returns_binary_response_body_bytes() {
463
+ use v4_1::standout::app::http::HostRequestBuilder;
464
+
465
+ let server = MockServer::start();
466
+ let body = b"%PDF-1.3".to_vec();
467
+ let mock = server.mock(|when, then| {
468
+ when.method(POST).path("/download");
469
+ then.status(200)
470
+ .header("Content-Type", "application/pdf")
471
+ .body(body.clone());
472
+ });
473
+ let url = format!("{}/download", server.base_url());
474
+
475
+ let mut app_state = AppState::default();
476
+ let builder = app_state.new();
477
+ let builder = app_state.method(builder, v4_1::standout::app::http::Method::Post);
478
+ let builder = app_state.url(builder, url);
479
+
480
+ let response = app_state.send(builder).expect("Request failed");
481
+
482
+ assert_eq!(response.status, 200);
483
+ assert_eq!(response.body_bytes, Some(body));
484
+ mock.assert();
485
+ }
425
486
  }
@@ -1,4 +1,5 @@
1
- use magnus::{prelude::*, scan_args::scan_args, Error, RHash, Ruby, Symbol, TryConvert, Value};
1
+ use magnus::{prelude::*, scan_args::scan_args, Error, RHash, Ruby, TryConvert, Value};
2
+ use crate::error_mapping::runtime_error;
2
3
  use crate::types::{ActionContext, ReferenceObject};
3
4
  use super::connection::RConnection;
4
5
 
@@ -33,12 +34,12 @@ impl RActionContext {
33
34
  retry: Option<Value>,
34
35
  ) -> Result<Self, Error> {
35
36
  if connection.is_nil() {
36
- return Err(Error::new(magnus::exception::runtime_error(), "Connection is required"));
37
+ return Err(runtime_error("Connection is required"));
37
38
  }
38
39
 
39
40
  let wrapped_connection: RConnection = match TryConvert::try_convert(connection) {
40
41
  Ok(conn) => conn,
41
- Err(_) => return Err(Error::new(magnus::exception::runtime_error(), "Connection is required")),
42
+ Err(_) => return Err(runtime_error("Connection is required")),
42
43
  };
43
44
 
44
45
  let reference_object = match retry {
@@ -99,12 +100,12 @@ impl TryConvert for RActionContext {
99
100
  };
100
101
 
101
102
  if connection_val.is_nil() {
102
- return Err(Error::new(magnus::exception::runtime_error(), "Connection is required"));
103
+ return Err(runtime_error("Connection is required"));
103
104
  }
104
105
 
105
106
  let wrapped_connection: RConnection = match TryConvert::try_convert(connection_val) {
106
107
  Ok(conn) => conn,
107
- Err(_) => return Err(Error::new(magnus::exception::runtime_error(), "Connection is required")),
108
+ Err(_) => return Err(runtime_error("Connection is required")),
108
109
  };
109
110
 
110
111
  let inner = ActionContext {
@@ -124,8 +125,8 @@ impl TryConvert for RActionContext {
124
125
  fn fetch_hash_string(hash: Value, key: &str) -> Result<String, Error> {
125
126
  let value: Value = hash.funcall("[]", (key,))?;
126
127
  let value = if value.is_nil() {
127
- let symbol = Symbol::new(key);
128
- hash.funcall("[]", (symbol,))?
128
+ let ruby = Ruby::get().unwrap();
129
+ hash.funcall("[]", (ruby.to_symbol(key),))?
129
130
  } else {
130
131
  value
131
132
  };
@@ -5,6 +5,7 @@ use wasmtime::Store;
5
5
 
6
6
  use crate::app_state::AppState;
7
7
  use crate::component::{app, build_engine, build_linker, build_store, BridgeWrapper};
8
+ use crate::error_mapping::runtime_error;
8
9
  use crate::types::{ActionContext, ActionResponse, AppError, ErrorCode, TriggerContext, TriggerResponse};
9
10
  use super::{
10
11
  action_context::RActionContext,
@@ -33,10 +34,45 @@ impl MutRApp {
33
34
  if let Some(instance) = &*instance {
34
35
  Ok(instance.wit_version().to_string())
35
36
  } else {
36
- Err(Error::new(
37
- magnus::exception::runtime_error(),
38
- "App not initialized",
39
- ))
37
+ Err(runtime_error("App not initialized"))
38
+ }
39
+ }
40
+
41
+ pub fn connections_supported(&self) -> Result<bool, Error> {
42
+ let binding = self.0.borrow();
43
+ let instance = binding.instance.borrow();
44
+
45
+ if let Some(instance) = &*instance {
46
+ Ok(instance.connections_supported())
47
+ } else {
48
+ Err(runtime_error("App not initialized"))
49
+ }
50
+ }
51
+
52
+ pub fn connection_config(&self) -> Result<String, Error> {
53
+ let binding = self.0.borrow();
54
+ let mut instance = binding.instance.borrow_mut();
55
+ let mut store = binding.store.borrow_mut();
56
+
57
+ if let (Some(instance), Some(store)) = (&mut *instance, &mut *store) {
58
+ if !instance.connections_supported() {
59
+ return Err(AppError {
60
+ code: ErrorCode::Unsupported,
61
+ message: "connections interface not supported by this component version"
62
+ .to_string(),
63
+ }
64
+ .into());
65
+ }
66
+
67
+ instance.call_connection_config(store).map_err(|err| {
68
+ runtime_error(format!("Failed to read connection-config: {err}"))
69
+ })
70
+ } else {
71
+ Err(AppError {
72
+ code: ErrorCode::InternalError,
73
+ message: "App instance couldn't be initialized".to_string(),
74
+ }
75
+ .into())
40
76
  }
41
77
  }
42
78
 
@@ -44,10 +80,7 @@ impl MutRApp {
44
80
  let mut this = self.0.borrow_mut();
45
81
  let engine = build_engine();
46
82
  let linker = build_linker(&engine).map_err(|e| {
47
- Error::new(
48
- magnus::exception::runtime_error(),
49
- format!("Failed to build linker: {}", e),
50
- )
83
+ runtime_error(format!("Failed to build linker: {}", e))
51
84
  })?;
52
85
  let mut store = if env_vars.is_empty() {
53
86
  build_store(&engine, None)
@@ -57,12 +90,9 @@ impl MutRApp {
57
90
 
58
91
  let app = app(component_path.clone(), engine, &mut store, linker).map_err(|e| {
59
92
  if e.to_string().contains("Incompatible WASM file version") {
60
- Error::new(magnus::exception::runtime_error(), e.to_string())
93
+ runtime_error(e.to_string())
61
94
  } else {
62
- Error::new(
63
- magnus::exception::runtime_error(),
64
- format!("Failed to initialize app: {}", e),
65
- )
95
+ runtime_error(format!("Failed to initialize app: {}", e))
66
96
  }
67
97
  })?;
68
98
 
@@ -85,10 +115,7 @@ impl MutRApp {
85
115
  if let Some(wit_err) = err.downcast_ref::<AppError>() {
86
116
  Err(wit_err.clone().into())
87
117
  } else {
88
- Err(Error::new(
89
- magnus::exception::runtime_error(),
90
- format!("Unexpected error: {:?}", err),
91
- ))
118
+ Err(runtime_error(format!("Unexpected error: {:?}", err)))
92
119
  }
93
120
  }
94
121
  }
@@ -114,10 +141,7 @@ impl MutRApp {
114
141
  if let Some(wit_err) = err.downcast_ref::<AppError>() {
115
142
  Err(wit_err.clone().into())
116
143
  } else {
117
- Err(Error::new(
118
- magnus::exception::runtime_error(),
119
- format!("Unexpected error: {:?}", err),
120
- ))
144
+ Err(runtime_error(format!("Unexpected error: {:?}", err)))
121
145
  }
122
146
  }
123
147
  }
@@ -143,10 +167,7 @@ impl MutRApp {
143
167
  if let Some(wit_err) = err.downcast_ref::<AppError>() {
144
168
  Err(wit_err.clone().into())
145
169
  } else {
146
- Err(Error::new(
147
- magnus::exception::runtime_error(),
148
- format!("Unexpected error: {:?}", err),
149
- ))
170
+ Err(runtime_error(format!("Unexpected error: {:?}", err)))
150
171
  }
151
172
  }
152
173
  }
@@ -208,10 +229,7 @@ impl MutRApp {
208
229
  if let Some(wit_err) = err.downcast_ref::<AppError>() {
209
230
  Err(wit_err.clone().into())
210
231
  } else {
211
- Err(Error::new(
212
- magnus::exception::runtime_error(),
213
- format!("Unexpected error: {:?}", err),
214
- ))
232
+ Err(runtime_error(format!("Unexpected error: {:?}", err)))
215
233
  }
216
234
  }
217
235
  }
@@ -237,10 +255,7 @@ impl MutRApp {
237
255
  if let Some(wit_err) = err.downcast_ref::<AppError>() {
238
256
  Err(wit_err.clone().into())
239
257
  } else {
240
- Err(Error::new(
241
- magnus::exception::runtime_error(),
242
- format!("Unexpected error: {:?}", err),
243
- ))
258
+ Err(runtime_error(format!("Unexpected error: {:?}", err)))
244
259
  }
245
260
  }
246
261
  }
@@ -266,10 +281,7 @@ impl MutRApp {
266
281
  if let Some(wit_err) = err.downcast_ref::<AppError>() {
267
282
  Err(wit_err.clone().into())
268
283
  } else {
269
- Err(Error::new(
270
- magnus::exception::runtime_error(),
271
- format!("Unexpected error: {:?}", err),
272
- ))
284
+ Err(runtime_error(format!("Unexpected error: {:?}", err)))
273
285
  }
274
286
  }
275
287
  }
@@ -1,4 +1,5 @@
1
1
  use magnus::{prelude::*, Error, TryConvert, Value};
2
+ use crate::error_mapping::runtime_error;
2
3
  use crate::types::TriggerContext;
3
4
  use super::connection::RConnection;
4
5
 
@@ -11,12 +12,12 @@ pub struct RTriggerContext {
11
12
  impl RTriggerContext {
12
13
  pub fn new(trigger_id: String, connection: Value, store: String, serialized_input: String) -> Result<Self, Error> {
13
14
  if connection.is_nil() {
14
- return Err(Error::new(magnus::exception::runtime_error(), "Connection is required"));
15
+ return Err(runtime_error("Connection is required"));
15
16
  }
16
17
 
17
18
  let wrapped_connection: RConnection = match TryConvert::try_convert(connection) {
18
19
  Ok(conn) => conn,
19
- Err(_) => return Err(Error::new(magnus::exception::runtime_error(), "Connection is required")),
20
+ Err(_) => return Err(runtime_error("Connection is required")),
20
21
  };
21
22
 
22
23
  let inner = TriggerContext {
@@ -56,12 +57,12 @@ impl TryConvert for RTriggerContext {
56
57
  let serialized_input: String = val.funcall("serialized_input", ())?;
57
58
 
58
59
  if connection_val.is_nil() {
59
- return Err(Error::new(magnus::exception::runtime_error(), "Connection is required"));
60
+ return Err(runtime_error("Connection is required"));
60
61
  }
61
62
 
62
63
  let wrapped_connection: RConnection = match TryConvert::try_convert(connection_val) {
63
64
  Ok(conn) => conn,
64
- Err(_) => return Err(Error::new(magnus::exception::runtime_error(), "Connection is required")),
65
+ Err(_) => return Err(runtime_error("Connection is required")),
65
66
  };
66
67
 
67
68
  let inner = TriggerContext {
@@ -243,6 +243,8 @@ interface http {
243
243
  status: u16,
244
244
  headers: headers,
245
245
  body: string,
246
+ /// Raw response payload for binary responses.
247
+ body-bytes: option<list<u8>>,
246
248
  }
247
249
 
248
250
  record request {