@temporalio/core-bridge 1.4.4 → 1.5.1

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 (123) hide show
  1. package/Cargo.lock +327 -419
  2. package/Cargo.toml +1 -1
  3. package/index.js +25 -2
  4. package/lib/errors.d.ts +22 -0
  5. package/lib/errors.js +65 -0
  6. package/lib/errors.js.map +1 -0
  7. package/lib/index.d.ts +440 -0
  8. package/lib/index.js +8 -0
  9. package/lib/index.js.map +1 -0
  10. package/package.json +11 -5
  11. package/releases/aarch64-apple-darwin/index.node +0 -0
  12. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  13. package/releases/x86_64-apple-darwin/index.node +0 -0
  14. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  15. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  16. package/sdk-core/.buildkite/docker/Dockerfile +1 -1
  17. package/sdk-core/.buildkite/docker/docker-compose.yaml +2 -2
  18. package/sdk-core/bridge-ffi/Cargo.toml +1 -1
  19. package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +0 -25
  20. package/sdk-core/bridge-ffi/src/lib.rs +29 -108
  21. package/sdk-core/bridge-ffi/src/wrappers.rs +35 -25
  22. package/sdk-core/client/Cargo.toml +1 -1
  23. package/sdk-core/client/src/lib.rs +12 -20
  24. package/sdk-core/client/src/raw.rs +9 -8
  25. package/sdk-core/client/src/retry.rs +100 -23
  26. package/sdk-core/core/Cargo.toml +5 -5
  27. package/sdk-core/core/benches/workflow_replay.rs +13 -10
  28. package/sdk-core/core/src/abstractions.rs +22 -22
  29. package/sdk-core/core/src/core_tests/activity_tasks.rs +1 -1
  30. package/sdk-core/core/src/core_tests/local_activities.rs +228 -6
  31. package/sdk-core/core/src/core_tests/queries.rs +247 -89
  32. package/sdk-core/core/src/core_tests/workers.rs +2 -2
  33. package/sdk-core/core/src/core_tests/workflow_cancels.rs +1 -1
  34. package/sdk-core/core/src/core_tests/workflow_tasks.rs +46 -27
  35. package/sdk-core/core/src/lib.rs +139 -32
  36. package/sdk-core/core/src/replay/mod.rs +185 -41
  37. package/sdk-core/core/src/telemetry/log_export.rs +190 -0
  38. package/sdk-core/core/src/telemetry/metrics.rs +184 -139
  39. package/sdk-core/core/src/telemetry/mod.rs +296 -318
  40. package/sdk-core/core/src/telemetry/prometheus_server.rs +4 -3
  41. package/sdk-core/core/src/test_help/mod.rs +9 -7
  42. package/sdk-core/core/src/worker/activities/local_activities.rs +2 -1
  43. package/sdk-core/core/src/worker/activities.rs +40 -23
  44. package/sdk-core/core/src/worker/client/mocks.rs +1 -1
  45. package/sdk-core/core/src/worker/client.rs +30 -4
  46. package/sdk-core/core/src/worker/mod.rs +22 -18
  47. package/sdk-core/core/src/worker/workflow/driven_workflow.rs +10 -19
  48. package/sdk-core/core/src/worker/workflow/history_update.rs +99 -25
  49. package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +1 -5
  50. package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +1 -5
  51. package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +1 -5
  52. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +1 -5
  53. package/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +1 -5
  54. package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +2 -6
  55. package/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +1 -5
  56. package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +18 -21
  57. package/sdk-core/core/src/worker/workflow/machines/mod.rs +12 -38
  58. package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +178 -0
  59. package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +1 -5
  60. package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +1 -5
  61. package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +1 -5
  62. package/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +8 -2
  63. package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +1 -5
  64. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +232 -216
  65. package/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +1 -6
  66. package/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +4 -4
  67. package/sdk-core/core/src/worker/workflow/managed_run.rs +13 -5
  68. package/sdk-core/core/src/worker/workflow/mod.rs +61 -9
  69. package/sdk-core/core/src/worker/workflow/wft_poller.rs +2 -2
  70. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +56 -11
  71. package/sdk-core/core-api/Cargo.toml +4 -3
  72. package/sdk-core/core-api/src/lib.rs +1 -43
  73. package/sdk-core/core-api/src/telemetry.rs +147 -0
  74. package/sdk-core/core-api/src/worker.rs +13 -0
  75. package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +1 -1
  76. package/sdk-core/histories/evict_while_la_running_no_interference-23_history.bin +0 -0
  77. package/sdk-core/histories/evict_while_la_running_no_interference-85_history.bin +0 -0
  78. package/sdk-core/protos/api_upstream/.github/CODEOWNERS +1 -1
  79. package/sdk-core/protos/api_upstream/buf.yaml +0 -3
  80. package/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +3 -7
  81. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +8 -0
  82. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +1 -2
  83. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +2 -0
  84. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +3 -0
  85. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +13 -0
  86. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +19 -59
  87. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +0 -19
  88. package/sdk-core/protos/api_upstream/temporal/api/schedule/v1/message.proto +108 -29
  89. package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +2 -2
  90. package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +1 -0
  91. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +47 -8
  92. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +15 -1
  93. package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +2 -0
  94. package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +8 -1
  95. package/sdk-core/sdk/src/interceptors.rs +36 -3
  96. package/sdk-core/sdk/src/lib.rs +7 -4
  97. package/sdk-core/sdk/src/workflow_context.rs +13 -2
  98. package/sdk-core/sdk-core-protos/src/history_builder.rs +47 -1
  99. package/sdk-core/sdk-core-protos/src/history_info.rs +22 -22
  100. package/sdk-core/sdk-core-protos/src/lib.rs +49 -27
  101. package/sdk-core/test-utils/Cargo.toml +1 -0
  102. package/sdk-core/test-utils/src/lib.rs +81 -29
  103. package/sdk-core/tests/integ_tests/metrics_tests.rs +37 -0
  104. package/sdk-core/tests/integ_tests/polling_tests.rs +0 -13
  105. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +145 -4
  106. package/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +53 -0
  107. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +106 -20
  108. package/sdk-core/tests/integ_tests/workflow_tests.rs +18 -8
  109. package/sdk-core/tests/main.rs +6 -4
  110. package/src/conversions.rs +52 -47
  111. package/src/errors.rs +28 -86
  112. package/src/helpers.rs +3 -4
  113. package/src/lib.rs +2 -2
  114. package/src/runtime.rs +132 -61
  115. package/src/testing.rs +7 -4
  116. package/src/worker.rs +67 -50
  117. package/ts/errors.ts +55 -0
  118. package/{index.d.ts → ts/index.ts} +121 -15
  119. package/sdk-core/core/src/log_export.rs +0 -62
  120. package/sdk-core/core/src/worker/workflow/machines/mutable_side_effect_state_machine.rs +0 -127
  121. package/sdk-core/core/src/worker/workflow/machines/side_effect_state_machine.rs +0 -71
  122. package/sdk-core/protos/api_upstream/temporal/api/cluster/v1/message.proto +0 -83
  123. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/cluster.proto +0 -40
package/src/runtime.rs CHANGED
@@ -1,9 +1,8 @@
1
- use crate::conversions::*;
2
- use crate::errors::*;
3
- use crate::helpers::*;
4
- use crate::worker::*;
1
+ use crate::{conversions::*, errors::*, helpers::*, worker::*};
2
+ use neon::context::Context;
5
3
  use neon::prelude::*;
6
4
  use parking_lot::RwLock;
5
+ use std::cell::Cell;
7
6
  use std::{
8
7
  cell::RefCell,
9
8
  collections::HashMap,
@@ -12,17 +11,17 @@ use std::{
12
11
  time::{Duration, SystemTime, UNIX_EPOCH},
13
12
  };
14
13
  use temporal_client::{ClientInitError, ConfiguredClient, TemporalServiceClientWithMetrics};
15
- use temporal_sdk_core::ephemeral_server::EphemeralServer as CoreEphemeralServer;
14
+ use temporal_sdk_core::api::telemetry::{CoreTelemetry, TelemetryOptions};
15
+ use temporal_sdk_core::CoreRuntime;
16
16
  use temporal_sdk_core::{
17
- fetch_global_buffered_logs, init_replay_worker, init_worker,
18
- protos::temporal::api::history::v1::History, telemetry_init, ClientOptions, RetryClient,
19
- WorkerConfig,
17
+ ephemeral_server::EphemeralServer as CoreEphemeralServer, init_replay_worker, init_worker,
18
+ replay::HistoryForReplay, ClientOptions, RetryClient, WorkerConfig,
20
19
  };
21
- use tokio::{
22
- runtime::Runtime,
23
- sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
24
- sync::Mutex,
20
+ use tokio::sync::{
21
+ mpsc::{channel, unbounded_channel, Sender, UnboundedReceiver, UnboundedSender},
22
+ Mutex,
25
23
  };
24
+ use tokio_stream::wrappers::ReceiverStream;
26
25
 
27
26
  pub type RawClient = RetryClient<ConfiguredClient<TemporalServiceClientWithMetrics>>;
28
27
 
@@ -84,10 +83,9 @@ pub enum RuntimeRequest {
84
83
  },
85
84
  /// A request to register a replay worker
86
85
  InitReplayWorker {
86
+ runtime: Arc<RuntimeHandle>,
87
87
  /// Worker configuration. Must have unique task queue name.
88
88
  config: WorkerConfig,
89
- /// The history this worker should replay
90
- history: History,
91
89
  /// Used to send the result back into JS
92
90
  callback: Root<JsFunction>,
93
91
  },
@@ -105,22 +103,27 @@ pub enum RuntimeRequest {
105
103
  server: Arc<Mutex<CoreEphemeralServer>>,
106
104
  callback: Root<JsFunction>,
107
105
  },
108
- }
109
-
110
- /// Inits a multi-threaded tokio runtime used to interact with sdk-core APIs
111
- pub fn tokio_runtime() -> Runtime {
112
- tokio::runtime::Builder::new_multi_thread()
113
- .enable_all()
114
- .thread_name("core")
115
- .build()
116
- .expect("Tokio runtime must construct properly")
106
+ PushReplayHistory {
107
+ tx: Sender<HistoryForReplay>,
108
+ pushme: HistoryForReplay,
109
+ callback: Root<JsFunction>,
110
+ },
117
111
  }
118
112
 
119
113
  /// Builds a tokio runtime and starts polling on [RuntimeRequest]s via an internal channel.
120
114
  /// Bridges requests from JS to core and sends responses back to JS using a neon::Channel.
121
115
  /// Blocks current thread until a [Shutdown] request is received in channel.
122
- pub fn start_bridge_loop(channel: Arc<Channel>, receiver: &mut UnboundedReceiver<RuntimeRequest>) {
123
- tokio_runtime().block_on(async {
116
+ pub fn start_bridge_loop(
117
+ telemetry_options: TelemetryOptions,
118
+ channel: Arc<Channel>,
119
+ receiver: &mut UnboundedReceiver<RuntimeRequest>,
120
+ ) {
121
+ let mut tokio_builder = tokio::runtime::Builder::new_multi_thread();
122
+ tokio_builder.enable_all().thread_name("core");
123
+ let core_runtime =
124
+ CoreRuntime::new(telemetry_options, tokio_builder).expect("Failed to create CoreRuntime");
125
+
126
+ core_runtime.tokio_handle().block_on(async {
124
127
  loop {
125
128
  let request_option = receiver.recv().await;
126
129
  let request = match request_option {
@@ -145,23 +148,25 @@ pub fn start_bridge_loop(channel: Arc<Channel>, receiver: &mut UnboundedReceiver
145
148
  // returned client directly at the moment, when we repurpose the client to be
146
149
  // used by a Worker, `init_worker` will attach the correct metrics meter for
147
150
  // us.
148
- tokio::spawn(async move {
151
+ core_runtime.tokio_handle().spawn(async move {
149
152
  match options
150
153
  .connect_no_namespace(None, headers.map(|h| Arc::new(RwLock::new(h))))
151
154
  .await
152
155
  {
153
156
  Err(err) => {
154
157
  send_error(channel.clone(), callback, |cx| match err {
155
- ClientInitError::SystemInfoCallError(e) => TRANSPORT_ERROR
156
- .from_string(
158
+ ClientInitError::SystemInfoCallError(e) => {
159
+ make_named_error_from_string(
157
160
  cx,
161
+ TRANSPORT_ERROR,
158
162
  format!("Failed to call GetSystemInfo: {}", e),
159
- ),
163
+ )
164
+ }
160
165
  ClientInitError::TonicTransportError(e) => {
161
- TRANSPORT_ERROR.from_error(cx, e)
166
+ make_named_error_from_error(cx, TRANSPORT_ERROR, e)
162
167
  }
163
168
  ClientInitError::InvalidUri(e) => {
164
- Ok(JsError::type_error(cx, format!("{}", e))?.upcast())
169
+ Ok(JsError::type_error(cx, format!("{}", e))?)
165
170
  }
166
171
  });
167
172
  }
@@ -185,7 +190,7 @@ pub fn start_bridge_loop(channel: Arc<Channel>, receiver: &mut UnboundedReceiver
185
190
  send_result(channel.clone(), callback, |cx| Ok(cx.undefined()));
186
191
  }
187
192
  RuntimeRequest::PollLogs { callback } => {
188
- let logs = fetch_global_buffered_logs();
193
+ let logs = core_runtime.telemetry().fetch_buffered_logs();
189
194
  send_result(channel.clone(), callback, |cx| {
190
195
  let logarr = cx.empty_array();
191
196
  for (i, cl) in logs.into_iter().enumerate() {
@@ -209,28 +214,49 @@ pub fn start_bridge_loop(channel: Arc<Channel>, receiver: &mut UnboundedReceiver
209
214
  callback,
210
215
  } => {
211
216
  let client = (*client).clone();
212
- let worker = init_worker(config, client.into_inner());
213
- let (tx, rx) = unbounded_channel();
214
- tokio::spawn(start_worker_loop(worker, rx, channel.clone()));
215
- send_result(channel.clone(), callback, |cx| {
216
- Ok(cx.boxed(RefCell::new(Some(WorkerHandle { sender: tx }))))
217
- });
217
+ match init_worker(&core_runtime, config, client.into_inner()) {
218
+ Ok(worker) => {
219
+ let (tx, rx) = unbounded_channel();
220
+ core_runtime.tokio_handle().spawn(start_worker_loop(
221
+ worker,
222
+ rx,
223
+ channel.clone(),
224
+ ));
225
+ send_result(channel.clone(), callback, |cx| {
226
+ Ok(cx.boxed(RefCell::new(Some(WorkerHandle { sender: tx }))))
227
+ });
228
+ }
229
+ Err(err) => send_error(channel.clone(), callback, move |cx| {
230
+ make_named_error_from_error(cx, UNEXPECTED_ERROR, err.deref())
231
+ }),
232
+ }
218
233
  }
219
234
  RuntimeRequest::InitReplayWorker {
235
+ runtime,
220
236
  config,
221
- history,
222
237
  callback,
223
238
  } => {
224
- match init_replay_worker(config, &history) {
239
+ let (tunnel, stream) = HistoryForReplayTunnel::new(runtime);
240
+ match init_replay_worker(config, Box::pin(stream)) {
225
241
  Ok(worker) => {
226
242
  let (tx, rx) = unbounded_channel();
227
- tokio::spawn(start_worker_loop(worker, rx, channel.clone()));
243
+ core_runtime.tokio_handle().spawn(start_worker_loop(
244
+ worker,
245
+ rx,
246
+ channel.clone(),
247
+ ));
228
248
  send_result(channel.clone(), callback, |cx| {
229
- Ok(cx.boxed(RefCell::new(Some(WorkerHandle { sender: tx }))))
249
+ let worker =
250
+ cx.boxed(RefCell::new(Some(WorkerHandle { sender: tx })));
251
+ let tunnel = cx.boxed(tunnel);
252
+ let retme = cx.empty_object();
253
+ retme.set(cx, "worker", worker)?;
254
+ retme.set(cx, "pusher", tunnel)?;
255
+ Ok(retme)
230
256
  })
231
257
  }
232
258
  Err(err) => send_error(channel.clone(), callback, move |cx| {
233
- UNEXPECTED_ERROR.from_error(cx, err.deref())
259
+ make_named_error_from_error(cx, UNEXPECTED_ERROR, err.deref())
234
260
  }),
235
261
  };
236
262
  }
@@ -239,7 +265,7 @@ pub fn start_bridge_loop(channel: Arc<Channel>, receiver: &mut UnboundedReceiver
239
265
  config,
240
266
  callback,
241
267
  } => {
242
- tokio::spawn(async move {
268
+ core_runtime.tokio_handle().spawn(async move {
243
269
  let result = match config {
244
270
  EphemeralServerConfig::TestServer(config) => {
245
271
  config.start_server().await
@@ -252,7 +278,7 @@ pub fn start_bridge_loop(channel: Arc<Channel>, receiver: &mut UnboundedReceiver
252
278
  Err(err) => {
253
279
  let err_str = format!("Failed to start ephemeral server: {}", err);
254
280
  send_error(channel.clone(), callback, |cx| {
255
- UNEXPECTED_ERROR.from_string(cx, err_str)
281
+ make_named_error_from_string(cx, UNEXPECTED_ERROR, err_str)
256
282
  });
257
283
  }
258
284
  Ok(server) => {
@@ -267,7 +293,7 @@ pub fn start_bridge_loop(channel: Arc<Channel>, receiver: &mut UnboundedReceiver
267
293
  });
268
294
  }
269
295
  RuntimeRequest::ShutdownEphemeralServer { server, callback } => {
270
- tokio::spawn(async move {
296
+ core_runtime.tokio_handle().spawn(async move {
271
297
  void_future_to_js(
272
298
  channel,
273
299
  callback,
@@ -276,8 +302,9 @@ pub fn start_bridge_loop(channel: Arc<Channel>, receiver: &mut UnboundedReceiver
276
302
  guard.shutdown().await
277
303
  },
278
304
  |cx, err| {
279
- UNEXPECTED_ERROR.from_string(
305
+ make_named_error_from_string(
280
306
  cx,
307
+ UNEXPECTED_ERROR,
281
308
  format!("Failed to start test server: {}", err),
282
309
  )
283
310
  },
@@ -285,6 +312,30 @@ pub fn start_bridge_loop(channel: Arc<Channel>, receiver: &mut UnboundedReceiver
285
312
  .await
286
313
  });
287
314
  }
315
+ RuntimeRequest::PushReplayHistory {
316
+ tx,
317
+ pushme,
318
+ callback,
319
+ } => {
320
+ core_runtime.tokio_handle().spawn(async move {
321
+ let sendfut = async move {
322
+ tx.send(pushme).await.map_err(|e| {
323
+ format!(
324
+ "Receive side of history replay channel is gone. This is an sdk bug. {:?}",
325
+ e
326
+ )
327
+ })
328
+ };
329
+ void_future_to_js(channel, callback, sendfut, |cx, err| {
330
+ make_named_error_from_string(
331
+ cx,
332
+ UNEXPECTED_ERROR,
333
+ format!("Error pushing replay history {}", err),
334
+ )
335
+ })
336
+ .await
337
+ });
338
+ }
288
339
  }
289
340
  }
290
341
  })
@@ -314,24 +365,15 @@ pub fn get_time_of_day(mut cx: FunctionContext) -> JsResult<JsArray> {
314
365
  system_time_to_js(&mut cx, SystemTime::now())
315
366
  }
316
367
 
317
- /// Initialize Core global telemetry.
368
+ /// Initialize Core global telemetry and create the tokio runtime required to run Core.
318
369
  /// This should typically be called once on process startup.
319
- pub fn init_telemetry(mut cx: FunctionContext) -> JsResult<JsUndefined> {
320
- let telemetry_options = cx.argument::<JsObject>(0)?.as_telemetry_options(&mut cx)?;
321
- telemetry_init(&telemetry_options).map_err(|err| {
322
- cx.throw_type_error::<String, ()>(format!("{}", err))
323
- .unwrap_err()
324
- })?;
325
- Ok(cx.undefined())
326
- }
327
-
328
- /// Create the tokio runtime required to run Core.
329
370
  /// Immediately spawns a poller thread that will block on [RuntimeRequest]s
330
371
  pub fn runtime_new(mut cx: FunctionContext) -> JsResult<BoxedRuntime> {
372
+ let telemetry_options = cx.argument::<JsObject>(0)?.as_telemetry_options(&mut cx)?;
331
373
  let channel = Arc::new(cx.channel());
332
374
  let (sender, mut receiver) = unbounded_channel::<RuntimeRequest>();
333
375
 
334
- std::thread::spawn(move || start_bridge_loop(channel, &mut receiver));
376
+ std::thread::spawn(move || start_bridge_loop(telemetry_options, channel, &mut receiver));
335
377
 
336
378
  Ok(cx.boxed(Arc::new(RuntimeHandle { sender })))
337
379
  }
@@ -401,8 +443,7 @@ pub fn client_new(mut cx: FunctionContext) -> JsResult<JsUndefined> {
401
443
  pub fn client_close(mut cx: FunctionContext) -> JsResult<JsUndefined> {
402
444
  let client = cx.argument::<BoxedClient>(0)?;
403
445
  if client.replace(None).is_none() {
404
- ILLEGAL_STATE_ERROR
405
- .from_string(&mut cx, "Client already closed")
446
+ make_named_error_from_string(&mut cx, ILLEGAL_STATE_ERROR, "Client already closed")
406
447
  .and_then(|err| cx.throw(err))?;
407
448
  };
408
449
  Ok(cx.undefined())
@@ -434,3 +475,33 @@ pub fn client_update_headers(mut cx: FunctionContext) -> JsResult<JsUndefined> {
434
475
 
435
476
  Ok(cx.undefined())
436
477
  }
478
+
479
+ pub(crate) struct HistoryForReplayTunnel {
480
+ pub(crate) runtime: Arc<RuntimeHandle>,
481
+ sender: Cell<Option<Sender<HistoryForReplay>>>,
482
+ }
483
+ impl HistoryForReplayTunnel {
484
+ fn new(runtime: Arc<RuntimeHandle>) -> (Self, ReceiverStream<HistoryForReplay>) {
485
+ let (sender, rx) = channel(1);
486
+ (
487
+ HistoryForReplayTunnel {
488
+ runtime,
489
+ sender: Cell::new(Some(sender)),
490
+ },
491
+ ReceiverStream::new(rx),
492
+ )
493
+ }
494
+ pub fn get_chan(&self) -> Result<Sender<HistoryForReplay>, &'static str> {
495
+ let chan = self.sender.take();
496
+ self.sender.set(chan.clone());
497
+ if let Some(chan) = chan {
498
+ Ok(chan)
499
+ } else {
500
+ Err("History replay channel is already closed")
501
+ }
502
+ }
503
+ pub fn shutdown(&self) {
504
+ self.sender.take();
505
+ }
506
+ }
507
+ impl Finalize for HistoryForReplayTunnel {}
package/src/testing.rs CHANGED
@@ -32,11 +32,14 @@ pub fn get_ephemeral_server_target(mut cx: FunctionContext) -> JsResult<JsString
32
32
  let target = server
33
33
  .borrow()
34
34
  .as_ref()
35
- .map(|s| cx.string(s.core_server.blocking_lock().target.to_owned()));
35
+ .map(|s| cx.string(s.core_server.blocking_lock().target.as_str()));
36
36
  if target.is_none() {
37
- ILLEGAL_STATE_ERROR
38
- .from_string(&mut cx, "Tried to use closed test server")
39
- .and_then(|err| cx.throw(err))?;
37
+ make_named_error_from_string(
38
+ &mut cx,
39
+ ILLEGAL_STATE_ERROR,
40
+ "Tried to use closed test server",
41
+ )
42
+ .and_then(|err| cx.throw(err))?;
40
43
  };
41
44
  Ok(target.unwrap())
42
45
  }
package/src/worker.rs CHANGED
@@ -1,13 +1,10 @@
1
- use crate::conversions::ObjectHandleConversionsExt;
2
- use crate::errors::*;
3
- use crate::helpers::*;
4
- use crate::runtime::*;
1
+ use crate::{conversions::ObjectHandleConversionsExt, errors::*, helpers::*, runtime::*};
5
2
  use futures::stream::StreamExt;
6
- use neon::prelude::*;
7
- use neon::types::buffer::TypedArray;
3
+ use neon::{prelude::*, types::buffer::TypedArray};
8
4
  use opentelemetry::trace::{FutureExt, SpanContext, TraceContextExt};
9
5
  use prost::Message;
10
6
  use std::{cell::RefCell, sync::Arc};
7
+ use temporal_sdk_core::replay::HistoryForReplay;
11
8
  use temporal_sdk_core::{
12
9
  api::{
13
10
  errors::{CompleteActivityError, CompleteWfError, PollActivityError, PollWfError},
@@ -96,7 +93,7 @@ pub async fn start_worker_loop(
96
93
  callback,
97
94
  } => {
98
95
  handle_poll_workflow_activation_request(
99
- &worker, otel_span, channel, callback,
96
+ worker, otel_span, channel, callback,
100
97
  )
101
98
  .await
102
99
  }
@@ -104,7 +101,7 @@ pub async fn start_worker_loop(
104
101
  otel_span,
105
102
  callback,
106
103
  } => {
107
- handle_poll_activity_task_request(&worker, otel_span, channel, callback)
104
+ handle_poll_activity_task_request(worker, otel_span, channel, callback)
108
105
  .await
109
106
  }
110
107
  WorkerRequest::CompleteWorkflowActivation {
@@ -196,12 +193,12 @@ async fn handle_poll_workflow_activation_request(
196
193
  }
197
194
  Err(err) => {
198
195
  send_error(channel, callback, move |cx| match err {
199
- PollWfError::ShutDown => SHUTDOWN_ERROR.from_error(cx, err),
200
- PollWfError::TonicError(_) => TRANSPORT_ERROR.from_error(cx, err),
196
+ PollWfError::ShutDown => make_named_error_from_error(cx, SHUTDOWN_ERROR, err),
197
+ PollWfError::TonicError(_) => make_named_error_from_error(cx, TRANSPORT_ERROR, err),
201
198
  PollWfError::AutocompleteError(CompleteWfError::MalformedWorkflowCompletion {
202
199
  reason,
203
200
  ..
204
- }) => Ok(JsError::type_error(cx, reason)?.upcast()),
201
+ }) => Ok(JsError::type_error(cx, reason)?),
205
202
  });
206
203
  }
207
204
  }
@@ -229,8 +226,10 @@ pub async fn handle_poll_activity_task_request(
229
226
  }
230
227
  Err(err) => {
231
228
  send_error(channel, callback, move |cx| match err {
232
- PollActivityError::ShutDown => SHUTDOWN_ERROR.from_error(cx, err),
233
- PollActivityError::TonicError(_) => TRANSPORT_ERROR.from_error(cx, err),
229
+ PollActivityError::ShutDown => make_named_error_from_error(cx, SHUTDOWN_ERROR, err),
230
+ PollActivityError::TonicError(_) => {
231
+ make_named_error_from_error(cx, TRANSPORT_ERROR, err)
232
+ }
234
233
  });
235
234
  }
236
235
  }
@@ -270,27 +269,51 @@ pub fn worker_new(mut cx: FunctionContext) -> JsResult<JsUndefined> {
270
269
  pub fn replay_worker_new(mut cx: FunctionContext) -> JsResult<JsUndefined> {
271
270
  let runtime = cx.argument::<BoxedRuntime>(0)?;
272
271
  let worker_options = cx.argument::<JsObject>(1)?;
273
- let history_binary = cx.argument::<JsArrayBuffer>(2)?;
274
- let callback = cx.argument::<JsFunction>(3)?;
272
+ let callback = cx.argument::<JsFunction>(2)?;
275
273
 
276
274
  let config = worker_options.as_worker_config(&mut cx)?;
277
- let data = history_binary.as_slice(&mut cx);
275
+ let request = RuntimeRequest::InitReplayWorker {
276
+ runtime: (*runtime).clone(),
277
+ config,
278
+ callback: callback.root(&mut cx),
279
+ };
280
+ if let Err(err) = runtime.sender.send(request) {
281
+ callback_with_unexpected_error(&mut cx, callback, err)?;
282
+ };
283
+
284
+ Ok(cx.undefined())
285
+ }
286
+
287
+ pub fn push_history(mut cx: FunctionContext) -> JsResult<JsUndefined> {
288
+ let pusher = cx.argument::<JsBox<HistoryForReplayTunnel>>(0)?;
289
+ let workflow_id = cx.argument::<JsString>(1)?;
290
+ let history_binary = cx.argument::<JsArrayBuffer>(2)?;
291
+ let callback = cx.argument::<JsFunction>(3)?;
292
+ let data = history_binary.as_slice(&cx);
278
293
  match History::decode_length_delimited(data) {
279
- Ok(history) => {
280
- let request = RuntimeRequest::InitReplayWorker {
281
- config,
282
- history,
283
- callback: callback.root(&mut cx),
284
- };
285
- if let Err(err) = runtime.sender.send(request) {
286
- callback_with_unexpected_error(&mut cx, callback, err)?;
287
- };
294
+ Ok(hist) => {
295
+ let workflow_id = workflow_id.value(&mut cx);
296
+ if let Err(e) = pusher.get_chan().map(|chan| {
297
+ pusher
298
+ .runtime
299
+ .sender
300
+ .send(RuntimeRequest::PushReplayHistory {
301
+ tx: chan,
302
+ pushme: HistoryForReplay::new(hist, workflow_id),
303
+ callback: callback.root(&mut cx),
304
+ })
305
+ }) {
306
+ callback_with_unexpected_error(&mut cx, callback, e)?;
307
+ }
308
+ Ok(cx.undefined())
288
309
  }
289
- Err(_) => callback_with_error(&mut cx, callback, |cx| {
290
- JsError::type_error(cx, "Cannot decode History from buffer")
291
- })?,
310
+ Err(e) => cx.throw_error(format!("Error decoding history: {:?}", e)),
292
311
  }
312
+ }
293
313
 
314
+ pub fn close_history_stream(mut cx: FunctionContext) -> JsResult<JsUndefined> {
315
+ let pusher = cx.argument::<JsBox<HistoryForReplayTunnel>>(0)?;
316
+ pusher.shutdown();
294
317
  Ok(cx.undefined())
295
318
  }
296
319
 
@@ -351,9 +374,7 @@ pub fn worker_complete_workflow_activation(mut cx: FunctionContext) -> JsResult<
351
374
  callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Worker")?;
352
375
  }
353
376
  Some(worker) => {
354
- match WorkflowActivationCompletion::decode_length_delimited(
355
- completion.as_slice(&mut cx),
356
- ) {
377
+ match WorkflowActivationCompletion::decode_length_delimited(completion.as_slice(&cx)) {
357
378
  Ok(completion) => {
358
379
  let request = WorkerRequest::CompleteWorkflowActivation {
359
380
  completion,
@@ -384,7 +405,7 @@ pub fn worker_complete_activity_task(mut cx: FunctionContext) -> JsResult<JsUnde
384
405
  callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Worker")?;
385
406
  }
386
407
  Some(worker) => {
387
- match ActivityTaskCompletion::decode_length_delimited(result.as_slice(&mut cx)) {
408
+ match ActivityTaskCompletion::decode_length_delimited(result.as_slice(&cx)) {
388
409
  Ok(completion) => {
389
410
  let request = WorkerRequest::CompleteActivityTask {
390
411
  completion,
@@ -409,22 +430,20 @@ pub fn worker_record_activity_heartbeat(mut cx: FunctionContext) -> JsResult<JsU
409
430
  let worker = cx.argument::<BoxedWorker>(0)?;
410
431
  let heartbeat = cx.argument::<JsArrayBuffer>(1)?;
411
432
  match worker.borrow().as_ref() {
412
- None => UNEXPECTED_ERROR
413
- .from_string(&mut cx, "Tried to use closed Worker")
414
- .and_then(|err| cx.throw(err))?,
415
- Some(worker) => {
416
- match ActivityHeartbeat::decode_length_delimited(heartbeat.as_slice(&mut cx)) {
417
- Ok(heartbeat) => {
418
- let request = WorkerRequest::RecordActivityHeartbeat { heartbeat };
419
- if let Err(err) = worker.sender.send(request) {
420
- UNEXPECTED_ERROR
421
- .from_error(&mut cx, err)
422
- .and_then(|err| cx.throw(err))?;
423
- }
433
+ None => {
434
+ make_named_error_from_string(&mut cx, UNEXPECTED_ERROR, "Tried to use closed Worker")
435
+ .and_then(|err| cx.throw(err))?
436
+ }
437
+ Some(worker) => match ActivityHeartbeat::decode_length_delimited(heartbeat.as_slice(&cx)) {
438
+ Ok(heartbeat) => {
439
+ let request = WorkerRequest::RecordActivityHeartbeat { heartbeat };
440
+ if let Err(err) = worker.sender.send(request) {
441
+ make_named_error_from_error(&mut cx, UNEXPECTED_ERROR, err)
442
+ .and_then(|err| cx.throw(err))?;
424
443
  }
425
- Err(_) => cx.throw_type_error("Cannot decode ActivityHeartbeat from buffer")?,
426
444
  }
427
- }
445
+ Err(_) => cx.throw_type_error("Cannot decode ActivityHeartbeat from buffer")?,
446
+ },
428
447
  };
429
448
  Ok(cx.undefined())
430
449
  }
@@ -444,8 +463,7 @@ pub fn worker_initiate_shutdown(mut cx: FunctionContext) -> JsResult<JsUndefined
444
463
  if let Err(err) = worker.sender.send(WorkerRequest::InitiateShutdown {
445
464
  callback: callback.root(&mut cx),
446
465
  }) {
447
- UNEXPECTED_ERROR
448
- .from_error(&mut cx, err)
466
+ make_named_error_from_error(&mut cx, UNEXPECTED_ERROR, err)
449
467
  .and_then(|err| cx.throw(err))?;
450
468
  };
451
469
  }
@@ -456,8 +474,7 @@ pub fn worker_initiate_shutdown(mut cx: FunctionContext) -> JsResult<JsUndefined
456
474
  pub fn worker_finalize_shutdown(mut cx: FunctionContext) -> JsResult<JsUndefined> {
457
475
  let worker = cx.argument::<BoxedWorker>(0)?;
458
476
  if worker.replace(None).is_none() {
459
- ILLEGAL_STATE_ERROR
460
- .from_string(&mut cx, "Worker already closed")
477
+ make_named_error_from_string(&mut cx, ILLEGAL_STATE_ERROR, "Worker already closed")
461
478
  .and_then(|err| cx.throw(err))?;
462
479
  }
463
480
 
package/ts/errors.ts ADDED
@@ -0,0 +1,55 @@
1
+ import { IllegalStateError } from '@temporalio/common';
2
+
3
+ /**
4
+ * The worker has been shut down
5
+ */
6
+ export class ShutdownError extends Error {
7
+ public readonly name = 'ShutdownError';
8
+ }
9
+
10
+ /**
11
+ * Thrown after shutdown was requested as a response to a poll function, JS should stop polling
12
+ * once this error is encountered
13
+ */
14
+ export class TransportError extends Error {
15
+ public readonly name = 'TransportError';
16
+ }
17
+
18
+ /**
19
+ * Something unexpected happened, considered fatal
20
+ */
21
+ export class UnexpectedError extends Error {
22
+ public readonly name = 'UnexpectedError';
23
+ }
24
+ export { IllegalStateError };
25
+
26
+ export function convertFromNamedError(e: unknown, keepStackTrace: boolean): unknown {
27
+ // Check if the error's class is exactly Error (not a descendant of it).
28
+ // The instanceof check both ensure that e is indeed an object AND avoid
29
+ // TypeScript from complaining on accessing Error properties.
30
+ if (e instanceof Error && Object.getPrototypeOf(e).name === 'Error') {
31
+ let newerr: Error;
32
+ switch (e.name) {
33
+ case 'TransportError':
34
+ newerr = new TransportError(e.message);
35
+ newerr.stack = keepStackTrace ? e.stack : undefined;
36
+ return newerr;
37
+
38
+ case 'IllegalStateError':
39
+ newerr = new IllegalStateError(e.message);
40
+ newerr.stack = keepStackTrace ? e.stack : undefined;
41
+ return newerr;
42
+
43
+ case 'ShutdownError':
44
+ newerr = new ShutdownError(e.message);
45
+ newerr.stack = keepStackTrace ? e.stack : undefined;
46
+ return newerr;
47
+
48
+ case 'UnexpectedError':
49
+ newerr = new UnexpectedError(e.message);
50
+ newerr.stack = keepStackTrace ? e.stack : undefined;
51
+ return newerr;
52
+ }
53
+ }
54
+ return e;
55
+ }