@temporalio/core-bridge 0.16.4 → 0.18.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 (170) hide show
  1. package/Cargo.lock +339 -226
  2. package/Cargo.toml +7 -3
  3. package/common.js +50 -0
  4. package/index.d.ts +7 -0
  5. package/index.js +12 -0
  6. package/package.json +7 -4
  7. package/releases/aarch64-apple-darwin/index.node +0 -0
  8. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  9. package/{index.node → releases/index.node} +0 -0
  10. package/releases/x86_64-apple-darwin/index.node +0 -0
  11. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  12. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  13. package/scripts/build.js +10 -50
  14. package/sdk-core/.buildkite/docker/Dockerfile +1 -1
  15. package/sdk-core/.buildkite/docker/docker-compose.yaml +2 -2
  16. package/sdk-core/.buildkite/pipeline.yml +2 -0
  17. package/sdk-core/Cargo.toml +1 -88
  18. package/sdk-core/README.md +30 -6
  19. package/sdk-core/bridge-ffi/Cargo.toml +24 -0
  20. package/sdk-core/bridge-ffi/LICENSE.txt +23 -0
  21. package/sdk-core/bridge-ffi/build.rs +25 -0
  22. package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +216 -0
  23. package/sdk-core/bridge-ffi/src/lib.rs +829 -0
  24. package/sdk-core/bridge-ffi/src/wrappers.rs +193 -0
  25. package/sdk-core/client/Cargo.toml +32 -0
  26. package/sdk-core/{src/pollers/gateway.rs → client/src/lib.rs} +101 -195
  27. package/sdk-core/client/src/metrics.rs +89 -0
  28. package/sdk-core/client/src/mocks.rs +167 -0
  29. package/sdk-core/{src/pollers → client/src}/retry.rs +172 -14
  30. package/sdk-core/core/Cargo.toml +96 -0
  31. package/sdk-core/{src → core/src}/core_tests/activity_tasks.rs +193 -37
  32. package/sdk-core/{src → core/src}/core_tests/child_workflows.rs +14 -14
  33. package/sdk-core/{src → core/src}/core_tests/determinism.rs +8 -8
  34. package/sdk-core/core/src/core_tests/local_activities.rs +328 -0
  35. package/sdk-core/{src → core/src}/core_tests/mod.rs +6 -9
  36. package/sdk-core/{src → core/src}/core_tests/queries.rs +54 -54
  37. package/sdk-core/{src → core/src}/core_tests/replay_flag.rs +8 -12
  38. package/sdk-core/{src → core/src}/core_tests/workers.rs +120 -33
  39. package/sdk-core/{src → core/src}/core_tests/workflow_cancels.rs +16 -26
  40. package/sdk-core/{src → core/src}/core_tests/workflow_tasks.rs +280 -292
  41. package/sdk-core/core/src/lib.rs +374 -0
  42. package/sdk-core/{src → core/src}/log_export.rs +3 -27
  43. package/sdk-core/core/src/pending_activations.rs +162 -0
  44. package/sdk-core/{src → core/src}/pollers/mod.rs +4 -22
  45. package/sdk-core/{src → core/src}/pollers/poll_buffer.rs +1 -1
  46. package/sdk-core/core/src/protosext/mod.rs +396 -0
  47. package/sdk-core/core/src/replay/mod.rs +210 -0
  48. package/sdk-core/core/src/retry_logic.rs +144 -0
  49. package/sdk-core/{src → core/src}/telemetry/metrics.rs +3 -58
  50. package/sdk-core/{src → core/src}/telemetry/mod.rs +8 -8
  51. package/sdk-core/{src → core/src}/telemetry/prometheus_server.rs +0 -0
  52. package/sdk-core/{src → core/src}/test_help/mod.rs +35 -83
  53. package/sdk-core/{src → core/src}/worker/activities/activity_heartbeat_manager.rs +95 -42
  54. package/sdk-core/core/src/worker/activities/local_activities.rs +973 -0
  55. package/sdk-core/{src → core/src}/worker/activities.rs +52 -33
  56. package/sdk-core/{src → core/src}/worker/dispatcher.rs +8 -6
  57. package/sdk-core/{src → core/src}/worker/mod.rs +347 -221
  58. package/sdk-core/core/src/worker/wft_delivery.rs +81 -0
  59. package/sdk-core/{src → core/src}/workflow/bridge.rs +5 -2
  60. package/sdk-core/{src → core/src}/workflow/driven_workflow.rs +17 -7
  61. package/sdk-core/{src → core/src}/workflow/history_update.rs +33 -7
  62. package/sdk-core/{src → core/src/workflow}/machines/activity_state_machine.rs +26 -26
  63. package/sdk-core/{src → core/src/workflow}/machines/cancel_external_state_machine.rs +8 -11
  64. package/sdk-core/{src → core/src/workflow}/machines/cancel_workflow_state_machine.rs +19 -21
  65. package/sdk-core/{src → core/src/workflow}/machines/child_workflow_state_machine.rs +20 -31
  66. package/sdk-core/{src → core/src/workflow}/machines/complete_workflow_state_machine.rs +3 -5
  67. package/sdk-core/{src → core/src/workflow}/machines/continue_as_new_workflow_state_machine.rs +18 -18
  68. package/sdk-core/{src → core/src/workflow}/machines/fail_workflow_state_machine.rs +5 -6
  69. package/sdk-core/core/src/workflow/machines/local_activity_state_machine.rs +1451 -0
  70. package/sdk-core/{src → core/src/workflow}/machines/mod.rs +54 -107
  71. package/sdk-core/{src → core/src/workflow}/machines/mutable_side_effect_state_machine.rs +0 -0
  72. package/sdk-core/{src → core/src/workflow}/machines/patch_state_machine.rs +29 -30
  73. package/sdk-core/{src → core/src/workflow}/machines/side_effect_state_machine.rs +0 -0
  74. package/sdk-core/{src → core/src/workflow}/machines/signal_external_state_machine.rs +17 -19
  75. package/sdk-core/{src → core/src/workflow}/machines/timer_state_machine.rs +20 -21
  76. package/sdk-core/{src → core/src/workflow}/machines/transition_coverage.rs +5 -2
  77. package/sdk-core/{src → core/src/workflow}/machines/upsert_search_attributes_state_machine.rs +0 -0
  78. package/sdk-core/core/src/workflow/machines/workflow_machines/local_acts.rs +96 -0
  79. package/sdk-core/{src → core/src/workflow}/machines/workflow_machines.rs +357 -171
  80. package/sdk-core/{src → core/src/workflow}/machines/workflow_task_state_machine.rs +1 -1
  81. package/sdk-core/{src → core/src}/workflow/mod.rs +200 -39
  82. package/sdk-core/{src → core/src}/workflow/workflow_tasks/cache_manager.rs +0 -0
  83. package/sdk-core/{src → core/src}/workflow/workflow_tasks/concurrency_manager.rs +38 -5
  84. package/sdk-core/{src → core/src}/workflow/workflow_tasks/mod.rs +317 -103
  85. package/sdk-core/{test_utils → core-api}/Cargo.toml +10 -7
  86. package/sdk-core/{src → core-api/src}/errors.rs +42 -92
  87. package/sdk-core/core-api/src/lib.rs +158 -0
  88. package/sdk-core/{src/worker/config.rs → core-api/src/worker.rs} +18 -23
  89. package/sdk-core/etc/deps.svg +156 -0
  90. package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +5 -5
  91. package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +3 -5
  92. package/sdk-core/fsm/rustfsm_trait/src/lib.rs +7 -1
  93. package/sdk-core/histories/fail_wf_task.bin +0 -0
  94. package/sdk-core/histories/timer_workflow_history.bin +0 -0
  95. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +44 -13
  96. package/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +19 -1
  97. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +1 -1
  98. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +9 -0
  99. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +1 -0
  100. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +1 -0
  101. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +13 -0
  102. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +14 -7
  103. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +176 -18
  104. package/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +6 -0
  105. package/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +11 -0
  106. package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +3 -0
  107. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +156 -7
  108. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +135 -104
  109. package/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +78 -0
  110. package/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +78 -0
  111. package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +205 -0
  112. package/sdk-core/protos/local/temporal/sdk/core/bridge/service.proto +61 -0
  113. package/sdk-core/protos/local/{child_workflow.proto → temporal/sdk/core/child_workflow/child_workflow.proto} +1 -1
  114. package/sdk-core/protos/local/{common.proto → temporal/sdk/core/common/common.proto} +5 -3
  115. package/sdk-core/protos/local/{core_interface.proto → temporal/sdk/core/core_interface.proto} +10 -10
  116. package/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +30 -0
  117. package/sdk-core/protos/local/{workflow_activation.proto → temporal/sdk/core/workflow_activation/workflow_activation.proto} +35 -11
  118. package/sdk-core/protos/local/{workflow_commands.proto → temporal/sdk/core/workflow_commands/workflow_commands.proto} +55 -4
  119. package/sdk-core/protos/local/{workflow_completion.proto → temporal/sdk/core/workflow_completion/workflow_completion.proto} +3 -3
  120. package/sdk-core/sdk/Cargo.toml +32 -0
  121. package/sdk-core/{src/prototype_rust_sdk → sdk/src}/conversions.rs +0 -0
  122. package/sdk-core/sdk/src/lib.rs +699 -0
  123. package/sdk-core/sdk/src/payload_converter.rs +11 -0
  124. package/sdk-core/sdk/src/workflow_context/options.rs +180 -0
  125. package/sdk-core/{src/prototype_rust_sdk → sdk/src}/workflow_context.rs +201 -124
  126. package/sdk-core/{src/prototype_rust_sdk → sdk/src}/workflow_future.rs +63 -30
  127. package/sdk-core/sdk-core-protos/Cargo.toml +10 -0
  128. package/sdk-core/sdk-core-protos/build.rs +28 -6
  129. package/sdk-core/sdk-core-protos/src/constants.rs +7 -0
  130. package/sdk-core/{src/test_help → sdk-core-protos/src}/history_builder.rs +134 -49
  131. package/sdk-core/sdk-core-protos/src/history_info.rs +216 -0
  132. package/sdk-core/sdk-core-protos/src/lib.rs +601 -168
  133. package/sdk-core/sdk-core-protos/src/task_token.rs +38 -0
  134. package/sdk-core/sdk-core-protos/src/utilities.rs +14 -0
  135. package/sdk-core/test-utils/Cargo.toml +32 -0
  136. package/sdk-core/{src/test_help → test-utils/src}/canned_histories.rs +59 -78
  137. package/sdk-core/test-utils/src/histfetch.rs +28 -0
  138. package/sdk-core/{test_utils → test-utils}/src/lib.rs +131 -68
  139. package/sdk-core/tests/integ_tests/client_tests.rs +1 -1
  140. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +11 -7
  141. package/sdk-core/tests/integ_tests/polling_tests.rs +12 -11
  142. package/sdk-core/tests/integ_tests/queries_tests.rs +82 -78
  143. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +91 -71
  144. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +3 -4
  145. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +2 -4
  146. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +4 -6
  147. package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +4 -6
  148. package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -4
  149. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +496 -0
  150. package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +5 -8
  151. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +125 -0
  152. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +7 -13
  153. package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +33 -5
  154. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +12 -16
  155. package/sdk-core/tests/integ_tests/workflow_tests.rs +85 -82
  156. package/sdk-core/tests/load_tests.rs +6 -6
  157. package/sdk-core/tests/main.rs +2 -2
  158. package/src/conversions.rs +24 -21
  159. package/src/errors.rs +8 -0
  160. package/src/lib.rs +323 -211
  161. package/sdk-core/protos/local/activity_result.proto +0 -46
  162. package/sdk-core/protos/local/activity_task.proto +0 -66
  163. package/sdk-core/src/core_tests/retry.rs +0 -147
  164. package/sdk-core/src/lib.rs +0 -403
  165. package/sdk-core/src/machines/local_activity_state_machine.rs +0 -117
  166. package/sdk-core/src/pending_activations.rs +0 -249
  167. package/sdk-core/src/protosext/mod.rs +0 -160
  168. package/sdk-core/src/prototype_rust_sdk.rs +0 -412
  169. package/sdk-core/src/task_token.rs +0 -20
  170. package/sdk-core/src/test_help/history_info.rs +0 -157
package/src/lib.rs CHANGED
@@ -4,27 +4,42 @@ mod errors;
4
4
  use crate::conversions::ObjectHandleConversionsExt;
5
5
  use errors::*;
6
6
  use neon::prelude::*;
7
+ use once_cell::sync::OnceCell;
7
8
  use opentelemetry::trace::{FutureExt, SpanContext, TraceContextExt};
8
9
  use prost::Message;
9
10
  use std::{
10
11
  fmt::Display,
11
12
  future::Future,
13
+ ops::Deref,
12
14
  sync::Arc,
13
15
  time::{Duration, SystemTime, UNIX_EPOCH},
14
16
  };
15
17
  use temporal_sdk_core::{
16
- errors::{
17
- CompleteActivityError, CompleteWfError, CoreInitError, PollActivityError, PollWfError,
18
+ api::{
19
+ errors::{
20
+ CompleteActivityError, CompleteWfError, CoreInitError, PollActivityError, PollWfError,
21
+ },
22
+ Core,
18
23
  },
19
- init, Core, CoreInitOptions, CoreInitOptionsBuilder, WorkerConfig,
24
+ init,
25
+ protos::{
26
+ coresdk::{
27
+ workflow_completion::WorkflowActivationCompletion, ActivityHeartbeat,
28
+ ActivityTaskCompletion,
29
+ },
30
+ temporal::api::history::v1::History,
31
+ },
32
+ replay::{init_core_replay, ReplayCore, ReplayCoreImpl},
33
+ CoreInitOptionsBuilder, CoreSDK, WorkerConfig,
20
34
  };
21
- use temporal_sdk_core_protos::coresdk::{
22
- workflow_completion::WfActivationCompletion, ActivityHeartbeat, ActivityTaskCompletion,
35
+ use tokio::{
36
+ runtime::Runtime,
37
+ sync::mpsc::{unbounded_channel, UnboundedSender},
23
38
  };
24
- use tokio::sync::mpsc::{unbounded_channel, UnboundedSender};
25
39
 
26
40
  /// A request from JS to bridge to core
27
- pub enum Request {
41
+ #[derive(Debug)]
42
+ enum Request {
28
43
  /// A request to shutdown Core, any registered workers will be shutdown as well.
29
44
  /// Breaks from the thread loop.
30
45
  Shutdown {
@@ -45,6 +60,15 @@ pub enum Request {
45
60
  /// Used to send the result back into JS
46
61
  callback: Root<JsFunction>,
47
62
  },
63
+ /// A request to register a replay worker. Will only work against a replay core instance.
64
+ RegisterReplayWorker {
65
+ /// Worker configuration. Must have unique task queue name.
66
+ config: WorkerConfig,
67
+ /// The history this worker should replay
68
+ history: History,
69
+ /// Used to send the result back into JS
70
+ callback: Root<JsFunction>,
71
+ },
48
72
  /// A request to poll for workflow activations
49
73
  PollWorkflowActivation {
50
74
  /// Name of queue to poll on
@@ -55,7 +79,7 @@ pub enum Request {
55
79
  },
56
80
  /// A request to complete a single workflow activation
57
81
  CompleteWorkflowActivation {
58
- completion: WfActivationCompletion,
82
+ completion: WorkflowActivationCompletion,
59
83
  otel_span: SpanContext,
60
84
  /// Used to send the result back into JS
61
85
  callback: Root<JsFunction>,
@@ -85,27 +109,51 @@ pub enum Request {
85
109
  }
86
110
 
87
111
  #[derive(Clone)]
88
- pub struct CoreHandle {
112
+ struct CoreHandle {
89
113
  sender: UnboundedSender<Request>,
90
114
  }
91
-
92
115
  /// Box it so we can use Core from JS
93
116
  type BoxedCore = JsBox<CoreHandle>;
94
-
95
117
  impl Finalize for CoreHandle {}
96
118
 
97
119
  /// Worker struct, hold a reference for the channel sender responsible for sending requests from
98
120
  /// JS to a bridge thread which forwards them to core
99
- pub struct Worker {
121
+ struct Worker {
100
122
  core: CoreHandle,
101
123
  queue: String,
102
124
  }
103
-
104
125
  /// Box it so we can use Worker from JS
105
126
  type BoxedWorker = JsBox<Worker>;
106
-
107
127
  impl Finalize for Worker {}
108
128
 
129
+ enum CoreType {
130
+ Real(CoreSDK),
131
+ Replay(ReplayCoreImpl),
132
+ }
133
+
134
+ impl Deref for CoreType {
135
+ type Target = dyn Core;
136
+
137
+ fn deref(&self) -> &Self::Target {
138
+ match self {
139
+ CoreType::Real(ref r) => r,
140
+ CoreType::Replay(ref r) => r,
141
+ }
142
+ }
143
+ }
144
+
145
+ /// Lazy-inits or returns a global tokio runtime that we use for interactions with Core(s)
146
+ fn tokio_runtime() -> &'static Runtime {
147
+ static INSTANCE: OnceCell<Runtime> = OnceCell::new();
148
+ INSTANCE.get_or_init(|| {
149
+ tokio::runtime::Builder::new_multi_thread()
150
+ .enable_all()
151
+ .thread_name("core")
152
+ .build()
153
+ .expect("Tokio runtime must construct properly")
154
+ })
155
+ }
156
+
109
157
  /// Send a result to JS via callback using an [EventQueue]
110
158
  fn send_result<F, T>(queue: Arc<EventQueue>, callback: Root<JsFunction>, res_fn: F)
111
159
  where
@@ -196,222 +244,243 @@ async fn void_future_to_js<E, F, ER, EF>(
196
244
  /// Bridges requests from JS to core and sends responses back to JS using a neon::EventQueue.
197
245
  /// Blocks current thread until a [BreakPoller] request is received in channel.
198
246
  fn start_bridge_loop(
199
- core_init_options: CoreInitOptions,
247
+ core_init_fut: impl Future<Output = Result<CoreType, CoreInitError>>,
200
248
  event_queue: Arc<EventQueue>,
201
249
  callback: Root<JsFunction>,
202
250
  ) {
203
251
  let (sender, mut receiver) = unbounded_channel::<Request>();
204
252
 
205
- tokio::runtime::Builder::new_multi_thread()
206
- .enable_all()
207
- .build()
208
- .unwrap()
209
- .block_on(async {
210
- match init(core_init_options).await {
211
- Err(err) => {
212
- send_error(event_queue.clone(), callback, |cx| match err {
213
- CoreInitError::InvalidUri(_) => {
214
- Ok(JsError::type_error(cx, "Invalid URI")?.upcast())
253
+ tokio_runtime().block_on(async {
254
+ match core_init_fut.await {
255
+ Err(err) => {
256
+ send_error(event_queue.clone(), callback, |cx| match err {
257
+ CoreInitError::GatewayInitError(err) => TRANSPORT_ERROR.from_error(cx, err),
258
+ e @ CoreInitError::TelemetryInitError(_) => UNEXPECTED_ERROR.from_error(cx, e),
259
+ });
260
+ }
261
+ Ok(result) => {
262
+ let core = Arc::new(result);
263
+ let core_handle = CoreHandle { sender };
264
+ // Clone once to send back to JS via event queue
265
+ let cloned_core_handle = core_handle.clone();
266
+ send_result(event_queue.clone(), callback, |cx| {
267
+ Ok(cx.boxed(cloned_core_handle))
268
+ });
269
+
270
+ loop {
271
+ let request_option = receiver.recv().await;
272
+ let request = match request_option {
273
+ None => break,
274
+ Some(request) => request,
275
+ };
276
+
277
+ let core = core.clone();
278
+ let event_queue = event_queue.clone();
279
+
280
+ match request {
281
+ Request::Shutdown { callback } => {
282
+ void_future_to_js(
283
+ event_queue,
284
+ callback,
285
+ async move {
286
+ core.shutdown().await;
287
+ // Wrap the empty result in a valid Result object
288
+ let result: Result<(), String> = Ok(());
289
+ result
290
+ },
291
+ |cx, err| UNEXPECTED_ERROR.from_error(cx, err),
292
+ )
293
+ .await;
294
+ break;
215
295
  }
216
- CoreInitError::TonicTransportError(err) => {
217
- TRANSPORT_ERROR.from_error(cx, err)
296
+ Request::PollLogs { callback } => {
297
+ let logs = core.fetch_buffered_logs();
298
+ send_result(event_queue.clone(), callback, |cx| {
299
+ let logarr = cx.empty_array();
300
+ for (i, cl) in logs.into_iter().enumerate() {
301
+ // Not much to do here except for panic when there's an
302
+ // error here.
303
+ let logobj = cx.empty_object();
304
+ let level = cx.string(cl.level.to_string());
305
+ logobj.set(cx, "level", level).unwrap();
306
+ let ts = system_time_to_js(cx, cl.timestamp).unwrap();
307
+ logobj.set(cx, "timestamp", ts).unwrap();
308
+ let msg = cx.string(cl.message);
309
+ logobj.set(cx, "message", msg).unwrap();
310
+ logarr.set(cx, i as u32, logobj).unwrap();
311
+ }
312
+ Ok(logarr)
313
+ });
218
314
  }
219
- e @ CoreInitError::TelemetryInitError(_) => {
220
- UNEXPECTED_ERROR.from_error(cx, e)
315
+ Request::ShutdownWorker {
316
+ task_queue,
317
+ callback,
318
+ } => {
319
+ tokio::spawn(void_future_to_js(
320
+ event_queue,
321
+ callback,
322
+ async move {
323
+ core.shutdown_worker(&task_queue).await;
324
+ // Wrap the empty result in a valid Result object
325
+ let result: Result<(), String> = Ok(());
326
+ result
327
+ },
328
+ |cx, err| UNEXPECTED_ERROR.from_error(cx, err),
329
+ ));
221
330
  }
222
- });
223
- }
224
- Ok(result) => {
225
- let core = Arc::new(result);
226
- let core_handle = CoreHandle {
227
- sender: sender.clone(),
228
- };
229
- // Clone once to send back to JS via event queue
230
- let cloned_core_handle = core_handle.clone();
231
- send_result(event_queue.clone(), callback, |cx| {
232
- Ok(cx.boxed(cloned_core_handle))
233
- });
234
-
235
- loop {
236
- let request_option = receiver.recv().await;
237
- let request = match request_option {
238
- None => break,
239
- Some(request) => request,
240
- };
241
- let core = core.clone();
242
- let event_queue = event_queue.clone();
243
-
244
- match request {
245
- Request::Shutdown { callback } => {
246
- void_future_to_js(
247
- event_queue,
248
- callback,
249
- async move {
250
- core.shutdown().await;
251
- // Wrap the empty result in a valid Result object
252
- let result: Result<(), String> = Ok(());
253
- result
254
- },
255
- |cx, err| UNEXPECTED_ERROR.from_error(cx, err),
331
+ Request::RegisterWorker { config, callback } => {
332
+ let task_queue = config.task_queue.clone();
333
+ match core.register_worker(config) {
334
+ Ok(_) => {
335
+ let core_handle = core_handle.clone();
336
+ send_result(event_queue.clone(), callback, |cx| {
337
+ Ok(cx.boxed(Worker {
338
+ core: core_handle,
339
+ queue: task_queue,
340
+ }))
341
+ })
342
+ }
343
+ Err(err) => send_error(event_queue.clone(), callback, |cx| {
344
+ UNEXPECTED_ERROR.from_error(cx, err)
345
+ }),
346
+ };
347
+ }
348
+ Request::RegisterReplayWorker {
349
+ config,
350
+ history,
351
+ callback,
352
+ } => match *core {
353
+ CoreType::Real(_) => {
354
+ panic!(
355
+ "Attempted to use a real core instance to register a \
356
+ replay worker. This is a bug in the TS SDK."
256
357
  )
257
- .await;
258
- break;
259
- }
260
- Request::PollLogs { callback } => {
261
- let logs = core.fetch_buffered_logs();
262
- send_result(event_queue.clone(), callback, |cx| {
263
- let logarr = cx.empty_array();
264
- for (i, cl) in logs.into_iter().enumerate() {
265
- // Not much to do here except for panic when there's an
266
- // error here.
267
- let logobj = cx.empty_object();
268
- let level = cx.string(cl.level.to_string());
269
- logobj.set(cx, "level", level).unwrap();
270
- let ts = system_time_to_js(cx, cl.timestamp).unwrap();
271
- logobj.set(cx, "timestamp", ts).unwrap();
272
- let msg = cx.string(cl.message);
273
- logobj.set(cx, "message", msg).unwrap();
274
- logarr.set(cx, i as u32, logobj).unwrap();
275
- }
276
- Ok(logarr)
277
- });
278
358
  }
279
- Request::ShutdownWorker {
280
- task_queue,
281
- callback,
282
- } => {
283
- tokio::spawn(void_future_to_js(
284
- event_queue,
285
- callback,
286
- async move {
287
- core.shutdown_worker(&task_queue).await;
288
- // Wrap the empty result in a valid Result object
289
- let result: Result<(), String> = Ok(());
290
- result
291
- },
292
- |cx, err| UNEXPECTED_ERROR.from_error(cx, err),
293
- ));
294
- }
295
- Request::RegisterWorker { config, callback } => {
296
- let task_queue = config.clone().task_queue;
297
- let core_handle = core_handle.clone();
298
- tokio::spawn(async move {
299
- match core.register_worker(config).await {
300
- Ok(_) => send_result(event_queue.clone(), callback, |cx| {
359
+ CoreType::Replay(ref rc) => {
360
+ let task_queue = config.task_queue.clone();
361
+ match rc.make_replay_worker(config, &history) {
362
+ Ok(_) => {
363
+ let core_handle = core_handle.clone();
364
+ send_result(event_queue.clone(), callback, |cx| {
301
365
  Ok(cx.boxed(Worker {
302
366
  core: core_handle,
303
367
  queue: task_queue,
304
368
  }))
305
- }),
306
- Err(err) => {
307
- send_error(event_queue.clone(), callback, |cx| {
308
- UNEXPECTED_ERROR.from_error(cx, err)
309
- })
310
- }
311
- };
312
- });
369
+ })
370
+ }
371
+ Err(err) => send_error(event_queue.clone(), callback, |cx| {
372
+ UNEXPECTED_ERROR.from_error(cx, err)
373
+ }),
374
+ };
313
375
  }
314
- Request::PollWorkflowActivation {
315
- queue_name,
316
- otel_span,
317
- callback,
318
- } => {
319
- tokio::spawn(handle_poll_workflow_activation_request(
376
+ },
377
+ Request::PollWorkflowActivation {
378
+ queue_name,
379
+ otel_span,
380
+ callback,
381
+ } => {
382
+ // dbg!(&queue_name);
383
+ tokio::spawn(async move {
384
+ handle_poll_workflow_activation_request(
320
385
  queue_name,
321
386
  otel_span,
322
- core,
387
+ core.as_ref().deref(),
323
388
  event_queue,
324
389
  callback,
325
- ));
326
- }
327
- Request::PollActivityTask {
328
- queue_name,
329
- otel_span,
330
- callback,
331
- } => {
332
- tokio::spawn(handle_poll_activity_task_request(
390
+ )
391
+ .await
392
+ });
393
+ }
394
+ Request::PollActivityTask {
395
+ queue_name,
396
+ otel_span,
397
+ callback,
398
+ } => {
399
+ tokio::spawn(async move {
400
+ handle_poll_activity_task_request(
333
401
  queue_name,
334
402
  otel_span,
335
- core,
403
+ core.as_ref().deref(),
336
404
  event_queue,
337
405
  callback,
338
- ));
339
- }
340
- Request::CompleteWorkflowActivation {
341
- completion,
342
- otel_span,
406
+ )
407
+ .await
408
+ });
409
+ }
410
+ Request::CompleteWorkflowActivation {
411
+ completion,
412
+ otel_span,
413
+ callback,
414
+ } => {
415
+ let otel_ctx =
416
+ opentelemetry::Context::new().with_remote_span_context(otel_span);
417
+ tokio::spawn(void_future_to_js(
418
+ event_queue,
343
419
  callback,
344
- } => {
345
- let otel_ctx = opentelemetry::Context::new()
346
- .with_remote_span_context(otel_span);
347
- tokio::spawn(void_future_to_js(
348
- event_queue,
349
- callback,
350
- async move {
351
- core.complete_workflow_activation(completion)
352
- .with_context(otel_ctx)
353
- .await
354
- },
355
- |cx, err| match err {
356
- CompleteWfError::NoWorkerForQueue(queue_name) => {
357
- let args = vec![cx.string(queue_name).upcast()];
358
- NO_WORKER_ERROR.construct(cx, args)
359
- }
360
- CompleteWfError::TonicError(_) => {
361
- TRANSPORT_ERROR.from_error(cx, err)
362
- }
363
- CompleteWfError::MalformedWorkflowCompletion {
364
- reason,
365
- ..
366
- } => Ok(JsError::type_error(cx, reason)?.upcast()),
367
- },
368
- ));
369
- }
370
- Request::CompleteActivityTask {
371
- completion,
372
- otel_span,
420
+ async move {
421
+ core.complete_workflow_activation(completion)
422
+ .with_context(otel_ctx)
423
+ .await
424
+ },
425
+ |cx, err| match err {
426
+ CompleteWfError::NoWorkerForQueue(queue_name) => {
427
+ let args = vec![cx.string(queue_name).upcast()];
428
+ NO_WORKER_ERROR.construct(cx, args)
429
+ }
430
+ CompleteWfError::TonicError(_) => {
431
+ TRANSPORT_ERROR.from_error(cx, err)
432
+ }
433
+ CompleteWfError::MalformedWorkflowCompletion {
434
+ reason, ..
435
+ } => Ok(JsError::type_error(cx, reason)?.upcast()),
436
+ },
437
+ ));
438
+ }
439
+ Request::CompleteActivityTask {
440
+ completion,
441
+ otel_span,
442
+ callback,
443
+ } => {
444
+ let otel_ctx =
445
+ opentelemetry::Context::new().with_remote_span_context(otel_span);
446
+ tokio::spawn(void_future_to_js(
447
+ event_queue,
373
448
  callback,
374
- } => {
375
- let otel_ctx = opentelemetry::Context::new()
376
- .with_remote_span_context(otel_span);
377
- tokio::spawn(void_future_to_js(
378
- event_queue,
379
- callback,
380
- async move {
381
- core.complete_activity_task(completion)
382
- .with_context(otel_ctx)
383
- .await
384
- },
385
- |cx, err| match err {
386
- CompleteActivityError::MalformedActivityCompletion {
387
- reason,
388
- ..
389
- } => Ok(JsError::type_error(cx, reason)?.upcast()),
390
- CompleteActivityError::TonicError(_) => {
391
- TRANSPORT_ERROR.from_error(cx, err)
392
- }
393
- CompleteActivityError::NoWorkerForQueue(queue_name) => {
394
- let args = vec![cx.string(queue_name).upcast()];
395
- NO_WORKER_ERROR.construct(cx, args)
396
- }
397
- },
398
- ));
399
- }
400
- Request::RecordActivityHeartbeat { heartbeat } => {
401
- core.record_activity_heartbeat(heartbeat)
402
- }
449
+ async move {
450
+ core.complete_activity_task(completion)
451
+ .with_context(otel_ctx)
452
+ .await
453
+ },
454
+ |cx, err| match err {
455
+ CompleteActivityError::MalformedActivityCompletion {
456
+ reason,
457
+ ..
458
+ } => Ok(JsError::type_error(cx, reason)?.upcast()),
459
+ CompleteActivityError::TonicError(_) => {
460
+ TRANSPORT_ERROR.from_error(cx, err)
461
+ }
462
+ CompleteActivityError::NoWorkerForQueue(queue_name) => {
463
+ let args = vec![cx.string(queue_name).upcast()];
464
+ NO_WORKER_ERROR.construct(cx, args)
465
+ }
466
+ },
467
+ ));
468
+ }
469
+ Request::RecordActivityHeartbeat { heartbeat } => {
470
+ core.record_activity_heartbeat(heartbeat)
403
471
  }
404
472
  }
405
473
  }
406
474
  }
407
- })
475
+ }
476
+ })
408
477
  }
409
478
 
410
479
  /// Called within the poll loop thread, calls core and triggers JS callback with result
411
480
  async fn handle_poll_workflow_activation_request(
412
481
  queue_name: String,
413
482
  span_context: SpanContext,
414
- core: Arc<impl Core>,
483
+ core: &dyn Core,
415
484
  event_queue: Arc<EventQueue>,
416
485
  callback: Root<JsFunction>,
417
486
  ) {
@@ -462,7 +531,7 @@ async fn handle_poll_workflow_activation_request(
462
531
  async fn handle_poll_activity_task_request(
463
532
  queue_name: String,
464
533
  span_context: SpanContext,
465
- core: Arc<impl Core>,
534
+ core: &dyn Core,
466
535
  event_queue: Arc<EventQueue>,
467
536
  callback: Root<JsFunction>,
468
537
  ) {
@@ -517,21 +586,30 @@ fn core_new(mut cx: FunctionContext) -> JsResult<JsUndefined> {
517
586
 
518
587
  let callback = cx.argument::<JsFunction>(1)?.root(&mut cx);
519
588
  let queue = Arc::new(cx.queue());
520
- std::thread::spawn(move || {
521
- start_bridge_loop(
522
- CoreInitOptionsBuilder::default()
523
- .gateway_opts(gateway_opts)
524
- .telemetry_opts(telemetry_opts)
525
- .build()
526
- .expect("Core init options must be valid"),
527
- queue,
528
- callback,
529
- )
530
- });
589
+ let core_init_fut = async move {
590
+ let opts = CoreInitOptionsBuilder::default()
591
+ .gateway_opts(gateway_opts)
592
+ .telemetry_opts(telemetry_opts)
593
+ .build()
594
+ .expect("Core init options must be valid");
595
+ Ok(CoreType::Real(init(opts).await?))
596
+ };
597
+ std::thread::spawn(move || start_bridge_loop(core_init_fut, queue, callback));
531
598
 
532
599
  Ok(cx.undefined())
533
600
  }
534
601
 
602
+ /// Create a new "replay" instance of core, which can only be used for replaying static histories.
603
+ fn replay_core_new(mut cx: FunctionContext) -> JsResult<JsUndefined> {
604
+ let telem_options = cx.argument::<JsObject>(0)?;
605
+ let telem_options = telem_options.as_telemetry_options(&mut cx)?;
606
+ let callback = cx.argument::<JsFunction>(1)?.root(&mut cx);
607
+ let queue = Arc::new(cx.queue());
608
+ let core_init_fut = async { Ok(CoreType::Replay(init_core_replay(telem_options))) };
609
+ std::thread::spawn(move || start_bridge_loop(core_init_fut, queue, callback));
610
+ Ok(cx.undefined())
611
+ }
612
+
535
613
  /// Create a new worker asynchronously.
536
614
  /// Worker is registered on supplied core instance and returned to JS using supplied callback.
537
615
  fn worker_new(mut cx: FunctionContext) -> JsResult<JsUndefined> {
@@ -552,6 +630,38 @@ fn worker_new(mut cx: FunctionContext) -> JsResult<JsUndefined> {
552
630
  Ok(cx.undefined())
553
631
  }
554
632
 
633
+ /// Create a new replay worker asynchronously.
634
+ /// Worker is registered on supplied core instance and returned to JS using supplied callback.
635
+ /// The provided core instance must be a replay core.
636
+ fn replay_worker_new(mut cx: FunctionContext) -> JsResult<JsUndefined> {
637
+ let core = cx.argument::<BoxedCore>(0)?;
638
+ let worker_options = cx.argument::<JsObject>(1)?;
639
+ let history_binary = cx.argument::<JsArrayBuffer>(2)?;
640
+ let callback = cx.argument::<JsFunction>(3)?;
641
+
642
+ let config = worker_options.as_worker_config(&mut cx)?;
643
+
644
+ match cx.borrow(&history_binary, |data| {
645
+ History::decode_length_delimited(data.as_slice::<u8>())
646
+ }) {
647
+ Ok(history) => {
648
+ let request = Request::RegisterReplayWorker {
649
+ config,
650
+ history,
651
+ callback: callback.root(&mut cx),
652
+ };
653
+ if let Err(err) = core.sender.send(request) {
654
+ callback_with_unexpected_error(&mut cx, callback, err)?;
655
+ };
656
+ }
657
+ Err(_) => callback_with_error(&mut cx, callback, |cx| {
658
+ JsError::type_error(cx, "Cannot decode History from buffer")
659
+ })?,
660
+ }
661
+
662
+ Ok(cx.undefined())
663
+ }
664
+
555
665
  /// Shutdown the Core instance and break out of the thread loop
556
666
  fn core_shutdown(mut cx: FunctionContext) -> JsResult<JsUndefined> {
557
667
  let core = cx.argument::<BoxedCore>(0)?;
@@ -619,12 +729,12 @@ fn worker_complete_workflow_activation(mut cx: FunctionContext) -> JsResult<JsUn
619
729
  let completion = cx.argument::<JsArrayBuffer>(2)?;
620
730
  let callback = cx.argument::<JsFunction>(3)?;
621
731
  let result = cx.borrow(&completion, |data| {
622
- WfActivationCompletion::decode_length_delimited(data.as_slice::<u8>())
732
+ WorkflowActivationCompletion::decode_length_delimited(data.as_slice::<u8>())
623
733
  });
624
734
  match result {
625
735
  Ok(completion) => {
626
736
  // Add the task queue from our Worker
627
- let completion = WfActivationCompletion {
737
+ let completion = WorkflowActivationCompletion {
628
738
  task_queue: worker.queue.clone(),
629
739
  ..completion
630
740
  };
@@ -634,7 +744,7 @@ fn worker_complete_workflow_activation(mut cx: FunctionContext) -> JsResult<JsUn
634
744
  callback: callback.root(&mut cx),
635
745
  };
636
746
  if let Err(err) = worker.core.sender.send(request) {
637
- callback_with_error(&mut cx, callback, |cx| UNEXPECTED_ERROR.from_error(cx, err))?;
747
+ callback_with_unexpected_error(&mut cx, callback, err)?;
638
748
  };
639
749
  }
640
750
  Err(_) => callback_with_error(&mut cx, callback, |cx| {
@@ -732,7 +842,7 @@ where
732
842
  let ts = cx.empty_array();
733
843
  ts.set(cx, 0, ts_seconds).unwrap();
734
844
  ts.set(cx, 1, only_nanos).unwrap();
735
- return Ok(ts);
845
+ Ok(ts)
736
846
  }
737
847
 
738
848
  /// Helper to get the current time in nanosecond resolution.
@@ -745,7 +855,9 @@ fn main(mut cx: ModuleContext) -> NeonResult<()> {
745
855
  cx.export_function("getTimeOfDay", get_time_of_day)?;
746
856
  cx.export_function("registerErrors", errors::register_errors)?;
747
857
  cx.export_function("newCore", core_new)?;
858
+ cx.export_function("newReplayCore", replay_core_new)?;
748
859
  cx.export_function("newWorker", worker_new)?;
860
+ cx.export_function("newReplayWorker", replay_worker_new)?;
749
861
  cx.export_function("workerShutdown", worker_shutdown)?;
750
862
  cx.export_function("coreShutdown", core_shutdown)?;
751
863
  cx.export_function("corePollLogs", core_poll_logs)?;