@observertc/observer-js 1.3.0-rc.3 → 1.3.0-rc.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -404,7 +404,7 @@ See [Mediasoup router observation](#mediasoup-router-observation) for the full d
404
404
  | Event | Extra | Fires when |
405
405
  |-------|-------|-----------|
406
406
  | `client-added` | — | a client is created |
407
- | `client-sink-created` | `{ sink: Sink<ClientSample> }` | the client's sample sink was created by `createClientSink`; fires right after `client-added` |
407
+ | `client-sink-created` | `{ sink: Sink<ClientSample> }` | the client's sample sink was created through `saveClientSamples`; fires right after `client-added` |
408
408
  | `client-updated` | `{ sample: ClientSample, elapsedTimeInMs: number, context?: AcceptContext }` | the client processed a sample |
409
409
  | `client-closed` | — | the client closed |
410
410
  | `client-joined` | — | first `CLIENT_JOINED` event seen |
@@ -480,7 +480,10 @@ type ObserverConfig<AppData = Record<string, unknown>> = {
480
480
  createCallAppData?: (p: { callId: string; observer: Observer; acceptCtx?: AcceptContext }) => Record<string, unknown>;
481
481
  createClientAppData?: (p: { clientId: string; observedCall: ObservedCall; acceptCtx?: AcceptContext }) => Record<string, unknown>;
482
482
  // a JSONL file per client: return its path, or undefined for none (see Sinks).
483
- createClientSink?: (p: { id: string; clientId: string; observedCall: ObservedCall }) => SinkDestination<ClientSample> | undefined;
483
+ // where each client's samples are saved: exactly one of jsonlPath / sink, each a per-client callback (see Sinks).
484
+ saveClientSamples?:
485
+ | { jsonlPath: (p: { id: string; clientId: string; observedCall: ObservedCall }) => string | undefined }
486
+ | { sink: (p: { id: string; clientId: string; observedCall: ObservedCall }) => Sink<ClientSample> | undefined };
484
487
  // remote-track-resolver factory — produces a call's RemoteTrackResolver (see Remote track resolution).
485
488
  createRemoteTrackResolver?: (observedCall: ObservedCall) => RemoteTrackResolver | undefined;
486
489
  };
@@ -1947,7 +1950,7 @@ and never crashes the process.
1947
1950
  import type { Sink, SinkSettings } from '@observertc/observer-js';
1948
1951
 
1949
1952
  interface Sink<T = unknown> { // typically: class X extends EventEmitter implements Sink<T>
1950
- readonly id: string;
1953
+ readonly id: string; // unique per observer
1951
1954
  write(record: T): boolean; // `false` once ended (nothing written), or on backpressure
1952
1955
  end(): void; // idempotent; MUST emit `ended` synchronously, then flush and emit `close`
1953
1956
  on / once / off(event, listener); // the events below
@@ -1977,18 +1980,20 @@ its id — refuse writes after it, and emit `close` / `error` for its destinatio
1977
1980
  ### Client samples
1978
1981
 
1979
1982
  A client writes every accepted sample to the sink registered under its **client id** in its call's
1980
- registry (`observedClient.sink` returns it), and ends that sink when it closes. `createClientSink`
1981
- decides it, per client, by returning a **`SinkDestination`** — exactly one of `jsonlPath` and `sink`:
1983
+ registry (`observedClient.sink` returns it), and ends that sink when it closes. The
1984
+ **`saveClientSamples`** config option decides it — exactly one of two per-client callbacks, the same
1985
+ `jsonlPath` / `sink` pair as `createExtensionStats` takes:
1982
1986
 
1983
- | Returns | Sink |
1984
- |---------|------|
1985
- | `{ jsonlPath }` | a JSONL file at that path: a `JsonlFileSink` created by the library under the client id |
1986
- | `{ sink }` | your `Sink<ClientSample>`; its `id` must be `params.id`, the client id |
1987
- | `undefined` | none |
1987
+ | `saveClientSamples` | Called per client with `{ id, observedCall, clientId }`, returns | Sink |
1988
+ |---------------------|------------------------------------------------------------------|------|
1989
+ | `{ jsonlPath }` | the path of the client's file, or `undefined` | a `JsonlFileSink` at that path, created by the library under the client id |
1990
+ | `{ sink }` | your `Sink<ClientSample>`, or `undefined` | that sink; its `id` must be `id` |
1988
1991
 
1989
1992
  ```ts
1990
1993
  const observer = new Observer({
1991
- createClientSink: ({ observedCall, clientId }) => ({ jsonlPath: `./stats/${observedCall.callId}__${clientId}.jsonl` }),
1994
+ saveClientSamples: {
1995
+ jsonlPath: ({ observedCall, clientId }) => `./stats/${observedCall.callId}__${clientId}.jsonl`,
1996
+ },
1992
1997
  });
1993
1998
 
1994
1999
  observer.on('client-sink-created', ({ observedClient, sink }) => {
@@ -1996,10 +2001,15 @@ observer.on('client-sink-created', ({ observedClient, sink }) => {
1996
2001
  });
1997
2002
  ```
1998
2003
 
1999
- `createClientSink` is called only when the client id is free as a sink id, so a clash opens no file.
2000
- A returned sink with a different id is ended and not used. Giving both `jsonlPath` and `sink` is a
2001
- compile error (and refused at runtime). For a JSONL file with a custom serializer, return
2002
- `{ sink: new JsonlFileSink({ id, path, serializeSample }) }`.
2004
+ **A client's sample sink id is its client id.** The `id` the callbacks receive is that id, and
2005
+ with `jsonlPath` the library uses it for you. Sink ids are unique per observer, so **client ids
2006
+ must be unique across the observer's open calls** — client monitors generate uuids, so this
2007
+ normally holds. A client whose id is already a sink id gets no sink, and a warning is logged.
2008
+
2009
+ The callback is called only when the id is free, so a clash opens no file; `undefined` gives that
2010
+ client no sink. A returned sink with a different id is ended and not used. Giving both `jsonlPath`
2011
+ and `sink` is a compile error (and refused at runtime). For a JSONL file with a custom serializer,
2012
+ use `sink: ({ id }) => new JsonlFileSink({ id, path, serializeSample })`.
2003
2013
 
2004
2014
  Any other destination works the same way — return your own implementation:
2005
2015
 
@@ -2031,7 +2041,9 @@ class HttpSink extends EventEmitter implements ClientSampleSink {
2031
2041
  }
2032
2042
 
2033
2043
  new Observer({
2034
- createClientSink: ({ id, clientId }) => ({ sink: new HttpSink({ id, url: `https://stats.example.com/${clientId}` }) }),
2044
+ saveClientSamples: {
2045
+ sink: ({ id, clientId }) => new HttpSink({ id, url: `https://stats.example.com/${clientId}` }), // id: the client id
2046
+ },
2035
2047
  });
2036
2048
 
2037
2049
  // Or later, on `client-added`, if the client has no sink yet:
package/dist/index.d.mts CHANGED
@@ -59,6 +59,12 @@ type SinkDestination<T = unknown> = {
59
59
  sink: Sink<T>;
60
60
  jsonlPath?: never;
61
61
  };
62
+ /** What the `saveClientSamples` callbacks receive: `id` is the client's sample sink id, the client id. */
63
+ type ClientSinkParams<Call = unknown> = {
64
+ id: string;
65
+ observedCall: Call;
66
+ clientId: string;
67
+ };
62
68
 
63
69
  /** A sink class `SinkRegistry.create` can construct: its constructor takes one settings object (with `id`). */
64
70
  type SinkConstructor = new (settings: any) => Sink<any>;
@@ -4004,7 +4010,7 @@ declare class ObservedClient<AppData extends Record<string, unknown> = Record<st
4004
4010
  /** The distinct `publicAddress`es currently known by this client's open peer connections. */
4005
4011
  /**
4006
4012
  * The sink this client's samples are written to: whatever is registered under its client id in
4007
- * `call.sinks` (the one `createClientSink` returned, or a sink the application added), if any.
4013
+ * `call.sinks` (the one created through `saveClientSamples`, or a sink the application added), if any.
4008
4014
  */
4009
4015
  get sink(): Sink<ClientSample> | undefined;
4010
4016
  get publicAddresses(): string[];
@@ -5883,8 +5889,8 @@ declare class ObservedCall<AppData extends Record<string, unknown> = Record<stri
5883
5889
  private _onClientUpdate;
5884
5890
  private _clientJoined;
5885
5891
  private _clientLeft;
5886
- /** Emit an Observer-bus event scoped to this call. */
5887
5892
  private _createClientSink;
5893
+ /** Emit an Observer-bus event scoped to this call. */
5888
5894
  private _notify;
5889
5895
  }
5890
5896
 
@@ -6445,6 +6451,14 @@ type ClientAppDataFactory = (params: {
6445
6451
  observedCall: ObservedCall;
6446
6452
  acceptCtx?: AcceptContext;
6447
6453
  }) => Record<string, unknown>;
6454
+ /** `ObserverConfig.saveClientSamples`: exactly one of the two per-client callbacks. */
6455
+ type SaveClientSamplesConfig = {
6456
+ jsonlPath: (params: ClientSinkParams<ObservedCall>) => string | undefined;
6457
+ sink?: never;
6458
+ } | {
6459
+ sink: (params: ClientSinkParams<ObservedCall>) => Sink<ClientSample> | undefined;
6460
+ jsonlPath?: never;
6461
+ };
6448
6462
  type ObserverConfig<AppData extends Record<string, unknown> = Record<string, unknown>> = {
6449
6463
  /**
6450
6464
  * Application-owned data for the observer itself. Never read or modified by the library.
@@ -6598,22 +6612,15 @@ type ObserverConfig<AppData extends Record<string, unknown> = Record<string, unk
6598
6612
  /** Same as `createCallAppData`, for clients. Receives the (already-created) parent call. */
6599
6613
  createClientAppData?: ClientAppDataFactory;
6600
6614
  /**
6601
- * The sink a newly-created client writes its samples to: `{ jsonlPath }` (a `JsonlFileSink` is
6602
- * created at that path under the client id), `{ sink }` (a `Sink` whose `id` is `params.id`, the
6603
- * client id), or `undefined` for none. The sink is added to `observedCall.sinks` and is ended
6604
- * when the client closes; `client-sink-created` announces it.
6605
- *
6606
- * ```ts
6607
- * createClientSink: ({ observedCall, clientId }) => ({ jsonlPath: `./stats/${observedCall.callId}__${clientId}.jsonl` }),
6608
- * ```
6615
+ * Where each client's samples are saved: exactly one of `jsonlPath` (return the path of the
6616
+ * client's JSONL file) and `sink` (return a sink whose `id` is `params.id`), each called per
6617
+ * client; `undefined` gives that client no sink. See the README's *Sinks* section.
6609
6618
  *
6610
- * Called only when the client id is free as a sink id, so a clash opens no file.
6619
+ * A client's sample sink id is its client id (`params.id`). Sink ids are unique per observer, so
6620
+ * client ids must be unique across the observer's open calls; a client whose id is already a sink
6621
+ * id gets no sink, and a warning is logged.
6611
6622
  */
6612
- createClientSink?: (params: {
6613
- id: string;
6614
- observedCall: ObservedCall;
6615
- clientId: string;
6616
- }) => SinkDestination<ClientSample> | undefined;
6623
+ saveClientSamples?: SaveClientSamplesConfig;
6617
6624
  /**
6618
6625
  * Optional factory invoked when a call is created, producing the call's `RemoteTrackResolver`
6619
6626
  * (or `undefined` for none). Use the built-ins
@@ -7183,7 +7190,7 @@ declare function setObserverLogger(logger: ObserverLogger): void;
7183
7190
  /** The lifecycle events a client sample sink may emit. Same as {@link SinkEvents}. */
7184
7191
  type ClientSampleSinkEvents = SinkEvents;
7185
7192
  /**
7186
- * A per-client destination for accepted samples. Return one from `ObserverConfig.createClientSink`
7193
+ * A per-client destination for accepted samples. Return one from `ObserverConfig.saveClientSamples.sink`
7187
7194
  * (with the `id` it receives), or create one under the client id with
7188
7195
  * `observedCall.sinks.create(MySink, { id: clientId, ... })`.
7189
7196
  *
@@ -7253,4 +7260,4 @@ declare function createInMemorySink(samples?: ClientSample[]): InMemorySink;
7253
7260
  declare function createDefaultMediasoupRemoteTrackResolverFactory(): RemoteTrackResolverFactory;
7254
7261
  declare function createP2pRemoteTrackResolverFactory(): RemoteTrackResolverFactory;
7255
7262
 
7256
- 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 SinkDestination, 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 };
7263
+ 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 ClientSinkParams, 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 SaveClientSamplesConfig, 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 SinkDestination, 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 };
package/dist/index.d.ts CHANGED
@@ -59,6 +59,12 @@ type SinkDestination<T = unknown> = {
59
59
  sink: Sink<T>;
60
60
  jsonlPath?: never;
61
61
  };
62
+ /** What the `saveClientSamples` callbacks receive: `id` is the client's sample sink id, the client id. */
63
+ type ClientSinkParams<Call = unknown> = {
64
+ id: string;
65
+ observedCall: Call;
66
+ clientId: string;
67
+ };
62
68
 
63
69
  /** A sink class `SinkRegistry.create` can construct: its constructor takes one settings object (with `id`). */
64
70
  type SinkConstructor = new (settings: any) => Sink<any>;
@@ -4004,7 +4010,7 @@ declare class ObservedClient<AppData extends Record<string, unknown> = Record<st
4004
4010
  /** The distinct `publicAddress`es currently known by this client's open peer connections. */
4005
4011
  /**
4006
4012
  * The sink this client's samples are written to: whatever is registered under its client id in
4007
- * `call.sinks` (the one `createClientSink` returned, or a sink the application added), if any.
4013
+ * `call.sinks` (the one created through `saveClientSamples`, or a sink the application added), if any.
4008
4014
  */
4009
4015
  get sink(): Sink<ClientSample> | undefined;
4010
4016
  get publicAddresses(): string[];
@@ -5883,8 +5889,8 @@ declare class ObservedCall<AppData extends Record<string, unknown> = Record<stri
5883
5889
  private _onClientUpdate;
5884
5890
  private _clientJoined;
5885
5891
  private _clientLeft;
5886
- /** Emit an Observer-bus event scoped to this call. */
5887
5892
  private _createClientSink;
5893
+ /** Emit an Observer-bus event scoped to this call. */
5888
5894
  private _notify;
5889
5895
  }
5890
5896
 
@@ -6445,6 +6451,14 @@ type ClientAppDataFactory = (params: {
6445
6451
  observedCall: ObservedCall;
6446
6452
  acceptCtx?: AcceptContext;
6447
6453
  }) => Record<string, unknown>;
6454
+ /** `ObserverConfig.saveClientSamples`: exactly one of the two per-client callbacks. */
6455
+ type SaveClientSamplesConfig = {
6456
+ jsonlPath: (params: ClientSinkParams<ObservedCall>) => string | undefined;
6457
+ sink?: never;
6458
+ } | {
6459
+ sink: (params: ClientSinkParams<ObservedCall>) => Sink<ClientSample> | undefined;
6460
+ jsonlPath?: never;
6461
+ };
6448
6462
  type ObserverConfig<AppData extends Record<string, unknown> = Record<string, unknown>> = {
6449
6463
  /**
6450
6464
  * Application-owned data for the observer itself. Never read or modified by the library.
@@ -6598,22 +6612,15 @@ type ObserverConfig<AppData extends Record<string, unknown> = Record<string, unk
6598
6612
  /** Same as `createCallAppData`, for clients. Receives the (already-created) parent call. */
6599
6613
  createClientAppData?: ClientAppDataFactory;
6600
6614
  /**
6601
- * The sink a newly-created client writes its samples to: `{ jsonlPath }` (a `JsonlFileSink` is
6602
- * created at that path under the client id), `{ sink }` (a `Sink` whose `id` is `params.id`, the
6603
- * client id), or `undefined` for none. The sink is added to `observedCall.sinks` and is ended
6604
- * when the client closes; `client-sink-created` announces it.
6605
- *
6606
- * ```ts
6607
- * createClientSink: ({ observedCall, clientId }) => ({ jsonlPath: `./stats/${observedCall.callId}__${clientId}.jsonl` }),
6608
- * ```
6615
+ * Where each client's samples are saved: exactly one of `jsonlPath` (return the path of the
6616
+ * client's JSONL file) and `sink` (return a sink whose `id` is `params.id`), each called per
6617
+ * client; `undefined` gives that client no sink. See the README's *Sinks* section.
6609
6618
  *
6610
- * Called only when the client id is free as a sink id, so a clash opens no file.
6619
+ * A client's sample sink id is its client id (`params.id`). Sink ids are unique per observer, so
6620
+ * client ids must be unique across the observer's open calls; a client whose id is already a sink
6621
+ * id gets no sink, and a warning is logged.
6611
6622
  */
6612
- createClientSink?: (params: {
6613
- id: string;
6614
- observedCall: ObservedCall;
6615
- clientId: string;
6616
- }) => SinkDestination<ClientSample> | undefined;
6623
+ saveClientSamples?: SaveClientSamplesConfig;
6617
6624
  /**
6618
6625
  * Optional factory invoked when a call is created, producing the call's `RemoteTrackResolver`
6619
6626
  * (or `undefined` for none). Use the built-ins
@@ -7183,7 +7190,7 @@ declare function setObserverLogger(logger: ObserverLogger): void;
7183
7190
  /** The lifecycle events a client sample sink may emit. Same as {@link SinkEvents}. */
7184
7191
  type ClientSampleSinkEvents = SinkEvents;
7185
7192
  /**
7186
- * A per-client destination for accepted samples. Return one from `ObserverConfig.createClientSink`
7193
+ * A per-client destination for accepted samples. Return one from `ObserverConfig.saveClientSamples.sink`
7187
7194
  * (with the `id` it receives), or create one under the client id with
7188
7195
  * `observedCall.sinks.create(MySink, { id: clientId, ... })`.
7189
7196
  *
@@ -7253,4 +7260,4 @@ declare function createInMemorySink(samples?: ClientSample[]): InMemorySink;
7253
7260
  declare function createDefaultMediasoupRemoteTrackResolverFactory(): RemoteTrackResolverFactory;
7254
7261
  declare function createP2pRemoteTrackResolverFactory(): RemoteTrackResolverFactory;
7255
7262
 
7256
- 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 SinkDestination, 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 };
7263
+ 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 ClientSinkParams, 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 SaveClientSamplesConfig, 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 SinkDestination, 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 };
package/dist/index.js CHANGED
@@ -312,11 +312,11 @@ var SinkRegistry = class {
312
312
  }
313
313
  /** End every sink of this registry and of its descendants. */
314
314
  clear() {
315
- for (const sink of [...this._sinks.values()]) {
315
+ for (const [id, sink] of [...this._sinks.entries()]) {
316
316
  try {
317
317
  sink.end();
318
318
  } catch (err) {
319
- logger.warn("Sink %s failed to end: %o", sink.id, err);
319
+ logger.warn("Sink %s failed to end: %o", id, err);
320
320
  }
321
321
  }
322
322
  }
@@ -3258,7 +3258,7 @@ var ObservedClient = class extends import_node_events3.EventEmitter {
3258
3258
  /** The distinct `publicAddress`es currently known by this client's open peer connections. */
3259
3259
  /**
3260
3260
  * The sink this client's samples are written to: whatever is registered under its client id in
3261
- * `call.sinks` (the one `createClientSink` returned, or a sink the application added), if any.
3261
+ * `call.sinks` (the one created through `saveClientSamples`, or a sink the application added), if any.
3262
3262
  */
3263
3263
  get sink() {
3264
3264
  return this.call.sinks.get(this.clientId);
@@ -5411,17 +5411,26 @@ var ObservedCall = class extends import_node_events4.EventEmitter {
5411
5411
  if (!client.leftAt) return;
5412
5412
  this.endedAt = Math.max(this.endedAt ?? client.leftAt, client.leftAt);
5413
5413
  }
5414
- /** Emit an Observer-bus event scoped to this call. */
5415
5414
  _createClientSink(clientId) {
5416
- const createClientSink = this.observer.config.createClientSink;
5417
- if (!createClientSink) return void 0;
5415
+ const config = this.observer.config.saveClientSamples;
5416
+ if (!config || !config.jsonlPath && !config.sink) return void 0;
5417
+ if (config.jsonlPath && config.sink) {
5418
+ logger6.warn("saveClientSamples: give either jsonlPath or sink, not both; no client samples are saved");
5419
+ return void 0;
5420
+ }
5418
5421
  if (this.observer.sinks.get(clientId)) {
5419
5422
  logger6.warn("No sample sink for client %s in call %s: sink id %s is already in use", clientId, this.callId, clientId);
5420
5423
  return void 0;
5421
5424
  }
5422
- const destination = createClientSink({ id: clientId, observedCall: this, clientId });
5423
- return destination ? this.sinks.createFromDestination(clientId, destination) : void 0;
5425
+ const params = { id: clientId, observedCall: this, clientId };
5426
+ if (config.jsonlPath) {
5427
+ const jsonlPath = config.jsonlPath(params);
5428
+ return jsonlPath === void 0 ? void 0 : this.sinks.createFromDestination(clientId, { jsonlPath });
5429
+ }
5430
+ const sink = config.sink?.(params);
5431
+ return sink ? this.sinks.createFromDestination(clientId, { sink }) : void 0;
5424
5432
  }
5433
+ /** Emit an Observer-bus event scoped to this call. */
5425
5434
  _notify(type, ...args) {
5426
5435
  this.observer.emit(type, ...args);
5427
5436
  }