@temporalio/core-bridge 1.14.1 → 1.15.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 (135) hide show
  1. package/Cargo.lock +648 -606
  2. package/bridge-macros/src/derive_tryintojs.rs +40 -0
  3. package/lib/native.d.ts +23 -2
  4. package/package.json +12 -13
  5. package/releases/aarch64-apple-darwin/index.node +0 -0
  6. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  7. package/releases/x86_64-apple-darwin/index.node +0 -0
  8. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  9. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  10. package/sdk-core/.cargo/multi-worker-manual-test +0 -0
  11. package/sdk-core/AGENTS.md +2 -2
  12. package/sdk-core/Cargo.toml +1 -1
  13. package/sdk-core/README.md +5 -5
  14. package/sdk-core/crates/client/src/raw.rs +90 -0
  15. package/sdk-core/crates/client/src/worker/mod.rs +103 -28
  16. package/sdk-core/crates/common/Cargo.toml +1 -1
  17. package/sdk-core/crates/common/protos/api_upstream/.github/workflows/create-release.yml +0 -5
  18. package/sdk-core/crates/common/protos/api_upstream/README.md +8 -0
  19. package/sdk-core/crates/common/protos/api_upstream/buf.yaml +3 -0
  20. package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv2.json +2738 -2452
  21. package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv3.yaml +1657 -124
  22. package/sdk-core/crates/common/protos/api_upstream/temporal/api/activity/v1/message.proto +155 -3
  23. package/sdk-core/crates/common/protos/api_upstream/temporal/api/command/v1/message.proto +26 -0
  24. package/sdk-core/crates/common/protos/api_upstream/temporal/api/common/v1/message.proto +8 -1
  25. package/sdk-core/crates/common/protos/api_upstream/temporal/api/deployment/v1/message.proto +26 -0
  26. package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/activity.proto +81 -0
  27. package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/event_type.proto +4 -0
  28. package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +4 -0
  29. package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +15 -0
  30. package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/workflow.proto +62 -15
  31. package/sdk-core/crates/common/protos/api_upstream/temporal/api/errordetails/v1/message.proto +8 -0
  32. package/sdk-core/crates/common/protos/api_upstream/temporal/api/history/v1/message.proto +107 -17
  33. package/sdk-core/crates/common/protos/api_upstream/temporal/api/namespace/v1/message.proto +15 -0
  34. package/sdk-core/crates/common/protos/api_upstream/temporal/api/nexus/v1/message.proto +4 -0
  35. package/sdk-core/crates/common/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +4 -0
  36. package/sdk-core/crates/common/protos/api_upstream/temporal/api/schedule/v1/message.proto +2 -2
  37. package/sdk-core/crates/common/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +2 -0
  38. package/sdk-core/crates/common/protos/api_upstream/temporal/api/worker/v1/message.proto +4 -7
  39. package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflow/v1/message.proto +80 -22
  40. package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +285 -19
  41. package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +154 -10
  42. package/sdk-core/crates/common/protos/local/temporal/sdk/core/core_interface.proto +15 -0
  43. package/sdk-core/crates/common/protos/local/temporal/sdk/core/nexus/nexus.proto +5 -0
  44. package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +4 -0
  45. package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +17 -0
  46. package/sdk-core/crates/common/src/lib.rs +3 -3
  47. package/sdk-core/crates/common/src/protos/canned_histories.rs +16 -0
  48. package/sdk-core/crates/common/src/protos/mod.rs +12 -0
  49. package/sdk-core/crates/common/src/telemetry/metrics.rs +6 -4
  50. package/sdk-core/crates/common/src/telemetry.rs +14 -15
  51. package/sdk-core/crates/common/src/worker.rs +66 -99
  52. package/sdk-core/crates/common/tests/worker_task_types_test.rs +9 -9
  53. package/sdk-core/crates/sdk/src/lib.rs +10 -8
  54. package/sdk-core/crates/sdk/src/workflow_context/options.rs +19 -0
  55. package/sdk-core/crates/sdk-core/Cargo.toml +2 -1
  56. package/sdk-core/crates/sdk-core/benches/workflow_replay_bench.rs +4 -19
  57. package/sdk-core/crates/sdk-core/src/core_tests/mod.rs +9 -6
  58. package/sdk-core/crates/sdk-core/src/core_tests/workers.rs +166 -13
  59. package/sdk-core/crates/sdk-core/src/core_tests/workflow_tasks.rs +42 -33
  60. package/sdk-core/crates/sdk-core/src/ephemeral_server/mod.rs +6 -9
  61. package/sdk-core/crates/sdk-core/src/lib.rs +20 -13
  62. package/sdk-core/crates/sdk-core/src/pollers/poll_buffer.rs +301 -21
  63. package/sdk-core/crates/sdk-core/src/telemetry/log_export.rs +7 -10
  64. package/sdk-core/crates/sdk-core/src/telemetry/metrics.rs +4 -2
  65. package/sdk-core/crates/sdk-core/src/telemetry/mod.rs +2 -3
  66. package/sdk-core/crates/sdk-core/src/test_help/integ_helpers.rs +30 -8
  67. package/sdk-core/crates/sdk-core/src/worker/activities/activity_heartbeat_manager.rs +1 -0
  68. package/sdk-core/crates/sdk-core/src/worker/client/mocks.rs +3 -1
  69. package/sdk-core/crates/sdk-core/src/worker/client.rs +2 -6
  70. package/sdk-core/crates/sdk-core/src/worker/heartbeat.rs +4 -4
  71. package/sdk-core/crates/sdk-core/src/worker/mod.rs +92 -53
  72. package/sdk-core/crates/sdk-core/src/worker/nexus.rs +5 -0
  73. package/sdk-core/crates/sdk-core/src/worker/tuner/resource_based.rs +12 -14
  74. package/sdk-core/crates/sdk-core/src/worker/tuner.rs +36 -36
  75. package/sdk-core/crates/sdk-core/src/worker/workflow/machines/patch_state_machine.rs +5 -8
  76. package/sdk-core/crates/sdk-core/src/worker/workflow/machines/workflow_machines.rs +12 -1
  77. package/sdk-core/crates/sdk-core/src/worker/workflow/managed_run.rs +6 -23
  78. package/sdk-core/crates/sdk-core/src/worker/workflow/mod.rs +46 -3
  79. package/sdk-core/crates/sdk-core/tests/common/mod.rs +45 -45
  80. package/sdk-core/crates/sdk-core/tests/global_metric_tests.rs +7 -10
  81. package/sdk-core/crates/sdk-core/tests/heavy_tests/fuzzy_workflow.rs +3 -5
  82. package/sdk-core/crates/sdk-core/tests/heavy_tests.rs +34 -42
  83. package/sdk-core/crates/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +21 -26
  84. package/sdk-core/crates/sdk-core/tests/integ_tests/heartbeat_tests.rs +1 -0
  85. package/sdk-core/crates/sdk-core/tests/integ_tests/metrics_tests.rs +147 -72
  86. package/sdk-core/crates/sdk-core/tests/integ_tests/polling_tests.rs +27 -48
  87. package/sdk-core/crates/sdk-core/tests/integ_tests/update_tests.rs +5 -15
  88. package/sdk-core/crates/sdk-core/tests/integ_tests/worker_heartbeat_tests.rs +61 -66
  89. package/sdk-core/crates/sdk-core/tests/integ_tests/worker_tests.rs +16 -14
  90. package/sdk-core/crates/sdk-core/tests/integ_tests/worker_versioning_tests.rs +15 -21
  91. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/activities.rs +16 -19
  92. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -3
  93. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -3
  94. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +4 -12
  95. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +14 -9
  96. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +1 -3
  97. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/eager.rs +2 -6
  98. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +4 -8
  99. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +1 -3
  100. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/nexus.rs +11 -13
  101. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/patches.rs +11 -27
  102. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/resets.rs +3 -5
  103. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/signals.rs +4 -12
  104. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +7 -13
  105. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/timers.rs +4 -12
  106. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +1 -3
  107. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests.rs +16 -30
  108. package/sdk-core/crates/sdk-core/tests/main.rs +6 -2
  109. package/sdk-core/crates/sdk-core/tests/manual_tests.rs +40 -49
  110. package/sdk-core/crates/sdk-core/tests/runner.rs +4 -6
  111. package/sdk-core/crates/sdk-core/tests/shared_tests/mod.rs +28 -13
  112. package/sdk-core/crates/sdk-core-c-bridge/Cargo.toml +1 -0
  113. package/sdk-core/crates/sdk-core-c-bridge/include/temporal-sdk-core-c-bridge.h +24 -13
  114. package/sdk-core/crates/sdk-core-c-bridge/src/client.rs +103 -19
  115. package/sdk-core/crates/sdk-core-c-bridge/src/lib.rs +89 -5
  116. package/sdk-core/crates/sdk-core-c-bridge/src/metric.rs +1 -2
  117. package/sdk-core/crates/sdk-core-c-bridge/src/runtime.rs +59 -66
  118. package/sdk-core/crates/sdk-core-c-bridge/src/testing.rs +10 -10
  119. package/sdk-core/crates/sdk-core-c-bridge/src/tests/context.rs +46 -11
  120. package/sdk-core/crates/sdk-core-c-bridge/src/tests/mod.rs +103 -7
  121. package/sdk-core/crates/sdk-core-c-bridge/src/tests/utils.rs +6 -48
  122. package/sdk-core/crates/sdk-core-c-bridge/src/worker.rs +13 -17
  123. package/sdk-core/docker-cgroup-tests.sh +0 -0
  124. package/sdk-core/etc/cargo-tokio-console.sh +0 -0
  125. package/sdk-core/etc/integ-with-otel.sh +0 -0
  126. package/sdk-core/etc/regen-depgraph.sh +0 -0
  127. package/src/client.rs +30 -0
  128. package/src/helpers/try_into_js.rs +88 -2
  129. package/src/metrics.rs +272 -22
  130. package/src/runtime.rs +91 -41
  131. package/src/testing.rs +9 -16
  132. package/src/worker.rs +76 -55
  133. package/ts/native.ts +38 -2
  134. package/LICENSE +0 -21
  135. package/sdk-core/crates/macros/LICENSE.txt +0 -21
@@ -333,19 +333,15 @@ async fn worker_shutdown_api(#[case] use_cache: bool, #[case] api_success: bool)
333
333
  .returning(|| ("test-core".to_string(), "0.0.0".to_string()));
334
334
  mock.expect_identity()
335
335
  .returning(|| "test-identity".to_string());
336
- if use_cache {
337
- if api_success {
338
- mock.expect_shutdown_worker()
339
- .times(1)
340
- .returning(|_, _| Ok(ShutdownWorkerResponse {}));
341
- } else {
342
- // worker.shutdown() should succeed even if shutdown_worker fails
343
- mock.expect_shutdown_worker()
344
- .times(1)
345
- .returning(|_, _| Err(tonic::Status::unavailable("fake shutdown error")));
346
- }
336
+ if api_success {
337
+ mock.expect_shutdown_worker()
338
+ .times(1)
339
+ .returning(|_, _| Ok(ShutdownWorkerResponse {}));
347
340
  } else {
348
- mock.expect_shutdown_worker().times(0);
341
+ // worker.shutdown() should succeed even if shutdown_worker fails
342
+ mock.expect_shutdown_worker()
343
+ .times(1)
344
+ .returning(|_, _| Err(tonic::Status::unavailable("fake shutdown error")));
349
345
  }
350
346
 
351
347
  let t = canned_histories::single_timer("1");
@@ -397,10 +393,16 @@ fn create_test_activity_task() -> PollActivityTaskQueueResponse {
397
393
  }
398
394
 
399
395
  fn create_test_nexus_task() -> PollNexusTaskQueueResponse {
396
+ create_test_nexus_task_with_headers(Default::default())
397
+ }
398
+
399
+ fn create_test_nexus_task_with_headers(
400
+ header: std::collections::HashMap<String, String>,
401
+ ) -> PollNexusTaskQueueResponse {
400
402
  PollNexusTaskQueueResponse {
401
403
  task_token: b"nex-task".to_vec(),
402
404
  request: Some(NexusRequest {
403
- header: Default::default(),
405
+ header,
404
406
  scheduled_time: None,
405
407
  variant: Some(temporalio_common::protos::temporal::api::nexus::v1::request::Variant::StartOperation(
406
408
  StartOperationRequest {
@@ -413,6 +415,7 @@ fn create_test_nexus_task() -> PollNexusTaskQueueResponse {
413
415
  links: vec![],
414
416
  }
415
417
  )),
418
+ endpoint: "".to_string(),
416
419
  }),
417
420
  poller_scaling_decision: None,
418
421
  }
@@ -589,3 +592,153 @@ async fn test_task_type_combinations_unified(
589
592
  worker.shutdown().await;
590
593
  worker.finalize_shutdown().await;
591
594
  }
595
+
596
+ #[tokio::test]
597
+ async fn nexus_request_deadline_missing_header() {
598
+ let mut client = mock_worker_client();
599
+ client
600
+ .expect_complete_nexus_task()
601
+ .returning(|_, _| Ok(RespondNexusTaskCompletedResponse::default()));
602
+ let nexus_task = create_test_nexus_task(); // No headers
603
+ let mut mocks = MocksHolder::from_client_with_custom(
604
+ client,
605
+ None::<stream::Empty<_>>,
606
+ None::<Vec<QueueResponse<PollActivityTaskQueueResponse>>>,
607
+ Some(vec![QueueResponse::from(nexus_task)]),
608
+ );
609
+ mocks.worker_cfg(|w| {
610
+ w.task_queue = "test-queue".to_string();
611
+ w.task_types = WorkerTaskTypes {
612
+ enable_workflows: false,
613
+ enable_local_activities: false,
614
+ enable_remote_activities: false,
615
+ enable_nexus: true,
616
+ };
617
+ w.skip_client_worker_set_check = true;
618
+ });
619
+ let worker = mock_worker(mocks);
620
+
621
+ let nexus_task = worker.poll_nexus_task().await.unwrap();
622
+ assert!(
623
+ nexus_task.request_deadline.is_none(),
624
+ "request_deadline should be None when header is missing"
625
+ );
626
+
627
+ worker
628
+ .complete_nexus_task(create_test_nexus_completion(nexus_task.task_token()))
629
+ .await
630
+ .unwrap();
631
+ worker.initiate_shutdown();
632
+ assert_matches!(
633
+ worker.poll_nexus_task().await.unwrap_err(),
634
+ PollError::ShutDown
635
+ );
636
+ worker.shutdown().await;
637
+ worker.finalize_shutdown().await;
638
+ }
639
+
640
+ #[tokio::test]
641
+ async fn nexus_request_deadline_valid_header() {
642
+ let mut client = mock_worker_client();
643
+ client
644
+ .expect_complete_nexus_task()
645
+ .returning(|_, _| Ok(RespondNexusTaskCompletedResponse::default()));
646
+ let mut headers = std::collections::HashMap::new();
647
+ headers.insert("request-timeout".to_string(), "30s".to_string());
648
+ let nexus_task = create_test_nexus_task_with_headers(headers);
649
+ let mut mocks = MocksHolder::from_client_with_custom(
650
+ client,
651
+ None::<stream::Empty<_>>,
652
+ None::<Vec<QueueResponse<PollActivityTaskQueueResponse>>>,
653
+ Some(vec![QueueResponse::from(nexus_task)]),
654
+ );
655
+ mocks.worker_cfg(|w| {
656
+ w.task_queue = "test-queue".to_string();
657
+ w.task_types = WorkerTaskTypes {
658
+ enable_workflows: false,
659
+ enable_local_activities: false,
660
+ enable_remote_activities: false,
661
+ enable_nexus: true,
662
+ };
663
+ w.skip_client_worker_set_check = true;
664
+ });
665
+ let worker = mock_worker(mocks);
666
+
667
+ let before = std::time::SystemTime::now();
668
+ let nexus_task = worker.poll_nexus_task().await.unwrap();
669
+ let after = std::time::SystemTime::now();
670
+
671
+ assert!(
672
+ nexus_task.request_deadline.is_some(),
673
+ "request_deadline should be Some when valid header is present"
674
+ );
675
+ let deadline: std::time::SystemTime = nexus_task.request_deadline.unwrap().try_into().unwrap();
676
+ // Deadline should be approximately 30s from now
677
+ let expected_min = before + Duration::from_secs(30);
678
+ let expected_max = after + Duration::from_secs(30);
679
+ assert!(
680
+ deadline >= expected_min && deadline <= expected_max,
681
+ "deadline {:?} should be between {:?} and {:?}",
682
+ deadline,
683
+ expected_min,
684
+ expected_max
685
+ );
686
+
687
+ worker
688
+ .complete_nexus_task(create_test_nexus_completion(nexus_task.task_token()))
689
+ .await
690
+ .unwrap();
691
+ worker.initiate_shutdown();
692
+ assert_matches!(
693
+ worker.poll_nexus_task().await.unwrap_err(),
694
+ PollError::ShutDown
695
+ );
696
+ worker.shutdown().await;
697
+ worker.finalize_shutdown().await;
698
+ }
699
+
700
+ #[tokio::test]
701
+ async fn nexus_request_deadline_invalid_header() {
702
+ let mut client = mock_worker_client();
703
+ client
704
+ .expect_complete_nexus_task()
705
+ .returning(|_, _| Ok(RespondNexusTaskCompletedResponse::default()));
706
+ let mut headers = std::collections::HashMap::new();
707
+ headers.insert("request-timeout".to_string(), "invalid".to_string());
708
+ let nexus_task = create_test_nexus_task_with_headers(headers);
709
+ let mut mocks = MocksHolder::from_client_with_custom(
710
+ client,
711
+ None::<stream::Empty<_>>,
712
+ None::<Vec<QueueResponse<PollActivityTaskQueueResponse>>>,
713
+ Some(vec![QueueResponse::from(nexus_task)]),
714
+ );
715
+ mocks.worker_cfg(|w| {
716
+ w.task_queue = "test-queue".to_string();
717
+ w.task_types = WorkerTaskTypes {
718
+ enable_workflows: false,
719
+ enable_local_activities: false,
720
+ enable_remote_activities: false,
721
+ enable_nexus: true,
722
+ };
723
+ w.skip_client_worker_set_check = true;
724
+ });
725
+ let worker = mock_worker(mocks);
726
+
727
+ let nexus_task = worker.poll_nexus_task().await.unwrap();
728
+ assert!(
729
+ nexus_task.request_deadline.is_none(),
730
+ "request_deadline should be None when header is invalid"
731
+ );
732
+
733
+ worker
734
+ .complete_nexus_task(create_test_nexus_completion(nexus_task.task_token()))
735
+ .await
736
+ .unwrap();
737
+ worker.initiate_shutdown();
738
+ assert_matches!(
739
+ worker.poll_nexus_task().await.unwrap_err(),
740
+ PollError::ShutDown
741
+ );
742
+ worker.shutdown().await;
743
+ worker.finalize_shutdown().await;
744
+ }
@@ -1621,6 +1621,7 @@ async fn history_byte_size_and_can_suggestion_in_activation() {
1621
1621
  he.attributes
1622
1622
  {
1623
1623
  attrs.suggest_continue_as_new = true;
1624
+ attrs.suggest_continue_as_new_reasons = vec![1, 2, 3, 4]
1624
1625
  }
1625
1626
  });
1626
1627
 
@@ -1645,6 +1646,7 @@ async fn history_byte_size_and_can_suggestion_in_activation() {
1645
1646
  let activation = worker.poll_workflow_activation().await.unwrap();
1646
1647
  assert_eq!(activation.history_size_bytes, 70);
1647
1648
  assert!(activation.continue_as_new_suggested);
1649
+ assert_eq!(activation.suggest_continue_as_new_reasons, vec![1, 2, 3, 4])
1648
1650
  }
1649
1651
 
1650
1652
  /// This test verifies that WFTs which come as replies to completing a WFT are properly delivered
@@ -2022,12 +2024,15 @@ async fn no_race_acquiring_permits() {
2022
2024
  .returning(|_| async move { Ok(Default::default()) }.boxed());
2023
2025
 
2024
2026
  let worker = Worker::new_test(
2025
- test_worker_cfg()
2026
- .max_outstanding_workflow_tasks(2_usize)
2027
- .max_cached_workflows(0_usize)
2028
- .ignore_evicts_on_shutdown(false)
2029
- .build()
2030
- .unwrap(),
2027
+ {
2028
+ let mut cfg = test_worker_cfg()
2029
+ .max_outstanding_workflow_tasks(2_usize)
2030
+ .max_cached_workflows(0_usize)
2031
+ .build()
2032
+ .unwrap();
2033
+ cfg.ignore_evicts_on_shutdown = false;
2034
+ cfg
2035
+ },
2031
2036
  mock_client,
2032
2037
  );
2033
2038
 
@@ -2667,13 +2672,14 @@ async fn poller_wont_run_ahead_of_task_slots() {
2667
2672
  .returning(|_| Ok(Default::default()));
2668
2673
 
2669
2674
  let worker = Worker::new_test(
2670
- test_worker_cfg()
2671
- .max_cached_workflows(10_usize)
2672
- .max_outstanding_workflow_tasks(10_usize)
2673
- .workflow_task_poller_behavior(PollerBehavior::SimpleMaximum(10_usize))
2674
- .task_types(WorkerTaskTypes::workflow_only())
2675
- .build()
2676
- .unwrap(),
2675
+ {
2676
+ let mut cfg = test_worker_cfg().build().unwrap();
2677
+ cfg.max_cached_workflows = 10_usize;
2678
+ cfg.max_outstanding_workflow_tasks = Some(10_usize);
2679
+ cfg.workflow_task_poller_behavior = PollerBehavior::SimpleMaximum(10_usize);
2680
+ cfg.task_types = WorkerTaskTypes::workflow_only();
2681
+ cfg
2682
+ },
2677
2683
  mock_client,
2678
2684
  );
2679
2685
 
@@ -2730,10 +2736,11 @@ async fn poller_wont_poll_until_lang_polls() {
2730
2736
  });
2731
2737
 
2732
2738
  let worker = Worker::new_test(
2733
- test_worker_cfg()
2734
- .task_types(WorkerTaskTypes::workflow_only())
2735
- .build()
2736
- .unwrap(),
2739
+ {
2740
+ let mut cfg = test_worker_cfg().build().unwrap();
2741
+ cfg.task_types = WorkerTaskTypes::workflow_only();
2742
+ cfg
2743
+ },
2737
2744
  mock_client,
2738
2745
  );
2739
2746
 
@@ -2868,17 +2875,18 @@ async fn slot_provider_cant_hand_out_more_permits_than_cache_size() {
2868
2875
  }
2869
2876
 
2870
2877
  let worker = Worker::new_test(
2871
- test_worker_cfg()
2872
- .max_cached_workflows(10_usize)
2873
- .tuner(Arc::new(
2878
+ {
2879
+ let mut cfg = test_worker_cfg().build().unwrap();
2880
+ cfg.max_cached_workflows = 10_usize;
2881
+ cfg.tuner = Some(Arc::new(
2874
2882
  TunerBuilder::default()
2875
2883
  .workflow_slot_supplier(Arc::new(EndlessSupplier {}))
2876
2884
  .build(),
2877
- ))
2878
- .workflow_task_poller_behavior(PollerBehavior::SimpleMaximum(10_usize))
2879
- .task_types(WorkerTaskTypes::workflow_only())
2880
- .build()
2881
- .unwrap(),
2885
+ ));
2886
+ cfg.workflow_task_poller_behavior = PollerBehavior::SimpleMaximum(10_usize);
2887
+ cfg.task_types = WorkerTaskTypes::workflow_only();
2888
+ cfg
2889
+ },
2882
2890
  mock_client,
2883
2891
  );
2884
2892
 
@@ -3019,14 +3027,15 @@ async fn both_normal_and_sticky_pollers_poll_concurrently() {
3019
3027
  });
3020
3028
 
3021
3029
  let worker = Worker::new(
3022
- test_worker_cfg()
3023
- .max_cached_workflows(500_usize) // We need cache, but don't want to deal with evictions
3024
- .max_outstanding_workflow_tasks(2_usize)
3025
- .workflow_task_poller_behavior(PollerBehavior::SimpleMaximum(2_usize))
3026
- .nonsticky_to_sticky_poll_ratio(0.2)
3027
- .task_types(WorkerTaskTypes::workflow_only())
3028
- .build()
3029
- .unwrap(),
3030
+ {
3031
+ let mut cfg = test_worker_cfg().build().unwrap();
3032
+ cfg.max_cached_workflows = 500_usize; // We need cache, but don't want to deal with evictions
3033
+ cfg.max_outstanding_workflow_tasks = Some(2_usize);
3034
+ cfg.workflow_task_poller_behavior = PollerBehavior::SimpleMaximum(2_usize);
3035
+ cfg.nonsticky_to_sticky_poll_ratio = 0.2;
3036
+ cfg.task_types = WorkerTaskTypes::workflow_only();
3037
+ cfg
3038
+ },
3030
3039
  Some("stickytq".to_string()),
3031
3040
  Arc::new(mock_client),
3032
3041
  None,
@@ -24,30 +24,28 @@ use std::os::unix::fs::OpenOptionsExt;
24
24
  use std::process::Stdio;
25
25
 
26
26
  /// Configuration for Temporal CLI dev server.
27
- #[derive(Debug, Clone, derive_builder::Builder)]
27
+ #[derive(Debug, Clone, bon::Builder)]
28
+ #[builder(on(String, into))]
28
29
  pub struct TemporalDevServerConfig {
29
30
  /// Required path to executable or download info.
30
31
  pub exe: EphemeralExe,
31
32
  /// Namespace to use.
32
- #[builder(default = "\"default\".to_owned()")]
33
+ #[builder(default = "default".to_owned())]
33
34
  pub namespace: String,
34
35
  /// IP to bind to.
35
- #[builder(default = "\"127.0.0.1\".to_owned()")]
36
+ #[builder(default = "127.0.0.1".to_owned())]
36
37
  pub ip: String,
37
38
  /// Port to use or obtains a free one if none given.
38
- #[builder(default)]
39
39
  pub port: Option<u16>,
40
40
  /// Port to use for the UI server or obtains a free one if none given.
41
- #[builder(default)]
42
41
  pub ui_port: Option<u16>,
43
42
  /// Sqlite DB filename if persisting or non-persistent if none.
44
- #[builder(default)]
45
43
  pub db_filename: Option<String>,
46
44
  /// Whether to enable the UI. If ui_port is set, assumes true.
47
45
  #[builder(default)]
48
46
  pub ui: bool,
49
47
  /// Log format and level
50
- #[builder(default = "(\"pretty\".to_owned(), \"warn\".to_owned())")]
48
+ #[builder(default = ("pretty".to_owned(), "warn".to_owned()))]
51
49
  pub log: (String, String),
52
50
  /// Additional arguments to Temporal dev server.
53
51
  #[builder(default)]
@@ -131,12 +129,11 @@ impl TemporalDevServerConfig {
131
129
  }
132
130
 
133
131
  /// Configuration for the test server.
134
- #[derive(Debug, Clone, derive_builder::Builder)]
132
+ #[derive(Debug, Clone, bon::Builder)]
135
133
  pub struct TestServerConfig {
136
134
  /// Required path to executable or download info.
137
135
  pub exe: EphemeralExe,
138
136
  /// Port to use or obtains a free one if none given.
139
- #[builder(default)]
140
137
  pub port: Option<u16>,
141
138
  /// Additional arguments to the test server.
142
139
  #[builder(default)]
@@ -240,9 +240,9 @@ pub struct CoreRuntime {
240
240
  heartbeat_interval: Option<Duration>,
241
241
  }
242
242
 
243
- /// Holds telemetry options, as well as worker heartbeat_interval. Construct with [RuntimeOptionsBuilder]
244
- #[derive(Default, derive_builder::Builder)]
245
- #[builder(build_fn(validate = "Self::validate"))]
243
+ /// Holds telemetry options, as well as worker heartbeat_interval. Construct with [RuntimeOptions::builder]
244
+ #[derive(Default, bon::Builder)]
245
+ #[builder(finish_fn(vis = "", name = build_internal))]
246
246
  #[non_exhaustive]
247
247
  pub struct RuntimeOptions {
248
248
  /// Telemetry configuration options.
@@ -252,21 +252,28 @@ pub struct RuntimeOptions {
252
252
  /// workers created using this runtime.
253
253
  ///
254
254
  /// Interval must be between 1s and 60s, inclusive.
255
- #[builder(default = "Some(Duration::from_secs(60))")]
255
+ #[builder(required, default = Some(Duration::from_secs(60)))]
256
256
  heartbeat_interval: Option<Duration>,
257
257
  }
258
258
 
259
- impl RuntimeOptionsBuilder {
260
- fn validate(&self) -> Result<(), String> {
261
- if let Some(Some(interval)) = self.heartbeat_interval
262
- && (interval < Duration::from_secs(1) || interval > Duration::from_secs(60))
259
+ impl<S: runtime_options_builder::State> RuntimeOptionsBuilder<S> {
260
+ /// Builds the RuntimeOptions
261
+ ///
262
+ /// # Errors
263
+ /// Returns an error if heartbeat_interval is set but not between 1s and 60s inclusive.
264
+ pub fn build(self) -> Result<RuntimeOptions, String> {
265
+ let options = self.build_internal();
263
266
  {
264
- return Err(format!(
265
- "heartbeat_interval ({interval:?}) must be between 1s and 60s",
266
- ));
267
+ if let Some(interval) = options.heartbeat_interval
268
+ && (interval < Duration::from_secs(1) || interval > Duration::from_secs(60))
269
+ {
270
+ return Err(format!(
271
+ "heartbeat_interval ({interval:?}) must be between 1s and 60s",
272
+ ));
273
+ }
274
+
275
+ Ok(options)
267
276
  }
268
-
269
- Ok(())
270
277
  }
271
278
  }
272
279