@temporalio/core-bridge 1.1.0 → 1.4.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.
Files changed (124) hide show
  1. package/Cargo.lock +765 -128
  2. package/Cargo.toml +2 -2
  3. package/common.js +7 -3
  4. package/index.d.ts +118 -5
  5. package/index.js +2 -6
  6. package/package.json +2 -3
  7. package/releases/aarch64-apple-darwin/index.node +0 -0
  8. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  9. package/releases/x86_64-apple-darwin/index.node +0 -0
  10. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  11. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  12. package/scripts/build.js +4 -3
  13. package/sdk-core/.buildkite/docker/Dockerfile +2 -1
  14. package/sdk-core/.buildkite/pipeline.yml +2 -0
  15. package/sdk-core/.cargo/config.toml +1 -1
  16. package/sdk-core/ARCHITECTURE.md +2 -2
  17. package/sdk-core/README.md +12 -0
  18. package/sdk-core/bridge-ffi/Cargo.toml +2 -2
  19. package/sdk-core/bridge-ffi/src/lib.rs +2 -2
  20. package/sdk-core/client/Cargo.toml +7 -5
  21. package/sdk-core/client/src/lib.rs +354 -226
  22. package/sdk-core/client/src/metrics.rs +13 -11
  23. package/sdk-core/client/src/raw.rs +352 -107
  24. package/sdk-core/client/src/retry.rs +188 -147
  25. package/sdk-core/client/src/workflow_handle/mod.rs +1 -1
  26. package/sdk-core/core/Cargo.toml +28 -15
  27. package/sdk-core/core/src/core_tests/activity_tasks.rs +98 -33
  28. package/sdk-core/core/src/core_tests/child_workflows.rs +125 -3
  29. package/sdk-core/core/src/core_tests/local_activities.rs +6 -6
  30. package/sdk-core/core/src/core_tests/workers.rs +3 -2
  31. package/sdk-core/core/src/core_tests/workflow_tasks.rs +70 -2
  32. package/sdk-core/core/src/ephemeral_server/mod.rs +515 -0
  33. package/sdk-core/core/src/lib.rs +62 -28
  34. package/sdk-core/core/src/pollers/mod.rs +2 -0
  35. package/sdk-core/core/src/pollers/poll_buffer.rs +4 -4
  36. package/sdk-core/core/src/replay/mod.rs +3 -3
  37. package/sdk-core/core/src/retry_logic.rs +10 -9
  38. package/sdk-core/core/src/telemetry/metrics.rs +48 -39
  39. package/sdk-core/core/src/telemetry/mod.rs +46 -12
  40. package/sdk-core/core/src/telemetry/prometheus_server.rs +17 -13
  41. package/sdk-core/core/src/test_help/mod.rs +18 -8
  42. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +10 -10
  43. package/sdk-core/core/src/worker/activities/local_activities.rs +13 -13
  44. package/sdk-core/core/src/worker/activities.rs +6 -12
  45. package/sdk-core/core/src/worker/client/mocks.rs +1 -0
  46. package/sdk-core/core/src/worker/client.rs +193 -64
  47. package/sdk-core/core/src/worker/mod.rs +14 -19
  48. package/sdk-core/core/src/worker/workflow/driven_workflow.rs +3 -0
  49. package/sdk-core/core/src/worker/workflow/history_update.rs +5 -5
  50. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +133 -85
  51. package/sdk-core/core/src/worker/workflow/machines/mod.rs +3 -2
  52. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +160 -105
  53. package/sdk-core/core/src/worker/workflow/managed_run.rs +2 -1
  54. package/sdk-core/core/src/worker/workflow/mod.rs +62 -58
  55. package/sdk-core/core/src/worker/workflow/run_cache.rs +5 -3
  56. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +7 -5
  57. package/sdk-core/core-api/Cargo.toml +3 -3
  58. package/sdk-core/core-api/src/errors.rs +3 -11
  59. package/sdk-core/core-api/src/worker.rs +7 -0
  60. package/sdk-core/protos/api_upstream/.buildkite/Dockerfile +1 -1
  61. package/sdk-core/protos/api_upstream/.github/CODEOWNERS +1 -1
  62. package/sdk-core/protos/api_upstream/.github/PULL_REQUEST_TEMPLATE.md +2 -6
  63. package/sdk-core/protos/api_upstream/.github/workflows/trigger-api-go-update.yml +29 -0
  64. package/sdk-core/protos/api_upstream/Makefile +2 -2
  65. package/sdk-core/protos/api_upstream/buf.yaml +1 -0
  66. package/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +86 -0
  67. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +26 -0
  68. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +46 -0
  69. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +7 -0
  70. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +14 -0
  71. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +51 -0
  72. package/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +18 -0
  73. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +57 -1
  74. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +1 -3
  75. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +4 -2
  76. package/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +11 -0
  77. package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +23 -0
  78. package/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +46 -0
  79. package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +1 -0
  80. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +172 -0
  81. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +30 -0
  82. package/sdk-core/protos/grpc/health/v1/health.proto +63 -0
  83. package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +18 -15
  84. package/sdk-core/protos/testsrv_upstream/Makefile +80 -0
  85. package/sdk-core/protos/testsrv_upstream/api-linter.yaml +38 -0
  86. package/sdk-core/protos/testsrv_upstream/buf.yaml +13 -0
  87. package/sdk-core/protos/testsrv_upstream/dependencies/gogoproto/gogo.proto +141 -0
  88. package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +63 -0
  89. package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +90 -0
  90. package/sdk-core/sdk/Cargo.toml +2 -2
  91. package/sdk-core/sdk/src/lib.rs +2 -2
  92. package/sdk-core/sdk/src/workflow_context/options.rs +36 -8
  93. package/sdk-core/sdk/src/workflow_context.rs +30 -6
  94. package/sdk-core/sdk/src/workflow_future.rs +4 -4
  95. package/sdk-core/sdk-core-protos/Cargo.toml +5 -5
  96. package/sdk-core/sdk-core-protos/build.rs +9 -1
  97. package/sdk-core/sdk-core-protos/src/history_builder.rs +6 -1
  98. package/sdk-core/sdk-core-protos/src/lib.rs +93 -32
  99. package/sdk-core/test-utils/Cargo.toml +3 -3
  100. package/sdk-core/test-utils/src/canned_histories.rs +58 -0
  101. package/sdk-core/test-utils/src/lib.rs +35 -12
  102. package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +128 -0
  103. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +55 -5
  104. package/sdk-core/tests/integ_tests/polling_tests.rs +2 -1
  105. package/sdk-core/tests/integ_tests/queries_tests.rs +5 -5
  106. package/sdk-core/tests/integ_tests/visibility_tests.rs +93 -0
  107. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +93 -10
  108. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -1
  109. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +14 -14
  110. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +2 -6
  111. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +12 -12
  112. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +12 -1
  113. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +3 -3
  114. package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +8 -2
  115. package/sdk-core/tests/integ_tests/workflow_tests.rs +19 -4
  116. package/sdk-core/tests/load_tests.rs +2 -1
  117. package/sdk-core/tests/main.rs +17 -0
  118. package/sdk-core/tests/runner.rs +93 -0
  119. package/src/conversions.rs +157 -94
  120. package/src/helpers.rs +190 -0
  121. package/src/lib.rs +10 -912
  122. package/src/runtime.rs +436 -0
  123. package/src/testing.rs +67 -0
  124. package/src/worker.rs +465 -0
@@ -1,5 +1,5 @@
1
+ use crate::helpers::*;
1
2
  use log::LevelFilter;
2
- use neon::types::buffer::TypedArray;
3
3
  use neon::{
4
4
  context::Context,
5
5
  handle::Handle,
@@ -7,107 +7,50 @@ use neon::{
7
7
  types::{JsBoolean, JsNumber, JsString},
8
8
  };
9
9
  use opentelemetry::trace::{SpanContext, SpanId, TraceFlags, TraceId, TraceState};
10
- use std::{collections::HashMap, fmt::Display, net::SocketAddr, str::FromStr, time::Duration};
10
+ use std::{collections::HashMap, net::SocketAddr, str::FromStr, time::Duration};
11
11
  use temporal_sdk_core::{
12
12
  api::worker::{WorkerConfig, WorkerConfigBuilder},
13
- ClientOptions, ClientOptionsBuilder, ClientTlsConfig, Logger, MetricsExporter,
14
- OtelCollectorOptions, RetryConfig, TelemetryOptions, TelemetryOptionsBuilder, TlsConfig,
15
- TraceExporter, Url,
13
+ ephemeral_server::{
14
+ TemporaliteConfig, TemporaliteConfigBuilder, TestServerConfig, TestServerConfigBuilder,
15
+ },
16
+ ClientOptions, ClientOptionsBuilder, ClientTlsConfig, Logger, MetricTemporality,
17
+ MetricsExporter, OtelCollectorOptions, RetryConfig, TelemetryOptions, TelemetryOptionsBuilder,
18
+ TlsConfig, TraceExporter, Url,
16
19
  };
17
20
 
18
- #[macro_export]
19
- macro_rules! js_optional_getter {
20
- ($js_cx:expr, $js_obj:expr, $prop_name:expr, $js_type:ty) => {
21
- match get_optional($js_cx, $js_obj, $prop_name) {
22
- None => None,
23
- Some(val) => {
24
- if val.is_a::<$js_type, _>($js_cx) {
25
- Some(val.downcast_or_throw::<$js_type, _>($js_cx)?)
26
- } else {
27
- Some($js_cx.throw_type_error(format!("Invalid {}", $prop_name))?)
28
- }
29
- }
30
- }
31
- };
32
- }
33
-
34
- macro_rules! js_value_getter {
35
- ($js_cx:expr, $js_obj:expr, $prop_name:expr, $js_type:ty) => {
36
- match js_optional_getter!($js_cx, $js_obj, $prop_name, $js_type) {
37
- Some(val) => val.value($js_cx),
38
- None => $js_cx.throw_type_error(format!("{} must be defined", $prop_name))?,
39
- }
40
- };
21
+ pub enum EphemeralServerConfig {
22
+ TestServer(TestServerConfig),
23
+ Temporalite(TemporaliteConfig),
41
24
  }
42
25
 
43
- /// Helper for extracting an optional attribute from [obj].
44
- /// If [obj].[attr] is undefined or not present, None is returned
45
- pub fn get_optional<'a, C, K>(
46
- cx: &mut C,
47
- obj: &Handle<JsObject>,
48
- attr: K,
49
- ) -> Option<Handle<'a, JsValue>>
50
- where
51
- K: neon::object::PropertyKey,
52
- C: Context<'a>,
53
- {
54
- match obj.get_value(cx, attr) {
55
- Err(_) => None,
56
- Ok(val) => match val.is_a::<JsUndefined, _>(cx) {
57
- true => None,
58
- false => Some(val),
59
- },
60
- }
26
+ pub trait ArrayHandleConversionsExt {
27
+ fn to_vec_of_string(&self, cx: &mut FunctionContext) -> NeonResult<Vec<String>>;
61
28
  }
62
29
 
63
- /// Helper for extracting a Vec<u8> from optional Buffer at [obj].[attr]
64
- fn get_optional_vec<'a, C, K>(
65
- cx: &mut C,
66
- obj: &Handle<JsObject>,
67
- attr: K,
68
- ) -> Result<Option<Vec<u8>>, neon::result::Throw>
69
- where
70
- K: neon::object::PropertyKey + Display + Clone,
71
- C: Context<'a>,
72
- {
73
- if let Some(val) = get_optional(cx, obj, attr.clone()) {
74
- let buf = val.downcast::<JsBuffer, C>(cx).map_err(|_| {
75
- cx.throw_type_error::<_, Option<Vec<u8>>>(format!("Invalid {}", attr))
76
- .unwrap_err()
77
- })?;
78
- Ok(Some(buf.as_slice(cx).to_vec()))
79
- } else {
80
- Ok(None)
81
- }
82
- }
30
+ impl ArrayHandleConversionsExt for Handle<'_, JsArray> {
31
+ fn to_vec_of_string(&self, cx: &mut FunctionContext) -> NeonResult<Vec<String>> {
32
+ let js_vec = self.to_vec(cx)?;
33
+ let len = js_vec.len();
34
+ let mut ret_vec = Vec::<String>::with_capacity(len);
83
35
 
84
- /// Helper for extracting a Vec<u8> from optional Buffer at [obj].[attr]
85
- fn get_vec<'a, C, K>(
86
- cx: &mut C,
87
- obj: &Handle<JsObject>,
88
- attr: K,
89
- full_attr_path: &str,
90
- ) -> Result<Vec<u8>, neon::result::Throw>
91
- where
92
- K: neon::object::PropertyKey + Display + Clone,
93
- C: Context<'a>,
94
- {
95
- if let Some(val) = get_optional(cx, obj, attr.clone()) {
96
- let buf = val.downcast::<JsBuffer, C>(cx).map_err(|_| {
97
- cx.throw_type_error::<_, Option<Vec<u8>>>(format!("Invalid {}", attr))
98
- .unwrap_err()
99
- })?;
100
- Ok(buf.as_slice(cx).to_vec())
101
- } else {
102
- cx.throw_type_error::<_, Vec<u8>>(format!("Invalid or missing {}", full_attr_path))
36
+ for i in 0..len - 1 {
37
+ ret_vec[i] = js_vec[i].downcast_or_throw::<JsString, _>(cx)?.value(cx);
38
+ }
39
+ Ok(ret_vec)
103
40
  }
104
41
  }
105
42
 
106
- pub(crate) trait ObjectHandleConversionsExt {
43
+ pub trait ObjectHandleConversionsExt {
44
+ fn set_default(&self, cx: &mut FunctionContext, key: &str, value: &str) -> NeonResult<()>;
107
45
  fn as_otel_span_context(&self, ctx: &mut FunctionContext) -> NeonResult<SpanContext>;
108
46
  fn as_client_options(&self, ctx: &mut FunctionContext) -> NeonResult<ClientOptions>;
109
47
  fn as_telemetry_options(&self, cx: &mut FunctionContext) -> NeonResult<TelemetryOptions>;
110
48
  fn as_worker_config(&self, cx: &mut FunctionContext) -> NeonResult<WorkerConfig>;
49
+ fn as_ephemeral_server_config(
50
+ &self,
51
+ cx: &mut FunctionContext,
52
+ sdk_version: String,
53
+ ) -> NeonResult<EphemeralServerConfig>;
111
54
  fn as_hash_map_of_string_to_string(
112
55
  &self,
113
56
  cx: &mut FunctionContext,
@@ -154,8 +97,7 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
154
97
  let tls_cfg = match js_optional_getter!(cx, self, "tls", JsObject) {
155
98
  None => None,
156
99
  Some(tls) => {
157
- let domain = js_optional_getter!(cx, &tls, "serverNameOverride", JsString)
158
- .map(|h| h.value(cx));
100
+ let domain = js_optional_value_getter!(cx, &tls, "serverNameOverride", JsString);
159
101
 
160
102
  let server_root_ca_cert = get_optional_vec(cx, &tls, "serverRootCACertificate")?;
161
103
  let client_tls_config =
@@ -207,13 +149,13 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
207
149
  "maxInterval",
208
150
  JsNumber
209
151
  ) as u64),
210
- max_elapsed_time: js_optional_getter!(
152
+ max_elapsed_time: js_optional_value_getter!(
211
153
  cx,
212
154
  &retry_config,
213
155
  "maxElapsedTime",
214
156
  JsNumber
215
157
  )
216
- .map(|val| Duration::from_millis(val.value(cx) as u64)),
158
+ .map(|val| Duration::from_millis(val as u64)),
217
159
  max_retries: js_value_getter!(cx, retry_config, "maxRetries", JsNumber) as usize,
218
160
  },
219
161
  };
@@ -235,12 +177,11 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
235
177
  fn as_telemetry_options(&self, cx: &mut FunctionContext) -> NeonResult<TelemetryOptions> {
236
178
  let mut telemetry_opts = TelemetryOptionsBuilder::default();
237
179
 
238
- if let Some(tf) = js_optional_getter!(cx, self, "tracingFilter", JsString) {
239
- telemetry_opts.tracing_filter(tf.value(cx));
180
+ if let Some(tf) = js_optional_value_getter!(cx, self, "tracingFilter", JsString) {
181
+ telemetry_opts.tracing_filter(tf);
240
182
  }
241
183
  telemetry_opts.no_temporal_prefix_for_metrics(
242
- js_optional_getter!(cx, self, "noTemporalPrefixForMetrics", JsBoolean)
243
- .map(|b| b.value(cx))
184
+ js_optional_value_getter!(cx, self, "noTemporalPrefixForMetrics", JsBoolean)
244
185
  .unwrap_or_default(),
245
186
  );
246
187
  if let Some(ref logging) = js_optional_getter!(cx, self, "logging", JsObject) {
@@ -265,6 +206,22 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
265
206
  }
266
207
  }
267
208
  if let Some(metrics) = js_optional_getter!(cx, self, "metrics", JsObject) {
209
+ if let Some(temporality) =
210
+ js_optional_value_getter!(cx, &metrics, "temporality", JsString)
211
+ {
212
+ match temporality.as_str() {
213
+ "cumulative" => {
214
+ telemetry_opts.metric_temporality(MetricTemporality::Cumulative);
215
+ }
216
+ "delta" => {
217
+ telemetry_opts.metric_temporality(MetricTemporality::Delta);
218
+ }
219
+ _ => {
220
+ cx.throw_type_error("Invalid telemetryOptions.metrics.temporality, expected 'cumulative' or 'delta'")?;
221
+ }
222
+ };
223
+ }
224
+
268
225
  if let Some(ref prom) = js_optional_getter!(cx, &metrics, "prometheus", JsObject) {
269
226
  let addr = js_value_getter!(cx, prom, "bindAddress", JsString);
270
227
  match addr.parse::<SocketAddr>() {
@@ -382,4 +339,110 @@ impl ObjectHandleConversionsExt for Handle<'_, JsObject> {
382
339
  Err(e) => cx.throw_error(format!("Invalid worker config: {:?}", e)),
383
340
  }
384
341
  }
342
+
343
+ fn set_default(&self, cx: &mut FunctionContext, key: &str, value: &str) -> NeonResult<()> {
344
+ let key = cx.string(key);
345
+ let existing: Option<Handle<JsString>> = self.get_opt(cx, key)?;
346
+ if existing.is_none() {
347
+ let value = cx.string(value);
348
+ self.set(cx, key, value)?;
349
+ }
350
+ Ok(())
351
+ }
352
+
353
+ fn as_ephemeral_server_config(
354
+ &self,
355
+ cx: &mut FunctionContext,
356
+ sdk_version: String,
357
+ ) -> NeonResult<EphemeralServerConfig> {
358
+ let js_executable = js_optional_getter!(cx, self, "executable", JsObject)
359
+ .unwrap_or_else(|| cx.empty_object());
360
+ js_executable.set_default(cx, "type", "cached-download")?;
361
+
362
+ let exec_type = js_value_getter!(cx, &js_executable, "type", JsString);
363
+ let executable = match exec_type.as_str() {
364
+ "cached-download" => {
365
+ let version = js_optional_value_getter!(cx, &js_executable, "version", JsString)
366
+ .unwrap_or("default".to_owned());
367
+ let dest_dir =
368
+ js_optional_value_getter!(cx, &js_executable, "downloadDir", JsString);
369
+
370
+ let exec_version = match version.as_str() {
371
+ "default" => {
372
+ temporal_sdk_core::ephemeral_server::EphemeralExeVersion::Default {
373
+ sdk_name: "sdk-typescript".to_owned(),
374
+ sdk_version,
375
+ }
376
+ }
377
+ _ => temporal_sdk_core::ephemeral_server::EphemeralExeVersion::Fixed(version),
378
+ };
379
+ temporal_sdk_core::ephemeral_server::EphemeralExe::CachedDownload {
380
+ version: exec_version,
381
+ dest_dir,
382
+ }
383
+ }
384
+ "existing-path" => {
385
+ let path = js_value_getter!(cx, &js_executable, "path", JsString);
386
+ temporal_sdk_core::ephemeral_server::EphemeralExe::ExistingPath(path)
387
+ }
388
+ _ => {
389
+ return cx.throw_type_error(format!("Invalid executable type: {}", exec_type))?;
390
+ }
391
+ };
392
+ let port = js_optional_getter!(cx, self, "port", JsNumber).map(|s| s.value(cx) as u16);
393
+
394
+ let server_type = js_value_getter!(cx, self, "type", JsString);
395
+ match server_type.as_str() {
396
+ "temporalite" => {
397
+ let mut config = TemporaliteConfigBuilder::default();
398
+ config.exe(executable).port(port);
399
+
400
+ if let Some(extra_args) = js_optional_getter!(cx, self, "extraArgs", JsArray) {
401
+ config.extra_args(extra_args.to_vec_of_string(cx)?);
402
+ };
403
+ if let Some(namespace) = js_optional_value_getter!(cx, self, "namespace", JsString)
404
+ {
405
+ config.namespace(namespace);
406
+ }
407
+ if let Some(ip) = js_optional_value_getter!(cx, self, "ip", JsString) {
408
+ config.ip(ip);
409
+ }
410
+ config.db_filename(js_optional_value_getter!(cx, self, "dbFilename", JsString));
411
+ config.ui(js_optional_value_getter!(cx, self, "ui", JsBoolean).unwrap_or_default());
412
+
413
+ if let Some(log) = js_optional_getter!(cx, self, "log", JsObject) {
414
+ let format = js_value_getter!(cx, &log, "format", JsString);
415
+ let level = js_value_getter!(cx, &log, "level", JsString);
416
+ config.log((format, level));
417
+ }
418
+
419
+ match config.build() {
420
+ Ok(config) => Ok(EphemeralServerConfig::Temporalite(config)),
421
+ Err(err) => {
422
+ cx.throw_type_error(format!("Invalid temporalite config: {:?}", err))
423
+ }
424
+ }
425
+ }
426
+ "time-skipping" => {
427
+ let mut config = TestServerConfigBuilder::default();
428
+ config.exe(executable).port(port);
429
+
430
+ if let Some(extra_args_js) = js_optional_getter!(cx, self, "extraArgs", JsArray) {
431
+ let extra_args = extra_args_js.to_vec_of_string(cx)?;
432
+ config.extra_args(extra_args);
433
+ };
434
+
435
+ match config.build() {
436
+ Ok(config) => Ok(EphemeralServerConfig::TestServer(config)),
437
+ Err(err) => {
438
+ cx.throw_type_error(format!("Invalid test server config: {:?}", err))
439
+ }
440
+ }
441
+ }
442
+ s => cx.throw_type_error(format!(
443
+ "Invalid ephemeral server type: {}, expected 'temporalite' or 'time-skipping'",
444
+ s
445
+ )),
446
+ }
447
+ }
385
448
  }
package/src/helpers.rs ADDED
@@ -0,0 +1,190 @@
1
+ use crate::errors::*;
2
+ use neon::prelude::*;
3
+ use neon::types::buffer::TypedArray;
4
+ use std::{fmt::Display, future::Future, sync::Arc};
5
+
6
+ /// Send a result to JS via callback using a [Channel]
7
+ pub fn send_result<F, T>(channel: Arc<Channel>, callback: Root<JsFunction>, res_fn: F)
8
+ where
9
+ F: for<'a> FnOnce(&mut TaskContext<'a>) -> NeonResult<Handle<'a, T>> + Send + 'static,
10
+ T: Value,
11
+ {
12
+ channel.send(move |mut cx| {
13
+ let callback = callback.into_inner(&mut cx);
14
+ let this = cx.undefined();
15
+ let error = cx.undefined();
16
+ let result = res_fn(&mut cx)?;
17
+ let args: Vec<Handle<JsValue>> = vec![error.upcast(), result.upcast()];
18
+ callback.call(&mut cx, this, args)?;
19
+ Ok(())
20
+ });
21
+ }
22
+
23
+ /// Send an error to JS via callback using a [Channel]
24
+ pub fn send_error<E, F>(channel: Arc<Channel>, callback: Root<JsFunction>, error_ctor: F)
25
+ where
26
+ E: Object,
27
+ F: for<'a> FnOnce(&mut TaskContext<'a>) -> JsResult<'a, E> + Send + 'static,
28
+ {
29
+ channel.send(move |mut cx| {
30
+ let callback = callback.into_inner(&mut cx);
31
+ callback_with_error(&mut cx, callback, error_ctor)
32
+ });
33
+ }
34
+
35
+ /// Call `callback` with given error
36
+ pub fn callback_with_error<'a, C, E, F>(
37
+ cx: &mut C,
38
+ callback: Handle<JsFunction>,
39
+ error_ctor: F,
40
+ ) -> NeonResult<()>
41
+ where
42
+ C: Context<'a>,
43
+ E: Object,
44
+ F: FnOnce(&mut C) -> JsResult<'a, E> + Send + 'static,
45
+ {
46
+ let this = cx.undefined();
47
+ let error = error_ctor(cx)?;
48
+ let result = cx.undefined();
49
+ let args: Vec<Handle<JsValue>> = vec![error.upcast(), result.upcast()];
50
+ callback.call(cx, this, args)?;
51
+ Ok(())
52
+ }
53
+
54
+ /// Call `callback` with an UnexpectedError created from `err`
55
+ pub fn callback_with_unexpected_error<'a, C, E>(
56
+ cx: &mut C,
57
+ callback: Handle<JsFunction>,
58
+ err: E,
59
+ ) -> NeonResult<()>
60
+ where
61
+ C: Context<'a>,
62
+ E: std::fmt::Display,
63
+ {
64
+ let err_str = format!("{}", err);
65
+ callback_with_error(cx, callback, move |cx| {
66
+ UNEXPECTED_ERROR.from_string(cx, err_str)
67
+ })
68
+ }
69
+
70
+ /// When Future completes, call given JS callback using a neon::Channel with either error or
71
+ /// undefined
72
+ pub async fn void_future_to_js<E, F, ER, EF>(
73
+ channel: Arc<Channel>,
74
+ callback: Root<JsFunction>,
75
+ f: F,
76
+ error_function: EF,
77
+ ) where
78
+ E: Display + Send + 'static,
79
+ F: Future<Output = Result<(), E>> + Send,
80
+ ER: Object,
81
+ EF: for<'a> FnOnce(&mut TaskContext<'a>, E) -> JsResult<'a, ER> + Send + 'static,
82
+ {
83
+ match f.await {
84
+ Ok(()) => {
85
+ send_result(channel, callback, |cx| Ok(cx.undefined()));
86
+ }
87
+ Err(err) => {
88
+ send_error(channel, callback, |cx| error_function(cx, err));
89
+ }
90
+ }
91
+ }
92
+
93
+ macro_rules! js_optional_getter {
94
+ ($js_cx:expr, $js_obj:expr, $prop_name:expr, $js_type:ty) => {
95
+ match get_optional($js_cx, $js_obj, $prop_name) {
96
+ None => None,
97
+ Some(val) => {
98
+ if val.is_a::<$js_type, _>($js_cx) {
99
+ Some(val.downcast_or_throw::<$js_type, _>($js_cx)?)
100
+ } else {
101
+ Some($js_cx.throw_type_error(format!("Invalid {}", $prop_name))?)
102
+ }
103
+ }
104
+ }
105
+ };
106
+ }
107
+
108
+ pub(crate) use js_optional_getter;
109
+
110
+ macro_rules! js_optional_value_getter {
111
+ ($js_cx:expr, $js_obj:expr, $prop_name:expr, $js_type:ty) => {
112
+ js_optional_getter!($js_cx, $js_obj, $prop_name, $js_type).map(|v| v.value($js_cx))
113
+ };
114
+ }
115
+
116
+ pub(crate) use js_optional_value_getter;
117
+
118
+ macro_rules! js_value_getter {
119
+ ($js_cx:expr, $js_obj:expr, $prop_name:expr, $js_type:ty) => {
120
+ match js_optional_getter!($js_cx, $js_obj, $prop_name, $js_type) {
121
+ Some(val) => val.value($js_cx),
122
+ None => $js_cx.throw_type_error(format!("{} must be defined", $prop_name))?,
123
+ }
124
+ };
125
+ }
126
+
127
+ pub(crate) use js_value_getter;
128
+
129
+ /// Helper for extracting an optional attribute from [obj].
130
+ /// If [obj].[attr] is undefined or not present, None is returned
131
+ pub fn get_optional<'a, C, K>(
132
+ cx: &mut C,
133
+ obj: &Handle<JsObject>,
134
+ attr: K,
135
+ ) -> Option<Handle<'a, JsValue>>
136
+ where
137
+ K: neon::object::PropertyKey,
138
+ C: Context<'a>,
139
+ {
140
+ match obj.get_value(cx, attr) {
141
+ Err(_) => None,
142
+ Ok(val) => match val.is_a::<JsUndefined, _>(cx) {
143
+ true => None,
144
+ false => Some(val),
145
+ },
146
+ }
147
+ }
148
+
149
+ /// Helper for extracting a Vec<u8> from optional Buffer at [obj].[attr]
150
+ pub fn get_optional_vec<'a, C, K>(
151
+ cx: &mut C,
152
+ obj: &Handle<JsObject>,
153
+ attr: K,
154
+ ) -> Result<Option<Vec<u8>>, neon::result::Throw>
155
+ where
156
+ K: neon::object::PropertyKey + Display + Clone,
157
+ C: Context<'a>,
158
+ {
159
+ if let Some(val) = get_optional(cx, obj, attr.clone()) {
160
+ let buf = val.downcast::<JsBuffer, C>(cx).map_err(|_| {
161
+ cx.throw_type_error::<_, Option<Vec<u8>>>(format!("Invalid {}", attr))
162
+ .unwrap_err()
163
+ })?;
164
+ Ok(Some(buf.as_slice(cx).to_vec()))
165
+ } else {
166
+ Ok(None)
167
+ }
168
+ }
169
+
170
+ /// Helper for extracting a Vec<u8> from optional Buffer at [obj].[attr]
171
+ pub fn get_vec<'a, C, K>(
172
+ cx: &mut C,
173
+ obj: &Handle<JsObject>,
174
+ attr: K,
175
+ full_attr_path: &str,
176
+ ) -> Result<Vec<u8>, neon::result::Throw>
177
+ where
178
+ K: neon::object::PropertyKey + Display + Clone,
179
+ C: Context<'a>,
180
+ {
181
+ if let Some(val) = get_optional(cx, obj, attr.clone()) {
182
+ let buf = val.downcast::<JsBuffer, C>(cx).map_err(|_| {
183
+ cx.throw_type_error::<_, Option<Vec<u8>>>(format!("Invalid {}", attr))
184
+ .unwrap_err()
185
+ })?;
186
+ Ok(buf.as_slice(cx).to_vec())
187
+ } else {
188
+ cx.throw_type_error::<_, Vec<u8>>(format!("Invalid or missing {}", full_attr_path))
189
+ }
190
+ }