@replit/river 0.200.0-rc.9 → 0.200.2

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 (72) hide show
  1. package/README.md +8 -8
  2. package/dist/{chunk-42Z2FQIU.js → chunk-6BH2CXVE.js} +21 -13
  3. package/dist/chunk-6BH2CXVE.js.map +1 -0
  4. package/dist/{chunk-4HT6P2ZG.js → chunk-A4JKES5A.js} +22 -30
  5. package/dist/chunk-A4JKES5A.js.map +1 -0
  6. package/dist/{chunk-4PVU7J25.js → chunk-AJGIY2UB.js} +1 -1
  7. package/dist/chunk-AJGIY2UB.js.map +1 -0
  8. package/dist/{chunk-EETL2L77.js → chunk-GJUUVID2.js} +14 -32
  9. package/dist/chunk-GJUUVID2.js.map +1 -0
  10. package/dist/{chunk-GR3AQKHL.js → chunk-HRKM7BIE.js} +14 -4
  11. package/dist/chunk-HRKM7BIE.js.map +1 -0
  12. package/dist/{chunk-ZXZE253M.js → chunk-PJB2Y2AV.js} +24 -37
  13. package/dist/chunk-PJB2Y2AV.js.map +1 -0
  14. package/dist/{chunk-I75XYO5W.js → chunk-QIDEN5PP.js} +82 -20
  15. package/dist/chunk-QIDEN5PP.js.map +1 -0
  16. package/dist/{chunk-VXYHC666.js → chunk-YTMS7OP6.js} +1 -1
  17. package/dist/chunk-YTMS7OP6.js.map +1 -0
  18. package/dist/chunk-Z4PX66JO.js +307 -0
  19. package/dist/chunk-Z4PX66JO.js.map +1 -0
  20. package/dist/{client-22a47343.d.ts → client-9292552a.d.ts} +3 -4
  21. package/dist/codec/index.cjs.map +1 -1
  22. package/dist/codec/index.js +1 -1
  23. package/dist/connection-94dea547.d.ts +32 -0
  24. package/dist/{context-b4aff18f.d.ts → context-69f37ac1.d.ts} +48 -43
  25. package/dist/logging/index.cjs.map +1 -1
  26. package/dist/logging/index.d.cts +1 -1
  27. package/dist/logging/index.d.ts +1 -1
  28. package/dist/logging/index.js +1 -1
  29. package/dist/{message-7d135e38.d.ts → message-57bb8187.d.ts} +5 -3
  30. package/dist/router/index.cjs +649 -709
  31. package/dist/router/index.cjs.map +1 -1
  32. package/dist/router/index.d.cts +22 -12
  33. package/dist/router/index.d.ts +22 -12
  34. package/dist/router/index.js +502 -404
  35. package/dist/router/index.js.map +1 -1
  36. package/dist/{server-dd6a9853.d.ts → server-8fdd7fb2.d.ts} +5 -5
  37. package/dist/{services-1b5ac5bc.d.ts → services-259f39a3.d.ts} +191 -194
  38. package/dist/transport/impls/ws/client.cjs +129 -62
  39. package/dist/transport/impls/ws/client.cjs.map +1 -1
  40. package/dist/transport/impls/ws/client.d.cts +4 -4
  41. package/dist/transport/impls/ws/client.d.ts +4 -4
  42. package/dist/transport/impls/ws/client.js +7 -7
  43. package/dist/transport/impls/ws/client.js.map +1 -1
  44. package/dist/transport/impls/ws/server.cjs +146 -70
  45. package/dist/transport/impls/ws/server.cjs.map +1 -1
  46. package/dist/transport/impls/ws/server.d.cts +6 -5
  47. package/dist/transport/impls/ws/server.d.ts +6 -5
  48. package/dist/transport/impls/ws/server.js +21 -9
  49. package/dist/transport/impls/ws/server.js.map +1 -1
  50. package/dist/transport/index.cjs +138 -92
  51. package/dist/transport/index.cjs.map +1 -1
  52. package/dist/transport/index.d.cts +4 -4
  53. package/dist/transport/index.d.ts +4 -4
  54. package/dist/transport/index.js +7 -7
  55. package/dist/util/testHelpers.cjs +265 -327
  56. package/dist/util/testHelpers.cjs.map +1 -1
  57. package/dist/util/testHelpers.d.cts +36 -31
  58. package/dist/util/testHelpers.d.ts +36 -31
  59. package/dist/util/testHelpers.js +82 -52
  60. package/dist/util/testHelpers.js.map +1 -1
  61. package/package.json +4 -3
  62. package/dist/chunk-42Z2FQIU.js.map +0 -1
  63. package/dist/chunk-4HT6P2ZG.js.map +0 -1
  64. package/dist/chunk-4PVU7J25.js.map +0 -1
  65. package/dist/chunk-EETL2L77.js.map +0 -1
  66. package/dist/chunk-GR3AQKHL.js.map +0 -1
  67. package/dist/chunk-I75XYO5W.js.map +0 -1
  68. package/dist/chunk-MQ6ANR3H.js +0 -451
  69. package/dist/chunk-MQ6ANR3H.js.map +0 -1
  70. package/dist/chunk-VXYHC666.js.map +0 -1
  71. package/dist/chunk-ZXZE253M.js.map +0 -1
  72. package/dist/connection-260e45a8.d.ts +0 -11
@@ -1,13 +1,23 @@
1
1
  import {
2
2
  Connection
3
- } from "./chunk-I75XYO5W.js";
3
+ } from "./chunk-QIDEN5PP.js";
4
4
 
5
5
  // transport/impls/ws/connection.ts
6
+ var WS_HEALTHY_CLOSE_CODE = 1e3;
6
7
  var WebSocketConnection = class extends Connection {
7
8
  ws;
8
- constructor(ws) {
9
+ extras;
10
+ get loggingMetadata() {
11
+ const metadata = super.loggingMetadata;
12
+ if (this.extras) {
13
+ metadata.extras = this.extras;
14
+ }
15
+ return metadata;
16
+ }
17
+ constructor(ws, extras) {
9
18
  super();
10
19
  this.ws = ws;
20
+ this.extras = extras;
11
21
  this.ws.binaryType = "arraybuffer";
12
22
  let didError = false;
13
23
  this.ws.onerror = () => {
@@ -40,11 +50,11 @@ var WebSocketConnection = class extends Connection {
40
50
  return true;
41
51
  }
42
52
  close() {
43
- this.ws.close();
53
+ this.ws.close(WS_HEALTHY_CLOSE_CODE);
44
54
  }
45
55
  };
46
56
 
47
57
  export {
48
58
  WebSocketConnection
49
59
  };
50
- //# sourceMappingURL=chunk-GR3AQKHL.js.map
60
+ //# sourceMappingURL=chunk-HRKM7BIE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../transport/impls/ws/connection.ts"],"sourcesContent":["import { Connection } from '../../connection';\nimport { WsLike } from './wslike';\n\ninterface ConnectionInfoExtras {\n headers: Record<string, string>;\n}\n\nconst WS_HEALTHY_CLOSE_CODE = 1000;\n\nexport class WebSocketConnection extends Connection {\n ws: WsLike;\n extras?: ConnectionInfoExtras;\n\n get loggingMetadata() {\n const metadata = super.loggingMetadata;\n if (this.extras) {\n metadata.extras = this.extras;\n }\n\n return metadata;\n }\n\n constructor(ws: WsLike, extras?: ConnectionInfoExtras) {\n super();\n this.ws = ws;\n this.extras = extras;\n this.ws.binaryType = 'arraybuffer';\n\n // Websockets are kinda shitty, they emit error events with no\n // information other than it errored, so we have to do some extra\n // work to figure out what happened.\n let didError = false;\n this.ws.onerror = () => {\n didError = true;\n };\n\n this.ws.onclose = ({ code, reason }) => {\n if (didError) {\n const err = new Error(\n `websocket closed with code and reason: ${code} - ${reason}`,\n );\n\n for (const cb of this.errorListeners) {\n cb(err);\n }\n }\n\n for (const cb of this.closeListeners) {\n cb();\n }\n };\n\n this.ws.onmessage = (msg) => {\n for (const cb of this.dataListeners) {\n cb(msg.data as Uint8Array);\n }\n };\n }\n\n send(payload: Uint8Array) {\n if (this.ws.readyState !== this.ws.OPEN) {\n return false;\n }\n\n this.ws.send(payload);\n\n return true;\n }\n\n close() {\n // we close with 1000 normal even if its not really healthy at the river level\n // if we don't specify this, it defaults to 1005 which\n // some proxies/loggers detect as an error\n this.ws.close(WS_HEALTHY_CLOSE_CODE);\n }\n}\n"],"mappings":";;;;;AAOA,IAAM,wBAAwB;AAEvB,IAAM,sBAAN,cAAkC,WAAW;AAAA,EAClD;AAAA,EACA;AAAA,EAEA,IAAI,kBAAkB;AACpB,UAAM,WAAW,MAAM;AACvB,QAAI,KAAK,QAAQ;AACf,eAAS,SAAS,KAAK;AAAA,IACzB;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,YAAY,IAAY,QAA+B;AACrD,UAAM;AACN,SAAK,KAAK;AACV,SAAK,SAAS;AACd,SAAK,GAAG,aAAa;AAKrB,QAAI,WAAW;AACf,SAAK,GAAG,UAAU,MAAM;AACtB,iBAAW;AAAA,IACb;AAEA,SAAK,GAAG,UAAU,CAAC,EAAE,MAAM,OAAO,MAAM;AACtC,UAAI,UAAU;AACZ,cAAM,MAAM,IAAI;AAAA,UACd,0CAA0C,IAAI,MAAM,MAAM;AAAA,QAC5D;AAEA,mBAAW,MAAM,KAAK,gBAAgB;AACpC,aAAG,GAAG;AAAA,QACR;AAAA,MACF;AAEA,iBAAW,MAAM,KAAK,gBAAgB;AACpC,WAAG;AAAA,MACL;AAAA,IACF;AAEA,SAAK,GAAG,YAAY,CAAC,QAAQ;AAC3B,iBAAW,MAAM,KAAK,eAAe;AACnC,WAAG,IAAI,IAAkB;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAAA,EAEA,KAAK,SAAqB;AACxB,QAAI,KAAK,GAAG,eAAe,KAAK,GAAG,MAAM;AACvC,aAAO;AAAA,IACT;AAEA,SAAK,GAAG,KAAK,OAAO;AAEpB,WAAO;AAAA,EACT;AAAA,EAEA,QAAQ;AAIN,SAAK,GAAG,MAAM,qBAAqB;AAAA,EACrC;AACF;","names":[]}
@@ -1,19 +1,20 @@
1
1
  import {
2
2
  ProtocolError,
3
3
  Transport
4
- } from "./chunk-I75XYO5W.js";
4
+ } from "./chunk-QIDEN5PP.js";
5
5
  import {
6
6
  ServerSessionStateGraph,
7
7
  defaultServerTransportOptions
8
- } from "./chunk-42Z2FQIU.js";
8
+ } from "./chunk-6BH2CXVE.js";
9
9
  import {
10
10
  ControlMessageHandshakeRequestSchema,
11
11
  HandshakeErrorCustomHandlerFatalResponseCodes,
12
12
  acceptedProtocolVersions,
13
13
  coerceErrorString,
14
14
  currentProtocolVersion,
15
- handshakeResponseMessage
16
- } from "./chunk-EETL2L77.js";
15
+ handshakeResponseMessage,
16
+ isAcceptedProtocolVersion
17
+ } from "./chunk-GJUUVID2.js";
17
18
 
18
19
  // transport/server.ts
19
20
  import { SpanStatusCode } from "@opentelemetry/api";
@@ -48,35 +49,13 @@ var ServerTransport = class extends Transport {
48
49
  extendHandshake(options) {
49
50
  this.handshakeExtensions = options;
50
51
  }
51
- send(to, msg) {
52
- if (this.getStatus() === "closed") {
53
- const err = "transport is closed, cant send";
54
- this.log?.error(err, {
55
- clientId: this.clientId,
56
- transportMessage: msg,
57
- tags: ["invariant-violation"]
58
- });
59
- throw new Error(err);
60
- }
61
- const session = this.sessions.get(to);
62
- if (!session) {
63
- const err = `session to ${to} does not exist`;
64
- this.log?.error(err, {
65
- clientId: this.clientId,
66
- transportMessage: msg,
67
- tags: ["invariant-violation"]
68
- });
69
- throw new Error(err);
70
- }
71
- return session.send(msg);
72
- }
73
52
  deletePendingSession(pendingSession) {
74
53
  pendingSession.close();
75
54
  this.pendingSessions.delete(pendingSession);
76
55
  }
77
- deleteSession(session) {
56
+ deleteSession(session, options) {
78
57
  this.sessionHandshakeMetadata.delete(session.to);
79
- super.deleteSession(session);
58
+ super.deleteSession(session, options);
80
59
  }
81
60
  handleConnection(conn) {
82
61
  if (this.getStatus() !== "open")
@@ -189,7 +168,7 @@ var ServerTransport = class extends Transport {
189
168
  return;
190
169
  }
191
170
  const gotVersion = msg.payload.protocolVersion;
192
- if (!acceptedProtocolVersions.includes(gotVersion)) {
171
+ if (!isAcceptedProtocolVersion(gotVersion)) {
193
172
  this.rejectHandshakeRequest(
194
173
  session,
195
174
  msg.from,
@@ -203,7 +182,6 @@ var ServerTransport = class extends Transport {
203
182
  );
204
183
  return;
205
184
  }
206
- let oldSession = this.sessions.get(msg.from);
207
185
  let parsedMetadata = {};
208
186
  if (this.handshakeExtensions) {
209
187
  if (!Value.Check(this.handshakeExtensions.schema, msg.payload.metadata)) {
@@ -225,7 +203,9 @@ var ServerTransport = class extends Transport {
225
203
  );
226
204
  return;
227
205
  }
228
- const previousParsedMetadata = oldSession ? this.sessionHandshakeMetadata.get(oldSession.to) : void 0;
206
+ const previousParsedMetadata = this.sessionHandshakeMetadata.get(
207
+ msg.from
208
+ );
229
209
  const parsedMetadataOrFailureCode = await this.handshakeExtensions.validate(
230
210
  msg.payload.metadata,
231
211
  previousParsedMetadata
@@ -254,7 +234,8 @@ var ServerTransport = class extends Transport {
254
234
  }
255
235
  let connectCase = "new session";
256
236
  const clientNextExpectedSeq = msg.payload.expectedSessionState.nextExpectedSeq;
257
- const clientNextSentSeq = msg.payload.expectedSessionState.nextSentSeq ?? 0;
237
+ const clientNextSentSeq = msg.payload.expectedSessionState.nextSentSeq;
238
+ let oldSession = this.sessions.get(msg.from);
258
239
  if (this.options.enableTransparentSessionReconnects && oldSession && oldSession.id === msg.payload.sessionId) {
259
240
  connectCase = "transparent reconnection";
260
241
  const ourNextSeq = oldSession.nextSeq();
@@ -367,19 +348,25 @@ var ServerTransport = class extends Transport {
367
348
  );
368
349
  this.onConnClosed(connectedSession);
369
350
  },
370
- onMessage: (msg2) => this.handleMsg(msg2),
351
+ onMessage: (msg2) => {
352
+ this.handleMsg(msg2);
353
+ },
371
354
  onInvalidMessage: (reason) => {
372
355
  this.protocolError({
373
- type: ProtocolError.MessageOrderingViolated,
356
+ type: ProtocolError.InvalidMessage,
374
357
  message: reason
375
358
  });
376
- this.deleteSession(connectedSession);
359
+ this.deleteSession(connectedSession, { unhealthy: true });
377
360
  }
378
361
  },
379
362
  gotVersion
380
363
  );
381
364
  this.sessionHandshakeMetadata.set(connectedSession.to, parsedMetadata);
382
- this.updateSession(connectedSession);
365
+ if (oldSession) {
366
+ this.updateSession(connectedSession);
367
+ } else {
368
+ this.createSession(connectedSession);
369
+ }
383
370
  this.pendingSessions.delete(session);
384
371
  connectedSession.startActiveHeartbeat();
385
372
  }
@@ -388,4 +375,4 @@ var ServerTransport = class extends Transport {
388
375
  export {
389
376
  ServerTransport
390
377
  };
391
- //# sourceMappingURL=chunk-ZXZE253M.js.map
378
+ //# sourceMappingURL=chunk-PJB2Y2AV.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 HandshakeErrorCustomHandlerFatalResponseCodes,\n HandshakeErrorResponseCodes,\n OpaqueTransportMessage,\n acceptedProtocolVersions,\n TransportClientId,\n handshakeResponseMessage,\n currentProtocolVersion,\n isAcceptedProtocolVersion,\n} from './message';\nimport {\n ProvidedServerTransportOptions,\n ServerTransportOptions,\n defaultServerTransportOptions,\n} from './options';\nimport { DeleteSessionOptions, 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 { SessionState } from './sessionStateMachine/common';\nimport {\n ServerSession,\n ServerSessionStateGraph,\n} 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\n sessions = new Map<TransportClientId, ServerSession<ConnType>>();\n pendingSessions = new Set<SessionWaitingForHandshake<ConnType>>();\n\n constructor(\n clientId: TransportClientId,\n providedOptions?: ProvidedServerTransportOptions,\n ) {\n super(clientId, providedOptions);\n this.sessions = new Map();\n this.options = {\n ...defaultServerTransportOptions,\n ...providedOptions,\n };\n this.log?.info(`initiated server transport`, {\n clientId: this.clientId,\n protocolVersion: currentProtocolVersion,\n });\n }\n\n extendHandshake(options: ServerHandshakeOptions) {\n this.handshakeExtensions = options;\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(\n session: ServerSession<ConnType>,\n options?: DeleteSessionOptions,\n ): void {\n this.sessionHandshakeMetadata.delete(session.to);\n super.deleteSession(session, options);\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 = ServerSessionStateGraph.entrypoint(\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\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, code) => {\n this.log?.error(\n `invalid handshake: ${reason}`,\n pendingSession.loggingMetadata,\n );\n this.deletePendingSession(pendingSession);\n this.protocolError({\n type: ProtocolError.HandshakeFailed,\n code,\n message: reason,\n });\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({\n type: ProtocolError.HandshakeFailed,\n code,\n message: reason,\n });\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 (!isAcceptedProtocolVersion(gotVersion)) {\n this.rejectHandshakeRequest(\n session,\n msg.from,\n `expected protocol version oneof [${acceptedProtocolVersions.toString()}], 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 // invariant: must pass custom validation if defined\n let parsedMetadata: ParsedMetadata = {};\n if (this.handshakeExtensions) {\n if (!Value.Check(this.handshakeExtensions.schema, msg.payload.metadata)) {\n this.rejectHandshakeRequest(\n session,\n msg.from,\n 'received malformed handshake metadata',\n 'MALFORMED_HANDSHAKE_META',\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n validationErrors: [\n ...Value.Errors(\n this.handshakeExtensions.schema,\n msg.payload.metadata,\n ),\n ],\n },\n );\n\n return;\n }\n\n const previousParsedMetadata = this.sessionHandshakeMetadata.get(\n msg.from,\n );\n\n const parsedMetadataOrFailureCode =\n await this.handshakeExtensions.validate(\n msg.payload.metadata,\n previousParsedMetadata,\n );\n\n // double-check to make sure we haven't transitioned the session yet\n if (session._isConsumed) {\n // bail out, don't need to do anything\n return;\n }\n\n // handler rejected the connection\n if (\n Value.Check(\n HandshakeErrorCustomHandlerFatalResponseCodes,\n parsedMetadataOrFailureCode,\n )\n ) {\n this.rejectHandshakeRequest(\n session,\n msg.from,\n 'rejected by handshake handler',\n parsedMetadataOrFailureCode,\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n clientId: this.clientId,\n },\n );\n\n return;\n }\n\n // success!\n parsedMetadata = parsedMetadataOrFailureCode;\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 const clientNextExpectedSeq =\n msg.payload.expectedSessionState.nextExpectedSeq;\n const clientNextSentSeq = msg.payload.expectedSessionState.nextSentSeq;\n\n let oldSession = this.sessions.get(msg.from);\n if (\n this.options.enableTransparentSessionReconnects &&\n oldSession &&\n oldSession.id === msg.payload.sessionId\n ) {\n connectCase = 'transparent reconnection';\n\n // invariant: ordering must be correct\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.NoConnection) {\n const noConnectionSession =\n ServerSessionStateGraph.transition.ConnectedToNoConnection(\n oldSession,\n {\n onSessionGracePeriodElapsed: () => {\n this.onSessionGracePeriodElapsed(noConnectionSession);\n },\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.log?.info(\n `client is reconnecting to a new session (${msg.payload.sessionId}) with an old session (${oldSession.id}) already existing, closing old session`,\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n sessionId: msg.payload.sessionId,\n },\n );\n this.deleteSession(oldSession);\n oldSession = undefined;\n }\n\n if (!oldSession && (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 connectCase = 'unknown session';\n\n const rejectionMessage = this.options.enableTransparentSessionReconnects\n ? `client is trying to reconnect to a session the server don't know about: ${msg.payload.sessionId}`\n : `client is attempting a transparent reconnect to a session but the server does not support it: ${msg.payload.sessionId}`;\n\n this.rejectHandshakeRequest(\n session,\n msg.from,\n rejectionMessage,\n 'SESSION_STATE_MISMATCH',\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n transportMessage: msg,\n },\n );\n\n return;\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\n const responseMsg = handshakeResponseMessage({\n from: this.clientId,\n to: msg.from,\n status: {\n ok: true,\n sessionId,\n },\n });\n\n session.sendHandshake(responseMsg);\n\n // transition\n const connectedSession =\n ServerSessionStateGraph.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) => {\n this.handleMsg(msg);\n },\n onInvalidMessage: (reason) => {\n this.protocolError({\n type: ProtocolError.InvalidMessage,\n message: reason,\n });\n this.deleteSession(connectedSession, { unhealthy: true });\n },\n },\n gotVersion,\n );\n\n this.sessionHandshakeMetadata.set(connectedSession.to, parsedMetadata);\n if (oldSession) {\n this.updateSession(connectedSession);\n } else {\n this.createSession(connectedSession);\n }\n\n this.pendingSessions.delete(session);\n connectedSession.startActiveHeartbeat();\n }\n}\n"],"mappings":"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","names":["msg"]}
@@ -1,20 +1,21 @@
1
1
  import {
2
2
  BaseLogger,
3
3
  createLogProxy
4
- } from "./chunk-VXYHC666.js";
4
+ } from "./chunk-YTMS7OP6.js";
5
5
  import {
6
6
  SessionStateGraph,
7
7
  defaultTransportOptions
8
- } from "./chunk-42Z2FQIU.js";
8
+ } from "./chunk-6BH2CXVE.js";
9
9
  import {
10
10
  generateId
11
- } from "./chunk-EETL2L77.js";
11
+ } from "./chunk-GJUUVID2.js";
12
12
 
13
13
  // transport/events.ts
14
14
  var ProtocolError = {
15
15
  RetriesExceeded: "conn_retry_exceeded",
16
16
  HandshakeFailed: "handshake_failed",
17
- MessageOrderingViolated: "message_ordering_violated"
17
+ MessageOrderingViolated: "message_ordering_violated",
18
+ InvalidMessage: "invalid_message"
18
19
  };
19
20
  var EventDispatcher = class {
20
21
  eventListeners = {};
@@ -89,12 +90,12 @@ var Transport = class {
89
90
  /**
90
91
  * Called when a message is received by this transport.
91
92
  * You generally shouldn't need to override this in downstream transport implementations.
92
- * @param msg The received message.
93
+ * @param message The received message.
93
94
  */
94
- handleMsg(msg) {
95
+ handleMsg(message) {
95
96
  if (this.getStatus() !== "open")
96
97
  return;
97
- this.eventDispatcher.dispatchEvent("message", msg);
98
+ this.eventDispatcher.dispatchEvent("message", message);
98
99
  }
99
100
  /**
100
101
  * Adds a listener to this transport.
@@ -134,28 +135,59 @@ var Transport = class {
134
135
  getStatus() {
135
136
  return this.status;
136
137
  }
137
- updateSession(session) {
138
+ // state transitions
139
+ createSession(session) {
138
140
  const activeSession = this.sessions.get(session.to);
139
- if (activeSession && activeSession.id !== session.id) {
140
- const msg = `attempt to transition active session for ${session.to} but active session (${activeSession.id}) is different from handle (${session.id})`;
141
+ if (activeSession) {
142
+ const msg = `attempt to create session for ${session.to} but active session (${activeSession.id}) already exists`;
143
+ this.log?.error(msg, {
144
+ ...session.loggingMetadata,
145
+ tags: ["invariant-violation"]
146
+ });
141
147
  throw new Error(msg);
142
148
  }
143
149
  this.sessions.set(session.to, session);
150
+ this.eventDispatcher.dispatchEvent("sessionStatus", {
151
+ status: "connect",
152
+ session
153
+ });
154
+ this.eventDispatcher.dispatchEvent("sessionTransition", {
155
+ state: session.state,
156
+ session
157
+ });
158
+ }
159
+ updateSession(session) {
160
+ const activeSession = this.sessions.get(session.to);
144
161
  if (!activeSession) {
145
- this.eventDispatcher.dispatchEvent("sessionStatus", {
146
- status: "connect",
147
- session
162
+ const msg = `attempt to transition session for ${session.to} but no active session exists`;
163
+ this.log?.error(msg, {
164
+ ...session.loggingMetadata,
165
+ tags: ["invariant-violation"]
166
+ });
167
+ throw new Error(msg);
168
+ }
169
+ if (activeSession.id !== session.id) {
170
+ const msg = `attempt to transition active session for ${session.to} but active session (${activeSession.id}) is different from handle (${session.id})`;
171
+ this.log?.error(msg, {
172
+ ...session.loggingMetadata,
173
+ tags: ["invariant-violation"]
148
174
  });
175
+ throw new Error(msg);
149
176
  }
177
+ this.sessions.set(session.to, session);
150
178
  this.eventDispatcher.dispatchEvent("sessionTransition", {
151
179
  state: session.state,
152
180
  session
153
181
  });
154
- return session;
155
182
  }
156
- // state transitions
157
- deleteSession(session) {
158
- session.log?.info(`closing session ${session.id}`, session.loggingMetadata);
183
+ deleteSession(session, options) {
184
+ if (session._isConsumed)
185
+ return;
186
+ const loggingMetadata = session.loggingMetadata;
187
+ if (loggingMetadata.tags && options?.unhealthy) {
188
+ loggingMetadata.tags.push("unhealthy-session");
189
+ }
190
+ session.log?.info(`closing session ${session.id}`, loggingMetadata);
159
191
  this.eventDispatcher.dispatchEvent("sessionStatus", {
160
192
  status: "disconnect",
161
193
  session
@@ -178,7 +210,8 @@ var Transport = class {
178
210
  this.onSessionGracePeriodElapsed(noConnectionSession);
179
211
  }
180
212
  });
181
- return this.updateSession(noConnectionSession);
213
+ this.updateSession(noConnectionSession);
214
+ return noConnectionSession;
182
215
  }
183
216
  onConnClosed(session) {
184
217
  let noConnectionSession;
@@ -195,7 +228,36 @@ var Transport = class {
195
228
  }
196
229
  });
197
230
  }
198
- return this.updateSession(noConnectionSession);
231
+ this.updateSession(noConnectionSession);
232
+ return noConnectionSession;
233
+ }
234
+ /**
235
+ * Gets a send closure scoped to a specific session. Sending using the returned
236
+ * closure after the session has transitioned to a different state will be a noop.
237
+ *
238
+ * Session objects themselves can become stale as they transition between
239
+ * states. As stale sessions cannot be used again (and will throw), holding
240
+ * onto a session object is not recommended.
241
+ */
242
+ getSessionBoundSendFn(to, sessionId) {
243
+ if (this.getStatus() !== "open") {
244
+ throw new Error("cannot get a bound send function on a closed transport");
245
+ }
246
+ return (msg) => {
247
+ const session = this.sessions.get(to);
248
+ if (!session) {
249
+ throw new Error(
250
+ `session scope for ${sessionId} has ended (close), can't send`
251
+ );
252
+ }
253
+ const sameSession = session.id === sessionId;
254
+ if (!sameSession) {
255
+ throw new Error(
256
+ `session scope for ${sessionId} has ended (transition), can't send`
257
+ );
258
+ }
259
+ return session.send(msg);
260
+ };
199
261
  }
200
262
  };
201
263
 
@@ -275,4 +337,4 @@ export {
275
337
  Transport,
276
338
  Connection
277
339
  };
278
- //# sourceMappingURL=chunk-I75XYO5W.js.map
340
+ //# sourceMappingURL=chunk-QIDEN5PP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../transport/events.ts","../transport/transport.ts","../transport/connection.ts"],"sourcesContent":["import { type Static } from '@sinclair/typebox';\nimport { Connection } from './connection';\nimport { OpaqueTransportMessage, HandshakeErrorResponseCodes } from './message';\nimport { Session, SessionState } from './sessionStateMachine';\nimport { TransportStatus } from './transport';\n\nexport const ProtocolError = {\n RetriesExceeded: 'conn_retry_exceeded',\n HandshakeFailed: 'handshake_failed',\n MessageOrderingViolated: 'message_ordering_violated',\n InvalidMessage: 'invalid_message',\n} as const;\n\nexport type ProtocolErrorType =\n (typeof ProtocolError)[keyof typeof ProtocolError];\n\nexport interface EventMap {\n message: OpaqueTransportMessage;\n sessionStatus: {\n status: 'connect' | 'disconnect';\n session: Session<Connection>;\n };\n sessionTransition:\n | { state: SessionState.Connected }\n | { state: SessionState.Handshaking }\n | { state: SessionState.Connecting }\n | { state: SessionState.BackingOff }\n | { state: SessionState.NoConnection };\n protocolError:\n | {\n type: (typeof ProtocolError)['HandshakeFailed'];\n code: Static<typeof HandshakeErrorResponseCodes>;\n message: string;\n }\n | {\n type: Omit<\n ProtocolErrorType,\n (typeof ProtocolError)['HandshakeFailed']\n >;\n message: string;\n };\n transportStatus: {\n status: TransportStatus;\n };\n}\n\nexport type EventTypes = keyof EventMap;\nexport type EventHandler<K extends EventTypes> = (\n event: EventMap[K],\n) => unknown;\n\nexport class EventDispatcher<T extends EventTypes> {\n private eventListeners: { [K in T]?: Set<EventHandler<K>> } = {};\n\n removeAllListeners() {\n this.eventListeners = {};\n }\n\n numberOfListeners<K extends T>(eventType: K) {\n return this.eventListeners[eventType]?.size ?? 0;\n }\n\n addEventListener<K extends T>(eventType: K, handler: EventHandler<K>) {\n if (!this.eventListeners[eventType]) {\n this.eventListeners[eventType] = new Set();\n }\n\n this.eventListeners[eventType]?.add(handler);\n }\n\n removeEventListener<K extends T>(eventType: K, handler: EventHandler<K>) {\n const handlers = this.eventListeners[eventType];\n if (handlers) {\n this.eventListeners[eventType]?.delete(handler);\n }\n }\n\n dispatchEvent<K extends T>(eventType: K, event: EventMap[K]) {\n const handlers = this.eventListeners[eventType];\n if (handlers) {\n // copying ensures that adding more listeners in a handler doesn't\n // affect the current dispatch.\n const copy = [...handlers];\n for (const handler of copy) {\n handler(event);\n }\n }\n }\n}\n","import {\n OpaqueTransportMessage,\n PartialTransportMessage,\n TransportClientId,\n} from './message';\nimport {\n BaseLogger,\n LogFn,\n Logger,\n LoggingLevel,\n createLogProxy,\n} from '../logging/log';\nimport { EventDispatcher, EventHandler, EventMap, EventTypes } from './events';\nimport {\n ProvidedTransportOptions,\n TransportOptions,\n defaultTransportOptions,\n} from './options';\nimport {\n SessionConnected,\n SessionConnecting,\n SessionHandshaking,\n SessionNoConnection,\n SessionState,\n} from './sessionStateMachine';\nimport { Connection } from './connection';\nimport { Session, SessionStateGraph } from './sessionStateMachine/transitions';\nimport { SessionId } from './sessionStateMachine/common';\n\n/**\n * Represents the possible states of a transport.\n * @property {'open'} open - The transport is open and operational (note that this doesn't mean it is actively connected)\n * @property {'closed'} closed - The transport is permanently closed and cannot be reopened.\n */\nexport type TransportStatus = 'open' | 'closed';\n\nexport interface DeleteSessionOptions {\n unhealthy: boolean;\n}\n\nexport type SessionBoundSendFn = (\n msg: PartialTransportMessage,\n) => string | undefined;\n\n/**\n * Transports manage the lifecycle (creation/deletion) of sessions\n *\n * ```plaintext\n * ▲\n * incoming │\n * messages │\n * ▼\n * ┌─────────────┐ 1:N ┌───────────┐ 1:1* ┌────────────┐\n * │ Transport │ ◄─────► │ Session │ ◄─────► │ Connection │\n * └─────────────┘ └───────────┘ └────────────┘\n * ▲ * (may or may not be initialized yet)\n * │\n * ▼\n * ┌───────────┐\n * │ Message │\n * │ Listeners │\n * └───────────┘\n * ```\n * @abstract\n */\nexport abstract class Transport<ConnType extends Connection> {\n /**\n * The status of the transport.\n */\n private status: TransportStatus;\n\n /**\n * The client ID of this transport.\n */\n clientId: TransportClientId;\n\n /**\n * The event dispatcher for handling events of type EventTypes.\n */\n eventDispatcher: EventDispatcher<EventTypes>;\n\n /**\n * The options for this transport.\n */\n protected options: TransportOptions;\n log?: Logger;\n\n sessions: Map<TransportClientId, Session<ConnType>>;\n\n /**\n * Creates a new Transport instance.\n * @param codec The codec used to encode and decode messages.\n * @param clientId The client ID of this transport.\n */\n constructor(\n clientId: TransportClientId,\n providedOptions?: ProvidedTransportOptions,\n ) {\n this.options = { ...defaultTransportOptions, ...providedOptions };\n this.eventDispatcher = new EventDispatcher();\n this.clientId = clientId;\n this.status = 'open';\n this.sessions = new Map();\n }\n\n bindLogger(fn: LogFn | Logger, level?: LoggingLevel) {\n // construct logger from fn\n if (typeof fn === 'function') {\n this.log = createLogProxy(new BaseLogger(fn, level));\n\n return;\n }\n\n // object case, just assign\n this.log = createLogProxy(fn);\n }\n\n /**\n * Called when a message is received by this transport.\n * You generally shouldn't need to override this in downstream transport implementations.\n * @param message The received message.\n */\n protected handleMsg(message: OpaqueTransportMessage) {\n if (this.getStatus() !== 'open') return;\n this.eventDispatcher.dispatchEvent('message', message);\n }\n\n /**\n * Adds a listener to this transport.\n * @param the type of event to listen for\n * @param handler The message handler to add.\n */\n addEventListener<K extends EventTypes, T extends EventHandler<K>>(\n type: K,\n handler: T,\n ): void {\n this.eventDispatcher.addEventListener(type, handler);\n }\n\n /**\n * Removes a listener from this transport.\n * @param the type of event to un-listen on\n * @param handler The message handler to remove.\n */\n removeEventListener<K extends EventTypes, T extends EventHandler<K>>(\n type: K,\n handler: T,\n ): void {\n this.eventDispatcher.removeEventListener(type, handler);\n }\n\n protected protocolError(message: EventMap['protocolError']) {\n this.eventDispatcher.dispatchEvent('protocolError', message);\n }\n\n /**\n * Default close implementation for transports. You should override this in the downstream\n * implementation if you need to do any additional cleanup and call super.close() at the end.\n * Closes the transport. Any messages sent while the transport is closed will be silently discarded.\n */\n close() {\n this.status = 'closed';\n\n for (const session of this.sessions.values()) {\n this.deleteSession(session);\n }\n\n this.eventDispatcher.dispatchEvent('transportStatus', {\n status: this.status,\n });\n\n this.eventDispatcher.removeAllListeners();\n this.log?.info(`manually closed transport`, { clientId: this.clientId });\n }\n\n getStatus(): TransportStatus {\n return this.status;\n }\n\n // state transitions\n protected createSession<S extends Session<ConnType>>(session: S): void {\n const activeSession = this.sessions.get(session.to);\n if (activeSession) {\n const msg = `attempt to create session for ${session.to} but active session (${activeSession.id}) already exists`;\n this.log?.error(msg, {\n ...session.loggingMetadata,\n tags: ['invariant-violation'],\n });\n throw new Error(msg);\n }\n\n this.sessions.set(session.to, session);\n this.eventDispatcher.dispatchEvent('sessionStatus', {\n status: 'connect',\n session: session,\n });\n\n this.eventDispatcher.dispatchEvent('sessionTransition', {\n state: session.state,\n session: session,\n } as EventMap['sessionTransition']);\n }\n\n protected updateSession<S extends Session<ConnType>>(session: S): void {\n const activeSession = this.sessions.get(session.to);\n if (!activeSession) {\n const msg = `attempt to transition session for ${session.to} but no active session exists`;\n this.log?.error(msg, {\n ...session.loggingMetadata,\n tags: ['invariant-violation'],\n });\n throw new Error(msg);\n }\n\n if (activeSession.id !== session.id) {\n const msg = `attempt to transition active session for ${session.to} but active session (${activeSession.id}) is different from handle (${session.id})`;\n this.log?.error(msg, {\n ...session.loggingMetadata,\n tags: ['invariant-violation'],\n });\n throw new Error(msg);\n }\n\n this.sessions.set(session.to, session);\n this.eventDispatcher.dispatchEvent('sessionTransition', {\n state: session.state,\n session: session,\n } as EventMap['sessionTransition']);\n }\n\n protected deleteSession(\n session: Session<ConnType>,\n options?: DeleteSessionOptions,\n ) {\n // ensure idempotency esp re: dispatching events\n if (session._isConsumed) return;\n\n const loggingMetadata = session.loggingMetadata;\n if (loggingMetadata.tags && options?.unhealthy) {\n loggingMetadata.tags.push('unhealthy-session');\n }\n\n session.log?.info(`closing session ${session.id}`, loggingMetadata);\n this.eventDispatcher.dispatchEvent('sessionStatus', {\n status: 'disconnect',\n session: session,\n });\n\n const to = session.to;\n session.close();\n this.sessions.delete(to);\n }\n\n // common listeners\n protected onSessionGracePeriodElapsed(session: Session<ConnType>) {\n this.log?.warn(\n `session to ${session.to} grace period elapsed, closing`,\n session.loggingMetadata,\n );\n\n this.deleteSession(session);\n }\n\n protected onConnectingFailed(\n session: SessionConnecting<ConnType>,\n ): SessionNoConnection {\n // transition to no connection\n const noConnectionSession =\n SessionStateGraph.transition.ConnectingToNoConnection(session, {\n onSessionGracePeriodElapsed: () => {\n this.onSessionGracePeriodElapsed(noConnectionSession);\n },\n });\n\n this.updateSession(noConnectionSession);\n\n return noConnectionSession;\n }\n\n protected onConnClosed(\n session: SessionHandshaking<ConnType> | SessionConnected<ConnType>,\n ): SessionNoConnection {\n // transition to no connection\n let noConnectionSession: SessionNoConnection;\n if (session.state === SessionState.Handshaking) {\n noConnectionSession =\n SessionStateGraph.transition.HandshakingToNoConnection(session, {\n onSessionGracePeriodElapsed: () => {\n this.onSessionGracePeriodElapsed(noConnectionSession);\n },\n });\n } else {\n noConnectionSession =\n SessionStateGraph.transition.ConnectedToNoConnection(session, {\n onSessionGracePeriodElapsed: () => {\n this.onSessionGracePeriodElapsed(noConnectionSession);\n },\n });\n }\n\n this.updateSession(noConnectionSession);\n\n return noConnectionSession;\n }\n\n /**\n * Gets a send closure scoped to a specific session. Sending using the returned\n * closure after the session has transitioned to a different state will be a noop.\n *\n * Session objects themselves can become stale as they transition between\n * states. As stale sessions cannot be used again (and will throw), holding\n * onto a session object is not recommended.\n */\n getSessionBoundSendFn(\n to: TransportClientId,\n sessionId: SessionId,\n ): SessionBoundSendFn {\n if (this.getStatus() !== 'open') {\n throw new Error('cannot get a bound send function on a closed transport');\n }\n\n return (msg: PartialTransportMessage) => {\n const session = this.sessions.get(to);\n if (!session) {\n throw new Error(\n `session scope for ${sessionId} has ended (close), can't send`,\n );\n }\n\n const sameSession = session.id === sessionId;\n if (!sameSession) {\n throw new Error(\n `session scope for ${sessionId} has ended (transition), can't send`,\n );\n }\n\n return session.send(msg);\n };\n }\n}\n","import { TelemetryInfo } from '../tracing';\nimport { MessageMetadata } from '../logging';\nimport { generateId } from './id';\n\n/**\n * A connection is the actual raw underlying transport connection.\n * It’s responsible for dispatching to/from the actual connection itself\n * This should be instantiated as soon as the client/server has a connection\n * It’s tied to the lifecycle of the underlying transport connection (i.e. if the WS drops, this connection should be deleted)\n */\nexport abstract class Connection {\n id: string;\n telemetry?: TelemetryInfo;\n\n constructor() {\n this.id = `conn-${generateId()}`; // for debugging, no collision safety needed\n }\n\n get loggingMetadata(): MessageMetadata {\n const metadata: MessageMetadata = { connId: this.id };\n const spanContext = this.telemetry?.span.spanContext();\n\n if (this.telemetry?.span.isRecording() && spanContext) {\n metadata.telemetry = {\n traceId: spanContext.traceId,\n spanId: spanContext.spanId,\n };\n }\n\n return metadata;\n }\n\n // can't use event emitter because we need this to work in both node + browser\n private _dataListeners = new Set<(msg: Uint8Array) => void>();\n private _closeListeners = new Set<() => void>();\n private _errorListeners = new Set<(err: Error) => void>();\n\n get dataListeners() {\n return [...this._dataListeners];\n }\n\n get closeListeners() {\n return [...this._closeListeners];\n }\n\n get errorListeners() {\n return [...this._errorListeners];\n }\n\n /**\n * Handle adding a callback for when a message is received.\n * @param msg The message that was received.\n */\n addDataListener(cb: (msg: Uint8Array) => void) {\n this._dataListeners.add(cb);\n }\n\n removeDataListener(cb: (msg: Uint8Array) => void): void {\n this._dataListeners.delete(cb);\n }\n\n /**\n * Handle adding a callback for when the connection is closed.\n * This should also be called if an error happens and after notifying all the error listeners.\n * @param cb The callback to call when the connection is closed.\n */\n addCloseListener(cb: () => void): void {\n this._closeListeners.add(cb);\n }\n\n removeCloseListener(cb: () => void): void {\n this._closeListeners.delete(cb);\n }\n\n /**\n * Handle adding a callback for when an error is received.\n * This should only be used for this.logging errors, all cleanup\n * should be delegated to addCloseListener.\n *\n * The implementer should take care such that the implemented\n * connection will call both the close and error callbacks\n * on an error.\n *\n * @param cb The callback to call when an error is received.\n */\n addErrorListener(cb: (err: Error) => void): void {\n this._errorListeners.add(cb);\n }\n\n removeErrorListener(cb: (err: Error) => void): void {\n this._errorListeners.delete(cb);\n }\n\n /**\n * Sends a message over the connection.\n * @param msg The message to send.\n * @returns true if the message was sent, false otherwise.\n */\n abstract send(msg: Uint8Array): boolean;\n\n /**\n * Closes the connection.\n */\n abstract close(): void;\n}\n"],"mappings":"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","names":[]}
@@ -76,4 +76,4 @@ export {
76
76
  jsonLogger,
77
77
  createLogProxy
78
78
  };
79
- //# sourceMappingURL=chunk-VXYHC666.js.map
79
+ //# sourceMappingURL=chunk-YTMS7OP6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../logging/log.ts"],"sourcesContent":["import { ValueError } from '@sinclair/typebox/value';\nimport { OpaqueTransportMessage, ProtocolVersion } from '../transport/message';\n\nconst LoggingLevels = {\n debug: -1,\n info: 0,\n warn: 1,\n error: 2,\n} as const;\nexport type LoggingLevel = keyof typeof LoggingLevels;\n\nexport type LogFn = (\n msg: string,\n ctx?: MessageMetadata,\n level?: LoggingLevel,\n) => void;\nexport type Logger = {\n [key in LoggingLevel]: (msg: string, metadata?: MessageMetadata) => void;\n};\n\nexport type Tags =\n | 'invariant-violation'\n | 'state-transition'\n | 'invalid-request'\n | 'unhealthy-session';\n\nconst cleanedLogFn = (log: LogFn) => {\n return (msg: string, metadata?: MessageMetadata) => {\n // skip cloning object if metadata has no transportMessage\n if (!metadata?.transportMessage) {\n log(msg, metadata);\n\n return;\n }\n\n // clone metadata and clean transportMessage\n const { payload, ...rest } = metadata.transportMessage;\n metadata.transportMessage = rest;\n log(msg, metadata);\n };\n};\n\nexport type MessageMetadata = Partial<{\n protocolVersion: ProtocolVersion;\n clientId: string;\n connectedTo: string;\n sessionId: string;\n connId: string;\n transportMessage: Partial<OpaqueTransportMessage>;\n validationErrors: Array<ValueError>;\n tags: Array<Tags>;\n telemetry: {\n traceId: string;\n spanId: string;\n };\n extras: unknown;\n}>;\n\nexport class BaseLogger implements Logger {\n minLevel: LoggingLevel;\n private output: LogFn;\n\n constructor(output: LogFn, minLevel: LoggingLevel = 'info') {\n this.minLevel = minLevel;\n this.output = output;\n }\n\n debug(msg: string, metadata?: MessageMetadata) {\n if (LoggingLevels[this.minLevel] <= LoggingLevels.debug) {\n this.output(msg, metadata ?? {}, 'debug');\n }\n }\n\n info(msg: string, metadata?: MessageMetadata) {\n if (LoggingLevels[this.minLevel] <= LoggingLevels.info) {\n this.output(msg, metadata ?? {}, 'info');\n }\n }\n\n warn(msg: string, metadata?: MessageMetadata) {\n if (LoggingLevels[this.minLevel] <= LoggingLevels.warn) {\n this.output(msg, metadata ?? {}, 'warn');\n }\n }\n\n error(msg: string, metadata?: MessageMetadata) {\n if (LoggingLevels[this.minLevel] <= LoggingLevels.error) {\n this.output(msg, metadata ?? {}, 'error');\n }\n }\n}\n\nexport const stringLogger: LogFn = (msg, ctx, level = 'info') => {\n const from = ctx?.clientId ? `${ctx.clientId} -- ` : '';\n console.log(`[river:${level}] ${from}${msg}`);\n};\n\nconst colorMap = {\n debug: '\\u001b[34m',\n info: '\\u001b[32m',\n warn: '\\u001b[33m',\n error: '\\u001b[31m',\n};\n\nexport const coloredStringLogger: LogFn = (msg, ctx, level = 'info') => {\n const color = colorMap[level];\n const from = ctx?.clientId ? `${ctx.clientId} -- ` : '';\n console.log(`[river:${color}${level}\\u001b[0m] ${from}${msg}`);\n};\n\nexport const jsonLogger: LogFn = (msg, ctx, level) => {\n console.log(JSON.stringify({ msg, ctx, level }));\n};\n\nexport const createLogProxy = (log: Logger) => ({\n debug: cleanedLogFn(log.debug.bind(log)),\n info: cleanedLogFn(log.info.bind(log)),\n warn: cleanedLogFn(log.warn.bind(log)),\n error: cleanedLogFn(log.error.bind(log)),\n});\n"],"mappings":";AAGA,IAAM,gBAAgB;AAAA,EACpB,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT;AAkBA,IAAM,eAAe,CAAC,QAAe;AACnC,SAAO,CAAC,KAAa,aAA+B;AAElD,QAAI,CAAC,UAAU,kBAAkB;AAC/B,UAAI,KAAK,QAAQ;AAEjB;AAAA,IACF;AAGA,UAAM,EAAE,SAAS,GAAG,KAAK,IAAI,SAAS;AACtC,aAAS,mBAAmB;AAC5B,QAAI,KAAK,QAAQ;AAAA,EACnB;AACF;AAkBO,IAAM,aAAN,MAAmC;AAAA,EACxC;AAAA,EACQ;AAAA,EAER,YAAY,QAAe,WAAyB,QAAQ;AAC1D,SAAK,WAAW;AAChB,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,MAAM,KAAa,UAA4B;AAC7C,QAAI,cAAc,KAAK,QAAQ,KAAK,cAAc,OAAO;AACvD,WAAK,OAAO,KAAK,YAAY,CAAC,GAAG,OAAO;AAAA,IAC1C;AAAA,EACF;AAAA,EAEA,KAAK,KAAa,UAA4B;AAC5C,QAAI,cAAc,KAAK,QAAQ,KAAK,cAAc,MAAM;AACtD,WAAK,OAAO,KAAK,YAAY,CAAC,GAAG,MAAM;AAAA,IACzC;AAAA,EACF;AAAA,EAEA,KAAK,KAAa,UAA4B;AAC5C,QAAI,cAAc,KAAK,QAAQ,KAAK,cAAc,MAAM;AACtD,WAAK,OAAO,KAAK,YAAY,CAAC,GAAG,MAAM;AAAA,IACzC;AAAA,EACF;AAAA,EAEA,MAAM,KAAa,UAA4B;AAC7C,QAAI,cAAc,KAAK,QAAQ,KAAK,cAAc,OAAO;AACvD,WAAK,OAAO,KAAK,YAAY,CAAC,GAAG,OAAO;AAAA,IAC1C;AAAA,EACF;AACF;AAEO,IAAM,eAAsB,CAAC,KAAK,KAAK,QAAQ,WAAW;AAC/D,QAAM,OAAO,KAAK,WAAW,GAAG,IAAI,QAAQ,SAAS;AACrD,UAAQ,IAAI,UAAU,KAAK,KAAK,IAAI,GAAG,GAAG,EAAE;AAC9C;AAEA,IAAM,WAAW;AAAA,EACf,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT;AAEO,IAAM,sBAA6B,CAAC,KAAK,KAAK,QAAQ,WAAW;AACtE,QAAM,QAAQ,SAAS,KAAK;AAC5B,QAAM,OAAO,KAAK,WAAW,GAAG,IAAI,QAAQ,SAAS;AACrD,UAAQ,IAAI,UAAU,KAAK,GAAG,KAAK,YAAc,IAAI,GAAG,GAAG,EAAE;AAC/D;AAEO,IAAM,aAAoB,CAAC,KAAK,KAAK,UAAU;AACpD,UAAQ,IAAI,KAAK,UAAU,EAAE,KAAK,KAAK,MAAM,CAAC,CAAC;AACjD;AAEO,IAAM,iBAAiB,CAAC,SAAiB;AAAA,EAC9C,OAAO,aAAa,IAAI,MAAM,KAAK,GAAG,CAAC;AAAA,EACvC,MAAM,aAAa,IAAI,KAAK,KAAK,GAAG,CAAC;AAAA,EACrC,MAAM,aAAa,IAAI,KAAK,KAAK,GAAG,CAAC;AAAA,EACrC,OAAO,aAAa,IAAI,MAAM,KAAK,GAAG,CAAC;AACzC;","names":[]}