stitchkit 0.77.0 → 0.78.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.
- package/dist/agent-runtime-coding-tools.js +1 -1
- package/dist/agent-runtime-harness.js +6 -6
- package/dist/agent-runtime.js +11 -11
- package/dist/application/decisions.d.ts +28 -25
- package/dist/application/decisions.d.ts.map +1 -1
- package/dist/application/kernel.d.ts +2 -0
- package/dist/application/kernel.d.ts.map +1 -1
- package/dist/application/keyspace.d.ts +11 -1
- package/dist/application/keyspace.d.ts.map +1 -1
- package/dist/application/schedule.d.ts.map +1 -1
- package/dist/application/server-resource.d.ts.map +1 -1
- package/dist/application-diagnostic-journal.d.ts +21 -0
- package/dist/application-diagnostic-journal.d.ts.map +1 -0
- package/dist/application-diagnostic-journal.js +714 -0
- package/dist/application-opentelemetry.js +92 -5
- package/dist/application-schemas.js +13 -98
- package/dist/application.d.ts +1 -2
- package/dist/application.d.ts.map +1 -1
- package/dist/application.js +1775 -858
- package/dist/cli.js +7 -7
- package/dist/contract/index.js +9 -7
- package/dist/files/boundary.d.ts.map +1 -1
- package/dist/files.js +1 -1
- package/dist/{index-3z73fh2c.js → index-1ckavx4h.js} +49 -1
- package/dist/{index-s2rchahr.js → index-31k1ns1s.js} +4 -4
- package/dist/{index-m668wzyc.js → index-4ntp63ps.js} +4 -4
- package/dist/{index-1rxswfbv.js → index-56fm73re.js} +1 -1
- package/dist/{index-ezmn6ac6.js → index-5r0ak8fr.js} +3 -3
- package/dist/{index-f6n5n7nz.js → index-7z69kyrh.js} +10 -87
- package/dist/{index-k2zczx1g.js → index-93xp5tgg.js} +1 -1
- package/dist/{index-bfcpjw20.js → index-actkayzn.js} +7 -3
- package/dist/index-anyqnqcv.js +79 -0
- package/dist/{index-7qy2ex0m.js → index-cwp38wxn.js} +1 -1
- package/dist/{index-35z5h2ty.js → index-dmfn9m4x.js} +125 -4
- package/dist/index-h4pj6fta.js +87 -0
- package/dist/{index-jqtsc9mj.js → index-j6mfaw26.js} +2 -2
- package/dist/{index-58jzmnn4.js → index-k45qqrdh.js} +513 -39
- package/dist/{index-8eywc9zv.js → index-kw12fsbc.js} +0 -1
- package/dist/index-n1g2xm6a.js +203 -0
- package/dist/{index-t8qyvrvg.js → index-n7apa6sy.js} +12 -12
- package/dist/{index-s4c8wy8m.js → index-qaajjfp6.js} +8 -8
- package/dist/{index-nemjkxjp.js → index-s5tn4kdv.js} +14 -10
- package/dist/index-wfq8zerm.js +92 -0
- package/dist/index.js +53 -1215
- package/dist/internal/deadline.d.ts +17 -0
- package/dist/internal/deadline.d.ts.map +1 -0
- package/dist/live.js +12 -89
- package/dist/node.js +5 -6
- package/dist/observability/context.d.ts.map +1 -1
- package/dist/observability/index.js +3 -3
- package/dist/primitives.js +2 -1
- package/dist/remote.js +3 -6
- package/dist/server/event-bus.d.ts.map +1 -1
- package/dist/server/index.js +31 -30
- package/dist/testing.js +1562 -87
- package/dist/tool-invoker.js +6 -6
- package/dist/tools.js +66 -23
- package/llms-full.txt +110 -13
- package/package.json +8 -4
- package/dist/index-3cwck0rm.js +0 -897
- package/dist/index-7b188kmz.js +0 -49
- package/dist/index-a916km3r.js +0 -45
- package/dist/index-vc1b0b1b.js +0 -825
- package/dist/{index-da1aqnhb.js → index-aewy3x5j.js} +3 -3
- package/dist/{index-nt1mp8km.js → index-efm026pw.js} +3 -3
package/dist/index-3cwck0rm.js
DELETED
|
@@ -1,897 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ApplicationIdSchema
|
|
3
|
-
} from "./index-8eywc9zv.js";
|
|
4
|
-
|
|
5
|
-
// src/application/channel.ts
|
|
6
|
-
import { z } from "zod";
|
|
7
|
-
var PositiveSafeIntegerSchema = z.number().int().positive().safe();
|
|
8
|
-
var BoundedChannelPolicySchema = z.enum(["ordered", "latest"]);
|
|
9
|
-
var BoundedChannelStateSchema = z.enum(["open", "draining", "closed", "failed"]);
|
|
10
|
-
var BoundedChannelSnapshotSchema = z.object({
|
|
11
|
-
state: BoundedChannelStateSchema,
|
|
12
|
-
policy: BoundedChannelPolicySchema,
|
|
13
|
-
queuedItems: z.number().int().nonnegative(),
|
|
14
|
-
queuedBytes: z.number().int().nonnegative(),
|
|
15
|
-
waitingReader: z.boolean(),
|
|
16
|
-
offered: z.number().int().nonnegative(),
|
|
17
|
-
delivered: z.number().int().nonnegative(),
|
|
18
|
-
queued: z.number().int().nonnegative(),
|
|
19
|
-
coalesced: z.number().int().nonnegative(),
|
|
20
|
-
refused: z.number().int().nonnegative(),
|
|
21
|
-
discarded: z.number().int().nonnegative()
|
|
22
|
-
}).strict().readonly();
|
|
23
|
-
|
|
24
|
-
class BoundedChannelReaderError extends Error {
|
|
25
|
-
constructor() {
|
|
26
|
-
super("A bounded channel permits only one pending next() call");
|
|
27
|
-
this.name = "BoundedChannelReaderError";
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
function createBoundedChannel(config) {
|
|
31
|
-
const policy = BoundedChannelPolicySchema.parse(config.policy);
|
|
32
|
-
const maxItems = PositiveSafeIntegerSchema.parse(config.maxItems);
|
|
33
|
-
const maxBytes = PositiveSafeIntegerSchema.parse(config.maxBytes);
|
|
34
|
-
const queue = [];
|
|
35
|
-
let state = "open";
|
|
36
|
-
let queuedBytes = 0;
|
|
37
|
-
let waiting;
|
|
38
|
-
let failure;
|
|
39
|
-
let hasFailure = false;
|
|
40
|
-
let offered = 0;
|
|
41
|
-
let delivered = 0;
|
|
42
|
-
let queued = 0;
|
|
43
|
-
let coalesced = 0;
|
|
44
|
-
let refused = 0;
|
|
45
|
-
let discarded = 0;
|
|
46
|
-
const removeAbortListener = () => {
|
|
47
|
-
config.signal?.removeEventListener("abort", abortChannel);
|
|
48
|
-
};
|
|
49
|
-
const snapshot = () => BoundedChannelSnapshotSchema.parse({
|
|
50
|
-
state,
|
|
51
|
-
policy,
|
|
52
|
-
queuedItems: queue.length,
|
|
53
|
-
queuedBytes,
|
|
54
|
-
waitingReader: waiting !== undefined,
|
|
55
|
-
offered,
|
|
56
|
-
delivered,
|
|
57
|
-
queued,
|
|
58
|
-
coalesced,
|
|
59
|
-
refused,
|
|
60
|
-
discarded
|
|
61
|
-
});
|
|
62
|
-
const clearQueue = () => {
|
|
63
|
-
discarded += queue.length;
|
|
64
|
-
queue.length = 0;
|
|
65
|
-
queuedBytes = 0;
|
|
66
|
-
};
|
|
67
|
-
const settleClosed = () => {
|
|
68
|
-
if (state !== "draining" || queue.length > 0)
|
|
69
|
-
return;
|
|
70
|
-
state = "closed";
|
|
71
|
-
removeAbortListener();
|
|
72
|
-
const reader = waiting;
|
|
73
|
-
waiting = undefined;
|
|
74
|
-
reader?.resolve({ done: true, value: undefined });
|
|
75
|
-
};
|
|
76
|
-
const close = (options = {}) => {
|
|
77
|
-
if (state === "closed" || state === "failed")
|
|
78
|
-
return snapshot();
|
|
79
|
-
const mode = options.mode ?? "drain";
|
|
80
|
-
if (mode !== "drain" && mode !== "discard") {
|
|
81
|
-
throw new TypeError("Bounded channel close mode must be drain or discard");
|
|
82
|
-
}
|
|
83
|
-
if (mode === "discard") {
|
|
84
|
-
clearQueue();
|
|
85
|
-
state = "closed";
|
|
86
|
-
removeAbortListener();
|
|
87
|
-
const reader = waiting;
|
|
88
|
-
waiting = undefined;
|
|
89
|
-
reader?.resolve({ done: true, value: undefined });
|
|
90
|
-
return snapshot();
|
|
91
|
-
}
|
|
92
|
-
state = "draining";
|
|
93
|
-
settleClosed();
|
|
94
|
-
return snapshot();
|
|
95
|
-
};
|
|
96
|
-
function abortChannel() {
|
|
97
|
-
close({ mode: "discard" });
|
|
98
|
-
}
|
|
99
|
-
const channel = {
|
|
100
|
-
[Symbol.asyncIterator]() {
|
|
101
|
-
return channel;
|
|
102
|
-
},
|
|
103
|
-
offer(value) {
|
|
104
|
-
offered += 1;
|
|
105
|
-
if (state !== "open") {
|
|
106
|
-
refused += 1;
|
|
107
|
-
return { outcome: "refused", reason: "not-open" };
|
|
108
|
-
}
|
|
109
|
-
const bytes = config.sizeOf(value);
|
|
110
|
-
if (!Number.isSafeInteger(bytes) || bytes < 0) {
|
|
111
|
-
throw new TypeError("Bounded channel sizeOf() must return a non-negative safe integer");
|
|
112
|
-
}
|
|
113
|
-
if (bytes > maxBytes) {
|
|
114
|
-
refused += 1;
|
|
115
|
-
return { outcome: "refused", reason: "item-too-large" };
|
|
116
|
-
}
|
|
117
|
-
const reader = waiting;
|
|
118
|
-
if (reader) {
|
|
119
|
-
waiting = undefined;
|
|
120
|
-
delivered += 1;
|
|
121
|
-
reader.resolve({ done: false, value });
|
|
122
|
-
return { outcome: "delivered" };
|
|
123
|
-
}
|
|
124
|
-
if (policy === "latest" && queue.length > 0) {
|
|
125
|
-
const previous = queue[0];
|
|
126
|
-
if (!previous)
|
|
127
|
-
throw new Error("Bounded latest channel lost its pending value");
|
|
128
|
-
queue[0] = { value, bytes };
|
|
129
|
-
queuedBytes += bytes - previous.bytes;
|
|
130
|
-
coalesced += 1;
|
|
131
|
-
return { outcome: "coalesced", replaced: 1 };
|
|
132
|
-
}
|
|
133
|
-
if (queue.length >= maxItems) {
|
|
134
|
-
refused += 1;
|
|
135
|
-
return { outcome: "refused", reason: "item-capacity" };
|
|
136
|
-
}
|
|
137
|
-
if (queuedBytes + bytes > maxBytes) {
|
|
138
|
-
refused += 1;
|
|
139
|
-
return { outcome: "refused", reason: "byte-capacity" };
|
|
140
|
-
}
|
|
141
|
-
queue.push({ value, bytes });
|
|
142
|
-
queuedBytes += bytes;
|
|
143
|
-
queued += 1;
|
|
144
|
-
return { outcome: "queued" };
|
|
145
|
-
},
|
|
146
|
-
next() {
|
|
147
|
-
if (hasFailure)
|
|
148
|
-
return Promise.reject(failure);
|
|
149
|
-
const next = queue.shift();
|
|
150
|
-
if (next) {
|
|
151
|
-
queuedBytes -= next.bytes;
|
|
152
|
-
delivered += 1;
|
|
153
|
-
settleClosed();
|
|
154
|
-
return Promise.resolve({ done: false, value: next.value });
|
|
155
|
-
}
|
|
156
|
-
if (state === "closed" || state === "draining") {
|
|
157
|
-
settleClosed();
|
|
158
|
-
return Promise.resolve({ done: true, value: undefined });
|
|
159
|
-
}
|
|
160
|
-
if (waiting)
|
|
161
|
-
return Promise.reject(new BoundedChannelReaderError);
|
|
162
|
-
return new Promise((resolve, reject) => {
|
|
163
|
-
waiting = { resolve, reject };
|
|
164
|
-
});
|
|
165
|
-
},
|
|
166
|
-
return() {
|
|
167
|
-
close({ mode: "discard" });
|
|
168
|
-
return Promise.resolve({ done: true, value: undefined });
|
|
169
|
-
},
|
|
170
|
-
throw(error) {
|
|
171
|
-
channel.fail(error);
|
|
172
|
-
return Promise.reject(error);
|
|
173
|
-
},
|
|
174
|
-
close,
|
|
175
|
-
fail(error) {
|
|
176
|
-
if (state === "closed" || state === "failed")
|
|
177
|
-
return snapshot();
|
|
178
|
-
failure = error;
|
|
179
|
-
hasFailure = true;
|
|
180
|
-
state = "failed";
|
|
181
|
-
clearQueue();
|
|
182
|
-
removeAbortListener();
|
|
183
|
-
const reader = waiting;
|
|
184
|
-
waiting = undefined;
|
|
185
|
-
reader?.reject(error);
|
|
186
|
-
return snapshot();
|
|
187
|
-
},
|
|
188
|
-
getSnapshot: snapshot
|
|
189
|
-
};
|
|
190
|
-
if (config.signal?.aborted)
|
|
191
|
-
abortChannel();
|
|
192
|
-
else
|
|
193
|
-
config.signal?.addEventListener("abort", abortChannel, { once: true });
|
|
194
|
-
return channel;
|
|
195
|
-
}
|
|
196
|
-
var CreditWindowSnapshotSchema = z.object({
|
|
197
|
-
state: z.enum(["open", "closed"]),
|
|
198
|
-
capacityBytes: PositiveSafeIntegerSchema,
|
|
199
|
-
availableBytes: z.number().int().nonnegative(),
|
|
200
|
-
leasedBytes: z.number().int().nonnegative(),
|
|
201
|
-
acquired: z.number().int().nonnegative(),
|
|
202
|
-
refused: z.number().int().nonnegative(),
|
|
203
|
-
replenishedBytes: z.number().int().nonnegative(),
|
|
204
|
-
waiting: z.number().int().nonnegative()
|
|
205
|
-
}).strict().readonly();
|
|
206
|
-
function createCreditWindow(config) {
|
|
207
|
-
const capacityBytes = PositiveSafeIntegerSchema.parse(config.capacityBytes);
|
|
208
|
-
let availableBytes = capacityBytes;
|
|
209
|
-
let state = "open";
|
|
210
|
-
let acquired = 0;
|
|
211
|
-
let refused = 0;
|
|
212
|
-
let replenishedBytes = 0;
|
|
213
|
-
const waiters = new Set;
|
|
214
|
-
const snapshot = () => CreditWindowSnapshotSchema.parse({
|
|
215
|
-
state,
|
|
216
|
-
capacityBytes,
|
|
217
|
-
availableBytes,
|
|
218
|
-
leasedBytes: capacityBytes - availableBytes,
|
|
219
|
-
acquired,
|
|
220
|
-
refused,
|
|
221
|
-
replenishedBytes,
|
|
222
|
-
waiting: waiters.size
|
|
223
|
-
});
|
|
224
|
-
const serveWaiters = () => {
|
|
225
|
-
for (const waiter of waiters) {
|
|
226
|
-
if (waiter.bytes > availableBytes)
|
|
227
|
-
return;
|
|
228
|
-
waiter.settle({ outcome: "leased", lease: lease(waiter.bytes) });
|
|
229
|
-
}
|
|
230
|
-
};
|
|
231
|
-
const lease = (requested) => {
|
|
232
|
-
availableBytes -= requested;
|
|
233
|
-
acquired += 1;
|
|
234
|
-
let leaseReleased = false;
|
|
235
|
-
return {
|
|
236
|
-
bytes: requested,
|
|
237
|
-
get released() {
|
|
238
|
-
return leaseReleased;
|
|
239
|
-
},
|
|
240
|
-
release() {
|
|
241
|
-
if (leaseReleased)
|
|
242
|
-
return;
|
|
243
|
-
leaseReleased = true;
|
|
244
|
-
availableBytes += requested;
|
|
245
|
-
replenishedBytes += requested;
|
|
246
|
-
if (availableBytes > capacityBytes) {
|
|
247
|
-
throw new Error("Credit window accounting exceeded its capacity");
|
|
248
|
-
}
|
|
249
|
-
serveWaiters();
|
|
250
|
-
}
|
|
251
|
-
};
|
|
252
|
-
};
|
|
253
|
-
const tryLease = (requested) => {
|
|
254
|
-
if (state === "closed")
|
|
255
|
-
return { outcome: "refused", reason: "closed" };
|
|
256
|
-
if (requested > capacityBytes) {
|
|
257
|
-
return { outcome: "refused", reason: "larger-than-window" };
|
|
258
|
-
}
|
|
259
|
-
if (requested > availableBytes) {
|
|
260
|
-
return { outcome: "refused", reason: "insufficient-credit" };
|
|
261
|
-
}
|
|
262
|
-
return { outcome: "leased", lease: lease(requested) };
|
|
263
|
-
};
|
|
264
|
-
const acquireNow = (requested) => {
|
|
265
|
-
const result = tryLease(requested);
|
|
266
|
-
if (result.outcome === "refused")
|
|
267
|
-
refused += 1;
|
|
268
|
-
return result;
|
|
269
|
-
};
|
|
270
|
-
const acquireWaiting = (requested, options) => {
|
|
271
|
-
const timeoutMs = options.timeoutMs === undefined ? undefined : PositiveSafeIntegerSchema.parse(options.timeoutMs);
|
|
272
|
-
const immediate = tryLease(requested);
|
|
273
|
-
if (immediate.outcome === "leased")
|
|
274
|
-
return Promise.resolve(immediate);
|
|
275
|
-
if (immediate.reason !== "insufficient-credit") {
|
|
276
|
-
refused += 1;
|
|
277
|
-
return Promise.resolve({ outcome: "refused", reason: immediate.reason });
|
|
278
|
-
}
|
|
279
|
-
if (options.signal?.aborted) {
|
|
280
|
-
refused += 1;
|
|
281
|
-
return Promise.resolve({ outcome: "refused", reason: "aborted" });
|
|
282
|
-
}
|
|
283
|
-
return new Promise((resolve) => {
|
|
284
|
-
let settled = false;
|
|
285
|
-
let timer;
|
|
286
|
-
const waiter = {
|
|
287
|
-
bytes: requested,
|
|
288
|
-
settle(result) {
|
|
289
|
-
if (settled)
|
|
290
|
-
return;
|
|
291
|
-
settled = true;
|
|
292
|
-
if (timer !== undefined)
|
|
293
|
-
clearTimeout(timer);
|
|
294
|
-
options.signal?.removeEventListener("abort", onAbort);
|
|
295
|
-
waiters.delete(waiter);
|
|
296
|
-
if (result.outcome === "refused")
|
|
297
|
-
refused += 1;
|
|
298
|
-
resolve(result);
|
|
299
|
-
}
|
|
300
|
-
};
|
|
301
|
-
function onAbort() {
|
|
302
|
-
waiter.settle({ outcome: "refused", reason: "aborted" });
|
|
303
|
-
}
|
|
304
|
-
waiters.add(waiter);
|
|
305
|
-
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
306
|
-
if (timeoutMs !== undefined) {
|
|
307
|
-
timer = setTimeout(() => {
|
|
308
|
-
waiter.settle({ outcome: "refused", reason: "timed-out" });
|
|
309
|
-
}, timeoutMs);
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
|
-
};
|
|
313
|
-
function acquire(bytes, options) {
|
|
314
|
-
const requested = PositiveSafeIntegerSchema.parse(bytes);
|
|
315
|
-
return options === undefined ? acquireNow(requested) : acquireWaiting(requested, options);
|
|
316
|
-
}
|
|
317
|
-
return {
|
|
318
|
-
acquire,
|
|
319
|
-
close() {
|
|
320
|
-
state = "closed";
|
|
321
|
-
for (const waiter of [...waiters]) {
|
|
322
|
-
waiter.settle({ outcome: "refused", reason: "closed" });
|
|
323
|
-
}
|
|
324
|
-
return snapshot();
|
|
325
|
-
},
|
|
326
|
-
getSnapshot: snapshot
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// src/application/latest-sink.ts
|
|
331
|
-
import { z as z2 } from "zod";
|
|
332
|
-
var SnapshotRevisionSchema = z2.number().int().nonnegative();
|
|
333
|
-
var ApplicationSnapshotSinkStatusSchema = z2.object({
|
|
334
|
-
accepting: z2.boolean(),
|
|
335
|
-
received: z2.number().int().nonnegative(),
|
|
336
|
-
accepted: z2.number().int().nonnegative(),
|
|
337
|
-
rejected: z2.number().int().nonnegative(),
|
|
338
|
-
delivered: z2.number().int().nonnegative(),
|
|
339
|
-
coalesced: z2.number().int().nonnegative(),
|
|
340
|
-
failed: z2.number().int().nonnegative(),
|
|
341
|
-
inFlight: z2.boolean(),
|
|
342
|
-
pending: z2.boolean(),
|
|
343
|
-
lastAcceptedRevision: SnapshotRevisionSchema.optional(),
|
|
344
|
-
lastDeliveredRevision: SnapshotRevisionSchema.optional()
|
|
345
|
-
}).strict().readonly();
|
|
346
|
-
function createApplicationSnapshotSink(config) {
|
|
347
|
-
let accepting = true;
|
|
348
|
-
let received = 0;
|
|
349
|
-
let accepted = 0;
|
|
350
|
-
let rejected = 0;
|
|
351
|
-
let delivered = 0;
|
|
352
|
-
let coalesced = 0;
|
|
353
|
-
let failed = 0;
|
|
354
|
-
let lastAcceptedRevision;
|
|
355
|
-
let lastDeliveredRevision;
|
|
356
|
-
let inFlight;
|
|
357
|
-
let closePromise;
|
|
358
|
-
let resolveClose;
|
|
359
|
-
const pending = createBoundedChannel({
|
|
360
|
-
policy: "latest",
|
|
361
|
-
maxItems: 1,
|
|
362
|
-
maxBytes: 1,
|
|
363
|
-
sizeOf: () => 1
|
|
364
|
-
});
|
|
365
|
-
const getStatus = () => ApplicationSnapshotSinkStatusSchema.parse({
|
|
366
|
-
accepting,
|
|
367
|
-
received,
|
|
368
|
-
accepted,
|
|
369
|
-
rejected,
|
|
370
|
-
delivered,
|
|
371
|
-
coalesced,
|
|
372
|
-
failed,
|
|
373
|
-
inFlight: inFlight !== undefined,
|
|
374
|
-
pending: pending.getSnapshot().queuedItems > 0,
|
|
375
|
-
...lastAcceptedRevision !== undefined && { lastAcceptedRevision },
|
|
376
|
-
...lastDeliveredRevision !== undefined && { lastDeliveredRevision }
|
|
377
|
-
});
|
|
378
|
-
const reportFailure = (error, snapshot) => {
|
|
379
|
-
if (!config.onSinkError)
|
|
380
|
-
return;
|
|
381
|
-
Promise.resolve().then(() => config.onSinkError?.({ error, snapshot })).catch(() => {
|
|
382
|
-
return;
|
|
383
|
-
});
|
|
384
|
-
};
|
|
385
|
-
const settleCloseIfIdle = () => {
|
|
386
|
-
if (accepting || inFlight || pending.getSnapshot().queuedItems > 0 || !resolveClose)
|
|
387
|
-
return;
|
|
388
|
-
const resolve = resolveClose;
|
|
389
|
-
resolveClose = undefined;
|
|
390
|
-
resolve(getStatus());
|
|
391
|
-
};
|
|
392
|
-
const startWrite = (snapshot) => {
|
|
393
|
-
const write = Promise.resolve().then(() => config.write(snapshot)).then(() => {
|
|
394
|
-
delivered += 1;
|
|
395
|
-
lastDeliveredRevision = snapshot.revision;
|
|
396
|
-
}).catch((error) => {
|
|
397
|
-
failed += 1;
|
|
398
|
-
reportFailure(error, snapshot);
|
|
399
|
-
}).finally(() => {
|
|
400
|
-
if (pending.getSnapshot().queuedItems > 0) {
|
|
401
|
-
pending.next().then((next) => {
|
|
402
|
-
if (next.done) {
|
|
403
|
-
inFlight = undefined;
|
|
404
|
-
settleCloseIfIdle();
|
|
405
|
-
} else {
|
|
406
|
-
startWrite(next.value);
|
|
407
|
-
}
|
|
408
|
-
});
|
|
409
|
-
} else {
|
|
410
|
-
inFlight = undefined;
|
|
411
|
-
settleCloseIfIdle();
|
|
412
|
-
}
|
|
413
|
-
});
|
|
414
|
-
inFlight = write;
|
|
415
|
-
};
|
|
416
|
-
return {
|
|
417
|
-
publish(snapshot) {
|
|
418
|
-
received += 1;
|
|
419
|
-
const revision = SnapshotRevisionSchema.parse(snapshot.revision);
|
|
420
|
-
if (!accepting || lastAcceptedRevision !== undefined && revision <= lastAcceptedRevision) {
|
|
421
|
-
rejected += 1;
|
|
422
|
-
return false;
|
|
423
|
-
}
|
|
424
|
-
accepted += 1;
|
|
425
|
-
lastAcceptedRevision = revision;
|
|
426
|
-
if (!inFlight) {
|
|
427
|
-
startWrite(snapshot);
|
|
428
|
-
} else {
|
|
429
|
-
const offered = pending.offer(snapshot);
|
|
430
|
-
if (offered.outcome === "coalesced")
|
|
431
|
-
coalesced += offered.replaced;
|
|
432
|
-
}
|
|
433
|
-
return true;
|
|
434
|
-
},
|
|
435
|
-
getStatus,
|
|
436
|
-
close() {
|
|
437
|
-
if (closePromise)
|
|
438
|
-
return closePromise;
|
|
439
|
-
accepting = false;
|
|
440
|
-
pending.close({ mode: "drain" });
|
|
441
|
-
closePromise = new Promise((resolve) => {
|
|
442
|
-
resolveClose = resolve;
|
|
443
|
-
settleCloseIfIdle();
|
|
444
|
-
});
|
|
445
|
-
return closePromise;
|
|
446
|
-
}
|
|
447
|
-
};
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// src/application/activity.ts
|
|
451
|
-
import { z as z3 } from "zod";
|
|
452
|
-
var BoundedOperationalIdSchema = z3.string().min(1).max(64).regex(/^[a-zA-Z0-9][a-zA-Z0-9._:-]*$/);
|
|
453
|
-
var NonNegativeIntegerSchema = z3.number().int().nonnegative();
|
|
454
|
-
var ActivityIdSchema = BoundedOperationalIdSchema;
|
|
455
|
-
var ActivityStageIdSchema = BoundedOperationalIdSchema;
|
|
456
|
-
var ActivityStageSnapshotSchema = z3.object({
|
|
457
|
-
id: ActivityStageIdSchema,
|
|
458
|
-
active: NonNegativeIntegerSchema,
|
|
459
|
-
queued: NonNegativeIntegerSchema,
|
|
460
|
-
completed: NonNegativeIntegerSchema,
|
|
461
|
-
failed: NonNegativeIntegerSchema
|
|
462
|
-
}).strict().readonly();
|
|
463
|
-
var ActivityTotalsSchema = z3.object({
|
|
464
|
-
active: NonNegativeIntegerSchema,
|
|
465
|
-
queued: NonNegativeIntegerSchema,
|
|
466
|
-
completed: NonNegativeIntegerSchema,
|
|
467
|
-
failed: NonNegativeIntegerSchema
|
|
468
|
-
}).strict().readonly();
|
|
469
|
-
var ActivitySnapshotSchema = z3.object({
|
|
470
|
-
id: ActivityIdSchema,
|
|
471
|
-
epoch: z3.string().uuid(),
|
|
472
|
-
revision: NonNegativeIntegerSchema,
|
|
473
|
-
capturedAt: z3.string().datetime({ offset: true }),
|
|
474
|
-
changedAt: z3.string().datetime({ offset: true }),
|
|
475
|
-
stages: z3.array(ActivityStageSnapshotSchema).min(1).max(64).readonly(),
|
|
476
|
-
totals: ActivityTotalsSchema
|
|
477
|
-
}).strict().readonly();
|
|
478
|
-
var ActivityLiveStateSchema = z3.enum(["active", "queued"]);
|
|
479
|
-
var ActivityTokenBrand = Symbol("stitchkit.application.activity");
|
|
480
|
-
function emptyCounters() {
|
|
481
|
-
return { active: 0, queued: 0, completed: 0, failed: 0 };
|
|
482
|
-
}
|
|
483
|
-
function totalsOf(stages) {
|
|
484
|
-
return stages.reduce((totals, stage) => ({
|
|
485
|
-
active: totals.active + stage.active,
|
|
486
|
-
queued: totals.queued + stage.queued,
|
|
487
|
-
completed: totals.completed + stage.completed,
|
|
488
|
-
failed: totals.failed + stage.failed
|
|
489
|
-
}), emptyCounters());
|
|
490
|
-
}
|
|
491
|
-
function createActivityProjection(config) {
|
|
492
|
-
const id = ActivityIdSchema.parse(config.id);
|
|
493
|
-
const declaredStages = z3.array(ActivityStageIdSchema).min(1).max(64).parse(config.stages);
|
|
494
|
-
const uniqueStages = new Set(declaredStages);
|
|
495
|
-
if (uniqueStages.size !== declaredStages.length) {
|
|
496
|
-
throw new Error("[stitchkit] createActivityProjection: stage ids must be unique");
|
|
497
|
-
}
|
|
498
|
-
const epoch = z3.string().uuid().parse(config.epoch ?? crypto.randomUUID());
|
|
499
|
-
const now = config.now ?? (() => new Date);
|
|
500
|
-
const counters = new Map(declaredStages.map((stage) => [stage, emptyCounters()]));
|
|
501
|
-
const activities = new WeakMap;
|
|
502
|
-
const subscribers = new Set;
|
|
503
|
-
let revision = 0;
|
|
504
|
-
let changedAt = now().toISOString();
|
|
505
|
-
const countersFor = (stage) => {
|
|
506
|
-
const value = counters.get(stage);
|
|
507
|
-
if (!value) {
|
|
508
|
-
throw new Error(`[stitchkit] createActivityProjection: undeclared stage "${stage}"`);
|
|
509
|
-
}
|
|
510
|
-
return value;
|
|
511
|
-
};
|
|
512
|
-
const getSnapshot = () => {
|
|
513
|
-
const stages = declaredStages.map((stage) => ({ id: stage, ...countersFor(stage) }));
|
|
514
|
-
return ActivitySnapshotSchema.parse({
|
|
515
|
-
id,
|
|
516
|
-
epoch,
|
|
517
|
-
revision,
|
|
518
|
-
capturedAt: now().toISOString(),
|
|
519
|
-
changedAt,
|
|
520
|
-
stages,
|
|
521
|
-
totals: totalsOf(stages)
|
|
522
|
-
});
|
|
523
|
-
};
|
|
524
|
-
const publishChange = () => {
|
|
525
|
-
revision += 1;
|
|
526
|
-
changedAt = now().toISOString();
|
|
527
|
-
const snapshot = getSnapshot();
|
|
528
|
-
for (const subscriber of subscribers)
|
|
529
|
-
subscriber.publish(snapshot);
|
|
530
|
-
};
|
|
531
|
-
const requireActivity = (token) => {
|
|
532
|
-
const activity = activities.get(token);
|
|
533
|
-
if (!activity) {
|
|
534
|
-
throw new Error("[stitchkit] activity token belongs to another projection");
|
|
535
|
-
}
|
|
536
|
-
return activity;
|
|
537
|
-
};
|
|
538
|
-
const leaveLiveState = (activity) => {
|
|
539
|
-
if (activity.state === "active" || activity.state === "queued") {
|
|
540
|
-
countersFor(activity.stage)[activity.state] -= 1;
|
|
541
|
-
}
|
|
542
|
-
};
|
|
543
|
-
const settle = (token, terminal) => {
|
|
544
|
-
const activity = requireActivity(token);
|
|
545
|
-
if (activity.state === "completed" || activity.state === "failed")
|
|
546
|
-
return false;
|
|
547
|
-
leaveLiveState(activity);
|
|
548
|
-
countersFor(activity.stage)[terminal] += 1;
|
|
549
|
-
activity.state = terminal;
|
|
550
|
-
publishChange();
|
|
551
|
-
return true;
|
|
552
|
-
};
|
|
553
|
-
return {
|
|
554
|
-
open(stage, rawState = "active") {
|
|
555
|
-
const parsedStage = ActivityStageIdSchema.parse(stage);
|
|
556
|
-
const state = ActivityLiveStateSchema.parse(rawState);
|
|
557
|
-
countersFor(parsedStage)[state] += 1;
|
|
558
|
-
const token = Object.freeze({
|
|
559
|
-
get [ActivityTokenBrand]() {
|
|
560
|
-
return true;
|
|
561
|
-
}
|
|
562
|
-
});
|
|
563
|
-
activities.set(token, { stage: parsedStage, state });
|
|
564
|
-
publishChange();
|
|
565
|
-
return token;
|
|
566
|
-
},
|
|
567
|
-
transition(token, input) {
|
|
568
|
-
const activity = requireActivity(token);
|
|
569
|
-
if (activity.state === "completed" || activity.state === "failed") {
|
|
570
|
-
throw new Error("[stitchkit] cannot transition a terminal activity");
|
|
571
|
-
}
|
|
572
|
-
const stage = ActivityStageIdSchema.parse(input.stage);
|
|
573
|
-
const state = ActivityLiveStateSchema.parse(input.state);
|
|
574
|
-
countersFor(stage);
|
|
575
|
-
if (activity.stage === stage && activity.state === state)
|
|
576
|
-
return false;
|
|
577
|
-
leaveLiveState(activity);
|
|
578
|
-
countersFor(stage)[state] += 1;
|
|
579
|
-
activity.stage = stage;
|
|
580
|
-
activity.state = state;
|
|
581
|
-
publishChange();
|
|
582
|
-
return true;
|
|
583
|
-
},
|
|
584
|
-
complete: (token) => settle(token, "completed"),
|
|
585
|
-
fail: (token) => settle(token, "failed"),
|
|
586
|
-
getSnapshot,
|
|
587
|
-
subscribe(listener) {
|
|
588
|
-
const sink = createApplicationSnapshotSink({
|
|
589
|
-
write: listener,
|
|
590
|
-
...config.onSubscriberError && { onSinkError: config.onSubscriberError }
|
|
591
|
-
});
|
|
592
|
-
subscribers.add(sink);
|
|
593
|
-
sink.publish(getSnapshot());
|
|
594
|
-
return () => {
|
|
595
|
-
subscribers.delete(sink);
|
|
596
|
-
sink.close();
|
|
597
|
-
};
|
|
598
|
-
}
|
|
599
|
-
};
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
// src/application/schedule.ts
|
|
603
|
-
import { z as z4 } from "zod";
|
|
604
|
-
var NonNegativeSafeIntegerSchema = z4.number().int().nonnegative().refine(Number.isSafeInteger, "Expected a safe integer");
|
|
605
|
-
var PositiveSafeIntegerSchema2 = NonNegativeSafeIntegerSchema.refine((value) => value > 0, "Expected a positive integer");
|
|
606
|
-
var ManagedScheduleOverlapSchema = z4.discriminatedUnion("mode", [
|
|
607
|
-
z4.object({ mode: z4.literal("skip") }).readonly(),
|
|
608
|
-
z4.object({ mode: z4.literal("queue-one") }).readonly(),
|
|
609
|
-
z4.object({
|
|
610
|
-
mode: z4.literal("parallel"),
|
|
611
|
-
maxConcurrent: PositiveSafeIntegerSchema2
|
|
612
|
-
}).readonly()
|
|
613
|
-
]);
|
|
614
|
-
var ManagedScheduleErrorPolicySchema = z4.enum(["continue", "stop-schedule"]);
|
|
615
|
-
var ManagedScheduleDescriptorSchema = z4.object({
|
|
616
|
-
id: ApplicationIdSchema,
|
|
617
|
-
everyMs: PositiveSafeIntegerSchema2,
|
|
618
|
-
startAfterMs: NonNegativeSafeIntegerSchema,
|
|
619
|
-
overlap: ManagedScheduleOverlapSchema,
|
|
620
|
-
errorPolicy: ManagedScheduleErrorPolicySchema
|
|
621
|
-
}).readonly();
|
|
622
|
-
var ManagedScheduleStatusSchema = z4.object({
|
|
623
|
-
descriptor: ManagedScheduleDescriptorSchema,
|
|
624
|
-
state: z4.enum(["inactive", "scheduled", "running", "draining", "stopped"]),
|
|
625
|
-
revision: NonNegativeSafeIntegerSchema,
|
|
626
|
-
capturedAt: z4.string().datetime({ offset: true }),
|
|
627
|
-
changedAt: z4.string().datetime({ offset: true }),
|
|
628
|
-
accepting: z4.boolean(),
|
|
629
|
-
active: NonNegativeSafeIntegerSchema,
|
|
630
|
-
queued: z4.boolean(),
|
|
631
|
-
runsStarted: NonNegativeSafeIntegerSchema,
|
|
632
|
-
runsCompleted: NonNegativeSafeIntegerSchema,
|
|
633
|
-
runsFailed: NonNegativeSafeIntegerSchema,
|
|
634
|
-
ticksSkipped: NonNegativeSafeIntegerSchema,
|
|
635
|
-
nextRunAt: z4.string().datetime({ offset: true }).nullable(),
|
|
636
|
-
lastScheduledAt: z4.string().datetime({ offset: true }).nullable(),
|
|
637
|
-
lastStartedAt: z4.string().datetime({ offset: true }).nullable(),
|
|
638
|
-
lastFinishedAt: z4.string().datetime({ offset: true }).nullable()
|
|
639
|
-
}).readonly();
|
|
640
|
-
var systemClock = {
|
|
641
|
-
now: () => performance.now(),
|
|
642
|
-
wallNow: () => new Date,
|
|
643
|
-
schedule(callback, delayMs) {
|
|
644
|
-
const timer = setTimeout(callback, delayMs);
|
|
645
|
-
return { cancel: () => clearTimeout(timer) };
|
|
646
|
-
}
|
|
647
|
-
};
|
|
648
|
-
var DEFAULT_OVERLAP = { mode: "skip" };
|
|
649
|
-
function createManagedSchedule(config) {
|
|
650
|
-
const descriptor = ManagedScheduleDescriptorSchema.parse({
|
|
651
|
-
id: config.id,
|
|
652
|
-
everyMs: config.everyMs,
|
|
653
|
-
startAfterMs: config.startAfterMs ?? config.everyMs,
|
|
654
|
-
overlap: config.overlap ?? DEFAULT_OVERLAP,
|
|
655
|
-
errorPolicy: config.errorPolicy ?? "continue"
|
|
656
|
-
});
|
|
657
|
-
const dependsOn = config.dependsOn?.map((id) => ApplicationIdSchema.parse(id));
|
|
658
|
-
const clock = config.clock ?? systemClock;
|
|
659
|
-
let revision = 0;
|
|
660
|
-
let accepting = false;
|
|
661
|
-
let activated = false;
|
|
662
|
-
let stopped = false;
|
|
663
|
-
let timer = null;
|
|
664
|
-
let activationContext = null;
|
|
665
|
-
let nextRunAt = null;
|
|
666
|
-
let queuedAt = null;
|
|
667
|
-
let runsStarted = 0;
|
|
668
|
-
let runsCompleted = 0;
|
|
669
|
-
let runsFailed = 0;
|
|
670
|
-
let ticksSkipped = 0;
|
|
671
|
-
let lastScheduledAt = null;
|
|
672
|
-
let lastStartedAt = null;
|
|
673
|
-
let lastFinishedAt = null;
|
|
674
|
-
let changedAt = clock.wallNow().toISOString();
|
|
675
|
-
const active = new Set;
|
|
676
|
-
const changed = () => {
|
|
677
|
-
revision += 1;
|
|
678
|
-
changedAt = clock.wallNow().toISOString();
|
|
679
|
-
};
|
|
680
|
-
const cancelFuture = () => {
|
|
681
|
-
timer?.cancel();
|
|
682
|
-
timer = null;
|
|
683
|
-
nextRunAt = null;
|
|
684
|
-
queuedAt = null;
|
|
685
|
-
};
|
|
686
|
-
const stopAdmission = () => {
|
|
687
|
-
if (!accepting && stopped)
|
|
688
|
-
return;
|
|
689
|
-
accepting = false;
|
|
690
|
-
stopped = true;
|
|
691
|
-
cancelFuture();
|
|
692
|
-
changed();
|
|
693
|
-
};
|
|
694
|
-
const reportError = (error, context) => {
|
|
695
|
-
if (!config.onError)
|
|
696
|
-
return;
|
|
697
|
-
Promise.resolve().then(() => config.onError?.(error, context)).catch(() => {});
|
|
698
|
-
};
|
|
699
|
-
const state = () => {
|
|
700
|
-
if (!activated)
|
|
701
|
-
return stopped ? "stopped" : "inactive";
|
|
702
|
-
if (!accepting)
|
|
703
|
-
return active.size > 0 ? "draining" : "stopped";
|
|
704
|
-
return active.size > 0 ? "running" : "scheduled";
|
|
705
|
-
};
|
|
706
|
-
const wallAt = (monotonicAt, anchor) => {
|
|
707
|
-
if (monotonicAt === null)
|
|
708
|
-
return null;
|
|
709
|
-
return new Date(anchor.wallNow + monotonicAt - anchor.monotonicNow).toISOString();
|
|
710
|
-
};
|
|
711
|
-
const status = () => {
|
|
712
|
-
const captured = clock.wallNow();
|
|
713
|
-
const anchor = { monotonicNow: clock.now(), wallNow: captured.getTime() };
|
|
714
|
-
return ManagedScheduleStatusSchema.parse({
|
|
715
|
-
descriptor,
|
|
716
|
-
state: state(),
|
|
717
|
-
revision,
|
|
718
|
-
capturedAt: captured.toISOString(),
|
|
719
|
-
changedAt,
|
|
720
|
-
accepting,
|
|
721
|
-
active: active.size,
|
|
722
|
-
queued: queuedAt !== null,
|
|
723
|
-
runsStarted,
|
|
724
|
-
runsCompleted,
|
|
725
|
-
runsFailed,
|
|
726
|
-
ticksSkipped,
|
|
727
|
-
nextRunAt: wallAt(nextRunAt, anchor),
|
|
728
|
-
lastScheduledAt,
|
|
729
|
-
lastStartedAt,
|
|
730
|
-
lastFinishedAt
|
|
731
|
-
});
|
|
732
|
-
};
|
|
733
|
-
let startExecution = () => {
|
|
734
|
-
return;
|
|
735
|
-
};
|
|
736
|
-
const settleExecution = () => {
|
|
737
|
-
if (!accepting || queuedAt === null || active.size !== 0)
|
|
738
|
-
return;
|
|
739
|
-
const successorAt = queuedAt;
|
|
740
|
-
queuedAt = null;
|
|
741
|
-
changed();
|
|
742
|
-
startExecution(successorAt);
|
|
743
|
-
};
|
|
744
|
-
startExecution = (scheduledAt) => {
|
|
745
|
-
const resourceContext = activationContext;
|
|
746
|
-
if (!accepting || !resourceContext)
|
|
747
|
-
return;
|
|
748
|
-
const startedAt = clock.now();
|
|
749
|
-
const wallStartedAt = clock.wallNow().getTime();
|
|
750
|
-
const runContext = {
|
|
751
|
-
applicationId: resourceContext.applicationId,
|
|
752
|
-
signal: resourceContext.signal,
|
|
753
|
-
scheduledAt,
|
|
754
|
-
startedAt,
|
|
755
|
-
now: () => clock.now()
|
|
756
|
-
};
|
|
757
|
-
runsStarted += 1;
|
|
758
|
-
lastScheduledAt = new Date(wallStartedAt + scheduledAt - startedAt).toISOString();
|
|
759
|
-
lastStartedAt = new Date(wallStartedAt).toISOString();
|
|
760
|
-
changed();
|
|
761
|
-
let tracked;
|
|
762
|
-
tracked = Promise.resolve().then(() => config.run(runContext)).then(() => {
|
|
763
|
-
runsCompleted += 1;
|
|
764
|
-
lastFinishedAt = clock.wallNow().toISOString();
|
|
765
|
-
if (descriptor.errorPolicy === "continue" && accepting) {
|
|
766
|
-
resourceContext.reportHealth("healthy");
|
|
767
|
-
}
|
|
768
|
-
}, (error) => {
|
|
769
|
-
runsFailed += 1;
|
|
770
|
-
lastFinishedAt = clock.wallNow().toISOString();
|
|
771
|
-
reportError(error, runContext);
|
|
772
|
-
if (descriptor.errorPolicy === "stop-schedule") {
|
|
773
|
-
resourceContext.reportHealth("unhealthy");
|
|
774
|
-
stopAdmission();
|
|
775
|
-
} else {
|
|
776
|
-
resourceContext.reportHealth("degraded");
|
|
777
|
-
}
|
|
778
|
-
}).finally(() => {
|
|
779
|
-
active.delete(tracked);
|
|
780
|
-
changed();
|
|
781
|
-
settleExecution();
|
|
782
|
-
});
|
|
783
|
-
active.add(tracked);
|
|
784
|
-
};
|
|
785
|
-
const dispatchTick = (scheduledAt) => {
|
|
786
|
-
if (!accepting)
|
|
787
|
-
return;
|
|
788
|
-
if (descriptor.overlap.mode === "skip") {
|
|
789
|
-
if (active.size > 0) {
|
|
790
|
-
ticksSkipped += 1;
|
|
791
|
-
changed();
|
|
792
|
-
return;
|
|
793
|
-
}
|
|
794
|
-
startExecution(scheduledAt);
|
|
795
|
-
return;
|
|
796
|
-
}
|
|
797
|
-
if (descriptor.overlap.mode === "queue-one") {
|
|
798
|
-
if (active.size > 0) {
|
|
799
|
-
queuedAt = scheduledAt;
|
|
800
|
-
changed();
|
|
801
|
-
return;
|
|
802
|
-
}
|
|
803
|
-
startExecution(scheduledAt);
|
|
804
|
-
return;
|
|
805
|
-
}
|
|
806
|
-
if (active.size >= descriptor.overlap.maxConcurrent) {
|
|
807
|
-
ticksSkipped += 1;
|
|
808
|
-
changed();
|
|
809
|
-
return;
|
|
810
|
-
}
|
|
811
|
-
startExecution(scheduledAt);
|
|
812
|
-
};
|
|
813
|
-
const arm = () => {
|
|
814
|
-
if (!accepting || nextRunAt === null)
|
|
815
|
-
return;
|
|
816
|
-
const delayMs = Math.max(0, nextRunAt - clock.now());
|
|
817
|
-
timer = clock.schedule(() => {
|
|
818
|
-
timer = null;
|
|
819
|
-
if (!accepting || nextRunAt === null)
|
|
820
|
-
return;
|
|
821
|
-
const scheduledAt = nextRunAt;
|
|
822
|
-
const observedAt = clock.now();
|
|
823
|
-
const elapsedIntervals = Math.max(1, Math.floor((observedAt - scheduledAt) / descriptor.everyMs) + 1);
|
|
824
|
-
if (elapsedIntervals > 1) {
|
|
825
|
-
ticksSkipped += elapsedIntervals - 1;
|
|
826
|
-
changed();
|
|
827
|
-
}
|
|
828
|
-
nextRunAt = scheduledAt + elapsedIntervals * descriptor.everyMs;
|
|
829
|
-
arm();
|
|
830
|
-
dispatchTick(scheduledAt);
|
|
831
|
-
}, delayMs);
|
|
832
|
-
};
|
|
833
|
-
const waitForActive = async (context, deadlineAt) => {
|
|
834
|
-
if (active.size === 0 || context.signal.aborted)
|
|
835
|
-
return;
|
|
836
|
-
await new Promise((resolve) => {
|
|
837
|
-
let settled = false;
|
|
838
|
-
let deadlineTimer = null;
|
|
839
|
-
const finish = () => {
|
|
840
|
-
if (settled)
|
|
841
|
-
return;
|
|
842
|
-
settled = true;
|
|
843
|
-
deadlineTimer?.cancel();
|
|
844
|
-
context.signal.removeEventListener("abort", finish);
|
|
845
|
-
resolve();
|
|
846
|
-
};
|
|
847
|
-
context.signal.addEventListener("abort", finish, { once: true });
|
|
848
|
-
if (deadlineAt !== undefined) {
|
|
849
|
-
deadlineTimer = clock.schedule(finish, Math.max(0, deadlineAt - context.now()));
|
|
850
|
-
}
|
|
851
|
-
Promise.allSettled([...active]).then(finish);
|
|
852
|
-
});
|
|
853
|
-
};
|
|
854
|
-
const drain = async (context) => {
|
|
855
|
-
stopAdmission();
|
|
856
|
-
await waitForActive(context, context.deadlineAt);
|
|
857
|
-
};
|
|
858
|
-
return {
|
|
859
|
-
id: descriptor.id,
|
|
860
|
-
...dependsOn && { dependsOn },
|
|
861
|
-
...config.required !== undefined && { required: config.required },
|
|
862
|
-
get status() {
|
|
863
|
-
return status();
|
|
864
|
-
},
|
|
865
|
-
start() {
|
|
866
|
-
if (stopped)
|
|
867
|
-
throw new Error(`[stitchkit] schedule "${descriptor.id}" is stopped`);
|
|
868
|
-
},
|
|
869
|
-
activate(context) {
|
|
870
|
-
if (activated || stopped)
|
|
871
|
-
return;
|
|
872
|
-
activated = true;
|
|
873
|
-
accepting = true;
|
|
874
|
-
activationContext = context;
|
|
875
|
-
nextRunAt = clock.now() + descriptor.startAfterMs;
|
|
876
|
-
context.reportHealth("healthy");
|
|
877
|
-
changed();
|
|
878
|
-
arm();
|
|
879
|
-
},
|
|
880
|
-
stopAdmission() {
|
|
881
|
-
stopAdmission();
|
|
882
|
-
},
|
|
883
|
-
drain,
|
|
884
|
-
close() {
|
|
885
|
-
stopAdmission();
|
|
886
|
-
},
|
|
887
|
-
async force(context) {
|
|
888
|
-
stopAdmission();
|
|
889
|
-
await waitForActive(context, context.forceDeadlineAt);
|
|
890
|
-
if (active.size > 0) {
|
|
891
|
-
throw new Error(`[stitchkit] schedule "${descriptor.id}" still has active executions`);
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
};
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
export { BoundedChannelPolicySchema, BoundedChannelStateSchema, BoundedChannelSnapshotSchema, BoundedChannelReaderError, createBoundedChannel, CreditWindowSnapshotSchema, createCreditWindow, ApplicationSnapshotSinkStatusSchema, createApplicationSnapshotSink, ActivityIdSchema, ActivityStageIdSchema, ActivityStageSnapshotSchema, ActivitySnapshotSchema, ActivityLiveStateSchema, ActivityTokenBrand, createActivityProjection, ManagedScheduleOverlapSchema, ManagedScheduleErrorPolicySchema, ManagedScheduleDescriptorSchema, ManagedScheduleStatusSchema, createManagedSchedule };
|