@temporalio/core-bridge 0.22.0 → 1.0.0-rc.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 (139) hide show
  1. package/Cargo.lock +120 -15
  2. package/Cargo.toml +3 -1
  3. package/README.md +1 -1
  4. package/index.d.ts +137 -33
  5. package/package.json +6 -6
  6. package/releases/aarch64-apple-darwin/index.node +0 -0
  7. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  8. package/releases/x86_64-apple-darwin/index.node +0 -0
  9. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  10. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  11. package/sdk-core/.buildkite/docker/docker-compose.yaml +4 -2
  12. package/sdk-core/ARCHITECTURE.md +9 -7
  13. package/sdk-core/README.md +5 -1
  14. package/sdk-core/arch_docs/diagrams/workflow_internals.svg +1 -0
  15. package/sdk-core/bridge-ffi/src/lib.rs +1 -1
  16. package/sdk-core/bridge-ffi/src/wrappers.rs +60 -37
  17. package/sdk-core/client/Cargo.toml +1 -0
  18. package/sdk-core/client/src/lib.rs +50 -15
  19. package/sdk-core/client/src/raw.rs +167 -55
  20. package/sdk-core/client/src/retry.rs +9 -4
  21. package/sdk-core/client/src/workflow_handle/mod.rs +4 -2
  22. package/sdk-core/core/Cargo.toml +2 -0
  23. package/sdk-core/core/benches/workflow_replay.rs +1 -7
  24. package/sdk-core/core/src/abstractions.rs +137 -16
  25. package/sdk-core/core/src/core_tests/activity_tasks.rs +258 -63
  26. package/sdk-core/core/src/core_tests/child_workflows.rs +1 -2
  27. package/sdk-core/core/src/core_tests/determinism.rs +2 -2
  28. package/sdk-core/core/src/core_tests/local_activities.rs +8 -7
  29. package/sdk-core/core/src/core_tests/queries.rs +146 -60
  30. package/sdk-core/core/src/core_tests/replay_flag.rs +1 -1
  31. package/sdk-core/core/src/core_tests/workers.rs +39 -23
  32. package/sdk-core/core/src/core_tests/workflow_cancels.rs +1 -1
  33. package/sdk-core/core/src/core_tests/workflow_tasks.rs +387 -280
  34. package/sdk-core/core/src/lib.rs +8 -5
  35. package/sdk-core/core/src/pollers/poll_buffer.rs +16 -10
  36. package/sdk-core/core/src/protosext/mod.rs +7 -9
  37. package/sdk-core/core/src/retry_logic.rs +73 -16
  38. package/sdk-core/core/src/telemetry/metrics.rs +21 -7
  39. package/sdk-core/core/src/telemetry/mod.rs +182 -110
  40. package/sdk-core/core/src/test_help/mod.rs +341 -109
  41. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +18 -9
  42. package/sdk-core/core/src/worker/activities/local_activities.rs +22 -25
  43. package/sdk-core/core/src/worker/activities.rs +156 -29
  44. package/sdk-core/core/src/worker/client.rs +1 -0
  45. package/sdk-core/core/src/worker/mod.rs +132 -659
  46. package/sdk-core/core/src/{workflow → worker/workflow}/bridge.rs +1 -1
  47. package/sdk-core/core/src/{workflow → worker/workflow}/driven_workflow.rs +1 -1
  48. package/sdk-core/core/src/{workflow → worker/workflow}/history_update.rs +16 -2
  49. package/sdk-core/core/src/{workflow → worker/workflow}/machines/activity_state_machine.rs +39 -4
  50. package/sdk-core/core/src/{workflow → worker/workflow}/machines/cancel_external_state_machine.rs +5 -2
  51. package/sdk-core/core/src/{workflow → worker/workflow}/machines/cancel_workflow_state_machine.rs +1 -1
  52. package/sdk-core/core/src/{workflow → worker/workflow}/machines/child_workflow_state_machine.rs +2 -4
  53. package/sdk-core/core/src/{workflow → worker/workflow}/machines/complete_workflow_state_machine.rs +0 -0
  54. package/sdk-core/core/src/{workflow → worker/workflow}/machines/continue_as_new_workflow_state_machine.rs +1 -1
  55. package/sdk-core/core/src/{workflow → worker/workflow}/machines/fail_workflow_state_machine.rs +0 -0
  56. package/sdk-core/core/src/{workflow → worker/workflow}/machines/local_activity_state_machine.rs +2 -5
  57. package/sdk-core/core/src/{workflow → worker/workflow}/machines/mod.rs +1 -1
  58. package/sdk-core/core/src/{workflow → worker/workflow}/machines/mutable_side_effect_state_machine.rs +0 -0
  59. package/sdk-core/core/src/{workflow → worker/workflow}/machines/patch_state_machine.rs +1 -1
  60. package/sdk-core/core/src/{workflow → worker/workflow}/machines/side_effect_state_machine.rs +0 -0
  61. package/sdk-core/core/src/{workflow → worker/workflow}/machines/signal_external_state_machine.rs +4 -2
  62. package/sdk-core/core/src/{workflow → worker/workflow}/machines/timer_state_machine.rs +1 -2
  63. package/sdk-core/core/src/{workflow → worker/workflow}/machines/transition_coverage.rs +1 -1
  64. package/sdk-core/core/src/{workflow → worker/workflow}/machines/upsert_search_attributes_state_machine.rs +5 -7
  65. package/sdk-core/core/src/{workflow → worker/workflow}/machines/workflow_machines/local_acts.rs +2 -2
  66. package/sdk-core/core/src/{workflow → worker/workflow}/machines/workflow_machines.rs +40 -16
  67. package/sdk-core/core/src/{workflow → worker/workflow}/machines/workflow_task_state_machine.rs +0 -0
  68. package/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +198 -0
  69. package/sdk-core/core/src/worker/workflow/managed_run.rs +627 -0
  70. package/sdk-core/core/src/worker/workflow/mod.rs +1115 -0
  71. package/sdk-core/core/src/worker/workflow/run_cache.rs +143 -0
  72. package/sdk-core/core/src/worker/workflow/wft_poller.rs +88 -0
  73. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +936 -0
  74. package/sdk-core/core-api/src/errors.rs +3 -10
  75. package/sdk-core/core-api/src/lib.rs +2 -1
  76. package/sdk-core/core-api/src/worker.rs +26 -2
  77. package/sdk-core/etc/dynamic-config.yaml +2 -0
  78. package/sdk-core/integ-with-otel.sh +1 -1
  79. package/sdk-core/protos/api_upstream/Makefile +4 -4
  80. package/sdk-core/protos/api_upstream/api-linter.yaml +2 -0
  81. package/sdk-core/protos/api_upstream/buf.yaml +8 -9
  82. package/sdk-core/protos/api_upstream/temporal/api/cluster/v1/message.proto +83 -0
  83. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +7 -1
  84. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/cluster.proto +40 -0
  85. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +3 -0
  86. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +3 -1
  87. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/schedule.proto +60 -0
  88. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +3 -0
  89. package/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +32 -4
  90. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +69 -19
  91. package/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +13 -0
  92. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +163 -0
  93. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +97 -0
  94. package/sdk-core/protos/api_upstream/temporal/api/schedule/v1/message.proto +300 -0
  95. package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +25 -0
  96. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +180 -3
  97. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +53 -3
  98. package/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +2 -2
  99. package/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +6 -5
  100. package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +27 -6
  101. package/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +2 -1
  102. package/sdk-core/protos/local/temporal/sdk/core/common/common.proto +0 -64
  103. package/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +2 -1
  104. package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +11 -8
  105. package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +30 -25
  106. package/sdk-core/sdk/src/activity_context.rs +12 -5
  107. package/sdk-core/sdk/src/app_data.rs +37 -0
  108. package/sdk-core/sdk/src/lib.rs +76 -43
  109. package/sdk-core/sdk/src/workflow_context/options.rs +8 -6
  110. package/sdk-core/sdk/src/workflow_context.rs +14 -19
  111. package/sdk-core/sdk/src/workflow_future.rs +11 -6
  112. package/sdk-core/sdk-core-protos/src/history_builder.rs +19 -5
  113. package/sdk-core/sdk-core-protos/src/history_info.rs +11 -6
  114. package/sdk-core/sdk-core-protos/src/lib.rs +87 -176
  115. package/sdk-core/test-utils/src/histfetch.rs +1 -1
  116. package/sdk-core/test-utils/src/lib.rs +93 -77
  117. package/sdk-core/tests/integ_tests/client_tests.rs +2 -2
  118. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +11 -9
  119. package/sdk-core/tests/integ_tests/polling_tests.rs +12 -0
  120. package/sdk-core/tests/integ_tests/queries_tests.rs +39 -22
  121. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +49 -4
  122. package/sdk-core/tests/integ_tests/workflow_tests/appdata_propagation.rs +61 -0
  123. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -1
  124. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +74 -13
  125. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +19 -0
  126. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -1
  127. package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +6 -3
  128. package/sdk-core/tests/integ_tests/workflow_tests.rs +10 -23
  129. package/sdk-core/tests/load_tests.rs +8 -3
  130. package/sdk-core/tests/main.rs +7 -3
  131. package/src/conversions.rs +149 -70
  132. package/src/errors.rs +10 -21
  133. package/src/lib.rs +400 -319
  134. package/sdk-core/core/src/pending_activations.rs +0 -173
  135. package/sdk-core/core/src/worker/wft_delivery.rs +0 -81
  136. package/sdk-core/core/src/workflow/mod.rs +0 -478
  137. package/sdk-core/core/src/workflow/workflow_tasks/cache_manager.rs +0 -194
  138. package/sdk-core/core/src/workflow/workflow_tasks/concurrency_manager.rs +0 -418
  139. package/sdk-core/core/src/workflow/workflow_tasks/mod.rs +0 -989
package/src/lib.rs CHANGED
@@ -1,12 +1,16 @@
1
1
  mod conversions;
2
2
  mod errors;
3
3
 
4
- use crate::conversions::ObjectHandleConversionsExt;
4
+ use crate::conversions::{get_optional, ObjectHandleConversionsExt};
5
5
  use errors::*;
6
+ use futures::stream::StreamExt;
6
7
  use neon::prelude::*;
7
- use once_cell::sync::OnceCell;
8
+ use neon::types::buffer::TypedArray;
8
9
  use opentelemetry::trace::{FutureExt, SpanContext, TraceContextExt};
10
+ use parking_lot::RwLock;
9
11
  use prost::Message;
12
+ use std::collections::HashMap;
13
+ use std::ops::Deref;
10
14
  use std::{
11
15
  cell::RefCell,
12
16
  fmt::Display,
@@ -20,7 +24,7 @@ use temporal_client::{
20
24
  use temporal_sdk_core::{
21
25
  api::{
22
26
  errors::{CompleteActivityError, CompleteWfError, PollActivityError, PollWfError},
23
- Worker as CoreWorker,
27
+ Worker as CoreWorkerTrait,
24
28
  },
25
29
  fetch_global_buffered_logs, init_replay_worker, init_worker,
26
30
  protos::{
@@ -30,17 +34,18 @@ use temporal_sdk_core::{
30
34
  },
31
35
  temporal::api::history::v1::History,
32
36
  },
33
- telemetry_init, ClientOptions, RetryClient, WorkerConfig,
37
+ telemetry_init, ClientOptions, RetryClient, Worker as CoreWorker, WorkerConfig,
34
38
  };
35
39
  use tokio::{
36
40
  runtime::Runtime,
37
41
  sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
38
42
  };
43
+ use tokio_stream::wrappers::UnboundedReceiverStream;
39
44
 
40
45
  type RawClient = RetryClient<ConfiguredClient<WorkflowServiceClientWithMetrics>>;
41
46
 
42
47
  /// A request from JS to bridge to core
43
- enum Request {
48
+ enum RuntimeRequest {
44
49
  /// A request to shutdown the runtime, breaks from the thread loop.
45
50
  Shutdown {
46
51
  /// Used to send the result back into JS
@@ -50,19 +55,19 @@ enum Request {
50
55
  CreateClient {
51
56
  runtime: Arc<RuntimeHandle>,
52
57
  options: ClientOptions,
58
+ headers: Option<HashMap<String, String>>,
53
59
  /// Used to send the result back into JS
54
60
  callback: Root<JsFunction>,
55
61
  },
56
- /// A request to shutdown a worker, the worker instance will remain active to
57
- /// allow draining of pending tasks
58
- ShutdownWorker {
59
- worker: Arc<dyn CoreWorker>,
62
+ /// A request to update a client's HTTP request headers
63
+ UpdateClientHeaders {
64
+ client: Arc<RawClient>,
65
+ headers: HashMap<String, String>,
60
66
  /// Used to send the result back into JS
61
67
  callback: Root<JsFunction>,
62
68
  },
63
69
  /// A request to create a new Worker using a connected client
64
70
  InitWorker {
65
- runtime: Arc<RuntimeHandle>,
66
71
  /// Worker configuration e.g. limits and task queue
67
72
  config: WorkerConfig,
68
73
  /// A client created with a [CreateClient] request
@@ -72,7 +77,6 @@ enum Request {
72
77
  },
73
78
  /// A request to register a replay worker
74
79
  InitReplayWorker {
75
- runtime: Arc<RuntimeHandle>,
76
80
  /// Worker configuration. Must have unique task queue name.
77
81
  config: WorkerConfig,
78
82
  /// The history this worker should replay
@@ -80,16 +84,29 @@ enum Request {
80
84
  /// Used to send the result back into JS
81
85
  callback: Root<JsFunction>,
82
86
  },
87
+ /// A request to drain logs from core so they can be emitted in node
88
+ PollLogs {
89
+ /// Logs are sent to this function
90
+ callback: Root<JsFunction>,
91
+ },
92
+ }
93
+
94
+ #[derive(Debug)]
95
+ enum WorkerRequest {
96
+ /// A request to shutdown a worker, the worker instance will remain active to
97
+ /// allow draining of pending tasks
98
+ InitiateShutdown {
99
+ /// Used to send the result back into JS
100
+ callback: Root<JsFunction>,
101
+ },
83
102
  /// A request to poll for workflow activations
84
103
  PollWorkflowActivation {
85
- worker: Arc<dyn CoreWorker>,
86
104
  otel_span: SpanContext,
87
105
  /// Used to send the result back into JS
88
106
  callback: Root<JsFunction>,
89
107
  },
90
108
  /// A request to complete a single workflow activation
91
109
  CompleteWorkflowActivation {
92
- worker: Arc<dyn CoreWorker>,
93
110
  completion: WorkflowActivationCompletion,
94
111
  otel_span: SpanContext,
95
112
  /// Used to send the result back into JS
@@ -97,33 +114,23 @@ enum Request {
97
114
  },
98
115
  /// A request to poll for activity tasks
99
116
  PollActivityTask {
100
- worker: Arc<dyn CoreWorker>,
101
117
  otel_span: SpanContext,
102
118
  /// Used to report completion or error back into JS
103
119
  callback: Root<JsFunction>,
104
120
  },
105
121
  /// A request to complete a single activity task
106
122
  CompleteActivityTask {
107
- worker: Arc<dyn CoreWorker>,
108
123
  completion: ActivityTaskCompletion,
109
124
  otel_span: SpanContext,
110
125
  /// Used to send the result back into JS
111
126
  callback: Root<JsFunction>,
112
127
  },
113
128
  /// A request to send a heartbeat from a running activity
114
- RecordActivityHeartbeat {
115
- worker: Arc<dyn CoreWorker>,
116
- heartbeat: ActivityHeartbeat,
117
- },
118
- /// A request to drain logs from core so they can be emitted in node
119
- PollLogs {
120
- /// Logs are sent to this function
121
- callback: Root<JsFunction>,
122
- },
129
+ RecordActivityHeartbeat { heartbeat: ActivityHeartbeat },
123
130
  }
124
131
 
125
132
  struct RuntimeHandle {
126
- sender: UnboundedSender<Request>,
133
+ sender: UnboundedSender<RuntimeRequest>,
127
134
  }
128
135
 
129
136
  /// Box it so we can use the runtime from JS
@@ -141,34 +148,30 @@ impl Finalize for Client {}
141
148
 
142
149
  /// Worker struct, hold a reference for the channel sender responsible for sending requests from
143
150
  /// JS to a bridge thread which forwards them to core
144
- struct Worker {
145
- runtime: Arc<RuntimeHandle>,
146
- core_worker: Arc<dyn CoreWorker>,
151
+ struct WorkerHandle {
152
+ sender: UnboundedSender<WorkerRequest>,
147
153
  }
148
154
 
149
155
  /// Box it so we can use Worker from JS
150
- type BoxedWorker = JsBox<Worker>;
151
- impl Finalize for Worker {}
152
-
153
- /// Lazy-inits or returns a global tokio runtime that we use for interactions with Core(s)
154
- fn tokio_runtime() -> &'static Runtime {
155
- static INSTANCE: OnceCell<Runtime> = OnceCell::new();
156
- INSTANCE.get_or_init(|| {
157
- tokio::runtime::Builder::new_multi_thread()
158
- .enable_all()
159
- .thread_name("core")
160
- .build()
161
- .expect("Tokio runtime must construct properly")
162
- })
156
+ type BoxedWorker = JsBox<RefCell<Option<WorkerHandle>>>;
157
+ impl Finalize for WorkerHandle {}
158
+
159
+ /// Inits a multi-threaded tokio runtime used to interact with sdk-core APIs
160
+ fn tokio_runtime() -> Runtime {
161
+ tokio::runtime::Builder::new_multi_thread()
162
+ .enable_all()
163
+ .thread_name("core")
164
+ .build()
165
+ .expect("Tokio runtime must construct properly")
163
166
  }
164
167
 
165
- /// Send a result to JS via callback using an [EventQueue]
166
- fn send_result<F, T>(queue: Arc<EventQueue>, callback: Root<JsFunction>, res_fn: F)
168
+ /// Send a result to JS via callback using a [Channel]
169
+ fn send_result<F, T>(channel: Arc<Channel>, callback: Root<JsFunction>, res_fn: F)
167
170
  where
168
171
  F: for<'a> FnOnce(&mut TaskContext<'a>) -> NeonResult<Handle<'a, T>> + Send + 'static,
169
172
  T: Value,
170
173
  {
171
- queue.send(move |mut cx| {
174
+ channel.send(move |mut cx| {
172
175
  let callback = callback.into_inner(&mut cx);
173
176
  let this = cx.undefined();
174
177
  let error = cx.undefined();
@@ -179,13 +182,13 @@ where
179
182
  });
180
183
  }
181
184
 
182
- /// Send an error to JS via callback using an [EventQueue]
183
- fn send_error<E, F>(queue: Arc<EventQueue>, callback: Root<JsFunction>, error_ctor: F)
185
+ /// Send an error to JS via callback using a [Channel]
186
+ fn send_error<E, F>(channel: Arc<Channel>, callback: Root<JsFunction>, error_ctor: F)
184
187
  where
185
188
  E: Object,
186
189
  F: for<'a> FnOnce(&mut TaskContext<'a>) -> JsResult<'a, E> + Send + 'static,
187
190
  {
188
- queue.send(move |mut cx| {
191
+ channel.send(move |mut cx| {
189
192
  let callback = callback.into_inner(&mut cx);
190
193
  callback_with_error(&mut cx, callback, error_ctor)
191
194
  });
@@ -226,33 +229,33 @@ where
226
229
  })
227
230
  }
228
231
 
229
- /// When Future completes, call given JS callback using a neon::EventQueue with either error or
232
+ /// When Future completes, call given JS callback using a neon::Channel with either error or
230
233
  /// undefined
231
234
  async fn void_future_to_js<E, F, ER, EF>(
232
- queue: Arc<EventQueue>,
235
+ channel: Arc<Channel>,
233
236
  callback: Root<JsFunction>,
234
237
  f: F,
235
238
  error_function: EF,
236
239
  ) where
237
240
  E: Display + Send + 'static,
238
- F: Future<Output = Result<(), E>> + Send + 'static,
241
+ F: Future<Output = Result<(), E>> + Send,
239
242
  ER: Object,
240
243
  EF: for<'a> FnOnce(&mut TaskContext<'a>, E) -> JsResult<'a, ER> + Send + 'static,
241
244
  {
242
245
  match f.await {
243
246
  Ok(()) => {
244
- send_result(queue, callback, |cx| Ok(cx.undefined()));
247
+ send_result(channel, callback, |cx| Ok(cx.undefined()));
245
248
  }
246
249
  Err(err) => {
247
- send_error(queue, callback, |cx| error_function(cx, err));
250
+ send_error(channel, callback, |cx| error_function(cx, err));
248
251
  }
249
252
  }
250
253
  }
251
254
 
252
- /// Builds a tokio runtime and starts polling on [Request]s via an internal channel.
253
- /// Bridges requests from JS to core and sends responses back to JS using a neon::EventQueue.
255
+ /// Builds a tokio runtime and starts polling on [RuntimeRequest]s via an internal channel.
256
+ /// Bridges requests from JS to core and sends responses back to JS using a neon::Channel.
254
257
  /// Blocks current thread until a [Shutdown] request is received in channel.
255
- fn start_bridge_loop(event_queue: Arc<EventQueue>, receiver: &mut UnboundedReceiver<Request>) {
258
+ fn start_bridge_loop(channel: Arc<Channel>, receiver: &mut UnboundedReceiver<RuntimeRequest>) {
256
259
  tokio_runtime().block_on(async {
257
260
  loop {
258
261
  let request_option = receiver.recv().await;
@@ -261,48 +264,65 @@ fn start_bridge_loop(event_queue: Arc<EventQueue>, receiver: &mut UnboundedRecei
261
264
  Some(request) => request,
262
265
  };
263
266
 
264
- let event_queue = event_queue.clone();
267
+ let channel = channel.clone();
265
268
 
266
269
  match request {
267
- Request::Shutdown { callback } => {
268
- send_result(event_queue, callback, |cx| Ok(cx.undefined()));
270
+ RuntimeRequest::Shutdown { callback } => {
271
+ send_result(channel, callback, |cx| Ok(cx.undefined()));
269
272
  break;
270
273
  }
271
- Request::CreateClient {
274
+ RuntimeRequest::CreateClient {
272
275
  runtime,
273
276
  options,
277
+ headers,
274
278
  callback,
275
279
  } => {
276
280
  // `metrics_meter` (second arg) can be None here since we don't use the
277
281
  // returned client directly at the moment, when we repurpose the client to be
278
282
  // used by a Worker, `init_worker` will attach the correct metrics meter for
279
283
  // us.
280
- match options.connect_no_namespace(None).await {
281
- Err(err) => {
282
- send_error(event_queue.clone(), callback, |cx| match err {
283
- ClientInitError::SystemInfoCallError(e) => TRANSPORT_ERROR
284
- .from_error(cx, format!("Failed to call GetSystemInfo: {}", e)),
285
- ClientInitError::TonicTransportError(e) => {
286
- TRANSPORT_ERROR.from_error(cx, e)
287
- }
288
- ClientInitError::InvalidUri(e) => {
289
- Ok(JsError::type_error(cx, format!("{}", e))?.upcast())
290
- }
291
- });
292
- }
293
- Ok(client) => {
294
- send_result(event_queue.clone(), callback, |cx| {
295
- Ok(cx.boxed(RefCell::new(Some(Client {
296
- runtime,
297
- core_client: Arc::new(client),
298
- }))))
299
- });
284
+ tokio::spawn(async move {
285
+ match options
286
+ .connect_no_namespace(None, headers.map(|h| Arc::new(RwLock::new(h))))
287
+ .await
288
+ {
289
+ Err(err) => {
290
+ send_error(channel.clone(), callback, |cx| match err {
291
+ ClientInitError::SystemInfoCallError(e) => TRANSPORT_ERROR
292
+ .from_string(
293
+ cx,
294
+ format!("Failed to call GetSystemInfo: {}", e),
295
+ ),
296
+ ClientInitError::TonicTransportError(e) => {
297
+ TRANSPORT_ERROR.from_error(cx, e)
298
+ }
299
+ ClientInitError::InvalidUri(e) => {
300
+ Ok(JsError::type_error(cx, format!("{}", e))?.upcast())
301
+ }
302
+ });
303
+ }
304
+ Ok(client) => {
305
+ send_result(channel.clone(), callback, |cx| {
306
+ Ok(cx.boxed(RefCell::new(Some(Client {
307
+ runtime,
308
+ core_client: Arc::new(client),
309
+ }))))
310
+ });
311
+ }
300
312
  }
301
- }
313
+ });
302
314
  }
303
- Request::PollLogs { callback } => {
315
+ RuntimeRequest::UpdateClientHeaders {
316
+ client,
317
+ headers,
318
+ callback,
319
+ } => {
320
+ client.get_client().set_headers(headers);
321
+ send_result(channel.clone(), callback, |cx| Ok(cx.undefined()));
322
+ }
323
+ RuntimeRequest::PollLogs { callback } => {
304
324
  let logs = fetch_global_buffered_logs();
305
- send_result(event_queue.clone(), callback, |cx| {
325
+ send_result(channel.clone(), callback, |cx| {
306
326
  let logarr = cx.empty_array();
307
327
  for (i, cl) in logs.into_iter().enumerate() {
308
328
  // Not much to do here except for panic when there's an
@@ -319,21 +339,7 @@ fn start_bridge_loop(event_queue: Arc<EventQueue>, receiver: &mut UnboundedRecei
319
339
  Ok(logarr)
320
340
  });
321
341
  }
322
- Request::ShutdownWorker { worker, callback } => {
323
- tokio::spawn(void_future_to_js(
324
- event_queue,
325
- callback,
326
- async move {
327
- worker.shutdown().await;
328
- // Wrap the empty result in a valid Result object
329
- let result: Result<(), String> = Ok(());
330
- result
331
- },
332
- |cx, err| UNEXPECTED_ERROR.from_error(cx, err),
333
- ));
334
- }
335
- Request::InitWorker {
336
- runtime,
342
+ RuntimeRequest::InitWorker {
337
343
  config,
338
344
  client,
339
345
  callback,
@@ -341,134 +347,139 @@ fn start_bridge_loop(event_queue: Arc<EventQueue>, receiver: &mut UnboundedRecei
341
347
  let client = (*client).clone();
342
348
  let worker =
343
349
  init_worker(config, AnyClient::LowLevel(Box::new(client.into_inner())));
344
- send_result(event_queue.clone(), callback, |cx| {
345
- Ok(cx.boxed(Worker {
346
- core_worker: Arc::new(worker),
347
- runtime,
348
- }))
350
+ let (tx, rx) = unbounded_channel();
351
+ tokio::spawn(start_worker_loop(worker, rx, channel.clone()));
352
+ send_result(channel.clone(), callback, |cx| {
353
+ Ok(cx.boxed(RefCell::new(Some(WorkerHandle { sender: tx }))))
349
354
  });
350
355
  }
351
- Request::InitReplayWorker {
352
- runtime,
356
+ RuntimeRequest::InitReplayWorker {
353
357
  config,
354
358
  history,
355
359
  callback,
356
360
  } => {
357
361
  match init_replay_worker(config, &history) {
358
- Ok(worker) => send_result(event_queue.clone(), callback, |cx| {
359
- Ok(cx.boxed(Worker {
360
- core_worker: Arc::new(worker),
361
- runtime,
362
- }))
363
- }),
364
- Err(err) => send_error(event_queue.clone(), callback, |cx| {
365
- UNEXPECTED_ERROR.from_error(cx, err)
362
+ Ok(worker) => {
363
+ let (tx, rx) = unbounded_channel();
364
+ tokio::spawn(start_worker_loop(worker, rx, channel.clone()));
365
+ send_result(channel.clone(), callback, |cx| {
366
+ Ok(cx.boxed(RefCell::new(Some(WorkerHandle { sender: tx }))))
367
+ })
368
+ }
369
+ Err(err) => send_error(channel.clone(), callback, move |cx| {
370
+ UNEXPECTED_ERROR.from_error(cx, err.deref())
366
371
  }),
367
372
  };
368
373
  }
369
- Request::PollWorkflowActivation {
370
- worker,
371
- otel_span,
372
- callback,
373
- } => {
374
- tokio::spawn(async move {
374
+ }
375
+ }
376
+ })
377
+ }
378
+
379
+ /// Polls on [WorkerRequest]s via given channel.
380
+ /// Bridges requests from JS to core and sends responses back to JS using a neon::Channel.
381
+ /// Returns when the given channel is dropped.
382
+ async fn start_worker_loop(
383
+ worker: CoreWorker,
384
+ rx: UnboundedReceiver<WorkerRequest>,
385
+ channel: Arc<Channel>,
386
+ ) {
387
+ UnboundedReceiverStream::new(rx)
388
+ .for_each_concurrent(None, |request| {
389
+ let worker = &worker;
390
+ let channel = channel.clone();
391
+ async move {
392
+ match request {
393
+ WorkerRequest::InitiateShutdown { callback } => {
394
+ worker.initiate_shutdown();
395
+ send_result(channel, callback, |cx| Ok(cx.undefined()));
396
+ }
397
+ WorkerRequest::PollWorkflowActivation {
398
+ otel_span,
399
+ callback,
400
+ } => {
375
401
  handle_poll_workflow_activation_request(
376
- &*worker,
377
- otel_span,
378
- event_queue,
379
- callback,
402
+ &worker, otel_span, channel, callback,
380
403
  )
381
404
  .await
382
- });
383
- }
384
- Request::PollActivityTask {
385
- worker,
386
- otel_span,
387
- callback,
388
- } => {
389
- tokio::spawn(async move {
390
- handle_poll_activity_task_request(
391
- &*worker,
392
- otel_span,
393
- event_queue,
405
+ }
406
+ WorkerRequest::PollActivityTask {
407
+ otel_span,
408
+ callback,
409
+ } => {
410
+ handle_poll_activity_task_request(&worker, otel_span, channel, callback)
411
+ .await
412
+ }
413
+ WorkerRequest::CompleteWorkflowActivation {
414
+ completion,
415
+ otel_span,
416
+ callback,
417
+ } => {
418
+ let otel_ctx =
419
+ opentelemetry::Context::new().with_remote_span_context(otel_span);
420
+ void_future_to_js(
421
+ channel,
394
422
  callback,
423
+ async move {
424
+ worker
425
+ .complete_workflow_activation(completion)
426
+ .with_context(otel_ctx)
427
+ .await
428
+ },
429
+ |cx, err| match err {
430
+ CompleteWfError::TonicError(_) => {
431
+ TRANSPORT_ERROR.from_error(cx, err)
432
+ }
433
+ CompleteWfError::MalformedWorkflowCompletion { reason, .. } => {
434
+ Ok(JsError::type_error(cx, reason)?.upcast())
435
+ }
436
+ },
395
437
  )
396
- .await
397
- });
398
- }
399
- Request::CompleteWorkflowActivation {
400
- worker,
401
- completion,
402
- otel_span,
403
- callback,
404
- } => {
405
- let otel_ctx =
406
- opentelemetry::Context::new().with_remote_span_context(otel_span);
407
- tokio::spawn(void_future_to_js(
408
- event_queue,
409
- callback,
410
- async move {
411
- worker
412
- .complete_workflow_activation(completion)
413
- .with_context(otel_ctx)
414
- .await
415
- },
416
- |cx, err| match err {
417
- CompleteWfError::NoWorkerForQueue(queue_name) => {
418
- let args = vec![cx.string(queue_name).upcast()];
419
- NO_WORKER_ERROR.construct(cx, args)
420
- }
421
- CompleteWfError::TonicError(_) => TRANSPORT_ERROR.from_error(cx, err),
422
- CompleteWfError::MalformedWorkflowCompletion { reason, .. } => {
423
- Ok(JsError::type_error(cx, reason)?.upcast())
424
- }
425
- },
426
- ));
427
- }
428
- Request::CompleteActivityTask {
429
- worker,
430
- completion,
431
- otel_span,
432
- callback,
433
- } => {
434
- let otel_ctx =
435
- opentelemetry::Context::new().with_remote_span_context(otel_span);
436
- tokio::spawn(void_future_to_js(
437
- event_queue,
438
+ .await;
439
+ }
440
+ WorkerRequest::CompleteActivityTask {
441
+ completion,
442
+ otel_span,
438
443
  callback,
439
- async move {
440
- worker
441
- .complete_activity_task(completion)
442
- .with_context(otel_ctx)
443
- .await
444
- },
445
- |cx, err| match err {
446
- CompleteActivityError::MalformedActivityCompletion {
447
- reason, ..
448
- } => Ok(JsError::type_error(cx, reason)?.upcast()),
449
- CompleteActivityError::TonicError(_) => {
450
- TRANSPORT_ERROR.from_error(cx, err)
451
- }
452
- CompleteActivityError::NoWorkerForQueue(queue_name) => {
453
- let args = vec![cx.string(queue_name).upcast()];
454
- NO_WORKER_ERROR.construct(cx, args)
455
- }
456
- },
457
- ));
458
- }
459
- Request::RecordActivityHeartbeat { worker, heartbeat } => {
460
- worker.record_activity_heartbeat(heartbeat)
444
+ } => {
445
+ let otel_ctx =
446
+ opentelemetry::Context::new().with_remote_span_context(otel_span);
447
+ void_future_to_js(
448
+ channel,
449
+ callback,
450
+ async move {
451
+ worker
452
+ .complete_activity_task(completion)
453
+ .with_context(otel_ctx)
454
+ .await
455
+ },
456
+ |cx, err| match err {
457
+ CompleteActivityError::MalformedActivityCompletion {
458
+ reason,
459
+ ..
460
+ } => Ok(JsError::type_error(cx, reason)?.upcast()),
461
+ CompleteActivityError::TonicError(_) => {
462
+ TRANSPORT_ERROR.from_error(cx, err)
463
+ }
464
+ },
465
+ )
466
+ .await;
467
+ }
468
+ WorkerRequest::RecordActivityHeartbeat { heartbeat } => {
469
+ worker.record_activity_heartbeat(heartbeat)
470
+ }
461
471
  }
462
472
  }
463
- }
464
- })
473
+ })
474
+ .await;
475
+ worker.finalize_shutdown().await;
465
476
  }
466
477
 
467
478
  /// Called within the poll loop thread, calls core and triggers JS callback with result
468
479
  async fn handle_poll_workflow_activation_request(
469
- worker: &dyn CoreWorker,
480
+ worker: &CoreWorker,
470
481
  span_context: SpanContext,
471
- event_queue: Arc<EventQueue>,
482
+ channel: Arc<Channel>,
472
483
  callback: Root<JsFunction>,
473
484
  ) {
474
485
  let otel_ctx = opentelemetry::Context::new().with_remote_span_context(span_context);
@@ -478,24 +489,19 @@ async fn handle_poll_workflow_activation_request(
478
489
  .await
479
490
  {
480
491
  Ok(task) => {
481
- send_result(event_queue, callback, move |cx| {
492
+ send_result(channel, callback, move |cx| {
482
493
  let len = task.encoded_len();
483
- let mut result = JsArrayBuffer::new(cx, len as u32)?;
484
- cx.borrow_mut(&mut result, |data| {
485
- let mut slice = data.as_mut_slice::<u8>();
486
- if task.encode(&mut slice).is_err() {
487
- panic!("Failed to encode task")
488
- };
489
- });
494
+ let mut result = JsArrayBuffer::new(cx, len)?;
495
+ let mut slice = result.as_mut_slice(cx);
496
+ if task.encode(&mut slice).is_err() {
497
+ panic!("Failed to encode task")
498
+ };
490
499
  Ok(result)
491
500
  });
492
501
  }
493
502
  Err(err) => {
494
- send_error(event_queue, callback, move |cx| match err {
503
+ send_error(channel, callback, move |cx| match err {
495
504
  PollWfError::ShutDown => SHUTDOWN_ERROR.from_error(cx, err),
496
- PollWfError::AutocompleteError(CompleteWfError::NoWorkerForQueue(_)) => {
497
- UNEXPECTED_ERROR.from_error(cx, "TODO this error shouldn't exist")
498
- }
499
505
  PollWfError::TonicError(_)
500
506
  | PollWfError::AutocompleteError(CompleteWfError::TonicError(_)) => {
501
507
  TRANSPORT_ERROR.from_error(cx, err)
@@ -511,28 +517,26 @@ async fn handle_poll_workflow_activation_request(
511
517
 
512
518
  /// Called within the poll loop thread, calls core and triggers JS callback with result
513
519
  async fn handle_poll_activity_task_request(
514
- worker: &dyn CoreWorker,
520
+ worker: &CoreWorker,
515
521
  span_context: SpanContext,
516
- event_queue: Arc<EventQueue>,
522
+ channel: Arc<Channel>,
517
523
  callback: Root<JsFunction>,
518
524
  ) {
519
525
  let otel_ctx = opentelemetry::Context::new().with_remote_span_context(span_context);
520
526
  match worker.poll_activity_task().with_context(otel_ctx).await {
521
527
  Ok(task) => {
522
- send_result(event_queue, callback, move |cx| {
528
+ send_result(channel, callback, move |cx| {
523
529
  let len = task.encoded_len();
524
- let mut result = JsArrayBuffer::new(cx, len as u32)?;
525
- cx.borrow_mut(&mut result, |data| {
526
- let mut slice = data.as_mut_slice::<u8>();
527
- if task.encode(&mut slice).is_err() {
528
- panic!("Failed to encode task")
529
- };
530
- });
530
+ let mut result = JsArrayBuffer::new(cx, len)?;
531
+ let mut slice = result.as_mut_slice(cx);
532
+ if task.encode(&mut slice).is_err() {
533
+ panic!("Failed to encode task")
534
+ };
531
535
  Ok(result)
532
536
  });
533
537
  }
534
538
  Err(err) => {
535
- send_error(event_queue, callback, move |cx| match err {
539
+ send_error(channel, callback, move |cx| match err {
536
540
  PollActivityError::ShutDown => SHUTDOWN_ERROR.from_error(cx, err),
537
541
  PollActivityError::TonicError(_) => TRANSPORT_ERROR.from_error(cx, err),
538
542
  });
@@ -554,12 +558,12 @@ fn init_telemetry(mut cx: FunctionContext) -> JsResult<JsUndefined> {
554
558
  }
555
559
 
556
560
  /// Create the tokio runtime required to run Core.
557
- /// Immediately spawns a poller thread that will block on [Request]s
561
+ /// Immediately spawns a poller thread that will block on [RuntimeRequest]s
558
562
  fn runtime_new(mut cx: FunctionContext) -> JsResult<BoxedRuntime> {
559
- let queue = Arc::new(cx.queue());
560
- let (sender, mut receiver) = unbounded_channel::<Request>();
563
+ let channel = Arc::new(cx.channel());
564
+ let (sender, mut receiver) = unbounded_channel::<RuntimeRequest>();
561
565
 
562
- std::thread::spawn(move || start_bridge_loop(queue, &mut receiver));
566
+ std::thread::spawn(move || start_bridge_loop(channel, &mut receiver));
563
567
 
564
568
  Ok(cx.boxed(Arc::new(RuntimeHandle { sender })))
565
569
  }
@@ -568,12 +572,28 @@ fn runtime_new(mut cx: FunctionContext) -> JsResult<BoxedRuntime> {
568
572
  /// Client will be returned in the supplied `callback`.
569
573
  fn client_new(mut cx: FunctionContext) -> JsResult<JsUndefined> {
570
574
  let runtime = cx.argument::<BoxedRuntime>(0)?;
571
- let client_options = cx.argument::<JsObject>(1)?.as_client_options(&mut cx)?;
575
+ let opts = cx.argument::<JsObject>(1)?;
572
576
  let callback = cx.argument::<JsFunction>(2)?;
573
577
 
574
- let request = Request::CreateClient {
578
+ let client_options = opts.as_client_options(&mut cx)?;
579
+ let headers = match js_optional_getter!(&mut cx, &opts, "metadata", JsObject) {
580
+ None => None,
581
+ Some(h) => Some(
582
+ h.as_hash_map_of_string_to_string(&mut cx)
583
+ .map_err(|reason| {
584
+ cx.throw_type_error::<_, HashMap<String, String>>(format!(
585
+ "Invalid metadata: {}",
586
+ reason
587
+ ))
588
+ .unwrap_err()
589
+ })?,
590
+ ),
591
+ };
592
+
593
+ let request = RuntimeRequest::CreateClient {
575
594
  runtime: (**runtime).clone(),
576
595
  options: client_options,
596
+ headers,
577
597
  callback: callback.root(&mut cx),
578
598
  };
579
599
  if let Err(err) = runtime.sender.send(request) {
@@ -583,6 +603,33 @@ fn client_new(mut cx: FunctionContext) -> JsResult<JsUndefined> {
583
603
  Ok(cx.undefined())
584
604
  }
585
605
 
606
+ /// Update a Client's HTTP request headers
607
+ fn client_update_headers(mut cx: FunctionContext) -> JsResult<JsUndefined> {
608
+ let client = cx.argument::<BoxedClient>(0)?;
609
+ let headers = cx
610
+ .argument::<JsObject>(1)?
611
+ .as_hash_map_of_string_to_string(&mut cx)?;
612
+ let callback = cx.argument::<JsFunction>(2)?;
613
+
614
+ match &*client.borrow() {
615
+ None => {
616
+ callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Client")?;
617
+ }
618
+ Some(client) => {
619
+ let request = RuntimeRequest::UpdateClientHeaders {
620
+ client: client.core_client.clone(),
621
+ headers,
622
+ callback: callback.root(&mut cx),
623
+ };
624
+ if let Err(err) = client.runtime.sender.send(request) {
625
+ callback_with_unexpected_error(&mut cx, callback, err)?;
626
+ };
627
+ }
628
+ }
629
+
630
+ Ok(cx.undefined())
631
+ }
632
+
586
633
  /// Create a new worker asynchronously.
587
634
  /// Worker uses the provided connection and returned to JS using supplied `callback`.
588
635
  fn worker_new(mut cx: FunctionContext) -> JsResult<JsUndefined> {
@@ -593,14 +640,11 @@ fn worker_new(mut cx: FunctionContext) -> JsResult<JsUndefined> {
593
640
  let config = worker_options.as_worker_config(&mut cx)?;
594
641
  match &*client.borrow() {
595
642
  None => {
596
- callback_with_error(&mut cx, callback, move |cx| {
597
- UNEXPECTED_ERROR.from_string(cx, "Tried to use closed Client".to_string())
598
- })?;
643
+ callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Client")?;
599
644
  }
600
645
  Some(client) => {
601
- let request = Request::InitWorker {
646
+ let request = RuntimeRequest::InitWorker {
602
647
  client: client.core_client.clone(),
603
- runtime: client.runtime.clone(),
604
648
  config,
605
649
  callback: callback.root(&mut cx),
606
650
  };
@@ -622,13 +666,10 @@ fn replay_worker_new(mut cx: FunctionContext) -> JsResult<JsUndefined> {
622
666
  let callback = cx.argument::<JsFunction>(3)?;
623
667
 
624
668
  let config = worker_options.as_worker_config(&mut cx)?;
625
-
626
- match cx.borrow(&history_binary, |data| {
627
- History::decode_length_delimited(data.as_slice::<u8>())
628
- }) {
669
+ let data = history_binary.as_slice(&mut cx);
670
+ match History::decode_length_delimited(data) {
629
671
  Ok(history) => {
630
- let request = Request::InitReplayWorker {
631
- runtime: (**runtime).clone(),
672
+ let request = RuntimeRequest::InitReplayWorker {
632
673
  config,
633
674
  history,
634
675
  callback: callback.root(&mut cx),
@@ -649,7 +690,7 @@ fn replay_worker_new(mut cx: FunctionContext) -> JsResult<JsUndefined> {
649
690
  fn runtime_shutdown(mut cx: FunctionContext) -> JsResult<JsUndefined> {
650
691
  let runtime = cx.argument::<BoxedRuntime>(0)?;
651
692
  let callback = cx.argument::<JsFunction>(1)?;
652
- let request = Request::Shutdown {
693
+ let request = RuntimeRequest::Shutdown {
653
694
  callback: callback.root(&mut cx),
654
695
  };
655
696
  if let Err(err) = runtime.sender.send(request) {
@@ -662,7 +703,7 @@ fn runtime_shutdown(mut cx: FunctionContext) -> JsResult<JsUndefined> {
662
703
  fn poll_logs(mut cx: FunctionContext) -> JsResult<JsUndefined> {
663
704
  let runtime = cx.argument::<BoxedRuntime>(0)?;
664
705
  let callback = cx.argument::<JsFunction>(1)?;
665
- let request = Request::PollLogs {
706
+ let request = RuntimeRequest::PollLogs {
666
707
  callback: callback.root(&mut cx),
667
708
  };
668
709
  if let Err(err) = runtime.sender.send(request) {
@@ -677,13 +718,19 @@ fn worker_poll_workflow_activation(mut cx: FunctionContext) -> JsResult<JsUndefi
677
718
  let worker = cx.argument::<BoxedWorker>(0)?;
678
719
  let otel_span = cx.argument::<JsObject>(1)?;
679
720
  let callback = cx.argument::<JsFunction>(2)?;
680
- let request = Request::PollWorkflowActivation {
681
- worker: worker.core_worker.clone(),
682
- otel_span: otel_span.as_otel_span_context(&mut cx)?,
683
- callback: callback.root(&mut cx),
684
- };
685
- if let Err(err) = worker.runtime.sender.send(request) {
686
- callback_with_unexpected_error(&mut cx, callback, err)?;
721
+ match &*worker.borrow() {
722
+ None => {
723
+ callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Worker")?;
724
+ }
725
+ Some(worker) => {
726
+ let request = WorkerRequest::PollWorkflowActivation {
727
+ otel_span: otel_span.as_otel_span_context(&mut cx)?,
728
+ callback: callback.root(&mut cx),
729
+ };
730
+ if let Err(err) = worker.sender.send(request) {
731
+ callback_with_unexpected_error(&mut cx, callback, err)?;
732
+ }
733
+ }
687
734
  }
688
735
  Ok(cx.undefined())
689
736
  }
@@ -694,13 +741,19 @@ fn worker_poll_activity_task(mut cx: FunctionContext) -> JsResult<JsUndefined> {
694
741
  let worker = cx.argument::<BoxedWorker>(0)?;
695
742
  let otel_span = cx.argument::<JsObject>(1)?;
696
743
  let callback = cx.argument::<JsFunction>(2)?;
697
- let request = Request::PollActivityTask {
698
- worker: worker.core_worker.clone(),
699
- otel_span: otel_span.as_otel_span_context(&mut cx)?,
700
- callback: callback.root(&mut cx),
701
- };
702
- if let Err(err) = worker.runtime.sender.send(request) {
703
- callback_with_unexpected_error(&mut cx, callback, err)?;
744
+ match &*worker.borrow() {
745
+ None => {
746
+ callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Worker")?;
747
+ }
748
+ Some(worker) => {
749
+ let request = WorkerRequest::PollActivityTask {
750
+ otel_span: otel_span.as_otel_span_context(&mut cx)?,
751
+ callback: callback.root(&mut cx),
752
+ };
753
+ if let Err(err) = worker.sender.send(request) {
754
+ callback_with_unexpected_error(&mut cx, callback, err)?;
755
+ }
756
+ }
704
757
  }
705
758
  Ok(cx.undefined())
706
759
  }
@@ -711,24 +764,29 @@ fn worker_complete_workflow_activation(mut cx: FunctionContext) -> JsResult<JsUn
711
764
  let otel_span = cx.argument::<JsObject>(1)?;
712
765
  let completion = cx.argument::<JsArrayBuffer>(2)?;
713
766
  let callback = cx.argument::<JsFunction>(3)?;
714
- let result = cx.borrow(&completion, |data| {
715
- WorkflowActivationCompletion::decode_length_delimited(data.as_slice::<u8>())
716
- });
717
- match result {
718
- Ok(completion) => {
719
- let request = Request::CompleteWorkflowActivation {
720
- worker: worker.core_worker.clone(),
721
- completion,
722
- otel_span: otel_span.as_otel_span_context(&mut cx)?,
723
- callback: callback.root(&mut cx),
724
- };
725
- if let Err(err) = worker.runtime.sender.send(request) {
726
- callback_with_unexpected_error(&mut cx, callback, err)?;
727
- };
767
+ match &*worker.borrow() {
768
+ None => {
769
+ callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Worker")?;
770
+ }
771
+ Some(worker) => {
772
+ match WorkflowActivationCompletion::decode_length_delimited(
773
+ completion.as_slice(&mut cx),
774
+ ) {
775
+ Ok(completion) => {
776
+ let request = WorkerRequest::CompleteWorkflowActivation {
777
+ completion,
778
+ otel_span: otel_span.as_otel_span_context(&mut cx)?,
779
+ callback: callback.root(&mut cx),
780
+ };
781
+ if let Err(err) = worker.sender.send(request) {
782
+ callback_with_unexpected_error(&mut cx, callback, err)?;
783
+ };
784
+ }
785
+ Err(_) => callback_with_error(&mut cx, callback, |cx| {
786
+ JsError::type_error(cx, "Cannot decode Completion from buffer")
787
+ })?,
788
+ }
728
789
  }
729
- Err(_) => callback_with_error(&mut cx, callback, |cx| {
730
- JsError::type_error(cx, "Cannot decode Completion from buffer")
731
- })?,
732
790
  };
733
791
  Ok(cx.undefined())
734
792
  }
@@ -739,24 +797,27 @@ fn worker_complete_activity_task(mut cx: FunctionContext) -> JsResult<JsUndefine
739
797
  let otel_span = cx.argument::<JsObject>(1)?;
740
798
  let result = cx.argument::<JsArrayBuffer>(2)?;
741
799
  let callback = cx.argument::<JsFunction>(3)?;
742
- let result = cx.borrow(&result, |data| {
743
- ActivityTaskCompletion::decode_length_delimited(data.as_slice::<u8>())
744
- });
745
- match result {
746
- Ok(completion) => {
747
- let request = Request::CompleteActivityTask {
748
- worker: worker.core_worker.clone(),
749
- completion,
750
- otel_span: otel_span.as_otel_span_context(&mut cx)?,
751
- callback: callback.root(&mut cx),
752
- };
753
- if let Err(err) = worker.runtime.sender.send(request) {
754
- callback_with_unexpected_error(&mut cx, callback, err)?;
755
- };
800
+ match &*worker.borrow() {
801
+ None => {
802
+ callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Worker")?;
803
+ }
804
+ Some(worker) => {
805
+ match ActivityTaskCompletion::decode_length_delimited(result.as_slice(&mut cx)) {
806
+ Ok(completion) => {
807
+ let request = WorkerRequest::CompleteActivityTask {
808
+ completion,
809
+ otel_span: otel_span.as_otel_span_context(&mut cx)?,
810
+ callback: callback.root(&mut cx),
811
+ };
812
+ if let Err(err) = worker.sender.send(request) {
813
+ callback_with_unexpected_error(&mut cx, callback, err)?;
814
+ };
815
+ }
816
+ Err(_) => callback_with_error(&mut cx, callback, |cx| {
817
+ JsError::type_error(cx, "Cannot decode Completion from buffer")
818
+ })?,
819
+ }
756
820
  }
757
- Err(_) => callback_with_error(&mut cx, callback, |cx| {
758
- JsError::type_error(cx, "Cannot decode Completion from buffer")
759
- })?,
760
821
  };
761
822
  Ok(cx.undefined())
762
823
  }
@@ -765,41 +826,59 @@ fn worker_complete_activity_task(mut cx: FunctionContext) -> JsResult<JsUndefine
765
826
  fn worker_record_activity_heartbeat(mut cx: FunctionContext) -> JsResult<JsUndefined> {
766
827
  let worker = cx.argument::<BoxedWorker>(0)?;
767
828
  let heartbeat = cx.argument::<JsArrayBuffer>(1)?;
768
- let heartbeat = cx.borrow(&heartbeat, |data| {
769
- ActivityHeartbeat::decode_length_delimited(data.as_slice::<u8>())
770
- });
771
- match heartbeat {
772
- Ok(heartbeat) => {
773
- let request = Request::RecordActivityHeartbeat {
774
- worker: worker.core_worker.clone(),
775
- heartbeat,
776
- };
777
- match worker.runtime.sender.send(request) {
778
- Err(err) => UNEXPECTED_ERROR
779
- .from_error(&mut cx, err)
780
- .and_then(|err| cx.throw(err)),
781
- _ => Ok(cx.undefined()),
829
+ match &*worker.borrow() {
830
+ None => UNEXPECTED_ERROR
831
+ .from_string(&mut cx, "Tried to use closed Worker")
832
+ .and_then(|err| cx.throw(err))?,
833
+ Some(worker) => {
834
+ match ActivityHeartbeat::decode_length_delimited(heartbeat.as_slice(&mut cx)) {
835
+ Ok(heartbeat) => {
836
+ let request = WorkerRequest::RecordActivityHeartbeat { heartbeat };
837
+ if let Err(err) = worker.sender.send(request) {
838
+ UNEXPECTED_ERROR
839
+ .from_error(&mut cx, err)
840
+ .and_then(|err| cx.throw(err))?;
841
+ }
842
+ }
843
+ Err(_) => cx.throw_type_error("Cannot decode ActivityHeartbeat from buffer")?,
782
844
  }
783
845
  }
784
- Err(_) => cx.throw_type_error("Cannot decode ActivityHeartbeat from buffer"),
785
- }
846
+ };
847
+ Ok(cx.undefined())
786
848
  }
787
849
 
788
850
  /// Request shutdown of the worker.
789
851
  /// Once complete Core will stop polling on new tasks and activations on worker's task queue.
790
- /// Caller should drain any pending tasks and activations before breaking from
852
+ /// Caller should drain any pending tasks and activations and call worker_finalize_shutdown before breaking from
791
853
  /// the loop to ensure graceful shutdown.
792
- fn worker_shutdown(mut cx: FunctionContext) -> JsResult<JsUndefined> {
854
+ fn worker_initiate_shutdown(mut cx: FunctionContext) -> JsResult<JsUndefined> {
793
855
  let worker = cx.argument::<BoxedWorker>(0)?;
794
856
  let callback = cx.argument::<JsFunction>(1)?;
795
- if let Err(err) = worker.runtime.sender.send(Request::ShutdownWorker {
796
- worker: worker.core_worker.clone(),
797
- callback: callback.root(&mut cx),
798
- }) {
799
- UNEXPECTED_ERROR
800
- .from_error(&mut cx, err)
857
+ match &*worker.borrow() {
858
+ None => {
859
+ callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Worker")?;
860
+ }
861
+ Some(worker) => {
862
+ if let Err(err) = worker.sender.send(WorkerRequest::InitiateShutdown {
863
+ callback: callback.root(&mut cx),
864
+ }) {
865
+ UNEXPECTED_ERROR
866
+ .from_error(&mut cx, err)
867
+ .and_then(|err| cx.throw(err))?;
868
+ };
869
+ }
870
+ }
871
+ Ok(cx.undefined())
872
+ }
873
+
874
+ fn worker_finalize_shutdown(mut cx: FunctionContext) -> JsResult<JsUndefined> {
875
+ let worker = cx.argument::<BoxedWorker>(0)?;
876
+ if worker.replace(None).is_none() {
877
+ ILLEGAL_STATE_ERROR
878
+ .from_string(&mut cx, "Worker already closed")
801
879
  .and_then(|err| cx.throw(err))?;
802
- };
880
+ }
881
+
803
882
  Ok(cx.undefined())
804
883
  }
805
884
 
@@ -808,7 +887,7 @@ fn client_close(mut cx: FunctionContext) -> JsResult<JsUndefined> {
808
887
  let client = cx.argument::<BoxedClient>(0)?;
809
888
  if client.replace(None).is_none() {
810
889
  ILLEGAL_STATE_ERROR
811
- .from_error(&mut cx, "Client already closed")
890
+ .from_string(&mut cx, "Client already closed")
812
891
  .and_then(|err| cx.throw(err))?;
813
892
  };
814
893
  Ok(cx.undefined())
@@ -843,9 +922,11 @@ fn main(mut cx: ModuleContext) -> NeonResult<()> {
843
922
  cx.export_function("initTelemetry", init_telemetry)?;
844
923
  cx.export_function("newRuntime", runtime_new)?;
845
924
  cx.export_function("newClient", client_new)?;
925
+ cx.export_function("clientUpdateHeaders", client_update_headers)?;
846
926
  cx.export_function("newWorker", worker_new)?;
847
927
  cx.export_function("newReplayWorker", replay_worker_new)?;
848
- cx.export_function("workerShutdown", worker_shutdown)?;
928
+ cx.export_function("workerInitiateShutdown", worker_initiate_shutdown)?;
929
+ cx.export_function("workerFinalizeShutdown", worker_finalize_shutdown)?;
849
930
  cx.export_function("clientClose", client_close)?;
850
931
  cx.export_function("runtimeShutdown", runtime_shutdown)?;
851
932
  cx.export_function("pollLogs", poll_logs)?;