@trevrpc/trevrpc-js 0.2.2 → 1.0.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/README.md +89 -47
  2. package/bin/protoc-gen-trevrpc-js.js +1 -1
  3. package/package.json +11 -41
  4. package/src/bytes.js +10 -0
  5. package/src/call.js +287 -0
  6. package/src/channel.js +131 -296
  7. package/src/client.js +203 -1292
  8. package/src/framing.js +125 -519
  9. package/src/generator.js +26 -103
  10. package/src/index.d.ts +79 -452
  11. package/src/index.js +4 -3
  12. package/src/status.js +24 -70
  13. package/src/wire.js +4 -59
  14. package/examples/greeter/README.md +0 -35
  15. package/examples/greeter/client.js +0 -172
  16. package/examples/greeter/greeter.node.trevrpc.d.ts +0 -53
  17. package/examples/greeter/greeter.node.trevrpc.js +0 -9
  18. package/examples/greeter/greeter.proto +0 -18
  19. package/examples/greeter/greeter.trevrpc.d.ts +0 -55
  20. package/examples/greeter/greeter.trevrpc.js +0 -103
  21. package/examples/greeter/index.html +0 -140
  22. package/examples/greeter/static-server.js +0 -168
  23. package/src/advanced.d.ts +0 -29
  24. package/src/advanced.js +0 -1
  25. package/src/browser-channel.js +0 -49
  26. package/src/connect.js +0 -6
  27. package/src/connect.node.js +0 -6
  28. package/src/index.node.d.ts +0 -10
  29. package/src/index.node.js +0 -3
  30. package/src/native-loader.js +0 -118
  31. package/src/node-advanced.d.ts +0 -27
  32. package/src/node-advanced.js +0 -1
  33. package/src/node-generated.d.ts +0 -24
  34. package/src/node-generated.js +0 -126
  35. package/src/node-index.d.ts +0 -1
  36. package/src/node-index.js +0 -7
  37. package/src/node.d.ts +0 -298
  38. package/src/node.js +0 -1456
  39. package/src/runtime.js +0 -66
  40. package/src/server-response.js +0 -49
  41. package/src/timer.js +0 -29
  42. package/src/webtransport.js +0 -639
package/src/framing.js CHANGED
@@ -1,534 +1,211 @@
1
- import { FrameTooLargeError, invalidArgument, unavailable } from "./status.js";
2
- import {
3
- RpcStreamFrame,
4
- RpcStreamFrameKind,
5
- preflightWireMessage,
6
- validateWireMessage,
7
- } from "./wire.js";
1
+ import { byteView } from "./bytes.js";
2
+ import { Code, invalidArgument, resourceExhausted, unavailable } from "./status.js";
3
+ import { RpcStreamFrame, RpcStreamFrameKind, preflightWireMessage } from "./wire.js";
8
4
 
9
5
  export const DefaultMaxFrameSize = 4 * 1024 * 1024;
10
6
 
11
- const FrameHeaderLength = 4;
7
+ const HeaderLength = 4;
8
+ /** Tag of RpcStreamFrame.body: field 4, length-delimited. */
12
9
  const StreamFrameBodyTag = (4 << 3) | 2;
13
10
  const EmptyBytes = new Uint8Array(0);
14
- const utf8Decoder = new TextDecoder("utf-8", { fatal: true });
15
11
 
16
12
  /** Encodes a protobuf message body. */
17
13
  export function marshalMessage(messageType, message) {
18
- const prepared = prepareMessage(messageType, message);
19
- return messageType.encode(prepared).finish();
14
+ try {
15
+ return messageType.encode(messageType.fromObject(message)).finish();
16
+ } catch (error) {
17
+ throw invalidArgument(
18
+ `failed to encode protobuf message: ${error?.message ?? String(error)}`,
19
+ error,
20
+ );
21
+ }
20
22
  }
21
23
 
22
- /** Decodes a protobuf message body. */
24
+ /** Decodes a protobuf message body, rejecting malformed wire data. */
23
25
  export function unmarshalMessage(messageType, body) {
24
26
  try {
25
27
  preflightWireMessage(messageType, body);
26
28
  return messageType.decode(body);
27
29
  } catch (error) {
28
- const failure = invalidArgument(
30
+ throw invalidArgument(
29
31
  `failed to decode protobuf message: ${error?.message ?? String(error)}`,
32
+ error,
30
33
  );
31
- failure.reason = error?.reason ?? "malformed_protobuf";
32
- failure.cause = error;
33
- throw failure;
34
34
  }
35
35
  }
36
36
 
37
- /** Encodes a protobuf message into a length-prefixed TrevRPC frame. */
37
+ /** Encodes a protobuf message into a length-prefixed frame. */
38
38
  export function encodeFrame(messageType, message, maxFrameSize = DefaultMaxFrameSize) {
39
39
  const body = marshalMessage(messageType, message);
40
- if (body.byteLength > maxFrameSize) {
41
- throw new FrameTooLargeError(body.byteLength, maxFrameSize);
42
- }
43
-
44
- const frame = new Uint8Array(FrameHeaderLength + body.byteLength);
45
- const view = new DataView(frame.buffer, frame.byteOffset, frame.byteLength);
46
- view.setUint32(0, body.byteLength, false);
47
- frame.set(body, FrameHeaderLength);
40
+ checkFrameLength(body.byteLength, maxFrameSize);
41
+ const frame = new Uint8Array(HeaderLength + body.byteLength);
42
+ new DataView(frame.buffer).setUint32(0, body.byteLength, false);
43
+ frame.set(body, HeaderLength);
48
44
  return frame;
49
45
  }
50
46
 
51
- /** Encodes one streaming message frame carrying an already-encoded protobuf body. */
52
- export function encodeMessageStreamFrame(body, maxFrameSize = DefaultMaxFrameSize) {
53
- return encodeMessageStreamFrames([body], maxFrameSize);
54
- }
55
-
56
- /** Encodes multiple streaming message frames into one contiguous write buffer. */
57
- export function encodeMessageStreamFrames(
58
- bodies,
59
- maxFrameSize = DefaultMaxFrameSize,
60
- start = 0,
61
- end = undefined,
62
- ) {
63
- const range = bodyRange(bodies, start, end);
64
- let totalLength = 0;
65
- const byteBodies = Array.from({ length: range.end - range.start });
66
- for (let index = range.start; index < range.end; index += 1) {
67
- const body = byteView(range.bodies[index]);
68
- byteBodies[index - range.start] = body;
69
- const bodyLength = messageStreamFrameBodyLength(body.byteLength);
70
- if (bodyLength > maxFrameSize) {
71
- throw new FrameTooLargeError(bodyLength, maxFrameSize);
72
- }
73
- totalLength += FrameHeaderLength + bodyLength;
74
- }
75
-
76
- const frames = new Uint8Array(totalLength);
77
- const view = new DataView(frames.buffer, frames.byteOffset, frames.byteLength);
78
- let offset = 0;
79
- for (const body of byteBodies) {
80
- const bodyLength = messageStreamFrameBodyLength(body.byteLength);
81
- view.setUint32(offset, bodyLength, false);
82
- offset += FrameHeaderLength;
83
- if (body.byteLength > 0) {
84
- frames[offset++] = StreamFrameBodyTag;
85
- offset = appendVarint(frames, offset, body.byteLength);
86
- frames.set(body, offset);
87
- offset += body.byteLength;
88
- }
89
- }
90
-
91
- return frames;
92
- }
93
-
94
- /** Encodes each streaming message frame separately for vectored writers. */
95
- export function encodeMessageStreamFrameParts(
96
- bodies,
97
- maxFrameSize = DefaultMaxFrameSize,
98
- start = 0,
99
- end = undefined,
100
- ) {
101
- const range = bodyRange(bodies, start, end);
102
- const frames = Array.from({ length: range.end - range.start });
103
- for (let index = range.start; index < range.end; index += 1) {
104
- frames[index - range.start] = encodeMessageStreamFrame(range.bodies[index], maxFrameSize);
47
+ /**
48
+ * Encodes a streaming message frame carrying an already-encoded protobuf body.
49
+ * This is byte-identical to encodeFrame(RpcStreamFrame, { body }) without building the
50
+ * intermediate message.
51
+ */
52
+ export function encodeMessageFrame(body, maxFrameSize = DefaultMaxFrameSize) {
53
+ const frameLength =
54
+ body.byteLength === 0 ? 0 : 1 + varintLength(body.byteLength) + body.byteLength;
55
+ checkFrameLength(frameLength, maxFrameSize);
56
+
57
+ const frame = new Uint8Array(HeaderLength + frameLength);
58
+ new DataView(frame.buffer).setUint32(0, frameLength, false);
59
+ if (body.byteLength > 0) {
60
+ let offset = HeaderLength;
61
+ frame[offset++] = StreamFrameBodyTag;
62
+ offset = writeVarint(frame, offset, body.byteLength);
63
+ frame.set(body, offset);
105
64
  }
106
- return frames;
107
- }
108
-
109
- /** Decodes a protobuf message from a TrevRPC frame body. */
110
- export function decodeFrame(messageType, body) {
111
- return unmarshalMessage(messageType, body);
65
+ return frame;
112
66
  }
113
67
 
114
- /** Decodes a streaming RPC frame body using a fast path for common message frames. */
115
- export function decodeStreamFrameBody(body) {
68
+ /** Decodes the body of a response stream frame, validating its kind. */
69
+ export function decodeStreamFrame(body) {
116
70
  const bytes = byteView(body);
117
- try {
118
- preflightWireMessage(RpcStreamFrame, bytes);
119
- } catch (error) {
120
- const failure = invalidArgument(
121
- `failed to decode protobuf message: ${error?.message ?? String(error)}`,
122
- );
123
- failure.reason = error?.reason ?? "malformed_protobuf";
124
- failure.cause = error;
125
- throw failure;
126
- }
127
- if (bytes.byteLength === 0) {
128
- return streamFrameMessage(EmptyBytes);
71
+ const messageBody = bareMessageBody(bytes);
72
+ if (messageBody !== undefined) {
73
+ return { kind: RpcStreamFrameKind.Message, status: Code.Ok, message: "", body: messageBody };
129
74
  }
130
75
 
131
- const cursor = { offset: 0 };
132
- let kind = RpcStreamFrameKind.Message;
133
- let status = 0;
134
- let message = "";
135
- let frameBody = new Uint8Array(0);
136
-
137
- while (cursor.offset < bytes.byteLength) {
138
- const tag = consumeVarint(bytes, cursor, "invalid stream frame field tag");
139
- if (tag === 0) {
140
- throw invalidArgument("invalid stream frame field tag");
141
- }
142
-
143
- const field = Math.floor(tag / 8);
144
- const wireType = tag % 8;
145
- switch (field) {
146
- case 1:
147
- requireWireType(wireType, 0, "invalid stream frame kind wire type");
148
- kind = consumeVarint(bytes, cursor, "truncated stream frame kind");
149
- break;
150
- case 2:
151
- requireWireType(wireType, 0, "invalid stream frame status wire type");
152
- status = consumeVarint(bytes, cursor, "truncated stream frame status");
153
- break;
154
- case 3:
155
- requireWireType(wireType, 2, "invalid stream frame message wire type");
156
- message = decodeUtf8(consumeLengthDelimited(bytes, cursor, "invalid stream frame message"));
157
- break;
158
- case 4:
159
- requireWireType(wireType, 2, "invalid stream frame body wire type");
160
- frameBody = consumeLengthDelimited(bytes, cursor, "invalid stream frame body");
161
- break;
162
- case 5: {
163
- const frame = decodeFrame(RpcStreamFrame, bytes);
164
- validateDecodedWireMessage(RpcStreamFrame, frame);
165
- return frame;
166
- }
167
- default:
168
- skipProtoField(bytes, cursor, wireType);
169
- break;
170
- }
76
+ const frame = unmarshalMessage(RpcStreamFrame, bytes);
77
+ if (frame.kind !== RpcStreamFrameKind.Message && frame.kind !== RpcStreamFrameKind.Status) {
78
+ throw invalidArgument(`unsupported stream frame kind ${frame.kind}`);
171
79
  }
172
-
173
- const frame = { kind, status, message, body: frameBody, metadata: {} };
174
- validateDecodedWireMessage(RpcStreamFrame, frame);
175
80
  return frame;
176
81
  }
177
82
 
178
- /** Writes one length-prefixed protobuf frame. */
179
- export async function writeFrame(writer, messageType, message, maxFrameSize = DefaultMaxFrameSize) {
180
- await writer.write(encodeFrame(messageType, message, maxFrameSize));
181
- }
182
-
183
- /** Writes multiple streaming message frames in one write. */
184
- export async function writeMessageStreamFrames(
185
- writer,
186
- bodies,
187
- maxFrameSize = DefaultMaxFrameSize,
188
- start = 0,
189
- end = undefined,
190
- ) {
191
- if (typeof writer.writev === "function") {
192
- await writer.writev(encodeMessageStreamFrameParts(bodies, maxFrameSize, start, end));
193
- return;
194
- }
195
-
196
- await writer.write(encodeMessageStreamFrames(bodies, maxFrameSize, start, end));
83
+ /** Validates the body length stored in a frame header. */
84
+ export function frameBodyLength(header, maxFrameSize = DefaultMaxFrameSize) {
85
+ const length = new DataView(header.buffer, header.byteOffset, header.byteLength).getUint32(
86
+ 0,
87
+ false,
88
+ );
89
+ checkFrameLength(length, maxFrameSize);
90
+ return length;
197
91
  }
198
92
 
199
- /** Reads length-prefixed protobuf frames from a byte stream reader. */
93
+ /** Reads length-prefixed frames from a byte stream reader. */
200
94
  export class FrameReader {
201
- /** Creates a frame reader over a stream reader. */
95
+ #reader;
96
+ /** Bytes received from the reader but not yet consumed. */
97
+ #pending = EmptyBytes;
98
+
99
+ /** Creates a frame reader over a ReadableStream reader. */
202
100
  constructor(reader) {
203
- this.reader = reader;
204
- this.chunks = [];
205
- this.chunkHead = 0;
206
- this.buffered = 0;
101
+ this.#reader = reader;
207
102
  }
208
103
 
209
- /** Reads and decodes one frame. */
104
+ /** Reads and decodes one frame; a stream that ends first is an error. */
210
105
  async readFrame(messageType, maxFrameSize = DefaultMaxFrameSize) {
211
- const header = await this.readExact(FrameHeaderLength, false);
212
- const length = frameBodyLength(header, maxFrameSize);
213
- const body = await this.readExact(length, false);
214
- return decodeFrame(messageType, body);
106
+ return unmarshalMessage(messageType, await this.#readBody(maxFrameSize, false));
215
107
  }
216
108
 
217
- /** Reads and decodes one frame, or returns null when already at EOF. */
218
- async readFrameOrEOF(messageType, maxFrameSize = DefaultMaxFrameSize) {
219
- const header = await this.readExact(FrameHeaderLength, true);
220
- if (header == null) {
221
- return null;
222
- }
223
-
224
- const length = frameBodyLength(header, maxFrameSize);
225
- const body = await this.readExact(length, false);
226
- return decodeFrame(messageType, body);
227
- }
228
-
229
- /** Reads and decodes one streaming RPC frame, or returns null when already at EOF. */
230
- async readStreamFrameOrEOF(maxFrameSize = DefaultMaxFrameSize) {
231
- const header = await this.readExact(FrameHeaderLength, true);
232
- if (header == null) {
233
- return null;
234
- }
235
-
236
- const length = frameBodyLength(header, maxFrameSize);
237
- const body = await this.readExact(length, false);
238
- return decodeStreamFrameBody(body);
109
+ /** Reads one streaming RPC frame, or returns null when the stream ended cleanly. */
110
+ async readStreamFrame(maxFrameSize = DefaultMaxFrameSize) {
111
+ const body = await this.#readBody(maxFrameSize, true);
112
+ return body === null ? null : decodeStreamFrame(body);
239
113
  }
240
114
 
241
- /** Reads one streaming RPC frame, then drains complete frames already buffered. */
242
- async readStreamFrameBatchOrEOF(maxBatch = 32, maxFrameSize = DefaultMaxFrameSize) {
243
- const first = await this.readStreamFrameOrEOF(maxFrameSize);
244
- if (first == null) {
245
- return null;
246
- }
247
-
248
- const limit = Math.max(1, Math.floor(maxBatch));
249
- const frames = [first];
250
- while (frames.length < limit) {
251
- const body = this.tryReadFrameBody(maxFrameSize);
252
- if (body === undefined) {
253
- break;
115
+ /** Resolves when the stream ends cleanly; rejects if more data arrives first. */
116
+ async expectEnd() {
117
+ if (this.#pending.byteLength === 0) {
118
+ const { value, done } = await this.#nextChunk();
119
+ if (done) {
120
+ return;
254
121
  }
255
- frames.push(decodeStreamFrameBody(body));
122
+ this.#pending = value;
256
123
  }
257
- return frames;
124
+ throw unavailable("stream carried data after the final frame");
258
125
  }
259
126
 
260
- /** Reads stream message body batches, with at most one terminal status. */
261
- async readStreamMessageBodyBatchOrEOF(maxBatch = 32, maxFrameSize = DefaultMaxFrameSize) {
262
- const body = await this.readRawFrameBodyOrEOF(maxFrameSize);
263
- if (body == null) {
127
+ async #readBody(maxFrameSize, allowEnd) {
128
+ const header = await this.#readExact(HeaderLength, allowEnd);
129
+ if (header === null) {
264
130
  return null;
265
131
  }
266
-
267
- const limit = Math.max(1, Math.floor(maxBatch));
268
- const result = { bodies: [], status: null };
269
- pushStreamBodyOrStatus(result, body);
270
- while (result.status == null && result.bodies.length < limit) {
271
- const nextBody = this.tryReadFrameBody(maxFrameSize);
272
- if (nextBody === undefined) {
273
- break;
274
- }
275
- pushStreamBodyOrStatus(result, nextBody);
276
- }
277
-
278
- return result;
132
+ return await this.#readExact(frameBodyLength(header, maxFrameSize), false);
279
133
  }
280
134
 
281
- /** Reads one raw frame body, or returns null when already at EOF. */
282
- async readRawFrameBodyOrEOF(maxFrameSize = DefaultMaxFrameSize) {
283
- const header = await this.readExact(FrameHeaderLength, true);
284
- if (header == null) {
285
- return null;
135
+ /** Returns exactly size bytes, or null if allowEnd and the stream ended before the first byte. */
136
+ async #readExact(size, allowEnd) {
137
+ if (this.#pending.byteLength >= size) {
138
+ return this.#take(size);
286
139
  }
287
140
 
288
- return this.readExact(frameBodyLength(header, maxFrameSize), false);
289
- }
290
-
291
- /** Reads a complete buffered frame body without waiting, or undefined. */
292
- tryReadFrameBody(maxFrameSize = DefaultMaxFrameSize) {
293
- if (this.buffered < FrameHeaderLength) {
294
- return undefined;
295
- }
296
-
297
- const header = this.peek(FrameHeaderLength);
298
- const length = frameBodyLength(header, maxFrameSize);
299
- if (this.buffered < FrameHeaderLength + length) {
300
- return undefined;
301
- }
302
-
303
- this.consume(FrameHeaderLength);
304
- return this.consume(length);
305
- }
306
-
307
- /** Reads exactly size bytes from the underlying reader. */
308
- async readExact(size, allowEofAtStart) {
309
- if (size === 0) {
310
- return EmptyBytes;
311
- }
312
-
313
- while (this.buffered < size) {
314
- const { value, done } = await this.reader.read();
141
+ // Copies are needed only when a read spans chunks; whole frames inside one chunk are views.
142
+ const out = new Uint8Array(size);
143
+ let filled = this.#pending.byteLength;
144
+ out.set(this.#pending);
145
+ this.#pending = EmptyBytes;
146
+ while (filled < size) {
147
+ const { value, done } = await this.#nextChunk();
315
148
  if (done) {
316
- if (allowEofAtStart && this.buffered === 0) {
149
+ if (allowEnd && filled === 0) {
317
150
  return null;
318
151
  }
319
-
320
- const error = unavailable("transport unavailable: stream ended in the middle of a frame");
321
- error.reason = "incomplete_frame";
322
- throw error;
323
- }
324
-
325
- if (value == null || value.byteLength === 0) {
326
- continue;
152
+ throw unavailable("stream ended before a complete frame");
327
153
  }
328
-
329
- const bytes = value instanceof Uint8Array ? value : new Uint8Array(value);
330
- this.chunks.push(bytes);
331
- this.buffered += bytes.byteLength;
154
+ const count = Math.min(value.byteLength, size - filled);
155
+ out.set(value.subarray(0, count), filled);
156
+ filled += count;
157
+ this.#pending = value.subarray(count);
332
158
  }
333
-
334
- return this.consume(size);
159
+ return out;
335
160
  }
336
161
 
337
- /** Removes size bytes from the buffered data. */
338
- consume(size) {
339
- if (size === 0) {
340
- return EmptyBytes;
341
- }
342
-
343
- const first = this.chunks[this.chunkHead];
344
- if (first != null && first.byteLength >= size) {
345
- const result = first.subarray(0, size);
346
- if (first.byteLength === size) {
347
- this.chunkHead += 1;
348
- this.compactChunks();
349
- } else {
350
- this.chunks[this.chunkHead] = first.subarray(size);
162
+ async #nextChunk() {
163
+ for (;;) {
164
+ const { value, done } = await this.#reader.read();
165
+ if (done) {
166
+ return { done: true };
351
167
  }
352
- this.buffered -= size;
353
- return result;
354
- }
355
-
356
- const result = new Uint8Array(size);
357
- let offset = 0;
358
-
359
- while (offset < size) {
360
- const chunk = this.chunks[this.chunkHead];
361
- const needed = size - offset;
362
- if (chunk.byteLength <= needed) {
363
- result.set(chunk, offset);
364
- offset += chunk.byteLength;
365
- this.chunkHead += 1;
366
- } else {
367
- result.set(chunk.subarray(0, needed), offset);
368
- this.chunks[this.chunkHead] = chunk.subarray(needed);
369
- offset += needed;
168
+ if (value != null && value.byteLength > 0) {
169
+ return { done: false, value: byteView(value) };
370
170
  }
371
171
  }
372
-
373
- this.buffered -= size;
374
- this.compactChunks();
375
- return result;
376
172
  }
377
173
 
378
- /** Copies size bytes from the buffered data without removing them. */
379
- peek(size) {
380
- if (size === 0) {
381
- return EmptyBytes;
382
- }
383
-
384
- const first = this.chunks[this.chunkHead];
385
- if (first != null && first.byteLength >= size) {
386
- return first.subarray(0, size);
387
- }
388
-
389
- const result = new Uint8Array(size);
390
- let offset = 0;
391
-
392
- for (let index = this.chunkHead; index < this.chunks.length; index += 1) {
393
- const chunk = this.chunks[index];
394
- if (offset >= size) {
395
- break;
396
- }
397
- const count = Math.min(chunk.byteLength, size - offset);
398
- result.set(chunk.subarray(0, count), offset);
399
- offset += count;
400
- }
401
-
402
- return result;
403
- }
404
-
405
- compactChunks() {
406
- if (this.chunkHead === 0) {
407
- return;
408
- }
409
- if (this.chunkHead === this.chunks.length) {
410
- this.chunks.length = 0;
411
- this.chunkHead = 0;
412
- return;
413
- }
414
- if (this.chunkHead >= 32 && this.chunkHead * 2 >= this.chunks.length) {
415
- this.chunks.splice(0, this.chunkHead);
416
- this.chunkHead = 0;
417
- }
174
+ #take(size) {
175
+ const bytes = this.#pending.subarray(0, size);
176
+ this.#pending = this.#pending.subarray(size);
177
+ return bytes;
418
178
  }
419
179
  }
420
180
 
421
- function pushStreamBodyOrStatus(result, body) {
422
- const messageBody = tryDecodeStreamMessageBody(body);
423
- if (messageBody !== undefined) {
424
- result.bodies.push(messageBody);
425
- return;
426
- }
427
-
428
- const frame = decodeStreamFrameBody(body);
429
- switch (frame.kind) {
430
- case RpcStreamFrameKind.Message:
431
- result.bodies.push(frame.body ?? EmptyBytes);
432
- break;
433
- case RpcStreamFrameKind.Status:
434
- result.status = frame;
435
- break;
436
- default:
437
- throw invalidArgument("stream frame contained an unknown frame kind");
181
+ function checkFrameLength(length, maxFrameSize) {
182
+ if (length > maxFrameSize) {
183
+ throw resourceExhausted(`frame length ${length} exceeds maximum ${maxFrameSize}`);
438
184
  }
439
185
  }
440
186
 
441
- function tryDecodeStreamMessageBody(body) {
442
- const bytes = byteView(body);
187
+ /** Returns the body of a message-only stream frame, or undefined if it carries anything else. */
188
+ function bareMessageBody(bytes) {
443
189
  if (bytes.byteLength === 0) {
444
190
  return EmptyBytes;
445
191
  }
446
-
447
192
  if (bytes[0] !== StreamFrameBodyTag) {
448
193
  return undefined;
449
194
  }
450
195
 
451
- const cursor = { offset: 1 };
452
- const length = consumeVarint(bytes, cursor, "invalid stream frame body");
453
- const end = cursor.offset + length;
454
- if (end !== bytes.byteLength) {
455
- return undefined;
456
- }
457
- return bytes.subarray(cursor.offset, end);
458
- }
459
-
460
- /** Decodes and validates the body length stored in a TrevRPC frame header. */
461
- export function frameBodyLength(header, maxFrameSize = DefaultMaxFrameSize) {
462
- const view = new DataView(header.buffer, header.byteOffset, header.byteLength);
463
- const length = view.getUint32(0, false);
464
- if (length > maxFrameSize) {
465
- throw new FrameTooLargeError(length, maxFrameSize);
466
- }
467
-
468
- return length;
469
- }
470
-
471
- function validateDecodedWireMessage(messageType, message) {
472
- try {
473
- validateWireMessage(messageType, message);
474
- } catch (error) {
475
- const failure = invalidArgument(error?.message ?? String(error));
476
- failure.reason = error?.reason;
477
- failure.cause = error;
478
- throw failure;
479
- }
480
- }
481
-
482
- function prepareMessage(messageType, message) {
483
- const prepared =
484
- typeof messageType.fromObject === "function" ? messageType.fromObject(message) : message;
485
- const error = typeof messageType.verify === "function" ? messageType.verify(prepared) : null;
486
- if (error != null) {
487
- throw invalidArgument(`failed to encode protobuf message: ${error}`);
488
- }
489
-
490
- return prepared;
491
- }
492
-
493
- function streamFrameMessage(body) {
494
- return {
495
- kind: RpcStreamFrameKind.Message,
496
- status: 0,
497
- message: "",
498
- body,
499
- metadata: {},
500
- };
501
- }
502
-
503
- function byteView(value) {
504
- if (value instanceof Uint8Array) {
505
- return value;
506
- }
507
- if (ArrayBuffer.isView(value)) {
508
- return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
509
- }
510
- return new Uint8Array(value ?? 0);
511
- }
512
-
513
- function bodyRange(bodies, start, end) {
514
- const source = Array.isArray(bodies) ? bodies : Array.from(bodies);
515
- const from = clampRangeIndex(start, source.length);
516
- const to = Math.max(from, clampRangeIndex(end ?? source.length, source.length));
517
- return { bodies: source, start: from, end: to };
518
- }
519
-
520
- function clampRangeIndex(value, length) {
521
- if (!Number.isFinite(value)) {
522
- return length;
196
+ let length = 0;
197
+ let offset = 1;
198
+ for (let shift = 0; shift < 35 && offset < bytes.byteLength; shift += 7) {
199
+ const byte = bytes[offset++];
200
+ length += (byte & 0x7f) * 2 ** shift;
201
+ if (byte < 0x80) {
202
+ return offset + length === bytes.byteLength ? bytes.subarray(offset) : undefined;
203
+ }
523
204
  }
524
- return Math.min(length, Math.max(0, Math.floor(value)));
205
+ return undefined;
525
206
  }
526
207
 
527
- function messageStreamFrameBodyLength(bodyLength) {
528
- return bodyLength === 0 ? 0 : 1 + varintLength(bodyLength) + bodyLength;
529
- }
530
-
531
- function appendVarint(output, offset, value) {
208
+ function writeVarint(output, offset, value) {
532
209
  let remaining = value;
533
210
  while (remaining >= 0x80) {
534
211
  output[offset++] = (remaining % 0x80) | 0x80;
@@ -539,80 +216,9 @@ function appendVarint(output, offset, value) {
539
216
  }
540
217
 
541
218
  function varintLength(value) {
542
- let remaining = value;
543
219
  let length = 1;
544
- while (remaining >= 0x80) {
545
- remaining = Math.floor(remaining / 0x80);
220
+ for (let remaining = value; remaining >= 0x80; remaining = Math.floor(remaining / 0x80)) {
546
221
  length += 1;
547
222
  }
548
223
  return length;
549
224
  }
550
-
551
- function consumeVarint(data, cursor, message) {
552
- let value = 0;
553
- let multiplier = 1;
554
- for (let index = 0; index < 10; index += 1) {
555
- if (cursor.offset >= data.byteLength) {
556
- throw invalidArgument(message);
557
- }
558
- const byte = data[cursor.offset++];
559
- value += (byte & 0x7f) * multiplier;
560
- if (byte < 0x80) {
561
- return value;
562
- }
563
- multiplier *= 0x80;
564
- }
565
- throw invalidArgument("stream frame varint exceeded 64 bits");
566
- }
567
-
568
- function requireWireType(actual, expected, message) {
569
- if (actual !== expected) {
570
- throw invalidArgument(message);
571
- }
572
- }
573
-
574
- function consumeLengthDelimited(data, cursor, message) {
575
- const length = consumeVarint(data, cursor, message);
576
- const end = cursor.offset + length;
577
- if (!Number.isSafeInteger(end) || end > data.byteLength) {
578
- throw invalidArgument(message);
579
- }
580
- const value = data.subarray(cursor.offset, end);
581
- cursor.offset = end;
582
- return value;
583
- }
584
-
585
- function skipProtoField(data, cursor, wireType) {
586
- switch (wireType) {
587
- case 0:
588
- consumeVarint(data, cursor, "truncated stream frame varint");
589
- return;
590
- case 1:
591
- skipFixed(data, cursor, 8);
592
- return;
593
- case 2:
594
- consumeLengthDelimited(data, cursor, "truncated stream frame field");
595
- return;
596
- case 5:
597
- skipFixed(data, cursor, 4);
598
- return;
599
- default:
600
- throw invalidArgument("stream frame contained an unsupported wire type");
601
- }
602
- }
603
-
604
- function skipFixed(data, cursor, length) {
605
- const end = cursor.offset + length;
606
- if (end > data.byteLength) {
607
- throw invalidArgument("truncated stream frame fixed field");
608
- }
609
- cursor.offset = end;
610
- }
611
-
612
- function decodeUtf8(bytes) {
613
- try {
614
- return utf8Decoder.decode(bytes);
615
- } catch (error) {
616
- throw invalidArgument(`stream frame message was not valid UTF-8: ${error.message}`);
617
- }
618
- }