@anureo/sdk 0.2.0 → 0.3.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/README.md +162 -65
- package/package.json +29 -24
- package/src/adapter/identity.d.ts +41 -0
- package/src/adapter/identity.js +71 -0
- package/src/client-options.d.ts +71 -0
- package/src/client-options.js +0 -0
- package/src/client.d.ts +106 -0
- package/src/client.js +254 -0
- package/src/diagnostics/errors.d.ts +15 -0
- package/src/diagnostics/errors.js +59 -0
- package/src/diagnostics/events.d.ts +46 -0
- package/src/diagnostics/events.js +47 -0
- package/src/diagnostics.d.ts +20 -0
- package/src/diagnostics.js +56 -0
- package/src/errors.d.ts +48 -0
- package/src/errors.js +63 -0
- package/src/index.d.ts +27 -0
- package/src/index.js +23 -0
- package/src/lifecycle/approval.d.ts +16 -0
- package/src/lifecycle/approval.js +18 -0
- package/src/lifecycle/deferred.d.ts +10 -0
- package/src/lifecycle/deferred.js +12 -0
- package/src/lifecycle/errors.d.ts +18 -0
- package/src/lifecycle/errors.js +45 -0
- package/src/lifecycle/events.d.ts +35 -0
- package/src/lifecycle/events.js +141 -0
- package/src/lifecycle/execution-observation.d.ts +33 -0
- package/src/lifecycle/execution-observation.js +0 -0
- package/src/lifecycle/immutable.d.ts +12 -0
- package/src/lifecycle/immutable.js +28 -0
- package/src/lifecycle/input.d.ts +9 -0
- package/src/lifecycle/input.js +19 -0
- package/src/lifecycle/interactions.d.ts +129 -0
- package/src/lifecycle/interactions.js +282 -0
- package/src/lifecycle/limits.d.ts +7 -0
- package/src/lifecycle/limits.js +13 -0
- package/src/lifecycle/result.d.ts +77 -0
- package/src/lifecycle/result.js +213 -0
- package/src/lifecycle/value.d.ts +4 -0
- package/src/lifecycle/value.js +5 -0
- package/src/ports.d.ts +224 -0
- package/src/ports.js +0 -0
- package/src/privacy/file-change-limits.d.ts +20 -0
- package/src/privacy/file-change-limits.js +124 -0
- package/src/privacy/secrets.d.ts +205 -0
- package/src/privacy/secrets.js +564 -0
- package/src/process.d.ts +92 -0
- package/src/process.js +331 -0
- package/src/queue.d.ts +143 -0
- package/src/queue.js +266 -0
- package/src/request.d.ts +28 -0
- package/src/request.js +81 -0
- package/src/requests/guard.d.ts +15 -0
- package/src/requests/guard.js +93 -0
- package/src/session.d.ts +459 -0
- package/src/session.js +1135 -0
- package/src/structured-output/adapter.d.ts +8 -0
- package/src/structured-output/adapter.js +54 -0
- package/src/structured-output/bridge.d.ts +81 -0
- package/src/structured-output/bridge.js +274 -0
- package/src/structured-output/errors.d.ts +17 -0
- package/src/structured-output/errors.js +31 -0
- package/src/structured-output/limits.d.ts +19 -0
- package/src/structured-output/limits.js +0 -0
- package/src/structured-output/schema.d.ts +20 -0
- package/src/structured-output/schema.js +122 -0
- package/src/structured-output/session.d.ts +329 -0
- package/src/structured-output/session.js +846 -0
- package/src/structured-output/stdio.mjs +83 -0
- package/src/structured-output/tool.d.ts +38 -0
- package/src/structured-output/tool.js +34 -0
- package/src/turn.d.ts +75 -0
- package/src/turn.js +95 -0
- package/src/types.d.ts +62 -0
- package/src/types.js +0 -0
- package/dist/diagnostics.d.ts +0 -15
- package/dist/diagnostics.js +0 -34
- package/dist/errors.d.ts +0 -15
- package/dist/file-change-limits.d.ts +0 -5
- package/dist/identity.d.ts +0 -13
- package/dist/index.d.ts +0 -8
- package/dist/index.js +0 -1474
- package/dist/interactions.d.ts +0 -20
- package/dist/ports.d.ts +0 -66
- package/dist/process.d.ts +0 -34
- package/dist/process.js +0 -191
- package/dist/queue.d.ts +0 -36
- package/dist/queue.js +0 -166
- package/dist/runtime.d.ts +0 -85
- package/dist/secrets.d.ts +0 -28
package/src/process.js
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
// Owns harness subprocess I/O, exit observation, and process-tree cleanup.
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
4
|
+
/** Process failure category for adapters; native causes are reduced to a format-checked code. */
|
|
5
|
+
export class ProcessPortError extends Error {
|
|
6
|
+
/**
|
|
7
|
+
* Construct a failure using caller-supplied safe message text, which is retained verbatim.
|
|
8
|
+
* Supply a native cause to retain only its format-checked error code.
|
|
9
|
+
*/
|
|
10
|
+
constructor(code, message, options) {
|
|
11
|
+
// Native spawn errors contain spawnargs. Never retain their raw cause in a public error.
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = 'ProcessPortError';
|
|
14
|
+
this.code = code;
|
|
15
|
+
const nativeCode = readNativeErrorCode(options?.cause);
|
|
16
|
+
if (nativeCode !== undefined)
|
|
17
|
+
this.nativeCode = nativeCode;
|
|
18
|
+
}
|
|
19
|
+
/** Stable category for adapter startup, input, or disposal failures. */
|
|
20
|
+
code;
|
|
21
|
+
/**
|
|
22
|
+
* Native code of 1 to 64 characters, beginning with an uppercase letter and containing only
|
|
23
|
+
* uppercase letters, digits, and underscores.
|
|
24
|
+
*/
|
|
25
|
+
nativeCode;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Launch a harness executable directly with piped I/O and literal arguments, without a shell.
|
|
29
|
+
* Resolve after the spawn event provides a process handle. The caller handles protocol readiness.
|
|
30
|
+
* Invalid inputs or launch failures reject with HARNESS_UNAVAILABLE. Failed startup releases any
|
|
31
|
+
* local pipes it acquired. Drain output and await disposal even after child exit.
|
|
32
|
+
*
|
|
33
|
+
* ACP's processStream awaits launch to wire the pipes into its channel, propagates launch
|
|
34
|
+
* rejection, and delegates channel disposal to the handle.
|
|
35
|
+
*/
|
|
36
|
+
export async function spawnHarnessProcess(options) {
|
|
37
|
+
const timeoutMs = validateProcessOptions(options);
|
|
38
|
+
const owned = options.owned ?? true;
|
|
39
|
+
const child = launchProcess({ options, owned });
|
|
40
|
+
const initialPid = child.pid;
|
|
41
|
+
const exit = observeProcessExit(child);
|
|
42
|
+
// Prevent an unhandled pipe error if a peer closes while the caller isn't writing.
|
|
43
|
+
child.stdin.on('error', () => { });
|
|
44
|
+
const pid = await waitForSpawn(child, initialPid);
|
|
45
|
+
return createProcessHandle({ child, pid, owned, timeoutMs, ...exit });
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Return the validated grace period for disposal. Throw HARNESS_UNAVAILABLE for an empty
|
|
49
|
+
* executable, NUL bytes in the executable or arguments, or an invalid timeout. spawnHarnessProcess
|
|
50
|
+
* calls this before launching the child.
|
|
51
|
+
*/
|
|
52
|
+
function validateProcessOptions(options) {
|
|
53
|
+
if (!options.command || options.command.includes('\0') || options.args?.some((arg) => arg.includes('\0'))) {
|
|
54
|
+
throw new ProcessPortError('HARNESS_UNAVAILABLE', 'Invalid harness executable or argument');
|
|
55
|
+
}
|
|
56
|
+
const timeoutMs = options.shutdownTimeoutMs ?? 1000;
|
|
57
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 0 || timeoutMs > 60_000) {
|
|
58
|
+
throw new ProcessPortError('HARNESS_UNAVAILABLE', 'Invalid process shutdown timeout');
|
|
59
|
+
}
|
|
60
|
+
return timeoutMs;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Open piped stdio for a shell-free launch. Detach owned POSIX children so teardown can signal
|
|
64
|
+
* their whole group. Convert synchronous spawn failures to HARNESS_UNAVAILABLE. spawnHarnessProcess
|
|
65
|
+
* installs the event observers after this returns.
|
|
66
|
+
*/
|
|
67
|
+
function launchProcess({ options, owned }) {
|
|
68
|
+
try {
|
|
69
|
+
return spawn(options.command, [...(options.args ?? [])], {
|
|
70
|
+
cwd: options.cwd, env: options.env, stdio: ['pipe', 'pipe', 'pipe'],
|
|
71
|
+
detached: owned && process.platform !== 'win32', shell: false, windowsHide: true,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
catch (cause) {
|
|
75
|
+
throw new ProcessPortError('HARNESS_UNAVAILABLE', 'Unable to start harness executable', { cause });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Register child exit and error listeners before spawnHarnessProcess awaits startup. Update the exit
|
|
80
|
+
* flag before resolving so writes and teardown can consult the observation immediately.
|
|
81
|
+
*/
|
|
82
|
+
function observeProcessExit(child) {
|
|
83
|
+
let didExit = false;
|
|
84
|
+
const exited = new Promise((resolve) => {
|
|
85
|
+
child.once('exit', (code, signal) => {
|
|
86
|
+
didExit = true;
|
|
87
|
+
resolve({ code, signal });
|
|
88
|
+
});
|
|
89
|
+
child.once('error', (error) => {
|
|
90
|
+
if (!child.pid) {
|
|
91
|
+
didExit = true;
|
|
92
|
+
resolve({ code: null, signal: null, errorCode: error.code });
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
return { exited, didExit: () => didExit };
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Wait for the spawn event and retain its PID before the runtime can clear it on exit. Fall back
|
|
100
|
+
* to the initial PID if the event provides no usable PID. On failure, release the pipes and
|
|
101
|
+
* reject with HARNESS_UNAVAILABLE so spawnHarnessProcess never publishes a partial handle.
|
|
102
|
+
*/
|
|
103
|
+
async function waitForSpawn(child, initialPid) {
|
|
104
|
+
let pid = initialPid;
|
|
105
|
+
try {
|
|
106
|
+
await new Promise((resolve, reject) => {
|
|
107
|
+
child.once('spawn', () => {
|
|
108
|
+
const spawnPid = child.pid;
|
|
109
|
+
if (spawnPid !== undefined && spawnPid > 1)
|
|
110
|
+
pid = spawnPid;
|
|
111
|
+
resolve();
|
|
112
|
+
});
|
|
113
|
+
child.once('error', reject);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
catch (cause) {
|
|
117
|
+
return failProcessStartup(child, cause);
|
|
118
|
+
}
|
|
119
|
+
if (pid === undefined || pid <= 1)
|
|
120
|
+
return failProcessStartup(child);
|
|
121
|
+
return pid;
|
|
122
|
+
}
|
|
123
|
+
/** Release startup's local pipes before rejecting launch with a privacy-safe error. */
|
|
124
|
+
function failProcessStartup(child, cause) {
|
|
125
|
+
destroyProcessStreams(child);
|
|
126
|
+
throw new ProcessPortError('HARNESS_UNAVAILABLE', 'Unable to start harness executable', { cause });
|
|
127
|
+
}
|
|
128
|
+
/** Close all three local pipes after failed startup or after disposal, even if termination failed. */
|
|
129
|
+
function destroyProcessStreams(child) {
|
|
130
|
+
child.stdin.destroy();
|
|
131
|
+
child.stdout.destroy();
|
|
132
|
+
child.stderr.destroy();
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Give spawnHarnessProcess's callers the pipes and exit observation. Keep stdin write admission
|
|
136
|
+
* and the teardown promise together so disposing closes admission and shares the cleanup outcome.
|
|
137
|
+
*/
|
|
138
|
+
function createProcessHandle(resources) {
|
|
139
|
+
const { child, pid, owned, didExit, exited } = resources;
|
|
140
|
+
const { stdin, stdout, stderr } = child;
|
|
141
|
+
let disposePromise;
|
|
142
|
+
return {
|
|
143
|
+
pid, owned, stdout, stderr, exited,
|
|
144
|
+
write(chunk) {
|
|
145
|
+
if (disposePromise || isInputClosed(stdin) || didExit()) {
|
|
146
|
+
return Promise.reject(new ProcessPortError('PROCESS_WRITE_FAILED', 'Harness process input is closed'));
|
|
147
|
+
}
|
|
148
|
+
return runInputOperation('Unable to write harness input', (complete) => {
|
|
149
|
+
stdin.write(chunk, complete);
|
|
150
|
+
});
|
|
151
|
+
},
|
|
152
|
+
closeInput() {
|
|
153
|
+
return runInputOperation('Unable to close harness input', (complete) => {
|
|
154
|
+
if (isInputClosed(stdin))
|
|
155
|
+
complete();
|
|
156
|
+
else
|
|
157
|
+
stdin.end(complete);
|
|
158
|
+
});
|
|
159
|
+
},
|
|
160
|
+
dispose() {
|
|
161
|
+
return disposePromise ??= disposeProcess(resources);
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/** Treat ending as closed for writes and redundant close requests. */
|
|
166
|
+
function isInputClosed(stdin) {
|
|
167
|
+
return stdin.destroyed || stdin.writableEnded;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Settle the handle's write or close promise from the callback passed to stdin. Convert callback
|
|
171
|
+
* errors to PROCESS_WRITE_FAILED and let synchronous exceptions reject through the promise executor.
|
|
172
|
+
*/
|
|
173
|
+
function runInputOperation(failureMessage, start) {
|
|
174
|
+
return new Promise((resolve, reject) => {
|
|
175
|
+
start((error) => {
|
|
176
|
+
if (error)
|
|
177
|
+
reject(new ProcessPortError('PROCESS_WRITE_FAILED', failureMessage, { cause: error }));
|
|
178
|
+
else
|
|
179
|
+
resolve();
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* createProcessHandle's dispose method caches this promise. Terminate owned trees or unref
|
|
185
|
+
* external processes, then destroy local pipes even on failure. Convert native termination or
|
|
186
|
+
* unref errors to a ProcessPortError containing only safe error details.
|
|
187
|
+
*/
|
|
188
|
+
async function disposeProcess({ child, pid, owned, timeoutMs, didExit, exited }) {
|
|
189
|
+
try {
|
|
190
|
+
if (owned) {
|
|
191
|
+
await terminateTree(pid, timeoutMs, didExit);
|
|
192
|
+
if (!didExit())
|
|
193
|
+
await waitForTerminatedProcessExit(didExit, exited);
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
child.unref();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
catch (cause) {
|
|
200
|
+
if (cause instanceof ProcessPortError)
|
|
201
|
+
throw cause;
|
|
202
|
+
throw new ProcessPortError('PROCESS_DISPOSE_FAILED', 'Unable to dispose harness process', { cause });
|
|
203
|
+
}
|
|
204
|
+
finally {
|
|
205
|
+
destroyProcessStreams(child);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* After tree termination, disposeProcess allows 1000 ms for any pending child exit observation.
|
|
210
|
+
* A vanished group can precede the child exit event. Reject PROCESS_DISPOSE_FAILED if exit still
|
|
211
|
+
* has not been observed.
|
|
212
|
+
*/
|
|
213
|
+
async function waitForTerminatedProcessExit(didExit, exited) {
|
|
214
|
+
await Promise.race([exited, delay(1000)]);
|
|
215
|
+
if (!didExit())
|
|
216
|
+
throw new ProcessPortError('PROCESS_DISPOSE_FAILED', 'Harness process did not exit after termination');
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* disposeProcess awaits this only for owned children and sanitizes failures before rejecting its
|
|
220
|
+
* public dispose promise. Validate the retained PID, then delegate platform cleanup; callers retain
|
|
221
|
+
* ownership of exit observation and pipe destruction.
|
|
222
|
+
*/
|
|
223
|
+
async function terminateTree(pid, timeoutMs, didExit) {
|
|
224
|
+
// Bun may clear/change ChildProcess.pid after exit. Only use the original positive PID.
|
|
225
|
+
if (!Number.isSafeInteger(pid) || pid <= 1)
|
|
226
|
+
throw new ProcessPortError('PROCESS_DISPOSE_FAILED', 'Invalid original harness PID');
|
|
227
|
+
if (process.platform === 'win32')
|
|
228
|
+
return terminateWindowsTree(pid, timeoutMs, didExit);
|
|
229
|
+
return terminatePosixTree(pid, timeoutMs);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* terminateTree delegates here on POSIX; exit observation and pipe destruction remain in
|
|
233
|
+
* disposeProcess. Stop the detached POSIX group and force remaining descendants after the grace
|
|
234
|
+
* period.
|
|
235
|
+
*/
|
|
236
|
+
async function terminatePosixTree(pid, timeoutMs) {
|
|
237
|
+
// Descendants can keep inherited pipes open after the direct child has exited.
|
|
238
|
+
const group = -pid;
|
|
239
|
+
await signalGroupWithExitRace(group, 'SIGTERM');
|
|
240
|
+
const deadline = Date.now() + timeoutMs;
|
|
241
|
+
while (processOrGroupExists(group) && Date.now() < deadline)
|
|
242
|
+
await delay(10);
|
|
243
|
+
if (processOrGroupExists(group))
|
|
244
|
+
await signalGroupWithExitRace(group, 'SIGKILL');
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Signal the negative group PID for terminatePosixTree. A vanished group completes the attempt.
|
|
248
|
+
* Retry a short exit-time EPERM race up to four times, then propagate the failure to disposal.
|
|
249
|
+
*/
|
|
250
|
+
async function signalGroupWithExitRace(group, signal) {
|
|
251
|
+
for (let attempt = 0;; attempt++) {
|
|
252
|
+
try {
|
|
253
|
+
process.kill(group, signal);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
const { code } = error;
|
|
258
|
+
if (code === 'ESRCH')
|
|
259
|
+
return;
|
|
260
|
+
if (code !== 'EPERM' || attempt >= 4)
|
|
261
|
+
throw error;
|
|
262
|
+
await delay(10);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Stop the Windows tree for terminateTree. Attempt graceful taskkill while the child is still
|
|
268
|
+
* running. Escalate immediately if that attempt fails and exit is still pending, or after the
|
|
269
|
+
* grace period if the child remains alive. Forced taskkill failures reject teardown.
|
|
270
|
+
*/
|
|
271
|
+
async function terminateWindowsTree(pid, timeoutMs, didExit) {
|
|
272
|
+
if (!didExit()) {
|
|
273
|
+
try {
|
|
274
|
+
await windowsKillTree(pid, 'graceful');
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
// Console-less Windows children may reject non-forced taskkill, so retry while alive.
|
|
278
|
+
if (!didExit())
|
|
279
|
+
await windowsKillTree(pid, 'force');
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
const deadline = Date.now() + timeoutMs;
|
|
283
|
+
while (!didExit() && Date.now() < deadline)
|
|
284
|
+
await delay(10);
|
|
285
|
+
if (!didExit())
|
|
286
|
+
await windowsKillTree(pid, 'force');
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Run shell-free taskkill for terminateWindowsTree. Reject if the tool fails to start or exits
|
|
290
|
+
* unsuccessfully while the original PID is still present.
|
|
291
|
+
*/
|
|
292
|
+
async function windowsKillTree(pid, mode) {
|
|
293
|
+
await new Promise((resolve, reject) => {
|
|
294
|
+
const killer = spawn('taskkill.exe', ['/PID', String(pid), '/T', ...(mode === 'force' ? ['/F'] : [])], {
|
|
295
|
+
stdio: 'ignore', windowsHide: true, shell: false,
|
|
296
|
+
});
|
|
297
|
+
killer.once('error', reject);
|
|
298
|
+
killer.once('exit', (code) => {
|
|
299
|
+
// taskkill reports a nonzero result if the process exited during the call.
|
|
300
|
+
if (code === 0 || !processOrGroupExists(pid))
|
|
301
|
+
resolve();
|
|
302
|
+
else
|
|
303
|
+
reject(new ProcessPortError('PROCESS_DISPOSE_FAILED', 'Unable to terminate harness process tree'));
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* terminatePosixTree uses this for POSIX polling; windowsKillTree uses it to distinguish a taskkill
|
|
309
|
+
* failure from concurrent child exit. Probe a PID or negative POSIX group without signalling it; EPERM
|
|
310
|
+
* counts as existing so cleanup still escalates.
|
|
311
|
+
*/
|
|
312
|
+
function processOrGroupExists(pid) {
|
|
313
|
+
try {
|
|
314
|
+
process.kill(pid, 0);
|
|
315
|
+
return true;
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
return error.code === 'EPERM';
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Extract only a bounded uppercase code for ProcessPortError; a native cause may contain private
|
|
323
|
+
* spawn arguments and must never become part of the public error.
|
|
324
|
+
*/
|
|
325
|
+
function readNativeErrorCode(cause) {
|
|
326
|
+
if (!cause || typeof cause !== 'object' || !('code' in cause))
|
|
327
|
+
return undefined;
|
|
328
|
+
if (typeof cause.code !== 'string' || !/^[A-Z][A-Z0-9_]{0,63}$/.test(cause.code))
|
|
329
|
+
return undefined;
|
|
330
|
+
return cause.code;
|
|
331
|
+
}
|
package/src/queue.d.ts
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/** Queue budgets for producers and event subscriptions. Each resolved budget must be a positive safe integer. */
|
|
2
|
+
export interface QueueLimits {
|
|
3
|
+
/** Maximum buffered regular events; defaults to 2048. Direct delivery to a waiting reader consumes no slot. */
|
|
4
|
+
maxEvents?: number;
|
|
5
|
+
/** Maximum bytes buffered by regular events; defaults to 16 MiB. Controls have a separate count budget. */
|
|
6
|
+
maxBytes?: number;
|
|
7
|
+
/** Maximum bytes per regular or control event; defaults to 8 MiB. Each event must also fit maxBytes. */
|
|
8
|
+
maxFrameBytes?: number;
|
|
9
|
+
/** Maximum suspended write calls. Defaults to 32. */
|
|
10
|
+
maxWaitingWriters?: number;
|
|
11
|
+
/** Maximum buffered controls; defaults to 33. They drain after regular events and use separate capacity. */
|
|
12
|
+
maxControlEvents?: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Buffer a producer stream within event and byte budgets, or await capacity with write.
|
|
16
|
+
* One iterator owns reads at a time. Returning it releases ownership while the producer stays open.
|
|
17
|
+
* Producers own closure and consumers own iterator release. Buffered regular events drain before
|
|
18
|
+
* buffered controls, then readers observe completion or failure. Values are retained by reference;
|
|
19
|
+
* keep their measured size stable while queued. EventHub uses a private queue per observer to
|
|
20
|
+
* isolate overflow, while adapter transports use queues for incoming frames or native input.
|
|
21
|
+
*/
|
|
22
|
+
export declare class BoundedEventQueue<T> implements AsyncIterable<T> {
|
|
23
|
+
private readonly sizeOf;
|
|
24
|
+
/**
|
|
25
|
+
* Configure storage without claiming a reader. Invalid resolved budgets throw INVALID_PARAMS.
|
|
26
|
+
* Omitted or nullish budget fields select the QueueLimits defaults. The queue calls sizeOf during
|
|
27
|
+
* admission and propagates measurement failures. It defaults to measuring the UTF-8 byte length
|
|
28
|
+
* of the JSON serialization. A custom measurement must return a consistent, nonnegative byte
|
|
29
|
+
* count because a value can be measured more than once.
|
|
30
|
+
*/
|
|
31
|
+
constructor(limits?: QueueLimits, sizeOf?: (value: T) => number);
|
|
32
|
+
/** Resolved construction-time budgets exposed for producers; callers must treat this object as read-only. */
|
|
33
|
+
readonly limits: Required<QueueLimits>;
|
|
34
|
+
/** Count regular events and controls currently in the buffer. */
|
|
35
|
+
get bufferedEvents(): number;
|
|
36
|
+
/** Count bytes reserved by buffered regular events. Controls consume their own event budget. */
|
|
37
|
+
get bufferedBytes(): number;
|
|
38
|
+
/**
|
|
39
|
+
* Check regular-event admission without reserving capacity. A later push can still fail.
|
|
40
|
+
* Throw the stored non-nullish closure cause (or STREAM_CLOSED), FRAME_TOO_LARGE, a measurement
|
|
41
|
+
* failure, or BACKPRESSURE_EXCEEDED for a full regular buffer.
|
|
42
|
+
*/
|
|
43
|
+
assertWritable(value: T): void;
|
|
44
|
+
/**
|
|
45
|
+
* Deliver to a waiting read or buffer synchronously, throwing on closure, capacity or
|
|
46
|
+
* size/measurement failure without evicting data. Producers handle failures and own closure.
|
|
47
|
+
* EventHub.publish ends only the affected observer on failure. The message listener in
|
|
48
|
+
* createSocketTransport also closes the native socket.
|
|
49
|
+
*/
|
|
50
|
+
push(value: T): void;
|
|
51
|
+
/**
|
|
52
|
+
* Await capacity and resolve once delivered or buffered, without waiting for consumption.
|
|
53
|
+
* Reject on closure, size/measurement failure or exhausted writer capacity.
|
|
54
|
+
* Waking writers compete for capacity and recheck it before admitting a value. Pass accepted
|
|
55
|
+
* to run a synchronous callback after admission. If it throws, the promise rejects and the value
|
|
56
|
+
* remains admitted. Producers can end the queue to wake all blocked writers and reject admission.
|
|
57
|
+
*/
|
|
58
|
+
write(value: T, accepted?: () => void): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* Admit a control event even when the regular buffer is full. Each event still obeys
|
|
61
|
+
* maxFrameBytes and maxBytes; buffered controls have a separate maxControlEvents limit
|
|
62
|
+
* and do not contribute to bufferedBytes.
|
|
63
|
+
* Buffered regular events are delivered before buffered control events. Throws on closure,
|
|
64
|
+
* measurement/size failure or exhausted control capacity; callers still own producer closure.
|
|
65
|
+
*/
|
|
66
|
+
pushControl(value: T): void;
|
|
67
|
+
/**
|
|
68
|
+
* Admit a final control under the usual control budget, then close admission. Readers can
|
|
69
|
+
* receive the control before completion. Already-ended queues ignore the value. If admission
|
|
70
|
+
* throws, the producer still owns recovery or closure.
|
|
71
|
+
*/
|
|
72
|
+
endWithControl(value: T): void;
|
|
73
|
+
/**
|
|
74
|
+
* Idempotently close admission, wake blocked writers so they can reject, and settle any pending read.
|
|
75
|
+
* Buffered values drain before a truthy cause rejects reads; falsy causes complete reads normally.
|
|
76
|
+
* Closure checks throw a non-nullish stored cause, otherwise STREAM_CLOSED.
|
|
77
|
+
* Reader ownership remains until next observes drained closure or the consumer calls return or
|
|
78
|
+
* throw on the iterator. The caller still owns stopping any native producer.
|
|
79
|
+
*/
|
|
80
|
+
end(cause?: unknown): void;
|
|
81
|
+
/**
|
|
82
|
+
* Claim exclusive reader ownership, throwing EVENT_STREAM_ALREADY_OWNED until the prior reader
|
|
83
|
+
* releases it. next drains regular values then controls before terminal completion or rejection.
|
|
84
|
+
* A second next while a read is blocked rejects with CONCURRENT_EVENT_READ. return and throw
|
|
85
|
+
* release ownership and complete a blocked read while leaving the producer open. throw also
|
|
86
|
+
* rejects its own promise with the supplied cause. A pending read settled by end still needs
|
|
87
|
+
* a subsequent next, return or throw to release ownership.
|
|
88
|
+
*/
|
|
89
|
+
[Symbol.asyncIterator](): AsyncIterator<T>;
|
|
90
|
+
private readonly entries;
|
|
91
|
+
private bytes;
|
|
92
|
+
private activeReaderToken?;
|
|
93
|
+
private pendingRead?;
|
|
94
|
+
private controls;
|
|
95
|
+
private ended;
|
|
96
|
+
private failure?;
|
|
97
|
+
private waitingWriterResumes;
|
|
98
|
+
/**
|
|
99
|
+
* end, readBuffered and readNext wake blocked writers when they close the queue, free capacity
|
|
100
|
+
* or register a reader for direct delivery. Writers recheck admission on resumption.
|
|
101
|
+
*/
|
|
102
|
+
private resumeWaitingWriters;
|
|
103
|
+
/**
|
|
104
|
+
* assertWritable and write share this admission rule. A pending read permits direct delivery even
|
|
105
|
+
* at the buffer budget; checking capacity neither reserves a slot nor wakes a producer.
|
|
106
|
+
*/
|
|
107
|
+
private isRegularBufferFull;
|
|
108
|
+
/**
|
|
109
|
+
* push and pushControl try direct delivery after checking size. Report whether this settled
|
|
110
|
+
* a pending read so the caller can skip buffering.
|
|
111
|
+
*/
|
|
112
|
+
private deliverToWaitingReader;
|
|
113
|
+
/** Remove the pending read before its caller delivers a value, completes it or rejects it. */
|
|
114
|
+
private takePendingRead;
|
|
115
|
+
/**
|
|
116
|
+
* Producer admission uses this to check for closure. A closed queue throws the stored
|
|
117
|
+
* non-nullish cause or STREAM_CLOSED and keeps the reader attached.
|
|
118
|
+
*/
|
|
119
|
+
private assertOpen;
|
|
120
|
+
/**
|
|
121
|
+
* assertWritable, push, write and pushControl call before admission. Measure without reserving
|
|
122
|
+
* capacity; throw FRAME_TOO_LARGE above either single-frame or regular-byte limits, including for
|
|
123
|
+
* controls. Propagate sizeOf failures unchanged so the producer can handle admission failure.
|
|
124
|
+
*/
|
|
125
|
+
private checkedSize;
|
|
126
|
+
/**
|
|
127
|
+
* Settle the iterator's next call from buffered data or drained closure. Otherwise register a
|
|
128
|
+
* pending read and wake writers because they can now deliver directly. A detached reader
|
|
129
|
+
* completes immediately, and an overlapping blocked read rejects without replacing the first.
|
|
130
|
+
*/
|
|
131
|
+
private readNext;
|
|
132
|
+
/**
|
|
133
|
+
* readNext on drained closure and the iterator's return and throw release ownership here.
|
|
134
|
+
* createSubscription also calls return when cancelled. Only the active reader can have a pending
|
|
135
|
+
* read, so checking its token protects a replacement reader from stale calls.
|
|
136
|
+
*/
|
|
137
|
+
private detachReader;
|
|
138
|
+
/**
|
|
139
|
+
* Give readNext regular values before controls and release regular bytes before waking writers.
|
|
140
|
+
* Return undefined only when neither buffer has data, leaving terminal settlement to readNext.
|
|
141
|
+
*/
|
|
142
|
+
private readBuffered;
|
|
143
|
+
}
|