@workser/runtime 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +71 -2
- package/dist/cjs/client/follow.js +103 -0
- package/dist/cjs/client/index.js +6 -0
- package/dist/cjs/contract/actions.js +223 -0
- package/dist/cjs/contract/catalog.js +335 -0
- package/dist/cjs/contract/index.js +20 -0
- package/dist/cjs/contract/ordered-feed.js +94 -0
- package/dist/cjs/contract/types.js +9 -0
- package/dist/cjs/engine/engine.js +2 -0
- package/dist/cjs/index.js +27 -0
- package/dist/cjs/internal/async-queue.js +56 -0
- package/dist/cjs/internal/sse.js +170 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/place/place.js +2 -0
- package/dist/cjs/runner/index.js +6 -0
- package/dist/cjs/runner/journal.js +82 -0
- package/dist/cjs/runner/runner-client.js +477 -0
- package/dist/client/follow.d.ts +45 -0
- package/dist/client/follow.js +100 -0
- package/dist/client/follow.js.map +1 -0
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.js +2 -0
- package/dist/client/index.js.map +1 -0
- package/dist/contract/actions.d.ts +73 -0
- package/dist/contract/actions.js +217 -0
- package/dist/contract/actions.js.map +1 -0
- package/dist/contract/catalog.d.ts +97 -0
- package/dist/contract/catalog.js +328 -0
- package/dist/contract/catalog.js.map +1 -0
- package/dist/contract/index.d.ts +4 -0
- package/dist/contract/index.js +5 -0
- package/dist/contract/index.js.map +1 -0
- package/dist/contract/ordered-feed.d.ts +56 -0
- package/dist/contract/ordered-feed.js +91 -0
- package/dist/contract/ordered-feed.js.map +1 -0
- package/dist/contract/types.d.ts +311 -0
- package/dist/contract/types.js +9 -0
- package/dist/contract/types.js.map +1 -0
- package/dist/engine/engine.d.ts +39 -0
- package/dist/engine/engine.js +2 -0
- package/dist/engine/engine.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/async-queue.d.ts +14 -0
- package/dist/internal/async-queue.js +53 -0
- package/dist/internal/async-queue.js.map +1 -0
- package/dist/internal/sse.d.ts +57 -0
- package/dist/internal/sse.js +164 -0
- package/dist/internal/sse.js.map +1 -0
- package/dist/place/place.d.ts +81 -0
- package/dist/place/place.js +2 -0
- package/dist/place/place.js.map +1 -0
- package/dist/runner/index.d.ts +2 -0
- package/dist/runner/index.js +2 -0
- package/dist/runner/index.js.map +1 -0
- package/dist/runner/journal.d.ts +32 -0
- package/dist/runner/journal.js +79 -0
- package/dist/runner/journal.js.map +1 -0
- package/dist/runner/runner-client.d.ts +185 -0
- package/dist/runner/runner-client.js +473 -0
- package/dist/runner/runner-client.js.map +1 -0
- package/package.json +30 -4
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The run event catalog: every event a runner may send, and what it must
|
|
3
|
+
* carry. Pure and dependency-free: this file is the source of truth in
|
|
4
|
+
* `@workser/runtime`, and core-api keeps a generated copy of it (plan Phase 0).
|
|
5
|
+
*
|
|
6
|
+
* TWO GATES, ON PURPOSE
|
|
7
|
+
* ---------------------
|
|
8
|
+
* - A runner (`v1/runner/runs/:id/events`) is strict: an unknown type or a
|
|
9
|
+
* malformed payload is refused, so a screen never has to guess what an event
|
|
10
|
+
* means.
|
|
11
|
+
* - The service engine (`internal/runs/:id/events`) is lenient: it already
|
|
12
|
+
* sends ~30 types, some built at runtime, and refusing one would lose a live
|
|
13
|
+
* run's history. Unknown types are accepted and counted (`unknownLegacyType`)
|
|
14
|
+
* so the catalog can grow to cover them before that gate tightens.
|
|
15
|
+
*/
|
|
16
|
+
export const RUNNER_PROTOCOL_VERSION = 1;
|
|
17
|
+
/** Largest event payload we keep, serialised. Bigger outputs go to files. */
|
|
18
|
+
export const MAX_EVENT_DATA_BYTES = 64 * 1024;
|
|
19
|
+
/** Largest batch a runner may send in one call. */
|
|
20
|
+
export const MAX_EVENT_BATCH = 100;
|
|
21
|
+
const text = (maxLength = 20_000, required = true) => ({
|
|
22
|
+
type: 'string',
|
|
23
|
+
required,
|
|
24
|
+
maxLength,
|
|
25
|
+
});
|
|
26
|
+
const id = (required = true) => text(120, required);
|
|
27
|
+
/**
|
|
28
|
+
* What each event means, for the people reading it on a screen:
|
|
29
|
+
*
|
|
30
|
+
* - run.* the run's life. Terminal ones end the stream.
|
|
31
|
+
* - step.* one thing the agent did (a command, an edit, a tool call).
|
|
32
|
+
* - message* what the agent said; `.delta` streams it as it is written.
|
|
33
|
+
* - file/diff what changed on disk.
|
|
34
|
+
* - check.result a done-check (tests, typecheck, deck validates, numbers match).
|
|
35
|
+
* - preview.ready a URL the owner can open.
|
|
36
|
+
* - handoff.* a sub-job this run asked for started or ended (core-api
|
|
37
|
+
* writes these on the parent; `run.completed.result` is the
|
|
38
|
+
* child's typed answer).
|
|
39
|
+
* - usage tokens and cost as the runner saw them; billing uses the
|
|
40
|
+
* gateway's own figure, never this one.
|
|
41
|
+
* - log plain lines for the details view.
|
|
42
|
+
*/
|
|
43
|
+
export const RUN_EVENT_CATALOG = {
|
|
44
|
+
'run.started': { engine: text(80, false), place: text(40, false) },
|
|
45
|
+
'run.paused': { reason: text(500, false) },
|
|
46
|
+
'run.resumed': {},
|
|
47
|
+
// `result`: the typed answer a handoff's parent asked for (`Handoff.expected`).
|
|
48
|
+
'run.completed': { summary: text(20_000, false), result: { type: 'object' } },
|
|
49
|
+
'run.failed': { error_kind: text(80, false), message: text(4_000, false) },
|
|
50
|
+
'run.cancelled': { reason: text(500, false) },
|
|
51
|
+
// A person stopped the job before it read some messages sent into it (its
|
|
52
|
+
// unacked `message` commands). Core writes it on Stop; nothing new starts.
|
|
53
|
+
// `messages`: [{ pos, text (≤500 chars), author (name or null) }], oldest first.
|
|
54
|
+
'run.messages_unseen': {
|
|
55
|
+
count: { type: 'number', required: true },
|
|
56
|
+
messages: { type: 'array', required: true },
|
|
57
|
+
},
|
|
58
|
+
// A session-mode job finished a turn and is waiting for the next message.
|
|
59
|
+
'run.idle': { summary: text(20_000, false), turn: { type: 'number' } },
|
|
60
|
+
// A reply turned into a job (reply-or-job contract §2c): the AI agent called
|
|
61
|
+
// `begin_job`, or the safety net saw an action, a computer, a fan-out, a
|
|
62
|
+
// helper or a long run. Core makes it a job once (migration 300).
|
|
63
|
+
'run.became_job': {
|
|
64
|
+
reason: { type: 'string', required: true, oneOf: ['begin_job', 'action', 'computer', 'fan_out', 'long', 'helper', 'owner'] },
|
|
65
|
+
title: text(500, false),
|
|
66
|
+
eta: text(200, false),
|
|
67
|
+
},
|
|
68
|
+
// The engine's own session, so a later runner can reattach to it
|
|
69
|
+
// (core-api puts the latest into the brief as `run.resume`).
|
|
70
|
+
'engine.session': { engine: text(80), session_id: text(200), workdir: text(1_000) },
|
|
71
|
+
// The agent is waiting on a person (`owner`) or on an approval it raised.
|
|
72
|
+
// `run.resumed` says it carried on.
|
|
73
|
+
'run.waiting': {
|
|
74
|
+
on: { type: 'string', required: true, oneOf: ['owner', 'approval'] },
|
|
75
|
+
ask_id: id(false),
|
|
76
|
+
},
|
|
77
|
+
'step.started': {
|
|
78
|
+
step_id: id(),
|
|
79
|
+
title: text(500),
|
|
80
|
+
tool: text(120, false),
|
|
81
|
+
input: { type: 'object' },
|
|
82
|
+
// The sub-job this step handed work to, and the step it is nested under.
|
|
83
|
+
child_run_id: id(false),
|
|
84
|
+
parent_step_id: id(false),
|
|
85
|
+
},
|
|
86
|
+
// Live output of a long step. Throttle to about one a second per step.
|
|
87
|
+
'step.progress': {
|
|
88
|
+
step_id: id(),
|
|
89
|
+
text: text(2_000, false),
|
|
90
|
+
pct: { type: 'number' },
|
|
91
|
+
stdout_tail: text(2_000, false),
|
|
92
|
+
},
|
|
93
|
+
// The agent's to-do list changed (`write_todos`): the whole list, every time.
|
|
94
|
+
'plan.updated': {
|
|
95
|
+
todos: { type: 'array', required: true },
|
|
96
|
+
},
|
|
97
|
+
'step.completed': {
|
|
98
|
+
step_id: id(),
|
|
99
|
+
status: { type: 'string', required: true, oneOf: ['ok', 'error', 'skipped'] },
|
|
100
|
+
output: { type: 'object' },
|
|
101
|
+
error: text(4_000, false),
|
|
102
|
+
duration_ms: { type: 'number' },
|
|
103
|
+
},
|
|
104
|
+
message: {
|
|
105
|
+
message_id: id(false),
|
|
106
|
+
role: {
|
|
107
|
+
type: 'string',
|
|
108
|
+
required: true,
|
|
109
|
+
oneOf: ['assistant', 'user', 'system'],
|
|
110
|
+
},
|
|
111
|
+
text: text(),
|
|
112
|
+
},
|
|
113
|
+
'message.delta': { message_id: id(), text: text() },
|
|
114
|
+
'file.changed': {
|
|
115
|
+
path: text(1_000),
|
|
116
|
+
change: {
|
|
117
|
+
type: 'string',
|
|
118
|
+
required: true,
|
|
119
|
+
oneOf: ['added', 'modified', 'deleted', 'renamed'],
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
'diff.ready': {
|
|
123
|
+
files: { type: 'number', required: true },
|
|
124
|
+
additions: { type: 'number' },
|
|
125
|
+
deletions: { type: 'number' },
|
|
126
|
+
},
|
|
127
|
+
'check.result': {
|
|
128
|
+
name: text(200),
|
|
129
|
+
status: {
|
|
130
|
+
type: 'string',
|
|
131
|
+
required: true,
|
|
132
|
+
oneOf: ['pass', 'fail', 'skip'],
|
|
133
|
+
},
|
|
134
|
+
detail: text(8_000, false),
|
|
135
|
+
},
|
|
136
|
+
'preview.ready': { url: text(2_000) },
|
|
137
|
+
// Written by core-api on the PARENT when a handoff starts and ends.
|
|
138
|
+
'handoff.created': { child_run_id: id(), goal: text(2_000, false) },
|
|
139
|
+
'handoff.finished': {
|
|
140
|
+
child_run_id: id(),
|
|
141
|
+
status: {
|
|
142
|
+
type: 'string',
|
|
143
|
+
required: true,
|
|
144
|
+
oneOf: ['completed', 'failed', 'cancelled'],
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
usage: {
|
|
148
|
+
model: text(200, false),
|
|
149
|
+
tokens_in: { type: 'number' },
|
|
150
|
+
tokens_out: { type: 'number' },
|
|
151
|
+
cost_usd: { type: 'number' },
|
|
152
|
+
},
|
|
153
|
+
log: {
|
|
154
|
+
level: {
|
|
155
|
+
type: 'string',
|
|
156
|
+
required: true,
|
|
157
|
+
oneOf: ['debug', 'info', 'warn', 'error'],
|
|
158
|
+
},
|
|
159
|
+
text: text(),
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
/** Events that end a run. The stream closes after it delivers one. */
|
|
163
|
+
export const TERMINAL_EVENT_TYPES = new Set([
|
|
164
|
+
'run.completed',
|
|
165
|
+
'run.failed',
|
|
166
|
+
'run.cancelled',
|
|
167
|
+
]);
|
|
168
|
+
/**
|
|
169
|
+
* Types the service engine sends today that aren't runner events. Accepted on
|
|
170
|
+
* the lenient gate without being counted as unknown.
|
|
171
|
+
*/
|
|
172
|
+
export const LEGACY_EVENT_TYPES = new Set([
|
|
173
|
+
'browser.live',
|
|
174
|
+
'composio.authorization',
|
|
175
|
+
'computer.busy',
|
|
176
|
+
'computer.ready',
|
|
177
|
+
'computer.requested',
|
|
178
|
+
'computer.viewer.available',
|
|
179
|
+
'computer.viewer.unavailable',
|
|
180
|
+
'connector.execution.receipt',
|
|
181
|
+
'run.capabilities_attached',
|
|
182
|
+
'run.inference_overridden',
|
|
183
|
+
'run.limit_reached',
|
|
184
|
+
'run.llm_call',
|
|
185
|
+
'run.model_fallback',
|
|
186
|
+
'run.running',
|
|
187
|
+
'run.starting',
|
|
188
|
+
'run.tool_call',
|
|
189
|
+
'run.tool_refused',
|
|
190
|
+
'run.training_loaded',
|
|
191
|
+
'setup.change',
|
|
192
|
+
'ui.card',
|
|
193
|
+
'workser_computer.conversation.gate',
|
|
194
|
+
'workser_computer.route.selected',
|
|
195
|
+
]);
|
|
196
|
+
/** Is this a well-formed runner event? The strict gate. */
|
|
197
|
+
export function checkRunnerEvent(event) {
|
|
198
|
+
if (!event || typeof event !== 'object') {
|
|
199
|
+
return { ok: false, reason: 'An event must be an object.' };
|
|
200
|
+
}
|
|
201
|
+
if (typeof event.event_id !== 'string' || !event.event_id || event.event_id.length > 40) {
|
|
202
|
+
return { ok: false, reason: 'event_id must be a string of 1–40 characters.' };
|
|
203
|
+
}
|
|
204
|
+
if (!Number.isInteger(event.seq) || event.seq < 1) {
|
|
205
|
+
return { ok: false, reason: 'seq must be a positive whole number.' };
|
|
206
|
+
}
|
|
207
|
+
const shape = RUN_EVENT_CATALOG[event.type];
|
|
208
|
+
if (!shape) {
|
|
209
|
+
return { ok: false, reason: `Unknown event type "${String(event.type)}".` };
|
|
210
|
+
}
|
|
211
|
+
const data = event.data ?? {};
|
|
212
|
+
if (typeof data !== 'object' || Array.isArray(data) || data === null) {
|
|
213
|
+
return { ok: false, reason: `${event.type}: data must be an object.` };
|
|
214
|
+
}
|
|
215
|
+
const size = byteLength(data);
|
|
216
|
+
if (size > MAX_EVENT_DATA_BYTES) {
|
|
217
|
+
return {
|
|
218
|
+
ok: false,
|
|
219
|
+
reason: `${event.type}: data is ${size} bytes; the limit is ${MAX_EVENT_DATA_BYTES}. Put large output in a file.`,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
for (const [field, rule] of Object.entries(shape)) {
|
|
223
|
+
const value = data[field];
|
|
224
|
+
if (value === undefined || value === null) {
|
|
225
|
+
if (rule.required) {
|
|
226
|
+
return { ok: false, reason: `${event.type}: "${field}" is required.` };
|
|
227
|
+
}
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
const actual = Array.isArray(value) ? 'array' : typeof value;
|
|
231
|
+
if (actual !== rule.type) {
|
|
232
|
+
return {
|
|
233
|
+
ok: false,
|
|
234
|
+
reason: `${event.type}: "${field}" must be ${rule.type}, got ${actual}.`,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
if (rule.type === 'number' && !Number.isFinite(value)) {
|
|
238
|
+
return { ok: false, reason: `${event.type}: "${field}" must be finite.` };
|
|
239
|
+
}
|
|
240
|
+
if (rule.oneOf && !rule.oneOf.includes(value)) {
|
|
241
|
+
return {
|
|
242
|
+
ok: false,
|
|
243
|
+
reason: `${event.type}: "${field}" must be one of ${rule.oneOf.join(', ')}.`,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
if (rule.maxLength && value.length > rule.maxLength) {
|
|
247
|
+
return {
|
|
248
|
+
ok: false,
|
|
249
|
+
reason: `${event.type}: "${field}" is longer than ${rule.maxLength} characters.`,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return { ok: true };
|
|
254
|
+
}
|
|
255
|
+
/** The lenient gate: only the envelope is checked. */
|
|
256
|
+
export function checkLegacyEvent(event) {
|
|
257
|
+
if (typeof event.event_type !== 'string' || !event.event_type || event.event_type.length > 80) {
|
|
258
|
+
return { ok: false, reason: 'event_type must be a string of 1–80 characters.' };
|
|
259
|
+
}
|
|
260
|
+
if (typeof event.event_id !== 'string' || !event.event_id || event.event_id.length > 40) {
|
|
261
|
+
return { ok: false, reason: 'event_id must be a string of 1–40 characters.' };
|
|
262
|
+
}
|
|
263
|
+
if (!Number.isInteger(event.seq)) {
|
|
264
|
+
return { ok: false, reason: 'seq must be a whole number.' };
|
|
265
|
+
}
|
|
266
|
+
return { ok: true };
|
|
267
|
+
}
|
|
268
|
+
/** A type neither in the catalog nor in the known legacy list. */
|
|
269
|
+
export function unknownLegacyType(type) {
|
|
270
|
+
return !RUN_EVENT_CATALOG[type] && !LEGACY_EVENT_TYPES.has(type);
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* The run status a runner event moves the run to, or null when it doesn't.
|
|
274
|
+
* Status only moves forward: a terminal run never changes again.
|
|
275
|
+
*/
|
|
276
|
+
export function statusForRunnerEvent(type) {
|
|
277
|
+
switch (type) {
|
|
278
|
+
case 'run.started':
|
|
279
|
+
case 'run.resumed':
|
|
280
|
+
return 'RUNNING';
|
|
281
|
+
case 'run.completed':
|
|
282
|
+
return 'COMPLETED';
|
|
283
|
+
case 'run.failed':
|
|
284
|
+
return 'FAILED';
|
|
285
|
+
case 'run.cancelled':
|
|
286
|
+
return 'CANCELLED';
|
|
287
|
+
default:
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Check a runner batch against the run's cursor (the last seq accepted).
|
|
293
|
+
*
|
|
294
|
+
* Events at or below the cursor are retries and are dropped here — their
|
|
295
|
+
* `event_id` is checked against the stored row by the caller. What remains
|
|
296
|
+
* must continue the cursor exactly: `cursor+1, cursor+2, …`. A gap means the
|
|
297
|
+
* runner lost an event, and it is told where to resend from rather than
|
|
298
|
+
* having the hole papered over.
|
|
299
|
+
*/
|
|
300
|
+
export function checkBatchSeq(cursor, events) {
|
|
301
|
+
const sorted = [...events].sort((a, b) => a.seq - b.seq);
|
|
302
|
+
const fresh = sorted.filter((one) => one.seq > cursor);
|
|
303
|
+
let expected = cursor + 1;
|
|
304
|
+
for (const one of fresh) {
|
|
305
|
+
if (one.seq !== expected) {
|
|
306
|
+
return {
|
|
307
|
+
ok: false,
|
|
308
|
+
expected,
|
|
309
|
+
reason: one.seq < expected
|
|
310
|
+
? `seq ${one.seq} appears twice in this batch.`
|
|
311
|
+
: `seq ${one.seq} skips ahead; expected ${expected}. Resend from ${expected}.`,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
expected += 1;
|
|
315
|
+
}
|
|
316
|
+
return { ok: true, fresh, nextCursor: expected - 1 };
|
|
317
|
+
}
|
|
318
|
+
// TextEncoder, not Buffer: the same file runs in browsers and in core-api.
|
|
319
|
+
const encoder = new TextEncoder();
|
|
320
|
+
function byteLength(value) {
|
|
321
|
+
try {
|
|
322
|
+
return encoder.encode(JSON.stringify(value)).length;
|
|
323
|
+
}
|
|
324
|
+
catch {
|
|
325
|
+
return Number.POSITIVE_INFINITY;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
//# sourceMappingURL=catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/contract/catalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/contract/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One subscriber's view of a positioned log (run events, runner commands):
|
|
3
|
+
* everything after its cursor, in order, once each, with nothing missing.
|
|
4
|
+
*
|
|
5
|
+
* WHY THIS EXISTS
|
|
6
|
+
* ---------------
|
|
7
|
+
* The old stream replayed history, THEN subscribed to live events. Anything
|
|
8
|
+
* written while the history query ran fell between the two and was never
|
|
9
|
+
* shown. And live delivery across instances is a pointer read back from the
|
|
10
|
+
* database, so two events can arrive out of order, and a pointer lost while
|
|
11
|
+
* the listener reconnects is lost for good.
|
|
12
|
+
*
|
|
13
|
+
* The feed fixes all three by trusting only the database's order (`pos`):
|
|
14
|
+
* - live rows are OFFERED from the moment the subscriber exists, and wait
|
|
15
|
+
* behind the history replay rather than racing it;
|
|
16
|
+
* - a row that skips ahead makes the feed read the missing range first;
|
|
17
|
+
* - `catchUp()` (called on a timer) reads anything a lost pointer hid.
|
|
18
|
+
*
|
|
19
|
+
* Holes in `pos` are fine: position N+1 never commits before N has committed
|
|
20
|
+
* or rolled back (migration 239), so "everything after my cursor" read after
|
|
21
|
+
* seeing N+1 is complete.
|
|
22
|
+
*/
|
|
23
|
+
export interface Positioned {
|
|
24
|
+
pos: number | null;
|
|
25
|
+
}
|
|
26
|
+
export type FetchAfter<T> = (after: number, limit: number) => Promise<T[]>;
|
|
27
|
+
export declare const FEED_PAGE = 500;
|
|
28
|
+
export declare class OrderedFeed<T extends Positioned> {
|
|
29
|
+
private readonly fetchAfter;
|
|
30
|
+
private readonly emit;
|
|
31
|
+
private readonly onError;
|
|
32
|
+
private last;
|
|
33
|
+
private chain;
|
|
34
|
+
private closed;
|
|
35
|
+
constructor(start: number, fetchAfter: FetchAfter<T>, emit: (row: T) => void, onError?: (error: unknown) => void);
|
|
36
|
+
/** The position of the last row emitted. */
|
|
37
|
+
get cursor(): number;
|
|
38
|
+
/**
|
|
39
|
+
* Replay everything after the start cursor. Call once, right after creating.
|
|
40
|
+
*
|
|
41
|
+
* `show`, when given, hides replayed rows it rejects while still moving the
|
|
42
|
+
* cursor past them — for the old `after_seq` resume, which skipped rows by
|
|
43
|
+
* the writer's own number. Live rows are never filtered.
|
|
44
|
+
*/
|
|
45
|
+
start(show?: (row: T) => boolean): Promise<void>;
|
|
46
|
+
/** A live row. Emitted in order; a skip ahead reads the gap first. */
|
|
47
|
+
offer(row: T): Promise<void>;
|
|
48
|
+
/** Read anything after the cursor that live delivery didn't bring. */
|
|
49
|
+
catchUp(): Promise<void>;
|
|
50
|
+
/** Stop emitting. Pending work finishes silently. */
|
|
51
|
+
close(): void;
|
|
52
|
+
private push;
|
|
53
|
+
/** Emit stored rows after the cursor, up to `until` when given. */
|
|
54
|
+
private drain;
|
|
55
|
+
private enqueue;
|
|
56
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
export const FEED_PAGE = 500;
|
|
2
|
+
export class OrderedFeed {
|
|
3
|
+
fetchAfter;
|
|
4
|
+
emit;
|
|
5
|
+
onError;
|
|
6
|
+
last;
|
|
7
|
+
chain = Promise.resolve();
|
|
8
|
+
closed = false;
|
|
9
|
+
constructor(start, fetchAfter, emit, onError = () => undefined) {
|
|
10
|
+
this.fetchAfter = fetchAfter;
|
|
11
|
+
this.emit = emit;
|
|
12
|
+
this.onError = onError;
|
|
13
|
+
this.last = Math.max(0, Math.floor(start || 0));
|
|
14
|
+
}
|
|
15
|
+
/** The position of the last row emitted. */
|
|
16
|
+
get cursor() {
|
|
17
|
+
return this.last;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Replay everything after the start cursor. Call once, right after creating.
|
|
21
|
+
*
|
|
22
|
+
* `show`, when given, hides replayed rows it rejects while still moving the
|
|
23
|
+
* cursor past them — for the old `after_seq` resume, which skipped rows by
|
|
24
|
+
* the writer's own number. Live rows are never filtered.
|
|
25
|
+
*/
|
|
26
|
+
start(show) {
|
|
27
|
+
return this.enqueue(() => this.drain(undefined, show));
|
|
28
|
+
}
|
|
29
|
+
/** A live row. Emitted in order; a skip ahead reads the gap first. */
|
|
30
|
+
offer(row) {
|
|
31
|
+
return this.enqueue(async () => {
|
|
32
|
+
const pos = row.pos;
|
|
33
|
+
if (pos == null || pos <= this.last)
|
|
34
|
+
return;
|
|
35
|
+
if (pos > this.last + 1) {
|
|
36
|
+
await this.drain(pos);
|
|
37
|
+
if (pos <= this.last)
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
this.push(row);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/** Read anything after the cursor that live delivery didn't bring. */
|
|
44
|
+
catchUp() {
|
|
45
|
+
return this.enqueue(() => this.drain());
|
|
46
|
+
}
|
|
47
|
+
/** Stop emitting. Pending work finishes silently. */
|
|
48
|
+
close() {
|
|
49
|
+
this.closed = true;
|
|
50
|
+
}
|
|
51
|
+
push(row, show) {
|
|
52
|
+
if (this.closed || row.pos == null || row.pos <= this.last)
|
|
53
|
+
return;
|
|
54
|
+
this.last = row.pos;
|
|
55
|
+
if (show && !show(row))
|
|
56
|
+
return;
|
|
57
|
+
this.emit(row);
|
|
58
|
+
}
|
|
59
|
+
/** Emit stored rows after the cursor, up to `until` when given. */
|
|
60
|
+
async drain(until, show) {
|
|
61
|
+
for (;;) {
|
|
62
|
+
if (this.closed)
|
|
63
|
+
return;
|
|
64
|
+
const rows = await this.fetchAfter(this.last, FEED_PAGE);
|
|
65
|
+
for (const row of rows) {
|
|
66
|
+
if (row.pos == null)
|
|
67
|
+
continue;
|
|
68
|
+
if (until !== undefined && row.pos > until)
|
|
69
|
+
return;
|
|
70
|
+
this.push(row, show);
|
|
71
|
+
}
|
|
72
|
+
if (rows.length < FEED_PAGE)
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
enqueue(work) {
|
|
77
|
+
const next = this.chain.then(async () => {
|
|
78
|
+
if (this.closed)
|
|
79
|
+
return;
|
|
80
|
+
try {
|
|
81
|
+
await work();
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
this.onError(error);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
this.chain = next;
|
|
88
|
+
return next;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=ordered-feed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ordered-feed.js","sourceRoot":"","sources":["../../src/contract/ordered-feed.ts"],"names":[],"mappings":"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"}
|