@temporalio/core-bridge 1.1.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/Cargo.lock +765 -128
  2. package/Cargo.toml +2 -2
  3. package/common.js +7 -3
  4. package/index.d.ts +118 -5
  5. package/index.js +2 -6
  6. package/package.json +2 -3
  7. package/releases/aarch64-apple-darwin/index.node +0 -0
  8. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  9. package/releases/x86_64-apple-darwin/index.node +0 -0
  10. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  11. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  12. package/scripts/build.js +4 -3
  13. package/sdk-core/.buildkite/docker/Dockerfile +2 -1
  14. package/sdk-core/.buildkite/pipeline.yml +2 -0
  15. package/sdk-core/.cargo/config.toml +1 -1
  16. package/sdk-core/ARCHITECTURE.md +2 -2
  17. package/sdk-core/README.md +12 -0
  18. package/sdk-core/bridge-ffi/Cargo.toml +2 -2
  19. package/sdk-core/bridge-ffi/src/lib.rs +2 -2
  20. package/sdk-core/client/Cargo.toml +7 -5
  21. package/sdk-core/client/src/lib.rs +354 -226
  22. package/sdk-core/client/src/metrics.rs +13 -11
  23. package/sdk-core/client/src/raw.rs +352 -107
  24. package/sdk-core/client/src/retry.rs +188 -147
  25. package/sdk-core/client/src/workflow_handle/mod.rs +1 -1
  26. package/sdk-core/core/Cargo.toml +28 -15
  27. package/sdk-core/core/src/core_tests/activity_tasks.rs +98 -33
  28. package/sdk-core/core/src/core_tests/child_workflows.rs +125 -3
  29. package/sdk-core/core/src/core_tests/local_activities.rs +6 -6
  30. package/sdk-core/core/src/core_tests/workers.rs +3 -2
  31. package/sdk-core/core/src/core_tests/workflow_tasks.rs +70 -2
  32. package/sdk-core/core/src/ephemeral_server/mod.rs +515 -0
  33. package/sdk-core/core/src/lib.rs +62 -28
  34. package/sdk-core/core/src/pollers/mod.rs +2 -0
  35. package/sdk-core/core/src/pollers/poll_buffer.rs +4 -4
  36. package/sdk-core/core/src/replay/mod.rs +3 -3
  37. package/sdk-core/core/src/retry_logic.rs +10 -9
  38. package/sdk-core/core/src/telemetry/metrics.rs +48 -39
  39. package/sdk-core/core/src/telemetry/mod.rs +46 -12
  40. package/sdk-core/core/src/telemetry/prometheus_server.rs +17 -13
  41. package/sdk-core/core/src/test_help/mod.rs +18 -8
  42. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +10 -10
  43. package/sdk-core/core/src/worker/activities/local_activities.rs +13 -13
  44. package/sdk-core/core/src/worker/activities.rs +6 -12
  45. package/sdk-core/core/src/worker/client/mocks.rs +1 -0
  46. package/sdk-core/core/src/worker/client.rs +193 -64
  47. package/sdk-core/core/src/worker/mod.rs +14 -19
  48. package/sdk-core/core/src/worker/workflow/driven_workflow.rs +3 -0
  49. package/sdk-core/core/src/worker/workflow/history_update.rs +5 -5
  50. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +133 -85
  51. package/sdk-core/core/src/worker/workflow/machines/mod.rs +3 -2
  52. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +160 -105
  53. package/sdk-core/core/src/worker/workflow/managed_run.rs +2 -1
  54. package/sdk-core/core/src/worker/workflow/mod.rs +62 -58
  55. package/sdk-core/core/src/worker/workflow/run_cache.rs +5 -3
  56. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +7 -5
  57. package/sdk-core/core-api/Cargo.toml +3 -3
  58. package/sdk-core/core-api/src/errors.rs +3 -11
  59. package/sdk-core/core-api/src/worker.rs +7 -0
  60. package/sdk-core/protos/api_upstream/.buildkite/Dockerfile +1 -1
  61. package/sdk-core/protos/api_upstream/.github/CODEOWNERS +1 -1
  62. package/sdk-core/protos/api_upstream/.github/PULL_REQUEST_TEMPLATE.md +2 -6
  63. package/sdk-core/protos/api_upstream/.github/workflows/trigger-api-go-update.yml +29 -0
  64. package/sdk-core/protos/api_upstream/Makefile +2 -2
  65. package/sdk-core/protos/api_upstream/buf.yaml +1 -0
  66. package/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +86 -0
  67. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +26 -0
  68. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +46 -0
  69. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +7 -0
  70. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +14 -0
  71. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +51 -0
  72. package/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +18 -0
  73. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +57 -1
  74. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +1 -3
  75. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +4 -2
  76. package/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +11 -0
  77. package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +23 -0
  78. package/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +46 -0
  79. package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +1 -0
  80. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +172 -0
  81. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +30 -0
  82. package/sdk-core/protos/grpc/health/v1/health.proto +63 -0
  83. package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +18 -15
  84. package/sdk-core/protos/testsrv_upstream/Makefile +80 -0
  85. package/sdk-core/protos/testsrv_upstream/api-linter.yaml +38 -0
  86. package/sdk-core/protos/testsrv_upstream/buf.yaml +13 -0
  87. package/sdk-core/protos/testsrv_upstream/dependencies/gogoproto/gogo.proto +141 -0
  88. package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +63 -0
  89. package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +90 -0
  90. package/sdk-core/sdk/Cargo.toml +2 -2
  91. package/sdk-core/sdk/src/lib.rs +2 -2
  92. package/sdk-core/sdk/src/workflow_context/options.rs +36 -8
  93. package/sdk-core/sdk/src/workflow_context.rs +30 -6
  94. package/sdk-core/sdk/src/workflow_future.rs +4 -4
  95. package/sdk-core/sdk-core-protos/Cargo.toml +5 -5
  96. package/sdk-core/sdk-core-protos/build.rs +9 -1
  97. package/sdk-core/sdk-core-protos/src/history_builder.rs +6 -1
  98. package/sdk-core/sdk-core-protos/src/lib.rs +93 -32
  99. package/sdk-core/test-utils/Cargo.toml +3 -3
  100. package/sdk-core/test-utils/src/canned_histories.rs +58 -0
  101. package/sdk-core/test-utils/src/lib.rs +35 -12
  102. package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +128 -0
  103. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +55 -5
  104. package/sdk-core/tests/integ_tests/polling_tests.rs +2 -1
  105. package/sdk-core/tests/integ_tests/queries_tests.rs +5 -5
  106. package/sdk-core/tests/integ_tests/visibility_tests.rs +93 -0
  107. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +93 -10
  108. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -1
  109. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +14 -14
  110. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +2 -6
  111. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +12 -12
  112. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +12 -1
  113. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +3 -3
  114. package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +8 -2
  115. package/sdk-core/tests/integ_tests/workflow_tests.rs +19 -4
  116. package/sdk-core/tests/load_tests.rs +2 -1
  117. package/sdk-core/tests/main.rs +17 -0
  118. package/sdk-core/tests/runner.rs +93 -0
  119. package/src/conversions.rs +157 -94
  120. package/src/helpers.rs +190 -0
  121. package/src/lib.rs +10 -912
  122. package/src/runtime.rs +436 -0
  123. package/src/testing.rs +67 -0
  124. package/src/worker.rs +465 -0
package/src/worker.rs ADDED
@@ -0,0 +1,465 @@
1
+ use crate::conversions::ObjectHandleConversionsExt;
2
+ use crate::errors::*;
3
+ use crate::helpers::*;
4
+ use crate::runtime::*;
5
+ use futures::stream::StreamExt;
6
+ use neon::prelude::*;
7
+ use neon::types::buffer::TypedArray;
8
+ use opentelemetry::trace::{FutureExt, SpanContext, TraceContextExt};
9
+ use prost::Message;
10
+ use std::{cell::RefCell, sync::Arc};
11
+ use temporal_sdk_core::{
12
+ api::{
13
+ errors::{CompleteActivityError, CompleteWfError, PollActivityError, PollWfError},
14
+ Worker as CoreWorkerTrait,
15
+ },
16
+ protos::{
17
+ coresdk::{
18
+ workflow_completion::WorkflowActivationCompletion, ActivityHeartbeat,
19
+ ActivityTaskCompletion,
20
+ },
21
+ temporal::api::history::v1::History,
22
+ },
23
+ Worker as CoreWorker,
24
+ };
25
+ use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
26
+ use tokio_stream::wrappers::UnboundedReceiverStream;
27
+
28
+ /// Worker struct, hold a reference for the channel sender responsible for sending requests from
29
+ /// JS to a bridge thread which forwards them to core
30
+ pub struct WorkerHandle {
31
+ pub(crate) sender: UnboundedSender<WorkerRequest>,
32
+ }
33
+
34
+ /// Box it so we can use Worker from JS
35
+ pub type BoxedWorker = JsBox<RefCell<Option<WorkerHandle>>>;
36
+ impl Finalize for WorkerHandle {}
37
+
38
+ #[derive(Debug)]
39
+ pub enum WorkerRequest {
40
+ /// A request to shutdown a worker, the worker instance will remain active to
41
+ /// allow draining of pending tasks
42
+ InitiateShutdown {
43
+ /// Used to send the result back into JS
44
+ callback: Root<JsFunction>,
45
+ },
46
+ /// A request to poll for workflow activations
47
+ PollWorkflowActivation {
48
+ otel_span: SpanContext,
49
+ /// Used to send the result back into JS
50
+ callback: Root<JsFunction>,
51
+ },
52
+ /// A request to complete a single workflow activation
53
+ CompleteWorkflowActivation {
54
+ completion: WorkflowActivationCompletion,
55
+ otel_span: SpanContext,
56
+ /// Used to send the result back into JS
57
+ callback: Root<JsFunction>,
58
+ },
59
+ /// A request to poll for activity tasks
60
+ PollActivityTask {
61
+ otel_span: SpanContext,
62
+ /// Used to report completion or error back into JS
63
+ callback: Root<JsFunction>,
64
+ },
65
+ /// A request to complete a single activity task
66
+ CompleteActivityTask {
67
+ completion: ActivityTaskCompletion,
68
+ otel_span: SpanContext,
69
+ /// Used to send the result back into JS
70
+ callback: Root<JsFunction>,
71
+ },
72
+ /// A request to send a heartbeat from a running activity
73
+ RecordActivityHeartbeat { heartbeat: ActivityHeartbeat },
74
+ }
75
+
76
+ /// Polls on [WorkerRequest]s via given channel.
77
+ /// Bridges requests from JS to core and sends responses back to JS using a neon::Channel.
78
+ /// Returns when the given channel is dropped.
79
+ pub async fn start_worker_loop(
80
+ worker: CoreWorker,
81
+ rx: UnboundedReceiver<WorkerRequest>,
82
+ channel: Arc<Channel>,
83
+ ) {
84
+ UnboundedReceiverStream::new(rx)
85
+ .for_each_concurrent(None, |request| {
86
+ let worker = &worker;
87
+ let channel = channel.clone();
88
+ async move {
89
+ match request {
90
+ WorkerRequest::InitiateShutdown { callback } => {
91
+ worker.initiate_shutdown();
92
+ send_result(channel, callback, |cx| Ok(cx.undefined()));
93
+ }
94
+ WorkerRequest::PollWorkflowActivation {
95
+ otel_span,
96
+ callback,
97
+ } => {
98
+ handle_poll_workflow_activation_request(
99
+ &worker, otel_span, channel, callback,
100
+ )
101
+ .await
102
+ }
103
+ WorkerRequest::PollActivityTask {
104
+ otel_span,
105
+ callback,
106
+ } => {
107
+ handle_poll_activity_task_request(&worker, otel_span, channel, callback)
108
+ .await
109
+ }
110
+ WorkerRequest::CompleteWorkflowActivation {
111
+ completion,
112
+ otel_span,
113
+ callback,
114
+ } => {
115
+ let otel_ctx =
116
+ opentelemetry::Context::new().with_remote_span_context(otel_span);
117
+ void_future_to_js(
118
+ channel,
119
+ callback,
120
+ async move {
121
+ worker
122
+ .complete_workflow_activation(completion)
123
+ .with_context(otel_ctx)
124
+ .await
125
+ },
126
+ |cx, err| -> JsResult<JsObject> {
127
+ match err {
128
+ CompleteWfError::MalformedWorkflowCompletion {
129
+ reason, ..
130
+ } => Ok(JsError::type_error(cx, reason)?.upcast()),
131
+ }
132
+ },
133
+ )
134
+ .await;
135
+ }
136
+ WorkerRequest::CompleteActivityTask {
137
+ completion,
138
+ otel_span,
139
+ callback,
140
+ } => {
141
+ let otel_ctx =
142
+ opentelemetry::Context::new().with_remote_span_context(otel_span);
143
+ void_future_to_js(
144
+ channel,
145
+ callback,
146
+ async move {
147
+ worker
148
+ .complete_activity_task(completion)
149
+ .with_context(otel_ctx)
150
+ .await
151
+ },
152
+ |cx, err| -> JsResult<JsObject> {
153
+ match err {
154
+ CompleteActivityError::MalformedActivityCompletion {
155
+ reason,
156
+ ..
157
+ } => Ok(JsError::type_error(cx, reason)?.upcast()),
158
+ }
159
+ },
160
+ )
161
+ .await;
162
+ }
163
+ WorkerRequest::RecordActivityHeartbeat { heartbeat } => {
164
+ worker.record_activity_heartbeat(heartbeat)
165
+ }
166
+ }
167
+ }
168
+ })
169
+ .await;
170
+ worker.finalize_shutdown().await;
171
+ }
172
+
173
+ /// Called within the poll loop thread, calls core and triggers JS callback with result
174
+ async fn handle_poll_workflow_activation_request(
175
+ worker: &CoreWorker,
176
+ span_context: SpanContext,
177
+ channel: Arc<Channel>,
178
+ callback: Root<JsFunction>,
179
+ ) {
180
+ let otel_ctx = opentelemetry::Context::new().with_remote_span_context(span_context);
181
+ match worker
182
+ .poll_workflow_activation()
183
+ .with_context(otel_ctx)
184
+ .await
185
+ {
186
+ Ok(task) => {
187
+ send_result(channel, callback, move |cx| {
188
+ let len = task.encoded_len();
189
+ let mut result = JsArrayBuffer::new(cx, len)?;
190
+ let mut slice = result.as_mut_slice(cx);
191
+ if task.encode(&mut slice).is_err() {
192
+ panic!("Failed to encode task")
193
+ };
194
+ Ok(result)
195
+ });
196
+ }
197
+ Err(err) => {
198
+ 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),
201
+ PollWfError::AutocompleteError(CompleteWfError::MalformedWorkflowCompletion {
202
+ reason,
203
+ ..
204
+ }) => Ok(JsError::type_error(cx, reason)?.upcast()),
205
+ });
206
+ }
207
+ }
208
+ }
209
+
210
+ /// Called within the poll loop thread, calls core and triggers JS callback with result
211
+ pub async fn handle_poll_activity_task_request(
212
+ worker: &CoreWorker,
213
+ span_context: SpanContext,
214
+ channel: Arc<Channel>,
215
+ callback: Root<JsFunction>,
216
+ ) {
217
+ let otel_ctx = opentelemetry::Context::new().with_remote_span_context(span_context);
218
+ match worker.poll_activity_task().with_context(otel_ctx).await {
219
+ Ok(task) => {
220
+ send_result(channel, callback, move |cx| {
221
+ let len = task.encoded_len();
222
+ let mut result = JsArrayBuffer::new(cx, len)?;
223
+ let mut slice = result.as_mut_slice(cx);
224
+ if task.encode(&mut slice).is_err() {
225
+ panic!("Failed to encode task")
226
+ };
227
+ Ok(result)
228
+ });
229
+ }
230
+ Err(err) => {
231
+ 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),
234
+ });
235
+ }
236
+ }
237
+ }
238
+
239
+ // Below are functions exported to JS
240
+
241
+ /// Create a new worker asynchronously.
242
+ /// Worker uses the provided connection and returned to JS using supplied `callback`.
243
+ pub fn worker_new(mut cx: FunctionContext) -> JsResult<JsUndefined> {
244
+ let client = cx.argument::<BoxedClient>(0)?;
245
+ let worker_options = cx.argument::<JsObject>(1)?;
246
+ let callback = cx.argument::<JsFunction>(2)?;
247
+
248
+ let config = worker_options.as_worker_config(&mut cx)?;
249
+ match client.borrow().as_ref() {
250
+ None => {
251
+ callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Client")?;
252
+ }
253
+ Some(client) => {
254
+ let request = RuntimeRequest::InitWorker {
255
+ client: client.core_client.clone(),
256
+ config,
257
+ callback: callback.root(&mut cx),
258
+ };
259
+ if let Err(err) = client.runtime.sender.send(request) {
260
+ callback_with_unexpected_error(&mut cx, callback, err)?;
261
+ };
262
+ }
263
+ };
264
+
265
+ Ok(cx.undefined())
266
+ }
267
+
268
+ /// Create a new replay worker asynchronously.
269
+ /// Worker is returned to JS using supplied callback.
270
+ pub fn replay_worker_new(mut cx: FunctionContext) -> JsResult<JsUndefined> {
271
+ let runtime = cx.argument::<BoxedRuntime>(0)?;
272
+ let worker_options = cx.argument::<JsObject>(1)?;
273
+ let history_binary = cx.argument::<JsArrayBuffer>(2)?;
274
+ let callback = cx.argument::<JsFunction>(3)?;
275
+
276
+ let config = worker_options.as_worker_config(&mut cx)?;
277
+ let data = history_binary.as_slice(&mut cx);
278
+ 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
+ };
288
+ }
289
+ Err(_) => callback_with_error(&mut cx, callback, |cx| {
290
+ JsError::type_error(cx, "Cannot decode History from buffer")
291
+ })?,
292
+ }
293
+
294
+ Ok(cx.undefined())
295
+ }
296
+
297
+ /// Initiate a single workflow activation poll request.
298
+ /// There should be only one concurrent poll request for this type.
299
+ pub fn worker_poll_workflow_activation(mut cx: FunctionContext) -> JsResult<JsUndefined> {
300
+ let worker = cx.argument::<BoxedWorker>(0)?;
301
+ let otel_span = cx.argument::<JsObject>(1)?;
302
+ let callback = cx.argument::<JsFunction>(2)?;
303
+ match worker.borrow().as_ref() {
304
+ None => {
305
+ callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Worker")?;
306
+ }
307
+ Some(worker) => {
308
+ let request = WorkerRequest::PollWorkflowActivation {
309
+ otel_span: otel_span.as_otel_span_context(&mut cx)?,
310
+ callback: callback.root(&mut cx),
311
+ };
312
+ if let Err(err) = worker.sender.send(request) {
313
+ callback_with_unexpected_error(&mut cx, callback, err)?;
314
+ }
315
+ }
316
+ }
317
+ Ok(cx.undefined())
318
+ }
319
+
320
+ /// Initiate a single activity task poll request.
321
+ /// There should be only one concurrent poll request for this type.
322
+ pub fn worker_poll_activity_task(mut cx: FunctionContext) -> JsResult<JsUndefined> {
323
+ let worker = cx.argument::<BoxedWorker>(0)?;
324
+ let otel_span = cx.argument::<JsObject>(1)?;
325
+ let callback = cx.argument::<JsFunction>(2)?;
326
+ match worker.borrow().as_ref() {
327
+ None => {
328
+ callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Worker")?;
329
+ }
330
+ Some(worker) => {
331
+ let request = WorkerRequest::PollActivityTask {
332
+ otel_span: otel_span.as_otel_span_context(&mut cx)?,
333
+ callback: callback.root(&mut cx),
334
+ };
335
+ if let Err(err) = worker.sender.send(request) {
336
+ callback_with_unexpected_error(&mut cx, callback, err)?;
337
+ }
338
+ }
339
+ }
340
+ Ok(cx.undefined())
341
+ }
342
+
343
+ /// Submit a workflow activation completion to core.
344
+ pub fn worker_complete_workflow_activation(mut cx: FunctionContext) -> JsResult<JsUndefined> {
345
+ let worker = cx.argument::<BoxedWorker>(0)?;
346
+ let otel_span = cx.argument::<JsObject>(1)?;
347
+ let completion = cx.argument::<JsArrayBuffer>(2)?;
348
+ let callback = cx.argument::<JsFunction>(3)?;
349
+ match worker.borrow().as_ref() {
350
+ None => {
351
+ callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Worker")?;
352
+ }
353
+ Some(worker) => {
354
+ match WorkflowActivationCompletion::decode_length_delimited(
355
+ completion.as_slice(&mut cx),
356
+ ) {
357
+ Ok(completion) => {
358
+ let request = WorkerRequest::CompleteWorkflowActivation {
359
+ completion,
360
+ otel_span: otel_span.as_otel_span_context(&mut cx)?,
361
+ callback: callback.root(&mut cx),
362
+ };
363
+ if let Err(err) = worker.sender.send(request) {
364
+ callback_with_unexpected_error(&mut cx, callback, err)?;
365
+ };
366
+ }
367
+ Err(_) => callback_with_error(&mut cx, callback, |cx| {
368
+ JsError::type_error(cx, "Cannot decode Completion from buffer")
369
+ })?,
370
+ }
371
+ }
372
+ };
373
+ Ok(cx.undefined())
374
+ }
375
+
376
+ /// Submit an activity task completion to core.
377
+ pub fn worker_complete_activity_task(mut cx: FunctionContext) -> JsResult<JsUndefined> {
378
+ let worker = cx.argument::<BoxedWorker>(0)?;
379
+ let otel_span = cx.argument::<JsObject>(1)?;
380
+ let result = cx.argument::<JsArrayBuffer>(2)?;
381
+ let callback = cx.argument::<JsFunction>(3)?;
382
+ match worker.borrow().as_ref() {
383
+ None => {
384
+ callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Worker")?;
385
+ }
386
+ Some(worker) => {
387
+ match ActivityTaskCompletion::decode_length_delimited(result.as_slice(&mut cx)) {
388
+ Ok(completion) => {
389
+ let request = WorkerRequest::CompleteActivityTask {
390
+ completion,
391
+ otel_span: otel_span.as_otel_span_context(&mut cx)?,
392
+ callback: callback.root(&mut cx),
393
+ };
394
+ if let Err(err) = worker.sender.send(request) {
395
+ callback_with_unexpected_error(&mut cx, callback, err)?;
396
+ };
397
+ }
398
+ Err(_) => callback_with_error(&mut cx, callback, |cx| {
399
+ JsError::type_error(cx, "Cannot decode Completion from buffer")
400
+ })?,
401
+ }
402
+ }
403
+ };
404
+ Ok(cx.undefined())
405
+ }
406
+
407
+ /// Submit an activity heartbeat to core.
408
+ pub fn worker_record_activity_heartbeat(mut cx: FunctionContext) -> JsResult<JsUndefined> {
409
+ let worker = cx.argument::<BoxedWorker>(0)?;
410
+ let heartbeat = cx.argument::<JsArrayBuffer>(1)?;
411
+ 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
+ }
424
+ }
425
+ Err(_) => cx.throw_type_error("Cannot decode ActivityHeartbeat from buffer")?,
426
+ }
427
+ }
428
+ };
429
+ Ok(cx.undefined())
430
+ }
431
+
432
+ /// Request shutdown of the worker.
433
+ /// Once complete Core will stop polling on new tasks and activations on worker's task queue.
434
+ /// Caller should drain any pending tasks and activations and call worker_finalize_shutdown before breaking from
435
+ /// the loop to ensure graceful shutdown.
436
+ pub fn worker_initiate_shutdown(mut cx: FunctionContext) -> JsResult<JsUndefined> {
437
+ let worker = cx.argument::<BoxedWorker>(0)?;
438
+ let callback = cx.argument::<JsFunction>(1)?;
439
+ match worker.borrow().as_ref() {
440
+ None => {
441
+ callback_with_unexpected_error(&mut cx, callback, "Tried to use closed Worker")?;
442
+ }
443
+ Some(worker) => {
444
+ if let Err(err) = worker.sender.send(WorkerRequest::InitiateShutdown {
445
+ callback: callback.root(&mut cx),
446
+ }) {
447
+ UNEXPECTED_ERROR
448
+ .from_error(&mut cx, err)
449
+ .and_then(|err| cx.throw(err))?;
450
+ };
451
+ }
452
+ }
453
+ Ok(cx.undefined())
454
+ }
455
+
456
+ pub fn worker_finalize_shutdown(mut cx: FunctionContext) -> JsResult<JsUndefined> {
457
+ let worker = cx.argument::<BoxedWorker>(0)?;
458
+ if worker.replace(None).is_none() {
459
+ ILLEGAL_STATE_ERROR
460
+ .from_string(&mut cx, "Worker already closed")
461
+ .and_then(|err| cx.throw(err))?;
462
+ }
463
+
464
+ Ok(cx.undefined())
465
+ }