restate-sdk 0.12.0 → 0.13.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2e91954de78530040b4ef55bed9b2edda82440f8a376b779fb8d99d6dfc2cc80
4
- data.tar.gz: dbb6d5edbbfdbe5d4ed339546a9c28be5c46d771d8e859931bb63791b4eaf55e
3
+ metadata.gz: 3ce15bde18d1b56824de0e89d3b01a2a8df327c0adf454c922d72bef60a9afae
4
+ data.tar.gz: 9ceb40425d0be8ffb705e8639a7dc911791c5e77de56965f8b9214b640c7a6e6
5
5
  SHA512:
6
- metadata.gz: 9092d9e1f3bd1efed1331090c88e2bbe1cb556dcdf99d26a64112583a318eaea8002f44c8e23b86ae9f155cba31917de6a942822e8f7df2441d2b5fae99f7529
7
- data.tar.gz: 769a28444a0cbad0bc9aeaac73aa590823a998d939c804e58be68e2fecfb64cd2f6fef45efcb24ef8026d85fb9de73406e5f440174d3f71a05f2a6d85ed7c5f4
6
+ metadata.gz: edd0c0a9a4dd9df432ca4ee736f96f96daaee2e5485437968b43c9f10de6f1acc7dea1510d2e96436d49a25045f4ef83819fbac09388a1f80dba317d86f157dd
7
+ data.tar.gz: d94297953c904448104d9b033d9eafabdc63e75f605235aedb31501070ce3b528a590ac1ab7186e4044d6f385039404c6bfbaf8d6c34a0af2d718d5a9efe0029
data/Cargo.lock CHANGED
@@ -561,7 +561,7 @@ dependencies = [
561
561
 
562
562
  [[package]]
563
563
  name = "restate_internal"
564
- version = "0.12.0"
564
+ version = "0.13.0"
565
565
  dependencies = [
566
566
  "magnus",
567
567
  "rb-sys",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "restate_internal"
3
- version = "0.12.0"
3
+ version = "0.13.0"
4
4
  edition = "2021"
5
5
  publish = false
6
6
 
@@ -129,6 +129,7 @@ struct RbFailure {
129
129
  code: u16,
130
130
  message: String,
131
131
  stacktrace: Option<String>,
132
+ metadata: Vec<(String, String)>,
132
133
  }
133
134
 
134
135
  impl RbFailure {
@@ -141,6 +142,17 @@ impl RbFailure {
141
142
  fn stacktrace(&self) -> Option<&str> {
142
143
  self.stacktrace.as_deref()
143
144
  }
145
+ fn metadata_array(&self) -> Result<RArray, Error> {
146
+ let ruby = Ruby::get().map_err(|_| Error::new(vm_error_class(), "Ruby not available"))?;
147
+ let ary = ruby.ary_new_capa(self.metadata.len());
148
+ for (k, v) in &self.metadata {
149
+ let pair = ruby.ary_new_capa(2);
150
+ pair.push(ruby.str_new(k))?;
151
+ pair.push(ruby.str_new(v))?;
152
+ ary.push(pair)?;
153
+ }
154
+ Ok(ary)
155
+ }
144
156
  }
145
157
 
146
158
  impl From<TerminalFailure> for RbFailure {
@@ -149,6 +161,7 @@ impl From<TerminalFailure> for RbFailure {
149
161
  code: f.code,
150
162
  message: f.message,
151
163
  stacktrace: None,
164
+ metadata: f.metadata,
152
165
  }
153
166
  }
154
167
  }
@@ -158,7 +171,7 @@ impl From<RbFailure> for TerminalFailure {
158
171
  TerminalFailure {
159
172
  code: f.code,
160
173
  message: f.message,
161
- metadata: vec![],
174
+ metadata: f.metadata,
162
175
  }
163
176
  }
164
177
  }
@@ -929,17 +942,36 @@ fn parse_headers_array(ary: RArray) -> Result<Vec<Header>, Error> {
929
942
 
930
943
  // Constructor functions (free functions for use with function! macro)
931
944
 
932
- fn rb_failure_new(code: u16, message: String, stacktrace: Value) -> RbFailure {
945
+ fn rb_failure_new(
946
+ code: u16,
947
+ message: String,
948
+ stacktrace: Value,
949
+ metadata: Value,
950
+ ) -> Result<RbFailure, Error> {
933
951
  let st = if stacktrace.is_nil() {
934
952
  None
935
953
  } else {
936
954
  Some(String::try_convert(stacktrace).unwrap_or_default())
937
955
  };
938
- RbFailure {
956
+ let md = if metadata.is_nil() {
957
+ Vec::new()
958
+ } else {
959
+ let ary = RArray::try_convert(metadata)?;
960
+ let mut out = Vec::with_capacity(ary.len());
961
+ for item in ary.into_iter() {
962
+ let pair = RArray::try_convert(item)?;
963
+ let k: String = pair.entry(0)?;
964
+ let v: String = pair.entry(1)?;
965
+ out.push((k, v));
966
+ }
967
+ out
968
+ };
969
+ Ok(RbFailure {
939
970
  code,
940
971
  message,
941
972
  stacktrace: st,
942
- }
973
+ metadata: md,
974
+ })
943
975
  }
944
976
 
945
977
  fn rb_exponential_retry_config_new(
@@ -1002,12 +1034,13 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
1002
1034
  rh_class.define_method("status_code", method!(RbResponseHead::status_code, 0))?;
1003
1035
  rh_class.define_method("headers", method!(RbResponseHead::headers_array, 0))?;
1004
1036
 
1005
- // Failure - constructor takes (code, message, stacktrace_or_nil)
1037
+ // Failure - constructor takes (code, message, stacktrace_or_nil, metadata_or_nil)
1006
1038
  let failure_class = internal.define_class("Failure", ruby.class_object())?;
1007
- failure_class.define_singleton_method("new", function!(rb_failure_new, 3))?;
1039
+ failure_class.define_singleton_method("new", function!(rb_failure_new, 4))?;
1008
1040
  failure_class.define_method("code", method!(RbFailure::code, 0))?;
1009
1041
  failure_class.define_method("message", method!(RbFailure::message, 0))?;
1010
1042
  failure_class.define_method("stacktrace", method!(RbFailure::stacktrace, 0))?;
1043
+ failure_class.define_method("metadata", method!(RbFailure::metadata_array, 0))?;
1011
1044
 
1012
1045
  // Void, Suspended, StateKeys
1013
1046
  internal.define_class("Void", ruby.class_object())?;
@@ -20,9 +20,20 @@ module Restate
20
20
  attr_accessor :admin_url
21
21
 
22
22
  # Default headers sent with every ingress request.
23
+ # Can be a Hash or a callable (Proc/Lambda) returning a Hash.
24
+ # A callable is evaluated each time +Restate.client+ is called,
25
+ # which lets frameworks like Rails inject per-request context
26
+ # (e.g., team ID, shard routing, auth tokens).
27
+ #
28
+ # @example Static headers
29
+ # config.ingress_headers = { "Authorization" => "Bearer tok" }
30
+ #
31
+ # @example Dynamic headers
32
+ # config.ingress_headers = -> { { "X-Team-Id" => Current.team_id } }
23
33
  attr_accessor :ingress_headers
24
34
 
25
35
  # Default headers sent with every admin request.
36
+ # Accepts the same static Hash or callable forms as +ingress_headers+.
26
37
  attr_accessor :admin_headers
27
38
 
28
39
  def initialize
@@ -41,7 +41,7 @@ module Restate
41
41
  compact(
42
42
  protocolMode: protocol_mode,
43
43
  minProtocolVersion: 5,
44
- maxProtocolVersion: 5,
44
+ maxProtocolVersion: 6,
45
45
  services: services
46
46
  )
47
47
  end
@@ -6,12 +6,14 @@ module Restate
6
6
  #
7
7
  # @example
8
8
  # raise Restate::TerminalError.new('not found', status_code: 404)
9
+ # raise Restate::TerminalError.new('bad input', metadata: { 'field' => 'name' })
9
10
  class TerminalError < StandardError
10
- attr_reader :status_code
11
+ attr_reader :status_code, :metadata
11
12
 
12
- def initialize(message = 'Internal Server Error', status_code: 500)
13
+ def initialize(message = 'Internal Server Error', status_code: 500, metadata: nil)
13
14
  super(message)
14
15
  @status_code = status_code
16
+ @metadata = metadata
15
17
  end
16
18
  end
17
19
 
@@ -50,7 +50,7 @@ module Restate
50
50
  @vm.sys_write_output_success(out_buffer)
51
51
  @vm.sys_end
52
52
  rescue TerminalError => e
53
- failure = Failure.new(code: e.status_code, message: e.message)
53
+ failure = Failure.new(code: e.status_code, message: e.message, metadata: e.metadata)
54
54
  @vm.sys_write_output_failure(failure)
55
55
  @vm.sys_end
56
56
  rescue SuspendedError, InternalError
@@ -63,7 +63,7 @@ module Restate
63
63
  handled = false
64
64
  while cause
65
65
  if cause.is_a?(TerminalError)
66
- f = Failure.new(code: cause.status_code, message: cause.message)
66
+ f = Failure.new(code: cause.status_code, message: cause.message, metadata: cause.metadata)
67
67
  @vm.sys_write_output_failure(f)
68
68
  @vm.sys_end
69
69
  handled = true
@@ -472,7 +472,7 @@ module Restate
472
472
  when NotReady
473
473
  raise "Unexpected NotReady for handle #{handle}"
474
474
  when Failure
475
- raise TerminalError.new(result.message, status_code: result.code)
475
+ raise TerminalError.new(result.message, status_code: result.code, metadata: result.metadata)
476
476
  else
477
477
  block ? yield(result) : result
478
478
  end
@@ -561,7 +561,7 @@ module Restate
561
561
  buffer = serde.serialize(result)
562
562
  @vm.propose_run_completion_success(handle, buffer)
563
563
  rescue TerminalError => e
564
- failure = Failure.new(code: e.status_code, message: e.message)
564
+ failure = Failure.new(code: e.status_code, message: e.message, metadata: e.metadata)
565
565
  @vm.propose_run_completion_failure(handle, failure)
566
566
  rescue SuspendedError, InternalError
567
567
  raise
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Restate
5
- VERSION = '0.12.0'
5
+ VERSION = '0.13.0'
6
6
  end
data/lib/restate/vm.rb CHANGED
@@ -7,14 +7,19 @@ begin
7
7
  RUBY_VERSION =~ /(\d+\.\d+)/
8
8
  require_relative "#{Regexp.last_match(1)}/restate_internal"
9
9
  rescue LoadError
10
- # Fall back to the default location (development builds, source gem installs)
11
- require_relative 'restate_internal'
10
+ begin
11
+ # rake compile output (ext.lib_dir = 'lib/restate' in Rakefile)
12
+ require_relative 'restate_internal'
13
+ rescue LoadError
14
+ # gem install from source: extconf.rb builds to lib/restate_internal
15
+ require_relative '../restate_internal'
16
+ end
12
17
  end
13
18
 
14
19
  module Restate
15
20
  # Ruby-side data types for VM results
16
21
  Invocation = Struct.new(:invocation_id, :random_seed, :headers, :input_buffer, :key, keyword_init: true)
17
- Failure = Struct.new(:code, :message, :stacktrace, keyword_init: true)
22
+ Failure = Struct.new(:code, :message, :stacktrace, :metadata, keyword_init: true)
18
23
 
19
24
  class NotReady; end
20
25
  class Suspended; end
@@ -157,12 +162,12 @@ module Restate
157
162
  end
158
163
 
159
164
  def propose_run_completion_failure(handle, failure)
160
- native_failure = Internal::Failure.new(failure.code, failure.message, nil)
165
+ native_failure = Internal::Failure.new(failure.code, failure.message, nil, native_metadata(failure))
161
166
  @vm.propose_run_completion_failure(handle, native_failure)
162
167
  end
163
168
 
164
169
  def propose_run_completion_transient(handle, failure:, attempt_duration_ms:, config:)
165
- native_failure = Internal::Failure.new(failure.code, failure.message, failure.stacktrace)
170
+ native_failure = Internal::Failure.new(failure.code, failure.message, failure.stacktrace, nil)
166
171
  native_config = Internal::ExponentialRetryConfig.new(
167
172
  config.initial_interval, config.max_attempts,
168
173
  config.max_duration, config.max_interval,
@@ -176,7 +181,7 @@ module Restate
176
181
  end
177
182
 
178
183
  def sys_write_output_failure(failure)
179
- native_failure = Internal::Failure.new(failure.code, failure.message, nil)
184
+ native_failure = Internal::Failure.new(failure.code, failure.message, nil, native_metadata(failure))
180
185
  @vm.sys_write_output_failure(native_failure)
181
186
  end
182
187
 
@@ -198,7 +203,7 @@ module Restate
198
203
  end
199
204
 
200
205
  def sys_complete_awakeable_failure(awakeable_id, failure)
201
- native_failure = Internal::Failure.new(failure.code, failure.message, nil)
206
+ native_failure = Internal::Failure.new(failure.code, failure.message, nil, native_metadata(failure))
202
207
  @vm.sys_complete_awakeable_failure(awakeable_id, native_failure)
203
208
  end
204
209
 
@@ -215,7 +220,7 @@ module Restate
215
220
  end
216
221
 
217
222
  def sys_complete_promise_failure(key, failure)
218
- native_failure = Internal::Failure.new(failure.code, failure.message, nil)
223
+ native_failure = Internal::Failure.new(failure.code, failure.message, nil, native_metadata(failure))
219
224
  @vm.sys_complete_promise_failure(key, native_failure)
220
225
  end
221
226
 
@@ -232,12 +237,19 @@ module Restate
232
237
  end
233
238
 
234
239
  def sys_complete_signal_failure(invocation_id, name, failure)
235
- native_failure = Internal::Failure.new(failure.code, failure.message, nil)
240
+ native_failure = Internal::Failure.new(failure.code, failure.message, nil, native_metadata(failure))
236
241
  @vm.sys_complete_signal_failure(invocation_id, name, native_failure)
237
242
  end
238
243
 
239
244
  private
240
245
 
246
+ def native_metadata(failure)
247
+ md = failure.metadata
248
+ return nil if md.nil? || md.empty?
249
+
250
+ md.map { |k, v| [k.to_s, v.to_s] }
251
+ end
252
+
241
253
  def map_do_progress(result)
242
254
  case result
243
255
  when Internal::Suspended
@@ -270,7 +282,7 @@ module Restate
270
282
  # The native layer returns RString for both.
271
283
  result
272
284
  when Internal::Failure
273
- Failure.new(code: result.code, message: result.message)
285
+ Failure.new(code: result.code, message: result.message, metadata: result.metadata.to_h)
274
286
  when Internal::StateKeys
275
287
  result.keys
276
288
  else
data/lib/restate.rb CHANGED
@@ -76,7 +76,13 @@ module Restate # rubocop:disable Metrics/ModuleLength
76
76
  def client
77
77
  cfg = config
78
78
  Client.new(ingress_url: cfg.ingress_url, admin_url: cfg.admin_url,
79
- ingress_headers: cfg.ingress_headers, admin_headers: cfg.admin_headers)
79
+ ingress_headers: resolve_headers(cfg.ingress_headers),
80
+ admin_headers: resolve_headers(cfg.admin_headers))
81
+ end
82
+
83
+ # @!visibility private
84
+ def resolve_headers(headers)
85
+ headers.respond_to?(:call) ? headers.call : headers
80
86
  end
81
87
 
82
88
  # ── Context accessor (internal) ──
data/sig/restate.rbs CHANGED
@@ -69,7 +69,8 @@ module Restate
69
69
 
70
70
  class TerminalError < StandardError
71
71
  attr_reader status_code: Integer
72
- def initialize: (?String message, ?status_code: Integer) -> void
72
+ attr_reader metadata: Hash[String, String]?
73
+ def initialize: (?String message, ?status_code: Integer, ?metadata: Hash[String, String]?) -> void
73
74
  end
74
75
 
75
76
  class SuspendedError < StandardError
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: restate-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0
4
+ version: 0.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Restate Developers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-05-13 00:00:00.000000000 Z
11
+ date: 2026-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async