@lesomnus/grpc-dgram 0.0.1 → 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.
@@ -1,5 +1,19 @@
1
- import { A as DEFAULT_MAX_SEND_MSG_SIZE, B as checkSendSize, C as isTerminal, E as resetFor, F as Latch, G as noop, H as decompressPayload, I as Sweeper, J as reliableRxSize, K as nowMs, M as FlowReceiver, N as FlowSender, O as shapeOf, P as FrameQueue, Q as hasTransportInfo, R as abortListener, S as isReset, T as legalShape, U as getCompressor, V as compressPayload, W as nonzeroEpoch, X as unrefTimer, Y as sizeOr, Z as hasConnAttacher, _ as isData, at as validateMetadata, ct as resolveLimits, f as frame, g as isCompressed, k as DEFAULT_MAX_RECV_MSG_SIZE, lt as resolveRxConfig, m as hasUnknownFlags, p as frameStatus, q as rawPayload, x as isPing, y as isHeaderFrame, z as checkRecvSize } from "./wire-BR8KiyRg.mjs";
1
+ import { $ as sizeOr, A as DEFAULT_MAX_SEND_MSG_SIZE, C as isTerminal, E as resetFor, F as Latch, G as compressPayload, H as acquireBoth, I as PeerFlowRx, J as nonzeroEpoch, K as decompressPayload, L as Sweeper, M as FlowReceiver, N as FlowSender, O as shapeOf, P as FrameQueue, Q as reliableRxSize, S as isReset, T as legalShape, U as checkRecvSize, V as abortListener, W as checkSendSize, X as nowMs, Y as noop, Z as rawPayload, _ as isData, et as tryAcquireBoth, f as frame, g as isCompressed, k as DEFAULT_MAX_RECV_MSG_SIZE, m as hasUnknownFlags, mt as resolveRxConfig, nt as hasConnAttacher, p as frameStatus, pt as resolveLimits, q as getCompressor, rt as hasTransportInfo, tt as unrefTimer, ut as validateMetadata, x as isPing, y as isHeaderFrame, z as W_CONN } from "./wire-DqHx0oUs.mjs";
2
2
  import { a as isMessageTooLarge, i as abortCause, o as statusError, r as StatusError, s as toStatusError } from "./status-DZwMDWIn.mjs";
3
+ //#region src/interceptor.ts
4
+ /** @internal */
5
+ function chain1(is) {
6
+ if (is === void 0 || is.length === 0) return void 0;
7
+ const snapshot = [...is];
8
+ return (x, last) => snapshot.reduceRight((next, h) => (x2) => h(x2, next), last)(x);
9
+ }
10
+ /** @internal */
11
+ function chain2(is) {
12
+ if (is === void 0 || is.length === 0) return void 0;
13
+ const snapshot = [...is];
14
+ return (x, y, last) => snapshot.reduceRight((next, h) => (x2, y2) => h(x2, y2, next), last)(x, y);
15
+ }
16
+ //#endregion
3
17
  //#region src/seq.ts
4
18
  const W_FWD = 4096;
5
19
  const K_LOUD = 3;
@@ -26,6 +40,12 @@ var RxWindow = class {
26
40
  strict = false;
27
41
  beyondN = 0;
28
42
  beyondLast = 0;
43
+ gap = 0;
44
+ takeGap() {
45
+ const g = this.gap;
46
+ this.gap = 0;
47
+ return g;
48
+ }
29
49
  check(seq) {
30
50
  if (seq === 0) return 2;
31
51
  if (this.strict) {
@@ -38,6 +58,7 @@ var RxWindow = class {
38
58
  const d = seq - this.l >>> 0;
39
59
  if (d === 0 || d >= 2147483648) return 1;
40
60
  if (d <= 4096) {
61
+ this.gap = d - 1;
41
62
  this.l = seq;
42
63
  this.beyondN = 0;
43
64
  return 0;
@@ -50,6 +71,89 @@ var RxWindow = class {
50
71
  }
51
72
  };
52
73
  //#endregion
74
+ //#region src/stats.ts
75
+ var Counters = class {
76
+ n = {
77
+ skipped: 0,
78
+ dropped: 0,
79
+ offShape: 0,
80
+ resetSent: 0,
81
+ resetReceived: 0,
82
+ retransmit: 0,
83
+ probeSent: 0,
84
+ keepaliveSent: 0,
85
+ livenessExpired: 0,
86
+ tombstoneReplay: 0,
87
+ dataLoss: 0,
88
+ flowStall: 0,
89
+ flowResume: 0,
90
+ peerFlowStall: 0,
91
+ peerFlowResume: 0
92
+ };
93
+ observe = (ev) => {
94
+ const n = Math.max(ev.count, 1);
95
+ switch (ev.kind) {
96
+ case "skipped":
97
+ this.n.skipped += n;
98
+ break;
99
+ case "dropped":
100
+ this.n.dropped += n;
101
+ break;
102
+ case "off-shape":
103
+ this.n.offShape += n;
104
+ break;
105
+ case "reset-sent":
106
+ this.n.resetSent++;
107
+ break;
108
+ case "reset-received":
109
+ this.n.resetReceived++;
110
+ break;
111
+ case "retransmit":
112
+ this.n.retransmit++;
113
+ break;
114
+ case "probe-sent":
115
+ this.n.probeSent++;
116
+ break;
117
+ case "keepalive-sent":
118
+ this.n.keepaliveSent++;
119
+ break;
120
+ case "liveness-expired":
121
+ this.n.livenessExpired++;
122
+ break;
123
+ case "tombstone-replay":
124
+ this.n.tombstoneReplay++;
125
+ break;
126
+ case "data-loss":
127
+ this.n.dataLoss++;
128
+ break;
129
+ case "flow-stall":
130
+ this.n.flowStall++;
131
+ break;
132
+ case "flow-resume":
133
+ this.n.flowResume++;
134
+ break;
135
+ case "peer-flow-stall":
136
+ this.n.peerFlowStall++;
137
+ break;
138
+ case "peer-flow-resume":
139
+ this.n.peerFlowResume++;
140
+ break;
141
+ }
142
+ };
143
+ snapshot() {
144
+ return { ...this.n };
145
+ }
146
+ };
147
+ function statsSink(opt) {
148
+ if (opt === void 0) return [];
149
+ return typeof opt === "function" ? [opt] : [...opt];
150
+ }
151
+ function emit(sink, ev) {
152
+ for (const h of sink) try {
153
+ h(ev);
154
+ } catch {}
155
+ }
156
+ //#endregion
53
157
  //#region src/timing.ts
54
158
  function resolveTiming(t = {}) {
55
159
  const callMs = t.callMs ?? 5e3;
@@ -98,16 +202,23 @@ var Conn = class {
98
202
  maxRecv;
99
203
  maxSend;
100
204
  stallMs;
205
+ /** @internal */ pstats;
101
206
  ss = /* @__PURE__ */ new Map();
102
207
  tombs = /* @__PURE__ */ new Map();
103
208
  resetAt = /* @__PURE__ */ new Map();
104
209
  sidNext = 0;
105
210
  exhausted = false;
106
211
  closed = false;
212
+ /** @internal */ connTx = new FlowSender();
213
+ /** @internal */ connRx = new PeerFlowRx();
214
+ srvEpoch = 0;
215
+ srvEpochSet = false;
107
216
  lastRx = 0;
108
217
  lastTx = 0;
109
218
  lastPing = 0;
110
219
  sw = new Sweeper();
220
+ unaryInt;
221
+ streamInt;
111
222
  constructor(tx, opts = {}) {
112
223
  this.epoch = nonzeroEpoch();
113
224
  this.tx = tx;
@@ -121,21 +232,38 @@ var Conn = class {
121
232
  policy: rx.policy
122
233
  };
123
234
  this.limits = resolveLimits(opts.limits);
235
+ if (this.mode.reliable) {
236
+ this.connTx.assume(W_CONN);
237
+ this.connRx.enable(this.limits.maxPeerWindow, 0);
238
+ }
124
239
  this.defaults = opts.defaultCallOptions ?? {};
125
240
  this.compressor = opts.compressor ?? "";
126
241
  this.compressors = new Map(Object.entries(opts.compressors ?? {}));
127
242
  this.maxRecv = sizeOr(opts.maxRecvMsgSize, DEFAULT_MAX_RECV_MSG_SIZE);
128
243
  this.maxSend = sizeOr(opts.maxSendMsgSize, DEFAULT_MAX_SEND_MSG_SIZE);
129
244
  this.stallMs = opts.timing?.stallMs ?? 3e4;
245
+ this.pstats = statsSink(opts.protocolStats);
246
+ this.unaryInt = chain2(opts.unaryInterceptors);
247
+ this.streamInt = chain1(opts.streamInterceptors);
130
248
  if (hasConnAttacher(tx)) tx.attachConn(this);
131
249
  }
132
250
  get reliable() {
133
251
  return this.mode.reliable;
134
252
  }
253
+ protoEvent(kind, sid = 0) {
254
+ if (this.pstats.length === 0) return;
255
+ emit(this.pstats, {
256
+ kind,
257
+ sid,
258
+ method: "",
259
+ count: 0
260
+ });
261
+ }
135
262
  async handle(f, ctx = {}) {
136
263
  const sid = f.sid;
137
264
  if (isReset(f)) {
138
265
  if (f.epoch !== this.epoch) return;
266
+ this.protoEvent("reset-received", sid);
139
267
  const s = this.ss.get(sid);
140
268
  if (s !== void 0) {
141
269
  s.finishReset();
@@ -145,10 +273,14 @@ var Conn = class {
145
273
  return;
146
274
  }
147
275
  if (f.peerEpoch !== this.epoch) {
148
- if (isPing(f) && sid === 0) return;
276
+ if (sid === 0 && (isPing(f) || shapeOf(f) === 16)) return;
149
277
  await this.sendReset(f);
150
278
  return;
151
279
  }
280
+ if (sid === 0 && shapeOf(f) === 16) {
281
+ if (this.mode.reliable && this.serverEpochIs(f.epoch)) this.connTx.grant(f.window);
282
+ return;
283
+ }
152
284
  if (shapeOf(f) === 16) {
153
285
  const s = this.ss.get(sid);
154
286
  if (s !== void 0) await s.handleRx(f, ctx);
@@ -166,6 +298,9 @@ var Conn = class {
166
298
  await s.handleRx(f, ctx);
167
299
  return;
168
300
  }
301
+ this.lockServerEpoch(f.epoch);
302
+ this.observeAdvertisement(f);
303
+ this.creditUnbuffered(f);
169
304
  if (this.tombs.get(sid) !== void 0) {
170
305
  this.lastRx = nowMs();
171
306
  if (isTerminal(f)) this.clearTombAbort(sid);
@@ -177,6 +312,7 @@ var Conn = class {
177
312
  const st = err === void 0 || err === null ? statusError(14, "transport closed") : statusError(14, `transport closed: ${err instanceof Error ? err.message : String(err)}`);
178
313
  this.closed = true;
179
314
  this.failAll(st);
315
+ this.connTx.release();
180
316
  this.sw.stop();
181
317
  const cl = this.tx.close;
182
318
  if (typeof cl === "function") cl.call(this.tx);
@@ -191,6 +327,28 @@ var Conn = class {
191
327
  merged.timeoutMs = this.mode.timing.callMs;
192
328
  timeoutCause = statusError(4, "drpc: default call timeout");
193
329
  }
330
+ const call = {
331
+ desc,
332
+ opts: merged
333
+ };
334
+ const budget = merged.timeoutMs;
335
+ const deadlineAt = budget === void 0 ? void 0 : nowMs() + budget;
336
+ const last = (r, c) => {
337
+ if (deadlineAt === void 0 || c.opts.timeoutMs !== budget) return this.doInvoke(r, c, void 0);
338
+ return this.doInvoke(r, {
339
+ desc: c.desc,
340
+ opts: {
341
+ ...c.opts,
342
+ timeoutMs: deadlineAt - nowMs()
343
+ }
344
+ }, timeoutCause);
345
+ };
346
+ const out = this.unaryInt === void 0 ? await last(req, call) : await this.unaryInt(req, call, last);
347
+ if (out === void 0) throw statusError(13, "drpc: the interceptor chain resolved to no response");
348
+ return out;
349
+ }
350
+ async doInvoke(req, call, timeoutCause) {
351
+ const { desc, opts: merged } = call;
194
352
  const s = this.createStream(desc, merged, timeoutCause);
195
353
  let err;
196
354
  let out;
@@ -215,12 +373,18 @@ var Conn = class {
215
373
  return out;
216
374
  }
217
375
  newStream(desc, opts = {}) {
218
- const merged = {
219
- ...this.defaults,
220
- ...opts
376
+ const call = {
377
+ desc,
378
+ opts: {
379
+ ...this.defaults,
380
+ ...opts
381
+ }
221
382
  };
222
- const s = this.createStream(desc, merged, void 0);
223
- if (desc.clientStreams) s.sendOpen();
383
+ return this.streamInt === void 0 ? this.doNewStream(call) : this.streamInt(call, (c) => this.doNewStream(c));
384
+ }
385
+ doNewStream(call) {
386
+ const s = this.createStream(call.desc, call.opts, void 0);
387
+ if (call.desc.clientStreams) s.sendOpen();
224
388
  return s;
225
389
  }
226
390
  resolveCall(opts) {
@@ -281,6 +445,55 @@ var Conn = class {
281
445
  this.lastRx = nowMs();
282
446
  }
283
447
  /** @internal */
448
+ lockServerEpoch(epoch) {
449
+ const changed = this.srvEpochSet && this.srvEpoch !== epoch;
450
+ this.srvEpoch = epoch;
451
+ this.srvEpochSet = true;
452
+ if (!changed || !this.mode.reliable) return;
453
+ this.connTx.reassume(W_CONN);
454
+ this.connRx.renew();
455
+ }
456
+ /** @internal */
457
+ observeAdvertisement(f) {
458
+ if (!this.mode.reliable || !(isHeaderFrame(f) || isTerminal(f))) return;
459
+ this.connTx.observe(f.connWindow);
460
+ }
461
+ /** @internal */
462
+ get locked() {
463
+ return this.srvEpochSet;
464
+ }
465
+ serverEpochIs(epoch) {
466
+ return this.srvEpochSet && this.srvEpoch === epoch;
467
+ }
468
+ serverCurrent(epoch) {
469
+ return !this.srvEpochSet || this.srvEpoch === epoch;
470
+ }
471
+ /** @internal */
472
+ creditUnbuffered(f) {
473
+ if (!this.mode.reliable || !isData(f) || !this.serverCurrent(f.epoch)) return;
474
+ const g = this.connRx.unadmitted(f.epoch, 1);
475
+ if (g > 0) this.grantPeer(g);
476
+ }
477
+ /** @internal */
478
+ retirePeer(n, epoch) {
479
+ if (n === 0) return;
480
+ const g = this.connRx.retire(epoch, n, this.serverCurrent(epoch));
481
+ if (g > 0) this.grantPeer(g);
482
+ }
483
+ /** @internal */
484
+ get peerWindow() {
485
+ return this.limits.maxPeerWindow;
486
+ }
487
+ grantPeer(n) {
488
+ if (this.closed) return;
489
+ const f = frame({
490
+ epoch: this.epoch,
491
+ flags: 16,
492
+ window: n
493
+ });
494
+ Promise.resolve(this.tx.handle(f)).catch(noop);
495
+ }
496
+ /** @internal */
284
497
  retire(s) {
285
498
  this.ss.delete(s.sid);
286
499
  if (!this.mode.reliable) {
@@ -298,6 +511,7 @@ var Conn = class {
298
511
  tb.retxAt = now + tb.ivalMs;
299
512
  }
300
513
  this.tombs.set(s.sid, tb);
514
+ s.dropRetx();
301
515
  }
302
516
  this.kickSweep();
303
517
  }
@@ -319,6 +533,7 @@ var Conn = class {
319
533
  this.resetAt.set(sid, n);
320
534
  this.kickSweep();
321
535
  }
536
+ this.protoEvent("reset-sent", f.sid);
322
537
  try {
323
538
  await this.tx.handle(resetFor(f));
324
539
  } catch {}
@@ -357,12 +572,14 @@ var Conn = class {
357
572
  for (const [sid, at] of this.resetAt) if (now - at > t.tombstoneMs) this.resetAt.delete(sid);
358
573
  if (this.ss.size > 0) {
359
574
  if (now - this.lastRx >= t.livenessMs) {
575
+ this.protoEvent("liveness-expired");
360
576
  this.failAll(statusError(14, "peer lost"));
361
577
  return;
362
578
  }
363
579
  if (now - this.lastTx >= t.probeMs && now - this.lastPing >= t.probeMs) {
364
580
  this.lastPing = now;
365
581
  this.lastTx = now;
582
+ this.protoEvent("keepalive-sent");
366
583
  Promise.resolve(this.tx.handle(frame({
367
584
  epoch: this.epoch,
368
585
  flags: 8
@@ -370,14 +587,19 @@ var Conn = class {
370
587
  }
371
588
  }
372
589
  for (const s of streams) {
373
- for (const f of s.sweepRetx(now, t.probeMs)) s.transmit(f).catch(noop);
590
+ for (const f of s.sweepRetx(now, t.probeMs)) {
591
+ s.protoEvent("retransmit");
592
+ s.transmit(f).catch(noop);
593
+ }
374
594
  const p = s.probeDue(now, t.probeMs);
375
595
  if (p !== void 0) {
596
+ s.protoEvent("probe-sent");
376
597
  this.lastTx = now;
377
598
  Promise.resolve(this.tx.handle(p)).catch(noop);
378
599
  }
379
600
  }
380
601
  for (const f of tombRetx) {
602
+ this.protoEvent("retransmit", f.sid);
381
603
  this.lastTx = now;
382
604
  Promise.resolve(this.tx.handle(f)).catch(noop);
383
605
  }
@@ -417,6 +639,8 @@ var ClientStream = class {
417
639
  rxWin = new RxWindow();
418
640
  srvEpoch = 0;
419
641
  srvEpochSet = false;
642
+ pinned = 0;
643
+ rxReleased = false;
420
644
  rxq;
421
645
  rxPolicy;
422
646
  rxDropped = 0;
@@ -477,13 +701,43 @@ var ClientStream = class {
477
701
  this.signalDispose = abortListener(this.callerSignal, () => this.abortFromCtx());
478
702
  }
479
703
  }
704
+ pin() {
705
+ if (this.rxReleased) return false;
706
+ this.pinned++;
707
+ return true;
708
+ }
709
+ unpin() {
710
+ if (this.pinned === 0) return false;
711
+ this.pinned--;
712
+ return true;
713
+ }
714
+ undoCredit() {
715
+ if (this.flowTx.undo() && this.conn.isReliable) this.conn.connTx.undo();
716
+ }
717
+ /** @internal */
718
+ protoEvent(kind, count = 0) {
719
+ const sink = this.conn.pstats;
720
+ if (sink.length === 0) return;
721
+ emit(sink, {
722
+ kind,
723
+ sid: this.sid,
724
+ method: this.desc.path,
725
+ count
726
+ });
727
+ }
480
728
  /** @internal */
481
729
  async handleRx(f, ctx) {
482
- if (this.done.tripped) return;
730
+ if (this.done.tripped) {
731
+ this.conn.lockServerEpoch(f.epoch);
732
+ this.conn.observeAdvertisement(f);
733
+ this.conn.creditUnbuffered(f);
734
+ return;
735
+ }
483
736
  if (hasUnknownFlags(f) || !legalShape(shapeOf(f))) {
484
737
  const err = statusError(13, `drpc: frame carries unsupported flags 0x${(f.flags >>> 0).toString(16)}`);
485
738
  this.sendAbort(13);
486
739
  this.finishLocal(err);
740
+ this.conn.creditUnbuffered(f);
487
741
  return;
488
742
  }
489
743
  if (this.srvEpochSet && f.epoch !== this.srvEpoch) {
@@ -498,13 +752,16 @@ var ClientStream = class {
498
752
  if (v === 0 && !this.srvEpochSet) {
499
753
  this.srvEpoch = f.epoch;
500
754
  this.srvEpochSet = true;
755
+ this.conn.lockServerEpoch(f.epoch);
501
756
  }
757
+ if (v !== 0) this.conn.creditUnbuffered(f);
502
758
  switch (v) {
503
759
  case 1:
504
760
  this.noteValidatedRx();
505
761
  return;
506
762
  case 2: return;
507
763
  case 3: {
764
+ this.protoEvent("data-loss");
508
765
  const err = statusError(15, "seq window overrun: >W_fwd consecutive frames lost");
509
766
  this.sendAbort(15);
510
767
  this.finishLocal(err);
@@ -518,8 +775,13 @@ var ClientStream = class {
518
775
  }
519
776
  case 0: break;
520
777
  }
778
+ const gap = this.rxWin.takeGap();
779
+ if (gap > 0) this.protoEvent("skipped", gap);
521
780
  this.noteValidatedRx();
522
- if (this.conn.isReliable) this.flowTx.observe(f.window);
781
+ if (this.conn.isReliable) {
782
+ this.flowTx.observe(f.window);
783
+ this.conn.observeAdvertisement(f);
784
+ }
523
785
  if (isTerminal(f)) {
524
786
  this.latchHeader(f);
525
787
  if (f.trailer !== void 0) this.trailerMd = f.trailer;
@@ -528,21 +790,36 @@ var ClientStream = class {
528
790
  else if (isData(f)) {
529
791
  if (!this.desc.serverStreams) {
530
792
  this.rxDropped++;
793
+ this.protoEvent("off-shape", 1);
794
+ this.conn.creditUnbuffered(f);
531
795
  return;
532
796
  }
533
797
  this.latchHeader(f);
534
798
  if (this.conn.isReliable) {
799
+ if (!this.conn.connRx.admit()) {
800
+ const err = statusError(13, "drpc: peer exceeded the connection flow-control window");
801
+ this.sendAbort(13);
802
+ this.finishLocal(err);
803
+ this.conn.creditUnbuffered(f);
804
+ return;
805
+ }
806
+ if (!this.pin()) {
807
+ this.conn.retirePeer(1, f.epoch);
808
+ return;
809
+ }
535
810
  if (this.flowRx.active) {
536
811
  if (!this.rxq.tryPut(f)) {
812
+ if (this.unpin()) this.conn.retirePeer(1, f.epoch);
537
813
  const err = statusError(13, "drpc: peer exceeded the advertised flow-control window");
538
814
  this.sendAbort(13);
539
815
  this.finishLocal(err);
540
816
  }
541
817
  } else if (!await this.rxq.putBlocking(f, this.done, ctx.signal)) {
818
+ if (this.unpin()) this.conn.retirePeer(1, f.epoch);
542
819
  this.rxDropped++;
543
820
  this.finishLocal(statusError(14, "transport closed during delivery"));
544
821
  }
545
- } else this.rxq.putDrop(f, this.rxPolicy);
822
+ } else if (this.rxq.putDrop(f, this.rxPolicy) > 0) this.protoEvent("dropped", 1);
546
823
  } else this.rxDropped++;
547
824
  }
548
825
  grantWindow(n) {
@@ -592,7 +869,10 @@ var ClientStream = class {
592
869
  });
593
870
  if (this.codecName !== "") f.codec = this.codecName;
594
871
  if (this.ci.compressorName !== "") f.compressor = this.ci.compressorName;
595
- if (this.conn.isReliable) f.window = this.rxSize;
872
+ if (this.conn.isReliable) {
873
+ f.window = this.rxSize;
874
+ f.connWindow = this.conn.peerWindow;
875
+ }
596
876
  if (this.openHdr !== void 0) f.header = this.openHdr;
597
877
  if (this.deadlineAt !== void 0) f.timeoutMs = this.deadlineAt - nowMs();
598
878
  if (!this.conn.isReliable) {
@@ -627,27 +907,39 @@ var ClientStream = class {
627
907
  }
628
908
  const raw = this.reqCodec.marshal(m);
629
909
  const opening = !this.txOpened;
630
- if (!opening && !this.flowTx.tryAcquire()) switch (await this.flowTx.acquire(this.done, this.ci.stallMs, this.callerSignal)) {
631
- case "ok": break;
632
- case "ended": throw new EndOfStreamError();
633
- case "aborted": throw this.callerSignal === void 0 ? statusError(1, "call cancelled") : abortCause(this.callerSignal);
634
- case "stalled": throw statusError(14, `drpc: flow-control stall: the peer granted no credit for ${this.ci.stallMs}ms`);
910
+ const connTx = this.conn.isReliable ? this.conn.connTx : void 0;
911
+ if (!opening && !tryAcquireBoth(this.flowTx, connTx)) {
912
+ let peer = false;
913
+ switch (await acquireBoth(this.flowTx, connTx, this.done, this.ci.stallMs, this.callerSignal, (p) => {
914
+ peer = p;
915
+ this.protoEvent(p ? "peer-flow-stall" : "flow-stall");
916
+ })) {
917
+ case "ok":
918
+ this.protoEvent(peer ? "peer-flow-resume" : "flow-resume");
919
+ break;
920
+ case "ended": throw new EndOfStreamError();
921
+ case "aborted": throw this.callerSignal === void 0 ? statusError(1, "call cancelled") : abortCause(this.callerSignal);
922
+ case "stalled": throw statusError(14, `drpc: flow-control stall: the peer granted no credit for ${this.ci.stallMs}ms`);
923
+ case "peer-stalled": throw statusError(14, `drpc: flow-control stall: the peer granted no connection credit for ${this.ci.stallMs}ms`);
924
+ }
635
925
  }
636
926
  let enc = rawPayload(raw);
637
927
  if (this.ci.compressor !== void 0) try {
638
928
  enc = await compressPayload(this.ci.compressor, raw);
639
929
  } catch (e) {
640
- if (!opening) this.flowTx.undo();
930
+ if (!opening) this.undoCredit();
641
931
  throw e;
642
932
  }
643
933
  try {
644
934
  checkSendSize(enc.bytes.length, this.ci.maxSend);
645
935
  } catch (e) {
646
- if (!opening) this.flowTx.undo();
936
+ if (!opening) this.undoCredit();
647
937
  throw e;
648
938
  }
649
- if (this.done.tripped) throw new EndOfStreamError();
650
- if (!opening && this.txClosed) throw new EndOfStreamError();
939
+ if (this.done.tripped || !opening && this.txClosed) {
940
+ if (!opening) this.undoCredit();
941
+ throw new EndOfStreamError();
942
+ }
651
943
  let f;
652
944
  if (!this.txOpened) {
653
945
  f = this.openFrame();
@@ -674,7 +966,7 @@ var ClientStream = class {
674
966
  this.retxOpen = void 0;
675
967
  this.retxAt = 0;
676
968
  }
677
- if (isData(f)) this.flowTx.undo();
969
+ if (isData(f)) this.undoCredit();
678
970
  }
679
971
  async send(m) {
680
972
  let err;
@@ -712,6 +1004,7 @@ var ClientStream = class {
712
1004
  }
713
1005
  }
714
1006
  async recvBuffered(f) {
1007
+ if (this.unpin()) this.conn.retirePeer(1, f.epoch);
715
1008
  const m = await this.recvInto(f);
716
1009
  this.grantWindow(1);
717
1010
  return m;
@@ -798,6 +1091,7 @@ var ClientStream = class {
798
1091
  if (this.done.tripped) return;
799
1092
  this.sendAbort(1);
800
1093
  this.finishLocal(statusError(14, "call reset by peer"));
1094
+ if (this.conn.isReliable && !this.conn.locked) this.conn.connTx.refund(this.flowTx.drain());
801
1095
  }
802
1096
  release() {
803
1097
  this.flowTx.release();
@@ -807,6 +1101,10 @@ var ClientStream = class {
807
1101
  }
808
1102
  this.signalDispose();
809
1103
  this.conn.retire(this);
1104
+ const pinned = this.pinned;
1105
+ this.pinned = 0;
1106
+ this.rxReleased = true;
1107
+ this.conn.retirePeer(pinned, this.srvEpoch);
810
1108
  this.hdrLatch.trip();
811
1109
  }
812
1110
  /** @internal */
@@ -821,6 +1119,12 @@ var ClientStream = class {
821
1119
  this.conn.kickSweep();
822
1120
  }
823
1121
  /** @internal */
1122
+ dropRetx() {
1123
+ this.retxOpen = void 0;
1124
+ this.retxClose = void 0;
1125
+ this.retxAt = 0;
1126
+ }
1127
+ /** @internal */
824
1128
  sweepRetx(now, capMs) {
825
1129
  if (this.retxAt === 0 || now < this.retxAt) return [];
826
1130
  const out = [];
@@ -846,4 +1150,4 @@ var ClientStream = class {
846
1150
  }
847
1151
  };
848
1152
  //#endregion
849
- export { resolveTiming as a, RxWindow as c, statusDetails as i, TxSeq as l, Conn as n, K_LOUD as o, EndOfStreamError as r, RxVerdict as s, ClientStream as t, W_FWD as u };
1153
+ export { resolveTiming as a, statsSink as c, RxWindow as d, TxSeq as f, statusDetails as i, K_LOUD as l, chain2 as m, Conn as n, Counters as o, W_FWD as p, EndOfStreamError as r, emit as s, ClientStream as t, RxVerdict as u };