@net-mesh/core 0.21.0 → 0.23.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/mesh_rpc.d.ts CHANGED
@@ -72,11 +72,240 @@ export interface RawMeshRpc {
72
72
  call(targetNodeId: bigint, service: string, request: Buffer, opts?: CallOptions): Promise<Buffer>;
73
73
  callService(service: string, request: Buffer, opts?: CallOptions): Promise<Buffer>;
74
74
  callStreaming(targetNodeId: bigint, service: string, request: Buffer, opts?: CallOptions): Promise<RawRpcStream>;
75
+ /**
76
+ * Open a client-streaming call. Returns a `RawClientStreamCall`
77
+ * — push chunks via `send`, then `finish` to await the terminal
78
+ * response.
79
+ */
80
+ callClientStream(targetNodeId: bigint, service: string, opts?: CallOptions): Promise<RawClientStreamCall>;
81
+ /**
82
+ * Register a client-streaming handler. The handler receives a
83
+ * `RawRequestStream` and returns the terminal response as a
84
+ * Buffer. Throw `appError(code, body)` to surface a typed
85
+ * Application status.
86
+ */
87
+ serveClientStream(service: string, handler: (stream: RawRequestStream) => Promise<Buffer>): ServeHandle;
88
+ /**
89
+ * Open a duplex call. Both `requestWindowInitial` (upload flow
90
+ * control) and `streamWindowInitial` (download flow control)
91
+ * on `CallOptions` are independently opt-in.
92
+ */
93
+ callDuplex(targetNodeId: bigint, service: string, opts?: CallOptions): Promise<RawDuplexCall>;
94
+ /**
95
+ * Register a duplex handler. The napi side passes a 2-tuple
96
+ * `[stream, sink]` to the handler (per the
97
+ * `DuplexHandlerArgs::ToNapiValue` impl in the napi binding).
98
+ * Handler returns a terminal Buffer that the substrate discards;
99
+ * intermediate chunks come from `sink.send`. Throw
100
+ * `appError(code, body)` to surface a typed Application status.
101
+ */
102
+ serveDuplex(service: string, handler: (args: [RawRequestStream, RawResponseSink]) => Promise<Buffer>): ServeHandle;
75
103
  findServiceNodes(service: string): bigint[];
76
104
  /** Mint a fresh cancel token (`bigint`). */
77
105
  reserveCancelToken(): bigint;
78
106
  /** Abort the in-flight call associated with `token`. Idempotent. */
79
107
  cancelCall(token: bigint): void;
108
+ /**
109
+ * Install (or clear with `null` / `undefined`) the caller-side
110
+ * nRPC observer. The handler receives the napi POD
111
+ * (`RawRpcCallEvent`); the typed wrapper layer normalizes into
112
+ * a tagged-union {@link RpcCallEvent} before reaching user code.
113
+ * See `TypedMeshRpc.setObserver` for the locked-decision
114
+ * contract.
115
+ */
116
+ setObserver(observer: ((evt: RawRpcCallEvent) => void) | null | undefined): void;
117
+ /** Snapshot the per-service nRPC metrics registry. */
118
+ metricsSnapshot(): RpcMetricsSnapshot;
119
+ }
120
+ /**
121
+ * Status discriminant for an observed RPC call. Match on `kind`
122
+ * before reading other fields; this is a string-tagged union
123
+ * because the napi POD layer doesn't support TS discriminated
124
+ * unions natively (the napi side flattens it to `statusKind:
125
+ * string` + `statusMessage?: string` — see {@link RpcCallEvent}).
126
+ */
127
+ export type RpcCallStatus = {
128
+ kind: 'ok';
129
+ } | {
130
+ kind: 'error';
131
+ message: string;
132
+ } | {
133
+ kind: 'timeout';
134
+ } | {
135
+ kind: 'canceled';
136
+ };
137
+ /**
138
+ * Single observed RPC call boundary. Surfaced to the observer
139
+ * callback installed via `TypedMeshRpc.setObserver`. Mirrors the
140
+ * substrate's `RpcCallEvent` field-by-field — the typed wrapper
141
+ * reconstructs the `RpcCallStatus` tagged union from the napi
142
+ * POD's flat `statusKind` / `statusMessage` pair so user code
143
+ * sees a TS-idiomatic discriminated union.
144
+ */
145
+ export interface RpcCallEvent {
146
+ /** 64-bit node id of the calling node. */
147
+ caller: bigint;
148
+ /** 64-bit node id of the responding node. */
149
+ callee: bigint;
150
+ /** Service / method name. */
151
+ method: string;
152
+ /** Elapsed time in milliseconds. */
153
+ latencyMs: number;
154
+ /** Tagged-union outcome. Match on `status.kind`. */
155
+ status: RpcCallStatus;
156
+ /** Wire payload size of the request body (0 when not available). */
157
+ requestBytes: number;
158
+ /** Wire payload size of the response body (0 when not available). */
159
+ responseBytes: number;
160
+ /** v1 only emits `"outbound"`; `"inbound"` is reserved. */
161
+ direction: 'outbound' | 'inbound';
162
+ /** Unix-ms timestamp captured at fire time. */
163
+ tsUnixMs: bigint;
164
+ }
165
+ /**
166
+ * Status-kind discriminants emitted on `RawRpcCallEvent.statusKind`
167
+ * and consumed when normalizing into the tagged-union
168
+ * {@link RpcCallStatus}. Prefer these constants over hard-coding
169
+ * the string literal in `if (evt.statusKind === '...')` checks —
170
+ * a typo on the literal silently never fires.
171
+ *
172
+ * Named `STATUS_KIND_*` to disambiguate from the wire-level
173
+ * `STATUS_*` u16 status codes used by `RpcServerError`.
174
+ */
175
+ export declare const STATUS_KIND_OK: "ok";
176
+ export declare const STATUS_KIND_ERROR: "error";
177
+ export declare const STATUS_KIND_TIMEOUT: "timeout";
178
+ export declare const STATUS_KIND_CANCELED: "canceled";
179
+ /** Direction-kind discriminants on `RawRpcCallEvent.direction`. */
180
+ export declare const DIRECTION_OUTBOUND: "outbound";
181
+ export declare const DIRECTION_INBOUND: "inbound";
182
+ export type StatusKind = typeof STATUS_KIND_OK | typeof STATUS_KIND_ERROR | typeof STATUS_KIND_TIMEOUT | typeof STATUS_KIND_CANCELED;
183
+ export type DirectionKind = typeof DIRECTION_OUTBOUND | typeof DIRECTION_INBOUND;
184
+ /**
185
+ * Raw napi observer event shape (POD with flat `statusKind` /
186
+ * `statusMessage` strings). Internal — the typed wrapper
187
+ * normalizes it into {@link RpcCallEvent} before reaching user
188
+ * code. Exported so test stubs can construct events directly.
189
+ */
190
+ export interface RawRpcCallEvent {
191
+ caller: bigint;
192
+ callee: bigint;
193
+ method: string;
194
+ latencyMs: number;
195
+ statusKind: StatusKind;
196
+ statusMessage?: string | null;
197
+ requestBytes: number;
198
+ responseBytes: number;
199
+ direction: DirectionKind;
200
+ tsUnixMs: bigint;
201
+ }
202
+ /**
203
+ * Per-service caller + server-side nRPC counters at a point in
204
+ * time. Element of `RpcMetricsSnapshot.services`. All `bigint`
205
+ * fields are direct u64 counters; `inFlight` and `handlerInFlight`
206
+ * are signed i64 to track concurrent calls without underflow.
207
+ */
208
+ export interface ServiceMetrics {
209
+ service: string;
210
+ callsTotal: bigint;
211
+ errorsNoRoute: bigint;
212
+ errorsTimeout: bigint;
213
+ errorsServer: bigint;
214
+ errorsTransport: bigint;
215
+ inFlight: number;
216
+ latencySumNs: bigint;
217
+ latencyCount: bigint;
218
+ /**
219
+ * Cumulative histogram bucket counts; index `i` corresponds to
220
+ * the substrate's `DEFAULT_LATENCY_BUCKETS_SECS[i]`. Last entry
221
+ * is the `+Inf` bucket.
222
+ */
223
+ latencyBuckets: bigint[];
224
+ handlerInvocationsTotal: bigint;
225
+ handlerPanicsTotal: bigint;
226
+ handlerInFlight: number;
227
+ handlerDurationSumNs: bigint;
228
+ handlerDurationCount: bigint;
229
+ handlerDurationBuckets: bigint[];
230
+ streamingChunksEmittedTotal: bigint;
231
+ streamingChunksDroppedTotal: bigint;
232
+ capabilityDeniedTotal: bigint;
233
+ }
234
+ /**
235
+ * Snapshot of the per-service nRPC metrics registry. Returned by
236
+ * `TypedMeshRpc.metricsSnapshot`. Cheap — one DashMap iteration
237
+ * on the substrate side. Safe to collect on every Prometheus
238
+ * scrape.
239
+ */
240
+ export interface RpcMetricsSnapshot {
241
+ /**
242
+ * One entry per service that has been called at least once
243
+ * since the mesh was created. Sorted by service name.
244
+ */
245
+ services: ServiceMetrics[];
246
+ }
247
+ /**
248
+ * Raw napi `ClientStreamCall` — minimal shape consumed by
249
+ * `TypedClientStreamCall`. Caller pushes typed chunks via `send`
250
+ * then awaits the terminal response with `finish`.
251
+ */
252
+ export interface RawClientStreamCall {
253
+ send(body: Buffer): Promise<void>;
254
+ finish(): Promise<Buffer>;
255
+ callId(): Promise<bigint>;
256
+ flowControlled(): Promise<boolean>;
257
+ close(): Promise<void>;
258
+ }
259
+ /**
260
+ * Raw napi `JsRequestStream` — minimal shape consumed by
261
+ * `TypedRequestStream` server-side. Drain via `next()` until it
262
+ * returns `null` (clean EOF) or throws on terminal error.
263
+ *
264
+ * The diagnostic getters (`callerOrigin`, `callId`, `deadlineNs`,
265
+ * `headers`) are populated at handler-dispatch time and stable
266
+ * for the lifetime of the stream.
267
+ */
268
+ export interface RawRequestStream {
269
+ next(): Promise<Buffer | null>;
270
+ readonly callerOrigin: bigint;
271
+ readonly callId: bigint;
272
+ readonly deadlineNs: bigint;
273
+ readonly headers: [string, Buffer][];
274
+ }
275
+ /**
276
+ * Raw napi `DuplexCall` — combined send + receive surface. Use
277
+ * `intoSplit` to separate into independent sink + stream halves.
278
+ */
279
+ export interface RawDuplexCall {
280
+ send(body: Buffer): Promise<void>;
281
+ finishSending(): Promise<void>;
282
+ next(): Promise<Buffer | null>;
283
+ intoSplit(): Promise<[RawDuplexSink, RawDuplexStream]>;
284
+ callId(): Promise<bigint>;
285
+ flowControlled(): Promise<boolean>;
286
+ close(): Promise<void>;
287
+ }
288
+ /** Send-half of a `RawDuplexCall` after `intoSplit`. */
289
+ export interface RawDuplexSink {
290
+ send(body: Buffer): Promise<void>;
291
+ finish(): Promise<void>;
292
+ callId(): Promise<bigint>;
293
+ flowControlled(): Promise<boolean>;
294
+ close(): Promise<void>;
295
+ }
296
+ /** Receive-half of a `RawDuplexCall` after `intoSplit`. */
297
+ export interface RawDuplexStream {
298
+ next(): Promise<Buffer | null>;
299
+ callId(): Promise<bigint>;
300
+ close(): Promise<void>;
301
+ }
302
+ /**
303
+ * Raw napi `JsResponseSink` — outbound side surfaced to duplex
304
+ * server handlers. Non-async (try_send under the hood; drops on
305
+ * mpsc overflow). Returns `false` if the sink has been closed.
306
+ */
307
+ export interface RawResponseSink {
308
+ send(body: Buffer): boolean;
80
309
  }
81
310
  /** Raw napi `RpcStream` — minimal shape consumed by `TypedRpcStream`. */
82
311
  export interface RawRpcStream {
@@ -136,6 +365,94 @@ export declare class TypedMeshRpc {
136
365
  callStreaming<Req = unknown, Resp = unknown>(targetNodeId: bigint, service: string, req: Req, opts?: CallOptions): Promise<TypedRpcStream<Resp>>;
137
366
  /** Pass-through to `MeshRpc.findServiceNodes`. */
138
367
  findServiceNodes(service: string): bigint[];
368
+ /**
369
+ * Open a typed client-streaming call. Returns a
370
+ * `TypedClientStreamCall<Req, Resp>` — push each request via
371
+ * `send(value)`, then `finish()` to drain the terminal
372
+ * response.
373
+ *
374
+ * Cancellation (v3 / C-A1): `opts.signal` is wired end-to-end
375
+ * via the same `wireAbortSignal` helper unary calls use.
376
+ * `signal.aborted` triggers the substrate's
377
+ * `MeshNode::cancel(token)`, which drops the pending
378
+ * client-stream entry — the next `send()` / `finish()` observes
379
+ * a stream-closed error within bounded time, and the call's
380
+ * Drop emits CANCEL on the wire. Invoking `typedCall.close()`
381
+ * explicitly is still supported as the imperative cancel
382
+ * surface.
383
+ */
384
+ callClientStream<Req = unknown, Resp = unknown>(targetNodeId: bigint, service: string, opts?: CallOptions): Promise<TypedClientStreamCall<Req, Resp>>;
385
+ /**
386
+ * Register a typed client-streaming handler. The handler
387
+ * receives a `TypedRequestStream<Req>` (auto-decodes each
388
+ * inbound chunk to `Req`) and returns the terminal response
389
+ * `Resp` (encoded back to JSON before reaching the caller).
390
+ *
391
+ * Decode failure on a chunk surfaces from `stream.next()` as
392
+ * `nrpc:codec_decode:` — the handler may catch it (e.g. skip
393
+ * the bad chunk) or let it propagate. Letting it propagate
394
+ * surfaces to the caller as `RpcServerError(Internal)`; the
395
+ * handler may instead wrap it with `appError(NRPC_TYPED_BAD_REQUEST,
396
+ * ...)` to signal a typed bad-request status code.
397
+ */
398
+ serveClientStream<Req = unknown, Resp = unknown>(service: string, handler: (stream: TypedRequestStream<Req>) => Promise<Resp>): ServeHandle;
399
+ /**
400
+ * Open a typed duplex call. Returns a `TypedDuplexCall<Req,
401
+ * Resp>` — push typed requests via `send`, pull typed responses
402
+ * via `next` (or `for await`), or `intoSplit` to separate the
403
+ * halves for the encoder-task / decoder-task pattern.
404
+ *
405
+ * Cancellation (v3 / C-A1): `opts.signal` is wired through to
406
+ * the substrate's cancel-token primitive — same pattern as
407
+ * `callClientStream`. Aborting the signal drops both halves
408
+ * cleanly; the receive side observes EOF on its next pull and
409
+ * Drop emits CANCEL on the wire. `typedCall.close()` is still
410
+ * supported as the imperative cancel surface.
411
+ */
412
+ callDuplex<Req = unknown, Resp = unknown>(targetNodeId: bigint, service: string, opts?: CallOptions): Promise<TypedDuplexCall<Req, Resp>>;
413
+ /**
414
+ * Register a typed duplex handler. The user signature is
415
+ * `(stream: TypedRequestStream<Req>, sink: TypedResponseSink<Resp>) => Promise<void>`
416
+ * — the wrapper destructures the napi-side `[stream, sink]`
417
+ * tuple internally so the user handler never sees the array
418
+ * form. Handler return is `void`; the substrate emits the
419
+ * terminal frame automatically. To surface a typed Application
420
+ * status, throw `appError(code, body)` from the handler.
421
+ */
422
+ serveDuplex<Req = unknown, Resp = unknown>(service: string, handler: (stream: TypedRequestStream<Req>, sink: TypedResponseSink<Resp>) => Promise<void>): ServeHandle;
423
+ /**
424
+ * Install (pass a handler) or clear (pass `null`) the caller-side
425
+ * nRPC observer. The handler fires once per completed outbound
426
+ * RPC with a decoded {@link RpcCallEvent} — the tagged
427
+ * `status` discriminator is reconstructed from the napi POD's
428
+ * flat `statusKind` / `statusMessage` fields.
429
+ *
430
+ * **Callback contract (locked decision #1).** The handler fires
431
+ * synchronously from the substrate's dispatch path via a TSFN in
432
+ * `NonBlocking` mode — if the Node event loop is wedged, events
433
+ * are **dropped**, not buffered. Callbacks must therefore be
434
+ * cheap: push into a queue or ring buffer for slow consumers;
435
+ * do not do work inline. A bounded-mpsc + drop-counter layer is
436
+ * a deliberate post-v1 follow-up.
437
+ *
438
+ * **Mid-call swap.** The substrate uses `ArcSwap` for the
439
+ * observer slot, so swaps are atomic — but a swap mid-call
440
+ * means some events fire against the old handler, some against
441
+ * the new. Set the observer once at startup for clean
442
+ * semantics.
443
+ *
444
+ * v1 only emits `direction === 'outbound'` events; the
445
+ * server-side `'inbound'` hook is a planned follow-up.
446
+ */
447
+ setObserver(handler: ((evt: RpcCallEvent) => void) | null): void;
448
+ /**
449
+ * Snapshot the per-service nRPC metrics registry. Cheap — one
450
+ * DashMap iteration on the substrate side. Safe to collect on
451
+ * every Prometheus scrape. The returned object is a plain POD
452
+ * (no class wrapping); read fields directly or feed into your
453
+ * own exporter.
454
+ */
455
+ metricsSnapshot(): RpcMetricsSnapshot;
139
456
  /**
140
457
  * Direct-addressed typed call with retry. See {@link RetryPolicy}.
141
458
  */
@@ -167,6 +484,232 @@ export declare class TypedRpcStream<Resp = unknown> implements AsyncIterable<Res
167
484
  /** Close the stream; emits CANCEL to the server. Idempotent. */
168
485
  close(): Promise<void>;
169
486
  }
487
+ /**
488
+ * Typed client-streaming call handle. Push typed requests via
489
+ * `send`, then await the terminal response with `finish`. Drop
490
+ * / `close` fires CANCEL via the underlying raw call's drop.
491
+ */
492
+ export declare class TypedClientStreamCall<Req = unknown, Resp = unknown> {
493
+ private readonly _raw;
494
+ private readonly _detachSignal;
495
+ /**
496
+ * `detachSignal` is the cleanup function returned by
497
+ * `wireAbortSignal`. Run on `close()` (or on call resolution
498
+ * via `finish()`) so the AbortSignal listener doesn't outlive
499
+ * the call.
500
+ */
501
+ constructor(rawCall: RawClientStreamCall, detachSignal?: () => void);
502
+ /** Underlying raw call for users who want the Buffer-level surface. */
503
+ get raw(): RawClientStreamCall;
504
+ /**
505
+ * Encode `value` as JSON and push it as one request chunk.
506
+ * Throws `nrpc:codec_encode:` if encoding fails (the chunk is
507
+ * NOT sent in that case).
508
+ */
509
+ send(value: Req): Promise<void>;
510
+ /**
511
+ * Close the upload direction and await the terminal response.
512
+ * Consumes the call — subsequent `send` / `finish` throw
513
+ * `stream_closed`. Decode failure on the terminal Buffer
514
+ * surfaces as `nrpc:codec_decode:`.
515
+ */
516
+ finish(): Promise<Resp>;
517
+ /** Server-assigned `call_id`. */
518
+ callId(): Promise<bigint>;
519
+ /** `true` if the call was opened with `requestWindowInitial`. */
520
+ flowControlled(): Promise<boolean>;
521
+ /**
522
+ * Close without finishing. Fires CANCEL on the wire if the
523
+ * initial REQUEST has flown. Idempotent. A concurrent in-flight
524
+ * `send()` awaiting flow-control credit observes `stream_closed`.
525
+ *
526
+ * Detaches the `AbortSignal` listener (if one was wired by
527
+ * `TypedMeshRpc.callClientStream(opts.signal)`) so the
528
+ * signal can be reused for a subsequent call.
529
+ */
530
+ close(): Promise<void>;
531
+ }
532
+ /**
533
+ * Typed inbound request stream surfaced to client-streaming +
534
+ * duplex server handlers. Drain via `next()` until it returns
535
+ * `null` (clean EOF) or implements `AsyncIterable<Req>` for
536
+ * `for await (const req of stream)` style.
537
+ *
538
+ * Decode failure on a chunk surfaces as `nrpc:codec_decode:` —
539
+ * the handler may catch and skip, or let it propagate to abort
540
+ * the call. The underlying raw stream is closed on decode
541
+ * failure so subsequent `next()` returns `null`.
542
+ */
543
+ export declare class TypedRequestStream<Req = unknown> implements AsyncIterable<Req> {
544
+ private readonly _raw;
545
+ private _done;
546
+ constructor(rawStream: RawRequestStream);
547
+ /** Underlying raw stream for users who need the Buffer-level surface. */
548
+ get raw(): RawRequestStream;
549
+ /** Caller's peer origin hash (`0n` on the loopback fast path). */
550
+ get callerOrigin(): bigint;
551
+ /** Server-assigned `call_id`. */
552
+ get callId(): bigint;
553
+ /**
554
+ * Caller's declared deadline as a Unix-nanoseconds absolute
555
+ * timestamp. `0n` means no deadline was declared.
556
+ */
557
+ get deadlineNs(): bigint;
558
+ /**
559
+ * Initial-REQUEST headers carried by the caller. Each entry is
560
+ * `[name, value]` with `name` lowercase.
561
+ */
562
+ get headers(): [string, Buffer][];
563
+ /**
564
+ * Pull the next decoded request. Returns `null` on clean EOF.
565
+ * Throws `nrpc:codec_decode:` on decode failure (the underlying
566
+ * raw stream is marked closed; subsequent `next()` returns
567
+ * `null`).
568
+ */
569
+ next(): Promise<Req | null>;
570
+ /** Async iterator support: `for await (const req of stream) { ... }`. */
571
+ [Symbol.asyncIterator](): AsyncIterator<Req>;
572
+ }
573
+ /**
574
+ * Typed duplex call handle. Push typed requests via `send`,
575
+ * pull typed responses via `next` or `for await`, or call
576
+ * `intoSplit` to peel off independent sink + stream halves.
577
+ *
578
+ * After `intoSplit` returns, this call is "consumed" — calling
579
+ * `send` / `finishSending` / `next` / `close` on it throws.
580
+ */
581
+ export declare class TypedDuplexCall<Req = unknown, Resp = unknown> implements AsyncIterable<Resp> {
582
+ private readonly _raw;
583
+ private _done;
584
+ private readonly _detachSignal;
585
+ /**
586
+ * `detachSignal` is the cleanup function returned by
587
+ * `wireAbortSignal`. Run on `close()` (or transferred to one
588
+ * of the split halves on `intoSplit`) so the AbortSignal
589
+ * listener doesn't outlive the call.
590
+ */
591
+ constructor(rawCall: RawDuplexCall, detachSignal?: () => void);
592
+ /** Underlying raw call for users who want the Buffer-level surface. */
593
+ get raw(): RawDuplexCall;
594
+ /** Encode + push one request chunk. */
595
+ send(value: Req): Promise<void>;
596
+ /** Close the upload direction (emit REQUEST_END). */
597
+ finishSending(): Promise<void>;
598
+ /**
599
+ * Pull the next decoded response. Returns `null` on clean EOF.
600
+ * Decode failure throws `nrpc:codec_decode:` and closes the
601
+ * underlying duplex call — subsequent `next()` returns `null`.
602
+ */
603
+ next(): Promise<Resp | null>;
604
+ /** Async iterator support over the response stream. */
605
+ [Symbol.asyncIterator](): AsyncIterator<Resp>;
606
+ /**
607
+ * Split the duplex into independent typed sink + stream halves.
608
+ * After return, this `TypedDuplexCall` is consumed — subsequent
609
+ * `send` / `finishSending` / `next` throw `stream_closed`.
610
+ * CANCEL fires only when BOTH split halves drop without
611
+ * observing the response stream's terminal frame.
612
+ *
613
+ * The AbortSignal listener (if one was wired) transfers to the
614
+ * sink half — the sink is the half that issues the upload,
615
+ * which is typically dropped first. Wherever the listener
616
+ * ends up, it stays attached until that half's `close()` runs;
617
+ * the stream half can still observe cancel-mid-flight via the
618
+ * substrate's cancel-token primitive even if the sink half's
619
+ * listener has already detached.
620
+ */
621
+ intoSplit(): Promise<[
622
+ TypedDuplexSink<Req>,
623
+ TypedDuplexStream<Resp>
624
+ ]>;
625
+ /** Server-assigned `call_id`. */
626
+ callId(): Promise<bigint>;
627
+ /**
628
+ * `true` if the call was opened with non-`None`
629
+ * `requestWindowInitial`. Reports the UPLOAD-direction
630
+ * flow-control state.
631
+ */
632
+ flowControlled(): Promise<boolean>;
633
+ /**
634
+ * Close without observing the response terminator. Fires
635
+ * CANCEL on the wire. Idempotent. Concurrent in-flight
636
+ * `send()` awaiting credit observes `stream_closed`.
637
+ *
638
+ * Detaches the `AbortSignal` listener (if one was wired by
639
+ * `TypedMeshRpc.callDuplex(opts.signal)`) so the signal can
640
+ * be reused for a subsequent call.
641
+ */
642
+ close(): Promise<void>;
643
+ }
644
+ /** Send-half of a typed duplex call after `intoSplit`. */
645
+ export declare class TypedDuplexSink<Req = unknown> {
646
+ private readonly _raw;
647
+ private readonly _detachSignal;
648
+ constructor(rawSink: RawDuplexSink, detachSignal?: () => void);
649
+ /** Underlying raw sink for Buffer-level access. */
650
+ get raw(): RawDuplexSink;
651
+ /** Encode + push one request chunk. */
652
+ send(value: Req): Promise<void>;
653
+ /** Close the upload direction (emit REQUEST_END). */
654
+ finish(): Promise<void>;
655
+ /** Server-assigned `call_id`. */
656
+ callId(): Promise<bigint>;
657
+ /** `true` if the call was opened with `requestWindowInitial`. */
658
+ flowControlled(): Promise<boolean>;
659
+ /**
660
+ * Close without emitting REQUEST_END. Idempotent. Concurrent
661
+ * in-flight `send()` awaiting credit observes `stream_closed`.
662
+ *
663
+ * Detaches the AbortSignal listener (if one was transferred from
664
+ * the parent `TypedDuplexCall` via `intoSplit`).
665
+ */
666
+ close(): Promise<void>;
667
+ }
668
+ /** Receive-half of a typed duplex call after `intoSplit`. */
669
+ export declare class TypedDuplexStream<Resp = unknown> implements AsyncIterable<Resp> {
670
+ private readonly _raw;
671
+ private _done;
672
+ constructor(rawStream: RawDuplexStream);
673
+ /** Underlying raw stream for Buffer-level access. */
674
+ get raw(): RawDuplexStream;
675
+ /**
676
+ * Pull the next decoded response. Returns `null` on clean EOF.
677
+ * Decode failure throws `nrpc:codec_decode:` and closes the
678
+ * underlying stream.
679
+ */
680
+ next(): Promise<Resp | null>;
681
+ /** Async iterator support over the response stream. */
682
+ [Symbol.asyncIterator](): AsyncIterator<Resp>;
683
+ /** Server-assigned `call_id`. */
684
+ callId(): Promise<bigint>;
685
+ /** Close the stream. Idempotent. */
686
+ close(): Promise<void>;
687
+ }
688
+ /**
689
+ * Typed outbound response sink for duplex server handlers.
690
+ * Non-async — mirrors `RawResponseSink.send`. Returns `true`
691
+ * when the chunk was enqueued; `false` if the underlying sink
692
+ * is closed. Encode failure throws `nrpc:codec_encode:` and the
693
+ * chunk is NOT sent.
694
+ *
695
+ * Flow control: the underlying sink `try_send`s into a bounded
696
+ * 1024-chunk mpsc; bursts past the credit window are dropped
697
+ * (counted by `streaming_chunks_dropped_total`). Pace your
698
+ * `send` calls via REQUEST_GRANT cadence for lossless flow
699
+ * control.
700
+ */
701
+ export declare class TypedResponseSink<Resp = unknown> {
702
+ private readonly _raw;
703
+ constructor(rawSink: RawResponseSink);
704
+ /** Underlying raw sink for Buffer-level access. */
705
+ get raw(): RawResponseSink;
706
+ /**
707
+ * Encode + emit one response chunk. Returns `true` on
708
+ * successful enqueue; `false` if the sink has been closed.
709
+ * Throws `nrpc:codec_encode:` on encoding failure.
710
+ */
711
+ send(value: Resp): boolean;
712
+ }
170
713
  export declare function defaultRetryable(err: unknown): boolean;
171
714
  export interface RetryPolicyOptions {
172
715
  /** Total attempts (NOT additional retries). Default 3. Must be >= 1. */
@@ -295,3 +838,11 @@ export declare function appError(code: number, body: string | Buffer): Error;
295
838
  export declare const NRPC_TYPED_BAD_REQUEST: 32768;
296
839
  /** RpcStatus::Application(0x8001): typed handler returned `throw`. */
297
840
  export declare const NRPC_TYPED_HANDLER_ERROR: 32769;
841
+ /**
842
+ * Convert the napi POD shape (`RawRpcCallEvent` with flat
843
+ * `statusKind` / `statusMessage` fields) into the TS-idiomatic
844
+ * tagged `RpcCallEvent`. Exported for tests that need to
845
+ * synthesize observer events; production code receives already-
846
+ * decoded events via `TypedMeshRpc.setObserver`.
847
+ */
848
+ export declare function rawEventToTyped(raw: RawRpcCallEvent): RpcCallEvent;