@replit/river 0.18.5 → 0.19.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 (48) hide show
  1. package/dist/{chunk-VH3NGOXQ.js → chunk-D5PVGZPQ.js} +5 -3
  2. package/dist/{chunk-K7CUSLWL.js → chunk-JH275HID.js} +1 -1
  3. package/dist/{chunk-WER2DWCP.js → chunk-NBE3D667.js} +0 -4
  4. package/dist/{chunk-6Q3MSICL.js → chunk-SR4DBLJ6.js} +11 -3
  5. package/dist/{chunk-UABIFWM7.js → chunk-YFPVQTWL.js} +220 -105
  6. package/dist/{chunk-PUX3U2SZ.js → chunk-ZWPEZS27.js} +1 -1
  7. package/dist/{connection-893bd769.d.ts → connection-aa0ea000.d.ts} +1 -1
  8. package/dist/{connection-89918b74.d.ts → connection-cfec12e6.d.ts} +1 -1
  9. package/dist/index-e2513701.d.ts +342 -0
  10. package/dist/logging/index.cjs +0 -4
  11. package/dist/logging/index.d.cts +2 -1
  12. package/dist/logging/index.d.ts +2 -1
  13. package/dist/logging/index.js +1 -1
  14. package/dist/router/index.cjs +11 -2
  15. package/dist/router/index.d.cts +7 -383
  16. package/dist/router/index.d.ts +7 -383
  17. package/dist/router/index.js +3 -3
  18. package/dist/services-4bba42d8.d.ts +736 -0
  19. package/dist/services-5fc5712d.d.ts +736 -0
  20. package/dist/transport/impls/uds/client.cjs +80 -53
  21. package/dist/transport/impls/uds/client.d.cts +5 -5
  22. package/dist/transport/impls/uds/client.d.ts +5 -5
  23. package/dist/transport/impls/uds/client.js +4 -4
  24. package/dist/transport/impls/uds/server.cjs +151 -62
  25. package/dist/transport/impls/uds/server.d.cts +4 -4
  26. package/dist/transport/impls/uds/server.d.ts +4 -4
  27. package/dist/transport/impls/uds/server.js +4 -4
  28. package/dist/transport/impls/ws/client.cjs +80 -53
  29. package/dist/transport/impls/ws/client.d.cts +3 -3
  30. package/dist/transport/impls/ws/client.d.ts +3 -3
  31. package/dist/transport/impls/ws/client.js +4 -4
  32. package/dist/transport/impls/ws/server.cjs +151 -62
  33. package/dist/transport/impls/ws/server.d.cts +4 -4
  34. package/dist/transport/impls/ws/server.d.ts +4 -4
  35. package/dist/transport/impls/ws/server.js +4 -4
  36. package/dist/transport/index.cjs +188 -71
  37. package/dist/transport/index.d.cts +295 -3
  38. package/dist/transport/index.d.ts +295 -3
  39. package/dist/transport/index.js +3 -4
  40. package/dist/util/testHelpers.cjs +410 -401
  41. package/dist/util/testHelpers.d.cts +3 -3
  42. package/dist/util/testHelpers.d.ts +3 -3
  43. package/dist/util/testHelpers.js +4 -5
  44. package/package.json +1 -1
  45. package/dist/chunk-RPIDSIQG.js +0 -0
  46. package/dist/index-46ed19d8.d.ts +0 -111
  47. package/dist/index-d412ca83.d.ts +0 -420
  48. package/dist/procedures-bfffcb0b.d.ts +0 -324
@@ -0,0 +1,342 @@
1
+ import * as _sinclair_typebox from '@sinclair/typebox';
2
+ import { TSchema } from '@sinclair/typebox';
3
+ import { C as Codec } from './types-3e5768ec.js';
4
+
5
+ declare const LoggingLevels: {
6
+ readonly debug: -1;
7
+ readonly info: 0;
8
+ readonly warn: 1;
9
+ readonly error: 2;
10
+ };
11
+ type LoggingLevel = keyof typeof LoggingLevels;
12
+ type LogFn = (msg: string, ctx?: MessageMetadata, level?: LoggingLevel) => void;
13
+ type Logger = {
14
+ [key in LoggingLevel]: (msg: string, metadata?: MessageMetadata) => void;
15
+ };
16
+ type MessageMetadata = Record<string, unknown> & Partial<{
17
+ protocolVersion: string;
18
+ clientId: string;
19
+ connectedTo: string;
20
+ sessionId: string;
21
+ connId: string;
22
+ fullTransportMessage: OpaqueTransportMessage;
23
+ partialTransportMessage: Partial<PartialTransportMessage>;
24
+ }>;
25
+ declare const stringLogger: LogFn;
26
+ declare const coloredStringLogger: LogFn;
27
+ declare const jsonLogger: LogFn;
28
+ declare function bindLogger(fn: undefined, level?: LoggingLevel): undefined;
29
+ declare function bindLogger(fn: LogFn | Logger, level?: LoggingLevel): Logger;
30
+
31
+ /**
32
+ * A connection is the actual raw underlying transport connection.
33
+ * It’s responsible for dispatching to/from the actual connection itself
34
+ * This should be instantiated as soon as the client/server has a connection
35
+ * It’s tied to the lifecycle of the underlying transport connection (i.e. if the WS drops, this connection should be deleted)
36
+ */
37
+ declare abstract class Connection {
38
+ debugId: string;
39
+ constructor();
40
+ /**
41
+ * Handle adding a callback for when a message is received.
42
+ * @param msg The message that was received.
43
+ */
44
+ abstract addDataListener(cb: (msg: Uint8Array) => void): void;
45
+ abstract removeDataListener(cb: (msg: Uint8Array) => void): void;
46
+ /**
47
+ * Handle adding a callback for when the connection is closed.
48
+ * This should also be called if an error happens.
49
+ * @param cb The callback to call when the connection is closed.
50
+ */
51
+ abstract addCloseListener(cb: () => void): void;
52
+ /**
53
+ * Handle adding a callback for when an error is received.
54
+ * This should only be used for logging errors, all cleanup
55
+ * should be delegated to addCloseListener.
56
+ *
57
+ * The implementer should take care such that the implemented
58
+ * connection will call both the close and error callbacks
59
+ * on an error.
60
+ *
61
+ * @param cb The callback to call when an error is received.
62
+ */
63
+ abstract addErrorListener(cb: (err: Error) => void): void;
64
+ /**
65
+ * Sends a message over the connection.
66
+ * @param msg The message to send.
67
+ * @returns true if the message was sent, false otherwise.
68
+ */
69
+ abstract send(msg: Uint8Array): boolean;
70
+ /**
71
+ * Closes the connection.
72
+ */
73
+ abstract close(): void;
74
+ }
75
+ interface SessionOptions {
76
+ /**
77
+ * Frequency at which to send heartbeat acknowledgements
78
+ */
79
+ heartbeatIntervalMs: number;
80
+ /**
81
+ * Number of elapsed heartbeats without a response message before we consider
82
+ * the connection dead.
83
+ */
84
+ heartbeatsUntilDead: number;
85
+ /**
86
+ * Duration to wait between connection disconnect and actual session disconnect
87
+ */
88
+ sessionDisconnectGraceMs: number;
89
+ /**
90
+ * The codec to use for encoding/decoding messages over the wire
91
+ */
92
+ codec: Codec;
93
+ }
94
+ /**
95
+ * A session is a higher-level abstraction that operates over the span of potentially multiple transport-level connections
96
+ * - It’s responsible for tracking any metadata for a particular client that might need to be persisted across connections (i.e. the sendBuffer, ack, seq)
97
+ * - This will only be considered disconnected if
98
+ * - the server tells the client that we’ve reconnected but it doesn’t recognize us anymore (server definitely died) or
99
+ * - we hit a grace period after a connection disconnect
100
+ */
101
+ declare class Session<ConnType extends Connection> {
102
+ private codec;
103
+ private options;
104
+ /**
105
+ * The buffer of messages that have been sent but not yet acknowledged.
106
+ */
107
+ private sendBuffer;
108
+ /**
109
+ * The active connection associated with this session
110
+ */
111
+ connection?: ConnType;
112
+ readonly from: TransportClientId;
113
+ readonly to: TransportClientId;
114
+ /**
115
+ * The unique ID of this session.
116
+ */
117
+ id: string;
118
+ /**
119
+ * What the other side advertised as their session ID
120
+ * for this session.
121
+ */
122
+ advertisedSessionId?: string;
123
+ /**
124
+ * The metadata for this session, as parsed from the handshake.
125
+ *
126
+ * Will only ever be populated on the server side.
127
+ */
128
+ metadata?: ParsedHandshakeMetadata;
129
+ /**
130
+ * Number of messages we've sent along this session (excluding handshake and acks)
131
+ */
132
+ private seq;
133
+ /**
134
+ * Number of unique messages we've received this session (excluding handshake and acks)
135
+ */
136
+ private ack;
137
+ /**
138
+ * The grace period between when the inner connection is disconnected
139
+ * and when we should consider the entire session disconnected.
140
+ */
141
+ private disconnectionGrace?;
142
+ /**
143
+ * Number of heartbeats we've sent without a response.
144
+ */
145
+ private heartbeatMisses;
146
+ /**
147
+ * The interval for sending heartbeats.
148
+ */
149
+ private heartbeat;
150
+ constructor(conn: ConnType | undefined, from: TransportClientId, to: TransportClientId, options: SessionOptions);
151
+ get loggingMetadata(): Omit<MessageMetadata, 'parsedMsg'>;
152
+ /**
153
+ * Sends a message over the session's connection.
154
+ * If the connection is not ready or the message fails to send, the message can be buffered for retry unless skipped.
155
+ *
156
+ * @param msg The partial message to be sent, which will be constructed into a full message.
157
+ * @param addToSendBuff Whether to add the message to the send buffer for retry.
158
+ * @returns The full transport ID of the message that was attempted to be sent.
159
+ */
160
+ send(msg: PartialTransportMessage): string;
161
+ sendHeartbeat(): void;
162
+ resetBufferedMessages(): void;
163
+ sendBufferedMessages(conn: ConnType): void;
164
+ updateBookkeeping(ack: number, seq: number): void;
165
+ closeStaleConnection(conn?: ConnType): void;
166
+ replaceWithNewConnection(newConn: ConnType): void;
167
+ beginGrace(cb: () => void): void;
168
+ cancelGrace(): void;
169
+ close(): void;
170
+ get connected(): boolean;
171
+ get nextExpectedSeq(): number;
172
+ constructMsg<Payload>(partialMsg: PartialTransportMessage<Payload>): TransportMessage<Payload>;
173
+ inspectSendBuffer(): ReadonlyArray<OpaqueTransportMessage>;
174
+ }
175
+
176
+ /**
177
+ * Metadata associated with a handshake request, as sent by the client.
178
+ *
179
+ * You should use declaration merging to extend this interface
180
+ * with whatever you need. For example, if you need to store an
181
+ * identifier for the client, you could do:
182
+ * ```
183
+ * declare module '@replit/river' {
184
+ * interface HandshakeMetadataClient {
185
+ * id: string;
186
+ * }
187
+ * }
188
+ * ```
189
+ */
190
+ interface HandshakeRequestMetadata {
191
+ }
192
+ /**
193
+ * Metadata associated with a handshake response, after the server
194
+ * has processed the data in {@link HandshakeRequestMetadata}. This
195
+ * is a separate interface for multiple reasons, but one of the main
196
+ * ones is that the server should remove any sensitive data from the
197
+ * client's request metadata before storing it in the session, that
198
+ * way no secrets are persisted in memory.
199
+ *
200
+ * You should use declaration merging to extend this interface
201
+ * with whatever you need. For example, if you need to store an
202
+ * identifier for the client, you could do:
203
+ * ```
204
+ * declare module '@replit/river' {
205
+ * interface HandshakeMetadataServer {
206
+ * id: string;
207
+ * }
208
+ * }
209
+ * ```
210
+ */
211
+ interface ParsedHandshakeMetadata {
212
+ }
213
+ /**
214
+ * Options for extending the client handshake process.
215
+ */
216
+ interface ClientHandshakeOptions {
217
+ /**
218
+ * Schema for the metadata that the client sends to the server
219
+ * during the handshake.
220
+ *
221
+ * Needs to match {@link HandshakeRequestMetadata}.
222
+ */
223
+ schema: TSchema;
224
+ /**
225
+ * Gets the {@link HandshakeRequestMetadata} to send to the server.
226
+ */
227
+ get: () => HandshakeRequestMetadata | Promise<HandshakeRequestMetadata>;
228
+ }
229
+ /**
230
+ * Options for extending the server handshake process.
231
+ */
232
+ interface ServerHandshakeOptions {
233
+ /**
234
+ * Schema for the metadata that the server receives from the client
235
+ * during the handshake.
236
+ *
237
+ * Needs to match {@link HandshakeRequestMetadata}.
238
+ */
239
+ requestSchema: TSchema;
240
+ /**
241
+ * Schema for the transformed metadata that is then associated with the
242
+ * client's session.
243
+ *
244
+ * Needs to match {@link ParsedHandshakeMetadata}.
245
+ */
246
+ parsedSchema: TSchema;
247
+ /**
248
+ * Parses the {@link HandshakeRequestMetadata} sent by the client, transforming
249
+ * it into {@link ParsedHandshakeMetadata}.
250
+ *
251
+ * May return `false` if the client should be rejected.
252
+ *
253
+ * @param metadata - The metadata sent by the client.
254
+ * @param session - The session that the client would be associated with.
255
+ * @param isReconnect - Whether the client is reconnecting to the session,
256
+ * or if this is a new session.
257
+ */
258
+ parse: (metadata: HandshakeRequestMetadata, session: Session<Connection>, isReconnect: boolean) => false | ParsedHandshakeMetadata | Promise<false | ParsedHandshakeMetadata>;
259
+ }
260
+ /**
261
+ * Generic Typebox schema for a transport message.
262
+ * @template T The type of the payload.
263
+ * @param {T} t The payload schema.
264
+ * @returns The transport message schema.
265
+ */
266
+ declare const TransportMessageSchema: <T extends TSchema>(t: T) => _sinclair_typebox.TObject<{
267
+ id: _sinclair_typebox.TString;
268
+ from: _sinclair_typebox.TString;
269
+ to: _sinclair_typebox.TString;
270
+ seq: _sinclair_typebox.TInteger;
271
+ ack: _sinclair_typebox.TInteger;
272
+ serviceName: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
273
+ procedureName: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
274
+ streamId: _sinclair_typebox.TString;
275
+ controlFlags: _sinclair_typebox.TInteger;
276
+ payload: T;
277
+ }>;
278
+ /**
279
+ * Defines the schema for an opaque transport message that is agnostic to any
280
+ * procedure/service.
281
+ * @returns The transport message schema.
282
+ */
283
+ declare const OpaqueTransportMessageSchema: _sinclair_typebox.TObject<{
284
+ id: _sinclair_typebox.TString;
285
+ from: _sinclair_typebox.TString;
286
+ to: _sinclair_typebox.TString;
287
+ seq: _sinclair_typebox.TInteger;
288
+ ack: _sinclair_typebox.TInteger;
289
+ serviceName: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
290
+ procedureName: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
291
+ streamId: _sinclair_typebox.TString;
292
+ controlFlags: _sinclair_typebox.TInteger;
293
+ payload: _sinclair_typebox.TUnknown;
294
+ }>;
295
+ /**
296
+ * Represents a transport message. This is the same type as {@link TransportMessageSchema} but
297
+ * we can't statically infer generics from generic Typebox schemas so we have to define it again here.
298
+ *
299
+ * TypeScript can't enforce types when a bitmask is involved, so these are the semantics of
300
+ * `controlFlags`:
301
+ * * If `controlFlags & StreamOpenBit == StreamOpenBit`, `streamId` must be set to a unique value
302
+ * (suggestion: use `nanoid`).
303
+ * * If `controlFlags & StreamOpenBit == StreamOpenBit`, `serviceName` and `procedureName` must be set.
304
+ * * If `controlFlags & StreamClosedBit == StreamClosedBit` and the kind is `stream` or `subscription`,
305
+ * `payload` should be discarded (usually contains a control message).
306
+ * * If `controlFlags & AckBit == AckBit`, the message is an explicit acknowledgement message and doesn't
307
+ * contain any payload that is relevant to the application so should not be delivered.
308
+ * @template Payload The type of the payload.
309
+ */
310
+ interface TransportMessage<Payload = unknown> {
311
+ id: string;
312
+ from: string;
313
+ to: string;
314
+ seq: number;
315
+ ack: number;
316
+ serviceName?: string;
317
+ procedureName?: string;
318
+ streamId: string;
319
+ controlFlags: number;
320
+ payload: Payload;
321
+ }
322
+ type PartialTransportMessage<Payload = unknown> = Omit<TransportMessage<Payload>, 'id' | 'from' | 'to' | 'seq' | 'ack'>;
323
+ /**
324
+ * A type alias for a transport message with an opaque payload.
325
+ * @template T - The type of the opaque payload.
326
+ */
327
+ type OpaqueTransportMessage = TransportMessage;
328
+ type TransportClientId = string;
329
+ /**
330
+ * Checks if the given control flag (usually found in msg.controlFlag) is a stream open message.
331
+ * @param controlFlag - The control flag to check.
332
+ * @returns True if the control flag contains the StreamOpenBit, false otherwise.
333
+ */
334
+ declare function isStreamOpen(controlFlag: number): boolean;
335
+ /**
336
+ * Checks if the given control flag (usually found in msg.controlFlag) is a stream close message.
337
+ * @param controlFlag - The control flag to check.
338
+ * @returns True if the control flag contains the StreamCloseBit, false otherwise.
339
+ */
340
+ declare function isStreamClose(controlFlag: number): boolean;
341
+
342
+ export { Connection as C, HandshakeRequestMetadata as H, Logger as L, MessageMetadata as M, OpaqueTransportMessage as O, PartialTransportMessage as P, SessionOptions as S, TransportClientId as T, Session as a, ParsedHandshakeMetadata as b, TransportMessageSchema as c, OpaqueTransportMessageSchema as d, TransportMessage as e, ClientHandshakeOptions as f, ServerHandshakeOptions as g, isStreamClose as h, isStreamOpen as i, coloredStringLogger as j, jsonLogger as k, bindLogger as l, LogFn as m, stringLogger as s };
@@ -80,10 +80,6 @@ var jsonLogger = (msg, ctx, level) => {
80
80
  };
81
81
  var log = void 0;
82
82
  function bindLogger(fn, level) {
83
- if (!fn) {
84
- log = void 0;
85
- return;
86
- }
87
83
  if (typeof fn === "function") {
88
84
  log = new BaseLogger(fn, level);
89
85
  return log;
@@ -1,2 +1,3 @@
1
- export { g as LogFn, L as Logger, M as MessageMetadata, f as bindLogger, e as coloredStringLogger, j as jsonLogger, s as stringLogger } from '../index-46ed19d8.js';
1
+ export { m as LogFn, L as Logger, M as MessageMetadata, l as bindLogger, j as coloredStringLogger, k as jsonLogger, s as stringLogger } from '../index-e2513701.js';
2
2
  import '@sinclair/typebox';
3
+ import '../types-3e5768ec.js';
@@ -1,2 +1,3 @@
1
- export { g as LogFn, L as Logger, M as MessageMetadata, f as bindLogger, e as coloredStringLogger, j as jsonLogger, s as stringLogger } from '../index-46ed19d8.js';
1
+ export { m as LogFn, L as Logger, M as MessageMetadata, l as bindLogger, j as coloredStringLogger, k as jsonLogger, s as stringLogger } from '../index-e2513701.js';
2
2
  import '@sinclair/typebox';
3
+ import '../types-3e5768ec.js';
@@ -3,7 +3,7 @@ import {
3
3
  coloredStringLogger,
4
4
  jsonLogger,
5
5
  stringLogger
6
- } from "../chunk-WER2DWCP.js";
6
+ } from "../chunk-NBE3D667.js";
7
7
  export {
8
8
  bindLogger,
9
9
  coloredStringLogger,
@@ -628,7 +628,8 @@ var ControlMessageCloseSchema = import_typebox3.Type.Object({
628
628
  var ControlMessageHandshakeRequestSchema = import_typebox3.Type.Object({
629
629
  type: import_typebox3.Type.Literal("HANDSHAKE_REQ"),
630
630
  protocolVersion: import_typebox3.Type.String(),
631
- sessionId: import_typebox3.Type.String()
631
+ sessionId: import_typebox3.Type.String(),
632
+ metadata: import_typebox3.Type.Optional(import_typebox3.Type.Unknown())
632
633
  });
633
634
  var ControlMessageHandshakeResponseSchema = import_typebox3.Type.Object({
634
635
  type: import_typebox3.Type.Literal("HANDSHAKE_RESP"),
@@ -1162,6 +1163,13 @@ var RiverServer = class {
1162
1163
  })
1163
1164
  );
1164
1165
  };
1166
+ if (session.metadata === void 0) {
1167
+ log?.error(
1168
+ `(invariant violation) session doesn't have handshake metadata`,
1169
+ session.loggingMetadata
1170
+ );
1171
+ return;
1172
+ }
1165
1173
  let inputHandler;
1166
1174
  const procHasInitMessage = "init" in procedure;
1167
1175
  const serviceContextWithTransportInfo = {
@@ -1169,6 +1177,7 @@ var RiverServer = class {
1169
1177
  to: message.to,
1170
1178
  from: message.from,
1171
1179
  streamId: message.streamId,
1180
+ // we've already validated that the session has handshake metadata
1172
1181
  session
1173
1182
  };
1174
1183
  switch (procedure.type) {
@@ -1344,7 +1353,7 @@ function createServer(transport, services, extendedContext) {
1344
1353
  }
1345
1354
 
1346
1355
  // package.json
1347
- var version = "0.18.5";
1356
+ var version = "0.19.2";
1348
1357
  // Annotate the CommonJS export names for ESM import in node:
1349
1358
  0 && (module.exports = {
1350
1359
  Err,