@restatedev/restate-sdk-gen 0.0.0 → 1.14.3
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.
- package/LICENSE +21 -0
- package/README.md +115 -0
- package/dist/chunk-Bp6m_JJh.js +13 -0
- package/dist/index.cjs +1558 -0
- package/dist/index.d.cts +1022 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +1020 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1475 -0
- package/dist/index.js.map +1 -0
- package/package.json +56 -3
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1558 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (all$1) => {
|
|
9
|
+
let target = {};
|
|
10
|
+
for (var name in all$1) __defProp(target, name, {
|
|
11
|
+
get: all$1[name],
|
|
12
|
+
enumerable: true
|
|
13
|
+
});
|
|
14
|
+
return target;
|
|
15
|
+
};
|
|
16
|
+
var __copyProps = (to, from, except, desc) => {
|
|
17
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
18
|
+
key = keys[i];
|
|
19
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
20
|
+
get: ((k) => from[k]).bind(null, key),
|
|
21
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
27
|
+
value: mod,
|
|
28
|
+
enumerable: true
|
|
29
|
+
}) : target, mod));
|
|
30
|
+
|
|
31
|
+
//#endregion
|
|
32
|
+
let __restatedev_restate_sdk = require("@restatedev/restate-sdk");
|
|
33
|
+
__restatedev_restate_sdk = __toESM(__restatedev_restate_sdk);
|
|
34
|
+
let __restatedev_restate_sdk_clients = require("@restatedev/restate-sdk-clients");
|
|
35
|
+
__restatedev_restate_sdk_clients = __toESM(__restatedev_restate_sdk_clients);
|
|
36
|
+
let __restatedev_restate_sdk_core = require("@restatedev/restate-sdk-core");
|
|
37
|
+
__restatedev_restate_sdk_core = __toESM(__restatedev_restate_sdk_core);
|
|
38
|
+
|
|
39
|
+
//#region src/current.ts
|
|
40
|
+
let CURRENT = null;
|
|
41
|
+
/**
|
|
42
|
+
* Save the previous slot, install `value` as the current. Returns the
|
|
43
|
+
* previous value so `clearCurrent` can restore it (supports nested
|
|
44
|
+
* scheduler invocations on the same thread, even though we don't expect
|
|
45
|
+
* any in production).
|
|
46
|
+
*/
|
|
47
|
+
function setCurrent(value) {
|
|
48
|
+
const prev = CURRENT;
|
|
49
|
+
CURRENT = value;
|
|
50
|
+
return prev;
|
|
51
|
+
}
|
|
52
|
+
/** Restore a slot previously captured by `setCurrent`. */
|
|
53
|
+
function clearCurrent(prev) {
|
|
54
|
+
CURRENT = prev;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Read the slot, throwing if no fiber is currently advancing. Callers
|
|
58
|
+
* cast the return type — the slot is intentionally `unknown` here so
|
|
59
|
+
* this module stays free of cycles.
|
|
60
|
+
*/
|
|
61
|
+
function getCurrent() {
|
|
62
|
+
if (CURRENT === null) throw new Error("@restatedev/restate-sdk-gen: free-standing API called outside an active fiber. Call from inside `execute(ctx, gen(function*() { ... }))`.");
|
|
63
|
+
return CURRENT;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/invocation-reference.ts
|
|
68
|
+
var InvocationReferenceImpl = class {
|
|
69
|
+
constructor(id, _outputSerde) {
|
|
70
|
+
this.id = id;
|
|
71
|
+
this._outputSerde = _outputSerde;
|
|
72
|
+
}
|
|
73
|
+
attach(serde$1) {
|
|
74
|
+
return getCurrent().attach(this.id, serde$1 ?? this._outputSerde);
|
|
75
|
+
}
|
|
76
|
+
signal(name, serde$1) {
|
|
77
|
+
return getCurrent().invocationSignal(this.id, name, serde$1);
|
|
78
|
+
}
|
|
79
|
+
cancel() {
|
|
80
|
+
getCurrent().cancel(this.id);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/free.ts
|
|
86
|
+
/**
|
|
87
|
+
* Read the active `RestateOperations` from the current-fiber slot.
|
|
88
|
+
* Throws if called outside a fiber's synchronous advance span (e.g.,
|
|
89
|
+
* at module init or inside an `ops.run` async closure that resolved
|
|
90
|
+
* after the fiber returned).
|
|
91
|
+
*/
|
|
92
|
+
function currentOps() {
|
|
93
|
+
return getCurrent();
|
|
94
|
+
}
|
|
95
|
+
const rand = () => currentOps().rand;
|
|
96
|
+
const date = () => currentOps().date;
|
|
97
|
+
const logger = () => currentOps().console;
|
|
98
|
+
/**
|
|
99
|
+
* Returns the current invocation's request metadata plus the optional
|
|
100
|
+
* virtual-object / workflow key. The `key` field is only present when
|
|
101
|
+
* the handler belongs to an object or workflow.
|
|
102
|
+
*/
|
|
103
|
+
const handlerRequest = () => currentOps().handlerRequest();
|
|
104
|
+
/**
|
|
105
|
+
* Run a side-effecting closure as a journal entry. See
|
|
106
|
+
* `RestateOperations.run` for full semantics.
|
|
107
|
+
*
|
|
108
|
+
* `name` is derived from `action.name` (works for named functions and
|
|
109
|
+
* arrow functions assigned to a `const`) or specified explicitly via
|
|
110
|
+
* `opts.name`. If neither resolves, throws.
|
|
111
|
+
*/
|
|
112
|
+
const run = (action, opts) => currentOps().run(action, opts);
|
|
113
|
+
const sleep = (duration, name) => currentOps().sleep(duration, name);
|
|
114
|
+
const awakeable = (serde$1) => currentOps().awakeable(serde$1);
|
|
115
|
+
const resolveAwakeable = (id, payload, serde$1) => currentOps().resolveAwakeable(id, payload, serde$1);
|
|
116
|
+
const rejectAwakeable = (id, reason) => currentOps().rejectAwakeable(id, reason);
|
|
117
|
+
const signal = (name, serde$1) => currentOps().signal(name, serde$1);
|
|
118
|
+
const attach = (invocationId, serde$1) => currentOps().attach(invocationId, serde$1);
|
|
119
|
+
function client(def, key) {
|
|
120
|
+
return currentOps().client(def, key);
|
|
121
|
+
}
|
|
122
|
+
function sendClient(def, key) {
|
|
123
|
+
return currentOps().sendClient(def, key);
|
|
124
|
+
}
|
|
125
|
+
const call = (c) => currentOps().call(c);
|
|
126
|
+
const send = (c, outputSerde) => currentOps().send(c, outputSerde);
|
|
127
|
+
/**
|
|
128
|
+
* Create an InvocationReference from a known invocation ID (e.g. retrieved from state).
|
|
129
|
+
* `attach()` and `cancel()` on the result use the current fiber slot like all free functions.
|
|
130
|
+
*/
|
|
131
|
+
function invocation(id, opts) {
|
|
132
|
+
return new InvocationReferenceImpl(id, opts?.outputSerde);
|
|
133
|
+
}
|
|
134
|
+
const cancel = (invocationId) => currentOps().cancel(invocationId);
|
|
135
|
+
const channel = () => currentOps().channel();
|
|
136
|
+
const state = () => currentOps().state();
|
|
137
|
+
const sharedState = () => currentOps().sharedState();
|
|
138
|
+
const workflowPromise = (name, serde$1) => currentOps().workflowPromise(name, serde$1);
|
|
139
|
+
/**
|
|
140
|
+
* Wait for every future to settle; return their values in input order.
|
|
141
|
+
* Heterogeneous-tuple typing — `all([fA, fB])` where `fA: Future<A>`
|
|
142
|
+
* and `fB: Future<B>` yields `Future<[A, B]>`. Mirrors `Promise.all`.
|
|
143
|
+
*/
|
|
144
|
+
const all = (futures) => currentOps().all(futures);
|
|
145
|
+
/**
|
|
146
|
+
* Return the first future to settle; losers continue running but their
|
|
147
|
+
* results are discarded. Heterogeneous-tuple typing — `race([fA, fB])`
|
|
148
|
+
* yields `Future<A | B>`. Mirrors `Promise.race`.
|
|
149
|
+
*/
|
|
150
|
+
const race = (futures) => currentOps().race(futures);
|
|
151
|
+
/**
|
|
152
|
+
* First-to-succeed wins (non-rejected). Rejects with `AggregateError(errors)`
|
|
153
|
+
* when every input rejects (including the empty input case). Tuple-aware —
|
|
154
|
+
* `any([fA, fB])` yields `Future<A | B>`. Mirrors `Promise.any`.
|
|
155
|
+
*/
|
|
156
|
+
const any = (futures) => currentOps().any(futures);
|
|
157
|
+
/**
|
|
158
|
+
* Wait for every future to settle; never rejects. Tuple-aware —
|
|
159
|
+
* `allSettled([fA, fB])` yields
|
|
160
|
+
* `Future<[FutureSettledResult<A>, FutureSettledResult<B>]>`.
|
|
161
|
+
* Mirrors `Promise.allSettled`.
|
|
162
|
+
*/
|
|
163
|
+
const allSettled = (futures) => currentOps().allSettled(futures);
|
|
164
|
+
/**
|
|
165
|
+
* Register `op` as a fresh routine and return a `Future<T>` for its
|
|
166
|
+
* eventual outcome. Eager — the child is already in flight by the time
|
|
167
|
+
* `spawn` returns. See `RestateOperations.spawn` for full semantics.
|
|
168
|
+
*/
|
|
169
|
+
const spawn = (op) => currentOps().spawn(op);
|
|
170
|
+
|
|
171
|
+
//#endregion
|
|
172
|
+
//#region src/operation.ts
|
|
173
|
+
const opTag = Symbol("restateOperation");
|
|
174
|
+
function makePrimitive(node) {
|
|
175
|
+
const op = {
|
|
176
|
+
[opTag]: node,
|
|
177
|
+
*[Symbol.iterator]() {
|
|
178
|
+
return yield op;
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
return op;
|
|
182
|
+
}
|
|
183
|
+
function awaitRace(futures) {
|
|
184
|
+
const op = {
|
|
185
|
+
[opTag]: {
|
|
186
|
+
_tag: "AwaitRace",
|
|
187
|
+
futures
|
|
188
|
+
},
|
|
189
|
+
*[Symbol.iterator]() {
|
|
190
|
+
return yield op;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
return op;
|
|
194
|
+
}
|
|
195
|
+
function gen(body) {
|
|
196
|
+
return { [Symbol.iterator]: body };
|
|
197
|
+
}
|
|
198
|
+
function* select(branches) {
|
|
199
|
+
const tags = Object.keys(branches);
|
|
200
|
+
const tag = tags[(yield* awaitRace(tags.map((t) => branches[t]))).index];
|
|
201
|
+
return {
|
|
202
|
+
tag,
|
|
203
|
+
future: branches[tag]
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
//#endregion
|
|
208
|
+
//#region src/future.ts
|
|
209
|
+
const futureBacking = Symbol("restateFutureBacking");
|
|
210
|
+
/**
|
|
211
|
+
* Internal accessor — read the backing off a Future. Only the scheduler
|
|
212
|
+
* and fiber call this; user code never sees Backing.
|
|
213
|
+
*/
|
|
214
|
+
function getBacking(f) {
|
|
215
|
+
return f[futureBacking];
|
|
216
|
+
}
|
|
217
|
+
function makeFuture(backing) {
|
|
218
|
+
const future = {
|
|
219
|
+
[futureBacking]: backing,
|
|
220
|
+
*[Symbol.iterator]() {
|
|
221
|
+
return yield leafOp;
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
const leafOp = makePrimitive({
|
|
225
|
+
_tag: "Leaf",
|
|
226
|
+
future
|
|
227
|
+
});
|
|
228
|
+
return future;
|
|
229
|
+
}
|
|
230
|
+
function isJournalBacked(f) {
|
|
231
|
+
return getBacking(f).kind === "journal";
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
//#endregion
|
|
235
|
+
//#region src/fiber.ts
|
|
236
|
+
var Fiber = class {
|
|
237
|
+
it;
|
|
238
|
+
sched;
|
|
239
|
+
state = {
|
|
240
|
+
kind: "ready",
|
|
241
|
+
resume: null
|
|
242
|
+
};
|
|
243
|
+
waiters = [];
|
|
244
|
+
constructor(op, sched) {
|
|
245
|
+
this.it = op[Symbol.iterator]();
|
|
246
|
+
this.sched = sched;
|
|
247
|
+
}
|
|
248
|
+
isDone() {
|
|
249
|
+
return this.state.kind === "done";
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* For a fiber known to be done, return its settled outcome. Throws
|
|
253
|
+
* if called on a non-done fiber — callers must check `isDone()` first
|
|
254
|
+
* (or use `awaitCompletion` for the polymorphic version).
|
|
255
|
+
*/
|
|
256
|
+
settledValue() {
|
|
257
|
+
if (this.state.kind !== "done") throw new Error("Fiber.settledValue called on non-done fiber");
|
|
258
|
+
return this.state.settled;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Returns the parked sources this fiber is currently racing against.
|
|
262
|
+
* Empty if the fiber is parked only on routine waiters (e.g.,
|
|
263
|
+
* waiting on a sibling fiber to finish), or in any non-parked state.
|
|
264
|
+
* The scheduler reads these to assemble its main-loop race.
|
|
265
|
+
*/
|
|
266
|
+
parkedSources() {
|
|
267
|
+
return this.state.kind === "parked" ? this.state.promises : [];
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* "I want to be notified when this fiber is done." If the fiber is
|
|
271
|
+
* already done, returns its settled outcome immediately (caller
|
|
272
|
+
* should NOT also expect the waiter to be invoked). Otherwise
|
|
273
|
+
* returns null and queues the waiter for invocation when the fiber
|
|
274
|
+
* eventually finishes.
|
|
275
|
+
*/
|
|
276
|
+
awaitCompletion(waiter) {
|
|
277
|
+
if (this.state.kind === "done") return this.state.settled;
|
|
278
|
+
this.waiters.push(waiter);
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Wake this fiber with a resume value. Transitions to ready and
|
|
283
|
+
* notifies the scheduler. May be called from any state except done
|
|
284
|
+
* (waking a done fiber is a programming error and is ignored
|
|
285
|
+
* defensively).
|
|
286
|
+
*/
|
|
287
|
+
wake(resume) {
|
|
288
|
+
if (this.state.kind === "done") return;
|
|
289
|
+
this.state = {
|
|
290
|
+
kind: "ready",
|
|
291
|
+
resume
|
|
292
|
+
};
|
|
293
|
+
this.sched.markReady(this);
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Drive the fiber's iterator until it parks (yields a primitive
|
|
297
|
+
* whose dispatch ends with the fiber waiting on a source) or
|
|
298
|
+
* finishes (returns or throws).
|
|
299
|
+
*
|
|
300
|
+
* No-op if the fiber is not in the ready state — protects against
|
|
301
|
+
* stale entries in the scheduler's ready queue.
|
|
302
|
+
*/
|
|
303
|
+
advance() {
|
|
304
|
+
if (this.state.kind !== "ready") return;
|
|
305
|
+
const prevSlot = setCurrent(this.sched.contextSlot);
|
|
306
|
+
try {
|
|
307
|
+
let resume = this.state.resume;
|
|
308
|
+
while (true) {
|
|
309
|
+
let next;
|
|
310
|
+
try {
|
|
311
|
+
next = stepIterator(this.it, resume);
|
|
312
|
+
} catch (e) {
|
|
313
|
+
this.finish({
|
|
314
|
+
ok: false,
|
|
315
|
+
e
|
|
316
|
+
});
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
if (next.done) {
|
|
320
|
+
this.finish({
|
|
321
|
+
ok: true,
|
|
322
|
+
v: next.value
|
|
323
|
+
});
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
const node = next.value[opTag];
|
|
327
|
+
let outcome;
|
|
328
|
+
switch (node._tag) {
|
|
329
|
+
case "Leaf":
|
|
330
|
+
outcome = this.parkOnLeaf(node);
|
|
331
|
+
break;
|
|
332
|
+
case "AwaitRace":
|
|
333
|
+
outcome = this.parkOnAwaitRace(node.futures);
|
|
334
|
+
break;
|
|
335
|
+
}
|
|
336
|
+
if (outcome === null) return;
|
|
337
|
+
resume = outcome;
|
|
338
|
+
}
|
|
339
|
+
} finally {
|
|
340
|
+
clearCurrent(prevSlot);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Park on a single Future, or short-circuit if the Future is already
|
|
345
|
+
* settled. Returns the Settled value to feed back into the iterator
|
|
346
|
+
* if a short-circuit is possible (routine-backed future whose target
|
|
347
|
+
* already finished); returns null if the fiber is parked and the
|
|
348
|
+
* caller should suspend.
|
|
349
|
+
*/
|
|
350
|
+
parkOnLeaf(leaf) {
|
|
351
|
+
const backing = getBacking(leaf.future);
|
|
352
|
+
if (backing.kind === "journal") {
|
|
353
|
+
this.state = {
|
|
354
|
+
kind: "parked",
|
|
355
|
+
promises: [{
|
|
356
|
+
promise: backing.promise,
|
|
357
|
+
fire: (s) => this.wake(s)
|
|
358
|
+
}]
|
|
359
|
+
};
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
const settled = backing.target.awaitCompletion((s) => this.wake(s));
|
|
363
|
+
if (settled !== null) return settled;
|
|
364
|
+
this.state = {
|
|
365
|
+
kind: "parked",
|
|
366
|
+
promises: []
|
|
367
|
+
};
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Park on the first-to-settle of a list of Futures, or short-circuit
|
|
372
|
+
* if any source is already settled. Returns `{index, settled}`
|
|
373
|
+
* (wrapped as Settled) on short-circuit, or null if parked.
|
|
374
|
+
*
|
|
375
|
+
* On the parked path, every source registers a one-shot fire
|
|
376
|
+
* callback that wakes the fiber with `{index, settled}`. The `won`
|
|
377
|
+
* flag guards against duplicate wakes when multiple sources settle
|
|
378
|
+
* in the same tick. Local sources (fibers, channels) park on the
|
|
379
|
+
* target's waiter list; journal sources race in the main loop's
|
|
380
|
+
* race promise.
|
|
381
|
+
*/
|
|
382
|
+
parkOnAwaitRace(futures) {
|
|
383
|
+
for (let i = 0; i < futures.length; i++) {
|
|
384
|
+
const b = getBacking(futures[i]);
|
|
385
|
+
if (b.kind === "local" && b.target.isDone()) return {
|
|
386
|
+
ok: true,
|
|
387
|
+
v: {
|
|
388
|
+
index: i,
|
|
389
|
+
settled: b.target.settledValue()
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
let won = false;
|
|
394
|
+
const promises = [];
|
|
395
|
+
for (let i = 0; i < futures.length; i++) {
|
|
396
|
+
const idx = i;
|
|
397
|
+
const b = getBacking(futures[i]);
|
|
398
|
+
const fireOnce = (settled) => {
|
|
399
|
+
if (won) return;
|
|
400
|
+
won = true;
|
|
401
|
+
this.wake({
|
|
402
|
+
ok: true,
|
|
403
|
+
v: {
|
|
404
|
+
index: idx,
|
|
405
|
+
settled
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
};
|
|
409
|
+
if (b.kind === "local") b.target.awaitCompletion(fireOnce);
|
|
410
|
+
else promises.push({
|
|
411
|
+
promise: b.promise,
|
|
412
|
+
fire: fireOnce
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
this.state = {
|
|
416
|
+
kind: "parked",
|
|
417
|
+
promises
|
|
418
|
+
};
|
|
419
|
+
return null;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Iterator finished or threw. Transition to done, fire all waiters
|
|
423
|
+
* with the settled outcome, notify scheduler.
|
|
424
|
+
*/
|
|
425
|
+
finish(settled) {
|
|
426
|
+
this.state = {
|
|
427
|
+
kind: "done",
|
|
428
|
+
settled
|
|
429
|
+
};
|
|
430
|
+
const waiters = this.waiters;
|
|
431
|
+
this.waiters = [];
|
|
432
|
+
for (const w of waiters) w(settled);
|
|
433
|
+
this.sched.markDone(this);
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
/**
|
|
437
|
+
* Drive a generator iterator one step, feeding it whatever value or
|
|
438
|
+
* exception the caller is resuming with. `resume === null` is the
|
|
439
|
+
* very first step; `{ok: true, v}` resumes with a value; `{ok: false,
|
|
440
|
+
* e}` throws into the iterator (or, if the iterator has no `throw`
|
|
441
|
+
* method, rethrows so the fiber fails).
|
|
442
|
+
*/
|
|
443
|
+
function stepIterator(it, resume) {
|
|
444
|
+
if (resume === null) return it.next(void 0);
|
|
445
|
+
if (resume.ok) return it.next(resume.v);
|
|
446
|
+
if (it.throw) return it.throw(resume.e);
|
|
447
|
+
throw resume.e;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
//#endregion
|
|
451
|
+
//#region src/channel.ts
|
|
452
|
+
var ChannelImpl = class {
|
|
453
|
+
state = { kind: "pending" };
|
|
454
|
+
waiters = [];
|
|
455
|
+
fire(value) {
|
|
456
|
+
if (this.state.kind === "settled") return;
|
|
457
|
+
this.state = {
|
|
458
|
+
kind: "settled",
|
|
459
|
+
value
|
|
460
|
+
};
|
|
461
|
+
const ws = this.waiters;
|
|
462
|
+
this.waiters = [];
|
|
463
|
+
const settled = {
|
|
464
|
+
ok: true,
|
|
465
|
+
v: value
|
|
466
|
+
};
|
|
467
|
+
for (const w of ws) w(settled);
|
|
468
|
+
}
|
|
469
|
+
isDone() {
|
|
470
|
+
return this.state.kind === "settled";
|
|
471
|
+
}
|
|
472
|
+
settledValue() {
|
|
473
|
+
if (this.state.kind !== "settled") throw new Error("ChannelImpl.settledValue called on a pending channel");
|
|
474
|
+
return {
|
|
475
|
+
ok: true,
|
|
476
|
+
v: this.state.value
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
awaitCompletion(waiter) {
|
|
480
|
+
if (this.state.kind === "settled") return {
|
|
481
|
+
ok: true,
|
|
482
|
+
v: this.state.value
|
|
483
|
+
};
|
|
484
|
+
this.waiters.push(waiter);
|
|
485
|
+
return null;
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
function makeChannel() {
|
|
489
|
+
const impl = new ChannelImpl();
|
|
490
|
+
return {
|
|
491
|
+
send: (v) => gen(function* () {
|
|
492
|
+
impl.fire(v);
|
|
493
|
+
}),
|
|
494
|
+
receive: makeFuture({
|
|
495
|
+
kind: "local",
|
|
496
|
+
target: impl
|
|
497
|
+
})
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
//#endregion
|
|
502
|
+
//#region src/scheduler.ts
|
|
503
|
+
var Scheduler = class {
|
|
504
|
+
fibers = /* @__PURE__ */ new Set();
|
|
505
|
+
ready = [];
|
|
506
|
+
lib;
|
|
507
|
+
abortController = new AbortController();
|
|
508
|
+
/**
|
|
509
|
+
* Slot for the operations object bound to this scheduler. Defaults
|
|
510
|
+
* to the scheduler itself so tests (which don't construct a
|
|
511
|
+
* `RestateOperations`) still get a slot exposing `.spawn`. Production
|
|
512
|
+
* `execute()` overrides this with a `RestateOperations` instance
|
|
513
|
+
* after construction (we can't pass it into the ctor because
|
|
514
|
+
* `RestateOperations` needs the scheduler to construct itself).
|
|
515
|
+
* `Fiber.advance` publishes this to the module-level current-fiber
|
|
516
|
+
* slot read by free-standing API functions. Typed `unknown` here to
|
|
517
|
+
* keep this module independent of `restate-operations.ts`.
|
|
518
|
+
*/
|
|
519
|
+
contextSlot;
|
|
520
|
+
constructor(lib) {
|
|
521
|
+
this.lib = lib;
|
|
522
|
+
this.contextSlot = this;
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* The scheduler's current AbortSignal. Aborts when invocation
|
|
526
|
+
* cancellation is observed (the SDK rejects the main race promise
|
|
527
|
+
* with TerminalError). After cancellation has been delivered to
|
|
528
|
+
* fibers and the scheduler has resumed, this getter returns a
|
|
529
|
+
* *fresh* signal — one that is not aborted, even though the previous
|
|
530
|
+
* cancel was just delivered.
|
|
531
|
+
*
|
|
532
|
+
* Pass `signal` to AbortSignal-aware APIs in `ops.run` closures
|
|
533
|
+
* (e.g. `fetch(url, {signal})`) so they cancel promptly when the
|
|
534
|
+
* surrounding work is cancelled. Cleanup closures yielded after a
|
|
535
|
+
* caught CancelledError get a fresh, unaborted signal — so they can
|
|
536
|
+
* do real work and only abort if a *new* cancellation arrives.
|
|
537
|
+
*/
|
|
538
|
+
get abortSignal() {
|
|
539
|
+
return this.abortController.signal;
|
|
540
|
+
}
|
|
541
|
+
markReady(f) {
|
|
542
|
+
this.ready.push(f);
|
|
543
|
+
}
|
|
544
|
+
markDone(f) {
|
|
545
|
+
this.fibers.delete(f);
|
|
546
|
+
}
|
|
547
|
+
createFiber(op) {
|
|
548
|
+
const f = new Fiber(op, this);
|
|
549
|
+
this.fibers.add(f);
|
|
550
|
+
this.ready.push(f);
|
|
551
|
+
return f;
|
|
552
|
+
}
|
|
553
|
+
makeJournalFuture(promise) {
|
|
554
|
+
return makeFuture({
|
|
555
|
+
kind: "journal",
|
|
556
|
+
promise
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Register an Operation as a fresh fiber and return a Future that
|
|
561
|
+
* resolves with its eventual value. Eager: by the time this returns,
|
|
562
|
+
* the fiber is queued ready and will be advanced on the next drain.
|
|
563
|
+
*
|
|
564
|
+
* Used by combinator fallbacks (race, allSettled, …), by
|
|
565
|
+
* `RestateOperations.spawn`, and via the slot interface by the free
|
|
566
|
+
* `spawn` function.
|
|
567
|
+
*/
|
|
568
|
+
spawn(op) {
|
|
569
|
+
return makeFuture({
|
|
570
|
+
kind: "local",
|
|
571
|
+
target: this.createFiber(op)
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Construct a single-shot in-memory channel. Send must be called from
|
|
576
|
+
* a fiber currently advancing under this scheduler. See `channel.ts`
|
|
577
|
+
* for full semantics.
|
|
578
|
+
*/
|
|
579
|
+
makeChannel() {
|
|
580
|
+
return makeChannel();
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Combinator over Futures. Fast path when every input is journal-
|
|
584
|
+
* backed: use the lib's all/race for a single combinator entry.
|
|
585
|
+
* Otherwise, fall back to a synthesized fiber that yields each in
|
|
586
|
+
* turn.
|
|
587
|
+
*
|
|
588
|
+
* Tuple-aware typing (mirrors `Promise.all` in the standard lib):
|
|
589
|
+
* `all([fA, fB])` where `fA: Future<A>` and `fB: Future<B>`
|
|
590
|
+
* yields `Future<[A, B]>`, not `Future<(A | B)[]>`. The `const T`
|
|
591
|
+
* lets TS infer a tuple from a literal array.
|
|
592
|
+
*/
|
|
593
|
+
all(futures) {
|
|
594
|
+
const fs = futures;
|
|
595
|
+
if (fs.every(isJournalBacked)) {
|
|
596
|
+
const promises = fs.map((f) => f[futureBacking].promise);
|
|
597
|
+
return this.makeJournalFuture(this.lib.all(promises));
|
|
598
|
+
}
|
|
599
|
+
return this.spawn(gen(function* () {
|
|
600
|
+
const out = new Array(fs.length);
|
|
601
|
+
for (let i = 0; i < fs.length; i++) out[i] = yield* fs[i];
|
|
602
|
+
return out;
|
|
603
|
+
}));
|
|
604
|
+
}
|
|
605
|
+
race(futures) {
|
|
606
|
+
const fs = futures;
|
|
607
|
+
return this.spawn(gen(function* () {
|
|
608
|
+
const result = yield* awaitRace(fs);
|
|
609
|
+
if (result.settled.ok) return result.settled.v;
|
|
610
|
+
throw result.settled.e;
|
|
611
|
+
}));
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* First-success combinator. Mirrors `Promise.any` /
|
|
615
|
+
* `RestatePromise.any`: settles with the first input that succeeds
|
|
616
|
+
* (non-rejected); rejects with `AggregateError(errors)` when every
|
|
617
|
+
* input rejects (including the empty-array case).
|
|
618
|
+
*
|
|
619
|
+
* Fast path collapses to a single `lib.any` over journal awaitables.
|
|
620
|
+
* Fallback synthesizes a fiber that loops `awaitAnyOf` over the
|
|
621
|
+
* still-pending subset, accumulating rejections in input order until
|
|
622
|
+
* one input fulfills or all have rejected.
|
|
623
|
+
*
|
|
624
|
+
* Tuple-aware: `any([fA, fB])` where `fA: Future<A>` and `fB:
|
|
625
|
+
* Future<B>` yields `Future<A | B>` (the union of slot types), same
|
|
626
|
+
* shape as `Promise.any`.
|
|
627
|
+
*/
|
|
628
|
+
any(futures) {
|
|
629
|
+
const fs = futures;
|
|
630
|
+
if (fs.every(isJournalBacked)) {
|
|
631
|
+
const promises = fs.map((f) => f[futureBacking].promise);
|
|
632
|
+
return this.makeJournalFuture(this.lib.any(promises));
|
|
633
|
+
}
|
|
634
|
+
return this.spawn(gen(function* () {
|
|
635
|
+
const errors = new Array(fs.length);
|
|
636
|
+
const remaining = /* @__PURE__ */ new Set();
|
|
637
|
+
for (let i = 0; i < fs.length; i++) remaining.add(i);
|
|
638
|
+
while (remaining.size > 0) {
|
|
639
|
+
const liveIdx = Array.from(remaining);
|
|
640
|
+
const result = yield* awaitRace(liveIdx.map((i) => fs[i]));
|
|
641
|
+
const original = liveIdx[result.index];
|
|
642
|
+
if (result.settled.ok) return result.settled.v;
|
|
643
|
+
errors[original] = result.settled.e;
|
|
644
|
+
remaining.delete(original);
|
|
645
|
+
}
|
|
646
|
+
throw new AggregateError(errors, "All promises were rejected");
|
|
647
|
+
}));
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* Settle-all combinator. Mirrors `Promise.allSettled` /
|
|
651
|
+
* `RestatePromise.allSettled`: resolves with an array of
|
|
652
|
+
* `FutureSettledResult` in input order, never rejects.
|
|
653
|
+
*
|
|
654
|
+
* Fast path collapses to a single `lib.allSettled`. Fallback yields
|
|
655
|
+
* each Future in turn — safe because Futures are eager (already in
|
|
656
|
+
* flight); sequential harvesting just reads them as they complete
|
|
657
|
+
* without blocking concurrency.
|
|
658
|
+
*
|
|
659
|
+
* Tuple-aware: `allSettled([fA, fB])` yields
|
|
660
|
+
* `Future<[FutureSettledResult<A>, FutureSettledResult<B>]>`.
|
|
661
|
+
*/
|
|
662
|
+
allSettled(futures) {
|
|
663
|
+
const fs = futures;
|
|
664
|
+
if (fs.every(isJournalBacked)) {
|
|
665
|
+
const promises = fs.map((f) => f[futureBacking].promise);
|
|
666
|
+
return this.makeJournalFuture(this.lib.allSettled(promises));
|
|
667
|
+
}
|
|
668
|
+
return this.spawn(gen(function* () {
|
|
669
|
+
const out = new Array(fs.length);
|
|
670
|
+
for (let i = 0; i < fs.length; i++) try {
|
|
671
|
+
out[i] = {
|
|
672
|
+
status: "fulfilled",
|
|
673
|
+
value: yield* fs[i]
|
|
674
|
+
};
|
|
675
|
+
} catch (reason) {
|
|
676
|
+
out[i] = {
|
|
677
|
+
status: "rejected",
|
|
678
|
+
reason
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
return out;
|
|
682
|
+
}));
|
|
683
|
+
}
|
|
684
|
+
drainReady() {
|
|
685
|
+
while (this.ready.length > 0) this.ready.shift().advance();
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* Run an operation to completion. Drain the ready queue, then loop:
|
|
689
|
+
* collect every PromiseSource from every parked fiber, race them,
|
|
690
|
+
* dispatch the winner via its fire callback, drain. Stop when no
|
|
691
|
+
* fiber is alive.
|
|
692
|
+
*/
|
|
693
|
+
async run(op) {
|
|
694
|
+
const main = this.createFiber(op);
|
|
695
|
+
this.drainReady();
|
|
696
|
+
while (this.fibers.size > 0) {
|
|
697
|
+
const items = [];
|
|
698
|
+
for (const f of this.fibers) for (const src of f.parkedSources()) items.push(src);
|
|
699
|
+
if (items.length === 0) throw new Error("scheduler stuck: live fibers but nothing pending on a journal promise");
|
|
700
|
+
const tagged = items.map(({ promise }, i$1) => promise.map((v, e) => e !== void 0 ? {
|
|
701
|
+
i: i$1,
|
|
702
|
+
ok: false,
|
|
703
|
+
e
|
|
704
|
+
} : {
|
|
705
|
+
i: i$1,
|
|
706
|
+
ok: true,
|
|
707
|
+
v
|
|
708
|
+
}));
|
|
709
|
+
let raceWinner;
|
|
710
|
+
try {
|
|
711
|
+
raceWinner = await this.lib.race(tagged);
|
|
712
|
+
} catch (e) {
|
|
713
|
+
if (this.lib.isCancellation(e)) {
|
|
714
|
+
this.abortController.abort(e);
|
|
715
|
+
this.abortController = new AbortController();
|
|
716
|
+
}
|
|
717
|
+
const errSettled = {
|
|
718
|
+
ok: false,
|
|
719
|
+
e
|
|
720
|
+
};
|
|
721
|
+
for (const it of items) it.fire(errSettled);
|
|
722
|
+
this.drainReady();
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
const { i,...settledFields } = raceWinner;
|
|
726
|
+
const settled = settledFields.ok ? {
|
|
727
|
+
ok: true,
|
|
728
|
+
v: settledFields.v
|
|
729
|
+
} : {
|
|
730
|
+
ok: false,
|
|
731
|
+
e: settledFields.e
|
|
732
|
+
};
|
|
733
|
+
items[i].fire(settled);
|
|
734
|
+
this.drainReady();
|
|
735
|
+
}
|
|
736
|
+
if (!main.isDone()) throw new Error("scheduler exited but main fiber never completed");
|
|
737
|
+
const final = main.settledValue();
|
|
738
|
+
if (final.ok) return final.v;
|
|
739
|
+
throw final.e;
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
|
|
743
|
+
//#endregion
|
|
744
|
+
//#region src/state.ts
|
|
745
|
+
/**
|
|
746
|
+
* Build a `State<TState>` over the given context. The runtime delegates
|
|
747
|
+
* straight to `ctx.get` / `ctx.set` / etc.; the TState generic is purely
|
|
748
|
+
* a TS-level convenience and gets erased at runtime.
|
|
749
|
+
*
|
|
750
|
+
* For shared (read-only) contexts, the returned State has the same
|
|
751
|
+
* runtime shape but the caller should use the `SharedState<TState>`
|
|
752
|
+
* type to drop the write methods. The convenience method
|
|
753
|
+
* `RestateOperations.sharedState()` does this cast.
|
|
754
|
+
*/
|
|
755
|
+
function makeState(ctx, sched, adapt$1) {
|
|
756
|
+
const writeCtx = ctx;
|
|
757
|
+
return {
|
|
758
|
+
get(name, serde$1) {
|
|
759
|
+
return sched.makeJournalFuture(adapt$1(ctx.get(name, serde$1)));
|
|
760
|
+
},
|
|
761
|
+
keys() {
|
|
762
|
+
return sched.makeJournalFuture(adapt$1(ctx.stateKeys()));
|
|
763
|
+
},
|
|
764
|
+
set(name, value, serde$1) {
|
|
765
|
+
writeCtx.set(name, value, serde$1);
|
|
766
|
+
},
|
|
767
|
+
clear(name) {
|
|
768
|
+
writeCtx.clear(name);
|
|
769
|
+
},
|
|
770
|
+
clearAll() {
|
|
771
|
+
writeCtx.clearAll();
|
|
772
|
+
}
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
//#endregion
|
|
777
|
+
//#region src/clients.ts
|
|
778
|
+
function makeClient(def, key, call$1) {
|
|
779
|
+
return new Proxy({}, { get(_target, methodName) {
|
|
780
|
+
return (...args) => {
|
|
781
|
+
const { parameter, opts } = optsFromArgs$1(args);
|
|
782
|
+
const callOpts = opts;
|
|
783
|
+
const desc = def._handlers[methodName];
|
|
784
|
+
const outputSerde = callOpts?.output ?? desc?._outputSerde;
|
|
785
|
+
return call$1({
|
|
786
|
+
service: def.name,
|
|
787
|
+
key,
|
|
788
|
+
method: String(methodName),
|
|
789
|
+
parameter,
|
|
790
|
+
inputSerde: callOpts?.input ?? desc?._inputSerde ?? __restatedev_restate_sdk.serde.json,
|
|
791
|
+
outputSerde: outputSerde ?? __restatedev_restate_sdk.serde.json,
|
|
792
|
+
idempotencyKey: callOpts?.idempotencyKey,
|
|
793
|
+
headers: callOpts?.headers,
|
|
794
|
+
name: callOpts?.name
|
|
795
|
+
});
|
|
796
|
+
};
|
|
797
|
+
} });
|
|
798
|
+
}
|
|
799
|
+
function makeSendClient(def, key, send$1) {
|
|
800
|
+
return new Proxy({}, { get(_target, methodName) {
|
|
801
|
+
return (...args) => {
|
|
802
|
+
const { parameter, opts } = optsFromArgs$1(args);
|
|
803
|
+
const sendOpts = opts;
|
|
804
|
+
const desc = def._handlers[methodName];
|
|
805
|
+
return send$1({
|
|
806
|
+
service: def.name,
|
|
807
|
+
key,
|
|
808
|
+
method: String(methodName),
|
|
809
|
+
parameter,
|
|
810
|
+
inputSerde: sendOpts?.input ?? desc?._inputSerde ?? __restatedev_restate_sdk.serde.json,
|
|
811
|
+
delay: sendOpts?.delay,
|
|
812
|
+
idempotencyKey: sendOpts?.idempotencyKey,
|
|
813
|
+
headers: sendOpts?.headers,
|
|
814
|
+
name: sendOpts?.name
|
|
815
|
+
}, desc?._outputSerde);
|
|
816
|
+
};
|
|
817
|
+
} });
|
|
818
|
+
}
|
|
819
|
+
function optsFromArgs$1(args) {
|
|
820
|
+
let parameter;
|
|
821
|
+
let opts;
|
|
822
|
+
switch (args.length) {
|
|
823
|
+
case 0: break;
|
|
824
|
+
case 1:
|
|
825
|
+
if (args[0] instanceof __restatedev_restate_sdk.Opts) opts = args[0].getOpts();
|
|
826
|
+
else if (args[0] instanceof __restatedev_restate_sdk.SendOpts) opts = args[0].getOpts();
|
|
827
|
+
else parameter = args[0];
|
|
828
|
+
break;
|
|
829
|
+
case 2:
|
|
830
|
+
parameter = args[0];
|
|
831
|
+
if (args[1] instanceof __restatedev_restate_sdk.Opts) opts = args[1].getOpts();
|
|
832
|
+
else if (args[1] instanceof __restatedev_restate_sdk.SendOpts) opts = args[1].getOpts();
|
|
833
|
+
else throw new TypeError("The second argument must be either Opts or SendOpts");
|
|
834
|
+
break;
|
|
835
|
+
default: throw new TypeError("unexpected number of arguments");
|
|
836
|
+
}
|
|
837
|
+
return {
|
|
838
|
+
parameter,
|
|
839
|
+
opts
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
//#endregion
|
|
844
|
+
//#region src/durable-promise.ts
|
|
845
|
+
function wrapDurablePromise(dp, toFuture) {
|
|
846
|
+
return {
|
|
847
|
+
peek: () => toFuture(dp.peek()),
|
|
848
|
+
resolve: (value) => toFuture(dp.resolve(value)),
|
|
849
|
+
reject: (errorMsg) => toFuture(dp.reject(errorMsg)),
|
|
850
|
+
get: () => toFuture(dp.get())
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
//#endregion
|
|
855
|
+
//#region src/default-lib.ts
|
|
856
|
+
const defaultLib = {
|
|
857
|
+
all(items) {
|
|
858
|
+
return __restatedev_restate_sdk.RestatePromise.all(items);
|
|
859
|
+
},
|
|
860
|
+
race(items) {
|
|
861
|
+
return __restatedev_restate_sdk.RestatePromise.race(items);
|
|
862
|
+
},
|
|
863
|
+
any(items) {
|
|
864
|
+
return __restatedev_restate_sdk.RestatePromise.any(items);
|
|
865
|
+
},
|
|
866
|
+
allSettled(items) {
|
|
867
|
+
return __restatedev_restate_sdk.RestatePromise.allSettled(items);
|
|
868
|
+
},
|
|
869
|
+
isCancellation(e) {
|
|
870
|
+
return e instanceof __restatedev_restate_sdk.CancelledError;
|
|
871
|
+
}
|
|
872
|
+
};
|
|
873
|
+
|
|
874
|
+
//#endregion
|
|
875
|
+
//#region src/restate-operations.ts
|
|
876
|
+
function adapt(p) {
|
|
877
|
+
return p;
|
|
878
|
+
}
|
|
879
|
+
/**
|
|
880
|
+
* Wrap a user-supplied `run` closure to surface the abort reason
|
|
881
|
+
* (typically a TerminalError(CANCELLED)) on throw paths if the signal
|
|
882
|
+
* aborted during execution. This converts AbortError (and any other
|
|
883
|
+
* abort-caused failure) into the canonical cancellation TerminalError
|
|
884
|
+
* for journal recording.
|
|
885
|
+
*
|
|
886
|
+
* Defensive coercion: if `signal.reason` is itself not a TerminalError
|
|
887
|
+
* (which shouldn't happen in production but might during testing or
|
|
888
|
+
* with non-cancellation race rejections), we wrap it in one. The
|
|
889
|
+
* journal must record a *terminal* outcome to avoid retries against
|
|
890
|
+
* a cancelled invocation.
|
|
891
|
+
*
|
|
892
|
+
* Exposed for testing — the wrapper's behavior is the part that has
|
|
893
|
+
* semantic bite, separate from the ctx.run plumbing.
|
|
894
|
+
*/
|
|
895
|
+
function wrapActionForCancellation(signal$1, action) {
|
|
896
|
+
return async () => {
|
|
897
|
+
try {
|
|
898
|
+
return await action({ signal: signal$1 });
|
|
899
|
+
} catch (e) {
|
|
900
|
+
if (signal$1.aborted) throw asTerminalError(signal$1.reason);
|
|
901
|
+
throw e;
|
|
902
|
+
}
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Resolve the journal-entry name; throw `TerminalError` if neither
|
|
907
|
+
* source provides one.
|
|
908
|
+
*
|
|
909
|
+
* `TerminalError` (not a plain `Error`) because a missing name is a
|
|
910
|
+
* programming bug — retrying the invocation will hit the same code
|
|
911
|
+
* path and fail the same way. The SDK treats terminal errors as
|
|
912
|
+
* non-retryable; the invocation fails fast instead of looping.
|
|
913
|
+
*/
|
|
914
|
+
function resolveRunName(action, opts) {
|
|
915
|
+
const fromOpts = opts?.name?.trim();
|
|
916
|
+
if (fromOpts) return fromOpts;
|
|
917
|
+
const fromFn = action.name;
|
|
918
|
+
if (fromFn) return fromFn;
|
|
919
|
+
throw new __restatedev_restate_sdk.TerminalError("@restatedev/restate-sdk-gen: run() requires a journal-entry name. Either pass a named function (`run(myFn)`) or supply `{ name: '...' }` in the second argument.");
|
|
920
|
+
}
|
|
921
|
+
/** Translate our `RetryOptions` into the SDK's flat `RunOptions` shape. */
|
|
922
|
+
function toSdkRunOptions(opts) {
|
|
923
|
+
const out = {};
|
|
924
|
+
if (opts?.serde !== void 0) out.serde = opts.serde;
|
|
925
|
+
const r = opts?.retry;
|
|
926
|
+
if (r) {
|
|
927
|
+
if (r.maxAttempts !== void 0) out.maxRetryAttempts = r.maxAttempts;
|
|
928
|
+
if (r.maxDuration !== void 0) out.maxRetryDuration = r.maxDuration;
|
|
929
|
+
if (r.initialInterval !== void 0) out.initialRetryInterval = r.initialInterval;
|
|
930
|
+
if (r.maxInterval !== void 0) out.maxRetryInterval = r.maxInterval;
|
|
931
|
+
if (r.intervalFactor !== void 0) out.retryIntervalFactor = r.intervalFactor;
|
|
932
|
+
}
|
|
933
|
+
return out;
|
|
934
|
+
}
|
|
935
|
+
function asTerminalError(reason) {
|
|
936
|
+
if (reason instanceof __restatedev_restate_sdk.TerminalError) return reason;
|
|
937
|
+
return new __restatedev_restate_sdk.CancelledError();
|
|
938
|
+
}
|
|
939
|
+
var RestateOperations = class {
|
|
940
|
+
ctx;
|
|
941
|
+
sched;
|
|
942
|
+
constructor(context, sched) {
|
|
943
|
+
this.ctx = context;
|
|
944
|
+
this.sched = sched;
|
|
945
|
+
}
|
|
946
|
+
toFuture(p) {
|
|
947
|
+
return this.sched.makeJournalFuture(adapt(p));
|
|
948
|
+
}
|
|
949
|
+
get rand() {
|
|
950
|
+
return this.ctx.rand;
|
|
951
|
+
}
|
|
952
|
+
get date() {
|
|
953
|
+
return {
|
|
954
|
+
now: () => this.toFuture(this.ctx.date.now()),
|
|
955
|
+
toJSON: () => this.toFuture(this.ctx.date.toJSON())
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
get console() {
|
|
959
|
+
return this.ctx.console;
|
|
960
|
+
}
|
|
961
|
+
/**
|
|
962
|
+
* Run a side-effecting closure as a journal entry.
|
|
963
|
+
*
|
|
964
|
+
* The closure receives `{ signal }` — an AbortSignal that fires when
|
|
965
|
+
* invocation cancellation arrives. Pass it into AbortSignal-aware
|
|
966
|
+
* APIs (e.g. `fetch(url, { signal })`) to abort in-flight syscalls.
|
|
967
|
+
*
|
|
968
|
+
* `name` is the journal entry's stable identifier (must be
|
|
969
|
+
* deterministic across replay). It can come from either:
|
|
970
|
+
*
|
|
971
|
+
* - `opts.name` — explicit override
|
|
972
|
+
* - `action.name` — the function's own name (works for `function`
|
|
973
|
+
* declarations and arrow functions assigned to a `const`,
|
|
974
|
+
* since JS infers names from the binding site)
|
|
975
|
+
*
|
|
976
|
+
* If neither resolves, `run` throws.
|
|
977
|
+
*
|
|
978
|
+
* @example Named function — name derived
|
|
979
|
+
* async function fetchUser({ signal }: RunActionOpts): Promise<User> {
|
|
980
|
+
* const r = await fetch(`/users/${id}`, { signal });
|
|
981
|
+
* return r.json();
|
|
982
|
+
* }
|
|
983
|
+
* yield* run(fetchUser);
|
|
984
|
+
*
|
|
985
|
+
* @example Named arrow — name derived from binding
|
|
986
|
+
* const fetchUser = async ({ signal }: RunActionOpts) => { ... };
|
|
987
|
+
* yield* run(fetchUser);
|
|
988
|
+
*
|
|
989
|
+
* @example Inline arrow — name explicit
|
|
990
|
+
* yield* run(async ({ signal }) => fetch(url, { signal }), { name: "fetch" });
|
|
991
|
+
*
|
|
992
|
+
* @example With retry policy
|
|
993
|
+
* yield* run(fetchUser, { retry: { maxAttempts: 3 } });
|
|
994
|
+
*
|
|
995
|
+
* Cancellation hygiene: if the closure throws while the signal is
|
|
996
|
+
* aborted, we rethrow `signal.reason` (the original TerminalError)
|
|
997
|
+
* instead of whatever the closure threw. This ensures the journal
|
|
998
|
+
* entry records `TerminalError(CANCELLED)` rather than `AbortError`.
|
|
999
|
+
*/
|
|
1000
|
+
run(action, opts) {
|
|
1001
|
+
const name = resolveRunName(action, opts);
|
|
1002
|
+
const wrapped = wrapActionForCancellation(this.sched.abortSignal, action);
|
|
1003
|
+
return this.sched.makeJournalFuture(adapt(this.ctx.run(name, wrapped, toSdkRunOptions(opts))));
|
|
1004
|
+
}
|
|
1005
|
+
sleep(duration, name) {
|
|
1006
|
+
return this.sched.makeJournalFuture(adapt(this.ctx.sleep(duration, name)));
|
|
1007
|
+
}
|
|
1008
|
+
awakeable(serde$1) {
|
|
1009
|
+
const { id, promise } = this.ctx.awakeable(serde$1);
|
|
1010
|
+
return {
|
|
1011
|
+
id,
|
|
1012
|
+
promise: this.sched.makeJournalFuture(adapt(promise))
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
resolveAwakeable(id, payload, serde$1) {
|
|
1016
|
+
this.ctx.resolveAwakeable(id, payload, serde$1);
|
|
1017
|
+
}
|
|
1018
|
+
rejectAwakeable(id, reason) {
|
|
1019
|
+
this.ctx.rejectAwakeable(id, reason);
|
|
1020
|
+
}
|
|
1021
|
+
signal(name, serde$1) {
|
|
1022
|
+
return this.sched.makeJournalFuture(adapt(this.ctx.signal(name, serde$1)));
|
|
1023
|
+
}
|
|
1024
|
+
attach(invocationId, serde$1) {
|
|
1025
|
+
return this.sched.makeJournalFuture(adapt(this.ctx.attach(invocationId, serde$1)));
|
|
1026
|
+
}
|
|
1027
|
+
/**
|
|
1028
|
+
* Returns the current invocation's request metadata plus the optional
|
|
1029
|
+
* virtual-object / workflow key. The `key` field is only present when
|
|
1030
|
+
* the handler belongs to an object or workflow.
|
|
1031
|
+
*/
|
|
1032
|
+
handlerRequest() {
|
|
1033
|
+
const req = this.ctx.request();
|
|
1034
|
+
const ctx = this.ctx;
|
|
1035
|
+
return {
|
|
1036
|
+
attemptHeaders: req.attemptHeaders,
|
|
1037
|
+
body: req.body,
|
|
1038
|
+
extraArgs: req.extraArgs,
|
|
1039
|
+
headers: req.headers,
|
|
1040
|
+
id: req.id,
|
|
1041
|
+
target: req.target,
|
|
1042
|
+
get key() {
|
|
1043
|
+
return ctx.key;
|
|
1044
|
+
}
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
client(def, key) {
|
|
1048
|
+
return makeClient(def, key, (o) => this.call(o));
|
|
1049
|
+
}
|
|
1050
|
+
sendClient(def, key) {
|
|
1051
|
+
return makeSendClient(def, key, (o, serde$1) => this.send(o, serde$1));
|
|
1052
|
+
}
|
|
1053
|
+
call(opts) {
|
|
1054
|
+
const restatePromise = this.ctx.genericCall(opts);
|
|
1055
|
+
const resultFuture = this.toFuture(restatePromise);
|
|
1056
|
+
const invocation$1 = this.invocationReferenceFromHandle(restatePromise, opts.outputSerde);
|
|
1057
|
+
return Object.assign(resultFuture, { invocation: invocation$1 });
|
|
1058
|
+
}
|
|
1059
|
+
send(opts, outputSerde) {
|
|
1060
|
+
const handle = this.ctx.genericSend(opts);
|
|
1061
|
+
return this.invocationReferenceFromHandle(handle, outputSerde);
|
|
1062
|
+
}
|
|
1063
|
+
invocationReferenceFromHandle(handle, outputSerde) {
|
|
1064
|
+
const idFuture = this.toFuture(handle.invocationId);
|
|
1065
|
+
return this.sched.spawn(gen(function* () {
|
|
1066
|
+
return new InvocationReferenceImpl(yield* idFuture, outputSerde);
|
|
1067
|
+
}));
|
|
1068
|
+
}
|
|
1069
|
+
invocationSignal(invocationId, name, serde$1) {
|
|
1070
|
+
return this.ctx.invocation(invocationId).signal(name, serde$1);
|
|
1071
|
+
}
|
|
1072
|
+
/**
|
|
1073
|
+
* Cancel another invocation by its id. To observe cancellation
|
|
1074
|
+
* arriving at *this* invocation, catch the `TerminalError` thrown by
|
|
1075
|
+
* the next `yield*` boundary or use the `signal` exposed inside
|
|
1076
|
+
* `ops.run` closures.
|
|
1077
|
+
*/
|
|
1078
|
+
cancel(invocationId) {
|
|
1079
|
+
this.ctx.cancel(invocationId);
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* Workflow-bound durable promise. Use only inside a workflow handler
|
|
1083
|
+
* (the underlying context must be `WorkflowContext` or
|
|
1084
|
+
* `WorkflowSharedContext`). Returns a wrapper whose `peek`/`get`/
|
|
1085
|
+
* `resolve`/`reject` methods return Futures.
|
|
1086
|
+
*/
|
|
1087
|
+
workflowPromise(name, serde$1) {
|
|
1088
|
+
const wfCtx = this.ctx;
|
|
1089
|
+
return wrapDurablePromise(wfCtx.promise(name, serde$1), (p) => this.toFuture(p));
|
|
1090
|
+
}
|
|
1091
|
+
/**
|
|
1092
|
+
* Register `op` as a fresh routine and return a `Future<T>` for its
|
|
1093
|
+
* eventual outcome. Eager: by the time `spawn` returns, the child is
|
|
1094
|
+
* already queued and will advance on the next scheduler tick — same
|
|
1095
|
+
* model as `run`/`sleep`/`awakeable`. The returned Future can be
|
|
1096
|
+
* yielded on, handed to combinators, or stored.
|
|
1097
|
+
*/
|
|
1098
|
+
spawn(op) {
|
|
1099
|
+
return this.sched.spawn(op);
|
|
1100
|
+
}
|
|
1101
|
+
/**
|
|
1102
|
+
* Create a single-shot in-memory channel. Returns a Channel<T> with
|
|
1103
|
+
* `send(v)` (fire-and-forget, idempotent — first call settles, rest
|
|
1104
|
+
* are dropped) and `receive: Future<T>` (a stable settle-once Future,
|
|
1105
|
+
* the same handle on every access).
|
|
1106
|
+
*
|
|
1107
|
+
* Canonical use: cooperative cancellation. Spawn a routine that
|
|
1108
|
+
* selects over its work and `stop.receive`; the canceller calls
|
|
1109
|
+
* `stop.send()` to request termination. The receiver decides what
|
|
1110
|
+
* to do — return a partial result, do cleanup yields, ignore.
|
|
1111
|
+
*
|
|
1112
|
+
* Because `receive` is a stable, settle-once Future, multiple readers
|
|
1113
|
+
* all observe the same value (one-time broadcast) and the worker can
|
|
1114
|
+
* use it in every iteration of a select-loop without leaking orphan
|
|
1115
|
+
* receivers.
|
|
1116
|
+
*
|
|
1117
|
+
* Multi-event streams (producer-consumer, progress events) are NOT
|
|
1118
|
+
* supported — Channel is intentionally single-shot. A separate
|
|
1119
|
+
* primitive for that use case is yet to be designed.
|
|
1120
|
+
*/
|
|
1121
|
+
channel() {
|
|
1122
|
+
return this.sched.makeChannel();
|
|
1123
|
+
}
|
|
1124
|
+
/**
|
|
1125
|
+
* Per-invocation read-write key-value store. Use from a handler whose
|
|
1126
|
+
* underlying context is ObjectContext or WorkflowContext.
|
|
1127
|
+
*
|
|
1128
|
+
* The optional `TState` generic gives keyof-checked names and per-key
|
|
1129
|
+
* value types:
|
|
1130
|
+
*
|
|
1131
|
+
* ops.state<{count: number; user: User}>()
|
|
1132
|
+
* // state.get("count") → Future<number | null>
|
|
1133
|
+
*
|
|
1134
|
+
* Without it, names are `string` and values are inferred per call:
|
|
1135
|
+
*
|
|
1136
|
+
* ops.state()
|
|
1137
|
+
* // state.get<number>("count") → Future<number | null>
|
|
1138
|
+
*
|
|
1139
|
+
* Calling write methods from a shared (read-only) context throws at
|
|
1140
|
+
* runtime — for shared handlers, use `sharedState()` below to get a
|
|
1141
|
+
* narrower type that drops the write methods.
|
|
1142
|
+
*/
|
|
1143
|
+
state() {
|
|
1144
|
+
return makeState(this.ctx, this.sched, adapt);
|
|
1145
|
+
}
|
|
1146
|
+
/**
|
|
1147
|
+
* Per-invocation read-only key-value store. Use from a handler whose
|
|
1148
|
+
* underlying context is ObjectSharedContext or WorkflowSharedContext.
|
|
1149
|
+
*
|
|
1150
|
+
* Same `TState` generic as `state()`. Returns the read-only subset
|
|
1151
|
+
* (`get`, `keys`); attempting to call writes is a type error.
|
|
1152
|
+
*/
|
|
1153
|
+
sharedState() {
|
|
1154
|
+
return makeState(this.ctx, this.sched, adapt);
|
|
1155
|
+
}
|
|
1156
|
+
/**
|
|
1157
|
+
* Wait for every future to settle; return their values in input
|
|
1158
|
+
* order. Heterogeneous-tuple typing — `all([fA, fB])` where
|
|
1159
|
+
* `fA: Future<A>` and `fB: Future<B>` yields `Future<[A, B]>`.
|
|
1160
|
+
* Mirrors `Promise.all` from the standard lib.
|
|
1161
|
+
*/
|
|
1162
|
+
all(futures) {
|
|
1163
|
+
return this.sched.all(futures);
|
|
1164
|
+
}
|
|
1165
|
+
/**
|
|
1166
|
+
* Return the first future to settle; losers continue running but
|
|
1167
|
+
* their results are discarded. Heterogeneous-tuple typing —
|
|
1168
|
+
* `race([fA, fB])` yields `Future<A | B>`. Mirrors `Promise.race`.
|
|
1169
|
+
*/
|
|
1170
|
+
race(futures) {
|
|
1171
|
+
return this.sched.race(futures);
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* First-success combinator. Resolves with the first input that
|
|
1175
|
+
* succeeds (non-rejected); rejects with `AggregateError(errors)` when
|
|
1176
|
+
* every input rejects (including an empty input array). See `Promise.any`.
|
|
1177
|
+
*
|
|
1178
|
+
* Tuple-aware typing — `any([fA, fB])` where `fA: Future<A>` and
|
|
1179
|
+
* `fB: Future<B>` yields `Future<A | B>`.
|
|
1180
|
+
*/
|
|
1181
|
+
any(futures) {
|
|
1182
|
+
return this.sched.any(futures);
|
|
1183
|
+
}
|
|
1184
|
+
/**
|
|
1185
|
+
* Settle-all combinator. Resolves with an array of
|
|
1186
|
+
* `FutureSettledResult` in input order; never rejects. See
|
|
1187
|
+
* `Promise.allSettled`.
|
|
1188
|
+
*
|
|
1189
|
+
* Tuple-aware typing — `allSettled([fA, fB])` yields
|
|
1190
|
+
* `Future<[FutureSettledResult<A>, FutureSettledResult<B>]>`.
|
|
1191
|
+
*/
|
|
1192
|
+
allSettled(futures) {
|
|
1193
|
+
return this.sched.allSettled(futures);
|
|
1194
|
+
}
|
|
1195
|
+
*select(branches) {
|
|
1196
|
+
return yield* select(branches);
|
|
1197
|
+
}
|
|
1198
|
+
};
|
|
1199
|
+
/**
|
|
1200
|
+
* Run a generator-based workflow against a Restate context.
|
|
1201
|
+
*
|
|
1202
|
+
* `op` is an `Operation<T>` — typically the result of
|
|
1203
|
+
* `gen(function*() { ... })`. Inside the generator body, reach for the
|
|
1204
|
+
* free-standing API (`run`, `sleep`, `all`, `state`, …) imported
|
|
1205
|
+
* from `@restatedev/restate-sdk-gen`. They read the active scheduler from a
|
|
1206
|
+
* synchronous current-fiber slot installed by `Fiber.advance`.
|
|
1207
|
+
*
|
|
1208
|
+
* `gen()` already takes a factory, so the same `Operation` is re-
|
|
1209
|
+
* iterable across multiple `execute()` calls — no need for a builder
|
|
1210
|
+
* lambda at this boundary.
|
|
1211
|
+
*
|
|
1212
|
+
* @example
|
|
1213
|
+
* execute(ctx, gen(function* () {
|
|
1214
|
+
* const greeting = yield* run(async () => "hi", { name: "compose" });
|
|
1215
|
+
* return greeting;
|
|
1216
|
+
* }));
|
|
1217
|
+
*/
|
|
1218
|
+
async function execute(context, op) {
|
|
1219
|
+
const sched = new Scheduler(defaultLib);
|
|
1220
|
+
sched.contextSlot = new RestateOperations(context, sched);
|
|
1221
|
+
return sched.run(op);
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
//#endregion
|
|
1225
|
+
//#region src/define.ts
|
|
1226
|
+
function makeDescriptor(inputSerde, outputSerde) {
|
|
1227
|
+
return {
|
|
1228
|
+
_inputSerde: inputSerde,
|
|
1229
|
+
_outputSerde: outputSerde
|
|
1230
|
+
};
|
|
1231
|
+
}
|
|
1232
|
+
/** HandlerDef has _genFn as an object property; bare gen fns are plain functions */
|
|
1233
|
+
function isHandlerDef(entry) {
|
|
1234
|
+
return typeof entry === "object" && entry !== null && typeof entry._genFn === "function";
|
|
1235
|
+
}
|
|
1236
|
+
/** Convert a Standard Schema to a Serde via restate.serde.schema() */
|
|
1237
|
+
function toSerde(schema) {
|
|
1238
|
+
return __restatedev_restate_sdk.serde.schema(schema);
|
|
1239
|
+
}
|
|
1240
|
+
function extractEntry(entry) {
|
|
1241
|
+
if (isHandlerDef(entry)) return {
|
|
1242
|
+
genFn: entry._genFn,
|
|
1243
|
+
inputSerde: entry._inputSerde,
|
|
1244
|
+
outputSerde: entry._outputSerde
|
|
1245
|
+
};
|
|
1246
|
+
return {
|
|
1247
|
+
genFn: entry,
|
|
1248
|
+
inputSerde: void 0,
|
|
1249
|
+
outputSerde: void 0
|
|
1250
|
+
};
|
|
1251
|
+
}
|
|
1252
|
+
/** serdes(opts, fn) — explicit Serde per field */
|
|
1253
|
+
function serdes(opts, fn) {
|
|
1254
|
+
return {
|
|
1255
|
+
_genFn: fn,
|
|
1256
|
+
_inputSerde: opts.input,
|
|
1257
|
+
_outputSerde: opts.output
|
|
1258
|
+
};
|
|
1259
|
+
}
|
|
1260
|
+
/** schemas(opts, fn) — Standard Schema (Zod, TypeBox, Valibot, …) per field */
|
|
1261
|
+
function schemas(opts, fn) {
|
|
1262
|
+
return {
|
|
1263
|
+
_genFn: fn,
|
|
1264
|
+
_inputSerde: toSerde(opts.input),
|
|
1265
|
+
_outputSerde: toSerde(opts.output)
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1268
|
+
function service(config) {
|
|
1269
|
+
const { name, description, metadata, handlers, options } = config;
|
|
1270
|
+
const { handlers: perHandlerOpts,...serviceOpts } = options ?? {};
|
|
1271
|
+
const coreHandlers = {};
|
|
1272
|
+
const descriptors = {};
|
|
1273
|
+
for (const [handlerName, entry] of Object.entries(handlers)) {
|
|
1274
|
+
const { genFn, inputSerde, outputSerde } = extractEntry(entry);
|
|
1275
|
+
const handlerOpts = perHandlerOpts?.[handlerName] ?? {};
|
|
1276
|
+
coreHandlers[handlerName] = __restatedev_restate_sdk.handlers.handler({
|
|
1277
|
+
input: inputSerde,
|
|
1278
|
+
output: outputSerde,
|
|
1279
|
+
...handlerOpts
|
|
1280
|
+
}, async (ctx, input) => execute(ctx, genFn(input)));
|
|
1281
|
+
descriptors[handlerName] = makeDescriptor(inputSerde, outputSerde);
|
|
1282
|
+
}
|
|
1283
|
+
const coreDef = __restatedev_restate_sdk.service({
|
|
1284
|
+
name,
|
|
1285
|
+
handlers: coreHandlers,
|
|
1286
|
+
description,
|
|
1287
|
+
metadata,
|
|
1288
|
+
options: serviceOpts
|
|
1289
|
+
});
|
|
1290
|
+
return Object.assign(coreDef, {
|
|
1291
|
+
_kind: "service",
|
|
1292
|
+
_handlers: descriptors
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
function object(config) {
|
|
1296
|
+
const { name, description, metadata, handlers, options } = config;
|
|
1297
|
+
const { handlers: perHandlerOpts,...objectOpts } = options ?? {};
|
|
1298
|
+
const coreHandlers = {};
|
|
1299
|
+
const descriptors = {};
|
|
1300
|
+
for (const [handlerName, entry] of Object.entries(handlers)) {
|
|
1301
|
+
const { genFn, inputSerde, outputSerde } = extractEntry(entry);
|
|
1302
|
+
const { shared,...restOpts } = perHandlerOpts?.[handlerName] ?? {};
|
|
1303
|
+
const sdkOpts = {
|
|
1304
|
+
input: inputSerde,
|
|
1305
|
+
output: outputSerde,
|
|
1306
|
+
...restOpts
|
|
1307
|
+
};
|
|
1308
|
+
const fn = async (ctx, input) => execute(ctx, genFn(input));
|
|
1309
|
+
coreHandlers[handlerName] = shared ? __restatedev_restate_sdk.handlers.object.shared(sdkOpts, fn) : __restatedev_restate_sdk.handlers.object.exclusive(sdkOpts, fn);
|
|
1310
|
+
descriptors[handlerName] = makeDescriptor(inputSerde, outputSerde);
|
|
1311
|
+
}
|
|
1312
|
+
const coreDef = __restatedev_restate_sdk.object({
|
|
1313
|
+
name,
|
|
1314
|
+
handlers: coreHandlers,
|
|
1315
|
+
description,
|
|
1316
|
+
metadata,
|
|
1317
|
+
options: objectOpts
|
|
1318
|
+
});
|
|
1319
|
+
return Object.assign(coreDef, {
|
|
1320
|
+
_kind: "object",
|
|
1321
|
+
_handlers: descriptors
|
|
1322
|
+
});
|
|
1323
|
+
}
|
|
1324
|
+
function workflow(config) {
|
|
1325
|
+
const { name, description, metadata, handlers, options } = config;
|
|
1326
|
+
const { handlers: perHandlerOpts,...workflowOpts } = options ?? {};
|
|
1327
|
+
const coreHandlers = {};
|
|
1328
|
+
const descriptors = {};
|
|
1329
|
+
for (const [handlerName, entry] of Object.entries(handlers)) {
|
|
1330
|
+
const { genFn, inputSerde, outputSerde } = extractEntry(entry);
|
|
1331
|
+
const sdkOpts = {
|
|
1332
|
+
input: inputSerde,
|
|
1333
|
+
output: outputSerde,
|
|
1334
|
+
...perHandlerOpts?.[handlerName] ?? {}
|
|
1335
|
+
};
|
|
1336
|
+
const fn = async (ctx, input) => execute(ctx, genFn(input));
|
|
1337
|
+
coreHandlers[handlerName] = handlerName === "run" ? __restatedev_restate_sdk.handlers.workflow.workflow(sdkOpts, fn) : __restatedev_restate_sdk.handlers.workflow.shared(sdkOpts, fn);
|
|
1338
|
+
descriptors[handlerName] = makeDescriptor(inputSerde, outputSerde);
|
|
1339
|
+
}
|
|
1340
|
+
const coreDef = __restatedev_restate_sdk.workflow({
|
|
1341
|
+
name,
|
|
1342
|
+
handlers: coreHandlers,
|
|
1343
|
+
description,
|
|
1344
|
+
metadata,
|
|
1345
|
+
options: workflowOpts
|
|
1346
|
+
});
|
|
1347
|
+
return Object.assign(coreDef, {
|
|
1348
|
+
_kind: "workflow",
|
|
1349
|
+
_handlers: descriptors
|
|
1350
|
+
});
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
//#endregion
|
|
1354
|
+
//#region src/interface.ts
|
|
1355
|
+
var interface_exports = /* @__PURE__ */ __export({
|
|
1356
|
+
implement: () => implement,
|
|
1357
|
+
json: () => json,
|
|
1358
|
+
object: () => object$1,
|
|
1359
|
+
schemas: () => schemas$1,
|
|
1360
|
+
serdes: () => serdes$1,
|
|
1361
|
+
service: () => service$1,
|
|
1362
|
+
workflow: () => workflow$1
|
|
1363
|
+
});
|
|
1364
|
+
/** json<I, O>() — type params, default JSON serde */
|
|
1365
|
+
function json() {
|
|
1366
|
+
return makeDescriptor(void 0, void 0);
|
|
1367
|
+
}
|
|
1368
|
+
/** serdes(opts) — explicit Serde per field */
|
|
1369
|
+
function serdes$1(opts) {
|
|
1370
|
+
return makeDescriptor(opts.input, opts.output);
|
|
1371
|
+
}
|
|
1372
|
+
/** schemas(opts) — Standard Schema (Zod, TypeBox, Valibot, …) per field */
|
|
1373
|
+
function schemas$1(opts) {
|
|
1374
|
+
return makeDescriptor(opts.input ? toSerde(opts.input) : void 0, opts.output ? toSerde(opts.output) : void 0);
|
|
1375
|
+
}
|
|
1376
|
+
function service$1(name, handlers) {
|
|
1377
|
+
return {
|
|
1378
|
+
name,
|
|
1379
|
+
_kind: "service",
|
|
1380
|
+
_handlers: handlers
|
|
1381
|
+
};
|
|
1382
|
+
}
|
|
1383
|
+
function object$1(name, handlers) {
|
|
1384
|
+
return {
|
|
1385
|
+
name,
|
|
1386
|
+
_kind: "object",
|
|
1387
|
+
_handlers: handlers
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
function workflow$1(name, handlers) {
|
|
1391
|
+
return {
|
|
1392
|
+
name,
|
|
1393
|
+
_kind: "workflow",
|
|
1394
|
+
_handlers: handlers
|
|
1395
|
+
};
|
|
1396
|
+
}
|
|
1397
|
+
function implement(iface, config) {
|
|
1398
|
+
const handlerEntries = {};
|
|
1399
|
+
for (const [name, desc] of Object.entries(iface._handlers)) {
|
|
1400
|
+
const genFn = config.handlers[name];
|
|
1401
|
+
if (!genFn) throw new Error(`implement(): missing handler "${name}"`);
|
|
1402
|
+
handlerEntries[name] = {
|
|
1403
|
+
_genFn: genFn,
|
|
1404
|
+
_inputSerde: desc._inputSerde,
|
|
1405
|
+
_outputSerde: desc._outputSerde
|
|
1406
|
+
};
|
|
1407
|
+
}
|
|
1408
|
+
if (iface._kind === "service") return service({
|
|
1409
|
+
name: iface.name,
|
|
1410
|
+
handlers: handlerEntries,
|
|
1411
|
+
options: config.options
|
|
1412
|
+
});
|
|
1413
|
+
else if (iface._kind === "object") return object({
|
|
1414
|
+
name: iface.name,
|
|
1415
|
+
handlers: handlerEntries,
|
|
1416
|
+
options: config.options
|
|
1417
|
+
});
|
|
1418
|
+
else return workflow({
|
|
1419
|
+
name: iface.name,
|
|
1420
|
+
handlers: handlerEntries,
|
|
1421
|
+
options: config.options
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
//#endregion
|
|
1426
|
+
//#region src/ingress.ts
|
|
1427
|
+
var ingress_exports = /* @__PURE__ */ __export({
|
|
1428
|
+
SendOpts: () => __restatedev_restate_sdk_clients.SendOpts,
|
|
1429
|
+
client: () => client$1,
|
|
1430
|
+
connect: () => connect,
|
|
1431
|
+
sendClient: () => sendClient$1
|
|
1432
|
+
});
|
|
1433
|
+
/**
|
|
1434
|
+
* Connect to the Restate Ingress.
|
|
1435
|
+
*
|
|
1436
|
+
* @param opts connection options
|
|
1437
|
+
* @returns a connection the the restate ingress
|
|
1438
|
+
*/
|
|
1439
|
+
function connect(opts) {
|
|
1440
|
+
return (0, __restatedev_restate_sdk_clients.connect)(opts);
|
|
1441
|
+
}
|
|
1442
|
+
function client$1(ingress, def, key) {
|
|
1443
|
+
return new Proxy({}, { get(_target, methodName) {
|
|
1444
|
+
return (...args) => {
|
|
1445
|
+
const { parameter, opts } = optsFromArgs(args);
|
|
1446
|
+
const desc = def._handlers[methodName];
|
|
1447
|
+
const mergedOpts = __restatedev_restate_sdk_clients.Opts.from({
|
|
1448
|
+
...opts?.opts,
|
|
1449
|
+
input: opts?.opts?.input ?? desc?._inputSerde,
|
|
1450
|
+
output: opts?.opts?.output ?? desc?._outputSerde
|
|
1451
|
+
});
|
|
1452
|
+
return ingress.call({
|
|
1453
|
+
service: def.name,
|
|
1454
|
+
handler: methodName,
|
|
1455
|
+
parameter,
|
|
1456
|
+
key,
|
|
1457
|
+
opts: mergedOpts
|
|
1458
|
+
});
|
|
1459
|
+
};
|
|
1460
|
+
} });
|
|
1461
|
+
}
|
|
1462
|
+
function sendClient$1(ingress, def, key) {
|
|
1463
|
+
return new Proxy({}, { get(_target, methodName) {
|
|
1464
|
+
return (...args) => {
|
|
1465
|
+
const { parameter, opts } = optsFromArgs(args);
|
|
1466
|
+
const desc = def._handlers[methodName];
|
|
1467
|
+
const mergedOpts = __restatedev_restate_sdk_clients.SendOpts.from({
|
|
1468
|
+
...opts?.opts,
|
|
1469
|
+
input: opts?.opts?.input ?? desc?._inputSerde
|
|
1470
|
+
});
|
|
1471
|
+
return ingress.send({
|
|
1472
|
+
service: def.name,
|
|
1473
|
+
handler: methodName,
|
|
1474
|
+
parameter,
|
|
1475
|
+
key,
|
|
1476
|
+
opts: mergedOpts
|
|
1477
|
+
});
|
|
1478
|
+
};
|
|
1479
|
+
} });
|
|
1480
|
+
}
|
|
1481
|
+
function optsFromArgs(args) {
|
|
1482
|
+
let parameter;
|
|
1483
|
+
let opts;
|
|
1484
|
+
switch (args.length) {
|
|
1485
|
+
case 0: break;
|
|
1486
|
+
case 1:
|
|
1487
|
+
if (args[0] instanceof __restatedev_restate_sdk_clients.Opts) opts = args[0];
|
|
1488
|
+
else if (args[0] instanceof __restatedev_restate_sdk_clients.SendOpts) opts = args[0];
|
|
1489
|
+
else parameter = args[0];
|
|
1490
|
+
break;
|
|
1491
|
+
case 2:
|
|
1492
|
+
parameter = args[0];
|
|
1493
|
+
if (args[1] instanceof __restatedev_restate_sdk_clients.Opts) opts = args[1];
|
|
1494
|
+
else if (args[1] instanceof __restatedev_restate_sdk_clients.SendOpts) opts = args[1];
|
|
1495
|
+
else throw new TypeError("The second argument must be either Opts or SendOpts");
|
|
1496
|
+
break;
|
|
1497
|
+
default: throw new TypeError("unexpected number of arguments");
|
|
1498
|
+
}
|
|
1499
|
+
return {
|
|
1500
|
+
parameter,
|
|
1501
|
+
opts
|
|
1502
|
+
};
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
//#endregion
|
|
1506
|
+
exports.all = all;
|
|
1507
|
+
exports.allSettled = allSettled;
|
|
1508
|
+
exports.any = any;
|
|
1509
|
+
exports.attach = attach;
|
|
1510
|
+
exports.awakeable = awakeable;
|
|
1511
|
+
exports.call = call;
|
|
1512
|
+
exports.cancel = cancel;
|
|
1513
|
+
exports.channel = channel;
|
|
1514
|
+
exports.client = client;
|
|
1515
|
+
Object.defineProperty(exports, 'clients', {
|
|
1516
|
+
enumerable: true,
|
|
1517
|
+
get: function () {
|
|
1518
|
+
return ingress_exports;
|
|
1519
|
+
}
|
|
1520
|
+
});
|
|
1521
|
+
exports.date = date;
|
|
1522
|
+
exports.gen = gen;
|
|
1523
|
+
exports.handlerRequest = handlerRequest;
|
|
1524
|
+
Object.defineProperty(exports, 'iface', {
|
|
1525
|
+
enumerable: true,
|
|
1526
|
+
get: function () {
|
|
1527
|
+
return interface_exports;
|
|
1528
|
+
}
|
|
1529
|
+
});
|
|
1530
|
+
exports.implement = implement;
|
|
1531
|
+
exports.invocation = invocation;
|
|
1532
|
+
exports.logger = logger;
|
|
1533
|
+
exports.object = object;
|
|
1534
|
+
exports.race = race;
|
|
1535
|
+
exports.rand = rand;
|
|
1536
|
+
exports.rejectAwakeable = rejectAwakeable;
|
|
1537
|
+
exports.resolveAwakeable = resolveAwakeable;
|
|
1538
|
+
exports.run = run;
|
|
1539
|
+
exports.schemas = schemas;
|
|
1540
|
+
exports.select = select;
|
|
1541
|
+
exports.send = send;
|
|
1542
|
+
exports.sendClient = sendClient;
|
|
1543
|
+
Object.defineProperty(exports, 'serde', {
|
|
1544
|
+
enumerable: true,
|
|
1545
|
+
get: function () {
|
|
1546
|
+
return __restatedev_restate_sdk_core.serde;
|
|
1547
|
+
}
|
|
1548
|
+
});
|
|
1549
|
+
exports.serdes = serdes;
|
|
1550
|
+
exports.service = service;
|
|
1551
|
+
exports.sharedState = sharedState;
|
|
1552
|
+
exports.signal = signal;
|
|
1553
|
+
exports.sleep = sleep;
|
|
1554
|
+
exports.spawn = spawn;
|
|
1555
|
+
exports.state = state;
|
|
1556
|
+
exports.workflow = workflow;
|
|
1557
|
+
exports.workflowPromise = workflowPromise;
|
|
1558
|
+
exports.wrapActionForCancellation = wrapActionForCancellation;
|