restate-sdk 0.7.0 → 0.9.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 +4 -4
- data/Cargo.lock +1 -1
- data/README.md +15 -10
- data/ext/restate_internal/Cargo.toml +1 -1
- data/lib/restate/client.rb +5 -29
- data/lib/restate/config.rb +4 -11
- data/lib/restate/context.rb +13 -128
- data/lib/restate/discovery.rb +6 -15
- data/lib/restate/durable_future.rb +15 -39
- data/lib/restate/endpoint.rb +61 -48
- data/lib/restate/errors.rb +2 -15
- data/lib/restate/handler.rb +21 -21
- data/lib/restate/serde.rb +14 -130
- data/lib/restate/server.rb +12 -27
- data/lib/restate/server_context.rb +111 -276
- data/lib/restate/service.rb +2 -3
- data/lib/restate/service_dsl.rb +8 -8
- data/lib/restate/service_proxy.rb +2 -13
- data/lib/restate/testing.rb +1 -1
- data/lib/restate/version.rb +2 -2
- data/lib/restate/virtual_object.rb +3 -4
- data/lib/restate/vm.rb +9 -74
- data/lib/restate/workflow.rb +3 -4
- data/lib/restate.rb +4 -93
- data/sig/restate.rbs +202 -0
- metadata +3 -18
- data/lib/tapioca/dsl/compilers/restate.rb +0 -115
- data/rbi/restate-sdk.rbi +0 -582
data/lib/restate.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# typed: true
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
require 'sorbet-runtime'
|
|
5
4
|
require_relative 'restate/version'
|
|
6
5
|
require_relative 'restate/errors'
|
|
7
6
|
require_relative 'restate/serde'
|
|
@@ -31,8 +30,6 @@ require_relative 'restate/client'
|
|
|
31
30
|
# because +Thread.current[]+ is NOT inherited by child fibers, which prevents
|
|
32
31
|
# accidental context leaks when Async spawns child tasks for run blocks.
|
|
33
32
|
module Restate # rubocop:disable Metrics/ModuleLength
|
|
34
|
-
extend T::Sig
|
|
35
|
-
|
|
36
33
|
module_function
|
|
37
34
|
|
|
38
35
|
# Create an endpoint, optionally binding services.
|
|
@@ -40,13 +37,6 @@ module Restate # rubocop:disable Metrics/ModuleLength
|
|
|
40
37
|
#
|
|
41
38
|
# @param services [Array<Class>] service classes or instances to bind
|
|
42
39
|
# @return [Endpoint]
|
|
43
|
-
sig do
|
|
44
|
-
params(
|
|
45
|
-
services: T.untyped,
|
|
46
|
-
protocol: T.nilable(String),
|
|
47
|
-
identity_keys: T.nilable(T::Array[String])
|
|
48
|
-
).returns(Endpoint)
|
|
49
|
-
end
|
|
50
40
|
def endpoint(*services, protocol: nil, identity_keys: nil)
|
|
51
41
|
ep = Endpoint.new
|
|
52
42
|
ep.streaming_protocol if protocol == 'bidi'
|
|
@@ -65,15 +55,13 @@ module Restate # rubocop:disable Metrics/ModuleLength
|
|
|
65
55
|
# c.ingress_url = "http://localhost:8080"
|
|
66
56
|
# c.admin_url = "http://localhost:9070"
|
|
67
57
|
# end
|
|
68
|
-
|
|
69
|
-
def configure(&_block)
|
|
58
|
+
def configure(&)
|
|
70
59
|
yield config
|
|
71
60
|
end
|
|
72
61
|
|
|
73
62
|
# Returns the global configuration. Creates a default one on first access.
|
|
74
|
-
sig { returns(Config) }
|
|
75
63
|
def config
|
|
76
|
-
@config =
|
|
64
|
+
@config = nil unless defined?(@config)
|
|
77
65
|
@config ||= Config.new
|
|
78
66
|
end
|
|
79
67
|
|
|
@@ -84,7 +72,6 @@ module Restate # rubocop:disable Metrics/ModuleLength
|
|
|
84
72
|
# Restate.client.service(Greeter).greet("World")
|
|
85
73
|
# Restate.client.resolve_awakeable(id, payload)
|
|
86
74
|
# Restate.client.create_deployment("http://localhost:9080")
|
|
87
|
-
sig { returns(Client) }
|
|
88
75
|
def client
|
|
89
76
|
cfg = config
|
|
90
77
|
Client.new(ingress_url: cfg.ingress_url, admin_url: cfg.admin_url,
|
|
@@ -94,9 +81,6 @@ module Restate # rubocop:disable Metrics/ModuleLength
|
|
|
94
81
|
# ── Context accessor (internal) ──
|
|
95
82
|
|
|
96
83
|
# @!visibility private
|
|
97
|
-
sig do
|
|
98
|
-
params(service_kind: T.nilable(String), handler_kind: T.nilable(String)).returns(ServerContext)
|
|
99
|
-
end
|
|
100
84
|
def fetch_context!(service_kind: nil, handler_kind: nil) # rubocop:disable Metrics
|
|
101
85
|
ctx = Thread.current[:restate_context]
|
|
102
86
|
unless ctx
|
|
@@ -118,32 +102,23 @@ module Restate # rubocop:disable Metrics/ModuleLength
|
|
|
118
102
|
end
|
|
119
103
|
end
|
|
120
104
|
|
|
121
|
-
|
|
105
|
+
ctx
|
|
122
106
|
end
|
|
123
107
|
|
|
124
108
|
# ── Durable execution ──
|
|
125
109
|
|
|
126
110
|
# Execute a durable side effect. The block runs at most once; the result
|
|
127
111
|
# is journaled and replayed on retries. Returns a DurableFuture.
|
|
128
|
-
sig do
|
|
129
|
-
params(name: String, serde: T.untyped, retry_policy: T.nilable(RunRetryPolicy),
|
|
130
|
-
background: T::Boolean, action: T.proc.returns(T.untyped)).returns(DurableFuture)
|
|
131
|
-
end
|
|
132
112
|
def run(name, serde: JsonSerde, retry_policy: nil, background: false, &action)
|
|
133
113
|
fetch_context!.run(name, serde: serde, retry_policy: retry_policy, background: background, &action)
|
|
134
114
|
end
|
|
135
115
|
|
|
136
116
|
# Convenience shortcut for +run(...).await+. Returns the result directly.
|
|
137
|
-
sig do
|
|
138
|
-
params(name: String, serde: T.untyped, retry_policy: T.nilable(RunRetryPolicy),
|
|
139
|
-
background: T::Boolean, action: T.proc.returns(T.untyped)).returns(T.untyped)
|
|
140
|
-
end
|
|
141
117
|
def run_sync(name, serde: JsonSerde, retry_policy: nil, background: false, &action)
|
|
142
118
|
fetch_context!.run_sync(name, serde: serde, retry_policy: retry_policy, background: background, &action)
|
|
143
119
|
end
|
|
144
120
|
|
|
145
121
|
# Durable timer that survives handler restarts.
|
|
146
|
-
sig { params(seconds: Numeric).returns(DurableFuture) }
|
|
147
122
|
def sleep(seconds)
|
|
148
123
|
fetch_context!.sleep(seconds)
|
|
149
124
|
end
|
|
@@ -151,43 +126,36 @@ module Restate # rubocop:disable Metrics/ModuleLength
|
|
|
151
126
|
# ── State operations (VirtualObject / Workflow) ──
|
|
152
127
|
|
|
153
128
|
# Durably retrieve a state entry. Returns nil if unset.
|
|
154
|
-
sig { params(name: String, serde: T.untyped).returns(T.untyped) }
|
|
155
129
|
def get(name, serde: JsonSerde)
|
|
156
130
|
fetch_context!.get(name, serde: serde)
|
|
157
131
|
end
|
|
158
132
|
|
|
159
133
|
# Durably retrieve a state entry, returning a DurableFuture instead of blocking.
|
|
160
|
-
sig { params(name: String, serde: T.untyped).returns(DurableFuture) }
|
|
161
134
|
def get_async(name, serde: JsonSerde)
|
|
162
135
|
fetch_context!.get_async(name, serde: serde)
|
|
163
136
|
end
|
|
164
137
|
|
|
165
138
|
# Durably set a state entry.
|
|
166
|
-
sig { params(name: String, value: T.untyped, serde: T.untyped).void }
|
|
167
139
|
def set(name, value, serde: JsonSerde)
|
|
168
140
|
fetch_context!.set(name, value, serde: serde)
|
|
169
141
|
end
|
|
170
142
|
|
|
171
143
|
# Durably remove a single state entry.
|
|
172
|
-
sig { params(name: String).void }
|
|
173
144
|
def clear(name)
|
|
174
145
|
fetch_context!.clear(name)
|
|
175
146
|
end
|
|
176
147
|
|
|
177
148
|
# Durably remove all state entries.
|
|
178
|
-
sig { void }
|
|
179
149
|
def clear_all
|
|
180
150
|
fetch_context!.clear_all
|
|
181
151
|
end
|
|
182
152
|
|
|
183
153
|
# List all state entry names.
|
|
184
|
-
sig { returns(T.untyped) }
|
|
185
154
|
def state_keys
|
|
186
155
|
fetch_context!.state_keys
|
|
187
156
|
end
|
|
188
157
|
|
|
189
158
|
# List all state entry names, returning a DurableFuture.
|
|
190
|
-
sig { returns(DurableFuture) }
|
|
191
159
|
def state_keys_async
|
|
192
160
|
fetch_context!.state_keys_async
|
|
193
161
|
end
|
|
@@ -195,12 +163,6 @@ module Restate # rubocop:disable Metrics/ModuleLength
|
|
|
195
163
|
# ── Service communication ──
|
|
196
164
|
|
|
197
165
|
# Durably call a handler on a Restate service.
|
|
198
|
-
sig do
|
|
199
|
-
params(service: T.any(String, T::Class[T.anything]), handler: T.any(String, Symbol),
|
|
200
|
-
arg: T.untyped, key: T.nilable(String), idempotency_key: T.nilable(String),
|
|
201
|
-
headers: T.nilable(T::Hash[String, String]),
|
|
202
|
-
input_serde: T.untyped, output_serde: T.untyped).returns(DurableCallFuture)
|
|
203
|
-
end
|
|
204
166
|
def service_call(service, handler, arg, key: nil, idempotency_key: nil, headers: nil,
|
|
205
167
|
input_serde: NOT_SET, output_serde: NOT_SET)
|
|
206
168
|
ctx = fetch_context!
|
|
@@ -209,12 +171,6 @@ module Restate # rubocop:disable Metrics/ModuleLength
|
|
|
209
171
|
end
|
|
210
172
|
|
|
211
173
|
# Fire-and-forget send to a Restate service handler.
|
|
212
|
-
sig do
|
|
213
|
-
params(service: T.any(String, T::Class[T.anything]), handler: T.any(String, Symbol),
|
|
214
|
-
arg: T.untyped, key: T.nilable(String), delay: T.nilable(Numeric),
|
|
215
|
-
idempotency_key: T.nilable(String), headers: T.nilable(T::Hash[String, String]),
|
|
216
|
-
input_serde: T.untyped).returns(SendHandle)
|
|
217
|
-
end
|
|
218
174
|
def service_send(service, handler, arg, key: nil, delay: nil, idempotency_key: nil,
|
|
219
175
|
headers: nil, input_serde: NOT_SET)
|
|
220
176
|
ctx = fetch_context!
|
|
@@ -223,12 +179,6 @@ module Restate # rubocop:disable Metrics/ModuleLength
|
|
|
223
179
|
end
|
|
224
180
|
|
|
225
181
|
# Durably call a handler on a Restate virtual object.
|
|
226
|
-
sig do
|
|
227
|
-
params(service: T.any(String, T::Class[T.anything]), handler: T.any(String, Symbol),
|
|
228
|
-
key: String, arg: T.untyped, idempotency_key: T.nilable(String),
|
|
229
|
-
headers: T.nilable(T::Hash[String, String]),
|
|
230
|
-
input_serde: T.untyped, output_serde: T.untyped).returns(DurableCallFuture)
|
|
231
|
-
end
|
|
232
182
|
def object_call(service, handler, key, arg, idempotency_key: nil, headers: nil,
|
|
233
183
|
input_serde: NOT_SET, output_serde: NOT_SET)
|
|
234
184
|
ctx = fetch_context!
|
|
@@ -237,12 +187,6 @@ module Restate # rubocop:disable Metrics/ModuleLength
|
|
|
237
187
|
end
|
|
238
188
|
|
|
239
189
|
# Fire-and-forget send to a Restate virtual object handler.
|
|
240
|
-
sig do
|
|
241
|
-
params(service: T.any(String, T::Class[T.anything]), handler: T.any(String, Symbol),
|
|
242
|
-
key: String, arg: T.untyped, delay: T.nilable(Numeric),
|
|
243
|
-
idempotency_key: T.nilable(String), headers: T.nilable(T::Hash[String, String]),
|
|
244
|
-
input_serde: T.untyped).returns(SendHandle)
|
|
245
|
-
end
|
|
246
190
|
def object_send(service, handler, key, arg, delay: nil, idempotency_key: nil,
|
|
247
191
|
headers: nil, input_serde: NOT_SET)
|
|
248
192
|
ctx = fetch_context!
|
|
@@ -251,12 +195,6 @@ module Restate # rubocop:disable Metrics/ModuleLength
|
|
|
251
195
|
end
|
|
252
196
|
|
|
253
197
|
# Durably call a handler on a Restate workflow.
|
|
254
|
-
sig do
|
|
255
|
-
params(service: T.any(String, T::Class[T.anything]), handler: T.any(String, Symbol),
|
|
256
|
-
key: String, arg: T.untyped, idempotency_key: T.nilable(String),
|
|
257
|
-
headers: T.nilable(T::Hash[String, String]),
|
|
258
|
-
input_serde: T.untyped, output_serde: T.untyped).returns(DurableCallFuture)
|
|
259
|
-
end
|
|
260
198
|
def workflow_call(service, handler, key, arg, idempotency_key: nil, headers: nil,
|
|
261
199
|
input_serde: NOT_SET, output_serde: NOT_SET)
|
|
262
200
|
ctx = fetch_context!
|
|
@@ -266,12 +204,6 @@ module Restate # rubocop:disable Metrics/ModuleLength
|
|
|
266
204
|
end
|
|
267
205
|
|
|
268
206
|
# Fire-and-forget send to a Restate workflow handler.
|
|
269
|
-
sig do
|
|
270
|
-
params(service: T.any(String, T::Class[T.anything]), handler: T.any(String, Symbol),
|
|
271
|
-
key: String, arg: T.untyped, delay: T.nilable(Numeric),
|
|
272
|
-
idempotency_key: T.nilable(String), headers: T.nilable(T::Hash[String, String]),
|
|
273
|
-
input_serde: T.untyped).returns(SendHandle)
|
|
274
|
-
end
|
|
275
207
|
def workflow_send(service, handler, key, arg, delay: nil, idempotency_key: nil,
|
|
276
208
|
headers: nil, input_serde: NOT_SET)
|
|
277
209
|
ctx = fetch_context!
|
|
@@ -280,22 +212,12 @@ module Restate # rubocop:disable Metrics/ModuleLength
|
|
|
280
212
|
end
|
|
281
213
|
|
|
282
214
|
# Durably call a handler using raw bytes (no serialization).
|
|
283
|
-
sig do
|
|
284
|
-
params(service: String, handler: String, arg: String,
|
|
285
|
-
key: T.nilable(String), idempotency_key: T.nilable(String),
|
|
286
|
-
headers: T.nilable(T::Hash[String, String])).returns(DurableCallFuture)
|
|
287
|
-
end
|
|
288
215
|
def generic_call(service, handler, arg, key: nil, idempotency_key: nil, headers: nil)
|
|
289
216
|
fetch_context!.generic_call(service, handler, arg, key: key,
|
|
290
217
|
idempotency_key: idempotency_key, headers: headers)
|
|
291
218
|
end
|
|
292
219
|
|
|
293
220
|
# Fire-and-forget send using raw bytes (no serialization).
|
|
294
|
-
sig do
|
|
295
|
-
params(service: String, handler: String, arg: String,
|
|
296
|
-
key: T.nilable(String), delay: T.nilable(Numeric),
|
|
297
|
-
idempotency_key: T.nilable(String), headers: T.nilable(T::Hash[String, String])).returns(SendHandle)
|
|
298
|
-
end
|
|
299
221
|
def generic_send(service, handler, arg, key: nil, delay: nil, idempotency_key: nil, headers: nil)
|
|
300
222
|
fetch_context!.generic_send(service, handler, arg, key: key, delay: delay,
|
|
301
223
|
idempotency_key: idempotency_key, headers: headers)
|
|
@@ -304,19 +226,16 @@ module Restate # rubocop:disable Metrics/ModuleLength
|
|
|
304
226
|
# ── Awakeables ──
|
|
305
227
|
|
|
306
228
|
# Create an awakeable for external callbacks. Returns [awakeable_id, DurableFuture].
|
|
307
|
-
sig { params(serde: T.untyped).returns([String, DurableFuture]) }
|
|
308
229
|
def awakeable(serde: JsonSerde)
|
|
309
230
|
fetch_context!.awakeable(serde: serde)
|
|
310
231
|
end
|
|
311
232
|
|
|
312
233
|
# Resolve an awakeable with a success value.
|
|
313
|
-
sig { params(awakeable_id: String, payload: T.untyped, serde: T.untyped).void }
|
|
314
234
|
def resolve_awakeable(awakeable_id, payload, serde: JsonSerde)
|
|
315
235
|
fetch_context!.resolve_awakeable(awakeable_id, payload, serde: serde)
|
|
316
236
|
end
|
|
317
237
|
|
|
318
238
|
# Reject an awakeable with a terminal failure.
|
|
319
|
-
sig { params(awakeable_id: String, message: String, code: Integer).void }
|
|
320
239
|
def reject_awakeable(awakeable_id, message, code: 500)
|
|
321
240
|
fetch_context!.reject_awakeable(awakeable_id, message, code: code)
|
|
322
241
|
end
|
|
@@ -324,25 +243,21 @@ module Restate # rubocop:disable Metrics/ModuleLength
|
|
|
324
243
|
# ── Promises (Workflow only) ──
|
|
325
244
|
|
|
326
245
|
# Get a durable promise value, blocking until resolved.
|
|
327
|
-
sig { params(name: String, serde: T.untyped).returns(T.untyped) }
|
|
328
246
|
def promise(name, serde: JsonSerde)
|
|
329
247
|
fetch_context!.promise(name, serde: serde)
|
|
330
248
|
end
|
|
331
249
|
|
|
332
250
|
# Peek at a durable promise without blocking. Returns nil if not yet resolved.
|
|
333
|
-
sig { params(name: String, serde: T.untyped).returns(T.untyped) }
|
|
334
251
|
def peek_promise(name, serde: JsonSerde)
|
|
335
252
|
fetch_context!.peek_promise(name, serde: serde)
|
|
336
253
|
end
|
|
337
254
|
|
|
338
255
|
# Resolve a durable promise with a value.
|
|
339
|
-
sig { params(name: String, payload: T.untyped, serde: T.untyped).void }
|
|
340
256
|
def resolve_promise(name, payload, serde: JsonSerde)
|
|
341
257
|
fetch_context!.resolve_promise(name, payload, serde: serde)
|
|
342
258
|
end
|
|
343
259
|
|
|
344
260
|
# Reject a durable promise with a terminal failure.
|
|
345
|
-
sig { params(name: String, message: String, code: Integer).void }
|
|
346
261
|
def reject_promise(name, message, code: 500)
|
|
347
262
|
fetch_context!.reject_promise(name, message, code: code)
|
|
348
263
|
end
|
|
@@ -350,21 +265,18 @@ module Restate # rubocop:disable Metrics/ModuleLength
|
|
|
350
265
|
# ── Futures ──
|
|
351
266
|
|
|
352
267
|
# Wait until any of the given futures completes. Returns [completed, remaining].
|
|
353
|
-
sig { params(futures: T::Array[DurableFuture]).returns([T::Array[DurableFuture], T::Array[DurableFuture]]) }
|
|
354
268
|
def wait_any(*futures)
|
|
355
|
-
|
|
269
|
+
fetch_context!.wait_any(*futures)
|
|
356
270
|
end
|
|
357
271
|
|
|
358
272
|
# ── Request metadata ──
|
|
359
273
|
|
|
360
274
|
# Returns metadata about the current invocation (id, headers, raw body).
|
|
361
|
-
sig { returns(T.untyped) }
|
|
362
275
|
def request
|
|
363
276
|
fetch_context!.request
|
|
364
277
|
end
|
|
365
278
|
|
|
366
279
|
# Returns the key for this virtual object or workflow invocation.
|
|
367
|
-
sig { returns(String) }
|
|
368
280
|
def key
|
|
369
281
|
fetch_context!.key
|
|
370
282
|
end
|
|
@@ -372,7 +284,6 @@ module Restate # rubocop:disable Metrics/ModuleLength
|
|
|
372
284
|
# ── Invocation control ──
|
|
373
285
|
|
|
374
286
|
# Request cancellation of another invocation.
|
|
375
|
-
sig { params(invocation_id: String).void }
|
|
376
287
|
def cancel_invocation(invocation_id)
|
|
377
288
|
fetch_context!.cancel_invocation(invocation_id)
|
|
378
289
|
end
|
data/sig/restate.rbs
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# Public API type signatures for the Restate Ruby SDK.
|
|
2
|
+
# Used by Steep for static type checking and by Ruby LSP for IDE completion.
|
|
3
|
+
|
|
4
|
+
module Restate
|
|
5
|
+
# ── Endpoint ──
|
|
6
|
+
|
|
7
|
+
def self.endpoint: (*untyped services, ?protocol: String?, ?identity_keys: Array[String]?) -> Endpoint
|
|
8
|
+
def self.configure: () { (Config) -> void } -> void
|
|
9
|
+
def self.config: () -> Config
|
|
10
|
+
def self.client: () -> Client
|
|
11
|
+
|
|
12
|
+
# ── Durable execution ──
|
|
13
|
+
|
|
14
|
+
def self.run: (String name, ?serde: untyped, ?retry_policy: untyped, ?background: bool) { () -> untyped } -> DurableFuture
|
|
15
|
+
def self.run_sync: (String name, ?serde: untyped, ?retry_policy: untyped, ?background: bool) { () -> untyped } -> untyped
|
|
16
|
+
def self.sleep: (Numeric seconds) -> DurableFuture
|
|
17
|
+
|
|
18
|
+
# ── State operations ──
|
|
19
|
+
|
|
20
|
+
def self.get: (String name, ?serde: untyped) -> untyped
|
|
21
|
+
def self.get_async: (String name, ?serde: untyped) -> DurableFuture
|
|
22
|
+
def self.set: (String name, untyped value, ?serde: untyped) -> void
|
|
23
|
+
def self.clear: (String name) -> void
|
|
24
|
+
def self.clear_all: () -> void
|
|
25
|
+
def self.state_keys: () -> Array[String]
|
|
26
|
+
def self.state_keys_async: () -> DurableFuture
|
|
27
|
+
|
|
28
|
+
# ── Service communication ──
|
|
29
|
+
|
|
30
|
+
def self.service_call: (untyped service, (String | Symbol) handler, untyped arg, ?key: String?, ?idempotency_key: String?, ?headers: Hash[String, String]?, ?input_serde: untyped, ?output_serde: untyped) -> DurableCallFuture
|
|
31
|
+
def self.service_send: (untyped service, (String | Symbol) handler, untyped arg, ?key: String?, ?delay: Numeric?, ?idempotency_key: String?, ?headers: Hash[String, String]?, ?input_serde: untyped) -> SendHandle
|
|
32
|
+
def self.object_call: (untyped service, (String | Symbol) handler, String key, untyped arg, ?idempotency_key: String?, ?headers: Hash[String, String]?, ?input_serde: untyped, ?output_serde: untyped) -> DurableCallFuture
|
|
33
|
+
def self.object_send: (untyped service, (String | Symbol) handler, String key, untyped arg, ?delay: Numeric?, ?idempotency_key: String?, ?headers: Hash[String, String]?, ?input_serde: untyped) -> SendHandle
|
|
34
|
+
def self.workflow_call: (untyped service, (String | Symbol) handler, String key, untyped arg, ?idempotency_key: String?, ?headers: Hash[String, String]?, ?input_serde: untyped, ?output_serde: untyped) -> DurableCallFuture
|
|
35
|
+
def self.workflow_send: (untyped service, (String | Symbol) handler, String key, untyped arg, ?delay: Numeric?, ?idempotency_key: String?, ?headers: Hash[String, String]?, ?input_serde: untyped) -> SendHandle
|
|
36
|
+
def self.generic_call: (String service, String handler, String arg, ?key: String?, ?idempotency_key: String?, ?headers: Hash[String, String]?) -> DurableCallFuture
|
|
37
|
+
def self.generic_send: (String service, String handler, String arg, ?key: String?, ?delay: Numeric?, ?idempotency_key: String?, ?headers: Hash[String, String]?) -> SendHandle
|
|
38
|
+
|
|
39
|
+
# ── Awakeables ──
|
|
40
|
+
|
|
41
|
+
def self.awakeable: (?serde: untyped) -> [String, DurableFuture]
|
|
42
|
+
def self.resolve_awakeable: (String awakeable_id, untyped payload, ?serde: untyped) -> void
|
|
43
|
+
def self.reject_awakeable: (String awakeable_id, String message, ?code: Integer) -> void
|
|
44
|
+
|
|
45
|
+
# ── Promises ──
|
|
46
|
+
|
|
47
|
+
def self.promise: (String name, ?serde: untyped) -> untyped
|
|
48
|
+
def self.peek_promise: (String name, ?serde: untyped) -> untyped
|
|
49
|
+
def self.resolve_promise: (String name, untyped payload, ?serde: untyped) -> void
|
|
50
|
+
def self.reject_promise: (String name, String message, ?code: Integer) -> void
|
|
51
|
+
|
|
52
|
+
# ── Futures / Metadata / Control ──
|
|
53
|
+
|
|
54
|
+
def self.wait_any: (*DurableFuture futures) -> [Array[DurableFuture], Array[DurableFuture]]
|
|
55
|
+
def self.request: () -> untyped
|
|
56
|
+
def self.key: () -> String
|
|
57
|
+
def self.cancel_invocation: (String invocation_id) -> void
|
|
58
|
+
def self.fetch_context!: (?service_kind: String?, ?handler_kind: String?) -> untyped
|
|
59
|
+
|
|
60
|
+
# ── Errors ──
|
|
61
|
+
|
|
62
|
+
class TerminalError < StandardError
|
|
63
|
+
attr_reader status_code: Integer
|
|
64
|
+
def initialize: (?String message, ?status_code: Integer) -> void
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
class SuspendedError < StandardError
|
|
68
|
+
def initialize: () -> void
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
class InternalError < StandardError
|
|
72
|
+
def initialize: () -> void
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class DisconnectedError < StandardError
|
|
76
|
+
def initialize: () -> void
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# ── Futures ──
|
|
80
|
+
|
|
81
|
+
class DurableFuture
|
|
82
|
+
attr_reader handle: Integer
|
|
83
|
+
def initialize: (untyped ctx, Integer handle, ?serde: untyped) -> void
|
|
84
|
+
def await: () -> untyped
|
|
85
|
+
def completed?: () -> bool
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
class DurableCallFuture < DurableFuture
|
|
89
|
+
def initialize: (untyped ctx, Integer result_handle, Integer invocation_id_handle, output_serde: untyped) -> void
|
|
90
|
+
def invocation_id: () -> String
|
|
91
|
+
def cancel: () -> void
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
class SendHandle
|
|
95
|
+
def initialize: (untyped ctx, Integer invocation_id_handle) -> void
|
|
96
|
+
def invocation_id: () -> String
|
|
97
|
+
def cancel: () -> void
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# ── Config ──
|
|
101
|
+
|
|
102
|
+
class Config
|
|
103
|
+
attr_accessor ingress_url: String
|
|
104
|
+
attr_accessor admin_url: String
|
|
105
|
+
attr_accessor ingress_headers: Hash[String, String]
|
|
106
|
+
attr_accessor admin_headers: Hash[String, String]
|
|
107
|
+
def initialize: () -> void
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# ── Client ──
|
|
111
|
+
|
|
112
|
+
class Client
|
|
113
|
+
def initialize: (?ingress_url: String, ?admin_url: String, ?ingress_headers: Hash[String, String], ?admin_headers: Hash[String, String]) -> void
|
|
114
|
+
def service: (untyped service) -> ClientServiceProxy
|
|
115
|
+
def object: (untyped service, String key) -> ClientServiceProxy
|
|
116
|
+
def workflow: (untyped service, String key) -> ClientServiceProxy
|
|
117
|
+
def resolve_awakeable: (String awakeable_id, untyped payload) -> void
|
|
118
|
+
def reject_awakeable: (String awakeable_id, String message, ?code: Integer) -> void
|
|
119
|
+
def cancel_invocation: (String invocation_id) -> void
|
|
120
|
+
def kill_invocation: (String invocation_id) -> void
|
|
121
|
+
|
|
122
|
+
private
|
|
123
|
+
|
|
124
|
+
def resolve_name: (untyped service) -> String
|
|
125
|
+
def post_ingress: (String path, untyped body) -> untyped
|
|
126
|
+
def post_admin: (String path, untyped body) -> untyped
|
|
127
|
+
def parse_response: (Net::HTTPResponse response) -> untyped
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
class ClientServiceProxy
|
|
131
|
+
def initialize: (String base_url, String service_name, String? key, Hash[String, String] headers) -> void
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# ── Endpoint ──
|
|
135
|
+
|
|
136
|
+
class Endpoint
|
|
137
|
+
attr_reader services: Hash[String, untyped]
|
|
138
|
+
attr_reader identity_keys: Array[String]
|
|
139
|
+
attr_accessor protocol: String?
|
|
140
|
+
attr_reader middleware: Array[untyped]
|
|
141
|
+
attr_reader outbound_middleware: Array[untyped]
|
|
142
|
+
def initialize: () -> void
|
|
143
|
+
def bind: (*untyped svcs) -> self
|
|
144
|
+
def streaming_protocol: () -> self
|
|
145
|
+
def request_response_protocol: () -> self
|
|
146
|
+
def identity_key: (String key) -> self
|
|
147
|
+
def use: (untyped klass, *untyped args, **untyped kwargs) -> self
|
|
148
|
+
def use_outbound: (untyped klass, *untyped args, **untyped kwargs) -> self
|
|
149
|
+
def app: () -> untyped
|
|
150
|
+
|
|
151
|
+
private
|
|
152
|
+
|
|
153
|
+
def instantiate_middleware: (untyped klass, Array[untyped] args, Hash[Symbol, untyped] kwargs) -> untyped
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# ── Service proxies ──
|
|
157
|
+
|
|
158
|
+
class ServiceCallProxy
|
|
159
|
+
def initialize: (untyped service_class, ?key: String?, ?call_method: Symbol) -> void
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
class ServiceSendProxy
|
|
163
|
+
def initialize: (untyped service_class, ?key: String?, ?send_method: Symbol, ?delay: Numeric?) -> void
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# ── Service classes (declared but not checked — heavy metaprogramming) ──
|
|
167
|
+
|
|
168
|
+
class Service
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
class VirtualObject
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
class Workflow
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# ── Internal ──
|
|
178
|
+
|
|
179
|
+
class Server
|
|
180
|
+
def initialize: (untyped endpoint) -> void
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
module JsonSerde
|
|
184
|
+
def self.serialize: (untyped obj) -> String
|
|
185
|
+
def self.deserialize: (String? buf) -> untyped
|
|
186
|
+
def self.json_schema: () -> Hash[String, untyped]?
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
module BytesSerde
|
|
190
|
+
def self.serialize: (untyped obj) -> String
|
|
191
|
+
def self.deserialize: (String? buf) -> String?
|
|
192
|
+
def self.json_schema: () -> Hash[String, untyped]?
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
class AttemptFinishedEvent
|
|
196
|
+
def set?: () -> bool
|
|
197
|
+
def wait: () -> void
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
NOT_SET: untyped
|
|
201
|
+
RunRetryPolicy: untyped
|
|
202
|
+
end
|
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.
|
|
4
|
+
version: 0.9.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-03-
|
|
11
|
+
date: 2026-03-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: async
|
|
@@ -38,20 +38,6 @@ dependencies:
|
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: '2.0'
|
|
41
|
-
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: sorbet-runtime
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - ">="
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: '0'
|
|
48
|
-
type: :runtime
|
|
49
|
-
prerelease: false
|
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
-
requirements:
|
|
52
|
-
- - ">="
|
|
53
|
-
- !ruby/object:Gem::Version
|
|
54
|
-
version: '0'
|
|
55
41
|
description: Build resilient applications with distributed durable async/await using
|
|
56
42
|
Restate
|
|
57
43
|
email:
|
|
@@ -88,8 +74,7 @@ files:
|
|
|
88
74
|
- lib/restate/virtual_object.rb
|
|
89
75
|
- lib/restate/vm.rb
|
|
90
76
|
- lib/restate/workflow.rb
|
|
91
|
-
-
|
|
92
|
-
- rbi/restate-sdk.rbi
|
|
77
|
+
- sig/restate.rbs
|
|
93
78
|
homepage: https://github.com/restatedev/sdk-ruby
|
|
94
79
|
licenses:
|
|
95
80
|
- MIT
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
# typed: false
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
return unless defined?(Tapioca::Dsl::Compiler)
|
|
5
|
-
|
|
6
|
-
require 'restate'
|
|
7
|
-
|
|
8
|
-
module Tapioca
|
|
9
|
-
module Dsl
|
|
10
|
-
module Compilers
|
|
11
|
-
# Generates Sorbet sigs for Restate handler methods.
|
|
12
|
-
#
|
|
13
|
-
# Handlers take 0 or 1 parameters (the input). Context is implicit
|
|
14
|
-
# via +Restate.*+ module methods.
|
|
15
|
-
#
|
|
16
|
-
# Usage:
|
|
17
|
-
# bundle exec tapioca dsl
|
|
18
|
-
class Restate < Compiler
|
|
19
|
-
ConstantType = type_member { { fixed: Module } }
|
|
20
|
-
|
|
21
|
-
class << self
|
|
22
|
-
def gather_constants
|
|
23
|
-
# Load service files so they're visible to all_classes.
|
|
24
|
-
# In non-Rails apps, Tapioca doesn't auto-load application code.
|
|
25
|
-
load_service_files
|
|
26
|
-
|
|
27
|
-
all_classes.select do |klass|
|
|
28
|
-
klass.is_a?(Class) && (
|
|
29
|
-
klass < ::Restate::Service ||
|
|
30
|
-
klass < ::Restate::VirtualObject ||
|
|
31
|
-
klass < ::Restate::Workflow
|
|
32
|
-
)
|
|
33
|
-
rescue TypeError
|
|
34
|
-
false
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
private
|
|
39
|
-
|
|
40
|
-
def load_service_files # rubocop:disable Metrics/MethodLength
|
|
41
|
-
root = Bundler.root.to_s
|
|
42
|
-
patterns = [
|
|
43
|
-
"#{root}/*.rb",
|
|
44
|
-
"#{root}/app/**/*.rb",
|
|
45
|
-
"#{root}/services/**/*.rb",
|
|
46
|
-
"#{root}/examples/**/*.rb"
|
|
47
|
-
]
|
|
48
|
-
Dir.glob(patterns).each do |file|
|
|
49
|
-
next if file.end_with?('config.ru', 'Rakefile')
|
|
50
|
-
|
|
51
|
-
require file
|
|
52
|
-
rescue LoadError, StandardError
|
|
53
|
-
nil # skip files that can't be loaded
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def decorate # rubocop:disable Metrics/MethodLength
|
|
59
|
-
root.create_path(constant) do |klass|
|
|
60
|
-
constant.handlers.each do |name, handler|
|
|
61
|
-
params = []
|
|
62
|
-
if handler.arity == 1
|
|
63
|
-
input_type = resolve_input_type(handler)
|
|
64
|
-
params << create_param('input', type: input_type)
|
|
65
|
-
end
|
|
66
|
-
output_type = resolve_output_type(handler)
|
|
67
|
-
klass.create_method(name, parameters: params, return_type: output_type)
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
private
|
|
73
|
-
|
|
74
|
-
# Maps (service kind, handler kind) to the correct context module.
|
|
75
|
-
def resolve_context_type(klass, handler)
|
|
76
|
-
if klass < ::Restate::Workflow
|
|
77
|
-
handler.kind == 'workflow' ? 'Restate::WorkflowContext' : 'Restate::WorkflowSharedContext'
|
|
78
|
-
elsif klass < ::Restate::VirtualObject
|
|
79
|
-
handler.kind == 'shared' ? 'Restate::ObjectSharedContext' : 'Restate::ObjectContext'
|
|
80
|
-
else
|
|
81
|
-
'Restate::Context'
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
# Resolves the Sorbet type string for the handler's input serde.
|
|
86
|
-
def resolve_input_type(handler)
|
|
87
|
-
type_class = handler.handler_io&.input_serde
|
|
88
|
-
sorbet_type_name(type_class) || 'T.untyped'
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
# Resolves the Sorbet type string for the handler's output serde.
|
|
92
|
-
def resolve_output_type(handler)
|
|
93
|
-
type_class = handler.handler_io&.output_serde
|
|
94
|
-
sorbet_type_name(type_class) || 'T.untyped'
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
# Returns a Sorbet type string if the serde wraps a known type, nil otherwise.
|
|
98
|
-
def sorbet_type_name(serde)
|
|
99
|
-
return nil if serde.nil?
|
|
100
|
-
|
|
101
|
-
# TStructSerde exposes .struct_class (T::Struct subclasses are visible to Sorbet)
|
|
102
|
-
return serde.struct_class.name if serde.is_a?(::Restate::TStructSerde)
|
|
103
|
-
|
|
104
|
-
# TypeSerde wraps a primitive type in .type_class
|
|
105
|
-
if serde.respond_to?(:type_class)
|
|
106
|
-
name = serde.type_class.name
|
|
107
|
-
return name if %w[String Integer Float].include?(name)
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
nil
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
end
|