@trevrpc/trevrpc-js 0.2.3 → 1.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.
- package/README.md +127 -46
- package/package.json +12 -45
- package/src/bytes.js +10 -0
- package/src/call.js +520 -0
- package/src/channel.js +258 -288
- package/src/client.js +543 -1254
- package/src/datagram.js +336 -0
- package/src/framing.js +151 -548
- package/src/generator.js +66 -109
- package/src/index.d.ts +235 -463
- package/src/index.js +4 -3
- package/src/qmux/frames.js +262 -0
- package/src/qmux/params.js +104 -0
- package/src/qmux/session.js +739 -0
- package/src/race.js +90 -0
- package/src/status.js +24 -70
- package/src/varint.js +73 -0
- package/src/wire.js +21 -89
- package/examples/greeter/README.md +0 -35
- package/examples/greeter/client.js +0 -172
- package/examples/greeter/greeter.node.trevrpc.d.ts +0 -53
- package/examples/greeter/greeter.node.trevrpc.js +0 -9
- package/examples/greeter/greeter.proto +0 -18
- package/examples/greeter/greeter.trevrpc.d.ts +0 -55
- package/examples/greeter/greeter.trevrpc.js +0 -103
- package/examples/greeter/index.html +0 -140
- package/examples/greeter/static-server.js +0 -168
- package/src/advanced.d.ts +0 -29
- package/src/advanced.js +0 -1
- package/src/browser-channel.js +0 -49
- package/src/connect.js +0 -6
- package/src/connect.node.js +0 -6
- package/src/index.node.d.ts +0 -10
- package/src/index.node.js +0 -3
- package/src/native-loader.js +0 -118
- package/src/node-advanced.d.ts +0 -27
- package/src/node-advanced.js +0 -1
- package/src/node-generated.d.ts +0 -24
- package/src/node-generated.js +0 -126
- package/src/node-index.d.ts +0 -1
- package/src/node-index.js +0 -7
- package/src/node.d.ts +0 -298
- package/src/node.js +0 -1456
- package/src/runtime.js +0 -66
- package/src/server-response.js +0 -49
- package/src/timer.js +0 -29
- package/src/webtransport.js +0 -639
package/src/node.js
DELETED
|
@@ -1,1456 +0,0 @@
|
|
|
1
|
-
import { getSystemErrorName } from "node:util";
|
|
2
|
-
|
|
3
|
-
import { ChannelStateMachine, stripChannelOptions, waitForInitialReady } from "./channel.js";
|
|
4
|
-
import { RequestWriterSettlement } from "./client.js";
|
|
5
|
-
import { loadNativeAddon } from "./native-loader.js";
|
|
6
|
-
import { Code, TrevRpcError, cancelled, codeFromNumber, invalidArgument } from "./status.js";
|
|
7
|
-
import { scheduleTimeout } from "./timer.js";
|
|
8
|
-
import { RpcKind, RpcStreamFrameKind } from "./wire.js";
|
|
9
|
-
|
|
10
|
-
const EmptyBody = new Uint8Array(0);
|
|
11
|
-
const RecvManyBatchSize = 32;
|
|
12
|
-
const SendManyBatchSize = 16;
|
|
13
|
-
const NativeTransportClosed = -1001;
|
|
14
|
-
const NativeFrameLimit = -1002;
|
|
15
|
-
const NativeMsQuicTimeout = -1003;
|
|
16
|
-
const NativeStreamLimit = -1004;
|
|
17
|
-
const NativeInvalidFrame = -2001;
|
|
18
|
-
const NativeUnsupportedWireVersion = -2002;
|
|
19
|
-
const NativeInvalidFrameKind = -2003;
|
|
20
|
-
const NativeSendByteLimit = -2005;
|
|
21
|
-
const NativeSendCountLimit = -2006;
|
|
22
|
-
const NativeObjectClosed = -4001;
|
|
23
|
-
|
|
24
|
-
/** Native Node transport backed by trevrpc-c and MsQuic. */
|
|
25
|
-
export class RawNodeTransport {
|
|
26
|
-
constructor(nativeClient, options = {}) {
|
|
27
|
-
this.nativeClient = nativeClient;
|
|
28
|
-
this.maxFrameSize = options.maxFrameSize;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** Opens a TrevRPC client backed by the native C runtime. */
|
|
32
|
-
static async connect(urlOrOptions, options = {}) {
|
|
33
|
-
const connectOptions = normalizeNodeTransportOptions(urlOrOptions, options);
|
|
34
|
-
throwIfAborted(connectOptions.signal);
|
|
35
|
-
const native = loadNative();
|
|
36
|
-
const cancellation = connectOptions.signal == null ? undefined : native.createCancellation();
|
|
37
|
-
const cleanupAbort = onAbort(connectOptions.signal, () => cancellation?.cancel());
|
|
38
|
-
let nativeClient;
|
|
39
|
-
try {
|
|
40
|
-
nativeClient = await nativeConnect(native, connectOptions, cancellation);
|
|
41
|
-
throwIfAborted(connectOptions.signal);
|
|
42
|
-
return new RawNodeTransport(nativeClient, connectOptions);
|
|
43
|
-
} catch (error) {
|
|
44
|
-
if (connectOptions.signal?.aborted) {
|
|
45
|
-
closeNativeObjectQuietly(nativeClient);
|
|
46
|
-
throw signalAbortError(connectOptions.signal);
|
|
47
|
-
}
|
|
48
|
-
throw nativeError(error, "connect");
|
|
49
|
-
} finally {
|
|
50
|
-
cleanupAbort();
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** Sends a unary RPC request and returns its response. */
|
|
55
|
-
async call(request, options = {}) {
|
|
56
|
-
throwIfAborted(options.signal);
|
|
57
|
-
const cancellation =
|
|
58
|
-
options.signal == null ? undefined : createNativeCancellation(this.nativeClient);
|
|
59
|
-
const cleanupAbort = onAbort(options.signal, () => cancellation?.cancel());
|
|
60
|
-
try {
|
|
61
|
-
const response = await nativeCall(
|
|
62
|
-
this.nativeClient,
|
|
63
|
-
nativeRequest(request, RpcKind.Unary),
|
|
64
|
-
cancellation,
|
|
65
|
-
);
|
|
66
|
-
throwIfAborted(options.signal);
|
|
67
|
-
return response;
|
|
68
|
-
} catch (error) {
|
|
69
|
-
if (options.signal?.aborted) {
|
|
70
|
-
throw signalAbortError(options.signal);
|
|
71
|
-
}
|
|
72
|
-
throw nativeError(error, "unary call");
|
|
73
|
-
} finally {
|
|
74
|
-
cleanupAbort();
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/** Starts a streaming RPC and returns native response frames. */
|
|
79
|
-
async streamingCall(request, requestBody, options = {}) {
|
|
80
|
-
throwIfAborted(options.signal);
|
|
81
|
-
const cancellation =
|
|
82
|
-
options.signal == null ? undefined : createNativeCancellation(this.nativeClient);
|
|
83
|
-
const cleanupStartAbort = onAbort(options.signal, () => cancellation?.cancel());
|
|
84
|
-
let stream;
|
|
85
|
-
try {
|
|
86
|
-
stream = await nativeStartStream(
|
|
87
|
-
this.nativeClient,
|
|
88
|
-
nativeRequest(request, RpcKind.ServerStreaming),
|
|
89
|
-
cancellation,
|
|
90
|
-
);
|
|
91
|
-
cleanupStartAbort();
|
|
92
|
-
throwIfAborted(options.signal);
|
|
93
|
-
} catch (error) {
|
|
94
|
-
cleanupStartAbort();
|
|
95
|
-
closeNativeObjectQuietly(stream);
|
|
96
|
-
if (options.signal?.aborted) {
|
|
97
|
-
throw signalAbortError(options.signal);
|
|
98
|
-
}
|
|
99
|
-
throw nativeError(error, "start streaming call");
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const closeStream = once(() => closeNativeObject(stream, "close response stream"));
|
|
103
|
-
const writerSettlement = new RequestWriterSettlement(
|
|
104
|
-
(error) => error,
|
|
105
|
-
isNativeCleanupWriterError,
|
|
106
|
-
isNativeCleanupWriterError,
|
|
107
|
-
true,
|
|
108
|
-
);
|
|
109
|
-
writerSettlement.track(
|
|
110
|
-
writeRequestStream(stream, requestBody, closeStream, (error) =>
|
|
111
|
-
writerSettlement.recordError(error),
|
|
112
|
-
),
|
|
113
|
-
);
|
|
114
|
-
return new NativeResponseFrameStream(stream, writerSettlement, closeStream, options.signal);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/** Closes the underlying native client. */
|
|
118
|
-
close() {
|
|
119
|
-
try {
|
|
120
|
-
this.nativeClient.close();
|
|
121
|
-
} catch (error) {
|
|
122
|
-
throw nativeError(error, "close client");
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/** Resolves when the underlying native connection closes. */
|
|
127
|
-
get closed() {
|
|
128
|
-
return this.nativeClient.closed;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/** Native Node channel with background connection reconnection. */
|
|
133
|
-
export class Channel extends ChannelStateMachine {
|
|
134
|
-
constructor(target, options = {}) {
|
|
135
|
-
const normalized = normalizeNodeChannelOptions(target, options);
|
|
136
|
-
const transportOptions = stripChannelOptions(normalized.options);
|
|
137
|
-
super(
|
|
138
|
-
(signal) => RawNodeTransport.connect(normalized.endpoint, { ...transportOptions, signal }),
|
|
139
|
-
normalized.options,
|
|
140
|
-
(transport) => transport.closed,
|
|
141
|
-
(error) => error?.nativeCode === NativeTransportClosed,
|
|
142
|
-
);
|
|
143
|
-
this.endpoint = normalized.endpoint;
|
|
144
|
-
this.options = normalized.options;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/** Creates a native channel and waits for its first ready generation. */
|
|
148
|
-
static async connect(target, options = {}) {
|
|
149
|
-
const channel = new Channel(target, options);
|
|
150
|
-
await waitForInitialReady(channel, channel.options);
|
|
151
|
-
return channel;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/** Native Node server backed by trevrpc-c and MsQuic. */
|
|
156
|
-
export class NodeServer {
|
|
157
|
-
constructor(nativeServer, options = {}) {
|
|
158
|
-
this.nativeServer = nativeServer;
|
|
159
|
-
this.port = nativeServer.port;
|
|
160
|
-
this.maxFrameSize = options.maxFrameSize;
|
|
161
|
-
this.closed = null;
|
|
162
|
-
this.authorizer = options.authorizer ?? null;
|
|
163
|
-
this.metrics = options.metrics ?? null;
|
|
164
|
-
this.logger = options.logger ?? null;
|
|
165
|
-
this.activeCalls = new Set();
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/** Creates a native QUIC TrevRPC server backed by trevrpc-c. */
|
|
169
|
-
static async listen(urlOrOptions, options = {}) {
|
|
170
|
-
try {
|
|
171
|
-
const native = loadNative();
|
|
172
|
-
const listenOptions = normalizeNodeListenOptions(urlOrOptions, options);
|
|
173
|
-
const nativeServer = await native.listenMsQuic(listenOptions);
|
|
174
|
-
return new NodeServer(nativeServer, listenOptions);
|
|
175
|
-
} catch (error) {
|
|
176
|
-
throw nativeError(error, "listen");
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/** Registers one raw RPC handler. */
|
|
181
|
-
register(service, method, kind, handler) {
|
|
182
|
-
if (typeof handler !== "function") {
|
|
183
|
-
throw new TypeError("register requires a handler function");
|
|
184
|
-
}
|
|
185
|
-
try {
|
|
186
|
-
this.nativeServer.register(service, method, rpcKindNumber(kind), (nativeCall) => {
|
|
187
|
-
void this.#dispatch(handler, nativeCall);
|
|
188
|
-
});
|
|
189
|
-
return this;
|
|
190
|
-
} catch (error) {
|
|
191
|
-
throw nativeError(error, "register server handler");
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/** Registers handlers for a generated service descriptor. */
|
|
196
|
-
registerService(service, handlers) {
|
|
197
|
-
for (const [jsName, method] of Object.entries(service.methods)) {
|
|
198
|
-
const handler = handlers[jsName] ?? handlers[method.name];
|
|
199
|
-
if (handler != null) {
|
|
200
|
-
this.register(service.fullName, method.name, method.kind, handler);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
return this;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/** Sets an async authorizer that runs before registered handlers. */
|
|
207
|
-
setAuthorizer(authorizer) {
|
|
208
|
-
if (authorizer != null && typeof authorizer !== "function") {
|
|
209
|
-
throw new TypeError("authorizer must be a function");
|
|
210
|
-
}
|
|
211
|
-
this.authorizer = authorizer ?? null;
|
|
212
|
-
return this;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/** Clears the server authorizer. */
|
|
216
|
-
clearAuthorizer() {
|
|
217
|
-
this.authorizer = null;
|
|
218
|
-
return this;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/** Sets metrics callbacks. */
|
|
222
|
-
setMetrics(metrics) {
|
|
223
|
-
if (metrics != null && typeof metrics !== "object") {
|
|
224
|
-
throw new TypeError("metrics must be an object");
|
|
225
|
-
}
|
|
226
|
-
this.metrics = metrics ?? null;
|
|
227
|
-
return this;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/** Clears metrics callbacks. */
|
|
231
|
-
clearMetrics() {
|
|
232
|
-
this.metrics = null;
|
|
233
|
-
return this;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/** Sets a logger function or logger object. */
|
|
237
|
-
setLogger(logger) {
|
|
238
|
-
if (logger != null && typeof logger !== "function" && typeof logger !== "object") {
|
|
239
|
-
throw new TypeError("logger must be a function or object");
|
|
240
|
-
}
|
|
241
|
-
this.logger = logger ?? null;
|
|
242
|
-
return this;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/** Clears the logger. */
|
|
246
|
-
clearLogger() {
|
|
247
|
-
this.logger = null;
|
|
248
|
-
return this;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/** Starts accepting RPCs. The returned promise resolves after close. */
|
|
252
|
-
serve() {
|
|
253
|
-
this.closed ??= Promise.resolve()
|
|
254
|
-
.then(() => this.nativeServer.serve())
|
|
255
|
-
.catch((error) => {
|
|
256
|
-
throw nativeError(error, "serve");
|
|
257
|
-
});
|
|
258
|
-
return this.closed;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/** Requests graceful server shutdown and drains already admitted calls. */
|
|
262
|
-
close() {
|
|
263
|
-
try {
|
|
264
|
-
this.nativeServer.close();
|
|
265
|
-
} catch (error) {
|
|
266
|
-
throw nativeError(error, "close server");
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
async #dispatch(handler, nativeCall) {
|
|
271
|
-
const call = new NodeServerCall(nativeCall, (completedCall) => {
|
|
272
|
-
this.activeCalls.delete(completedCall);
|
|
273
|
-
this.#recordFinished(completedCall);
|
|
274
|
-
});
|
|
275
|
-
this.activeCalls.add(call);
|
|
276
|
-
this.#recordStarted(call);
|
|
277
|
-
try {
|
|
278
|
-
const authorization = await this.#authorize(call);
|
|
279
|
-
if (authorization != null) {
|
|
280
|
-
this.#log(
|
|
281
|
-
"warn",
|
|
282
|
-
"rpc.authorization_denied",
|
|
283
|
-
authorization.message,
|
|
284
|
-
call,
|
|
285
|
-
authorization.code,
|
|
286
|
-
);
|
|
287
|
-
await completeWithStatus(call, authorization);
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
const result = await handler(call);
|
|
291
|
-
if (!call.completed && !call.deferred) {
|
|
292
|
-
await completeDefault(call, result);
|
|
293
|
-
}
|
|
294
|
-
} catch (error) {
|
|
295
|
-
this.#log(
|
|
296
|
-
"error",
|
|
297
|
-
"rpc.handler_failed",
|
|
298
|
-
error?.message ?? "handler failed",
|
|
299
|
-
call,
|
|
300
|
-
error?.code,
|
|
301
|
-
);
|
|
302
|
-
if (!call.completed) {
|
|
303
|
-
await completeWithError(call, error);
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
async #authorize(call) {
|
|
309
|
-
if (this.authorizer == null) {
|
|
310
|
-
return null;
|
|
311
|
-
}
|
|
312
|
-
try {
|
|
313
|
-
return authorizationStatus(await this.authorizer(call));
|
|
314
|
-
} catch (error) {
|
|
315
|
-
this.#log(
|
|
316
|
-
"error",
|
|
317
|
-
"rpc.authorization_failed",
|
|
318
|
-
error?.message ?? "authorizer failed",
|
|
319
|
-
call,
|
|
320
|
-
error?.code,
|
|
321
|
-
);
|
|
322
|
-
return {
|
|
323
|
-
code: Code.Internal,
|
|
324
|
-
message: error?.statusMessage ?? error?.message ?? "authorizer failed",
|
|
325
|
-
metadata: error?.metadata ?? {},
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
#recordStarted(call) {
|
|
331
|
-
const event = rpcStartedEvent(call);
|
|
332
|
-
try {
|
|
333
|
-
this.metrics?.rpcStarted?.(event);
|
|
334
|
-
this.metrics?.started?.(event);
|
|
335
|
-
} catch (error) {
|
|
336
|
-
this.#log(
|
|
337
|
-
"error",
|
|
338
|
-
"metrics.rpc_started_failed",
|
|
339
|
-
error?.message ?? "metrics callback failed",
|
|
340
|
-
call,
|
|
341
|
-
);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
#recordFinished(call) {
|
|
346
|
-
const event = rpcFinishedEvent(call);
|
|
347
|
-
try {
|
|
348
|
-
this.metrics?.rpcFinished?.(event);
|
|
349
|
-
this.metrics?.finished?.(event);
|
|
350
|
-
} catch (error) {
|
|
351
|
-
this.#log(
|
|
352
|
-
"error",
|
|
353
|
-
"metrics.rpc_finished_failed",
|
|
354
|
-
error?.message ?? "metrics callback failed",
|
|
355
|
-
call,
|
|
356
|
-
);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
#log(level, event, message, call, status = undefined) {
|
|
361
|
-
const entry = logEvent(level, event, message, call, status);
|
|
362
|
-
try {
|
|
363
|
-
if (typeof this.logger === "function") {
|
|
364
|
-
this.logger(entry);
|
|
365
|
-
} else {
|
|
366
|
-
this.logger?.log?.(entry);
|
|
367
|
-
this.logger?.[level]?.(entry);
|
|
368
|
-
}
|
|
369
|
-
} catch {
|
|
370
|
-
// Logger failures must not affect RPC handling.
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/** Raw server call passed to NodeServer handlers. */
|
|
376
|
-
export class NodeServerCall {
|
|
377
|
-
constructor(nativeCall, onComplete = () => {}) {
|
|
378
|
-
this.nativeCall = nativeCall;
|
|
379
|
-
this.request = nativeCall.request;
|
|
380
|
-
this.completed = false;
|
|
381
|
-
this.deferred = false;
|
|
382
|
-
this.recvDone = false;
|
|
383
|
-
this.recvQueue = [];
|
|
384
|
-
this.responseBodyLength = 0;
|
|
385
|
-
this.finalStatus = Code.Ok;
|
|
386
|
-
this.startedAt = Date.now();
|
|
387
|
-
this.completedAt = null;
|
|
388
|
-
this.readBatchMaxMessages = RecvManyBatchSize;
|
|
389
|
-
this.writeBatchMaxMessages = SendManyBatchSize;
|
|
390
|
-
this.#onComplete = onComplete;
|
|
391
|
-
this.#abortController = new AbortController();
|
|
392
|
-
this.signal = this.#abortController.signal;
|
|
393
|
-
|
|
394
|
-
const context = nativeCall.context ?? {};
|
|
395
|
-
const remainingNanos = context.hasDeadline ? bigintValue(context.timeRemainingNanos) : null;
|
|
396
|
-
const remainingMs = remainingNanos == null ? null : Number(remainingNanos) / 1_000_000;
|
|
397
|
-
this.deadline = remainingMs == null ? null : new Date(this.startedAt + remainingMs);
|
|
398
|
-
if (context.cancelled) {
|
|
399
|
-
queueMicrotask(() => this.#cancel(cancelled("RPC cancelled by peer")));
|
|
400
|
-
} else if (remainingMs != null) {
|
|
401
|
-
const deadlineError = new TrevRpcError(Code.DeadlineExceeded, "RPC deadline exceeded");
|
|
402
|
-
this.#cancelDeadline = scheduleTimeout(
|
|
403
|
-
() => this.#completeDeadline(deadlineError),
|
|
404
|
-
Math.max(0, remainingMs),
|
|
405
|
-
);
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
#abortController;
|
|
410
|
-
#cancelDeadline = () => {};
|
|
411
|
-
#onComplete;
|
|
412
|
-
#terminalError = null;
|
|
413
|
-
#terminalSettled = false;
|
|
414
|
-
|
|
415
|
-
/** Keeps the call open after the handler returns. */
|
|
416
|
-
defer() {
|
|
417
|
-
this.#throwIfTerminal();
|
|
418
|
-
this.deferred = true;
|
|
419
|
-
return this;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
/** Sends a unary response and completes the call. */
|
|
423
|
-
async respond(response = {}) {
|
|
424
|
-
this.#throwIfTerminal();
|
|
425
|
-
const rpcResponse = responseObject(response);
|
|
426
|
-
const status = codeFromNumber(rpcResponse.status ?? rpcResponse.code ?? Code.Ok);
|
|
427
|
-
this.responseBodyLength = rpcResponse.body?.byteLength ?? 0;
|
|
428
|
-
this.#claimTerminal(status);
|
|
429
|
-
try {
|
|
430
|
-
await this.nativeCall.respond(rpcResponse);
|
|
431
|
-
if (!this.#settleTerminalSuccess()) {
|
|
432
|
-
throw this.#terminalError;
|
|
433
|
-
}
|
|
434
|
-
} catch (error) {
|
|
435
|
-
throw this.#settleTerminalFailure(error, "send unary response");
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
/** Sends one streaming response message. */
|
|
440
|
-
async sendMessage(body) {
|
|
441
|
-
this.#throwIfTerminal();
|
|
442
|
-
const bytes = byteBody(body);
|
|
443
|
-
try {
|
|
444
|
-
await this.nativeCall.sendMessage(bytes);
|
|
445
|
-
this.#throwIfTerminal();
|
|
446
|
-
this.responseBodyLength += bytes.byteLength;
|
|
447
|
-
} catch (error) {
|
|
448
|
-
if (this.#terminalError != null) {
|
|
449
|
-
throw this.#terminalError;
|
|
450
|
-
}
|
|
451
|
-
throw this.#observeNativeIoError(error, "send streaming response");
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
/** Sends multiple streaming response messages. */
|
|
456
|
-
async sendMany(bodies) {
|
|
457
|
-
this.#throwIfTerminal();
|
|
458
|
-
const batch = Array.from(bodies, byteBody);
|
|
459
|
-
if (batch.length === 0) {
|
|
460
|
-
return;
|
|
461
|
-
}
|
|
462
|
-
if (batch.length === 1 || typeof this.nativeCall.sendMessages !== "function") {
|
|
463
|
-
await sendManyIndividually(this, batch);
|
|
464
|
-
return;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
let bodyLength = 0;
|
|
468
|
-
for (const body of batch) {
|
|
469
|
-
bodyLength += body.byteLength;
|
|
470
|
-
}
|
|
471
|
-
try {
|
|
472
|
-
await this.nativeCall.sendMessages(batch);
|
|
473
|
-
this.#throwIfTerminal();
|
|
474
|
-
this.responseBodyLength += bodyLength;
|
|
475
|
-
} catch (error) {
|
|
476
|
-
if (this.#terminalError != null) {
|
|
477
|
-
throw this.#terminalError;
|
|
478
|
-
}
|
|
479
|
-
throw this.#observeNativeIoError(error, "send streaming response batch");
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
/** Receives one streaming request frame, or null after EOF. */
|
|
484
|
-
async recv() {
|
|
485
|
-
this.#throwIfTerminal();
|
|
486
|
-
if (this.recvDone) {
|
|
487
|
-
return null;
|
|
488
|
-
}
|
|
489
|
-
while (this.recvQueue.length === 0) {
|
|
490
|
-
const frames = await this.#recvMany();
|
|
491
|
-
this.#throwIfTerminal();
|
|
492
|
-
this.recvQueue.push(...frames);
|
|
493
|
-
}
|
|
494
|
-
const frame = this.recvQueue.shift();
|
|
495
|
-
if (frame == null) {
|
|
496
|
-
this.recvDone = true;
|
|
497
|
-
this.recvQueue.length = 0;
|
|
498
|
-
return null;
|
|
499
|
-
}
|
|
500
|
-
return frame;
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
/** Sends the terminal streaming status and completes the call. */
|
|
504
|
-
async finishStream(status = Code.Ok, message = "", metadata = {}) {
|
|
505
|
-
this.#throwIfTerminal();
|
|
506
|
-
const normalizedStatus = codeFromNumber(status);
|
|
507
|
-
this.#claimTerminal(normalizedStatus);
|
|
508
|
-
try {
|
|
509
|
-
await this.nativeCall.finishStream(normalizedStatus, message, metadata);
|
|
510
|
-
if (!this.#settleTerminalSuccess()) {
|
|
511
|
-
throw this.#terminalError;
|
|
512
|
-
}
|
|
513
|
-
} catch (error) {
|
|
514
|
-
throw this.#settleTerminalFailure(error, "finish response stream");
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
/** Cancels and closes the call. */
|
|
519
|
-
close(reason = cancelled("RPC cancelled")) {
|
|
520
|
-
if (this.#terminalSettled) {
|
|
521
|
-
return;
|
|
522
|
-
}
|
|
523
|
-
const error = cancellationError(reason);
|
|
524
|
-
if (!this.completed) {
|
|
525
|
-
this.#claimTerminal(error.code, error);
|
|
526
|
-
}
|
|
527
|
-
this.#settleLocalTerminal(error);
|
|
528
|
-
closeNativeObject(this.nativeCall, "close server call");
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
#cancel(error) {
|
|
532
|
-
if (this.#terminalSettled) {
|
|
533
|
-
return;
|
|
534
|
-
}
|
|
535
|
-
if (!this.completed) {
|
|
536
|
-
this.#claimTerminal(error.code, error);
|
|
537
|
-
}
|
|
538
|
-
this.#settleLocalTerminal(error);
|
|
539
|
-
closeNativeObjectQuietly(this.nativeCall);
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
#completeDeadline(error) {
|
|
543
|
-
if (this.#terminalSettled) {
|
|
544
|
-
return;
|
|
545
|
-
}
|
|
546
|
-
const sendStatus = !this.completed;
|
|
547
|
-
if (sendStatus) {
|
|
548
|
-
this.#claimTerminal(error.code, error);
|
|
549
|
-
}
|
|
550
|
-
this.#settleLocalTerminal(error);
|
|
551
|
-
if (sendStatus) {
|
|
552
|
-
void this.#sendAutomaticDeadline(error);
|
|
553
|
-
} else {
|
|
554
|
-
closeNativeObjectQuietly(this.nativeCall);
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
async #sendAutomaticDeadline(error) {
|
|
559
|
-
try {
|
|
560
|
-
if (this.request.kind === RpcKind.Unary) {
|
|
561
|
-
await this.nativeCall.respond({
|
|
562
|
-
status: error.code,
|
|
563
|
-
message: error.statusMessage,
|
|
564
|
-
metadata: error.metadata,
|
|
565
|
-
});
|
|
566
|
-
} else {
|
|
567
|
-
await this.nativeCall.finishStream(error.code, error.statusMessage, error.metadata);
|
|
568
|
-
}
|
|
569
|
-
} catch {
|
|
570
|
-
// The deadline remains the primary terminal reason.
|
|
571
|
-
} finally {
|
|
572
|
-
closeNativeObjectQuietly(this.nativeCall);
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
#claimTerminal(status, error = null) {
|
|
577
|
-
if (this.completed) {
|
|
578
|
-
return false;
|
|
579
|
-
}
|
|
580
|
-
this.completed = true;
|
|
581
|
-
this.finalStatus = codeFromNumber(status);
|
|
582
|
-
this.#terminalError = error;
|
|
583
|
-
return true;
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
#settleTerminalSuccess() {
|
|
587
|
-
if (this.#terminalSettled) {
|
|
588
|
-
return false;
|
|
589
|
-
}
|
|
590
|
-
this.#terminalSettled = true;
|
|
591
|
-
this.#clearDeadline();
|
|
592
|
-
this.#completeMetrics();
|
|
593
|
-
return true;
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
#settleTerminalFailure(error, operation) {
|
|
597
|
-
if (this.#terminalSettled) {
|
|
598
|
-
return this.#terminalError ?? nativeError(error, operation);
|
|
599
|
-
}
|
|
600
|
-
const normalized = nativeError(error, operation);
|
|
601
|
-
this.#terminalSettled = true;
|
|
602
|
-
this.#clearDeadline();
|
|
603
|
-
this.finalStatus = normalized.code;
|
|
604
|
-
this.#terminalError = normalized;
|
|
605
|
-
this.#abortController.abort(normalized);
|
|
606
|
-
this.recvDone = true;
|
|
607
|
-
this.recvQueue.length = 0;
|
|
608
|
-
closeNativeObjectQuietly(this.nativeCall);
|
|
609
|
-
this.#completeMetrics();
|
|
610
|
-
return normalized;
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
#settleLocalTerminal(error) {
|
|
614
|
-
this.#terminalSettled = true;
|
|
615
|
-
this.#clearDeadline();
|
|
616
|
-
this.finalStatus = error.code;
|
|
617
|
-
this.#terminalError = error;
|
|
618
|
-
this.#abortController.abort(error);
|
|
619
|
-
this.recvDone = true;
|
|
620
|
-
this.recvQueue.length = 0;
|
|
621
|
-
this.#completeMetrics();
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
#clearDeadline() {
|
|
625
|
-
this.#cancelDeadline();
|
|
626
|
-
this.#cancelDeadline = () => {};
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
#throwIfTerminal() {
|
|
630
|
-
if (this.#terminalError != null) {
|
|
631
|
-
throw this.#terminalError;
|
|
632
|
-
}
|
|
633
|
-
if (this.completed) {
|
|
634
|
-
throw new TrevRpcError(Code.FailedPrecondition, "server call already completed");
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
#completeMetrics() {
|
|
639
|
-
if (this.completedAt != null) {
|
|
640
|
-
return;
|
|
641
|
-
}
|
|
642
|
-
this.completedAt = Date.now();
|
|
643
|
-
this.#onComplete(this);
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
async #recvMany() {
|
|
647
|
-
try {
|
|
648
|
-
if (typeof this.nativeCall.recvMany === "function") {
|
|
649
|
-
return await this.nativeCall.recvMany(this.readBatchMaxMessages);
|
|
650
|
-
}
|
|
651
|
-
return [await this.nativeCall.recv()];
|
|
652
|
-
} catch (error) {
|
|
653
|
-
if (this.#terminalError != null) {
|
|
654
|
-
throw this.#terminalError;
|
|
655
|
-
}
|
|
656
|
-
throw this.#observeNativeIoError(error, "receive request stream");
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
#observeNativeIoError(error, operation) {
|
|
661
|
-
const normalized = nativeError(error, operation);
|
|
662
|
-
if (
|
|
663
|
-
!this.completed &&
|
|
664
|
-
(normalized.code === Code.Cancelled || normalized.code === Code.DeadlineExceeded)
|
|
665
|
-
) {
|
|
666
|
-
this.#cancel(normalized);
|
|
667
|
-
}
|
|
668
|
-
return this.#terminalError ?? normalized;
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
/** Creates an authorizer requiring one metadata key/value pair. */
|
|
673
|
-
export function metadataValueAuthorizer(key, value) {
|
|
674
|
-
const normalizedKey = String(key).toLowerCase();
|
|
675
|
-
const expected = metadataBytes(value);
|
|
676
|
-
return (call) => {
|
|
677
|
-
const actual = metadataValue(call.request.metadata, normalizedKey);
|
|
678
|
-
if (actual != null && bytesEqual(actual, expected)) {
|
|
679
|
-
return true;
|
|
680
|
-
}
|
|
681
|
-
return { code: Code.Unauthenticated, message: "request is not authenticated" };
|
|
682
|
-
};
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
/** Creates an authorizer requiring an Authorization: Bearer token metadata value. */
|
|
686
|
-
export function bearerAuthorizer(token) {
|
|
687
|
-
const expected = new TextEncoder().encode(`Bearer ${token}`);
|
|
688
|
-
return metadataValueAuthorizer("authorization", expected);
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
class NativeResponseFrameStream {
|
|
692
|
-
constructor(stream, writerSettlement, closeStream, signal = undefined) {
|
|
693
|
-
this.stream = stream;
|
|
694
|
-
this.done = false;
|
|
695
|
-
this.closeStream = closeStream;
|
|
696
|
-
this.signal = signal;
|
|
697
|
-
this.cleanupAbort = () => {};
|
|
698
|
-
this.cleanupDone = null;
|
|
699
|
-
this.recvQueue = [];
|
|
700
|
-
this.recvTask = null;
|
|
701
|
-
this.pendingBodyError = null;
|
|
702
|
-
this.pendingBodyEof = false;
|
|
703
|
-
this.readBatchMaxMessages = RecvManyBatchSize;
|
|
704
|
-
this.writerSettlement = writerSettlement;
|
|
705
|
-
this.cleanupAbort = onAbort(signal, () => this.startCleanup());
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
[Symbol.asyncIterator]() {
|
|
709
|
-
return this;
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
async next() {
|
|
713
|
-
if (this.done) {
|
|
714
|
-
return await this.#finishDone();
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
await this.#fillRecvQueue();
|
|
718
|
-
const frame = this.recvQueue.shift();
|
|
719
|
-
if (frame == null) {
|
|
720
|
-
return await this.#finishEof();
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
if (frame.kind === RpcStreamFrameKind.Status) {
|
|
724
|
-
await this.#finishStatus(frame);
|
|
725
|
-
}
|
|
726
|
-
return { done: false, value: frame };
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
async nextBatch() {
|
|
730
|
-
if (this.done) {
|
|
731
|
-
return await this.#finishDone();
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
await this.#fillRecvQueue();
|
|
735
|
-
const terminalIndex = this.recvQueue.findIndex(isTerminalFrame);
|
|
736
|
-
if (terminalIndex === 0) {
|
|
737
|
-
const frame = this.recvQueue.shift();
|
|
738
|
-
if (frame == null) {
|
|
739
|
-
return await this.#finishEof();
|
|
740
|
-
}
|
|
741
|
-
await this.#finishStatus(frame);
|
|
742
|
-
return { done: false, value: [frame] };
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
const count = terminalIndex < 0 ? this.recvQueue.length : terminalIndex;
|
|
746
|
-
const frames = this.recvQueue.splice(0, count);
|
|
747
|
-
return { done: false, value: frames };
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
async nextBodyBatch(max = this.readBatchMaxMessages) {
|
|
751
|
-
if (this.done) {
|
|
752
|
-
return await this.#finishDone();
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
if (typeof this.stream.recvBodyBatch !== "function") {
|
|
756
|
-
return await this.#nextBodyBatchFromFrames(max);
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
try {
|
|
760
|
-
if (this.pendingBodyError != null) {
|
|
761
|
-
const error = this.pendingBodyError;
|
|
762
|
-
this.pendingBodyError = null;
|
|
763
|
-
throw error;
|
|
764
|
-
}
|
|
765
|
-
if (this.pendingBodyEof) {
|
|
766
|
-
this.pendingBodyEof = false;
|
|
767
|
-
return await this.#finishEof();
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
const batch = await this.stream.recvBodyBatch(max);
|
|
771
|
-
if (batch == null) {
|
|
772
|
-
return await this.#finishEof();
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
const bodies = batch.bodies ?? [];
|
|
776
|
-
if (batch.unknownFrameKind != null) {
|
|
777
|
-
const error = invalidArgument("response stream contained an unknown frame kind");
|
|
778
|
-
if (bodies.length > 0) {
|
|
779
|
-
this.pendingBodyError = error;
|
|
780
|
-
return { done: false, value: { bodies, status: null } };
|
|
781
|
-
}
|
|
782
|
-
throw error;
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
const status = batch.status ?? null;
|
|
786
|
-
if (batch.eof && bodies.length === 0) {
|
|
787
|
-
return await this.#finishEof();
|
|
788
|
-
}
|
|
789
|
-
if (batch.eof && bodies.length > 0) {
|
|
790
|
-
this.pendingBodyEof = true;
|
|
791
|
-
return { done: false, value: { bodies, status: null } };
|
|
792
|
-
}
|
|
793
|
-
if (status != null) {
|
|
794
|
-
await this.#finishStatus(status);
|
|
795
|
-
}
|
|
796
|
-
return { done: false, value: { bodies, status } };
|
|
797
|
-
} catch (error) {
|
|
798
|
-
this.startCleanup();
|
|
799
|
-
if (this.signal?.aborted) {
|
|
800
|
-
throw signalAbortError(this.signal);
|
|
801
|
-
}
|
|
802
|
-
throw nativeError(error, "receive response stream");
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
async return() {
|
|
807
|
-
this.startCleanup();
|
|
808
|
-
return await this.#finishDone();
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
startCleanup() {
|
|
812
|
-
this.writerSettlement.beginCleanup();
|
|
813
|
-
if (this.cleanupDone != null) {
|
|
814
|
-
return;
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
this.done = true;
|
|
818
|
-
this.cleanupAbort();
|
|
819
|
-
this.recvQueue.length = 0;
|
|
820
|
-
this.pendingBodyError = null;
|
|
821
|
-
this.pendingBodyEof = false;
|
|
822
|
-
const recvTask = this.recvTask;
|
|
823
|
-
let closeError;
|
|
824
|
-
try {
|
|
825
|
-
this.closeStream();
|
|
826
|
-
} catch (error) {
|
|
827
|
-
closeError = error;
|
|
828
|
-
}
|
|
829
|
-
this.cleanupDone = (async () => {
|
|
830
|
-
await recvTask;
|
|
831
|
-
if (closeError != null) {
|
|
832
|
-
throw closeError;
|
|
833
|
-
}
|
|
834
|
-
})();
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
#startRecv() {
|
|
838
|
-
const recv =
|
|
839
|
-
typeof this.stream.recvMany === "function"
|
|
840
|
-
? this.stream.recvMany(this.readBatchMaxMessages)
|
|
841
|
-
: this.stream.recv().then((frame) => [frame]);
|
|
842
|
-
return recv.then(
|
|
843
|
-
(frames) => ({ frames }),
|
|
844
|
-
(error) => ({ error }),
|
|
845
|
-
);
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
async #fillRecvQueue() {
|
|
849
|
-
while (this.recvQueue.length === 0) {
|
|
850
|
-
this.recvTask ??= this.#startRecv();
|
|
851
|
-
const result = await this.recvTask;
|
|
852
|
-
this.recvTask = null;
|
|
853
|
-
if (result.error != null) {
|
|
854
|
-
this.startCleanup();
|
|
855
|
-
if (this.signal?.aborted) {
|
|
856
|
-
throw signalAbortError(this.signal);
|
|
857
|
-
}
|
|
858
|
-
throw nativeError(result.error, "receive response stream");
|
|
859
|
-
}
|
|
860
|
-
this.recvQueue.push(...result.frames);
|
|
861
|
-
if (this.recvQueue.length > 0 && !hasTerminalFrame(this.recvQueue)) {
|
|
862
|
-
this.recvTask = this.#startRecv();
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
async #nextBodyBatchFromFrames(max) {
|
|
868
|
-
try {
|
|
869
|
-
await this.#fillRecvQueue();
|
|
870
|
-
const frame = this.recvQueue[0];
|
|
871
|
-
if (frame == null) {
|
|
872
|
-
this.recvQueue.shift();
|
|
873
|
-
return await this.#finishEof();
|
|
874
|
-
}
|
|
875
|
-
if (frame.kind === RpcStreamFrameKind.Status) {
|
|
876
|
-
this.recvQueue.shift();
|
|
877
|
-
await this.#finishStatus(frame);
|
|
878
|
-
return { done: false, value: { bodies: [], status: frame } };
|
|
879
|
-
}
|
|
880
|
-
if (frame.kind !== RpcStreamFrameKind.Message) {
|
|
881
|
-
throw invalidArgument("response stream contained an unknown frame kind");
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
const limit = Math.max(1, Math.floor(max));
|
|
885
|
-
const bodies = [];
|
|
886
|
-
while (bodies.length < limit && this.recvQueue.length > 0) {
|
|
887
|
-
const queued = this.recvQueue[0];
|
|
888
|
-
if (isTerminalFrame(queued) || queued.kind !== RpcStreamFrameKind.Message) {
|
|
889
|
-
break;
|
|
890
|
-
}
|
|
891
|
-
this.recvQueue.shift();
|
|
892
|
-
bodies.push(queued.body ?? EmptyBody);
|
|
893
|
-
}
|
|
894
|
-
const status = this.recvQueue[0];
|
|
895
|
-
if (status?.kind === RpcStreamFrameKind.Status) {
|
|
896
|
-
this.recvQueue.shift();
|
|
897
|
-
await this.#finishStatus(status);
|
|
898
|
-
return { done: false, value: { bodies, status } };
|
|
899
|
-
}
|
|
900
|
-
return { done: false, value: { bodies, status: null } };
|
|
901
|
-
} catch (error) {
|
|
902
|
-
this.startCleanup();
|
|
903
|
-
if (this.signal?.aborted) {
|
|
904
|
-
throw signalAbortError(this.signal);
|
|
905
|
-
}
|
|
906
|
-
throw nativeError(error, "receive response stream");
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
async #finishEof() {
|
|
911
|
-
this.startCleanup();
|
|
912
|
-
return await this.#finishDone();
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
async #finishDone() {
|
|
916
|
-
await this.cleanupDone;
|
|
917
|
-
await this.writerSettlement.finish();
|
|
918
|
-
return { done: true, value: undefined };
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
#finishStatus(frame) {
|
|
922
|
-
this.writerSettlement.recordTerminal(frame.status ?? Code.Ok);
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
function hasTerminalFrame(frames) {
|
|
927
|
-
return frames.some(isTerminalFrame);
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
function isTerminalFrame(frame) {
|
|
931
|
-
return frame == null || frame.kind === RpcStreamFrameKind.Status;
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
function isNativeCleanupWriterError(error) {
|
|
935
|
-
return (
|
|
936
|
-
error?.code === Code.Cancelled ||
|
|
937
|
-
error?.nativeCode === NativeTransportClosed ||
|
|
938
|
-
error?.nativeCode === NativeObjectClosed ||
|
|
939
|
-
nativeErrorName(error?.nativeCode) === "ECANCELED"
|
|
940
|
-
);
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
function nativeErrorName(code) {
|
|
944
|
-
if (!Number.isInteger(code)) {
|
|
945
|
-
return null;
|
|
946
|
-
}
|
|
947
|
-
try {
|
|
948
|
-
return getSystemErrorName(code);
|
|
949
|
-
} catch {
|
|
950
|
-
return null;
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
async function writeRequestStream(stream, requestBody, closeStream, recordError) {
|
|
955
|
-
let iterator;
|
|
956
|
-
try {
|
|
957
|
-
iterator = requestBody[Symbol.asyncIterator]();
|
|
958
|
-
for (;;) {
|
|
959
|
-
const result = await nextRequestBodyBatch(iterator, SendManyBatchSize);
|
|
960
|
-
if (result.done) {
|
|
961
|
-
break;
|
|
962
|
-
}
|
|
963
|
-
const bodies = result.value.map(byteBody);
|
|
964
|
-
if (bodies.length === 1 || typeof stream.sendMessages !== "function") {
|
|
965
|
-
for (const body of bodies) {
|
|
966
|
-
await stream.sendMessage(body);
|
|
967
|
-
}
|
|
968
|
-
} else if (bodies.length > 1) {
|
|
969
|
-
await stream.sendMessages(bodies);
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
await stream.finishSend();
|
|
973
|
-
} catch (error) {
|
|
974
|
-
const normalized = nativeError(error, "send request stream");
|
|
975
|
-
recordError(normalized);
|
|
976
|
-
try {
|
|
977
|
-
await iterator?.return?.();
|
|
978
|
-
} catch {
|
|
979
|
-
// Preserve the upload error; closing the native stream releases resources.
|
|
980
|
-
}
|
|
981
|
-
closeStream();
|
|
982
|
-
throw normalized;
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
async function nextRequestBodyBatch(iterator, maxMessages) {
|
|
987
|
-
if (typeof iterator.nextBatch === "function") {
|
|
988
|
-
return await iterator.nextBatch(maxMessages);
|
|
989
|
-
}
|
|
990
|
-
const result = await iterator.next();
|
|
991
|
-
return result.done ? result : { done: false, value: [result.value] };
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
async function completeDefault(call, result) {
|
|
995
|
-
if (call.request.kind === RpcKind.Unary) {
|
|
996
|
-
await call.respond(result ?? {});
|
|
997
|
-
return;
|
|
998
|
-
}
|
|
999
|
-
|
|
1000
|
-
if (call.request.kind === RpcKind.ClientStreaming) {
|
|
1001
|
-
if (result == null) {
|
|
1002
|
-
await call.finishStream(Code.Ok);
|
|
1003
|
-
return;
|
|
1004
|
-
}
|
|
1005
|
-
const response = responseObject(result);
|
|
1006
|
-
await call.sendMessage(response.body);
|
|
1007
|
-
await call.finishStream(
|
|
1008
|
-
response.status ?? Code.Ok,
|
|
1009
|
-
response.message ?? "",
|
|
1010
|
-
response.metadata ?? {},
|
|
1011
|
-
);
|
|
1012
|
-
return;
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
if (isAsyncIterable(result)) {
|
|
1016
|
-
await sendResponseMessages(call, result);
|
|
1017
|
-
}
|
|
1018
|
-
await call.finishStream(Code.Ok);
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
async function sendResponseMessages(call, messages) {
|
|
1022
|
-
const iterator = messages[Symbol.asyncIterator]();
|
|
1023
|
-
try {
|
|
1024
|
-
for (;;) {
|
|
1025
|
-
const result = await nextResponseBodyBatch(iterator, call.writeBatchMaxMessages);
|
|
1026
|
-
if (result.done) {
|
|
1027
|
-
return;
|
|
1028
|
-
}
|
|
1029
|
-
await call.sendMany(result.value);
|
|
1030
|
-
}
|
|
1031
|
-
} catch (error) {
|
|
1032
|
-
try {
|
|
1033
|
-
await iterator.return?.();
|
|
1034
|
-
} catch {
|
|
1035
|
-
// Preserve the send or iterator error, matching for-await cleanup behavior.
|
|
1036
|
-
}
|
|
1037
|
-
throw error;
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
async function nextResponseBodyBatch(iterator, maxMessages) {
|
|
1042
|
-
if (typeof iterator.nextBatch === "function") {
|
|
1043
|
-
return await iterator.nextBatch(maxMessages);
|
|
1044
|
-
}
|
|
1045
|
-
const result = await iterator.next();
|
|
1046
|
-
return result.done ? result : { done: false, value: [result.value] };
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
async function sendManyIndividually(call, bodies) {
|
|
1050
|
-
for (const body of bodies) {
|
|
1051
|
-
await call.sendMessage(body);
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
|
-
async function completeWithError(call, error) {
|
|
1056
|
-
const status = Number.isInteger(error?.code) ? error.code : Code.Internal;
|
|
1057
|
-
const message = error?.statusMessage ?? error?.message ?? "handler failed";
|
|
1058
|
-
const metadata = error?.metadata ?? {};
|
|
1059
|
-
try {
|
|
1060
|
-
if (call.request.kind === RpcKind.Unary) {
|
|
1061
|
-
await call.respond({ status, message, metadata });
|
|
1062
|
-
} else {
|
|
1063
|
-
await call.finishStream(status, message, metadata);
|
|
1064
|
-
}
|
|
1065
|
-
} catch {
|
|
1066
|
-
call.close();
|
|
1067
|
-
}
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
async function completeWithStatus(call, status) {
|
|
1071
|
-
if (call.request.kind === RpcKind.Unary) {
|
|
1072
|
-
await call.respond({ status: status.code, message: status.message, metadata: status.metadata });
|
|
1073
|
-
} else {
|
|
1074
|
-
await call.finishStream(status.code, status.message, status.metadata);
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1077
|
-
|
|
1078
|
-
function authorizationStatus(result) {
|
|
1079
|
-
if (result == null || result === true) {
|
|
1080
|
-
return null;
|
|
1081
|
-
}
|
|
1082
|
-
if (result === false) {
|
|
1083
|
-
return { code: Code.PermissionDenied, message: "request denied", metadata: {} };
|
|
1084
|
-
}
|
|
1085
|
-
if (typeof result === "object") {
|
|
1086
|
-
const code = codeFromNumber(result.code ?? result.status ?? Code.PermissionDenied);
|
|
1087
|
-
if (code === Code.Ok) {
|
|
1088
|
-
return null;
|
|
1089
|
-
}
|
|
1090
|
-
return {
|
|
1091
|
-
code,
|
|
1092
|
-
message: result.message ?? result.statusMessage ?? "request denied",
|
|
1093
|
-
metadata: result.metadata ?? {},
|
|
1094
|
-
};
|
|
1095
|
-
}
|
|
1096
|
-
return null;
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
|
-
function rpcStartedEvent(call) {
|
|
1100
|
-
return {
|
|
1101
|
-
service: call.request.service,
|
|
1102
|
-
method: call.request.method,
|
|
1103
|
-
requestBodyLength: call.request.body?.byteLength ?? 0,
|
|
1104
|
-
kind: call.request.kind,
|
|
1105
|
-
};
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
function rpcFinishedEvent(call) {
|
|
1109
|
-
return {
|
|
1110
|
-
...rpcStartedEvent(call),
|
|
1111
|
-
responseBodyLength: call.responseBodyLength,
|
|
1112
|
-
status: call.finalStatus,
|
|
1113
|
-
elapsedMs:
|
|
1114
|
-
call.completedAt == null
|
|
1115
|
-
? undefined
|
|
1116
|
-
: call.completedAt - (call.startedAt ?? call.completedAt),
|
|
1117
|
-
};
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
function logEvent(level, event, message, call, status) {
|
|
1121
|
-
return {
|
|
1122
|
-
level,
|
|
1123
|
-
event,
|
|
1124
|
-
message,
|
|
1125
|
-
service: call?.request?.service,
|
|
1126
|
-
method: call?.request?.method,
|
|
1127
|
-
status: status == null ? undefined : codeFromNumber(status),
|
|
1128
|
-
};
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
function responseObject(response) {
|
|
1132
|
-
if (response == null) {
|
|
1133
|
-
return {};
|
|
1134
|
-
}
|
|
1135
|
-
if (
|
|
1136
|
-
response instanceof Uint8Array ||
|
|
1137
|
-
ArrayBuffer.isView(response) ||
|
|
1138
|
-
response instanceof ArrayBuffer ||
|
|
1139
|
-
Array.isArray(response)
|
|
1140
|
-
) {
|
|
1141
|
-
return { body: byteBody(response) };
|
|
1142
|
-
}
|
|
1143
|
-
const status = response.status ?? response.code;
|
|
1144
|
-
return {
|
|
1145
|
-
...response,
|
|
1146
|
-
status,
|
|
1147
|
-
body: byteBody(response.body),
|
|
1148
|
-
};
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
|
-
function rpcKindNumber(kind) {
|
|
1152
|
-
if (typeof kind === "number") {
|
|
1153
|
-
return kind;
|
|
1154
|
-
}
|
|
1155
|
-
switch (kind) {
|
|
1156
|
-
case "unary":
|
|
1157
|
-
return RpcKind.Unary;
|
|
1158
|
-
case "clientStreaming":
|
|
1159
|
-
return RpcKind.ClientStreaming;
|
|
1160
|
-
case "serverStreaming":
|
|
1161
|
-
return RpcKind.ServerStreaming;
|
|
1162
|
-
case "bidirectionalStreaming":
|
|
1163
|
-
return RpcKind.BidirectionalStreaming;
|
|
1164
|
-
default:
|
|
1165
|
-
throw new TypeError(`unsupported RPC kind ${JSON.stringify(kind)}`);
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
function isAsyncIterable(value) {
|
|
1170
|
-
return value != null && typeof value[Symbol.asyncIterator] === "function";
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
|
-
function normalizeNodeTransportOptions(target, options) {
|
|
1174
|
-
const normalized =
|
|
1175
|
-
typeof target === "string" || target instanceof URL
|
|
1176
|
-
? endpointFromUrl(target, options)
|
|
1177
|
-
: target != null && typeof target === "object"
|
|
1178
|
-
? { port: 443, ...target, ...options }
|
|
1179
|
-
: null;
|
|
1180
|
-
if (normalized == null) {
|
|
1181
|
-
throw new TypeError("native transport requires a URL or endpoint object");
|
|
1182
|
-
}
|
|
1183
|
-
validateNodeEndpoint(normalized);
|
|
1184
|
-
return normalized;
|
|
1185
|
-
}
|
|
1186
|
-
|
|
1187
|
-
function normalizeNodeChannelOptions(target, options) {
|
|
1188
|
-
const normalized = normalizeNodeTransportOptions(target, options);
|
|
1189
|
-
const endpoint = Object.freeze({ host: normalized.host, port: normalized.port });
|
|
1190
|
-
const effectiveOptions = { ...normalized };
|
|
1191
|
-
delete effectiveOptions.host;
|
|
1192
|
-
delete effectiveOptions.port;
|
|
1193
|
-
return Object.freeze({ endpoint, options: Object.freeze(effectiveOptions) });
|
|
1194
|
-
}
|
|
1195
|
-
|
|
1196
|
-
function endpointFromUrl(target, options) {
|
|
1197
|
-
const url = new URL(target);
|
|
1198
|
-
return {
|
|
1199
|
-
...options,
|
|
1200
|
-
host: url.hostname,
|
|
1201
|
-
port: Number(url.port || 443),
|
|
1202
|
-
};
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
|
-
function validateNodeEndpoint(endpoint) {
|
|
1206
|
-
if (typeof endpoint.host !== "string" || endpoint.host.length === 0) {
|
|
1207
|
-
throw new TypeError("native endpoint host must be a non-empty string");
|
|
1208
|
-
}
|
|
1209
|
-
if (!Number.isInteger(endpoint.port) || endpoint.port < 1 || endpoint.port > 65_535) {
|
|
1210
|
-
throw new TypeError("native endpoint port must be an integer from 1 through 65535");
|
|
1211
|
-
}
|
|
1212
|
-
}
|
|
1213
|
-
|
|
1214
|
-
function normalizeNodeListenOptions(urlOrOptions, options) {
|
|
1215
|
-
if (typeof urlOrOptions === "string" || urlOrOptions instanceof URL) {
|
|
1216
|
-
const url = new URL(urlOrOptions);
|
|
1217
|
-
return {
|
|
1218
|
-
...options,
|
|
1219
|
-
host: url.hostname,
|
|
1220
|
-
port: Number(url.port || 443),
|
|
1221
|
-
path: `${url.pathname || "/"}${url.search}`,
|
|
1222
|
-
origin: options.origin ?? url.origin,
|
|
1223
|
-
};
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
if (urlOrOptions == null || typeof urlOrOptions !== "object") {
|
|
1227
|
-
throw new TypeError("native server requires a URL or options object");
|
|
1228
|
-
}
|
|
1229
|
-
return { ...urlOrOptions, ...options };
|
|
1230
|
-
}
|
|
1231
|
-
|
|
1232
|
-
function createNativeCancellation(nativeClient) {
|
|
1233
|
-
return typeof nativeClient.createCancellation === "function"
|
|
1234
|
-
? nativeClient.createCancellation()
|
|
1235
|
-
: undefined;
|
|
1236
|
-
}
|
|
1237
|
-
|
|
1238
|
-
function nativeCall(nativeClient, request, cancellation) {
|
|
1239
|
-
return cancellation == null
|
|
1240
|
-
? nativeClient.call(request)
|
|
1241
|
-
: nativeClient.call(request, cancellation);
|
|
1242
|
-
}
|
|
1243
|
-
|
|
1244
|
-
function nativeStartStream(nativeClient, request, cancellation) {
|
|
1245
|
-
return cancellation == null
|
|
1246
|
-
? nativeClient.startStream(request)
|
|
1247
|
-
: nativeClient.startStream(request, cancellation);
|
|
1248
|
-
}
|
|
1249
|
-
|
|
1250
|
-
function nativeConnect(native, options, cancellation) {
|
|
1251
|
-
return cancellation == null
|
|
1252
|
-
? native.connectMsQuic(options)
|
|
1253
|
-
: native.connectMsQuic(options, cancellation);
|
|
1254
|
-
}
|
|
1255
|
-
|
|
1256
|
-
function once(callback) {
|
|
1257
|
-
let called = false;
|
|
1258
|
-
return () => {
|
|
1259
|
-
if (!called) {
|
|
1260
|
-
called = true;
|
|
1261
|
-
callback();
|
|
1262
|
-
}
|
|
1263
|
-
};
|
|
1264
|
-
}
|
|
1265
|
-
|
|
1266
|
-
function onAbort(signal, callback) {
|
|
1267
|
-
if (signal == null) {
|
|
1268
|
-
return () => {};
|
|
1269
|
-
}
|
|
1270
|
-
if (signal.aborted) {
|
|
1271
|
-
callback();
|
|
1272
|
-
return () => {};
|
|
1273
|
-
}
|
|
1274
|
-
signal.addEventListener("abort", callback, { once: true });
|
|
1275
|
-
return () => signal.removeEventListener("abort", callback);
|
|
1276
|
-
}
|
|
1277
|
-
|
|
1278
|
-
function throwIfAborted(signal) {
|
|
1279
|
-
if (signal?.aborted) {
|
|
1280
|
-
throw signalAbortError(signal);
|
|
1281
|
-
}
|
|
1282
|
-
}
|
|
1283
|
-
|
|
1284
|
-
function signalAbortError(signal) {
|
|
1285
|
-
return cancellationError(signal?.reason);
|
|
1286
|
-
}
|
|
1287
|
-
|
|
1288
|
-
function nativeRequest(request, defaultKind) {
|
|
1289
|
-
const native = {
|
|
1290
|
-
service: request.service,
|
|
1291
|
-
method: request.method,
|
|
1292
|
-
body: byteBody(request.body),
|
|
1293
|
-
metadata: request.metadata ?? {},
|
|
1294
|
-
};
|
|
1295
|
-
if (request.kind != null || defaultKind !== RpcKind.Unary) {
|
|
1296
|
-
native.kind = request.kind ?? defaultKind;
|
|
1297
|
-
}
|
|
1298
|
-
if (request.version != null) {
|
|
1299
|
-
native.version = request.version;
|
|
1300
|
-
}
|
|
1301
|
-
if (request.timeoutNanos != null) {
|
|
1302
|
-
native.timeoutNanos = request.timeoutNanos;
|
|
1303
|
-
}
|
|
1304
|
-
return native;
|
|
1305
|
-
}
|
|
1306
|
-
|
|
1307
|
-
function byteBody(body) {
|
|
1308
|
-
if (body == null) {
|
|
1309
|
-
return EmptyBody;
|
|
1310
|
-
}
|
|
1311
|
-
if (body instanceof Uint8Array) {
|
|
1312
|
-
return body;
|
|
1313
|
-
}
|
|
1314
|
-
if (ArrayBuffer.isView(body)) {
|
|
1315
|
-
return new Uint8Array(body.buffer, body.byteOffset, body.byteLength);
|
|
1316
|
-
}
|
|
1317
|
-
if (body instanceof ArrayBuffer) {
|
|
1318
|
-
return new Uint8Array(body);
|
|
1319
|
-
}
|
|
1320
|
-
return Uint8Array.from(body);
|
|
1321
|
-
}
|
|
1322
|
-
|
|
1323
|
-
function metadataBytes(value) {
|
|
1324
|
-
if (typeof value === "string") {
|
|
1325
|
-
return new TextEncoder().encode(value);
|
|
1326
|
-
}
|
|
1327
|
-
return byteBody(value);
|
|
1328
|
-
}
|
|
1329
|
-
|
|
1330
|
-
function metadataValue(metadata, key) {
|
|
1331
|
-
if (metadata == null) {
|
|
1332
|
-
return null;
|
|
1333
|
-
}
|
|
1334
|
-
return metadata[key] ?? metadata[String(key).toLowerCase()] ?? null;
|
|
1335
|
-
}
|
|
1336
|
-
|
|
1337
|
-
function bytesEqual(left, right) {
|
|
1338
|
-
if (left.byteLength !== right.byteLength) {
|
|
1339
|
-
return false;
|
|
1340
|
-
}
|
|
1341
|
-
for (let i = 0; i < left.byteLength; i += 1) {
|
|
1342
|
-
if (left[i] !== right[i]) {
|
|
1343
|
-
return false;
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
return true;
|
|
1347
|
-
}
|
|
1348
|
-
|
|
1349
|
-
function loadNative() {
|
|
1350
|
-
try {
|
|
1351
|
-
return loadNativeAddon();
|
|
1352
|
-
} catch (error) {
|
|
1353
|
-
throw new TrevRpcError(
|
|
1354
|
-
Code.Unavailable,
|
|
1355
|
-
error?.message ?? "native addon unavailable",
|
|
1356
|
-
{},
|
|
1357
|
-
{
|
|
1358
|
-
cause: error?.cause ?? error,
|
|
1359
|
-
},
|
|
1360
|
-
);
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
|
-
|
|
1364
|
-
function nativeError(error, operation) {
|
|
1365
|
-
if (error instanceof TrevRpcError) {
|
|
1366
|
-
return error;
|
|
1367
|
-
}
|
|
1368
|
-
const nativeCode = Number.isInteger(error?.nativeCode)
|
|
1369
|
-
? error.nativeCode
|
|
1370
|
-
: Number.isInteger(error?.code) && (error.code < 0 || error.code > Code.Unauthenticated)
|
|
1371
|
-
? error.code
|
|
1372
|
-
: undefined;
|
|
1373
|
-
const code = nativeStatusCode(error, nativeCode);
|
|
1374
|
-
const detail = error?.message ?? String(error);
|
|
1375
|
-
return new TrevRpcError(code, `${operation} failed: ${detail}`, error?.metadata ?? {}, {
|
|
1376
|
-
cause: error,
|
|
1377
|
-
nativeCode,
|
|
1378
|
-
});
|
|
1379
|
-
}
|
|
1380
|
-
|
|
1381
|
-
function nativeStatusCode(error, nativeCode) {
|
|
1382
|
-
if (Number.isInteger(error?.status)) {
|
|
1383
|
-
return codeFromNumber(error.status);
|
|
1384
|
-
}
|
|
1385
|
-
if (
|
|
1386
|
-
Number.isInteger(error?.code) &&
|
|
1387
|
-
error.code >= Code.Ok &&
|
|
1388
|
-
error.code <= Code.Unauthenticated
|
|
1389
|
-
) {
|
|
1390
|
-
return codeFromNumber(error.code);
|
|
1391
|
-
}
|
|
1392
|
-
if (nativeCode === NativeObjectClosed || nativeErrorName(nativeCode) === "ECANCELED") {
|
|
1393
|
-
return Code.Cancelled;
|
|
1394
|
-
}
|
|
1395
|
-
if (nativeCode === NativeMsQuicTimeout || nativeErrorName(nativeCode) === "ETIMEDOUT") {
|
|
1396
|
-
return Code.DeadlineExceeded;
|
|
1397
|
-
}
|
|
1398
|
-
if (
|
|
1399
|
-
nativeCode === NativeFrameLimit ||
|
|
1400
|
-
nativeCode === NativeStreamLimit ||
|
|
1401
|
-
nativeCode === NativeSendByteLimit ||
|
|
1402
|
-
nativeCode === NativeSendCountLimit
|
|
1403
|
-
) {
|
|
1404
|
-
return Code.ResourceExhausted;
|
|
1405
|
-
}
|
|
1406
|
-
if (nativeCode === NativeInvalidFrame || nativeCode === NativeInvalidFrameKind) {
|
|
1407
|
-
return Code.InvalidArgument;
|
|
1408
|
-
}
|
|
1409
|
-
if (nativeCode === NativeUnsupportedWireVersion) {
|
|
1410
|
-
return Code.FailedPrecondition;
|
|
1411
|
-
}
|
|
1412
|
-
return Code.Unavailable;
|
|
1413
|
-
}
|
|
1414
|
-
|
|
1415
|
-
function cancellationError(reason) {
|
|
1416
|
-
if (reason instanceof TrevRpcError) {
|
|
1417
|
-
return reason;
|
|
1418
|
-
}
|
|
1419
|
-
const error = cancelled(typeof reason === "string" ? reason : "RPC cancelled");
|
|
1420
|
-
if (reason !== undefined) {
|
|
1421
|
-
Object.defineProperty(error, "cause", { configurable: true, value: reason });
|
|
1422
|
-
}
|
|
1423
|
-
return error;
|
|
1424
|
-
}
|
|
1425
|
-
|
|
1426
|
-
function closeNativeObject(value, operation) {
|
|
1427
|
-
if (value == null) {
|
|
1428
|
-
return;
|
|
1429
|
-
}
|
|
1430
|
-
try {
|
|
1431
|
-
value.close();
|
|
1432
|
-
} catch (error) {
|
|
1433
|
-
throw nativeError(error, operation);
|
|
1434
|
-
}
|
|
1435
|
-
}
|
|
1436
|
-
|
|
1437
|
-
function closeNativeObjectQuietly(value) {
|
|
1438
|
-
try {
|
|
1439
|
-
value?.close();
|
|
1440
|
-
} catch {
|
|
1441
|
-
// Preserve the primary operation error.
|
|
1442
|
-
}
|
|
1443
|
-
}
|
|
1444
|
-
|
|
1445
|
-
function bigintValue(value) {
|
|
1446
|
-
if (typeof value === "bigint") {
|
|
1447
|
-
return value;
|
|
1448
|
-
}
|
|
1449
|
-
if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
|
|
1450
|
-
return BigInt(Math.floor(value));
|
|
1451
|
-
}
|
|
1452
|
-
if (typeof value === "string" && /^\d+$/u.test(value)) {
|
|
1453
|
-
return BigInt(value);
|
|
1454
|
-
}
|
|
1455
|
-
return 0n;
|
|
1456
|
-
}
|