@trevrpc/trevrpc-js 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.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +202 -0
  3. package/bin/protoc-gen-trevrpc-js.js +17 -0
  4. package/examples/greeter/README.md +35 -0
  5. package/examples/greeter/client.js +172 -0
  6. package/examples/greeter/greeter.node.trevrpc.d.ts +53 -0
  7. package/examples/greeter/greeter.node.trevrpc.js +9 -0
  8. package/examples/greeter/greeter.proto +18 -0
  9. package/examples/greeter/greeter.trevrpc.d.ts +55 -0
  10. package/examples/greeter/greeter.trevrpc.js +103 -0
  11. package/examples/greeter/index.html +140 -0
  12. package/examples/greeter/static-server.js +168 -0
  13. package/package.json +89 -0
  14. package/src/advanced.d.ts +29 -0
  15. package/src/advanced.js +1 -0
  16. package/src/browser-channel.js +45 -0
  17. package/src/channel.js +375 -0
  18. package/src/client.js +1397 -0
  19. package/src/connect.js +6 -0
  20. package/src/connect.node.js +6 -0
  21. package/src/framing.js +618 -0
  22. package/src/generator.js +868 -0
  23. package/src/index.d.ts +577 -0
  24. package/src/index.js +3 -0
  25. package/src/index.node.d.ts +10 -0
  26. package/src/index.node.js +3 -0
  27. package/src/metadata.js +112 -0
  28. package/src/native-loader.js +94 -0
  29. package/src/node-advanced.d.ts +27 -0
  30. package/src/node-advanced.js +1 -0
  31. package/src/node-generated.d.ts +24 -0
  32. package/src/node-generated.js +126 -0
  33. package/src/node-index.d.ts +1 -0
  34. package/src/node-index.js +7 -0
  35. package/src/node.d.ts +298 -0
  36. package/src/node.js +1456 -0
  37. package/src/runtime.js +66 -0
  38. package/src/server-response.js +49 -0
  39. package/src/status.js +145 -0
  40. package/src/timer.js +29 -0
  41. package/src/webtransport.js +624 -0
  42. package/src/wire.js +403 -0
package/src/client.js ADDED
@@ -0,0 +1,1397 @@
1
+ import { DefaultMaxFrameSize, marshalMessage, unmarshalMessage } from "./framing.js";
2
+ import { normalizeMetadata, validateMetadata } from "./metadata.js";
3
+ import {
4
+ Code,
5
+ FrameTooLargeError,
6
+ TrevRpcError,
7
+ cancelled,
8
+ deadlineExceeded,
9
+ internal,
10
+ invalidArgument,
11
+ isOkStatus,
12
+ resourceExhausted,
13
+ statusFromResponse,
14
+ unavailable,
15
+ } from "./status.js";
16
+ import { scheduleTimeout } from "./timer.js";
17
+ import { RpcKind, RpcStreamFrameKind, WireVersion } from "./wire.js";
18
+
19
+ const RequestStreamBatchSize = 16;
20
+ const PreparedCallOptions = Symbol("trevrpc.preparedCallOptions");
21
+ const EmptyMetadata = Object.freeze(Object.create(null));
22
+ const WriterTerminalNone = 0;
23
+ const WriterTerminalOk = 1;
24
+ const WriterTerminalNonOk = 2;
25
+
26
+ /** Internal stream-state failure with a stable diagnostic reason. */
27
+ class StreamStateError extends TrevRpcError {
28
+ constructor(reason, message) {
29
+ super(Code.Internal, message);
30
+ this.name = "StreamStateError";
31
+ this.reason = reason;
32
+ }
33
+ }
34
+
35
+ /** Coordinates request writer completion with response stream cleanup. */
36
+ export class RequestWriterSettlement {
37
+ constructor(
38
+ normalizeError = (error) => error,
39
+ isCleanupError = () => false,
40
+ isTerminalError = () => false,
41
+ terminalErrorRequiresTerminalFirst = false,
42
+ ) {
43
+ this._terminal = WriterTerminalNone;
44
+ this._cleanupStarted = false;
45
+ this._hasWriterError = false;
46
+ this._writerError = undefined;
47
+ this._errorAfterCleanup = false;
48
+ this._errorAfterTerminal = false;
49
+ this._errorReported = false;
50
+ this._normalizeError = normalizeError;
51
+ this._isCleanupError = isCleanupError;
52
+ this._isTerminalError = isTerminalError;
53
+ this._terminalErrorRequiresTerminalFirst = terminalErrorRequiresTerminalFirst;
54
+ this.done = Promise.resolve();
55
+ }
56
+
57
+ track(writerTask) {
58
+ this.done = Promise.resolve(writerTask).then(
59
+ () => {},
60
+ (error) => this.recordError(error),
61
+ );
62
+ }
63
+
64
+ recordError(error) {
65
+ if (this._hasWriterError) {
66
+ return;
67
+ }
68
+ this._hasWriterError = true;
69
+ this._writerError = this._normalizeError(error);
70
+ this._errorAfterCleanup = this._cleanupStarted;
71
+ this._errorAfterTerminal = this._terminal !== WriterTerminalNone;
72
+ }
73
+
74
+ recordTerminal(statusCode) {
75
+ if (this._terminal === WriterTerminalNone) {
76
+ this._terminal = statusCode === Code.Ok ? WriterTerminalOk : WriterTerminalNonOk;
77
+ }
78
+ }
79
+
80
+ beginCleanup() {
81
+ this._cleanupStarted = true;
82
+ }
83
+
84
+ async finish() {
85
+ await this.done;
86
+ if (
87
+ !this._hasWriterError ||
88
+ this._terminal === WriterTerminalNonOk ||
89
+ (this._terminal === WriterTerminalOk &&
90
+ (!this._terminalErrorRequiresTerminalFirst || this._errorAfterTerminal) &&
91
+ this._isTerminalError(this._writerError)) ||
92
+ (this._errorAfterCleanup && this._isCleanupError(this._writerError)) ||
93
+ this._errorReported
94
+ ) {
95
+ return;
96
+ }
97
+
98
+ this._errorReported = true;
99
+ throw this._writerError;
100
+ }
101
+ }
102
+
103
+ /** Returns the default client call options. */
104
+ export function defaultCallOptions() {
105
+ return {
106
+ timeoutMs: undefined,
107
+ maxResponseBodySize: DefaultMaxFrameSize,
108
+ maxResponseMessages: 4096,
109
+ maxResponseStreamBodySize: 16 * 1024 * 1024,
110
+ streamIdleTimeoutMs: 30_000,
111
+ metadata: {},
112
+ signal: undefined,
113
+ };
114
+ }
115
+
116
+ /** Merges call options and normalizes metadata. */
117
+ export function mergeCallOptions(base = {}, override = {}) {
118
+ if (isPreparedCallOptions(base) && !hasDefinedOptions(override)) {
119
+ return base;
120
+ }
121
+
122
+ const merged = { ...defaultCallOptions() };
123
+ copyDefined(merged, base);
124
+ copyDefined(merged, override);
125
+ merged.metadata = mergeMetadata(base, override);
126
+ return markPreparedCallOptions(merged);
127
+ }
128
+
129
+ function preparedCallOptions(options = {}) {
130
+ return isPreparedCallOptions(options) ? options : mergeCallOptions(options);
131
+ }
132
+
133
+ /** Calls a unary RPC and decodes the protobuf response. */
134
+ export async function unary(
135
+ transport,
136
+ service,
137
+ method,
138
+ requestType,
139
+ responseType,
140
+ request,
141
+ options = {},
142
+ ) {
143
+ return (
144
+ await unaryWithResponse(transport, service, method, requestType, responseType, request, options)
145
+ ).message;
146
+ }
147
+
148
+ /** Calls a unary RPC and returns the decoded response envelope. */
149
+ export async function unaryWithResponse(
150
+ transport,
151
+ service,
152
+ method,
153
+ requestType,
154
+ responseType,
155
+ request,
156
+ options = {},
157
+ ) {
158
+ const callOptions = preparedCallOptions(options);
159
+ const abortScope = callAbortScope(callOptions);
160
+
161
+ try {
162
+ throwIfAborted(abortScope.signal);
163
+ const requestBody = marshalMessage(requestType, request);
164
+ const rpcRequest = prepareClientRequest(
165
+ service,
166
+ method,
167
+ RpcKind.Unary,
168
+ requestBody,
169
+ abortScope.options,
170
+ );
171
+ const response = await waitForOperation(
172
+ transport.call(rpcRequest, abortScope.options),
173
+ abortScope.signal,
174
+ );
175
+ const responseStatus = validateResponse(response, callOptions.maxResponseBodySize);
176
+ abortScope.terminal();
177
+ if (!isOkStatus(responseStatus)) {
178
+ throw responseStatus;
179
+ }
180
+ return {
181
+ message: unmarshalMessage(responseType, response.body ?? new Uint8Array(0)),
182
+ metadata: response.metadata ?? {},
183
+ };
184
+ } finally {
185
+ abortScope.cleanup();
186
+ }
187
+ }
188
+
189
+ /** Calls a server-streaming RPC and returns a decoded response stream. */
190
+ export async function serverStreaming(
191
+ transport,
192
+ service,
193
+ method,
194
+ requestType,
195
+ responseType,
196
+ request,
197
+ options = {},
198
+ ) {
199
+ const callOptions = preparedCallOptions(options);
200
+ const abortScope = callAbortScope(callOptions);
201
+ try {
202
+ throwIfAborted(abortScope.signal);
203
+ const requestBody = marshalMessage(requestType, request);
204
+ const rpcRequest = prepareClientRequest(
205
+ service,
206
+ method,
207
+ RpcKind.ServerStreaming,
208
+ requestBody,
209
+ abortScope.options,
210
+ );
211
+ const frames = await waitForOperation(
212
+ transport.streamingCall(rpcRequest, emptyAsyncIterable(), abortScope.options),
213
+ abortScope.signal,
214
+ );
215
+ return responseMessageStream(frames, responseType, abortScope.options, abortScope);
216
+ } catch (error) {
217
+ abortScope.cleanup();
218
+ throw error;
219
+ }
220
+ }
221
+
222
+ /** Calls a client-streaming RPC and returns a sendable call object. */
223
+ export async function clientStreaming(
224
+ transport,
225
+ service,
226
+ method,
227
+ requestType,
228
+ responseType,
229
+ options = {},
230
+ ) {
231
+ const callOptions = preparedCallOptions(options);
232
+ const abortScope = callAbortScope(callOptions, { requestCancellation: true });
233
+ const requests = new RequestQueue(abortScope.signal);
234
+ try {
235
+ const rpcRequest = prepareClientRequest(
236
+ service,
237
+ method,
238
+ RpcKind.ClientStreaming,
239
+ new Uint8Array(0),
240
+ abortScope.options,
241
+ );
242
+ const frames = await waitForOperation(
243
+ transport.streamingCall(
244
+ rpcRequest,
245
+ encodeRequestStream(requestType, requests, abortScope.signal),
246
+ abortScope.options,
247
+ ),
248
+ abortScope.signal,
249
+ );
250
+ return new ClientStreamingCall(
251
+ requests,
252
+ responseMessageStream(frames, responseType, abortScope.options, abortScope, (error) =>
253
+ requests.close(error),
254
+ ),
255
+ );
256
+ } catch (error) {
257
+ requests.close(error);
258
+ abortScope.cleanup();
259
+ throw error;
260
+ }
261
+ }
262
+
263
+ /** Calls a bidirectional-streaming RPC and returns a sendable call object. */
264
+ export async function bidirectionalStreaming(
265
+ transport,
266
+ service,
267
+ method,
268
+ requestType,
269
+ responseType,
270
+ options = {},
271
+ ) {
272
+ const callOptions = preparedCallOptions(options);
273
+ const abortScope = callAbortScope(callOptions, { requestCancellation: true });
274
+ const requests = new RequestQueue(abortScope.signal);
275
+ try {
276
+ const rpcRequest = prepareClientRequest(
277
+ service,
278
+ method,
279
+ RpcKind.BidirectionalStreaming,
280
+ new Uint8Array(0),
281
+ abortScope.options,
282
+ );
283
+ const frames = await waitForOperation(
284
+ transport.streamingCall(
285
+ rpcRequest,
286
+ encodeRequestStream(requestType, requests, abortScope.signal),
287
+ abortScope.options,
288
+ ),
289
+ abortScope.signal,
290
+ );
291
+ return new BidirectionalStreamingCall(
292
+ requests,
293
+ responseMessageStream(frames, responseType, abortScope.options, abortScope, (error) =>
294
+ requests.close(error),
295
+ ),
296
+ );
297
+ } catch (error) {
298
+ requests.close(error);
299
+ abortScope.cleanup();
300
+ throw error;
301
+ }
302
+ }
303
+
304
+ /** A client-streaming RPC call. */
305
+ export class ClientStreamingCall {
306
+ constructor(requests, responses) {
307
+ this._requests = requests;
308
+ this._responses = responses;
309
+ }
310
+
311
+ /** Sends one request message. */
312
+ send(request) {
313
+ return this._requests.send(request);
314
+ }
315
+
316
+ /** Sends multiple request messages. */
317
+ sendMany(requests) {
318
+ return this._requests.sendMany(requests);
319
+ }
320
+
321
+ /** Closes the request stream. */
322
+ closeSend() {
323
+ this._requests.close();
324
+ return Promise.resolve();
325
+ }
326
+
327
+ /** Closes the request stream and returns the final response. */
328
+ async closeAndRecv() {
329
+ this._requests.close();
330
+ return await readUnaryResponseFromStream(this._responses);
331
+ }
332
+
333
+ /** Closes the request stream and returns the final response envelope. */
334
+ async closeAndRecvWithResponse() {
335
+ this._requests.close();
336
+ return await readUnaryResponseEnvelopeFromStream(this._responses);
337
+ }
338
+
339
+ /** Releases call resources without waiting for the response. */
340
+ async close(reason) {
341
+ const error = cancellationReason(reason, "request stream cancelled");
342
+ this._requests.close(error);
343
+ await this._responses.close(error);
344
+ }
345
+ }
346
+
347
+ /** A bidirectional-streaming RPC call. */
348
+ export class BidirectionalStreamingCall {
349
+ constructor(requests, responses) {
350
+ this._requests = requests;
351
+ this._responses = responses;
352
+ this.status = responses.status;
353
+ this._iterator = responses[Symbol.asyncIterator]();
354
+ }
355
+
356
+ /** Sends one request message. */
357
+ send(request) {
358
+ return this._requests.send(request);
359
+ }
360
+
361
+ /** Sends multiple request messages. */
362
+ sendMany(requests) {
363
+ return this._requests.sendMany(requests);
364
+ }
365
+
366
+ /** Receives one response message, or undefined after the response stream completes. */
367
+ async recv() {
368
+ const result = await this._iterator.next();
369
+ return result.done ? undefined : result.value;
370
+ }
371
+
372
+ /** Closes the request stream while keeping the response stream readable. */
373
+ closeSend() {
374
+ this._requests.close();
375
+ return Promise.resolve();
376
+ }
377
+
378
+ /** Releases call resources without waiting for more responses. */
379
+ async close(reason) {
380
+ const error = cancellationReason(reason, "request stream cancelled");
381
+ this._requests.close(error);
382
+ await this._responses.close(error);
383
+ }
384
+
385
+ [Symbol.asyncIterator]() {
386
+ return {
387
+ next: async () => {
388
+ const value = await this.recv();
389
+ return value === undefined ? { done: true, value: undefined } : { done: false, value };
390
+ },
391
+ return: async () => {
392
+ await this.close();
393
+ return { done: true, value: undefined };
394
+ },
395
+ };
396
+ }
397
+ }
398
+
399
+ /** Creates a service client from a generated service descriptor. */
400
+ export function createServiceClient(transport, service, root, options = {}) {
401
+ const client = {};
402
+ const baseOptions = mergeCallOptions(options);
403
+
404
+ for (const [jsName, method] of Object.entries(service.methods)) {
405
+ const requestType = root.lookupType(method.inputType);
406
+ const responseType = root.lookupType(method.outputType);
407
+ const callOptions = (override) => mergeCallOptions(baseOptions, override ?? {});
408
+
409
+ switch (method.kind) {
410
+ case "unary":
411
+ client[jsName] = (request, override) =>
412
+ unary(
413
+ transport,
414
+ service.fullName,
415
+ method.name,
416
+ requestType,
417
+ responseType,
418
+ request,
419
+ callOptions(override),
420
+ );
421
+ client[`${jsName}WithResponse`] = (request, override) =>
422
+ unaryWithResponse(
423
+ transport,
424
+ service.fullName,
425
+ method.name,
426
+ requestType,
427
+ responseType,
428
+ request,
429
+ callOptions(override),
430
+ );
431
+ break;
432
+ case "serverStreaming":
433
+ client[jsName] = (request, override) =>
434
+ serverStreaming(
435
+ transport,
436
+ service.fullName,
437
+ method.name,
438
+ requestType,
439
+ responseType,
440
+ request,
441
+ callOptions(override),
442
+ );
443
+ break;
444
+ case "clientStreaming":
445
+ client[jsName] = (override) =>
446
+ clientStreaming(
447
+ transport,
448
+ service.fullName,
449
+ method.name,
450
+ requestType,
451
+ responseType,
452
+ callOptions(override),
453
+ );
454
+ break;
455
+ case "bidirectionalStreaming":
456
+ client[jsName] = (override) =>
457
+ bidirectionalStreaming(
458
+ transport,
459
+ service.fullName,
460
+ method.name,
461
+ requestType,
462
+ responseType,
463
+ callOptions(override),
464
+ );
465
+ break;
466
+ default:
467
+ throw internal(`unsupported generated RPC kind ${JSON.stringify(method.kind)}`);
468
+ }
469
+ }
470
+
471
+ return client;
472
+ }
473
+
474
+ function prepareClientRequest(service, method, kind, body, options) {
475
+ const metadata = isPreparedCallOptions(options)
476
+ ? (options.metadata ?? EmptyMetadata)
477
+ : normalizeMetadata(options.metadata ?? {});
478
+ const request = {
479
+ service,
480
+ method,
481
+ body,
482
+ metadata,
483
+ version: WireVersion,
484
+ };
485
+ const timeout = timeoutNanos(options.timeoutMs);
486
+
487
+ if (kind !== RpcKind.Unary) {
488
+ request.kind = kind;
489
+ }
490
+
491
+ if (timeout != null) {
492
+ request.timeoutNanos = timeout;
493
+ }
494
+
495
+ return request;
496
+ }
497
+
498
+ function validateResponse(response, maxBodySize) {
499
+ if (response == null) {
500
+ throw internal("missing RPC response");
501
+ }
502
+
503
+ validateResponseMetadata(response.metadata ?? {});
504
+
505
+ const body = response.body ?? new Uint8Array(0);
506
+ if (body.byteLength > maxBodySize) {
507
+ throw new FrameTooLargeError(body.byteLength, maxBodySize);
508
+ }
509
+
510
+ return statusFromResponse(response);
511
+ }
512
+
513
+ function responseMessageStream(
514
+ frameStream,
515
+ responseType,
516
+ options,
517
+ abortScope,
518
+ onTerminal = () => {},
519
+ ) {
520
+ return new ResponseStreamController(frameStream, responseType, options, abortScope, onTerminal);
521
+ }
522
+
523
+ class ResponseStreamController {
524
+ constructor(frameStream, responseType, options, abortScope, onTerminal) {
525
+ this._iterator = frameStream[Symbol.asyncIterator]();
526
+ this._responseType = responseType;
527
+ this._options = options;
528
+ this._abortScope = abortScope;
529
+ this._onTerminal = onTerminal;
530
+ this._useBodyBatches = typeof this._iterator.nextBodyBatch === "function";
531
+ this._counters = { messages: 0, streamBodySize: 0 };
532
+ this._messages = [];
533
+ this._pendingError = null;
534
+ this._pendingStatus = null;
535
+ this._complete = false;
536
+ this._failure = null;
537
+ this._cleanupPromise = null;
538
+ this._statusSettled = false;
539
+ this._operation = Promise.resolve();
540
+ this.status = new Promise((resolve, reject) => {
541
+ this._resolveStatus = resolve;
542
+ this._rejectStatus = reject;
543
+ });
544
+ this.status.catch(() => {});
545
+ this._onAbort = () => {
546
+ const error = signalAbortError(this._options.signal);
547
+ this._recordFailure(error);
548
+ void this._cleanup(error);
549
+ };
550
+ if (this._options.signal?.aborted) {
551
+ this._onAbort();
552
+ } else {
553
+ this._options.signal?.addEventListener?.("abort", this._onAbort, { once: true });
554
+ }
555
+ }
556
+
557
+ [Symbol.asyncIterator]() {
558
+ return this;
559
+ }
560
+
561
+ next() {
562
+ return this._serialize(() => this._next());
563
+ }
564
+
565
+ async return() {
566
+ await this.close();
567
+ return { done: true, value: undefined };
568
+ }
569
+
570
+ async close(reason) {
571
+ if (this._complete) {
572
+ await this._cleanupPromise;
573
+ return;
574
+ }
575
+ const error = cancellationReason(reason, "response stream cancelled");
576
+ this._abortScope?.abort(error);
577
+ this._recordFailure(error);
578
+ await this._cleanup(error);
579
+ }
580
+
581
+ _serialize(operation) {
582
+ const result = this._operation.then(operation, operation);
583
+ this._operation = result.catch(() => {});
584
+ return result;
585
+ }
586
+
587
+ async _next() {
588
+ if (this._failure != null) {
589
+ throw this._failure;
590
+ }
591
+ if (this._complete) {
592
+ return { done: true, value: undefined };
593
+ }
594
+
595
+ try {
596
+ for (;;) {
597
+ if (this._messages.length > 0) {
598
+ return { done: false, value: this._messages.shift() };
599
+ }
600
+ if (this._pendingError != null) {
601
+ const error = this._pendingError;
602
+ this._pendingError = null;
603
+ throw error;
604
+ }
605
+ if (this._pendingStatus != null) {
606
+ const frame = this._pendingStatus;
607
+ this._pendingStatus = null;
608
+ await this._finishStatus(frame);
609
+ if (this._failure != null) {
610
+ throw this._failure;
611
+ }
612
+ return { done: true, value: undefined };
613
+ }
614
+
615
+ const result = await this._readNext();
616
+ if (result.done) {
617
+ throw new StreamStateError(
618
+ "missing_terminal_status",
619
+ "response stream ended before final status",
620
+ );
621
+ }
622
+ this._consumeBatch(result.value);
623
+ }
624
+ } catch (error) {
625
+ const primary = this._recordFailure(error);
626
+ await this._cleanup(primary);
627
+ throw primary;
628
+ }
629
+ }
630
+
631
+ _consumeBatch(value) {
632
+ if (this._useBodyBatches) {
633
+ for (const body of value.bodies) {
634
+ validateResponseMessageBody(body, this._options, this._counters);
635
+ this._messages.push(unmarshalMessage(this._responseType, body));
636
+ }
637
+ if (value.status != null) {
638
+ this._pendingStatus = this._claimStatus(value.status);
639
+ }
640
+ return;
641
+ }
642
+
643
+ for (let index = 0; index < value.length; index += 1) {
644
+ const frame = value[index];
645
+ if (frame == null) {
646
+ const error = new StreamStateError(
647
+ "missing_terminal_status",
648
+ "response stream ended before final status",
649
+ );
650
+ if (this._messages.length > 0) {
651
+ this._pendingError = error;
652
+ return;
653
+ }
654
+ throw error;
655
+ }
656
+ if (frame.kind === RpcStreamFrameKind.Message) {
657
+ const body = frame.body ?? new Uint8Array(0);
658
+ validateResponseMessageBody(body, this._options, this._counters);
659
+ this._messages.push(unmarshalMessage(this._responseType, body));
660
+ continue;
661
+ }
662
+ if (frame.kind === RpcStreamFrameKind.Status) {
663
+ if (index + 1 < value.length) {
664
+ throw new StreamStateError(
665
+ "trailing_frame",
666
+ "response stream contained a frame after final status",
667
+ );
668
+ }
669
+ this._pendingStatus = this._claimStatus(frame);
670
+ continue;
671
+ }
672
+ const error = invalidArgument("response stream contained an unknown frame kind");
673
+ error.reason = "unsupported_frame_kind";
674
+ throw error;
675
+ }
676
+ }
677
+
678
+ _claimStatus(frame) {
679
+ validateResponseMetadata(frame.metadata ?? {});
680
+ const status = {
681
+ code: frame.status ?? Code.Ok,
682
+ message: frame.message ?? "",
683
+ metadata: frame.metadata ?? {},
684
+ };
685
+ const terminalError =
686
+ status.code === Code.Ok
687
+ ? null
688
+ : statusFromResponse({
689
+ status: status.code,
690
+ message: status.message,
691
+ metadata: status.metadata,
692
+ });
693
+
694
+ if (!this._abortScope?.terminal()) {
695
+ throw signalAbortError(this._options.signal);
696
+ }
697
+ this._onTerminal(terminalError);
698
+ return { status, terminalError };
699
+ }
700
+
701
+ async _finishStatus(terminal) {
702
+ const { status, terminalError } = terminal;
703
+ let end;
704
+ try {
705
+ end = await this._readNext();
706
+ } catch (error) {
707
+ if (!isMalformedTrailingRead(error)) {
708
+ throw error;
709
+ }
710
+ const trailing = new StreamStateError(
711
+ "trailing_frame",
712
+ "response stream did not end cleanly after final status",
713
+ );
714
+ trailing.cause = error;
715
+ throw trailing;
716
+ }
717
+ if (!end.done) {
718
+ throw new StreamStateError(
719
+ "trailing_frame",
720
+ "response stream contained a frame after final status",
721
+ );
722
+ }
723
+
724
+ await this._cleanup(terminalError);
725
+ if (terminalError != null) {
726
+ this._recordFailure(terminalError);
727
+ throw terminalError;
728
+ }
729
+ this._complete = true;
730
+ this._settleStatus(true, status);
731
+ }
732
+
733
+ _readNext() {
734
+ return this._useBodyBatches
735
+ ? nextBodyBatchWithTimeout(
736
+ this._iterator,
737
+ this._options.streamIdleTimeoutMs,
738
+ this._options.signal,
739
+ this._abortScope?.abort,
740
+ )
741
+ : nextFrameBatchWithTimeout(
742
+ this._iterator,
743
+ this._options.streamIdleTimeoutMs,
744
+ this._options.signal,
745
+ this._abortScope?.abort,
746
+ );
747
+ }
748
+
749
+ _recordFailure(error) {
750
+ if (this._failure == null && !this._complete) {
751
+ this._failure = error;
752
+ this._settleStatus(false, error);
753
+ }
754
+ return this._failure ?? error;
755
+ }
756
+
757
+ _settleStatus(success, value) {
758
+ if (this._statusSettled) {
759
+ return;
760
+ }
761
+ this._statusSettled = true;
762
+ if (success) {
763
+ this._resolveStatus(value);
764
+ } else {
765
+ this._rejectStatus(value);
766
+ }
767
+ }
768
+
769
+ _cleanup(primaryError) {
770
+ if (this._cleanupPromise != null) {
771
+ return this._cleanupPromise;
772
+ }
773
+ this._cleanupPromise = (async () => {
774
+ this._options.signal?.removeEventListener?.("abort", this._onAbort);
775
+ try {
776
+ if (typeof this._iterator.return === "function") {
777
+ await this._iterator.return();
778
+ }
779
+ } catch (error) {
780
+ if (primaryError == null) {
781
+ throw error;
782
+ }
783
+ } finally {
784
+ this._abortScope?.cleanup();
785
+ }
786
+ })();
787
+ return this._cleanupPromise;
788
+ }
789
+ }
790
+
791
+ function validateResponseMessageBody(body, options, counters) {
792
+ if (options.maxResponseMessages >= 0 && counters.messages >= options.maxResponseMessages) {
793
+ throw resourceExhausted(
794
+ `response stream exceeded maximum of ${options.maxResponseMessages} messages`,
795
+ );
796
+ }
797
+
798
+ if (body.byteLength > options.maxResponseBodySize) {
799
+ throw new FrameTooLargeError(body.byteLength, options.maxResponseBodySize);
800
+ }
801
+
802
+ counters.messages += 1;
803
+ counters.streamBodySize = saturatingAdd(counters.streamBodySize, body.byteLength);
804
+ if (
805
+ options.maxResponseStreamBodySize >= 0 &&
806
+ counters.streamBodySize > options.maxResponseStreamBodySize
807
+ ) {
808
+ throw resourceExhausted(
809
+ `response stream exceeded maximum body size of ${options.maxResponseStreamBodySize} bytes`,
810
+ );
811
+ }
812
+ }
813
+
814
+ async function nextBodyBatchWithTimeout(iterator, idleTimeoutMs, signal, onTimeout) {
815
+ return await withTimeout(
816
+ iterator.nextBodyBatch(),
817
+ idleTimeoutMs,
818
+ () => unavailable("response stream idle timeout"),
819
+ onTimeout,
820
+ signal,
821
+ );
822
+ }
823
+
824
+ async function nextFrameBatchWithTimeout(iterator, idleTimeoutMs, signal, onTimeout) {
825
+ const hasBatchReader = typeof iterator.nextBatch === "function";
826
+ const result = await withTimeout(
827
+ hasBatchReader ? iterator.nextBatch() : iterator.next(),
828
+ idleTimeoutMs,
829
+ () => unavailable("response stream idle timeout"),
830
+ onTimeout,
831
+ signal,
832
+ );
833
+ return hasBatchReader || result.done ? result : { done: false, value: [result.value] };
834
+ }
835
+
836
+ function isMalformedTrailingRead(error) {
837
+ return (
838
+ error instanceof FrameTooLargeError ||
839
+ error?.reason === "malformed_protobuf" ||
840
+ error?.reason === "incomplete_frame"
841
+ );
842
+ }
843
+
844
+ function validateResponseMetadata(metadata) {
845
+ try {
846
+ validateMetadata(metadata);
847
+ } catch (error) {
848
+ throw new StreamStateError(
849
+ "invalid_metadata",
850
+ `invalid response metadata: ${error.statusMessage ?? error.message}`,
851
+ );
852
+ }
853
+ }
854
+
855
+ async function readUnaryResponseFromStream(stream) {
856
+ return (await readUnaryResponseEnvelopeFromStream(stream)).message;
857
+ }
858
+
859
+ async function readUnaryResponseEnvelopeFromStream(stream) {
860
+ let firstMessage;
861
+ let messageCount = 0;
862
+ for await (const message of stream) {
863
+ if (messageCount === 0) {
864
+ firstMessage = message;
865
+ }
866
+ messageCount += 1;
867
+ }
868
+
869
+ const status = stream.status == null ? { metadata: {} } : await stream.status;
870
+ if (messageCount === 0) {
871
+ throw new StreamStateError(
872
+ "response_cardinality",
873
+ "response stream ended without a response message",
874
+ );
875
+ }
876
+ if (messageCount !== 1) {
877
+ throw new StreamStateError(
878
+ "response_cardinality",
879
+ "client-streaming RPC returned more than one response message",
880
+ );
881
+ }
882
+
883
+ return { message: firstMessage, metadata: status.metadata ?? {} };
884
+ }
885
+
886
+ function timeoutNanos(timeoutMs) {
887
+ if (timeoutMs == null) {
888
+ return null;
889
+ }
890
+
891
+ if (!Number.isFinite(timeoutMs) || timeoutMs < 0) {
892
+ throw invalidArgument("RPC timeout must be a non-negative finite number of milliseconds");
893
+ }
894
+
895
+ const nanos = BigInt(Math.ceil(timeoutMs)) * 1_000_000n;
896
+ return String(nanos === 0n ? 1n : nanos);
897
+ }
898
+
899
+ function withTimeout(promise, timeoutMs, makeError, onTimeout, signal) {
900
+ if (timeoutMs == null && signal == null) {
901
+ return promise;
902
+ }
903
+
904
+ if (timeoutMs != null && !Number.isFinite(timeoutMs)) {
905
+ throw invalidArgument("RPC timeout must be a finite number of milliseconds");
906
+ }
907
+
908
+ return new Promise((resolve, reject) => {
909
+ let settled = false;
910
+ let cancelTimer = () => {};
911
+ const cleanup = () => {
912
+ cancelTimer();
913
+ signal?.removeEventListener?.("abort", onAbort);
914
+ };
915
+ const settle = (complete, value) => {
916
+ if (settled) {
917
+ return;
918
+ }
919
+ settled = true;
920
+ cleanup();
921
+ complete(value);
922
+ };
923
+ const onAbort = () => settle(reject, signalAbortError(signal));
924
+
925
+ if (timeoutMs != null && timeoutMs >= 0) {
926
+ cancelTimer = scheduleTimeout(() => {
927
+ const error = makeError();
928
+ onTimeout?.(error);
929
+ settle(reject, error);
930
+ }, timeoutMs);
931
+ }
932
+
933
+ if (signal != null) {
934
+ if (signal.aborted) {
935
+ onAbort();
936
+ return;
937
+ }
938
+ signal.addEventListener("abort", onAbort, { once: true });
939
+ }
940
+
941
+ promise.then(
942
+ (value) => {
943
+ settle(resolve, value);
944
+ },
945
+ (error) => {
946
+ settle(reject, error);
947
+ },
948
+ );
949
+ });
950
+ }
951
+
952
+ function signalAbortError(signal) {
953
+ const reason = signal?.reason;
954
+ return reason instanceof TrevRpcError ? reason : cancelled("RPC cancelled");
955
+ }
956
+
957
+ function cancellationReason(reason, fallback) {
958
+ if (reason instanceof TrevRpcError && reason.code === Code.Cancelled) {
959
+ return reason;
960
+ }
961
+ const error = cancelled(fallback);
962
+ if (reason !== undefined) {
963
+ Object.defineProperty(error, "cause", { configurable: true, value: reason });
964
+ }
965
+ return error;
966
+ }
967
+
968
+ function normalizeAbortReason(reason) {
969
+ return reason instanceof TrevRpcError ? reason : cancellationReason(reason, "RPC cancelled");
970
+ }
971
+
972
+ function throwIfAborted(signal) {
973
+ if (signal?.aborted) {
974
+ throw signalAbortError(signal);
975
+ }
976
+ }
977
+
978
+ function waitForOperation(promise, signal) {
979
+ return withTimeout(promise, null, null, null, signal);
980
+ }
981
+
982
+ function callAbortScope(options, { requestCancellation = false } = {}) {
983
+ const needsTransportSignal = options.signal != null || options.timeoutMs != null;
984
+ if (!needsTransportSignal && !requestCancellation) {
985
+ return {
986
+ options,
987
+ signal: undefined,
988
+ abort() {
989
+ return false;
990
+ },
991
+ terminal() {
992
+ return true;
993
+ },
994
+ cleanup() {},
995
+ };
996
+ }
997
+
998
+ if (options.timeoutMs != null && (!Number.isFinite(options.timeoutMs) || options.timeoutMs < 0)) {
999
+ throw invalidArgument("RPC timeout must be a non-negative finite number of milliseconds");
1000
+ }
1001
+
1002
+ const controller = new AbortController();
1003
+ const parentSignal = options.signal;
1004
+ const deadlineError =
1005
+ options.timeoutMs == null ? null : deadlineExceeded("RPC deadline exceeded");
1006
+ let settlement = "active";
1007
+ let cleaned = false;
1008
+ let cancelDeadline = () => {};
1009
+
1010
+ const clearDeadline = () => {
1011
+ cancelDeadline();
1012
+ cancelDeadline = () => {};
1013
+ };
1014
+ const removeParent = () => parentSignal?.removeEventListener?.("abort", onParentAbort);
1015
+ const abort = (reason) => {
1016
+ if (settlement !== "active") {
1017
+ return false;
1018
+ }
1019
+ settlement = "aborted";
1020
+ clearDeadline();
1021
+ removeParent();
1022
+ controller.abort(normalizeAbortReason(reason));
1023
+ return true;
1024
+ };
1025
+ const terminal = () => {
1026
+ if (settlement !== "active") {
1027
+ return false;
1028
+ }
1029
+ settlement = "terminal";
1030
+ clearDeadline();
1031
+ removeParent();
1032
+ return true;
1033
+ };
1034
+ const onParentAbort = () => abort(parentSignal.reason);
1035
+
1036
+ if (parentSignal != null) {
1037
+ if (parentSignal.aborted) {
1038
+ onParentAbort();
1039
+ } else {
1040
+ parentSignal.addEventListener("abort", onParentAbort, { once: true });
1041
+ }
1042
+ }
1043
+ if (deadlineError != null && settlement === "active") {
1044
+ cancelDeadline = scheduleTimeout(() => abort(deadlineError), options.timeoutMs);
1045
+ }
1046
+
1047
+ return {
1048
+ options: markPreparedCallOptions({ ...options, signal: controller.signal }),
1049
+ signal: controller.signal,
1050
+ abort,
1051
+ terminal,
1052
+ cleanup() {
1053
+ if (cleaned) {
1054
+ return;
1055
+ }
1056
+ cleaned = true;
1057
+ clearDeadline();
1058
+ removeParent();
1059
+ },
1060
+ };
1061
+ }
1062
+
1063
+ class RequestQueue {
1064
+ constructor(signal) {
1065
+ this._pending = [];
1066
+ this._waiter = undefined;
1067
+ this._closed = false;
1068
+ this._error = undefined;
1069
+ this._signal = signal;
1070
+ this._onAbort = () => this.close(signalAbortError(signal));
1071
+ if (signal?.aborted) {
1072
+ this._onAbort();
1073
+ } else {
1074
+ signal?.addEventListener?.("abort", this._onAbort, { once: true });
1075
+ }
1076
+ }
1077
+
1078
+ send(value) {
1079
+ return this.sendMany([value]);
1080
+ }
1081
+
1082
+ sendMany(values) {
1083
+ if (this._closed) {
1084
+ return Promise.reject(this._error ?? cancelled("request stream is closed"));
1085
+ }
1086
+
1087
+ const batch = Array.from(values);
1088
+ if (batch.length === 0) {
1089
+ return Promise.resolve();
1090
+ }
1091
+
1092
+ return new Promise((resolve, reject) => {
1093
+ let remaining = batch.length;
1094
+ const resolveOne = () => {
1095
+ remaining -= 1;
1096
+ if (remaining === 0) {
1097
+ resolve();
1098
+ }
1099
+ };
1100
+ for (const value of batch) {
1101
+ this._pending.push({ value, resolve: resolveOne, reject });
1102
+ }
1103
+ this._wakeWaiter();
1104
+ });
1105
+ }
1106
+
1107
+ close(error) {
1108
+ if (this._closed) {
1109
+ return;
1110
+ }
1111
+
1112
+ this._closed = true;
1113
+ this._error = error;
1114
+ this._signal?.removeEventListener?.("abort", this._onAbort);
1115
+ if (error != null) {
1116
+ for (const pending of this._pending.splice(0)) {
1117
+ pending.reject(error);
1118
+ }
1119
+ }
1120
+ if (this._waiter != null) {
1121
+ const waiter = this._waiter;
1122
+ this._waiter = undefined;
1123
+ if (error == null) {
1124
+ waiter.resolve({ done: true, value: undefined });
1125
+ } else {
1126
+ waiter.reject(error);
1127
+ }
1128
+ }
1129
+ }
1130
+
1131
+ [Symbol.asyncIterator]() {
1132
+ return this;
1133
+ }
1134
+
1135
+ next() {
1136
+ if (this._pending.length > 0) {
1137
+ const pending = this._pending.shift();
1138
+ pending.resolve();
1139
+ return Promise.resolve({ done: false, value: pending.value });
1140
+ }
1141
+ if (this._error != null) {
1142
+ return Promise.reject(this._error);
1143
+ }
1144
+ if (this._closed) {
1145
+ return Promise.resolve({ done: true, value: undefined });
1146
+ }
1147
+
1148
+ return new Promise((resolve, reject) => {
1149
+ this._waiter = { resolve, reject };
1150
+ });
1151
+ }
1152
+
1153
+ nextBatch(max = RequestStreamBatchSize) {
1154
+ if (this._pending.length > 0) {
1155
+ return Promise.resolve({ done: false, value: this._takePendingBatch(max) });
1156
+ }
1157
+ if (this._error != null) {
1158
+ return Promise.reject(this._error);
1159
+ }
1160
+ if (this._closed) {
1161
+ return Promise.resolve({ done: true, value: undefined });
1162
+ }
1163
+
1164
+ return new Promise((resolve, reject) => {
1165
+ this._waiter = { resolve, reject, batch: true, max };
1166
+ });
1167
+ }
1168
+
1169
+ return() {
1170
+ this.close(cancelled("request stream cancelled"));
1171
+ return Promise.resolve({ done: true, value: undefined });
1172
+ }
1173
+
1174
+ _takePendingBatch(max) {
1175
+ const count = Math.min(Math.max(1, max), this._pending.length);
1176
+ const values = Array.from({ length: count });
1177
+ for (let i = 0; i < count; i++) {
1178
+ const pending = this._pending.shift();
1179
+ pending.resolve();
1180
+ values[i] = pending.value;
1181
+ }
1182
+ return values;
1183
+ }
1184
+
1185
+ _wakeWaiter() {
1186
+ if (this._waiter == null || this._pending.length === 0) {
1187
+ return;
1188
+ }
1189
+
1190
+ const waiter = this._waiter;
1191
+ this._waiter = undefined;
1192
+ if (waiter.batch) {
1193
+ queueMicrotask(() => this._resolveBatchWaiter(waiter));
1194
+ return;
1195
+ }
1196
+
1197
+ const pending = this._pending.shift();
1198
+ pending.resolve();
1199
+ waiter.resolve({ done: false, value: pending.value });
1200
+ }
1201
+
1202
+ _resolveBatchWaiter(waiter) {
1203
+ if (this._pending.length > 0) {
1204
+ waiter.resolve({ done: false, value: this._takePendingBatch(waiter.max) });
1205
+ } else if (this._error != null) {
1206
+ waiter.reject(this._error);
1207
+ } else if (this._closed) {
1208
+ waiter.resolve({ done: true, value: undefined });
1209
+ } else {
1210
+ this._waiter = waiter;
1211
+ }
1212
+ }
1213
+ }
1214
+
1215
+ function encodeRequestStream(requestType, requests, signal) {
1216
+ const iterator = requestIterator(requests);
1217
+ let done = false;
1218
+ return {
1219
+ [Symbol.asyncIterator]() {
1220
+ return this;
1221
+ },
1222
+ async next() {
1223
+ if (done) {
1224
+ return { done: true, value: undefined };
1225
+ }
1226
+ const result = await nextRequest(iterator, signal);
1227
+ if (result.done) {
1228
+ done = true;
1229
+ return { done: true, value: undefined };
1230
+ }
1231
+
1232
+ return { done: false, value: marshalMessage(requestType, result.value) };
1233
+ },
1234
+ async nextBatch(max = RequestStreamBatchSize) {
1235
+ if (done) {
1236
+ return { done: true, value: undefined };
1237
+ }
1238
+ const result = await nextRequestBatch(iterator, signal, max);
1239
+ if (result.done) {
1240
+ done = true;
1241
+ return { done: true, value: undefined };
1242
+ }
1243
+
1244
+ return {
1245
+ done: false,
1246
+ value: result.value.map((value) => marshalMessage(requestType, value)),
1247
+ };
1248
+ },
1249
+ async return() {
1250
+ done = true;
1251
+ if (signal?.aborted && typeof iterator.return === "function") {
1252
+ await iterator.return();
1253
+ }
1254
+ return { done: true, value: undefined };
1255
+ },
1256
+ };
1257
+ }
1258
+
1259
+ function requestIterator(requests) {
1260
+ if (typeof requests?.[Symbol.asyncIterator] === "function") {
1261
+ return requests[Symbol.asyncIterator]();
1262
+ }
1263
+
1264
+ if (typeof requests?.[Symbol.iterator] === "function") {
1265
+ const iterator = requests[Symbol.iterator]();
1266
+ return {
1267
+ next() {
1268
+ return Promise.resolve(iterator.next());
1269
+ },
1270
+ return() {
1271
+ return Promise.resolve(
1272
+ typeof iterator.return === "function"
1273
+ ? iterator.return()
1274
+ : { done: true, value: undefined },
1275
+ );
1276
+ },
1277
+ };
1278
+ }
1279
+
1280
+ throw invalidArgument("request stream must be an async iterable or iterable");
1281
+ }
1282
+
1283
+ function nextRequest(iterator, signal) {
1284
+ return nextRequestResult(() => iterator.next(), signal);
1285
+ }
1286
+
1287
+ async function nextRequestBatch(iterator, signal, max) {
1288
+ if (typeof iterator.nextBatch === "function") {
1289
+ return await nextRequestResult(() => iterator.nextBatch(max), signal);
1290
+ }
1291
+
1292
+ const result = await nextRequest(iterator, signal);
1293
+ return result.done ? result : { done: false, value: [result.value] };
1294
+ }
1295
+
1296
+ function nextRequestResult(next, signal) {
1297
+ if (signal?.aborted) {
1298
+ return Promise.reject(signal.reason ?? cancelled("request stream cancelled"));
1299
+ }
1300
+ if (signal == null) {
1301
+ return next();
1302
+ }
1303
+
1304
+ return new Promise((resolve, reject) => {
1305
+ const onAbort = () => reject(signal.reason ?? cancelled("request stream cancelled"));
1306
+ signal.addEventListener("abort", onAbort, { once: true });
1307
+ next().then(
1308
+ (value) => {
1309
+ signal.removeEventListener("abort", onAbort);
1310
+ resolve(value);
1311
+ },
1312
+ (error) => {
1313
+ signal.removeEventListener("abort", onAbort);
1314
+ reject(error);
1315
+ },
1316
+ );
1317
+ });
1318
+ }
1319
+
1320
+ async function* emptyAsyncIterable() {}
1321
+
1322
+ function copyDefined(target, source) {
1323
+ for (const [key, value] of Object.entries(source ?? {})) {
1324
+ if (value !== undefined && key !== "metadata") {
1325
+ target[key] = value;
1326
+ }
1327
+ }
1328
+ }
1329
+
1330
+ function mergeMetadata(base, override) {
1331
+ const baseHasMetadata = hasMetadataEntries(base?.metadata);
1332
+ const overrideHasMetadata = hasMetadataEntries(override?.metadata);
1333
+
1334
+ if (!baseHasMetadata && !overrideHasMetadata) {
1335
+ return EmptyMetadata;
1336
+ }
1337
+
1338
+ if (!overrideHasMetadata) {
1339
+ return isPreparedCallOptions(base)
1340
+ ? (base.metadata ?? EmptyMetadata)
1341
+ : normalizeMetadata(base.metadata);
1342
+ }
1343
+
1344
+ if (!baseHasMetadata) {
1345
+ return isPreparedCallOptions(override)
1346
+ ? (override.metadata ?? EmptyMetadata)
1347
+ : normalizeMetadata(override.metadata);
1348
+ }
1349
+
1350
+ const metadata = Object.assign(
1351
+ Object.create(null),
1352
+ isPreparedCallOptions(base) ? base.metadata : normalizeMetadata(base.metadata),
1353
+ isPreparedCallOptions(override) ? override.metadata : normalizeMetadata(override.metadata),
1354
+ );
1355
+ validateMetadata(metadata);
1356
+ return metadata;
1357
+ }
1358
+
1359
+ function hasMetadataEntries(metadata) {
1360
+ if (metadata == null) {
1361
+ return false;
1362
+ }
1363
+ if (metadata instanceof Map) {
1364
+ return metadata.size > 0;
1365
+ }
1366
+ for (const _key in metadata) {
1367
+ return true;
1368
+ }
1369
+ return false;
1370
+ }
1371
+
1372
+ function hasDefinedOptions(options) {
1373
+ for (const [key, value] of Object.entries(options ?? {})) {
1374
+ if (key === "metadata") {
1375
+ if (hasMetadataEntries(value)) {
1376
+ return true;
1377
+ }
1378
+ } else if (value !== undefined) {
1379
+ return true;
1380
+ }
1381
+ }
1382
+ return false;
1383
+ }
1384
+
1385
+ function markPreparedCallOptions(options) {
1386
+ Object.defineProperty(options, PreparedCallOptions, { value: true });
1387
+ return options;
1388
+ }
1389
+
1390
+ function isPreparedCallOptions(options) {
1391
+ return options?.[PreparedCallOptions] === true;
1392
+ }
1393
+
1394
+ function saturatingAdd(left, right) {
1395
+ const sum = left + right;
1396
+ return Number.isSafeInteger(sum) ? sum : Number.MAX_SAFE_INTEGER;
1397
+ }