@replit/river 0.215.0 → 0.216.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 +47 -0
- package/dist/adapter-BXCk-dmy.d.ts +29 -0
- package/dist/adapter-D5X11kmP.d.cts +29 -0
- package/dist/{chunk-VXRAFUA3.js → chunk-75ZMPCKC.js} +26 -6
- package/dist/{chunk-VXRAFUA3.js.map → chunk-75ZMPCKC.js.map} +1 -1
- package/dist/chunk-SHND2JG6.js +86 -0
- package/dist/chunk-SHND2JG6.js.map +1 -0
- package/dist/{chunk-RDTQZ7HO.js → chunk-ZLMQQI43.js} +12 -2
- package/dist/chunk-ZLMQQI43.js.map +1 -0
- package/dist/{client-BhwlY0-L.d.ts → client-BNc5Pj_4.d.ts} +2 -2
- package/dist/{client-Dk4H7qeg.d.cts → client-BZUvFL6B.d.cts} +2 -2
- package/dist/codec/index.cjs.map +1 -1
- package/dist/codec/index.d.cts +3 -3
- package/dist/codec/index.d.ts +3 -3
- package/dist/codec/index.js +2 -2
- package/dist/{connection-DU9v3y8a.d.ts → connection-ou9w2dSY.d.ts} +3 -3
- package/dist/{connection-CS00EWuS.d.cts → connection-xxgJHs2o.d.cts} +3 -3
- package/dist/{index-CHCzvZ9D.d.cts → index-BAGGleT3.d.cts} +1 -1
- package/dist/{index-uF0cBM7z.d.ts → index-ZWkoesQD.d.ts} +1 -1
- package/dist/logging/index.d.cts +2 -2
- package/dist/logging/index.d.ts +2 -2
- package/dist/{message-aABg0s5M.d.cts → message-CpXWqmJw.d.cts} +1 -1
- package/dist/{message-aABg0s5M.d.ts → message-CpXWqmJw.d.ts} +1 -1
- package/dist/protobuf/codec.cjs +107 -0
- package/dist/protobuf/codec.cjs.map +1 -0
- package/dist/protobuf/codec.d.cts +13 -0
- package/dist/protobuf/codec.d.ts +13 -0
- package/dist/protobuf/codec.js +7 -0
- package/dist/protobuf/codec.js.map +1 -0
- package/dist/protobuf/index.cjs +1877 -0
- package/dist/protobuf/index.cjs.map +1 -0
- package/dist/protobuf/index.d.cts +488 -0
- package/dist/protobuf/index.d.ts +488 -0
- package/dist/protobuf/index.js +1260 -0
- package/dist/protobuf/index.js.map +1 -0
- package/dist/router/index.cjs +1 -1
- package/dist/router/index.cjs.map +1 -1
- package/dist/router/index.d.cts +12 -11
- package/dist/router/index.d.ts +12 -11
- package/dist/router/index.js +1 -1
- package/dist/{server-uNzkzIRh.d.ts → server-BPu7Td80.d.ts} +4 -4
- package/dist/{server-BR0DZaWi.d.cts → server-JdnoVO11.d.cts} +4 -4
- package/dist/{services-B5SY771g.d.ts → services-BrTFTO5Q.d.ts} +104 -93
- package/dist/{services-DBv2nmly.d.cts → services-cwGAC2rB.d.cts} +104 -93
- package/dist/testUtil/index.cjs +25 -5
- package/dist/testUtil/index.cjs.map +1 -1
- package/dist/testUtil/index.d.cts +8 -7
- package/dist/testUtil/index.d.ts +8 -7
- package/dist/testUtil/index.js +2 -2
- package/dist/transport/impls/ws/client.cjs +5 -1
- package/dist/transport/impls/ws/client.cjs.map +1 -1
- package/dist/transport/impls/ws/client.d.cts +7 -6
- package/dist/transport/impls/ws/client.d.ts +7 -6
- package/dist/transport/impls/ws/client.js +2 -2
- package/dist/transport/impls/ws/server.cjs +21 -5
- package/dist/transport/impls/ws/server.cjs.map +1 -1
- package/dist/transport/impls/ws/server.d.cts +7 -6
- package/dist/transport/impls/ws/server.d.ts +7 -6
- package/dist/transport/impls/ws/server.js +2 -2
- package/dist/transport/index.cjs +25 -5
- package/dist/transport/index.cjs.map +1 -1
- package/dist/transport/index.d.cts +8 -7
- package/dist/transport/index.d.ts +8 -7
- package/dist/transport/index.js +2 -2
- package/dist/{transport-BOL2p5s-.d.ts → transport-B1MUtXL7.d.ts} +7 -5
- package/dist/{transport-D3jzhFSi.d.cts → transport-BnU3Zb0Q.d.cts} +7 -5
- package/dist/types-BGGvYIJM.d.cts +20 -0
- package/dist/types-BGGvYIJM.d.ts +20 -0
- package/package.json +12 -1
- package/dist/adapter-CgF7vQPu.d.ts +0 -47
- package/dist/adapter-IGrG4KLL.d.cts +0 -47
- package/dist/chunk-RDTQZ7HO.js.map +0 -1
|
@@ -0,0 +1,1260 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ProtoCodec
|
|
3
|
+
} from "../chunk-SHND2JG6.js";
|
|
4
|
+
import {
|
|
5
|
+
CANCEL_CODE,
|
|
6
|
+
ControlMessageCloseSchema,
|
|
7
|
+
Err,
|
|
8
|
+
INVALID_REQUEST_CODE,
|
|
9
|
+
Ok,
|
|
10
|
+
ReadableBrokenError,
|
|
11
|
+
ReadableImpl,
|
|
12
|
+
UNCAUGHT_ERROR_CODE,
|
|
13
|
+
UNEXPECTED_DISCONNECT_CODE,
|
|
14
|
+
WritableImpl,
|
|
15
|
+
cancelMessage,
|
|
16
|
+
closeStreamMessage,
|
|
17
|
+
coerceErrorString,
|
|
18
|
+
createClientHandshakeOptions,
|
|
19
|
+
createHandlerSpan,
|
|
20
|
+
createProcTelemetryInfo,
|
|
21
|
+
createServerHandshakeOptions,
|
|
22
|
+
generateId,
|
|
23
|
+
getPropagationContext,
|
|
24
|
+
getTracer,
|
|
25
|
+
isStreamCancel,
|
|
26
|
+
isStreamClose,
|
|
27
|
+
isStreamOpen,
|
|
28
|
+
recordRiverError
|
|
29
|
+
} from "../chunk-ZLMQQI43.js";
|
|
30
|
+
|
|
31
|
+
// protobuf/client.ts
|
|
32
|
+
import { Value } from "@sinclair/typebox/value";
|
|
33
|
+
|
|
34
|
+
// protobuf/errors.ts
|
|
35
|
+
var RiverErrorCode = /* @__PURE__ */ ((RiverErrorCode2) => {
|
|
36
|
+
RiverErrorCode2["OK"] = "OK";
|
|
37
|
+
RiverErrorCode2["CANCELED"] = "CANCELED";
|
|
38
|
+
RiverErrorCode2["UNKNOWN"] = "UNKNOWN";
|
|
39
|
+
RiverErrorCode2["INVALID_ARGUMENT"] = "INVALID_ARGUMENT";
|
|
40
|
+
RiverErrorCode2["DEADLINE_EXCEEDED"] = "DEADLINE_EXCEEDED";
|
|
41
|
+
RiverErrorCode2["NOT_FOUND"] = "NOT_FOUND";
|
|
42
|
+
RiverErrorCode2["ALREADY_EXISTS"] = "ALREADY_EXISTS";
|
|
43
|
+
RiverErrorCode2["PERMISSION_DENIED"] = "PERMISSION_DENIED";
|
|
44
|
+
RiverErrorCode2["RESOURCE_EXHAUSTED"] = "RESOURCE_EXHAUSTED";
|
|
45
|
+
RiverErrorCode2["FAILED_PRECONDITION"] = "FAILED_PRECONDITION";
|
|
46
|
+
RiverErrorCode2["ABORTED"] = "ABORTED";
|
|
47
|
+
RiverErrorCode2["OUT_OF_RANGE"] = "OUT_OF_RANGE";
|
|
48
|
+
RiverErrorCode2["UNIMPLEMENTED"] = "UNIMPLEMENTED";
|
|
49
|
+
RiverErrorCode2["INTERNAL"] = "INTERNAL";
|
|
50
|
+
RiverErrorCode2["UNAVAILABLE"] = "UNAVAILABLE";
|
|
51
|
+
RiverErrorCode2["DATA_LOSS"] = "DATA_LOSS";
|
|
52
|
+
RiverErrorCode2["UNAUTHENTICATED"] = "UNAUTHENTICATED";
|
|
53
|
+
return RiverErrorCode2;
|
|
54
|
+
})(RiverErrorCode || {});
|
|
55
|
+
var riverErrorCodeSet = new Set(Object.values(RiverErrorCode));
|
|
56
|
+
var protocolErrorCodeSet = /* @__PURE__ */ new Set([
|
|
57
|
+
CANCEL_CODE,
|
|
58
|
+
INVALID_REQUEST_CODE,
|
|
59
|
+
UNCAUGHT_ERROR_CODE,
|
|
60
|
+
UNEXPECTED_DISCONNECT_CODE
|
|
61
|
+
]);
|
|
62
|
+
function isRiverError(value) {
|
|
63
|
+
if (!(typeof value === "object" && value !== null)) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
const candidate = value;
|
|
67
|
+
return isRiverErrorCode(candidate.code) && typeof candidate.message === "string";
|
|
68
|
+
}
|
|
69
|
+
function isProtocolError(value) {
|
|
70
|
+
if (!(typeof value === "object" && value !== null)) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
const candidate = value;
|
|
74
|
+
return isProtocolErrorCode(candidate.code) && typeof candidate.message === "string";
|
|
75
|
+
}
|
|
76
|
+
function isClientError(value) {
|
|
77
|
+
return isRiverError(value) || isProtocolError(value);
|
|
78
|
+
}
|
|
79
|
+
function isSerializedClientErrorResult(value) {
|
|
80
|
+
return isErrResultWithPayload(value, isClientError);
|
|
81
|
+
}
|
|
82
|
+
function isSerializedProtocolErrorResult(value) {
|
|
83
|
+
return isErrResultWithPayload(value, isProtocolError);
|
|
84
|
+
}
|
|
85
|
+
function isRiverErrorCode(value) {
|
|
86
|
+
return typeof value === "string" && riverErrorCodeSet.has(value);
|
|
87
|
+
}
|
|
88
|
+
function isProtocolErrorCode(value) {
|
|
89
|
+
return typeof value === "string" && protocolErrorCodeSet.has(value);
|
|
90
|
+
}
|
|
91
|
+
function isErrResultWithPayload(value, predicate) {
|
|
92
|
+
if (!(typeof value === "object" && value !== null)) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
const candidate = value;
|
|
96
|
+
return candidate.ok === false && predicate(candidate.payload);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// protobuf/shared.ts
|
|
100
|
+
import { create, fromBinary, toBinary } from "@bufbuild/protobuf";
|
|
101
|
+
var EMPTY_PROTO_BYTES = new Uint8Array(0);
|
|
102
|
+
function methodKindToProcType(methodKind) {
|
|
103
|
+
switch (methodKind) {
|
|
104
|
+
case "unary":
|
|
105
|
+
return "rpc";
|
|
106
|
+
case "server_streaming":
|
|
107
|
+
return "subscription";
|
|
108
|
+
case "client_streaming":
|
|
109
|
+
return "upload";
|
|
110
|
+
case "bidi_streaming":
|
|
111
|
+
return "stream";
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function methodKey(serviceName, methodName) {
|
|
115
|
+
return `${serviceName}/${methodName}`;
|
|
116
|
+
}
|
|
117
|
+
function encodeMessageBytes(schema, message) {
|
|
118
|
+
return toBinary(schema, create(schema, message));
|
|
119
|
+
}
|
|
120
|
+
function decodeMessageBytes(schema, payload) {
|
|
121
|
+
return fromBinary(schema, payload);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// protobuf/client.ts
|
|
125
|
+
var defaultClientOptions = {
|
|
126
|
+
connectOnInvoke: true,
|
|
127
|
+
eagerlyConnect: true
|
|
128
|
+
};
|
|
129
|
+
function createClient(service, transport, serverId, providedClientOptions = {}) {
|
|
130
|
+
if (providedClientOptions.handshakeOptions) {
|
|
131
|
+
transport.extendHandshake(providedClientOptions.handshakeOptions);
|
|
132
|
+
}
|
|
133
|
+
const clientOptions = { ...defaultClientOptions, ...providedClientOptions };
|
|
134
|
+
if (clientOptions.eagerlyConnect) {
|
|
135
|
+
transport.connect(serverId);
|
|
136
|
+
}
|
|
137
|
+
const client = {};
|
|
138
|
+
for (const methodName of Object.keys(service.method)) {
|
|
139
|
+
const method = service.method[methodName];
|
|
140
|
+
client[methodName] = createMethodCaller(
|
|
141
|
+
service,
|
|
142
|
+
method,
|
|
143
|
+
transport,
|
|
144
|
+
serverId,
|
|
145
|
+
clientOptions
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
return client;
|
|
149
|
+
}
|
|
150
|
+
function createMethodCaller(service, method, transport, serverId, clientOptions) {
|
|
151
|
+
switch (method.methodKind) {
|
|
152
|
+
case "unary":
|
|
153
|
+
return ((request, options) => {
|
|
154
|
+
if (transport.getStatus() === "closed") {
|
|
155
|
+
return Promise.resolve(
|
|
156
|
+
Err({
|
|
157
|
+
code: UNEXPECTED_DISCONNECT_CODE,
|
|
158
|
+
message: "transport is closed"
|
|
159
|
+
})
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
connectOnInvokeIfNeeded(clientOptions, transport, serverId);
|
|
163
|
+
const { resReadable } = startMethodCall(
|
|
164
|
+
service,
|
|
165
|
+
method,
|
|
166
|
+
transport,
|
|
167
|
+
serverId,
|
|
168
|
+
encodeMessageBytes(method.input, request),
|
|
169
|
+
true,
|
|
170
|
+
options?.signal
|
|
171
|
+
);
|
|
172
|
+
return getSingleMessage(resReadable, transport.log);
|
|
173
|
+
});
|
|
174
|
+
case "server_streaming":
|
|
175
|
+
return ((request, options) => {
|
|
176
|
+
if (transport.getStatus() === "closed") {
|
|
177
|
+
return createPreClosedReadable({
|
|
178
|
+
code: UNEXPECTED_DISCONNECT_CODE,
|
|
179
|
+
message: "transport is closed"
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
connectOnInvokeIfNeeded(clientOptions, transport, serverId);
|
|
183
|
+
return startMethodCall(
|
|
184
|
+
service,
|
|
185
|
+
method,
|
|
186
|
+
transport,
|
|
187
|
+
serverId,
|
|
188
|
+
encodeMessageBytes(method.input, request),
|
|
189
|
+
true,
|
|
190
|
+
options?.signal
|
|
191
|
+
).resReadable;
|
|
192
|
+
});
|
|
193
|
+
case "client_streaming":
|
|
194
|
+
return ((options) => {
|
|
195
|
+
if (transport.getStatus() === "closed") {
|
|
196
|
+
return createPreClosedClientStreamingCall();
|
|
197
|
+
}
|
|
198
|
+
connectOnInvokeIfNeeded(clientOptions, transport, serverId);
|
|
199
|
+
const { reqWritable, resReadable } = startMethodCall(
|
|
200
|
+
service,
|
|
201
|
+
method,
|
|
202
|
+
transport,
|
|
203
|
+
serverId,
|
|
204
|
+
EMPTY_PROTO_BYTES,
|
|
205
|
+
false,
|
|
206
|
+
options?.signal
|
|
207
|
+
);
|
|
208
|
+
let didFinalize = false;
|
|
209
|
+
return {
|
|
210
|
+
reqWritable,
|
|
211
|
+
finalize: () => {
|
|
212
|
+
if (didFinalize) {
|
|
213
|
+
throw new Error("client streaming call already finalized");
|
|
214
|
+
}
|
|
215
|
+
didFinalize = true;
|
|
216
|
+
if (!reqWritable.isClosed()) {
|
|
217
|
+
reqWritable.close();
|
|
218
|
+
}
|
|
219
|
+
return getSingleMessage(resReadable, transport.log);
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
});
|
|
223
|
+
case "bidi_streaming":
|
|
224
|
+
return ((options) => {
|
|
225
|
+
if (transport.getStatus() === "closed") {
|
|
226
|
+
return createPreClosedBiDiStreamingCall();
|
|
227
|
+
}
|
|
228
|
+
connectOnInvokeIfNeeded(clientOptions, transport, serverId);
|
|
229
|
+
return startMethodCall(
|
|
230
|
+
service,
|
|
231
|
+
method,
|
|
232
|
+
transport,
|
|
233
|
+
serverId,
|
|
234
|
+
EMPTY_PROTO_BYTES,
|
|
235
|
+
false,
|
|
236
|
+
options?.signal
|
|
237
|
+
);
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
function connectOnInvokeIfNeeded(clientOptions, transport, serverId) {
|
|
242
|
+
if (clientOptions.connectOnInvoke && !transport.sessions.has(serverId)) {
|
|
243
|
+
transport.connect(serverId);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function startMethodCall(service, method, transport, serverId, initialPayload, procClosesWithInit, abortSignal) {
|
|
247
|
+
const session = transport.sessions.get(serverId) ?? transport.createUnconnectedSession(serverId);
|
|
248
|
+
const sessionScopedSend = transport.getSessionBoundSendFn(
|
|
249
|
+
serverId,
|
|
250
|
+
session.id
|
|
251
|
+
);
|
|
252
|
+
const streamId = generateId();
|
|
253
|
+
const { span, ctx } = createProcTelemetryInfo(
|
|
254
|
+
transport.tracer,
|
|
255
|
+
session,
|
|
256
|
+
methodKindToProcType(method.methodKind),
|
|
257
|
+
service.typeName,
|
|
258
|
+
method.name,
|
|
259
|
+
streamId
|
|
260
|
+
);
|
|
261
|
+
let cleanClose = true;
|
|
262
|
+
const reqWritable = new WritableImpl({
|
|
263
|
+
writeCb: (value) => {
|
|
264
|
+
sessionScopedSend({
|
|
265
|
+
streamId,
|
|
266
|
+
payload: encodeMessageBytes(method.input, value),
|
|
267
|
+
controlFlags: 0
|
|
268
|
+
});
|
|
269
|
+
},
|
|
270
|
+
closeCb: () => {
|
|
271
|
+
span.addEvent("reqWritable closed");
|
|
272
|
+
if (!procClosesWithInit && cleanClose) {
|
|
273
|
+
sessionScopedSend(closeStreamMessage(streamId));
|
|
274
|
+
}
|
|
275
|
+
if (resReadable.isClosed()) {
|
|
276
|
+
cleanup();
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
const resReadable = new ReadableImpl();
|
|
281
|
+
const closeReadable = () => {
|
|
282
|
+
if (resReadable.isClosed()) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
resReadable._triggerClose();
|
|
286
|
+
span.addEvent("resReadable closed");
|
|
287
|
+
if (reqWritable.isClosed()) {
|
|
288
|
+
cleanup();
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
function cleanup() {
|
|
292
|
+
transport.removeEventListener("message", onMessage);
|
|
293
|
+
transport.removeEventListener("sessionStatus", onSessionStatus);
|
|
294
|
+
abortSignal?.removeEventListener("abort", onClientCancel);
|
|
295
|
+
span.end();
|
|
296
|
+
}
|
|
297
|
+
function pushResponseError(error) {
|
|
298
|
+
if (!resReadable.isClosed()) {
|
|
299
|
+
resReadable._pushValue(Err(error));
|
|
300
|
+
closeReadable();
|
|
301
|
+
}
|
|
302
|
+
reqWritable.close();
|
|
303
|
+
}
|
|
304
|
+
function onClientCancel() {
|
|
305
|
+
if (resReadable.isClosed() && reqWritable.isClosed()) {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
span.addEvent("sending cancel");
|
|
309
|
+
cleanClose = false;
|
|
310
|
+
const error = {
|
|
311
|
+
code: CANCEL_CODE,
|
|
312
|
+
message: "cancelled by client"
|
|
313
|
+
};
|
|
314
|
+
pushResponseError(error);
|
|
315
|
+
sessionScopedSend(cancelMessage(streamId, Err(error)));
|
|
316
|
+
}
|
|
317
|
+
function onMessage(msg) {
|
|
318
|
+
if (msg.streamId !== streamId) {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
if (msg.to !== transport.clientId) {
|
|
322
|
+
transport.log?.error("got stream message from unexpected client", {
|
|
323
|
+
clientId: transport.clientId,
|
|
324
|
+
transportMessage: msg
|
|
325
|
+
});
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
if (isStreamCancel(msg.controlFlags)) {
|
|
329
|
+
cleanClose = false;
|
|
330
|
+
span.addEvent("received cancel");
|
|
331
|
+
const error = isSerializedClientErrorResult(msg.payload) ? msg.payload.payload : {
|
|
332
|
+
code: CANCEL_CODE,
|
|
333
|
+
message: "stream cancelled with invalid payload"
|
|
334
|
+
};
|
|
335
|
+
pushResponseError(error);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
if (resReadable.isClosed()) {
|
|
339
|
+
transport.log?.error("received message after response stream is closed", {
|
|
340
|
+
clientId: transport.clientId,
|
|
341
|
+
transportMessage: msg
|
|
342
|
+
});
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
if (!Value.Check(ControlMessageCloseSchema, msg.payload)) {
|
|
346
|
+
if (msg.payload instanceof Uint8Array) {
|
|
347
|
+
try {
|
|
348
|
+
resReadable._pushValue(
|
|
349
|
+
Ok(
|
|
350
|
+
decodeMessageBytes(method.output, msg.payload)
|
|
351
|
+
)
|
|
352
|
+
);
|
|
353
|
+
} catch (err) {
|
|
354
|
+
pushResponseError({
|
|
355
|
+
code: INVALID_REQUEST_CODE,
|
|
356
|
+
message: "failed to decode protobuf response payload",
|
|
357
|
+
extras: { cause: err }
|
|
358
|
+
});
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
} else if (isSerializedClientErrorResult(msg.payload)) {
|
|
362
|
+
resReadable._pushValue(msg.payload);
|
|
363
|
+
} else {
|
|
364
|
+
pushResponseError({
|
|
365
|
+
code: INVALID_REQUEST_CODE,
|
|
366
|
+
message: "received invalid protobuf response payload"
|
|
367
|
+
});
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
if (isStreamClose(msg.controlFlags)) {
|
|
372
|
+
span.addEvent("received response close");
|
|
373
|
+
closeReadable();
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
function onSessionStatus(evt) {
|
|
377
|
+
if (evt.status !== "closing" || evt.session.to !== serverId || session.id !== evt.session.id) {
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
cleanClose = false;
|
|
381
|
+
pushResponseError({
|
|
382
|
+
code: UNEXPECTED_DISCONNECT_CODE,
|
|
383
|
+
message: `${serverId} unexpectedly disconnected`
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
abortSignal?.addEventListener("abort", onClientCancel);
|
|
387
|
+
transport.addEventListener("message", onMessage);
|
|
388
|
+
transport.addEventListener("sessionStatus", onSessionStatus);
|
|
389
|
+
try {
|
|
390
|
+
sessionScopedSend({
|
|
391
|
+
streamId,
|
|
392
|
+
serviceName: service.typeName,
|
|
393
|
+
procedureName: method.name,
|
|
394
|
+
tracing: getPropagationContext(ctx),
|
|
395
|
+
payload: initialPayload,
|
|
396
|
+
controlFlags: procClosesWithInit ? 2 /* StreamOpenBit */ | 8 /* StreamClosedBit */ : 2 /* StreamOpenBit */
|
|
397
|
+
});
|
|
398
|
+
} catch (err) {
|
|
399
|
+
cleanup();
|
|
400
|
+
throw err;
|
|
401
|
+
}
|
|
402
|
+
if (procClosesWithInit) {
|
|
403
|
+
reqWritable.close();
|
|
404
|
+
}
|
|
405
|
+
return { reqWritable, resReadable };
|
|
406
|
+
}
|
|
407
|
+
function createPreClosedReadable(error) {
|
|
408
|
+
const readable = new ReadableImpl();
|
|
409
|
+
readable._pushValue(Err(error));
|
|
410
|
+
readable._triggerClose();
|
|
411
|
+
return readable;
|
|
412
|
+
}
|
|
413
|
+
function createPreClosedWritable() {
|
|
414
|
+
const writable = new WritableImpl({
|
|
415
|
+
writeCb: () => void 0,
|
|
416
|
+
closeCb: () => void 0
|
|
417
|
+
});
|
|
418
|
+
writable.close();
|
|
419
|
+
return writable;
|
|
420
|
+
}
|
|
421
|
+
function createPreClosedClientStreamingCall() {
|
|
422
|
+
return {
|
|
423
|
+
reqWritable: createPreClosedWritable(),
|
|
424
|
+
finalize: () => Promise.resolve(
|
|
425
|
+
Err({
|
|
426
|
+
code: UNEXPECTED_DISCONNECT_CODE,
|
|
427
|
+
message: "transport is closed"
|
|
428
|
+
})
|
|
429
|
+
)
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
function createPreClosedBiDiStreamingCall() {
|
|
433
|
+
return {
|
|
434
|
+
reqWritable: createPreClosedWritable(),
|
|
435
|
+
resReadable: createPreClosedReadable({
|
|
436
|
+
code: UNEXPECTED_DISCONNECT_CODE,
|
|
437
|
+
message: "transport is closed"
|
|
438
|
+
})
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
async function getSingleMessage(resReadable, log) {
|
|
442
|
+
const ret = await resReadable.collect();
|
|
443
|
+
if (ret.length === 0) {
|
|
444
|
+
return Err({
|
|
445
|
+
code: INVALID_REQUEST_CODE,
|
|
446
|
+
message: "expected single response from server, got none"
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
if (ret.length > 1) {
|
|
450
|
+
log?.error("expected single protobuf response from server, got multiple");
|
|
451
|
+
}
|
|
452
|
+
const first = ret[0];
|
|
453
|
+
if (!first.ok) {
|
|
454
|
+
if (first.payload.code === ReadableBrokenError.code) {
|
|
455
|
+
return Err({
|
|
456
|
+
code: UNEXPECTED_DISCONNECT_CODE,
|
|
457
|
+
message: first.payload.message
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
return Err(first.payload);
|
|
461
|
+
}
|
|
462
|
+
return first;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// protobuf/handshake.ts
|
|
466
|
+
import { Type } from "@sinclair/typebox";
|
|
467
|
+
var HandshakeBytesSchema = Type.Uint8Array();
|
|
468
|
+
function createClientHandshakeOptions2(schema, construct) {
|
|
469
|
+
return createClientHandshakeOptions(
|
|
470
|
+
HandshakeBytesSchema,
|
|
471
|
+
async () => {
|
|
472
|
+
const metadata = await construct();
|
|
473
|
+
return encodeMessageBytes(schema, metadata);
|
|
474
|
+
}
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
function createServerHandshakeOptions2(schema, validate) {
|
|
478
|
+
return createServerHandshakeOptions(
|
|
479
|
+
HandshakeBytesSchema,
|
|
480
|
+
async (metadata, previousParsedMetadata) => {
|
|
481
|
+
let decoded;
|
|
482
|
+
try {
|
|
483
|
+
decoded = decodeMessageBytes(schema, metadata);
|
|
484
|
+
} catch {
|
|
485
|
+
return "REJECTED_BY_CUSTOM_HANDLER";
|
|
486
|
+
}
|
|
487
|
+
return await validate(decoded, previousParsedMetadata);
|
|
488
|
+
}
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// protobuf/service.ts
|
|
493
|
+
function buildMethodMap(descriptor, handlers) {
|
|
494
|
+
const methods = /* @__PURE__ */ new Map();
|
|
495
|
+
const typedMethods = descriptor.method;
|
|
496
|
+
for (const methodName of Object.keys(handlers)) {
|
|
497
|
+
const handler = handlers[methodName];
|
|
498
|
+
if (handler === void 0) {
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
const method = typedMethods[methodName];
|
|
502
|
+
if (!method) {
|
|
503
|
+
throw new Error(`unknown method ${methodName} on ${descriptor.typeName}`);
|
|
504
|
+
}
|
|
505
|
+
methods.set(method.name, {
|
|
506
|
+
service: descriptor,
|
|
507
|
+
method,
|
|
508
|
+
impl: handler
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
return methods;
|
|
512
|
+
}
|
|
513
|
+
var ProtoServiceScaffold = class {
|
|
514
|
+
descriptor;
|
|
515
|
+
config;
|
|
516
|
+
constructor(descriptor, config) {
|
|
517
|
+
this.descriptor = descriptor;
|
|
518
|
+
this.config = config;
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Type-check a partial set of handler implementations against this
|
|
522
|
+
* service's types. Returns the input unchanged -- this is purely a
|
|
523
|
+
* type-level helper for splitting handlers across files.
|
|
524
|
+
*
|
|
525
|
+
* @param handlers - A partial set of method implementations.
|
|
526
|
+
*/
|
|
527
|
+
procedures(handlers) {
|
|
528
|
+
return handlers;
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Finalize the scaffold into a service definition. Provide all handlers
|
|
532
|
+
* here (or spread in handler objects from {@link procedures}).
|
|
533
|
+
*
|
|
534
|
+
* @param handlers - Method implementations (missing methods return
|
|
535
|
+
* UNIMPLEMENTED at runtime).
|
|
536
|
+
*/
|
|
537
|
+
finalize(handlers) {
|
|
538
|
+
return createProtoService().define(
|
|
539
|
+
this.descriptor,
|
|
540
|
+
this.config,
|
|
541
|
+
handlers
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
function createProtoService() {
|
|
546
|
+
return class ProtoServiceSchema {
|
|
547
|
+
descriptor;
|
|
548
|
+
methods;
|
|
549
|
+
/** @internal */
|
|
550
|
+
initializeStateFn;
|
|
551
|
+
constructor(descriptor, initializeStateFn, methods) {
|
|
552
|
+
this.descriptor = descriptor;
|
|
553
|
+
this.initializeStateFn = initializeStateFn;
|
|
554
|
+
this.methods = methods;
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Create a live service instance with initialized state.
|
|
558
|
+
*
|
|
559
|
+
* @param ctx - The user-provided context, passed to `initializeState`.
|
|
560
|
+
*/
|
|
561
|
+
instantiate(ctx) {
|
|
562
|
+
const state = this.initializeStateFn ? this.initializeStateFn(ctx) : {};
|
|
563
|
+
return Object.freeze({
|
|
564
|
+
descriptor: this.descriptor,
|
|
565
|
+
state,
|
|
566
|
+
methods: this.methods,
|
|
567
|
+
async [Symbol.asyncDispose]() {
|
|
568
|
+
await state[Symbol.asyncDispose]?.();
|
|
569
|
+
state[Symbol.dispose]?.();
|
|
570
|
+
}
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
static define(descriptor, configOrHandlers, maybeHandlers) {
|
|
574
|
+
let initializeStateFn;
|
|
575
|
+
let handlers;
|
|
576
|
+
if ("initializeState" in configOrHandlers && typeof configOrHandlers.initializeState === "function") {
|
|
577
|
+
if (!maybeHandlers) {
|
|
578
|
+
throw new Error("expected handlers as third argument");
|
|
579
|
+
}
|
|
580
|
+
initializeStateFn = configOrHandlers.initializeState;
|
|
581
|
+
handlers = maybeHandlers;
|
|
582
|
+
} else {
|
|
583
|
+
initializeStateFn = void 0;
|
|
584
|
+
handlers = configOrHandlers;
|
|
585
|
+
}
|
|
586
|
+
return new ProtoServiceSchema(
|
|
587
|
+
descriptor,
|
|
588
|
+
initializeStateFn,
|
|
589
|
+
buildMethodMap(descriptor, handlers)
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Create a scaffold for splitting handler implementations across files.
|
|
594
|
+
*
|
|
595
|
+
* @param descriptor - The generated protobuf service descriptor.
|
|
596
|
+
* @param config - Service configuration including `initializeState`.
|
|
597
|
+
*/
|
|
598
|
+
static scaffold(descriptor, config) {
|
|
599
|
+
return new ProtoServiceScaffold(
|
|
600
|
+
descriptor,
|
|
601
|
+
config
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// protobuf/server.ts
|
|
608
|
+
import { Value as Value2 } from "@sinclair/typebox/value";
|
|
609
|
+
import { context as otelContext, trace } from "@opentelemetry/api";
|
|
610
|
+
var ProtobufServer = class {
|
|
611
|
+
streams;
|
|
612
|
+
transport;
|
|
613
|
+
methods;
|
|
614
|
+
serviceInstances;
|
|
615
|
+
userContext;
|
|
616
|
+
log;
|
|
617
|
+
middlewares;
|
|
618
|
+
serverCancelledStreams;
|
|
619
|
+
maxCancelledStreamTombstonesPerSession;
|
|
620
|
+
unregisterTransportListeners;
|
|
621
|
+
constructor(transport, services, options = {}) {
|
|
622
|
+
this.transport = transport;
|
|
623
|
+
this.log = transport.log;
|
|
624
|
+
this.middlewares = options.middlewares ?? [];
|
|
625
|
+
this.maxCancelledStreamTombstonesPerSession = options.maxCancelledStreamTombstonesPerSession ?? 200;
|
|
626
|
+
this.serverCancelledStreams = /* @__PURE__ */ new Map();
|
|
627
|
+
this.streams = /* @__PURE__ */ new Map();
|
|
628
|
+
this.userContext = options.extendedContext ?? {};
|
|
629
|
+
this.methods = /* @__PURE__ */ new Map();
|
|
630
|
+
this.serviceInstances = /* @__PURE__ */ new Map();
|
|
631
|
+
for (const svc of services) {
|
|
632
|
+
if (this.serviceInstances.has(svc.descriptor.typeName)) {
|
|
633
|
+
throw new Error(
|
|
634
|
+
`duplicate protobuf service registration for ${svc.descriptor.typeName}`
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
const instance = svc.instantiate(this.userContext);
|
|
638
|
+
this.serviceInstances.set(svc.descriptor.typeName, instance);
|
|
639
|
+
for (const [, reg] of instance.methods) {
|
|
640
|
+
this.methods.set(
|
|
641
|
+
methodKey(svc.descriptor.typeName, reg.method.name),
|
|
642
|
+
reg
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
if (options.handshakeOptions) {
|
|
647
|
+
transport.extendHandshake(options.handshakeOptions);
|
|
648
|
+
}
|
|
649
|
+
const handleCreatingNewStreams = (message) => {
|
|
650
|
+
if (message.to !== this.transport.clientId) {
|
|
651
|
+
this.log?.info(
|
|
652
|
+
`got msg with destination that isn't this server, ignoring`,
|
|
653
|
+
{
|
|
654
|
+
clientId: this.transport.clientId,
|
|
655
|
+
transportMessage: message
|
|
656
|
+
}
|
|
657
|
+
);
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
const stream = this.streams.get(message.streamId);
|
|
661
|
+
if (stream) {
|
|
662
|
+
stream.handleMsg(message);
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
if (this.serverCancelledStreams.get(message.from)?.has(message.streamId)) {
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
const newStreamProps = this.validateNewProcStream(message);
|
|
669
|
+
if (!newStreamProps) {
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
createHandlerSpan(
|
|
673
|
+
transport.tracer,
|
|
674
|
+
newStreamProps.initialSession,
|
|
675
|
+
methodKindToProcType(newStreamProps.method.methodKind),
|
|
676
|
+
newStreamProps.service.typeName,
|
|
677
|
+
newStreamProps.method.name,
|
|
678
|
+
newStreamProps.streamId,
|
|
679
|
+
newStreamProps.tracingCtx,
|
|
680
|
+
(span) => {
|
|
681
|
+
this.createNewProcStream(span, newStreamProps);
|
|
682
|
+
}
|
|
683
|
+
);
|
|
684
|
+
};
|
|
685
|
+
const handleSessionStatus = (evt) => {
|
|
686
|
+
if (evt.status !== "closing") {
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
const disconnectedClientId = evt.session.to;
|
|
690
|
+
this.log?.info(
|
|
691
|
+
`got session disconnect from ${disconnectedClientId}, cleaning up protobuf streams`,
|
|
692
|
+
evt.session.loggingMetadata
|
|
693
|
+
);
|
|
694
|
+
for (const stream of this.streams.values()) {
|
|
695
|
+
if (stream.from === disconnectedClientId) {
|
|
696
|
+
stream.handleSessionDisconnect();
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
this.serverCancelledStreams.delete(disconnectedClientId);
|
|
700
|
+
};
|
|
701
|
+
const handleTransportStatus = (evt) => {
|
|
702
|
+
if (evt.status === "closed") {
|
|
703
|
+
this.unregisterTransportListeners();
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
this.unregisterTransportListeners = () => {
|
|
707
|
+
this.transport.removeEventListener("message", handleCreatingNewStreams);
|
|
708
|
+
this.transport.removeEventListener("sessionStatus", handleSessionStatus);
|
|
709
|
+
this.transport.removeEventListener(
|
|
710
|
+
"transportStatus",
|
|
711
|
+
handleTransportStatus
|
|
712
|
+
);
|
|
713
|
+
};
|
|
714
|
+
this.transport.addEventListener("message", handleCreatingNewStreams);
|
|
715
|
+
this.transport.addEventListener("sessionStatus", handleSessionStatus);
|
|
716
|
+
this.transport.addEventListener("transportStatus", handleTransportStatus);
|
|
717
|
+
}
|
|
718
|
+
async close() {
|
|
719
|
+
this.unregisterTransportListeners();
|
|
720
|
+
for (const instance of this.serviceInstances.values()) {
|
|
721
|
+
await instance[Symbol.asyncDispose]();
|
|
722
|
+
}
|
|
723
|
+
const ctx = this.userContext;
|
|
724
|
+
if (ctx[Symbol.asyncDispose]) {
|
|
725
|
+
await ctx[Symbol.asyncDispose]?.();
|
|
726
|
+
} else if (ctx[Symbol.dispose]) {
|
|
727
|
+
ctx[Symbol.dispose]?.();
|
|
728
|
+
} else {
|
|
729
|
+
for (const value of Object.values(ctx)) {
|
|
730
|
+
if (value && typeof value === "object") {
|
|
731
|
+
const v = value;
|
|
732
|
+
if (v[Symbol.asyncDispose]) {
|
|
733
|
+
await v[Symbol.asyncDispose]?.();
|
|
734
|
+
} else if (v[Symbol.dispose]) {
|
|
735
|
+
v[Symbol.dispose]?.();
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
createNewProcStream(span, props) {
|
|
742
|
+
const {
|
|
743
|
+
streamId,
|
|
744
|
+
service,
|
|
745
|
+
method,
|
|
746
|
+
impl,
|
|
747
|
+
serviceContext,
|
|
748
|
+
serviceState,
|
|
749
|
+
sessionMetadata,
|
|
750
|
+
initialSession,
|
|
751
|
+
initialRequest,
|
|
752
|
+
closeRequestOnStart
|
|
753
|
+
} = props;
|
|
754
|
+
const { to: from, loggingMetadata, id: sessionId } = initialSession;
|
|
755
|
+
loggingMetadata.telemetry = {
|
|
756
|
+
traceId: span.spanContext().traceId,
|
|
757
|
+
spanId: span.spanContext().spanId
|
|
758
|
+
};
|
|
759
|
+
let cleanClose = true;
|
|
760
|
+
const finishedController = new AbortController();
|
|
761
|
+
const sessionScopedSend = this.transport.getSessionBoundSendFn(
|
|
762
|
+
from,
|
|
763
|
+
sessionId
|
|
764
|
+
);
|
|
765
|
+
const deferredCleanups = [];
|
|
766
|
+
let cleanupsHaveRun = false;
|
|
767
|
+
const runCleanupSafe = async (fn) => {
|
|
768
|
+
try {
|
|
769
|
+
await fn();
|
|
770
|
+
} catch (err) {
|
|
771
|
+
span.recordException(
|
|
772
|
+
err instanceof Error ? err : new Error(String(err))
|
|
773
|
+
);
|
|
774
|
+
}
|
|
775
|
+
};
|
|
776
|
+
const deferCleanup = (fn) => {
|
|
777
|
+
if (cleanupsHaveRun) {
|
|
778
|
+
void runCleanupSafe(fn);
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
deferredCleanups.push(fn);
|
|
782
|
+
};
|
|
783
|
+
const runDeferredCleanups = async () => {
|
|
784
|
+
if (deferredCleanups.length === 0) {
|
|
785
|
+
cleanupsHaveRun = true;
|
|
786
|
+
span.end();
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
const cleanupSpan = getTracer().startSpan(
|
|
790
|
+
"river.cleanup",
|
|
791
|
+
{},
|
|
792
|
+
trace.setSpan(otelContext.active(), span)
|
|
793
|
+
);
|
|
794
|
+
try {
|
|
795
|
+
for (let fn = deferredCleanups.pop(); fn; fn = deferredCleanups.pop()) {
|
|
796
|
+
await runCleanupSafe(fn);
|
|
797
|
+
}
|
|
798
|
+
} finally {
|
|
799
|
+
cleanupsHaveRun = true;
|
|
800
|
+
cleanupSpan.end();
|
|
801
|
+
span.end();
|
|
802
|
+
}
|
|
803
|
+
};
|
|
804
|
+
const cleanup = () => {
|
|
805
|
+
finishedController.abort();
|
|
806
|
+
this.streams.delete(streamId);
|
|
807
|
+
void runDeferredCleanups();
|
|
808
|
+
};
|
|
809
|
+
const reqReadable = new ReadableImpl();
|
|
810
|
+
const closeReadable = () => {
|
|
811
|
+
if (reqReadable.isClosed()) {
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
reqReadable._triggerClose();
|
|
815
|
+
if (resWritable.isClosed()) {
|
|
816
|
+
cleanup();
|
|
817
|
+
}
|
|
818
|
+
};
|
|
819
|
+
const procClosesWithResponse = method.methodKind === "unary" || method.methodKind === "client_streaming";
|
|
820
|
+
const resWritable = new WritableImpl({
|
|
821
|
+
writeCb: (response) => {
|
|
822
|
+
const payload = response.ok ? encodeMessageBytes(method.output, response.payload) : Err(response.payload);
|
|
823
|
+
if (!response.ok) {
|
|
824
|
+
recordRiverError(span, response.payload);
|
|
825
|
+
}
|
|
826
|
+
sessionScopedSend({
|
|
827
|
+
streamId,
|
|
828
|
+
controlFlags: procClosesWithResponse ? 8 /* StreamClosedBit */ : 0,
|
|
829
|
+
payload
|
|
830
|
+
});
|
|
831
|
+
if (procClosesWithResponse) {
|
|
832
|
+
resWritable.close();
|
|
833
|
+
}
|
|
834
|
+
},
|
|
835
|
+
closeCb: () => {
|
|
836
|
+
if (!procClosesWithResponse && cleanClose) {
|
|
837
|
+
sessionScopedSend(closeStreamMessage(streamId));
|
|
838
|
+
}
|
|
839
|
+
if (reqReadable.isClosed()) {
|
|
840
|
+
cleanup();
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
});
|
|
844
|
+
const cancelStream = (error) => {
|
|
845
|
+
this.cancelStream(from, sessionScopedSend, streamId, error);
|
|
846
|
+
};
|
|
847
|
+
const pushRequestError = (error) => {
|
|
848
|
+
if (!reqReadable.isClosed()) {
|
|
849
|
+
reqReadable._pushValue(Err(error));
|
|
850
|
+
closeReadable();
|
|
851
|
+
}
|
|
852
|
+
resWritable.close();
|
|
853
|
+
};
|
|
854
|
+
const onServerCancel = (error) => {
|
|
855
|
+
recordRiverError(span, error);
|
|
856
|
+
if (reqReadable.isClosed() && resWritable.isClosed()) {
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
cleanClose = false;
|
|
860
|
+
pushRequestError(error);
|
|
861
|
+
cancelStream(error);
|
|
862
|
+
};
|
|
863
|
+
const onHandlerError = (err) => {
|
|
864
|
+
const errorMsg = coerceErrorString(err);
|
|
865
|
+
span.recordException(err instanceof Error ? err : new Error(errorMsg));
|
|
866
|
+
this.log?.error(
|
|
867
|
+
`${service.typeName}.${method.name} handler threw an uncaught error`,
|
|
868
|
+
{
|
|
869
|
+
...loggingMetadata,
|
|
870
|
+
transportMessage: {
|
|
871
|
+
procedureName: method.name,
|
|
872
|
+
serviceName: service.typeName
|
|
873
|
+
},
|
|
874
|
+
extras: {
|
|
875
|
+
error: errorMsg,
|
|
876
|
+
originalException: err
|
|
877
|
+
},
|
|
878
|
+
tags: ["uncaught-handler-error"]
|
|
879
|
+
}
|
|
880
|
+
);
|
|
881
|
+
const error = {
|
|
882
|
+
code: UNCAUGHT_ERROR_CODE,
|
|
883
|
+
message: errorMsg
|
|
884
|
+
};
|
|
885
|
+
recordRiverError(span, error);
|
|
886
|
+
if (reqReadable.isClosed() && resWritable.isClosed()) {
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
if (!resWritable.isClosed()) {
|
|
890
|
+
resWritable.write(Err(error));
|
|
891
|
+
if (!procClosesWithResponse) {
|
|
892
|
+
resWritable.close();
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
if (!reqReadable.isClosed()) {
|
|
896
|
+
closeReadable();
|
|
897
|
+
}
|
|
898
|
+
};
|
|
899
|
+
const onMessage = (msg) => {
|
|
900
|
+
if (msg.from !== from) {
|
|
901
|
+
this.log?.error("got stream message from unexpected client", {
|
|
902
|
+
...loggingMetadata,
|
|
903
|
+
transportMessage: msg,
|
|
904
|
+
tags: ["invariant-violation"]
|
|
905
|
+
});
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
if (isStreamCancel(msg.controlFlags)) {
|
|
909
|
+
const error = isSerializedProtocolErrorResult(
|
|
910
|
+
msg.payload
|
|
911
|
+
) ? msg.payload.payload : {
|
|
912
|
+
code: CANCEL_CODE,
|
|
913
|
+
message: "stream cancelled, client sent invalid payload"
|
|
914
|
+
};
|
|
915
|
+
pushRequestError(error);
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
if (reqReadable.isClosed()) {
|
|
919
|
+
this.log?.warn("received message after request stream is closed", {
|
|
920
|
+
...loggingMetadata,
|
|
921
|
+
transportMessage: msg,
|
|
922
|
+
tags: ["invalid-request"]
|
|
923
|
+
});
|
|
924
|
+
onServerCancel({
|
|
925
|
+
code: INVALID_REQUEST_CODE,
|
|
926
|
+
message: "received message after request stream is closed"
|
|
927
|
+
});
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
if (msg.payload instanceof Uint8Array) {
|
|
931
|
+
try {
|
|
932
|
+
reqReadable._pushValue(
|
|
933
|
+
Ok(decodeMessageBytes(method.input, msg.payload))
|
|
934
|
+
);
|
|
935
|
+
} catch {
|
|
936
|
+
onServerCancel({
|
|
937
|
+
code: INVALID_REQUEST_CODE,
|
|
938
|
+
message: "failed to decode protobuf request payload"
|
|
939
|
+
});
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
if (isStreamClose(msg.controlFlags)) {
|
|
943
|
+
closeReadable();
|
|
944
|
+
}
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
if (Value2.Check(ControlMessageCloseSchema, msg.payload) && isStreamClose(msg.controlFlags)) {
|
|
948
|
+
closeReadable();
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
onServerCancel({
|
|
952
|
+
code: INVALID_REQUEST_CODE,
|
|
953
|
+
message: "received invalid protobuf request payload"
|
|
954
|
+
});
|
|
955
|
+
};
|
|
956
|
+
const procStream = {
|
|
957
|
+
from,
|
|
958
|
+
streamId,
|
|
959
|
+
service,
|
|
960
|
+
method,
|
|
961
|
+
handleMsg: onMessage,
|
|
962
|
+
handleSessionDisconnect: () => {
|
|
963
|
+
cleanClose = false;
|
|
964
|
+
pushRequestError({
|
|
965
|
+
code: UNEXPECTED_DISCONNECT_CODE,
|
|
966
|
+
message: "client unexpectedly disconnected"
|
|
967
|
+
});
|
|
968
|
+
}
|
|
969
|
+
};
|
|
970
|
+
const handlerContext = {
|
|
971
|
+
...serviceContext,
|
|
972
|
+
state: serviceState,
|
|
973
|
+
from,
|
|
974
|
+
sessionId,
|
|
975
|
+
metadata: sessionMetadata,
|
|
976
|
+
span,
|
|
977
|
+
service,
|
|
978
|
+
method,
|
|
979
|
+
deferCleanup,
|
|
980
|
+
cancel: (message) => {
|
|
981
|
+
const error = {
|
|
982
|
+
code: CANCEL_CODE,
|
|
983
|
+
message: message ?? "cancelled by server procedure handler"
|
|
984
|
+
};
|
|
985
|
+
onServerCancel(error);
|
|
986
|
+
return Err(error);
|
|
987
|
+
},
|
|
988
|
+
signal: finishedController.signal
|
|
989
|
+
};
|
|
990
|
+
const middlewareContext = {
|
|
991
|
+
...handlerContext,
|
|
992
|
+
streamId,
|
|
993
|
+
procedureName: method.name,
|
|
994
|
+
serviceName: service.typeName
|
|
995
|
+
};
|
|
996
|
+
if (initialRequest !== null) {
|
|
997
|
+
reqReadable._pushValue(Ok(initialRequest));
|
|
998
|
+
}
|
|
999
|
+
if (closeRequestOnStart) {
|
|
1000
|
+
closeReadable();
|
|
1001
|
+
}
|
|
1002
|
+
const handler = impl;
|
|
1003
|
+
const runProcedureHandler = async () => {
|
|
1004
|
+
try {
|
|
1005
|
+
switch (method.methodKind) {
|
|
1006
|
+
case "unary": {
|
|
1007
|
+
const response = await handler(
|
|
1008
|
+
requireInitialRequest(initialRequest, method),
|
|
1009
|
+
handlerContext
|
|
1010
|
+
);
|
|
1011
|
+
if (!resWritable.isClosed()) {
|
|
1012
|
+
resWritable.write(response);
|
|
1013
|
+
}
|
|
1014
|
+
break;
|
|
1015
|
+
}
|
|
1016
|
+
case "server_streaming":
|
|
1017
|
+
await handler({
|
|
1018
|
+
request: requireInitialRequest(initialRequest, method),
|
|
1019
|
+
ctx: handlerContext,
|
|
1020
|
+
resWritable
|
|
1021
|
+
});
|
|
1022
|
+
break;
|
|
1023
|
+
case "client_streaming": {
|
|
1024
|
+
const response = await handler({
|
|
1025
|
+
ctx: handlerContext,
|
|
1026
|
+
reqReadable
|
|
1027
|
+
});
|
|
1028
|
+
if (!resWritable.isClosed()) {
|
|
1029
|
+
resWritable.write(response);
|
|
1030
|
+
}
|
|
1031
|
+
break;
|
|
1032
|
+
}
|
|
1033
|
+
case "bidi_streaming":
|
|
1034
|
+
await handler({
|
|
1035
|
+
ctx: handlerContext,
|
|
1036
|
+
reqReadable,
|
|
1037
|
+
resWritable
|
|
1038
|
+
});
|
|
1039
|
+
break;
|
|
1040
|
+
}
|
|
1041
|
+
} catch (err) {
|
|
1042
|
+
onHandlerError(err);
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1045
|
+
this.middlewares.reduceRight(
|
|
1046
|
+
(next, middleware) => {
|
|
1047
|
+
return () => {
|
|
1048
|
+
middleware({
|
|
1049
|
+
ctx: middlewareContext,
|
|
1050
|
+
reqInit: initialRequest,
|
|
1051
|
+
next
|
|
1052
|
+
});
|
|
1053
|
+
};
|
|
1054
|
+
},
|
|
1055
|
+
() => {
|
|
1056
|
+
void runProcedureHandler();
|
|
1057
|
+
}
|
|
1058
|
+
)();
|
|
1059
|
+
if (!finishedController.signal.aborted) {
|
|
1060
|
+
this.streams.set(streamId, procStream);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
validateNewProcStream(initMessage) {
|
|
1064
|
+
const session = this.transport.sessions.get(initMessage.from);
|
|
1065
|
+
if (!session) {
|
|
1066
|
+
this.log?.error(`couldn't find session for ${initMessage.from}`, {
|
|
1067
|
+
clientId: this.transport.clientId,
|
|
1068
|
+
transportMessage: initMessage,
|
|
1069
|
+
tags: ["invariant-violation"]
|
|
1070
|
+
});
|
|
1071
|
+
return null;
|
|
1072
|
+
}
|
|
1073
|
+
const sessionScopedSend = this.transport.getSessionBoundSendFn(
|
|
1074
|
+
initMessage.from,
|
|
1075
|
+
session.id
|
|
1076
|
+
);
|
|
1077
|
+
const sendCancel = (error) => {
|
|
1078
|
+
this.cancelStream(
|
|
1079
|
+
initMessage.from,
|
|
1080
|
+
sessionScopedSend,
|
|
1081
|
+
initMessage.streamId,
|
|
1082
|
+
error
|
|
1083
|
+
);
|
|
1084
|
+
};
|
|
1085
|
+
const sessionMetadata = this.transport.sessionHandshakeMetadata.get(
|
|
1086
|
+
session.to
|
|
1087
|
+
);
|
|
1088
|
+
if (!sessionMetadata) {
|
|
1089
|
+
sendCancel({
|
|
1090
|
+
code: UNCAUGHT_ERROR_CODE,
|
|
1091
|
+
message: `session doesn't have handshake metadata`
|
|
1092
|
+
});
|
|
1093
|
+
return null;
|
|
1094
|
+
}
|
|
1095
|
+
if (!isStreamOpen(initMessage.controlFlags)) {
|
|
1096
|
+
sendCancel({
|
|
1097
|
+
code: INVALID_REQUEST_CODE,
|
|
1098
|
+
message: `can't create a new procedure stream from a message without the stream open bit set`
|
|
1099
|
+
});
|
|
1100
|
+
return null;
|
|
1101
|
+
}
|
|
1102
|
+
if (!initMessage.serviceName) {
|
|
1103
|
+
sendCancel({
|
|
1104
|
+
code: INVALID_REQUEST_CODE,
|
|
1105
|
+
message: `missing service name in stream open message`
|
|
1106
|
+
});
|
|
1107
|
+
return null;
|
|
1108
|
+
}
|
|
1109
|
+
if (!initMessage.procedureName) {
|
|
1110
|
+
sendCancel({
|
|
1111
|
+
code: INVALID_REQUEST_CODE,
|
|
1112
|
+
message: `missing procedure name in stream open message`
|
|
1113
|
+
});
|
|
1114
|
+
return null;
|
|
1115
|
+
}
|
|
1116
|
+
const route = this.methods.get(
|
|
1117
|
+
methodKey(initMessage.serviceName, initMessage.procedureName)
|
|
1118
|
+
);
|
|
1119
|
+
if (!route) {
|
|
1120
|
+
sendCancel({
|
|
1121
|
+
code: "UNIMPLEMENTED" /* UNIMPLEMENTED */,
|
|
1122
|
+
message: `${initMessage.serviceName}.${initMessage.procedureName} is not implemented`
|
|
1123
|
+
});
|
|
1124
|
+
return null;
|
|
1125
|
+
}
|
|
1126
|
+
const serviceInstance = this.serviceInstances.get(initMessage.serviceName);
|
|
1127
|
+
let initialRequest = null;
|
|
1128
|
+
let closeRequestOnStart = false;
|
|
1129
|
+
if (route.method.methodKind === "unary" || route.method.methodKind === "server_streaming") {
|
|
1130
|
+
if (!(initMessage.payload instanceof Uint8Array)) {
|
|
1131
|
+
sendCancel({
|
|
1132
|
+
code: INVALID_REQUEST_CODE,
|
|
1133
|
+
message: "expected protobuf request payload in opening frame"
|
|
1134
|
+
});
|
|
1135
|
+
return null;
|
|
1136
|
+
}
|
|
1137
|
+
try {
|
|
1138
|
+
initialRequest = decodeMessageBytes(
|
|
1139
|
+
route.method.input,
|
|
1140
|
+
initMessage.payload
|
|
1141
|
+
);
|
|
1142
|
+
} catch {
|
|
1143
|
+
sendCancel({
|
|
1144
|
+
code: INVALID_REQUEST_CODE,
|
|
1145
|
+
message: "failed to decode protobuf request payload"
|
|
1146
|
+
});
|
|
1147
|
+
return null;
|
|
1148
|
+
}
|
|
1149
|
+
if (!isStreamClose(initMessage.controlFlags)) {
|
|
1150
|
+
sendCancel({
|
|
1151
|
+
code: INVALID_REQUEST_CODE,
|
|
1152
|
+
message: "protobuf unary and server-streaming calls must close the request stream in the opening frame"
|
|
1153
|
+
});
|
|
1154
|
+
return null;
|
|
1155
|
+
}
|
|
1156
|
+
closeRequestOnStart = true;
|
|
1157
|
+
} else if (initMessage.payload instanceof Uint8Array) {
|
|
1158
|
+
if (initMessage.payload.byteLength > 0) {
|
|
1159
|
+
try {
|
|
1160
|
+
initialRequest = decodeMessageBytes(
|
|
1161
|
+
route.method.input,
|
|
1162
|
+
initMessage.payload
|
|
1163
|
+
);
|
|
1164
|
+
} catch {
|
|
1165
|
+
sendCancel({
|
|
1166
|
+
code: INVALID_REQUEST_CODE,
|
|
1167
|
+
message: "failed to decode protobuf request payload"
|
|
1168
|
+
});
|
|
1169
|
+
return null;
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
closeRequestOnStart = isStreamClose(initMessage.controlFlags);
|
|
1173
|
+
} else if (Value2.Check(ControlMessageCloseSchema, initMessage.payload) && isStreamClose(initMessage.controlFlags)) {
|
|
1174
|
+
closeRequestOnStart = true;
|
|
1175
|
+
} else {
|
|
1176
|
+
sendCancel({
|
|
1177
|
+
code: INVALID_REQUEST_CODE,
|
|
1178
|
+
message: "received invalid protobuf request payload"
|
|
1179
|
+
});
|
|
1180
|
+
return null;
|
|
1181
|
+
}
|
|
1182
|
+
return {
|
|
1183
|
+
streamId: initMessage.streamId,
|
|
1184
|
+
service: route.service,
|
|
1185
|
+
method: route.method,
|
|
1186
|
+
impl: route.impl,
|
|
1187
|
+
serviceContext: this.userContext,
|
|
1188
|
+
serviceState: serviceInstance?.state ?? {},
|
|
1189
|
+
sessionMetadata,
|
|
1190
|
+
initialSession: session,
|
|
1191
|
+
initialRequest,
|
|
1192
|
+
closeRequestOnStart,
|
|
1193
|
+
tracingCtx: initMessage.tracing
|
|
1194
|
+
};
|
|
1195
|
+
}
|
|
1196
|
+
cancelStream(to, sessionScopedSend, streamId, error) {
|
|
1197
|
+
let cancelledStreamsInSession = this.serverCancelledStreams.get(to);
|
|
1198
|
+
if (!cancelledStreamsInSession) {
|
|
1199
|
+
cancelledStreamsInSession = new LRUSet(
|
|
1200
|
+
this.maxCancelledStreamTombstonesPerSession
|
|
1201
|
+
);
|
|
1202
|
+
this.serverCancelledStreams.set(to, cancelledStreamsInSession);
|
|
1203
|
+
}
|
|
1204
|
+
cancelledStreamsInSession.add(streamId);
|
|
1205
|
+
sessionScopedSend(cancelMessage(streamId, Err(error)));
|
|
1206
|
+
}
|
|
1207
|
+
};
|
|
1208
|
+
function requireInitialRequest(initialRequest, method) {
|
|
1209
|
+
if (initialRequest === null) {
|
|
1210
|
+
throw new Error(
|
|
1211
|
+
`missing initial request for protobuf ${method.parent.typeName}.${method.name}`
|
|
1212
|
+
);
|
|
1213
|
+
}
|
|
1214
|
+
return initialRequest;
|
|
1215
|
+
}
|
|
1216
|
+
var LRUSet = class {
|
|
1217
|
+
constructor(maxItems) {
|
|
1218
|
+
this.maxItems = maxItems;
|
|
1219
|
+
}
|
|
1220
|
+
items = /* @__PURE__ */ new Set();
|
|
1221
|
+
add(item) {
|
|
1222
|
+
if (this.items.has(item)) {
|
|
1223
|
+
this.items.delete(item);
|
|
1224
|
+
} else if (this.items.size >= this.maxItems) {
|
|
1225
|
+
const first = this.items.values().next();
|
|
1226
|
+
if (!first.done) {
|
|
1227
|
+
this.items.delete(first.value);
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
this.items.add(item);
|
|
1231
|
+
}
|
|
1232
|
+
has(item) {
|
|
1233
|
+
return this.items.has(item);
|
|
1234
|
+
}
|
|
1235
|
+
};
|
|
1236
|
+
function createServer(transport, services, options) {
|
|
1237
|
+
return new ProtobufServer(transport, services, options);
|
|
1238
|
+
}
|
|
1239
|
+
export {
|
|
1240
|
+
CANCEL_CODE,
|
|
1241
|
+
Err,
|
|
1242
|
+
INVALID_REQUEST_CODE,
|
|
1243
|
+
Ok,
|
|
1244
|
+
ProtoCodec,
|
|
1245
|
+
ReadableBrokenError,
|
|
1246
|
+
RiverErrorCode,
|
|
1247
|
+
UNCAUGHT_ERROR_CODE,
|
|
1248
|
+
UNEXPECTED_DISCONNECT_CODE,
|
|
1249
|
+
createClient,
|
|
1250
|
+
createClientHandshakeOptions2 as createClientHandshakeOptions,
|
|
1251
|
+
createProtoService,
|
|
1252
|
+
createServer,
|
|
1253
|
+
createServerHandshakeOptions2 as createServerHandshakeOptions,
|
|
1254
|
+
isClientError,
|
|
1255
|
+
isProtocolError,
|
|
1256
|
+
isRiverError,
|
|
1257
|
+
isSerializedClientErrorResult,
|
|
1258
|
+
isSerializedProtocolErrorResult
|
|
1259
|
+
};
|
|
1260
|
+
//# sourceMappingURL=index.js.map
|