@replit/river 0.200.0-rc.1 → 0.200.0-rc.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +21 -20
  2. package/dist/{chunk-XLXRFSRB.js → chunk-3CCOX55A.js} +76 -42
  3. package/dist/chunk-3CCOX55A.js.map +1 -0
  4. package/dist/chunk-GWYJZFCW.js +653 -0
  5. package/dist/chunk-GWYJZFCW.js.map +1 -0
  6. package/dist/chunk-O4O2E6DJ.js +277 -0
  7. package/dist/chunk-O4O2E6DJ.js.map +1 -0
  8. package/dist/chunk-QXLXDJD5.js +382 -0
  9. package/dist/chunk-QXLXDJD5.js.map +1 -0
  10. package/dist/{chunk-WQVOMUNR.js → chunk-UFMDEG44.js} +24 -18
  11. package/dist/chunk-UFMDEG44.js.map +1 -0
  12. package/dist/{chunk-45HIR2AS.js → chunk-UIYGPURD.js} +90 -532
  13. package/dist/chunk-UIYGPURD.js.map +1 -0
  14. package/dist/{chunk-QMM35C3H.js → chunk-VXYHC666.js} +1 -1
  15. package/dist/chunk-VXYHC666.js.map +1 -0
  16. package/dist/chunk-WSCAA7VY.js +50 -0
  17. package/dist/chunk-WSCAA7VY.js.map +1 -0
  18. package/dist/chunk-ZQVKFLAB.js +399 -0
  19. package/dist/chunk-ZQVKFLAB.js.map +1 -0
  20. package/dist/client-a84783be.d.ts +49 -0
  21. package/dist/{connection-c6521735.d.ts → connection-320fb130.d.ts} +1 -5
  22. package/dist/connection-d0b488e6.d.ts +11 -0
  23. package/dist/context-c9668e95.d.ts +527 -0
  24. package/dist/logging/index.cjs.map +1 -1
  25. package/dist/logging/index.d.cts +1 -1
  26. package/dist/logging/index.d.ts +1 -1
  27. package/dist/logging/index.js +1 -1
  28. package/dist/{index-10ebd26a.d.ts → message-fd349b27.d.ts} +31 -31
  29. package/dist/router/index.cjs +142 -544
  30. package/dist/router/index.cjs.map +1 -1
  31. package/dist/router/index.d.cts +11 -46
  32. package/dist/router/index.d.ts +11 -46
  33. package/dist/router/index.js +2 -4
  34. package/dist/server-dbad597e.d.ts +42 -0
  35. package/dist/{services-34d97070.d.ts → services-690e5553.d.ts} +11 -602
  36. package/dist/transport/impls/uds/client.cjs +1240 -1237
  37. package/dist/transport/impls/uds/client.cjs.map +1 -1
  38. package/dist/transport/impls/uds/client.d.cts +4 -3
  39. package/dist/transport/impls/uds/client.d.ts +4 -3
  40. package/dist/transport/impls/uds/client.js +7 -13
  41. package/dist/transport/impls/uds/client.js.map +1 -1
  42. package/dist/transport/impls/uds/server.cjs +1302 -1168
  43. package/dist/transport/impls/uds/server.cjs.map +1 -1
  44. package/dist/transport/impls/uds/server.d.cts +4 -4
  45. package/dist/transport/impls/uds/server.d.ts +4 -4
  46. package/dist/transport/impls/uds/server.js +6 -6
  47. package/dist/transport/impls/ws/client.cjs +981 -986
  48. package/dist/transport/impls/ws/client.cjs.map +1 -1
  49. package/dist/transport/impls/ws/client.d.cts +6 -5
  50. package/dist/transport/impls/ws/client.d.ts +6 -5
  51. package/dist/transport/impls/ws/client.js +6 -7
  52. package/dist/transport/impls/ws/client.js.map +1 -1
  53. package/dist/transport/impls/ws/server.cjs +1183 -1064
  54. package/dist/transport/impls/ws/server.cjs.map +1 -1
  55. package/dist/transport/impls/ws/server.d.cts +4 -4
  56. package/dist/transport/impls/ws/server.d.ts +4 -4
  57. package/dist/transport/impls/ws/server.js +6 -6
  58. package/dist/transport/index.cjs +1435 -1377
  59. package/dist/transport/index.cjs.map +1 -1
  60. package/dist/transport/index.d.cts +4 -26
  61. package/dist/transport/index.d.ts +4 -26
  62. package/dist/transport/index.js +9 -9
  63. package/dist/util/testHelpers.cjs +782 -333
  64. package/dist/util/testHelpers.cjs.map +1 -1
  65. package/dist/util/testHelpers.d.cts +9 -4
  66. package/dist/util/testHelpers.d.ts +9 -4
  67. package/dist/util/testHelpers.js +45 -23
  68. package/dist/util/testHelpers.js.map +1 -1
  69. package/package.json +1 -1
  70. package/dist/chunk-45HIR2AS.js.map +0 -1
  71. package/dist/chunk-75B5D7MR.js +0 -347
  72. package/dist/chunk-75B5D7MR.js.map +0 -1
  73. package/dist/chunk-AJBZUHSI.js +0 -492
  74. package/dist/chunk-AJBZUHSI.js.map +0 -1
  75. package/dist/chunk-EMIERCU7.js +0 -59
  76. package/dist/chunk-EMIERCU7.js.map +0 -1
  77. package/dist/chunk-LH2VHMQM.js +0 -335
  78. package/dist/chunk-LH2VHMQM.js.map +0 -1
  79. package/dist/chunk-QMM35C3H.js.map +0 -1
  80. package/dist/chunk-W75HU4F6.js +0 -476
  81. package/dist/chunk-W75HU4F6.js.map +0 -1
  82. package/dist/chunk-WQVOMUNR.js.map +0 -1
  83. package/dist/chunk-XLXRFSRB.js.map +0 -1
  84. package/dist/connection-0638316b.d.ts +0 -17
@@ -0,0 +1,382 @@
1
+ import {
2
+ ProtocolError,
3
+ Transport
4
+ } from "./chunk-O4O2E6DJ.js";
5
+ import {
6
+ SessionStateGraph,
7
+ defaultServerTransportOptions
8
+ } from "./chunk-GWYJZFCW.js";
9
+ import {
10
+ ControlMessageHandshakeRequestSchema,
11
+ PROTOCOL_VERSION,
12
+ coerceErrorString,
13
+ handshakeResponseMessage
14
+ } from "./chunk-3CCOX55A.js";
15
+
16
+ // transport/server.ts
17
+ import { SpanStatusCode } from "@opentelemetry/api";
18
+ import { Value } from "@sinclair/typebox/value";
19
+ var ServerTransport = class extends Transport {
20
+ /**
21
+ * The options for this transport.
22
+ */
23
+ options;
24
+ /**
25
+ * Optional handshake options for the server.
26
+ */
27
+ handshakeExtensions;
28
+ /**
29
+ * A map of session handshake data for each session.
30
+ */
31
+ sessionHandshakeMetadata = /* @__PURE__ */ new Map();
32
+ pendingSessions = /* @__PURE__ */ new Set();
33
+ constructor(clientId, providedOptions) {
34
+ super(clientId, providedOptions);
35
+ this.options = {
36
+ ...defaultServerTransportOptions,
37
+ ...providedOptions
38
+ };
39
+ this.log?.info(`initiated server transport`, {
40
+ clientId: this.clientId,
41
+ protocolVersion: PROTOCOL_VERSION
42
+ });
43
+ }
44
+ extendHandshake(options) {
45
+ this.handshakeExtensions = options;
46
+ }
47
+ send(to, msg) {
48
+ if (this.getStatus() === "closed") {
49
+ const err = "transport is closed, cant send";
50
+ this.log?.error(err, {
51
+ clientId: this.clientId,
52
+ transportMessage: msg,
53
+ tags: ["invariant-violation"]
54
+ });
55
+ throw new Error(err);
56
+ }
57
+ const session = this.sessions.get(to);
58
+ if (!session) {
59
+ const err = `session to ${to} does not exist`;
60
+ this.log?.error(err, {
61
+ clientId: this.clientId,
62
+ transportMessage: msg,
63
+ tags: ["invariant-violation"]
64
+ });
65
+ throw new Error(err);
66
+ }
67
+ return session.send(msg);
68
+ }
69
+ deletePendingSession(pendingSession) {
70
+ pendingSession.close();
71
+ this.pendingSessions.delete(pendingSession);
72
+ }
73
+ deleteSession(session) {
74
+ this.sessionHandshakeMetadata.delete(session.to);
75
+ super.deleteSession(session);
76
+ }
77
+ handleConnection(conn) {
78
+ if (this.getStatus() !== "open")
79
+ return;
80
+ this.log?.info(`new incoming connection`, {
81
+ ...conn.loggingMetadata,
82
+ clientId: this.clientId
83
+ });
84
+ let receivedHandshake = false;
85
+ const pendingSession = SessionStateGraph.entrypoints.WaitingForHandshake(
86
+ this.clientId,
87
+ conn,
88
+ {
89
+ onConnectionClosed: () => {
90
+ this.log?.warn(
91
+ `connection from unknown closed before handshake finished`,
92
+ pendingSession.loggingMetadata
93
+ );
94
+ this.deletePendingSession(pendingSession);
95
+ },
96
+ onConnectionErrored: (err) => {
97
+ const errorString = coerceErrorString(err);
98
+ this.log?.warn(
99
+ `connection from unknown errored before handshake finished: ${errorString}`,
100
+ pendingSession.loggingMetadata
101
+ );
102
+ this.deletePendingSession(pendingSession);
103
+ },
104
+ onHandshakeTimeout: () => {
105
+ this.log?.warn(
106
+ `connection from unknown timed out before handshake finished`,
107
+ pendingSession.loggingMetadata
108
+ );
109
+ this.deletePendingSession(pendingSession);
110
+ },
111
+ onHandshake: (msg) => {
112
+ if (receivedHandshake) {
113
+ this.log?.error(
114
+ `received multiple handshake messages from pending session`,
115
+ {
116
+ ...pendingSession.loggingMetadata,
117
+ connectedTo: msg.from,
118
+ transportMessage: msg
119
+ }
120
+ );
121
+ this.deletePendingSession(pendingSession);
122
+ return;
123
+ }
124
+ receivedHandshake = true;
125
+ void this.onHandshakeRequest(pendingSession, msg);
126
+ },
127
+ onInvalidHandshake: (reason) => {
128
+ this.log?.error(
129
+ `invalid handshake: ${reason}`,
130
+ pendingSession.loggingMetadata
131
+ );
132
+ this.deletePendingSession(pendingSession);
133
+ this.protocolError(ProtocolError.HandshakeFailed, reason);
134
+ }
135
+ },
136
+ this.options,
137
+ this.log
138
+ );
139
+ this.pendingSessions.add(pendingSession);
140
+ }
141
+ rejectHandshakeRequest(session, to, reason, code, metadata) {
142
+ session.conn.telemetry?.span.setStatus({
143
+ code: SpanStatusCode.ERROR,
144
+ message: reason
145
+ });
146
+ this.log?.warn(reason, metadata);
147
+ session.sendHandshake(
148
+ handshakeResponseMessage({
149
+ from: this.clientId,
150
+ to,
151
+ status: {
152
+ ok: false,
153
+ code,
154
+ reason
155
+ }
156
+ })
157
+ );
158
+ this.protocolError(ProtocolError.HandshakeFailed, reason);
159
+ this.deletePendingSession(session);
160
+ }
161
+ async onHandshakeRequest(session, msg) {
162
+ if (!Value.Check(ControlMessageHandshakeRequestSchema, msg.payload)) {
163
+ this.rejectHandshakeRequest(
164
+ session,
165
+ msg.from,
166
+ "received invalid handshake request",
167
+ "MALFORMED_HANDSHAKE",
168
+ {
169
+ ...session.loggingMetadata,
170
+ transportMessage: msg,
171
+ connectedTo: msg.from,
172
+ validationErrors: [
173
+ ...Value.Errors(ControlMessageHandshakeRequestSchema, msg.payload)
174
+ ]
175
+ }
176
+ );
177
+ return;
178
+ }
179
+ const gotVersion = msg.payload.protocolVersion;
180
+ if (gotVersion !== PROTOCOL_VERSION) {
181
+ this.rejectHandshakeRequest(
182
+ session,
183
+ msg.from,
184
+ `expected protocol version ${PROTOCOL_VERSION}, got ${gotVersion}`,
185
+ "PROTOCOL_VERSION_MISMATCH",
186
+ {
187
+ ...session.loggingMetadata,
188
+ connectedTo: msg.from,
189
+ transportMessage: msg
190
+ }
191
+ );
192
+ return;
193
+ }
194
+ let oldSession = this.sessions.get(msg.from);
195
+ const parsedMetadata = await this.validateHandshakeMetadata(
196
+ session,
197
+ oldSession,
198
+ msg.payload.metadata,
199
+ msg.from
200
+ );
201
+ if (parsedMetadata === false) {
202
+ return;
203
+ }
204
+ let connectCase = "new session";
205
+ if (oldSession && oldSession.id === msg.payload.sessionId) {
206
+ connectCase = "transparent reconnection";
207
+ const clientNextExpectedSeq = msg.payload.expectedSessionState.nextExpectedSeq;
208
+ const clientNextSentSeq = msg.payload.expectedSessionState.nextSentSeq ?? 0;
209
+ const ourNextSeq = oldSession.nextSeq();
210
+ const ourAck = oldSession.ack;
211
+ if (clientNextSentSeq > ourAck) {
212
+ this.rejectHandshakeRequest(
213
+ session,
214
+ msg.from,
215
+ `client is in the future: server wanted next message to be ${ourAck} but client would have sent ${clientNextSentSeq}`,
216
+ "SESSION_STATE_MISMATCH",
217
+ {
218
+ ...session.loggingMetadata,
219
+ connectedTo: msg.from,
220
+ transportMessage: msg
221
+ }
222
+ );
223
+ return;
224
+ }
225
+ if (ourNextSeq > clientNextExpectedSeq) {
226
+ this.rejectHandshakeRequest(
227
+ session,
228
+ msg.from,
229
+ `server is in the future: client wanted next message to be ${clientNextExpectedSeq} but server would have sent ${ourNextSeq}`,
230
+ "SESSION_STATE_MISMATCH",
231
+ {
232
+ ...session.loggingMetadata,
233
+ connectedTo: msg.from,
234
+ transportMessage: msg
235
+ }
236
+ );
237
+ return;
238
+ }
239
+ if (oldSession.state === "Connected" /* Connected */) {
240
+ const noConnectionSession = SessionStateGraph.transition.ConnectedToNoConnection(oldSession, {
241
+ onSessionGracePeriodElapsed: () => {
242
+ this.onSessionGracePeriodElapsed(noConnectionSession);
243
+ }
244
+ });
245
+ oldSession = noConnectionSession;
246
+ } else if (oldSession.state === "Handshaking" /* Handshaking */) {
247
+ const noConnectionSession = SessionStateGraph.transition.HandshakingToNoConnection(oldSession, {
248
+ onSessionGracePeriodElapsed: () => {
249
+ this.onSessionGracePeriodElapsed(noConnectionSession);
250
+ }
251
+ });
252
+ oldSession = noConnectionSession;
253
+ } else if (oldSession.state === "Connecting" /* Connecting */) {
254
+ const noConnectionSession = SessionStateGraph.transition.ConnectingToNoConnection(oldSession, {
255
+ onSessionGracePeriodElapsed: () => {
256
+ this.onSessionGracePeriodElapsed(noConnectionSession);
257
+ }
258
+ });
259
+ oldSession = noConnectionSession;
260
+ }
261
+ this.updateSession(oldSession);
262
+ } else if (oldSession) {
263
+ connectCase = "hard reconnection";
264
+ this.deleteSession(oldSession);
265
+ oldSession = void 0;
266
+ } else {
267
+ connectCase = "unknown session";
268
+ const clientNextExpectedSeq = msg.payload.expectedSessionState.nextExpectedSeq;
269
+ const clientNextSentSeq = msg.payload.expectedSessionState.nextSentSeq ?? 0;
270
+ if (clientNextSentSeq > 0 || clientNextExpectedSeq > 0) {
271
+ this.rejectHandshakeRequest(
272
+ session,
273
+ msg.from,
274
+ `client is trying to reconnect to a session the server don't know about: ${msg.payload.sessionId}`,
275
+ "SESSION_STATE_MISMATCH",
276
+ {
277
+ ...session.loggingMetadata,
278
+ connectedTo: msg.from,
279
+ transportMessage: msg
280
+ }
281
+ );
282
+ return;
283
+ }
284
+ }
285
+ const sessionId = msg.payload.sessionId;
286
+ this.log?.info(
287
+ `handshake from ${msg.from} ok (${connectCase}), responding with handshake success`,
288
+ {
289
+ ...session.loggingMetadata,
290
+ connectedTo: msg.from
291
+ }
292
+ );
293
+ const responseMsg = handshakeResponseMessage({
294
+ from: this.clientId,
295
+ to: msg.from,
296
+ status: {
297
+ ok: true,
298
+ sessionId
299
+ }
300
+ });
301
+ session.sendHandshake(responseMsg);
302
+ const connectedSession = SessionStateGraph.transition.WaitingForHandshakeToConnected(
303
+ session,
304
+ // by this point oldSession is either no connection or we dont have an old session
305
+ oldSession,
306
+ sessionId,
307
+ msg.from,
308
+ msg.tracing,
309
+ {
310
+ onConnectionErrored: (err) => {
311
+ const errStr = coerceErrorString(err);
312
+ this.log?.warn(
313
+ `connection to ${connectedSession.to} errored: ${errStr}`,
314
+ connectedSession.loggingMetadata
315
+ );
316
+ },
317
+ onConnectionClosed: () => {
318
+ this.log?.info(
319
+ `connection to ${connectedSession.to} closed`,
320
+ connectedSession.loggingMetadata
321
+ );
322
+ this.onConnClosed(connectedSession);
323
+ },
324
+ onMessage: (msg2) => this.handleMsg(msg2),
325
+ onInvalidMessage: (reason) => {
326
+ this.protocolError(ProtocolError.MessageOrderingViolated, reason);
327
+ this.deleteSession(connectedSession);
328
+ }
329
+ }
330
+ );
331
+ this.sessionHandshakeMetadata.set(connectedSession.to, parsedMetadata);
332
+ this.updateSession(connectedSession);
333
+ this.pendingSessions.delete(session);
334
+ connectedSession.startActiveHeartbeat();
335
+ }
336
+ async validateHandshakeMetadata(handshakingSession, existingSession, rawMetadata, from) {
337
+ let parsedMetadata = {};
338
+ if (this.handshakeExtensions) {
339
+ if (!Value.Check(this.handshakeExtensions.schema, rawMetadata)) {
340
+ this.rejectHandshakeRequest(
341
+ handshakingSession,
342
+ from,
343
+ "received malformed handshake metadata",
344
+ "MALFORMED_HANDSHAKE_META",
345
+ {
346
+ ...handshakingSession.loggingMetadata,
347
+ connectedTo: from,
348
+ validationErrors: [
349
+ ...Value.Errors(this.handshakeExtensions.schema, rawMetadata)
350
+ ]
351
+ }
352
+ );
353
+ return false;
354
+ }
355
+ const previousParsedMetadata = existingSession ? this.sessionHandshakeMetadata.get(existingSession.to) : void 0;
356
+ parsedMetadata = await this.handshakeExtensions.validate(
357
+ rawMetadata,
358
+ previousParsedMetadata
359
+ );
360
+ if (parsedMetadata === false) {
361
+ this.rejectHandshakeRequest(
362
+ handshakingSession,
363
+ from,
364
+ "rejected by handshake handler",
365
+ "REJECTED_BY_CUSTOM_HANDLER",
366
+ {
367
+ ...handshakingSession.loggingMetadata,
368
+ connectedTo: from,
369
+ clientId: this.clientId
370
+ }
371
+ );
372
+ return false;
373
+ }
374
+ }
375
+ return parsedMetadata;
376
+ }
377
+ };
378
+
379
+ export {
380
+ ServerTransport
381
+ };
382
+ //# sourceMappingURL=chunk-QXLXDJD5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../transport/server.ts"],"sourcesContent":["import { SpanStatusCode } from '@opentelemetry/api';\nimport { ParsedMetadata } from '../router/context';\nimport { ServerHandshakeOptions } from '../router/handshake';\nimport {\n ControlMessageHandshakeRequestSchema,\n HandshakeErrorResponseCodes,\n OpaqueTransportMessage,\n PROTOCOL_VERSION,\n PartialTransportMessage,\n TransportClientId,\n handshakeResponseMessage,\n} from './message';\nimport {\n ProvidedServerTransportOptions,\n ServerTransportOptions,\n defaultServerTransportOptions,\n} from './options';\nimport { Transport } from './transport';\nimport { coerceErrorString } from '../util/stringify';\nimport { Static } from '@sinclair/typebox';\nimport { Value } from '@sinclair/typebox/value';\nimport { ProtocolError } from './events';\nimport { Connection } from './connection';\nimport { MessageMetadata } from '../logging';\nimport { SessionWaitingForHandshake } from './sessionStateMachine/SessionWaitingForHandshake';\nimport { Session, SessionState } from './sessionStateMachine/common';\nimport { SessionStateGraph } from './sessionStateMachine/transitions';\n\nexport abstract class ServerTransport<\n ConnType extends Connection,\n> extends Transport<ConnType> {\n /**\n * The options for this transport.\n */\n protected options: ServerTransportOptions;\n\n /**\n * Optional handshake options for the server.\n */\n handshakeExtensions?: ServerHandshakeOptions;\n\n /**\n * A map of session handshake data for each session.\n */\n sessionHandshakeMetadata = new Map<TransportClientId, ParsedMetadata>();\n pendingSessions = new Set<SessionWaitingForHandshake<ConnType>>();\n\n constructor(\n clientId: TransportClientId,\n providedOptions?: ProvidedServerTransportOptions,\n ) {\n super(clientId, providedOptions);\n this.options = {\n ...defaultServerTransportOptions,\n ...providedOptions,\n };\n this.log?.info(`initiated server transport`, {\n clientId: this.clientId,\n protocolVersion: PROTOCOL_VERSION,\n });\n }\n\n extendHandshake(options: ServerHandshakeOptions) {\n this.handshakeExtensions = options;\n }\n\n send(to: string, msg: PartialTransportMessage): string {\n if (this.getStatus() === 'closed') {\n const err = 'transport is closed, cant send';\n this.log?.error(err, {\n clientId: this.clientId,\n transportMessage: msg,\n tags: ['invariant-violation'],\n });\n\n throw new Error(err);\n }\n\n const session = this.sessions.get(to);\n if (!session) {\n const err = `session to ${to} does not exist`;\n this.log?.error(err, {\n clientId: this.clientId,\n transportMessage: msg,\n tags: ['invariant-violation'],\n });\n\n throw new Error(err);\n }\n\n return session.send(msg);\n }\n\n protected deletePendingSession(\n pendingSession: SessionWaitingForHandshake<ConnType>,\n ) {\n pendingSession.close();\n // we don't dispatch a session disconnect event\n // for a non-identified session, just delete directly\n\n this.pendingSessions.delete(pendingSession);\n }\n\n protected deleteSession(session: Session<ConnType>): void {\n this.sessionHandshakeMetadata.delete(session.to);\n super.deleteSession(session);\n }\n\n protected handleConnection(conn: ConnType) {\n if (this.getStatus() !== 'open') return;\n\n this.log?.info(`new incoming connection`, {\n ...conn.loggingMetadata,\n clientId: this.clientId,\n });\n\n let receivedHandshake = false;\n const pendingSession = SessionStateGraph.entrypoints.WaitingForHandshake(\n this.clientId,\n conn,\n {\n onConnectionClosed: () => {\n this.log?.warn(\n `connection from unknown closed before handshake finished`,\n pendingSession.loggingMetadata,\n );\n\n this.deletePendingSession(pendingSession);\n },\n onConnectionErrored: (err) => {\n const errorString = coerceErrorString(err);\n this.log?.warn(\n `connection from unknown errored before handshake finished: ${errorString}`,\n pendingSession.loggingMetadata,\n );\n\n this.deletePendingSession(pendingSession);\n },\n onHandshakeTimeout: () => {\n this.log?.warn(\n `connection from unknown timed out before handshake finished`,\n pendingSession.loggingMetadata,\n );\n\n this.deletePendingSession(pendingSession);\n },\n onHandshake: (msg) => {\n if (receivedHandshake) {\n this.log?.error(\n `received multiple handshake messages from pending session`,\n {\n ...pendingSession.loggingMetadata,\n connectedTo: msg.from,\n transportMessage: msg,\n },\n );\n\n this.deletePendingSession(pendingSession);\n return;\n }\n\n // let this resolve async, we just need to make sure its only\n // called once so we don't race while transitioning to connected\n // onHandshakeRequest is async as custom validation may be async\n receivedHandshake = true;\n void this.onHandshakeRequest(pendingSession, msg);\n },\n onInvalidHandshake: (reason) => {\n this.log?.error(\n `invalid handshake: ${reason}`,\n pendingSession.loggingMetadata,\n );\n this.deletePendingSession(pendingSession);\n this.protocolError(ProtocolError.HandshakeFailed, reason);\n },\n },\n this.options,\n this.log,\n );\n\n this.pendingSessions.add(pendingSession);\n }\n\n private rejectHandshakeRequest(\n session: SessionWaitingForHandshake<ConnType>,\n to: TransportClientId,\n reason: string,\n code: Static<typeof HandshakeErrorResponseCodes>,\n metadata: MessageMetadata,\n ) {\n session.conn.telemetry?.span.setStatus({\n code: SpanStatusCode.ERROR,\n message: reason,\n });\n\n this.log?.warn(reason, metadata);\n\n session.sendHandshake(\n handshakeResponseMessage({\n from: this.clientId,\n to,\n status: {\n ok: false,\n code,\n reason,\n },\n }),\n );\n\n this.protocolError(ProtocolError.HandshakeFailed, reason);\n this.deletePendingSession(session);\n }\n\n protected async onHandshakeRequest(\n session: SessionWaitingForHandshake<ConnType>,\n msg: OpaqueTransportMessage,\n ) {\n // invariant: msg is a handshake request\n if (!Value.Check(ControlMessageHandshakeRequestSchema, msg.payload)) {\n this.rejectHandshakeRequest(\n session,\n msg.from,\n 'received invalid handshake request',\n 'MALFORMED_HANDSHAKE',\n {\n ...session.loggingMetadata,\n transportMessage: msg,\n connectedTo: msg.from,\n validationErrors: [\n ...Value.Errors(ControlMessageHandshakeRequestSchema, msg.payload),\n ],\n },\n );\n\n return;\n }\n\n // invariant: handshake request passes all the validation\n const gotVersion = msg.payload.protocolVersion;\n if (gotVersion !== PROTOCOL_VERSION) {\n this.rejectHandshakeRequest(\n session,\n msg.from,\n `expected protocol version ${PROTOCOL_VERSION}, got ${gotVersion}`,\n 'PROTOCOL_VERSION_MISMATCH',\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n transportMessage: msg,\n },\n );\n\n return;\n }\n\n let oldSession = this.sessions.get(msg.from);\n\n // invariant: must pass custom validation if defined\n const parsedMetadata = await this.validateHandshakeMetadata(\n session,\n oldSession,\n msg.payload.metadata,\n msg.from,\n );\n\n if (parsedMetadata === false) {\n return;\n }\n\n // 4 connect cases\n // 1. new session\n // we dont have a session and the client is requesting a new one\n // we can create the session as normal\n // 2. client is reconnecting to an existing session but we don't have it\n // reject this handshake, there's nothing we can do to salvage it\n // 3. transparent reconnect (old session exists and is the same as the client wants)\n // assign to old session\n // 4. hard reconnect (oldSession exists but but the client wants a new one)\n // we close the old session and create a new one\n let connectCase:\n | 'new session'\n | 'unknown session'\n | 'transparent reconnection'\n | 'hard reconnection' = 'new session';\n if (oldSession && oldSession.id === msg.payload.sessionId) {\n connectCase = 'transparent reconnection';\n\n // invariant: ordering must be correct\n const clientNextExpectedSeq =\n msg.payload.expectedSessionState.nextExpectedSeq;\n // TODO: remove nullish coalescing when we're sure this is always set\n const clientNextSentSeq =\n msg.payload.expectedSessionState.nextSentSeq ?? 0;\n const ourNextSeq = oldSession.nextSeq();\n const ourAck = oldSession.ack;\n\n // two incorrect cases where we cannot permit a reconnect:\n // - if the client is about to send a message in the future w.r.t to the server\n // - client.seq > server.ack => nextSentSeq > oldSession.ack\n if (clientNextSentSeq > ourAck) {\n this.rejectHandshakeRequest(\n session,\n msg.from,\n `client is in the future: server wanted next message to be ${ourAck} but client would have sent ${clientNextSentSeq}`,\n 'SESSION_STATE_MISMATCH',\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n transportMessage: msg,\n },\n );\n\n return;\n }\n\n // - if the server is about to send a message in the future w.r.t to the client\n // - server.seq > client.ack => oldSession.nextSeq() > nextExpectedSeq\n if (ourNextSeq > clientNextExpectedSeq) {\n this.rejectHandshakeRequest(\n session,\n msg.from,\n `server is in the future: client wanted next message to be ${clientNextExpectedSeq} but server would have sent ${ourNextSeq}`,\n 'SESSION_STATE_MISMATCH',\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n transportMessage: msg,\n },\n );\n\n return;\n }\n\n // transparent reconnect seems ok, proceed by transitioning old session\n // to not connected\n if (oldSession.state === SessionState.Connected) {\n const noConnectionSession =\n SessionStateGraph.transition.ConnectedToNoConnection(oldSession, {\n onSessionGracePeriodElapsed: () => {\n this.onSessionGracePeriodElapsed(noConnectionSession);\n },\n });\n\n oldSession = noConnectionSession;\n } else if (oldSession.state === SessionState.Handshaking) {\n const noConnectionSession =\n SessionStateGraph.transition.HandshakingToNoConnection(oldSession, {\n onSessionGracePeriodElapsed: () => {\n this.onSessionGracePeriodElapsed(noConnectionSession);\n },\n });\n\n oldSession = noConnectionSession;\n } else if (oldSession.state === SessionState.Connecting) {\n const noConnectionSession =\n SessionStateGraph.transition.ConnectingToNoConnection(oldSession, {\n onSessionGracePeriodElapsed: () => {\n this.onSessionGracePeriodElapsed(noConnectionSession);\n },\n });\n\n oldSession = noConnectionSession;\n }\n\n this.updateSession(oldSession);\n } else if (oldSession) {\n connectCase = 'hard reconnection';\n\n // just nuke the old session entirely and proceed as if this was new\n this.deleteSession(oldSession);\n oldSession = undefined;\n } else {\n connectCase = 'unknown session';\n\n const clientNextExpectedSeq =\n msg.payload.expectedSessionState.nextExpectedSeq;\n // TODO: remove nullish coalescing when we're sure this is always set\n const clientNextSentSeq =\n msg.payload.expectedSessionState.nextSentSeq ?? 0;\n\n if (clientNextSentSeq > 0 || clientNextExpectedSeq > 0) {\n // we don't have a session, but the client is trying to reconnect\n // to an old session. we can't do anything about this, so we reject\n this.rejectHandshakeRequest(\n session,\n msg.from,\n `client is trying to reconnect to a session the server don't know about: ${msg.payload.sessionId}`,\n 'SESSION_STATE_MISMATCH',\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n transportMessage: msg,\n },\n );\n return;\n }\n }\n\n // from this point on, we're committed to connecting\n const sessionId = msg.payload.sessionId;\n this.log?.info(\n `handshake from ${msg.from} ok (${connectCase}), responding with handshake success`,\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n },\n );\n const responseMsg = handshakeResponseMessage({\n from: this.clientId,\n to: msg.from,\n status: {\n ok: true,\n sessionId,\n },\n });\n session.sendHandshake(responseMsg);\n\n // transition\n const connectedSession =\n SessionStateGraph.transition.WaitingForHandshakeToConnected(\n session,\n // by this point oldSession is either no connection or we dont have an old session\n oldSession,\n sessionId,\n msg.from,\n msg.tracing,\n {\n onConnectionErrored: (err) => {\n // just log, when we error we also emit close\n const errStr = coerceErrorString(err);\n this.log?.warn(\n `connection to ${connectedSession.to} errored: ${errStr}`,\n connectedSession.loggingMetadata,\n );\n },\n onConnectionClosed: () => {\n this.log?.info(\n `connection to ${connectedSession.to} closed`,\n connectedSession.loggingMetadata,\n );\n this.onConnClosed(connectedSession);\n },\n onMessage: (msg) => this.handleMsg(msg),\n onInvalidMessage: (reason) => {\n this.protocolError(ProtocolError.MessageOrderingViolated, reason);\n this.deleteSession(connectedSession);\n },\n },\n );\n\n this.sessionHandshakeMetadata.set(connectedSession.to, parsedMetadata);\n this.updateSession(connectedSession);\n this.pendingSessions.delete(session);\n connectedSession.startActiveHeartbeat();\n }\n\n private async validateHandshakeMetadata(\n handshakingSession: SessionWaitingForHandshake<ConnType>,\n existingSession: Session<ConnType> | undefined,\n rawMetadata: Static<\n typeof ControlMessageHandshakeRequestSchema\n >['metadata'],\n from: TransportClientId,\n ): Promise<ParsedMetadata | false> {\n let parsedMetadata: ParsedMetadata = {};\n if (this.handshakeExtensions) {\n // check that the metadata that was sent is the correct shape\n if (!Value.Check(this.handshakeExtensions.schema, rawMetadata)) {\n this.rejectHandshakeRequest(\n handshakingSession,\n from,\n 'received malformed handshake metadata',\n 'MALFORMED_HANDSHAKE_META',\n {\n ...handshakingSession.loggingMetadata,\n connectedTo: from,\n validationErrors: [\n ...Value.Errors(this.handshakeExtensions.schema, rawMetadata),\n ],\n },\n );\n\n return false;\n }\n\n const previousParsedMetadata = existingSession\n ? this.sessionHandshakeMetadata.get(existingSession.to)\n : undefined;\n\n parsedMetadata = await this.handshakeExtensions.validate(\n rawMetadata,\n previousParsedMetadata,\n );\n\n // handler rejected the connection\n if (parsedMetadata === false) {\n this.rejectHandshakeRequest(\n handshakingSession,\n from,\n 'rejected by handshake handler',\n 'REJECTED_BY_CUSTOM_HANDLER',\n {\n ...handshakingSession.loggingMetadata,\n connectedTo: from,\n clientId: this.clientId,\n },\n );\n\n return false;\n }\n }\n\n return parsedMetadata;\n }\n}\n"],"mappings":"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","names":["msg"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Connection
3
- } from "./chunk-75B5D7MR.js";
3
+ } from "./chunk-O4O2E6DJ.js";
4
4
 
5
5
  // transport/transforms/messageFraming.ts
6
6
  import { Transform } from "node:stream";
@@ -71,36 +71,42 @@ var UdsConnection = class extends Connection {
71
71
  this.framer = MessageFramer.createFramedStream();
72
72
  this.sock = sock;
73
73
  this.input = sock.pipe(this.framer);
74
- }
75
- addDataListener(cb) {
76
- this.input.on("data", cb);
77
- }
78
- removeDataListener(cb) {
79
- this.input.off("data", cb);
80
- }
81
- addCloseListener(cb) {
82
- this.sock.on("close", cb);
83
- }
84
- addErrorListener(cb) {
74
+ this.sock.on("close", () => {
75
+ for (const cb of this.closeListeners) {
76
+ cb();
77
+ }
78
+ });
85
79
  this.sock.on("error", (err) => {
86
80
  if (err instanceof Error && "code" in err && err.code === "EPIPE") {
87
81
  return;
88
82
  }
89
- cb(err);
83
+ for (const cb of this.errorListeners) {
84
+ cb(err);
85
+ }
86
+ });
87
+ this.input.on("data", (msg) => {
88
+ for (const cb of this.dataListeners) {
89
+ cb(msg);
90
+ }
91
+ });
92
+ this.sock.on("end", () => {
93
+ this.sock.destroy();
90
94
  });
91
95
  }
92
96
  send(payload) {
93
- if (this.framer.destroyed || !this.sock.writable)
97
+ if (this.framer.destroyed || !this.sock.writable || this.sock.closed) {
94
98
  return false;
95
- return this.sock.write(MessageFramer.write(payload));
99
+ }
100
+ this.sock.write(MessageFramer.write(payload));
101
+ return true;
96
102
  }
97
103
  close() {
98
- this.sock.destroy();
99
- this.framer.destroy();
104
+ this.sock.end();
105
+ this.framer.end();
100
106
  }
101
107
  };
102
108
 
103
109
  export {
104
110
  UdsConnection
105
111
  };
106
- //# sourceMappingURL=chunk-WQVOMUNR.js.map
112
+ //# sourceMappingURL=chunk-UFMDEG44.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../transport/transforms/messageFraming.ts","../transport/impls/uds/connection.ts"],"sourcesContent":["import { Transform, TransformCallback, TransformOptions } from 'node:stream';\n\nexport interface LengthEncodedOptions extends TransformOptions {\n /** Maximum in-memory buffer size before we throw */\n maxBufferSizeBytes: number;\n}\n\n/**\n * A transform stream that emits data each time a message with a network/BigEndian uint32 length prefix is received.\n * @extends Transform\n */\nexport class Uint32LengthPrefixFraming extends Transform {\n receivedBuffer: Buffer;\n maxBufferSizeBytes: number;\n\n constructor({ maxBufferSizeBytes, ...options }: LengthEncodedOptions) {\n super(options);\n this.maxBufferSizeBytes = maxBufferSizeBytes;\n this.receivedBuffer = Buffer.alloc(0);\n }\n\n _transform(chunk: Buffer, _encoding: BufferEncoding, cb: TransformCallback) {\n if (\n this.receivedBuffer.byteLength + chunk.byteLength >\n this.maxBufferSizeBytes\n ) {\n const err = new Error(\n `buffer overflow: ${this.receivedBuffer.byteLength}B > ${this.maxBufferSizeBytes}B`,\n );\n\n this.emit('error', err);\n cb(err);\n return;\n }\n\n this.receivedBuffer = Buffer.concat([this.receivedBuffer, chunk]);\n\n // ensure there's enough for a length prefix\n while (this.receivedBuffer.length > 4) {\n // read length from buffer (accounting for uint32 prefix)\n const claimedMessageLength = this.receivedBuffer.readUInt32BE(0) + 4;\n if (this.receivedBuffer.length >= claimedMessageLength) {\n // slice the buffer to extract the message\n const message = this.receivedBuffer.subarray(4, claimedMessageLength);\n this.push(message);\n this.receivedBuffer =\n this.receivedBuffer.subarray(claimedMessageLength);\n } else {\n // not enough data for a complete message, wait for more data\n break;\n }\n }\n\n cb();\n }\n\n _flush(cb: TransformCallback) {\n // if there's any leftover data that doesn't form a complete message\n if (this.receivedBuffer.length) {\n this.emit('error', new Error('got incomplete message while flushing'));\n }\n\n this.receivedBuffer = Buffer.alloc(0);\n cb();\n }\n\n _destroy(error: Error | null, callback: (error: Error | null) => void): void {\n this.receivedBuffer = Buffer.alloc(0);\n super._destroy(error, callback);\n }\n}\n\nfunction createLengthEncodedStream(options?: Partial<LengthEncodedOptions>) {\n return new Uint32LengthPrefixFraming({\n maxBufferSizeBytes: options?.maxBufferSizeBytes ?? 16 * 1024 * 1024, // 16MB\n });\n}\n\nexport const MessageFramer = {\n createFramedStream: createLengthEncodedStream,\n write: (buf: Uint8Array) => {\n const lengthPrefix = Buffer.alloc(4);\n lengthPrefix.writeUInt32BE(buf.length, 0);\n return Buffer.concat([lengthPrefix, buf]);\n },\n};\n","import { type Socket } from 'node:net';\nimport stream from 'node:stream';\nimport {\n MessageFramer,\n Uint32LengthPrefixFraming,\n} from '../../transforms/messageFraming';\nimport { Connection } from '../../connection';\n\nexport class UdsConnection extends Connection {\n sock: Socket;\n input: stream.Readable;\n framer: Uint32LengthPrefixFraming;\n constructor(sock: Socket) {\n super();\n this.framer = MessageFramer.createFramedStream();\n this.sock = sock;\n this.input = sock.pipe(this.framer);\n\n this.sock.on('close', () => {\n for (const cb of this.closeListeners) {\n cb();\n }\n });\n\n this.sock.on('error', (err) => {\n if (err instanceof Error && 'code' in err && err.code === 'EPIPE') {\n // Ignore EPIPE errors\n return;\n }\n\n for (const cb of this.errorListeners) {\n cb(err);\n }\n });\n\n this.input.on('data', (msg: Uint8Array) => {\n for (const cb of this.dataListeners) {\n cb(msg);\n }\n });\n\n this.sock.on('end', () => {\n this.sock.destroy();\n });\n }\n\n send(payload: Uint8Array) {\n if (this.framer.destroyed || !this.sock.writable || this.sock.closed) {\n return false;\n }\n this.sock.write(MessageFramer.write(payload));\n return true;\n }\n\n close() {\n this.sock.end();\n this.framer.end();\n }\n}\n"],"mappings":"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","names":[]}