@observertc/observer-js 1.2.0 → 1.3.0-rc.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.
package/dist/index.d.mts CHANGED
@@ -1,6 +1,128 @@
1
1
  import { EventEmitter } from 'node:events';
2
2
  import { types } from 'mediasoup';
3
3
 
4
+ /** The lifecycle events a sink emits. */
5
+ type SinkEvents = {
6
+ /**
7
+ * `end()` was called: the sink accepts no more writes. **Required**: a sink emits it
8
+ * synchronously, once, from `end()` — it is how its registry removes it and frees its id.
9
+ */
10
+ ended: [];
11
+ /** The destination is fully written and closed (e.g. a file flushed and its fd closed). */
12
+ close: [];
13
+ /** The destination failed. */
14
+ error: [Error];
15
+ /** All queued data was flushed (before `close`). */
16
+ finish: [];
17
+ /** The buffer drained after backpressure; safe to write more. */
18
+ drain: [];
19
+ };
20
+ /** What a registry passes to a sink's constructor. Implementations extend it with their own settings. */
21
+ type SinkSettings = {
22
+ /** Unique per observer. The registry generates one (uuid v4) when it is omitted. */
23
+ id: string;
24
+ };
25
+ /**
26
+ * A destination for records. It is **object-mode** and agnostic about what passes through it:
27
+ * `write` receives the record itself, and the implementation decides how to serialize it (JSON
28
+ * line, protobuf, a remote payload, …). `JsonlFileSink` and `InMemorySink` are the built-in
29
+ * implementations; `ClientSampleSink` is `Sink<ClientSample>`.
30
+ *
31
+ * Create sinks with a registry's `create(SinkClass, settings)`, which constructs
32
+ * `new SinkClass({ ...settings, id })` and registers it on the object that owns its lifecycle.
33
+ * `end()` closes a sink and — through the `ended` event — removes it from its registry; the owner
34
+ * ends whatever is left when it closes.
35
+ *
36
+ * An implementation typically `extends EventEmitter implements Sink<T>`, and must:
37
+ * - emit `ended` synchronously, once, from `end()` (and make a second `end()` a no-op);
38
+ * - refuse writes after `end()` (return `false`);
39
+ * - emit `close` when the destination is ready, and `error` when it fails.
40
+ */
41
+ interface Sink<T = unknown> {
42
+ readonly id: string;
43
+ /** Accept one record. `false` once ended (nothing written), or on backpressure. */
44
+ write(record: T): boolean;
45
+ /** No more writes; emit `ended`, then flush and emit `close`. Idempotent. */
46
+ end(): void;
47
+ on(event: keyof SinkEvents, listener: (...args: any[]) => void): this;
48
+ once(event: keyof SinkEvents, listener: (...args: any[]) => void): this;
49
+ off(event: keyof SinkEvents, listener: (...args: any[]) => void): this;
50
+ }
51
+
52
+ /** A sink class `SinkRegistry.create` can construct: its constructor takes one settings object (with `id`). */
53
+ type SinkConstructor = new (settings: any) => Sink<any>;
54
+ /** The settings `create` takes for `SinkClass`: its constructor's, with `id` optional. */
55
+ type SinkCreateSettings<C extends SinkConstructor> = Omit<ConstructorParameters<C>[0], 'id'> & {
56
+ id?: string;
57
+ };
58
+ /**
59
+ * The sinks of one object, ended by that object's lifecycle. `create(SinkClass, settings)` builds a
60
+ * sink and registers it; `sink.end()` closes it and removes it; the owner calls `clear()` when it
61
+ * closes, ending whatever is left.
62
+ *
63
+ * Registries form a tree like the issue registries: `observer.sinks` is the parent of every
64
+ * `observedCall.sinks` and `observedMediasoupRouter.sinks`. **A sink id is unique per observer**:
65
+ * `create` refuses an id that any registry of the tree holds. Each registry sees its own sinks and
66
+ * its descendants', so `get`, `write` and `clear` on a parent reach a child's sinks too.
67
+ *
68
+ * A client's sample sink is the one under its client id in its call's registry.
69
+ */
70
+ declare class SinkRegistry {
71
+ readonly parent?: SinkRegistry | undefined;
72
+ private readonly _sinks;
73
+ constructor(parent?: SinkRegistry | undefined);
74
+ /** Number of open sinks in this registry and its descendants. */
75
+ get size(): number;
76
+ keys(): IterableIterator<string>;
77
+ values(): IterableIterator<Sink<any>>;
78
+ get<T extends Sink<any> = Sink>(id: string): T | undefined;
79
+ /**
80
+ * Construct `new SinkClass(settings)` and register it. The id (`settings.id`, generated when
81
+ * omitted) is checked first, so nothing is constructed — no file is opened — when it is taken:
82
+ * `undefined` is returned and a warning logged.
83
+ */
84
+ create<C extends SinkConstructor>(SinkClass: C, settings: SinkCreateSettings<C>): InstanceType<C> | undefined;
85
+ /** Write `record` to the sink `id`. `false` when there is no such sink, or on backpressure. */
86
+ write(id: string, record: unknown): boolean;
87
+ /** End every sink of this registry and of its descendants. */
88
+ clear(): void;
89
+ /** This registry and its ancestors, up to the root. */
90
+ private _lineage;
91
+ }
92
+
93
+ /** The settings of `createExtensionStats` on an observer, a call or a mediasoup router. */
94
+ type CreateExtensionStatsSettings<C extends SinkConstructor> = {
95
+ /** Unique per observer: it is also the id of the sink that receives the stats. */
96
+ id: string;
97
+ /** What `sinks.create` takes; the sink is created under `id` (an `id` in its settings is ignored). */
98
+ createSink?: [SinkClass: C, settings: Omit<SinkCreateSettings<C>, 'id'>];
99
+ };
100
+ /**
101
+ * The extension stats of one object — `observer.extensionStats`, `observedCall.extensionStats`,
102
+ * `observedMediasoupRouter.extensionStats`: the last data per id (`values`), written to the
103
+ * object's sink with the same id. The owner closes it when it closes.
104
+ */
105
+ declare class ExtensionStatsRegistry {
106
+ /** The owner's id (call id, router id, or `observer`), used in log messages. */
107
+ readonly id: string;
108
+ private readonly _sinks;
109
+ /** The last data accepted for each extension stats id. */
110
+ readonly values: Map<string, unknown>;
111
+ closed: boolean;
112
+ constructor(
113
+ /** The owner's id (call id, router id, or `observer`), used in log messages. */
114
+ id: string, _sinks: SinkRegistry);
115
+ /**
116
+ * Refuse further `create` / `accept`, and end the extension stats' sinks (the sinks whose id is
117
+ * an extension stats id). Other sinks of the owner, e.g. client sample sinks, are left to the
118
+ * owner's `sinks.clear()`. Called by the owner when it closes.
119
+ */
120
+ close(): void;
121
+ create<C extends SinkConstructor>(settings: CreateExtensionStatsSettings<C>): InstanceType<C> | undefined;
122
+ accept(id: string, data: unknown): void;
123
+ get<T>(id: string): T | undefined;
124
+ }
125
+
4
126
  declare const schemaVersion = "3.7.0";
5
127
  /**
6
128
  * The WebRTC app provided custom stats payload
@@ -1947,7 +2069,9 @@ declare function mannKendallVerdict(s: number, variance: number, alpha?: number)
1947
2069
  * holds the values in `[i * bucketWidth, (i + 1) * bucketWidth)`. Interpolates linearly inside
1948
2070
  * the bucket the rank falls in, so the error is at most one bucket width. With `openEnded`, the
1949
2071
  * last bucket holds every value above its lower bound, and a percentile falling in it is reported
1950
- * as `max`. The estimate is clamped to `[min, max]` when given. `undefined` for an empty histogram.
2072
+ * as `max`. With `min`/`max` (the observed extremes), the bucket holding them is narrowed to the
2073
+ * observed range before interpolating, so the estimate never leaves `[min, max]`. `undefined` for an
2074
+ * empty histogram.
1951
2075
  */
1952
2076
  declare function percentileOfHistogram(histogram: readonly number[], bucketWidth: number, p: number, options?: {
1953
2077
  min?: number;
@@ -3023,52 +3147,38 @@ declare function isClientIssueResolutionEntry(issue: ClientIssue): boolean;
3023
3147
  /** Strip the `-resolved` suffix, so both entries of a lifecycle share one logical type. */
3024
3148
  declare function baseIssueType(type: string): string;
3025
3149
 
3026
- /** The lifecycle events a sink may emit (a subset of Node's writable-stream events). */
3027
- type ClientSampleSinkEvents = {
3028
- /** The destination is fully written and closed (e.g. a file flushed and its fd closed). */
3029
- close: [];
3030
- /** The destination failed. */
3031
- error: [Error];
3032
- /** `end()` was processed and all queued data was flushed (before `close`). */
3033
- finish: [];
3034
- /** The buffer drained after backpressure; safe to write more. */
3035
- drain: [];
3150
+ type JsonlFileSinkOptions<T = ClientSample> = Partial<SinkSettings> & {
3151
+ /** Absolute or relative file path to append JSONL lines to. */
3152
+ path: string;
3153
+ /** Optional custom serializer; defaults to `JSON.stringify`. */
3154
+ serializeSample?: (record: T) => string;
3036
3155
  };
3037
- declare interface ClientSampleSink {
3038
- on<E extends keyof ClientSampleSinkEvents>(event: E, listener: (...args: ClientSampleSinkEvents[E]) => void): this;
3039
- once<E extends keyof ClientSampleSinkEvents>(event: E, listener: (...args: ClientSampleSinkEvents[E]) => void): this;
3040
- off<E extends keyof ClientSampleSinkEvents>(event: E, listener: (...args: ClientSampleSinkEvents[E]) => void): this;
3041
- emit<E extends keyof ClientSampleSinkEvents>(event: E, ...args: ClientSampleSinkEvents[E]): boolean;
3042
- }
3043
3156
  /**
3044
- * A per-client destination for accepted samples — a typed `EventEmitter` base class. One sink is
3045
- * created per `ObservedClient` (via `ObserverConfig.createClientSink`). It is **object-mode**:
3046
- * `write` receives the `ClientSample` itself, so each sink decides how to serialize it (JSON
3047
- * line, protobuf, msgpack, a remote payload, …).
3048
- *
3049
- * The library calls `write(sample)` synchronously per accepted sample (not awaited), `end()`s the
3050
- * sink when the client closes, and attaches an `error` listener so a failing sink can't crash the
3051
- * process. The application — which created the sink — listens for `close` (destination ready,
3052
- * e.g. a file flushed and its fd closed) and `error`.
3053
- *
3054
- * Subclass this and implement `write`/`end` (and `emit` your lifecycle events). Built-in sinks
3055
- * (`JsonlFileSink`, `InMemorySink`) and their factories are exported from the package root.
3157
+ * A sink that serializes each record to a JSON line and appends it to a file. Defaults to client
3158
+ * samples, but takes any record type: `sinks.create(JsonlFileSink<PipeTransportStats[]>, { id, path })`. It wraps an
3159
+ * `fs.WriteStream` and re-emits its lifecycle events, so `close` fires once the file is flushed
3160
+ * and its descriptor is closed (file ready) and `error` surfaces file errors.
3056
3161
  */
3057
- declare abstract class ClientSampleSink extends EventEmitter {
3058
- /** Accept one sample; the sink serializes it however it likes. `false` signals backpressure. */
3059
- abstract write(sample: ClientSample): boolean;
3060
- /** Finish writing; the sink flushes and (if applicable) emits `close`. */
3061
- abstract end(): void;
3162
+ declare interface JsonlFileSink {
3163
+ on<E extends keyof SinkEvents>(event: E, listener: (...args: SinkEvents[E]) => void): this;
3164
+ once<E extends keyof SinkEvents>(event: E, listener: (...args: SinkEvents[E]) => void): this;
3165
+ off<E extends keyof SinkEvents>(event: E, listener: (...args: SinkEvents[E]) => void): this;
3166
+ emit<E extends keyof SinkEvents>(event: E, ...args: SinkEvents[E]): boolean;
3062
3167
  }
3063
- /**
3064
- * Produces a sink for a newly-created client, or `undefined` for no sink. Receives the
3065
- * client id and its (already-created) parent call, so the destination can be derived
3066
- * from `callId` / `clientId`.
3067
- */
3068
- type ClientSampleSinkFactory = (params: {
3069
- clientId: string;
3070
- observedCall: ObservedCall;
3071
- }) => ClientSampleSink | undefined;
3168
+ declare class JsonlFileSink<T = ClientSample> extends EventEmitter implements Sink<T> {
3169
+ readonly id: string;
3170
+ /** The file this sink writes to. Read it (e.g. in a `close` handler) to upload/move the file. */
3171
+ readonly path: string;
3172
+ private readonly _stream;
3173
+ private readonly _serializeSample;
3174
+ private _ended;
3175
+ constructor(options: JsonlFileSinkOptions<T>);
3176
+ get ended(): boolean;
3177
+ write(record: T): boolean;
3178
+ end(): void;
3179
+ }
3180
+ /** Build a JSONL file sink for a single path. */
3181
+ declare function createJsonlFileSink<T = ClientSample>(options: JsonlFileSinkOptions<T>): JsonlFileSink<T>;
3072
3182
 
3073
3183
  /** mediasoup `TransportTuple` (the selected local/remote address pair of a transport). */
3074
3184
  type TransportTuple = {
@@ -3320,13 +3430,19 @@ declare class ObservedMediasoupRouter<AppData extends Record<string, unknown> =
3320
3430
  readonly sample: MediasoupRouterSample;
3321
3431
  readonly webrtcTransportIds: Set<string>;
3322
3432
  closed: boolean;
3433
+ readonly sinks: SinkRegistry;
3434
+ readonly extensionStats: ExtensionStatsRegistry;
3323
3435
  private readonly _transportSamples;
3324
3436
  private readonly _producerSamples;
3325
3437
  private readonly _consumerSamples;
3326
3438
  private readonly _dataProducerSamples;
3327
3439
  private readonly _dataConsumerSamples;
3328
3440
  private readonly _enrich?;
3329
- constructor(settings: ObservedMediasoupRouterSettings<AppData>);
3441
+ /**
3442
+ * @param parentSinks the parent of `sinks`: `observer.sinks`, passed by
3443
+ * `Observer.createObservedMediasoupRouter`. Internal; a router constructed directly has none.
3444
+ */
3445
+ constructor(settings: ObservedMediasoupRouterSettings<AppData>, parentSinks?: SinkRegistry);
3330
3446
  get id(): string;
3331
3447
  get attachments(): Record<string, unknown>;
3332
3448
  getTransportSample(id: string): MediasoupTransportSample | undefined;
@@ -3351,6 +3467,9 @@ declare class ObservedMediasoupRouter<AppData extends Record<string, unknown> =
3351
3467
  */
3352
3468
  snapshot(): MediasoupRouterSample;
3353
3469
  close(): void;
3470
+ createExtensionStats<C extends SinkConstructor>(settings: CreateExtensionStatsSettings<C>): InstanceType<C> | undefined;
3471
+ acceptExtensionStats(id: string, data: unknown): void;
3472
+ getExtensionStats<T>(id: string): T | undefined;
3354
3473
  addTransport: (transport: types.Transport) => void;
3355
3474
  addWebRtcTransport(transport: types.WebRtcTransport): void;
3356
3475
  addPlainTransport(transport: types.PlainTransport): void;
@@ -3446,7 +3565,7 @@ type ObserverEvents = {
3446
3565
  }];
3447
3566
  'client-added': [ObservedClientScope];
3448
3567
  'client-sink-created': [ObservedClientScope & {
3449
- sink: ClientSampleSink;
3568
+ sink: JsonlFileSink;
3450
3569
  }];
3451
3570
  'client-updated': [ObservedClientScope & {
3452
3571
  sample: ClientSample;
@@ -3755,8 +3874,6 @@ declare class ObservedClient<AppData extends Record<string, unknown> = Record<st
3755
3874
  readonly call: ObservedCall;
3756
3875
  readonly clientId: string;
3757
3876
  readonly observedPeerConnections: Map<string, ObservedPeerConnection>;
3758
- /** Per-client sink (from `ObserverConfig.createClientSink`), if any. */
3759
- readonly sink?: ClientSampleSink;
3760
3877
  /** Ancestry base shared by all Observer-bus events originating at this client. */
3761
3878
  readonly eventScope: ObservedClientScope;
3762
3879
  readonly calculatedScore: CalculatedScore;
@@ -3886,6 +4003,11 @@ declare class ObservedClient<AppData extends Record<string, unknown> = Record<st
3886
4003
  private closeTimer?;
3887
4004
  constructor(settings: ObservedClientSettings<AppData>, call: ObservedCall, activeIssues: ObservedClientIssueRegistry);
3888
4005
  /** The distinct `publicAddress`es currently known by this client's open peer connections. */
4006
+ /**
4007
+ * The sink this client's samples are written to: whatever is registered under its client id in
4008
+ * `call.sinks` (the `createClientJsonlSink` file, or a sink the application added), if any.
4009
+ */
4010
+ get sink(): Sink<ClientSample> | undefined;
3889
4011
  get publicAddresses(): string[];
3890
4012
  get numberOfPeerConnections(): number;
3891
4013
  get score(): number | undefined;
@@ -5698,6 +5820,8 @@ declare class ObservedCall<AppData extends Record<string, unknown> = Record<stri
5698
5820
  readonly settings: Pick<ObservedCallSettings, 'closeCallIfEmptyForMs' | 'autoUpdateOnClientUpdate'>;
5699
5821
  /** Ancestry base shared by all Observer-bus events originating at this call. */
5700
5822
  readonly eventScope: ObservedCallScope;
5823
+ readonly sinks: SinkRegistry;
5824
+ readonly extensionStats: ExtensionStatsRegistry;
5701
5825
  private closeTimer?;
5702
5826
  constructor(settings: ObservedCallSettings<AppData>, observer: Observer, activeIssuesRegistry: ActiveIssuesRegistry);
5703
5827
  get numberOfClients(): number;
@@ -5750,6 +5874,9 @@ declare class ObservedCall<AppData extends Record<string, unknown> = Record<stri
5750
5874
  */
5751
5875
  addIssue(issue: Omit<CallIssue, 'scope'>): void;
5752
5876
  close(): void;
5877
+ createExtensionStats<C extends SinkConstructor>(settings: CreateExtensionStatsSettings<C>): InstanceType<C> | undefined;
5878
+ acceptExtensionStats(id: string, data: unknown): void;
5879
+ getExtensionStats<T>(id: string): T | undefined;
5753
5880
  getObservedClient<ClientAppData extends Record<string, unknown> = Record<string, unknown>>(clientId: string): ObservedClient<ClientAppData> | undefined;
5754
5881
  createObservedClient<ClientAppData extends Record<string, unknown> = Record<string, unknown>>(settings: ObservedClientSettings<ClientAppData>, acceptCtx?: AcceptContext): ObservedClient<ClientAppData> | undefined;
5755
5882
  getOrCreateObservedClient<ClientAppData extends Record<string, unknown> = Record<string, unknown>>(settings: ObservedClientSettings<ClientAppData>, acceptCtx?: AcceptContext): ObservedClient<ClientAppData> | undefined;
@@ -6471,11 +6598,38 @@ type ObserverConfig<AppData extends Record<string, unknown> = Record<string, unk
6471
6598
  /** Same as `createCallAppData`, for clients. Receives the (already-created) parent call. */
6472
6599
  createClientAppData?: ClientAppDataFactory;
6473
6600
  /**
6474
- * Optional factory invoked when a client is created, producing a per-client sink that
6475
- * receives every sample the client accepts (or `undefined` for no sink). The destination
6476
- * can be derived from `callId` / `clientId`.
6601
+ * Write every sample a client accepts to a JSONL file: return the file path for a newly-created
6602
+ * client, or `undefined` for no file. The directory must exist. The `JsonlFileSink` is added to
6603
+ * `observedCall.sinks` under the client id and is ended when the client closes; `client-sink-created`
6604
+ * announces it, so a `close` handler can upload or move the file once it is flushed.
6605
+ *
6606
+ * For any other destination, add your own sink under the client id instead, e.g. on
6607
+ * `client-added`: `observedCall.sinks.create(MySink, { id: observedClient.clientId, ... })`.
6608
+ *
6609
+ * Replaces `createClientSink` (removed in 1.3.0), and with it `createJsonlFileSinkFactory`.
6610
+ * To migrate:
6611
+ *
6612
+ * ```ts
6613
+ * // before (1.2): JSONL files
6614
+ * new Observer({ createClientSink: createJsonlFileSinkFactory({ directory: './stats' }) });
6615
+ * // after: return the path; `${callId}__${clientId}.jsonl` was the factory's default file name
6616
+ * new Observer({
6617
+ * createClientJsonlSink: ({ observedCall, clientId }) => path.join('./stats', `${observedCall.callId}__${clientId}.jsonl`),
6618
+ * });
6619
+ *
6620
+ * // before (1.2): any other sink
6621
+ * new Observer({ createClientSink: ({ clientId, observedCall }) => new HttpSink(url(observedCall, clientId)) });
6622
+ * // after: create it under the client id when the client is added (HttpSink implements
6623
+ * // `ClientSampleSink`, takes `SinkSettings & { ... }` and emits `ended` from `end()`)
6624
+ * observer.on('client-added', ({ observedCall, observedClient }) => {
6625
+ * observedCall.sinks.create(HttpSink, { id: observedClient.clientId, url: url(observedCall, observedClient.clientId) });
6626
+ * });
6627
+ * ```
6477
6628
  */
6478
- createClientSink?: ClientSampleSinkFactory;
6629
+ createClientJsonlSink?: (params: {
6630
+ observedCall: ObservedCall;
6631
+ clientId: string;
6632
+ }) => string | undefined;
6479
6633
  /**
6480
6634
  * Optional factory invoked when a call is created, producing the call's `RemoteTrackResolver`
6481
6635
  * (or `undefined` for none). Use the built-ins
@@ -6494,6 +6648,8 @@ declare class Observer<AppData extends Record<string, unknown> = Record<string,
6494
6648
  readonly observedTURN: ObservedTURN;
6495
6649
  readonly observedCalls: Map<string, ObservedCall<Record<string, unknown>>>;
6496
6650
  readonly observedMediasoupRouters: Map<string, ObservedMediasoupRouter<Record<string, unknown>>>;
6651
+ readonly sinks: SinkRegistry;
6652
+ readonly extensionStats: ExtensionStatsRegistry;
6497
6653
  /** Ancestry base shared by all Observer-bus events originating at the observer. */
6498
6654
  readonly eventScope: ObserverEventBase;
6499
6655
  readonly config: ObserverConfig<AppData>;
@@ -6644,6 +6800,9 @@ declare class Observer<AppData extends Record<string, unknown> = Record<string,
6644
6800
  matchPeerConnectionByWebRtcTransportId?: boolean;
6645
6801
  }): ObservedMediasoupRouter<Record<string, unknown>> | undefined;
6646
6802
  close(): void;
6803
+ createExtensionStats<C extends SinkConstructor>(settings: CreateExtensionStatsSettings<C>): InstanceType<C> | undefined;
6804
+ acceptExtensionStats(id: string, data: unknown): void;
6805
+ getExtensionStats<T>(id: string): T | undefined;
6647
6806
  accept(sample: ClientSample, context?: AcceptContext): void;
6648
6807
  update(): void;
6649
6808
  /**
@@ -7037,52 +7196,38 @@ declare function createLogger(moduleName: string): Logger;
7037
7196
  */
7038
7197
  declare function setObserverLogger(logger: ObserverLogger): void;
7039
7198
 
7040
- type JsonlFileSinkOptions = {
7041
- /** Absolute or relative file path to append JSONL lines to. */
7042
- path: string;
7043
- /** Optional custom serializer; defaults to `JSON.stringify`. */
7044
- serializeSample?: (sample: ClientSample) => string;
7045
- };
7199
+ /** The lifecycle events a client sample sink may emit. Same as {@link SinkEvents}. */
7200
+ type ClientSampleSinkEvents = SinkEvents;
7046
7201
  /**
7047
- * A sink that serializes each sample to a JSON line and appends it to a file. It wraps an
7048
- * `fs.WriteStream` and re-emits its lifecycle events, so `close` fires once the file is flushed
7049
- * and its descriptor is closed (file ready) and `error` surfaces file errors.
7202
+ * A per-client destination for accepted samples. Create one under the client id with
7203
+ * `observedCall.sinks.create(MySink, { id: clientId, ... })`, or let
7204
+ * `ObserverConfig.createClientJsonlSink` create a `JsonlFileSink` there.
7205
+ *
7206
+ * The library calls `write(sample)` synchronously per accepted sample (not awaited) and `end()`s
7207
+ * the sink when the client closes; the registry attaches an `error` listener so a failing sink
7208
+ * can't crash the process. Because `write` isn't awaited, backpressure and batching are the sink's
7209
+ * concern.
7050
7210
  */
7051
- declare class JsonlFileSink extends ClientSampleSink {
7052
- /** The file this sink writes to. Read it (e.g. in a `close` handler) to upload/move the file. */
7053
- readonly path: string;
7054
- private readonly _stream;
7055
- private readonly _serializeSample;
7056
- constructor(options: JsonlFileSinkOptions);
7057
- write(sample: ClientSample): boolean;
7058
- end(): void;
7059
- }
7060
- /** Build a JSONL file sink for a single path. */
7061
- declare function createJsonlFileSink(options: JsonlFileSinkOptions): ClientSampleSink;
7062
- type JsonlFileSinkFactoryOptions = {
7063
- /** Directory the per-client files are written into (must exists). */
7064
- directory: string;
7065
- /** Builds the file name from the call/client ids. Defaults to `${callId}__${clientId}.jsonl`. */
7066
- getFileName?: (params: {
7067
- callId: string;
7068
- clientId: string;
7069
- }) => string;
7070
- /** Optional custom serializer; defaults to `JSON.stringify`. */
7071
- serializeSample?: (sample: ClientSample) => string;
7072
- };
7073
- /**
7074
- * Convenience factory that writes one JSONL file per client, with the path derived from
7075
- * `callId` / `clientId`. Plug into `ObserverConfig.createClientSink`.
7076
- */
7077
- declare function createJsonlFileSinkFactory(options: JsonlFileSinkFactoryOptions): ClientSampleSinkFactory;
7211
+ type ClientSampleSink = Sink<ClientSample>;
7078
7212
 
7213
+ declare interface InMemorySink {
7214
+ on<E extends keyof SinkEvents>(event: E, listener: (...args: SinkEvents[E]) => void): this;
7215
+ once<E extends keyof SinkEvents>(event: E, listener: (...args: SinkEvents[E]) => void): this;
7216
+ off<E extends keyof SinkEvents>(event: E, listener: (...args: SinkEvents[E]) => void): this;
7217
+ emit<E extends keyof SinkEvents>(event: E, ...args: SinkEvents[E]): boolean;
7218
+ }
7079
7219
  /**
7080
- * A `ClientSampleSink` that collects the accepted sample objects into an array. Useful for
7081
- * tests and offline replay. Emits `close` on `end()`.
7220
+ * A client sample sink that collects the accepted sample objects into an array. Useful for tests
7221
+ * and offline replay. Emits `ended` and `close` on `end()`.
7082
7222
  */
7083
- declare class InMemorySink extends ClientSampleSink {
7223
+ declare class InMemorySink extends EventEmitter implements Sink<ClientSample> {
7224
+ readonly id: string;
7084
7225
  readonly samples: ClientSample[];
7085
- constructor(samples?: ClientSample[]);
7226
+ private _ended;
7227
+ constructor(settings?: Partial<SinkSettings> & {
7228
+ samples?: ClientSample[];
7229
+ });
7230
+ get ended(): boolean;
7086
7231
  write(sample: ClientSample): boolean;
7087
7232
  end(): void;
7088
7233
  }
@@ -7091,4 +7236,4 @@ declare function createInMemorySink(samples?: ClientSample[]): InMemorySink;
7091
7236
  declare function createDefaultMediasoupRemoteTrackResolverFactory(): RemoteTrackResolverFactory;
7092
7237
  declare function createP2pRemoteTrackResolverFactory(): RemoteTrackResolverFactory;
7093
7238
 
7094
- export { type AcceptContext, type AcceptMiddleware, type AcceptMiddlewarePayload, type ActiveClientIssue, type ActiveIssueTracker, ActiveIssuesRegistry, AudioEchoDetector, type AudioEchoDetectorConfig, AudioEchoTypes, type AudioInputDevice, type AvailableCallScopeDetectorsConfigs, type AvailableDetectorsConfigs, type AvailableObserverScopeDetectorsConfigs, type AvailableValidatorConfigs, CODEC_MISMATCH_ISSUE, type CalculatedScore, type CallAppDataFactory, CallConcurrentIssueDetector, type CallConcurrentIssueDetectorConfig, type CallConcurrentIssueGroup, CallConcurrentIssueTypes, type CallHealth, CallHealthAggregator, type CallIssue, type CallIssueSpread, type CallScopedEventName, type CallSummary, type CallSummaryClients, CallSummaryCollector, type CallSummaryConfig, type CallSummaryEnricher, type CallSummaryEnrichers, type CallSummaryScores, type CallSummarySection, type CallSummaryTruncation, type CallSummaryTurnServers, type CertificateStats, type ClientAppDataFactory, type ClientEvent, ClientEventTypes, type ClientHealth, type ClientHealthThresholds, type ClientIssue, type ClientLocation, type ClientLocationResolver, type ClientMetaData, ClientMetaTypes, type ClientPopulation, type ClientPopulationAxis, ClientPopulationIssueDetector, type ClientPopulationIssueDetectorConfig, ClientPopulationIssueTypes, type ClientSample, ClientSampleSink, type ClientSampleSinkEvents, type ClientSampleSinkFactory, type CodecConsistencyReportPayload, CodecConsistencyValidator, type CodecConsistencyValidatorConfig, type CodecEvidence, type CodecStats, type CorroboratedPublisherFault, type DataChannelStats, type Detector, Detectors, type ExtensionStat, GEOHASH_CELL_SIZES, type IceCandidatePairStats, type IceCandidateStats, type IceTransportStats, InMemorySink, type InboundRtpStats, type InboundTrackSample, type Issue, type IssueBase, type IssueConclusion, IssueFanOutDetector, type IssueFanOutDetectorConfig, IssueFanOutTypes, type IssueFaultDomain, JsonlFileSink, type JsonlFileSinkFactoryOptions, type JsonlFileSinkOptions, LOWEST_COMMON_DENOMINATOR_ISSUE, type Logger, type MannKendallResult, type MediaKind, type MediaPlayoutStats, type MediaSourceStats, type MediasoupConsumerSample, type MediasoupConsumerSampleEvent, type MediasoupDataConsumerSample, type MediasoupDataProducerSample, type MediasoupDirectTransportSample, type MediasoupDirectTransportSampleEventMap, type MediasoupPipeTransportSample, type MediasoupPipeTransportSampleEventMap, type MediasoupPlainTransportSample, type MediasoupPlainTransportSampleEventMap, type MediasoupProducerSample, type MediasoupProducerSampleEvent, type MediasoupRouterSample, type MediasoupSampleEnricher, type MediasoupTransportSample, type MediasoupWebRtcTransportSample, type MediasoupWebRtcTransportSampleEventMap, type Middleware, MiddlewareProcessor, ObservedCall, type ObservedCallScope, type ObservedCallSettings, ObservedCertificate, ObservedClient, ObservedClientIssueRegistry, type ObservedClientScope, type ObservedClientSettings, ObservedCodec, ObservedDataChannel, ObservedIceCandidate, ObservedIceCandidatePair, ObservedIceTransport, ObservedInboundRtp, ObservedInboundTrack, ObservedMediaPlayout, ObservedMediaSource, ObservedMediasoupRouter, type ObservedMediasoupRouterEvents, type ObservedMediasoupRouterScope, type ObservedMediasoupRouterSettings, ObservedOutboundRtp, ObservedOutboundTrack, ObservedPeerConnection, type ObservedPeerConnectionScope, ObservedPeerConnectionTransport, ObservedRemoteInboundRtp, ObservedRemoteOutboundRtp, ObservedTURN, type ObservedTURNEventMap, ObservedTurnServer, Observer, ObserverConcurrentIssueDetector, type ObserverConcurrentIssueDetectorConfig, type ObserverConcurrentIssueGroup, ObserverConcurrentIssueTypes, type ObserverEventBase, type ObserverEvents, type ObserverIssue, type ObserverIssueSpread, type ObserverLogger, type OutboundRtpStats, type OutboundTrackSample, type PageHinkleyResult, type PeerConnectionSample, type PeerConnectionTransportStats, type PsnrSum, PublisherFaultCorroborationDetector, type PublisherFaultCorroborationDetectorConfig, PublisherFaultTypes, type QualityLimitationDurations, RESOLVED_ISSUE_SUFFIX, RTT_HISTOGRAM_BUCKET_WIDTH_IN_MS, RTT_HISTOGRAM_NUMBER_OF_BUCKETS, type RemoteInboundRtpStats, type RemoteOutboundRtpStats, type RemoteTrackLinkEvidence, RemoteTrackResolver, type RemoteTrackResolverFactory, type RemoteTrackResolverReportPayload, RemoteTrackResolverValidator, type RemoteTrackResolverValidatorConfig, type RemoteTrackResolvers, type ResolvedActiveClientIssue, type RttSummary, type RunningValidator, SCORE_HISTOGRAM_BUCKET_WIDTH, SCORE_HISTOGRAM_NUMBER_OF_BUCKETS, type SampleRejectedReason, type ScoreCalculator, type ScoreSummary, SfuCongestionDetector, type SfuCongestionDetectorBucket, type SfuCongestionDetectorConfig, type SfuCongestionDetectorEvaluation, type SfuCongestionDetectorReport, type SimulcastReceiverEvidence, type SimulcastReceiverReportPayload, SimulcastReceiverValidator, type SimulcastReceiverValidatorConfig, SlidingWindow, type SlidingWindowEntry, type StatsSummary, TrackDeliveryMismatchDetector, type TrackDeliveryMismatchDetectorConfig, TrackDeliveryMismatchTypes, TrendTester, type TrendTesterConfig, type TurnServerHealth, TurnServerHealthDetector, type TurnServerHealthDetectorConfig, TurnServerHealthTypes, TurnServerOutageDetector, type TurnServerOutageDetectorConfig, TurnServerOutageTypes, UNRESOLVED_TRACK_LINKS_ISSUE, UnconsumedTrackDetector, type UnconsumedTrackDetectorConfig, UnconsumedTrackTypes, type ValidationReport, type Validator, type ValidatorName, baseIssueType, concludeCallIssue, concludeObserverIssue, correlation, counterDelta, createCallSummary, createDefaultMediasoupRemoteTrackResolverFactory, createInMemorySink, createJsonlFileSink, createJsonlFileSinkFactory, createLogger, createP2pRemoteTrackResolverFactory, defaultCallSummaryConfig, defaultClientHealthThresholds, geohash, isClientIssueResolutionEntry, isPublicIpAddress, issuePayloadAsString, mannKendall, mannKendallVerdict, median, medianAbsoluteDeviation, pageHinkley, percentile, percentileOfHistogram, percentileOfSorted, robustZScore, schemaVersion, setObserverLogger, summarize };
7239
+ export { type AcceptContext, type AcceptMiddleware, type AcceptMiddlewarePayload, type ActiveClientIssue, type ActiveIssueTracker, ActiveIssuesRegistry, AudioEchoDetector, type AudioEchoDetectorConfig, AudioEchoTypes, type AudioInputDevice, type AvailableCallScopeDetectorsConfigs, type AvailableDetectorsConfigs, type AvailableObserverScopeDetectorsConfigs, type AvailableValidatorConfigs, CODEC_MISMATCH_ISSUE, type CalculatedScore, type CallAppDataFactory, CallConcurrentIssueDetector, type CallConcurrentIssueDetectorConfig, type CallConcurrentIssueGroup, CallConcurrentIssueTypes, type CallHealth, CallHealthAggregator, type CallIssue, type CallIssueSpread, type CallScopedEventName, type CallSummary, type CallSummaryClients, CallSummaryCollector, type CallSummaryConfig, type CallSummaryEnricher, type CallSummaryEnrichers, type CallSummaryScores, type CallSummarySection, type CallSummaryTruncation, type CallSummaryTurnServers, type CertificateStats, type ClientAppDataFactory, type ClientEvent, ClientEventTypes, type ClientHealth, type ClientHealthThresholds, type ClientIssue, type ClientLocation, type ClientLocationResolver, type ClientMetaData, ClientMetaTypes, type ClientPopulation, type ClientPopulationAxis, ClientPopulationIssueDetector, type ClientPopulationIssueDetectorConfig, ClientPopulationIssueTypes, type ClientSample, type ClientSampleSink, type ClientSampleSinkEvents, type CodecConsistencyReportPayload, CodecConsistencyValidator, type CodecConsistencyValidatorConfig, type CodecEvidence, type CodecStats, type CorroboratedPublisherFault, type CreateExtensionStatsSettings, type DataChannelStats, type Detector, Detectors, type ExtensionStat, ExtensionStatsRegistry, GEOHASH_CELL_SIZES, type IceCandidatePairStats, type IceCandidateStats, type IceTransportStats, InMemorySink, type InboundRtpStats, type InboundTrackSample, type Issue, type IssueBase, type IssueConclusion, IssueFanOutDetector, type IssueFanOutDetectorConfig, IssueFanOutTypes, type IssueFaultDomain, JsonlFileSink, type JsonlFileSinkOptions, LOWEST_COMMON_DENOMINATOR_ISSUE, type Logger, type MannKendallResult, type MediaKind, type MediaPlayoutStats, type MediaSourceStats, type MediasoupConsumerSample, type MediasoupConsumerSampleEvent, type MediasoupDataConsumerSample, type MediasoupDataProducerSample, type MediasoupDirectTransportSample, type MediasoupDirectTransportSampleEventMap, type MediasoupPipeTransportSample, type MediasoupPipeTransportSampleEventMap, type MediasoupPlainTransportSample, type MediasoupPlainTransportSampleEventMap, type MediasoupProducerSample, type MediasoupProducerSampleEvent, type MediasoupRouterSample, type MediasoupSampleEnricher, type MediasoupTransportSample, type MediasoupWebRtcTransportSample, type MediasoupWebRtcTransportSampleEventMap, type Middleware, MiddlewareProcessor, ObservedCall, type ObservedCallScope, type ObservedCallSettings, ObservedCertificate, ObservedClient, ObservedClientIssueRegistry, type ObservedClientScope, type ObservedClientSettings, ObservedCodec, ObservedDataChannel, ObservedIceCandidate, ObservedIceCandidatePair, ObservedIceTransport, ObservedInboundRtp, ObservedInboundTrack, ObservedMediaPlayout, ObservedMediaSource, ObservedMediasoupRouter, type ObservedMediasoupRouterEvents, type ObservedMediasoupRouterScope, type ObservedMediasoupRouterSettings, ObservedOutboundRtp, ObservedOutboundTrack, ObservedPeerConnection, type ObservedPeerConnectionScope, ObservedPeerConnectionTransport, ObservedRemoteInboundRtp, ObservedRemoteOutboundRtp, ObservedTURN, type ObservedTURNEventMap, ObservedTurnServer, Observer, ObserverConcurrentIssueDetector, type ObserverConcurrentIssueDetectorConfig, type ObserverConcurrentIssueGroup, ObserverConcurrentIssueTypes, type ObserverEventBase, type ObserverEvents, type ObserverIssue, type ObserverIssueSpread, type ObserverLogger, type OutboundRtpStats, type OutboundTrackSample, type PageHinkleyResult, type PeerConnectionSample, type PeerConnectionTransportStats, type PsnrSum, PublisherFaultCorroborationDetector, type PublisherFaultCorroborationDetectorConfig, PublisherFaultTypes, type QualityLimitationDurations, RESOLVED_ISSUE_SUFFIX, RTT_HISTOGRAM_BUCKET_WIDTH_IN_MS, RTT_HISTOGRAM_NUMBER_OF_BUCKETS, type RemoteInboundRtpStats, type RemoteOutboundRtpStats, type RemoteTrackLinkEvidence, RemoteTrackResolver, type RemoteTrackResolverFactory, type RemoteTrackResolverReportPayload, RemoteTrackResolverValidator, type RemoteTrackResolverValidatorConfig, type RemoteTrackResolvers, type ResolvedActiveClientIssue, type RttSummary, type RunningValidator, SCORE_HISTOGRAM_BUCKET_WIDTH, SCORE_HISTOGRAM_NUMBER_OF_BUCKETS, type SampleRejectedReason, type ScoreCalculator, type ScoreSummary, SfuCongestionDetector, type SfuCongestionDetectorBucket, type SfuCongestionDetectorConfig, type SfuCongestionDetectorEvaluation, type SfuCongestionDetectorReport, type SimulcastReceiverEvidence, type SimulcastReceiverReportPayload, SimulcastReceiverValidator, type SimulcastReceiverValidatorConfig, type Sink, type SinkConstructor, type SinkCreateSettings, type SinkEvents, SinkRegistry, type SinkSettings, SlidingWindow, type SlidingWindowEntry, type StatsSummary, TrackDeliveryMismatchDetector, type TrackDeliveryMismatchDetectorConfig, TrackDeliveryMismatchTypes, TrendTester, type TrendTesterConfig, type TurnServerHealth, TurnServerHealthDetector, type TurnServerHealthDetectorConfig, TurnServerHealthTypes, TurnServerOutageDetector, type TurnServerOutageDetectorConfig, TurnServerOutageTypes, UNRESOLVED_TRACK_LINKS_ISSUE, UnconsumedTrackDetector, type UnconsumedTrackDetectorConfig, UnconsumedTrackTypes, type ValidationReport, type Validator, type ValidatorName, baseIssueType, concludeCallIssue, concludeObserverIssue, correlation, counterDelta, createCallSummary, createDefaultMediasoupRemoteTrackResolverFactory, createInMemorySink, createJsonlFileSink, createLogger, createP2pRemoteTrackResolverFactory, defaultCallSummaryConfig, defaultClientHealthThresholds, geohash, isClientIssueResolutionEntry, isPublicIpAddress, issuePayloadAsString, mannKendall, mannKendallVerdict, median, medianAbsoluteDeviation, pageHinkley, percentile, percentileOfHistogram, percentileOfSorted, robustZScore, schemaVersion, setObserverLogger, summarize };