@trevrpc/trevrpc-js 1.3.2 → 1.5.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/src/index.d.ts CHANGED
@@ -2,8 +2,138 @@ import type { INamespace, Root } from "protobufjs";
2
2
 
3
3
  export const protobuf: typeof import("protobufjs");
4
4
 
5
+ /**
6
+ * How decoded messages represent 64-bit integer fields: `bigint`, `number` (exact up to 2^53),
7
+ * decimal `string`, or protobuf.js's `long` (a Long object). 64-bit map keys are decimal strings
8
+ * in every representation.
9
+ */
10
+ export type Int64Representation = "bigint" | "number" | "string" | "long";
11
+
12
+ export interface CreateRootOptions {
13
+ /**
14
+ * How calls decode the 64-bit integers of the root's messages; a field's `jstype` option
15
+ * (`JS_STRING` or `JS_NUMBER`) overrides it. Without it they stay Long objects. Generated code
16
+ * passes the generator's `int64` option.
17
+ */
18
+ int64?: Int64Representation;
19
+ }
20
+
5
21
  /** Creates a protobuf root from a JSON namespace definition. */
6
- export function createRoot(json: INamespace): Root;
22
+ export function createRoot(json: INamespace, options?: CreateRootOptions): Root;
23
+
24
+ /** A 64-bit integer in any representation, including a Long. */
25
+ type Int64Value = bigint | number | string | { low: number; high: number; unsigned?: boolean };
26
+
27
+ /** A decoded google.protobuf.Timestamp message. */
28
+ export interface Timestamp {
29
+ seconds: bigint;
30
+ nanos: number;
31
+ }
32
+
33
+ /** A google.protobuf.Timestamp message to send. */
34
+ export interface TimestampInit {
35
+ seconds?: bigint | number | string;
36
+ nanos?: number;
37
+ }
38
+
39
+ /** A decoded google.protobuf.Duration message. */
40
+ export interface Duration {
41
+ seconds: bigint;
42
+ nanos: number;
43
+ }
44
+
45
+ /** A google.protobuf.Duration message to send. */
46
+ export interface DurationInit {
47
+ seconds?: bigint | number | string;
48
+ nanos?: number;
49
+ }
50
+
51
+ /** A decoded google.protobuf.Empty message. */
52
+ export interface Empty {}
53
+
54
+ /** A google.protobuf.Empty message to send. */
55
+ export interface EmptyInit {}
56
+
57
+ /** Values of the google.protobuf.NullValue enum. */
58
+ export const NullValue: Readonly<{ NULL_VALUE: 0 }>;
59
+ /** A value of the google.protobuf.NullValue enum. */
60
+ export type NullValue = (typeof NullValue)[keyof typeof NullValue];
61
+
62
+ /** A decoded google.protobuf.Struct message: a JSON object; see `structToJson`. */
63
+ export interface Struct {
64
+ fields: Record<string, Value>;
65
+ }
66
+
67
+ /** A google.protobuf.Struct message to send; see `structFromJson`. */
68
+ export interface StructInit {
69
+ fields?: Record<string, ValueInit>;
70
+ }
71
+
72
+ /**
73
+ * A decoded google.protobuf.Value message: a JSON value, which sets one member of the `kind`
74
+ * oneof; see `valueToJson`.
75
+ */
76
+ export interface Value {
77
+ nullValue: NullValue;
78
+ numberValue: number;
79
+ stringValue: string;
80
+ boolValue: boolean;
81
+ structValue: Struct | null;
82
+ listValue: ListValue | null;
83
+ }
84
+
85
+ /** A google.protobuf.Value message to send; see `valueFromJson`. */
86
+ export interface ValueInit {
87
+ nullValue?: NullValue;
88
+ numberValue?: number;
89
+ stringValue?: string;
90
+ boolValue?: boolean;
91
+ structValue?: StructInit | null;
92
+ listValue?: ListValueInit | null;
93
+ }
94
+
95
+ /** A decoded google.protobuf.ListValue message: a JSON array. */
96
+ export interface ListValue {
97
+ values: Value[];
98
+ }
99
+
100
+ /** A google.protobuf.ListValue message to send. */
101
+ export interface ListValueInit {
102
+ values?: ValueInit[];
103
+ }
104
+
105
+ export type JsonValue = null | boolean | number | string | JsonValue[] | JsonObject;
106
+ export type JsonObject = { [key: string]: JsonValue };
107
+
108
+ /** Returns the Timestamp of a Date, to the millisecond. */
109
+ export function timestampFromDate(date: Date): Timestamp;
110
+ /** Returns the Timestamp of milliseconds since the Unix epoch. */
111
+ export function timestampFromMs(ms: number): Timestamp;
112
+ /** Returns the Date of a Timestamp, whatever the representation of its seconds. */
113
+ export function timestampDate(timestamp: {
114
+ seconds?: Int64Value | null;
115
+ nanos?: number | null;
116
+ }): Date;
117
+ /** Returns the milliseconds since the Unix epoch of a Timestamp, rounded down. */
118
+ export function timestampMs(timestamp: {
119
+ seconds?: Int64Value | null;
120
+ nanos?: number | null;
121
+ }): number;
122
+ /** Returns the Duration of a number of milliseconds. */
123
+ export function durationFromMs(ms: number): Duration;
124
+ /** Returns the milliseconds of a Duration, rounded towards zero. */
125
+ export function durationMs(duration: {
126
+ seconds?: Int64Value | null;
127
+ nanos?: number | null;
128
+ }): number;
129
+ /** Returns the Struct of a JSON object. */
130
+ export function structFromJson(json: JsonObject): StructInit;
131
+ /** Returns the JSON object of a Struct. */
132
+ export function structToJson(struct: StructInit): JsonObject;
133
+ /** Returns the Value of a JSON value. */
134
+ export function valueFromJson(json: JsonValue): ValueInit;
135
+ /** Returns the JSON value of a Value; one with no member of `kind` set is null. */
136
+ export function valueToJson(value: ValueInit): JsonValue;
7
137
 
8
138
  export const Code: Readonly<{
9
139
  Ok: 0;
@@ -36,6 +166,8 @@ export type Metadata = Record<string, Uint8Array>;
36
166
 
37
167
  /** Error carrying a TrevRPC status code, message, and metadata. */
38
168
  export class TrevRpcError extends Error {
169
+ /** Always `"TrevRpcError"`, for matching errors by tag, as Effect's `catchTag` does. */
170
+ readonly _tag: "TrevRpcError";
39
171
  readonly code: StatusCode;
40
172
  readonly statusMessage: string;
41
173
  readonly metadata: Metadata;
@@ -65,6 +197,59 @@ export interface CallOptions {
65
197
  onResponseMetadata?(metadata: Metadata): void;
66
198
  }
67
199
 
200
+ /** The kind of an RPC, as in its generated descriptor. */
201
+ export type CallKind = "unary" | "serverStreaming" | "clientStreaming" | "bidirectionalStreaming";
202
+
203
+ /** What a call interceptor sees of a call before it starts. */
204
+ export interface InterceptedCall {
205
+ /** The method's full path, such as `/example.greeter.Greeter/SayHello`. */
206
+ readonly method: string;
207
+ /** The service's full name, such as `example.greeter.Greeter`. */
208
+ readonly service: string;
209
+ /** The method's name, such as `SayHello`. */
210
+ readonly name: string;
211
+ readonly kind: CallKind;
212
+ /**
213
+ * The call's request metadata. An interceptor may add, replace and delete entries, such as a
214
+ * refreshed token; the result is validated after the last interceptor, and invalid metadata
215
+ * fails the call with InvalidArgument.
216
+ */
217
+ metadata: Record<string, MetadataValue>;
218
+ /** The call's deadline, which starts once the interceptors are done. */
219
+ readonly timeoutMs: number | undefined;
220
+ /** The call's abort signal, which an interceptor that waits can watch. */
221
+ readonly signal: AbortSignal | undefined;
222
+ }
223
+
224
+ /** How an intercepted call ended. */
225
+ export interface CallEnd {
226
+ /** `Code.Ok` when the call succeeded; the code of `error` otherwise, or `Code.Unknown`. */
227
+ readonly code: StatusCode;
228
+ /** What failed the call, usually a TrevRpcError; undefined when it succeeded. */
229
+ readonly error: unknown;
230
+ }
231
+
232
+ /** Called once when an intercepted call ends. A finish function that throws is reported, not rethrown. */
233
+ export type CallFinish = (end: CallEnd) => void;
234
+
235
+ /**
236
+ * Runs before each call of a client, in order with the others. It may change the call's metadata,
237
+ * wait (for example for a token refresh), and return a function that is called once when the call
238
+ * ends: when a unary call settles, and when a streaming call receives its final status or fails,
239
+ * including when it is cancelled. Finish functions run innermost first. A streaming call that is
240
+ * neither read to its end nor closed does not end. An interceptor that throws fails the call with
241
+ * what it threw, before it starts; the finish functions of earlier interceptors still run.
242
+ */
243
+ export type CallInterceptor = (
244
+ call: InterceptedCall,
245
+ ) => void | CallFinish | Promise<void | CallFinish>;
246
+
247
+ /** Options of a service client: defaults for its calls, and interceptors that run around them. */
248
+ export interface ClientOptions extends CallOptions {
249
+ /** Interceptors for the client's calls, after those of its transport, such as a Channel's. */
250
+ interceptors?: readonly CallInterceptor[];
251
+ }
252
+
68
253
  export interface TransportStream {
69
254
  readable: ReadableStream<Uint8Array>;
70
255
  writable: WritableStream<Uint8Array>;
@@ -83,6 +268,8 @@ export interface DatagramTransport {
83
268
  }
84
269
 
85
270
  declare const compressionBrand: unique symbol;
271
+ declare const connectionMetadataBrand: unique symbol;
272
+ declare const streamCompressionBudgetBrand: unique symbol;
86
273
 
87
274
  /**
88
275
  * Compression settings, made by `createCompression()` from `@trevrpc/trevrpc-js/zstd`: the zstd
@@ -92,6 +279,22 @@ export interface Compression {
92
279
  readonly [compressionBrand]: true;
93
280
  }
94
281
 
282
+ /**
283
+ * A Channel's connection metadata, which `Channel.setConnectionMetadata()` replaces. See
284
+ * `Transport.connectionMetadata`.
285
+ */
286
+ export interface ConnectionMetadataSource {
287
+ readonly [connectionMetadataBrand]: true;
288
+ }
289
+
290
+ /**
291
+ * A Channel's stream compression memory budget, from `ConnectOptions.streamCompressionMemory`.
292
+ * See `Transport.streamCompressionBudget`.
293
+ */
294
+ export interface StreamCompressionBudget {
295
+ readonly [streamCompressionBudgetBrand]: true;
296
+ }
297
+
95
298
  /**
96
299
  * Anything that opens a bidirectional byte stream per call, such as a Channel or WebTransport.
97
300
  * Calls to methods that mark a direction unreliable use the `datagrams` of the session that opens
@@ -103,6 +306,22 @@ export interface Transport {
103
306
  readonly datagrams?: DatagramTransport;
104
307
  /** Compression settings for calls on this transport; absent without compression. */
105
308
  readonly compression?: Compression;
309
+ /**
310
+ * The connection metadata calls on this transport carry; a Channel's own. A transport that
311
+ * wraps a Channel forwards it, or its calls carry no connection metadata.
312
+ */
313
+ readonly connectionMetadata?: ConnectionMetadataSource;
314
+ /**
315
+ * The memory budget of the stream compression contexts of calls on this transport; a Channel's
316
+ * own. A transport that wraps a Channel forwards it, or its calls get a default budget of their
317
+ * own.
318
+ */
319
+ readonly streamCompressionBudget?: StreamCompressionBudget;
320
+ /**
321
+ * Interceptors that clients created on this transport run around every call, before their own;
322
+ * a Channel's own. A transport that wraps a Channel forwards them, or its clients skip them.
323
+ */
324
+ readonly interceptors?: readonly CallInterceptor[];
106
325
  /**
107
326
  * Like `createBidirectionalStream()`, but also returns the session that opened the stream.
108
327
  * Calls negotiate dictionaries and open datagram flows per session, so transports whose session
@@ -174,6 +393,9 @@ export interface StreamStatus {
174
393
  /**
175
394
  * Responses of a server-streaming call. Iteration ends after the final OK status and throws a
176
395
  * TrevRpcError for any other outcome. Leaving the loop early cancels the call.
396
+ *
397
+ * Buffered messages are delivered without returning to the event loop, so a page that renders a
398
+ * long stream must yield to let the browser render; see "Rendering long streams" in the guide.
177
399
  */
178
400
  export interface ResponseStream<TResponse> extends AsyncIterable<TResponse> {
179
401
  /** Settles with the final status, or rejects with the error that ended the call. */
@@ -211,6 +433,8 @@ export interface ClientStreamingCall<TRequest, TResponse> {
211
433
  * the call and reject with what they threw.
212
434
  */
213
435
  sendAll(requests: Iterable<TRequest> | AsyncIterable<TRequest>): Promise<void>;
436
+ /** Settles with the final status, or rejects with the error that ended the call. */
437
+ readonly status: Promise<StreamStatus>;
214
438
  /** Closes the request stream and returns the final response. */
215
439
  closeAndRecv(): Promise<TResponse>;
216
440
  /** Cancels the call unless it already finished. */
@@ -224,6 +448,9 @@ export interface ClientStreamingCall<TRequest, TResponse> {
224
448
  * Response datagrams are delivered after the response headers, merged with the stream's messages
225
449
  * in arrival order, and dropped when the call's queue is full (32 messages or 1 MiB) or the call
226
450
  * has ended. Without datagrams, such as over WebSocket, everything stays on the stream.
451
+ *
452
+ * Like a `ResponseStream`, iteration delivers buffered messages without returning to the event
453
+ * loop.
227
454
  */
228
455
  export interface BidirectionalStreamingCall<TRequest, TResponse> extends AsyncIterable<TResponse> {
229
456
  /** Settles with the final status, or rejects with the error that ended the call. */
@@ -276,12 +503,15 @@ export type ServiceClient = Record<
276
503
  (requestOrOptions?: unknown, options?: CallOptions) => Promise<unknown>
277
504
  >;
278
505
 
279
- /** Creates a service client from a generated service descriptor. */
506
+ /**
507
+ * Creates a service client from a generated service descriptor. Its calls run the transport's
508
+ * interceptors, then those of `options.interceptors`.
509
+ */
280
510
  export function createServiceClient<TClient extends ServiceClient = ServiceClient>(
281
511
  transport: Transport,
282
512
  service: RpcServiceDescriptor,
283
513
  root: Root,
284
- options?: CallOptions,
514
+ options?: ClientOptions,
285
515
  ): TClient;
286
516
 
287
517
  export interface WebTransportCertificateHash {
@@ -289,6 +519,22 @@ export interface WebTransportCertificateHash {
289
519
  value: ArrayBuffer | ArrayBufferView;
290
520
  }
291
521
 
522
+ export interface FetchServerCertificateHashesOptions {
523
+ signal?: AbortSignal;
524
+ /** The fetch implementation to use instead of the global one. */
525
+ fetch?: (input: string | URL, init?: RequestInit) => Promise<Response>;
526
+ }
527
+
528
+ /**
529
+ * Fetches the certificate hashes a TrevRPC development server publishes, as JSON such as
530
+ * `[{"algorithm":"sha-256","value":"<hex>"}]`, for `serverCertificateHashes`. A 404 yields no
531
+ * hashes; any other failure rejects with Unavailable.
532
+ */
533
+ export function fetchServerCertificateHashes(
534
+ url: string | URL,
535
+ options?: FetchServerCertificateHashesOptions,
536
+ ): Promise<WebTransportCertificateHash[]>;
537
+
292
538
  export interface WebTransportSessionLike extends Transport {
293
539
  readonly ready: Promise<unknown>;
294
540
  /**
@@ -326,6 +572,14 @@ export interface ConnectOptions {
326
572
  * only: the WebSocket fallback needs a certificate the browser trusts.
327
573
  */
328
574
  serverCertificateHashes?: readonly WebTransportCertificateHash[];
575
+ /**
576
+ * A URL that publishes the server's development certificate hashes, such as a path that the
577
+ * development web server proxies to the TrevRPC server's DevelopmentCertificate. Every
578
+ * WebTransport attempt fetches it, so reconnects follow a rotated certificate, and adds its
579
+ * hashes to `serverCertificateHashes`; a 404 adds none, so the same build works against a server
580
+ * with a trusted certificate. A failed fetch fails the WebTransport attempt.
581
+ */
582
+ serverCertificateHashesUrl?: string | URL;
329
583
  /**
330
584
  * WebTransport implementation to use instead of the global one. It is constructed with
331
585
  * `protocols: ["trevrpc-1"]`, the wire version, which cannot be changed.
@@ -350,13 +604,18 @@ export interface ConnectOptions {
350
604
  * metadata. Replace it with `Channel.setConnectionMetadata()`.
351
605
  */
352
606
  connectionMetadata?: MetadataInput;
353
- /** Cancels the initial connection attempt only. */
607
+ /**
608
+ * Interceptors that every client created on the channel runs around its calls, before the
609
+ * client's own; see `CallInterceptor`.
610
+ */
611
+ interceptors?: readonly CallInterceptor[];
612
+ /** Cancels the initial connection attempt of `connect()` only; `createChannel()` ignores it. */
354
613
  signal?: AbortSignal;
355
614
  /** Receives channel state transitions. */
356
615
  onStateChange?(change: ChannelStateChange): void;
357
616
  }
358
617
 
359
- export type ChannelState = "ready" | "reconnecting" | "closed";
618
+ export type ChannelState = "connecting" | "ready" | "reconnecting" | "closed";
360
619
 
361
620
  export interface ChannelStateChange {
362
621
  state: ChannelState;
@@ -371,13 +630,26 @@ export interface ChannelStateChange {
371
630
  transport?: TransportKind;
372
631
  }
373
632
 
374
- /** Opens a channel to a TrevRPC server and waits for its session to be ready. */
633
+ /**
634
+ * Opens a channel to a TrevRPC server and waits for its session to be ready. It rejects, leaving
635
+ * nothing behind, if the first attempt fails.
636
+ */
375
637
  export function connect(url: string | URL, options?: ConnectOptions): Promise<Channel>;
376
638
 
639
+ /**
640
+ * Returns a channel to a TrevRPC server that connects in the background, so that clients can be
641
+ * created on it right away. Calls started before its first session is ready wait for that attempt
642
+ * (within their own deadline and signal); a failed attempt fails them, and the channel goes on
643
+ * reconnecting with backoff. Close the channel to give up; the `signal` option does not apply.
644
+ * Throws if an option is invalid.
645
+ */
646
+ export function createChannel(url: string | URL, options?: ConnectOptions): Channel;
647
+
377
648
  /**
378
649
  * A session that reconnects in the background after it is lost: WebTransport, or QMux over
379
- * WebSocket when WebTransport is unavailable or blocked. Calls fail fast with Unavailable while it
380
- * reconnects and are never replayed.
650
+ * WebSocket when WebTransport is unavailable or blocked. Calls started while it first connects wait
651
+ * for that attempt; afterwards they fail fast with Unavailable while it reconnects and are never
652
+ * replayed.
381
653
  */
382
654
  export class Channel implements Transport {
383
655
  private constructor();
@@ -386,10 +658,16 @@ export class Channel implements Transport {
386
658
  static connect(url: string | URL, options?: ConnectOptions): Promise<Channel>;
387
659
 
388
660
  readonly state: ChannelState;
389
- /** The transport of the current or last session. */
390
- readonly transport: TransportKind;
661
+ /** The transport of the current or last session; undefined before the first. */
662
+ readonly transport: TransportKind | undefined;
391
663
  /** The compression settings from the connect options. */
392
664
  readonly compression?: Compression;
665
+ /** The connection metadata of the channel's calls, which a wrapping Transport forwards. */
666
+ readonly connectionMetadata: ConnectionMetadataSource;
667
+ /** The stream compression memory budget of the channel's calls, which a wrapping Transport forwards. */
668
+ readonly streamCompressionBudget: StreamCompressionBudget;
669
+ /** The interceptors from the connect options, which a wrapping Transport forwards. */
670
+ readonly interceptors: readonly CallInterceptor[];
393
671
  /**
394
672
  * Replaces the channel's connection metadata (see `ConnectOptions.connectionMetadata`); empty or
395
673
  * missing metadata removes it. Calls started after this use the new metadata, and each
package/src/index.js CHANGED
@@ -1,4 +1,18 @@
1
- export { Channel, connect } from "./channel.js";
1
+ export { fetchServerCertificateHashes } from "./certificate-hashes.js";
2
+ export { Channel, connect, createChannel } from "./channel.js";
2
3
  export { createServiceClient } from "./client.js";
3
4
  export { Code, TrevRpcError, codeName } from "./status.js";
4
5
  export { createRoot, protobuf } from "./wire.js";
6
+ export {
7
+ NullValue,
8
+ durationFromMs,
9
+ durationMs,
10
+ structFromJson,
11
+ structToJson,
12
+ timestampDate,
13
+ timestampFromDate,
14
+ timestampFromMs,
15
+ timestampMs,
16
+ valueFromJson,
17
+ valueToJson,
18
+ } from "./wkt.js";