@vampgg/utils 1.0.0-beta.1

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sam McCord
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,467 @@
1
+ # @vampgg/utils
2
+
3
+ Low-level utilities for building bebop/tempo-based applications. Provides the shared wire schema, transport channel/router implementations, error types, loggers, and async iteration primitives used across the vamp.
4
+
5
+ - **bebop** — binary serialization library for fast, compact data interchange
6
+ - **tempo** — typed RPC layer built on top of bebop
7
+
8
+ ## Development
9
+
10
+ ```bash
11
+ vp install # install dependencies
12
+ vp test # run tests
13
+ vp run build # regenerate schema then build (runs: bebopc build && vp pack)
14
+ ```
15
+
16
+ > `src/bebop.ts` is auto-generated by `bebopc` from the `.bop` schema files. Do not edit it by hand. Run `bebopc build` (or `vp run build`) to regenerate after changing `schema/*.bop`.
17
+
18
+ ---
19
+
20
+ ## Schema (`schema/`)
21
+
22
+ Two bebop types define the wire format for all RPC in this framework.
23
+
24
+ ### `Error` (struct — `schema/error.bop`)
25
+
26
+ Structured error type carried inside tempo error responses.
27
+
28
+ | Field | Type | Description |
29
+ | --------- | --------------------- | -------------------------------------------- |
30
+ | `code` | `uint32` | `TempoStatusCode` value |
31
+ | `tag` | `string` | Machine-readable error tag (see `ErrorTags`) |
32
+ | `stack` | `string` | Stack trace |
33
+ | `msg` | `string` | Human-readable message |
34
+ | `details` | `map[string, string]` | Arbitrary key-value context |
35
+
36
+ ### `Message` (message — `schema/message.bop`)
37
+
38
+ The universal envelope for all RPC calls. Fields are optional (bebop message type — missing fields are simply absent on the wire).
39
+
40
+ | Field | Type | Description |
41
+ | --------------- | -------- | --------------------------------------- |
42
+ | `methodId` | `uint32` | Numeric identifier for the RPC method |
43
+ | `messageId` | `guid` | UUID correlating request/response |
44
+ | `timestamp` | `date` | When the message was created |
45
+ | `data` | `byte[]` | Bebop-encoded request or response body |
46
+ | `deadline` | `date` | Absolute deadline for the call |
47
+ | `status` | `byte` | `TempoStatusCode` (OK, CANCELLED, etc.) |
48
+ | `msg` | `string` | Error message (on error responses) |
49
+ | `authorization` | `string` | Auth header value |
50
+ | `credential` | `string` | Serialized credential for storage |
51
+ | `headers` | `string` | HTTP-style metadata header string |
52
+
53
+ ---
54
+
55
+ ## Subpath Exports
56
+
57
+ ### `@vampgg/utils/bebop` — Generated Schema Bindings
58
+
59
+ Auto-generated. Exports `Error`, `Message`, and `BEBOP_SCHEMA`.
60
+
61
+ Each type exports as both an interface and a factory/static object (the bebop pattern):
62
+
63
+ ```ts
64
+ import { Message, Error } from "@vampgg/utils/bebop";
65
+
66
+ // Encode
67
+ const bytes = Message.encode({ methodId: 1, messageId: crypto.randomUUID(), data: payload });
68
+
69
+ // Decode
70
+ const msg = Message.decode(bytes);
71
+
72
+ // Factory (adds .encode() method to instance)
73
+ const record = Message({ methodId: 1, messageId: "..." });
74
+ record.encode(); // Uint8Array
75
+ ```
76
+
77
+ ---
78
+
79
+ ### `@vampgg/utils/error` — Typed Errors
80
+
81
+ ```ts
82
+ import { SystemError, ErrorTags } from "@vampgg/utils/error";
83
+ ```
84
+
85
+ **`SystemError<Tag>`** extends `TempoError` from `@tempojs/common`. Wraps a bebop `Error` struct. Use this as the base for all thrown errors in the framework.
86
+
87
+ ```ts
88
+ throw new SystemError(ErrorTags.Validation, {
89
+ code: TempoStatusCode.INVALID_ARGUMENT,
90
+ msg: "field 'name' is required",
91
+ details: new Map([["field", "name"]]),
92
+ });
93
+
94
+ // Convert an unknown caught error into a SystemError
95
+ const err = SystemError.generic(caughtError, ErrorTags.Unknown);
96
+ ```
97
+
98
+ **`ErrorTags`** enum — the set of recognized error tags:
99
+
100
+ | Tag | Meaning |
101
+ | --------------------- | ------------------------------------ |
102
+ | `PgOutage` | PostgreSQL connectivity failure |
103
+ | `RedisOutage` | Redis connectivity failure |
104
+ | `Unknown` | Unclassified error (default) |
105
+ | `NeedPermissions` | Missing required permissions |
106
+ | `Validation` | Input validation failure |
107
+ | `Authorization` | Auth check failed |
108
+ | `GameStateGeneration` | Game state generation failure |
109
+ | `InvalidPrompt` | AI prompt rejected |
110
+ | `UnknownAIProvider` | Unrecognized AI provider |
111
+ | `FailedToStart` | Service failed to initialize |
112
+ | `Unimplemented` | Method not yet implemented |
113
+ | `IncompatibleVersion` | Client/server version mismatch |
114
+ | `PingUnacceptable` | Ping latency out of acceptable range |
115
+
116
+ ---
117
+
118
+ ### `@vampgg/utils/context-logger` — Base Logger
119
+
120
+ ```ts
121
+ import { ContextLogger } from "@vampgg/utils/context-logger";
122
+ ```
123
+
124
+ Extends `ConsoleLogger` from `@tempojs/common`. Adds structured `bindings` (key-value context attached to every log line) and methods for deriving child loggers.
125
+
126
+ ```ts
127
+ const logger = new ContextLogger("my-service", TempoLogLevel.Info, undefined, { env: "prod" });
128
+
129
+ // Derive a child logger with additional bindings
130
+ const child = logger.clone("request-handler", false, { requestId: "abc" });
131
+
132
+ // Derive a child with a correlation ID
133
+ const correlated = logger.correlate("db-query", correlationId, false, { table: "users" });
134
+ ```
135
+
136
+ **`ContextLogger`** is the base class — extend it (or use `DebugLogger` / `PinoLogger`) rather than instantiating it directly in most cases.
137
+
138
+ ---
139
+
140
+ ### `@vampgg/utils/debug-logger` — Debug-Namespace Logger
141
+
142
+ ```ts
143
+ import { DebugLogger } from "@vampgg/utils/debug-logger";
144
+ ```
145
+
146
+ Uses the `debug` package. Each log level gets its own namespace: `<sourceName>:trace`, `<sourceName>:info`, etc. Enable output with the `DEBUG` environment variable.
147
+
148
+ ```ts
149
+ const logger = new DebugLogger("my-service");
150
+ // Enable with: DEBUG=my-service:* node ...
151
+
152
+ logger.info("server started", { port: 3000 });
153
+ logger.error("db failed", { query }, err);
154
+
155
+ const child = logger.clone("auth", false, { userId: "123" });
156
+ ```
157
+
158
+ ---
159
+
160
+ ### `@vampgg/utils/pino-logger` — Pino Structured Logger
161
+
162
+ ```ts
163
+ import { PinoLogger } from "@vampgg/utils/pino-logger";
164
+ import pino from "pino";
165
+ ```
166
+
167
+ Wraps a `pino` instance. Pass an existing `pino.Logger` to integrate with your existing pino setup (transports, formatters, etc.).
168
+
169
+ ```ts
170
+ const root = pino({ level: "info" });
171
+ const logger = new PinoLogger("my-service", TempoLogLevel.Info, root, { env: "prod" });
172
+
173
+ logger.info("started");
174
+ logger.warn("slow query", { duration: 450 });
175
+
176
+ const child = logger.clone("auth");
177
+ ```
178
+
179
+ ---
180
+
181
+ ### `@vampgg/utils/async-queue` — Bounded Async Queue
182
+
183
+ A single-consumer async queue that backs both iterators below. A backing **ring
184
+ buffer** (no `Array.shift()`), a **high-water mark** with an explicit overflow
185
+ policy, and a real **error channel** — so a slow consumer can't grow memory
186
+ without bound and a transport error surfaces at the `for await` instead of looking
187
+ like a clean end-of-stream.
188
+
189
+ ```ts
190
+ import { AsyncQueue, AsyncQueueOverflowError } from "@vampgg/utils/async-queue";
191
+
192
+ const q = new AsyncQueue<number>(
193
+ { highWaterMark: 1024, overflow: "error" }, // or "drop-latest"
194
+ () => cleanup(), // optional onDispose, called on close/return/throw
195
+ );
196
+
197
+ q.push(1); // returns false if dropped (overflow: "drop-latest")
198
+ q.close(); // graceful end → consumer sees { done: true }
199
+ q.fail(new Error("transport died")); // error channel → thrown at the consumer
200
+
201
+ for await (const n of q) {
202
+ /* … */
203
+ }
204
+ ```
205
+
206
+ `overflow: "error"` makes `push` past `highWaterMark` raise `AsyncQueueOverflowError`
207
+ at the consumer; `"drop-latest"` silently drops instead.
208
+
209
+ ### `@vampgg/utils/create-event-iterator` — Push-Based Async Generator
210
+
211
+ ```ts
212
+ import { createEventIterator } from "@vampgg/utils/create-event-iterator";
213
+ import type { Subscriber, Context, CleanupFn } from "@vampgg/utils/create-event-iterator";
214
+ ```
215
+
216
+ Converts any push-based event source (EventEmitter, WebSocket messages, browser events) into an `AsyncGenerator`. The subscriber receives `{ emit, cancel }` and returns an optional cleanup function.
217
+
218
+ ```ts
219
+ const stream = createEventIterator<MyEvent>(({ emit, cancel }) => {
220
+ const handler = (ev: MyEvent) => emit(ev);
221
+ emitter.on("data", handler);
222
+
223
+ return () => {
224
+ emitter.off("data", handler);
225
+ };
226
+ });
227
+
228
+ for await (const event of stream) {
229
+ // process event
230
+ }
231
+ ```
232
+
233
+ Call `cancel()` from within the subscriber (e.g. on a "close" event) to end iteration. Remaining buffered events are yielded before the generator returns.
234
+
235
+ ---
236
+
237
+ ### `@vampgg/utils/create-duplex-iterator` — Bidirectional Async Stream
238
+
239
+ ```ts
240
+ import { createDuplexIterator } from "@vampgg/utils/create-duplex-iterator";
241
+ import type { Writer } from "@vampgg/utils/create-duplex-iterator";
242
+ ```
243
+
244
+ Combines an outgoing `AsyncGenerator<BebopRecord>` with an incoming event source. For each outgoing record it calls `emitter`, then yields the next incoming event. Used internally by duplex-stream RPC methods.
245
+
246
+ ```ts
247
+ const stream = createDuplexIterator<IncomingRecord>(
248
+ outgoingGenerator, // AsyncGenerator<BebopRecord> — client sends these
249
+ (record) => ws.send(record.encode()), // called for each outgoing record
250
+ ({ emit, cancel }) => {
251
+ // subscriber for incoming records
252
+ ws.onmessage = (ev) => emit(decode(ev.data));
253
+ return () => {
254
+ ws.onmessage = null;
255
+ };
256
+ },
257
+ );
258
+
259
+ for await (const incoming of stream) {
260
+ // interleaved with outgoing sends
261
+ }
262
+ ```
263
+
264
+ ---
265
+
266
+ ### `@vampgg/utils/ws-channel` — WebSocket Tempo Client Channel
267
+
268
+ ```ts
269
+ import { TempoWSChannel } from "@vampgg/utils/ws-channel";
270
+ import type { TempoWSChannelOptions } from "@vampgg/utils/ws-channel";
271
+ ```
272
+
273
+ A `BaseChannel` implementation that transports tempo RPC calls over a WebSocket connection (using the `websocket-ts` library with auto-reconnect support). The wire format is `Message` encoded as bebop binary.
274
+
275
+ ```ts
276
+ // Connect to a new WebSocket
277
+ const channel = TempoWSChannel.forAddress("wss://api.example.com/rpc", {
278
+ maxRetries: 3,
279
+ });
280
+
281
+ // Wrap an existing WebSocket
282
+ const channel = TempoWSChannel.forWebSocket(existingWs);
283
+
284
+ await channel.waitForOpen();
285
+
286
+ // Used by tempo-generated client stubs — not called directly
287
+ // channel.startUnary(request, context, method, options)
288
+ // channel.startServerStream(...)
289
+ // channel.startClientStream(...)
290
+ // channel.startDuplexStream(...)
291
+ ```
292
+
293
+ All four tempo method types (unary, server-stream, client-stream, duplex) are supported with deadline and retry policy propagation.
294
+
295
+ ---
296
+
297
+ ### `@vampgg/utils/ws-router` — WebSocket Tempo Server Router
298
+
299
+ ```ts
300
+ import { TempoWsRouter } from "@vampgg/utils/ws-router";
301
+ ```
302
+
303
+ A `BaseRouter` that dispatches incoming `ArrayBuffer` WebSocket messages to registered tempo service methods. Extend or instantiate with a `ServiceRegistry` and optional `AuthInterceptor`.
304
+
305
+ ```ts
306
+ const router = new TempoWsRouter<MyContext>(logger, registry, configuration, authInterceptor);
307
+ router.useHooks(hookRegistry);
308
+
309
+ // In your WebSocket message handler:
310
+ ws.onmessage = async (ev) => {
311
+ const response = Message({});
312
+ await router.process(ev.data as ArrayBuffer, response, [ws, myContext]);
313
+ };
314
+ ```
315
+
316
+ Server-stream and duplex-stream methods must be implemented as `async function*` generators in the service. Streams send successive `Message` frames until a `CANCELLED` status frame closes the stream.
317
+
318
+ ---
319
+
320
+ ### `@vampgg/utils/worker-channel` — Bun Worker Tempo Client Channel
321
+
322
+ ```ts
323
+ import { TempoWorkerChannel } from "@vampgg/utils/worker-channel";
324
+ ```
325
+
326
+ A `BaseChannel` that communicates with a Bun `Worker` using `postMessage` / `onmessage`. Messages are transferred as `Uint8Array` with zero-copy buffer transfer.
327
+
328
+ ```ts
329
+ const channel = new TempoWorkerChannel("./my-worker.ts");
330
+ await channel.ready; // resolves when the worker sends its first message
331
+
332
+ // channel.startUnary(...) / startServerStream(...) etc.
333
+ ```
334
+
335
+ The `worker` property exposes the underlying `Bun.Worker` instance if you need direct access.
336
+
337
+ ---
338
+
339
+ ### `@vampgg/utils/worker-router` — Bun Worker Tempo Server Router
340
+
341
+ ```ts
342
+ import { TempoWorkerRouter } from "@vampgg/utils/worker-router";
343
+ ```
344
+
345
+ Runs inside a Bun Worker (`declare var self: Bun.Worker`). Dispatches incoming `Buffer` messages to registered tempo service methods, writing responses back via `postMessage`.
346
+
347
+ ```ts
348
+ // Inside worker.ts
349
+ const router = new TempoWorkerRouter<MyEnv>(logger, registry);
350
+
351
+ self.onmessage = async (ev: MessageEvent<Uint8Array>) => {
352
+ const response = Message({});
353
+ await router.process(ev.data, response, myEnv);
354
+ };
355
+
356
+ // Signal ready to the channel
357
+ postMessage(new Uint8Array(0));
358
+ ```
359
+
360
+ Supports client-stream cancellation: a `CANCELLED` status message from the client triggers generator cleanup and stops the stream.
361
+
362
+ ---
363
+
364
+ ### `@vampgg/utils/extension-channel` — Browser Extension Channel (Client)
365
+
366
+ ```ts
367
+ import { TempoExtensionChannel } from "@vampgg/utils/extension-channel";
368
+ ```
369
+
370
+ A `BaseChannel` for browser extensions. Sends messages via `browser.runtime.sendMessage` and receives responses via `browser.runtime.onMessage`. Uses `webextension-polyfill` for cross-browser compatibility.
371
+
372
+ ```ts
373
+ const channel = new TempoExtensionChannel({ logger });
374
+ // channel.startUnary(...) etc. — used by tempo-generated client stubs
375
+ ```
376
+
377
+ Messages are encoded as `Array<number>` (bebop bytes as a plain array) to satisfy browser extension message serialization constraints.
378
+
379
+ ---
380
+
381
+ ### `@vampgg/utils/extension-router` — Browser Extension Router (Server)
382
+
383
+ ```ts
384
+ import { TempoExtensionRouter, ExtensionBaseRouter } from "@vampgg/utils/extension-router";
385
+ import { TempoExtensionRouterConfiguration } from "@vampgg/utils/extension-router";
386
+ ```
387
+
388
+ Runs in the extension background script. Dispatches incoming `Message` objects (decoded from `browser.runtime.onMessage`) to registered tempo service methods and sends responses back via `tabs.sendMessage`.
389
+
390
+ ```ts
391
+ const router = new TempoExtensionRouter<{ sender: Runtime.MessageSender }>(
392
+ logger,
393
+ registry,
394
+ new TempoRouterConfiguration(),
395
+ );
396
+
397
+ browser.runtime.onMessage.addListener(async (raw, sender) => {
398
+ const msg = Message(Message.decode(new Uint8Array(raw as number[])));
399
+ const response = Message({});
400
+ await router.process(msg, response, { sender });
401
+ });
402
+ ```
403
+
404
+ ---
405
+
406
+ ### `@vampgg/utils/memory-storage` — In-Memory Credential Storage
407
+
408
+ ```ts
409
+ import { MemoryStorageStrategy } from "@vampgg/utils/memory-storage";
410
+ ```
411
+
412
+ Implements `CredentialStorage` from `@tempojs/client` using a `Map`. Suitable for testing or short-lived processes where persistence is not required.
413
+
414
+ ```ts
415
+ const storage = new MemoryStorageStrategy();
416
+ await storage.storeCredential("session", credential);
417
+ const cred = await storage.getCredential("session");
418
+ await storage.removeCredential("session");
419
+ ```
420
+
421
+ ---
422
+
423
+ ### `@vampgg/utils/sync-storage` — WebExtension Sync Storage
424
+
425
+ ```ts
426
+ import { SyncStorageStrategy } from "@vampgg/utils/sync-storage";
427
+ ```
428
+
429
+ Implements `CredentialStorage` backed by `browser.storage.sync` (via `webextension-polyfill`). Use in browser extension contexts where credentials should persist across browser sessions and sync across devices.
430
+
431
+ ```ts
432
+ const storage = new SyncStorageStrategy();
433
+ await storage.storeCredential("auth", credential);
434
+ ```
435
+
436
+ ---
437
+
438
+ ## Key Patterns
439
+
440
+ ### Adding a New Transport
441
+
442
+ To add a new transport (e.g., SharedWorker, BroadcastChannel):
443
+
444
+ 1. **Client side** — extend `BaseChannel` from `@tempojs/client`. Implement `startUnary`, `startClientStream`, `startServerStream`, `startDuplexStream`. Encode requests as `Message` binary (`Message.encode(msg)`), decode responses with `Message.decode(bytes)`. Use `createEventIterator` for server-stream and `createDuplexIterator` for duplex-stream.
445
+
446
+ 2. **Server side** — extend `BaseRouter` from `@tempojs/server`. Implement `process(rawRequest, responseMessage, env)`. Decode the incoming buffer with `Message.decode(...)`, dispatch by `methodId` via `this.registry.getMethod(methodId)`, encode responses with `Message.encode(response)`.
447
+
448
+ ### Schema Regeneration
449
+
450
+ After editing `schema/error.bop` or `schema/message.bop`, regenerate the TypeScript bindings:
451
+
452
+ ```bash
453
+ bebopc build
454
+ # or
455
+ vp run build # runs bebopc build then packs the library
456
+ ```
457
+
458
+ Never manually edit `src/bebop.ts`.
459
+
460
+ ### Wire Protocol
461
+
462
+ All transports use the same protocol:
463
+
464
+ 1. Client creates a `Message` with `methodId`, `messageId` (UUID), `data` (bebop-encoded request body), optional `deadline` and `authorization`.
465
+ 2. Server decodes the `Message`, routes by `methodId`, executes the method, and replies with a `Message` containing the same `messageId`, `status: OK`, and `data` (bebop-encoded response body).
466
+ 3. For streams, the server sends multiple frames with `status: OK` and terminates with `status: CANCELLED` and an empty `data`.
467
+ 4. Errors are returned as a `Message` with a non-OK `status` and a human-readable `msg`.
@@ -0,0 +1,68 @@
1
+ //#region src/async-queue.d.ts
2
+ /**
3
+ * Single-consumer, multi-producer bounded async queue.
4
+ *
5
+ * Replaces the ad-hoc "array + single wakeup promise" queues that previously
6
+ * backed `createEventIterator` / `createDuplexIterator`. Properties:
7
+ * - Ring buffer: O(1) enqueue/dequeue, never `Array.shift()`.
8
+ * - Bounded: `highWaterMark` caps buffered values; `push` returns `false` so
9
+ * producers can apply backpressure, and `overflow: "error"` turns a runaway
10
+ * producer into a thrown error instead of silent unbounded growth (OOM).
11
+ * - Error channel: `fail(e)` makes the consumer's `next()` throw `e`.
12
+ * - Lifecycle: a single `onDispose` runs exactly once on the first terminal
13
+ * transition (close / error / return / throw).
14
+ *
15
+ * Single-consumer contract: at most one `next()` may be outstanding at a time.
16
+ * Every current use is a single `for await` / `yield*`, which satisfies this.
17
+ */
18
+ interface AsyncQueueOptions {
19
+ /**
20
+ * Maximum number of buffered, not-yet-consumed values. When the buffer is
21
+ * full, `push` returns `false` (overflow). Defaults to 1024.
22
+ */
23
+ highWaterMark?: number;
24
+ /**
25
+ * Behavior when `push` is called on a full buffer:
26
+ * - "error" (default): the queue fails with an overflow error that is thrown
27
+ * into the consumer at the next `next()`.
28
+ * - "drop-latest": the pushed value is dropped, `push` returns false.
29
+ * Producers should honor the boolean return value to apply backpressure.
30
+ */
31
+ overflow?: "error" | "drop-latest";
32
+ }
33
+ declare class AsyncQueueOverflowError extends Error {
34
+ constructor(highWaterMark: number);
35
+ }
36
+ declare class AsyncQueue<T> implements AsyncIterableIterator<T> {
37
+ private buffer;
38
+ private head;
39
+ private count;
40
+ private readonly highWaterMark;
41
+ private readonly overflow;
42
+ private pendingResolve;
43
+ private pendingReject;
44
+ private closed;
45
+ private error;
46
+ private hasError;
47
+ private onDispose?;
48
+ private disposed;
49
+ constructor(opts?: AsyncQueueOptions, onDispose?: () => void | Promise<void>);
50
+ /** Producer: enqueue a value. Returns false if the buffer is full (backpressure signal). */
51
+ push(value: T): boolean;
52
+ /** Producer: terminate the stream as a clean completion. Idempotent. */
53
+ close(): void;
54
+ /** Producer: terminate the stream with an error thrown into the consumer. Idempotent. */
55
+ fail(error: unknown): void;
56
+ next(): Promise<IteratorResult<T>>;
57
+ /** Consumer aborted early (break / return). Runs dispose and ends the stream. */
58
+ return(value?: unknown): Promise<IteratorResult<T>>;
59
+ /** Consumer threw. Propagate after dispose. */
60
+ throw(e?: unknown): Promise<IteratorResult<T>>;
61
+ [Symbol.asyncIterator](): AsyncIterableIterator<T>;
62
+ private enqueue;
63
+ private dequeue;
64
+ private grow;
65
+ private dispose;
66
+ }
67
+ //#endregion
68
+ export { AsyncQueue, AsyncQueueOptions, AsyncQueueOverflowError };