@observertc/observer-js 1.0.0-beta.15 → 1.0.0-beta.16
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 +80 -4
- package/dist/index.d.mts +125 -5
- package/dist/index.d.ts +125 -5
- package/dist/index.js +163 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +163 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -497,9 +497,15 @@ Key members:
|
|
|
497
497
|
- `getOrCreateObservedCall<T>(settings): ObservedCall<T> | undefined`
|
|
498
498
|
- `update(): void` — force an aggregation/`observer-updated` tick
|
|
499
499
|
- `addObserverDetector(name, config?): this` — build a cross-call detector onto `observer.detectors`
|
|
500
|
-
- `addCallDetector(name, config?): this`
|
|
501
|
-
|
|
500
|
+
- `addCallDetector(name, config?): this` — register a call-scoped detector for every call created
|
|
501
|
+
from now on
|
|
502
|
+
- `removeCallDetector(name, { includeOpenCalls? }): number` — stop building it, and (by default) drop
|
|
503
|
+
it from calls already open. Returns how many live instances were removed
|
|
504
|
+
- `removeObserverDetector(name): number` — remove an observer-scoped detector. For one specific
|
|
505
|
+
instance use `observer.detectors.remove(detector)`
|
|
502
506
|
- `addValidator(name, config?): this` — start a one-shot structural check
|
|
507
|
+
- `cancelValidator(name | validator, reason?): number` — stop a running check; it finishes
|
|
508
|
+
`inconclusive` with the reason and emits `validation-ready`
|
|
503
509
|
- `close(): void`
|
|
504
510
|
- `readonly detectors: Detectors` — observer-scoped registry. **Starts empty**; nothing is implicit
|
|
505
511
|
- `readonly callDetectorConfigs: Map<name, config>` — what `addCallDetector` recorded
|
|
@@ -532,6 +538,8 @@ Key members:
|
|
|
532
538
|
- `getObservedClient<T>(clientId)`, `createObservedClient<T>(settings)`, `getOrCreateObservedClient<T>(settings)` (all `… | undefined`)
|
|
533
539
|
- `addIssue(issue: ObserverIssue): void` — raise a **call-level** issue → emits `call-issue`
|
|
534
540
|
- `addDetector(name, config?): this` — build a call-scoped detector onto this call only
|
|
541
|
+
- `removeDetector(name): number` — remove it from this call, `close()`ing it. For one specific
|
|
542
|
+
instance use `call.detectors.remove(detector)`
|
|
535
543
|
- `readonly detectors: Detectors` — server-side detector registry (empty by default; see [Detectors](#detectors-server-side-extension-point))
|
|
536
544
|
- `readonly activeIssuesRegistry: ActiveIssuesRegistry` — this call's open client issues, propagating into the observer's
|
|
537
545
|
- `readonly unconsumedOutboundTracks: Set<ObservedOutboundTrack>` — maintained by the resolver
|
|
@@ -959,12 +967,63 @@ observer.addObserverDetector('turn-server-outage-detector', { minClientsAtPeak:
|
|
|
959
967
|
observer.addCallDetector('call-concurrent-issue-detector', {
|
|
960
968
|
issueTypes: [ 'congestion', 'ice-disconnected' ],
|
|
961
969
|
});
|
|
962
|
-
observer.removeCallDetector('unconsumed-track-detector'); // undo, for future calls
|
|
963
|
-
|
|
964
970
|
// one specific call
|
|
965
971
|
observedCall.addDetector('issue-fan-out-detector', { issueTypes: [ 'freezed-video-track' ] });
|
|
966
972
|
```
|
|
967
973
|
|
|
974
|
+
Every `add*` is **chainable** — it returns the owning entity:
|
|
975
|
+
|
|
976
|
+
```ts
|
|
977
|
+
observer
|
|
978
|
+
.addObserverDetector('turn-server-health-detector')
|
|
979
|
+
.addObserverDetector('turn-server-outage-detector', { minClientsAtPeak: 10 })
|
|
980
|
+
.addValidator('remote-track-resolver');
|
|
981
|
+
```
|
|
982
|
+
|
|
983
|
+
#### Removing them
|
|
984
|
+
|
|
985
|
+
By **name**, on the entity — which removes *every* instance under that name:
|
|
986
|
+
|
|
987
|
+
```ts
|
|
988
|
+
observer.removeObserverDetector('turn-server-outage-detector'); // → 1
|
|
989
|
+
observer.removeCallDetector('call-concurrent-issue-detector'); // stops it everywhere
|
|
990
|
+
observedCall.removeDetector('issue-fan-out-detector'); // this call only
|
|
991
|
+
```
|
|
992
|
+
|
|
993
|
+
By **instance**, through the registry — which is where instances live, since `add*` returns the
|
|
994
|
+
entity rather than the detector:
|
|
995
|
+
|
|
996
|
+
```ts
|
|
997
|
+
observer
|
|
998
|
+
.addObserverDetector('client-population-issue-detector', { issueTypes: [ 'cpulimitation' ], groupBy: 'browser' })
|
|
999
|
+
.addObserverDetector('client-population-issue-detector', { issueTypes: [ 'cpulimitation' ], groupBy: 'operationSystem' });
|
|
1000
|
+
|
|
1001
|
+
const [ byBrowser, byOs ] = observer.detectors.getAll('client-population-issue-detector');
|
|
1002
|
+
|
|
1003
|
+
observer.detectors.remove(byOs); // keeps the browser axis running
|
|
1004
|
+
```
|
|
1005
|
+
|
|
1006
|
+
`Detectors` is a small collection: `instances` (a copy, in registration order), `listOfNames`,
|
|
1007
|
+
`size`, `get(name)`, `getAll(name)`, `has(name)`, `add(detector)`, `remove(detector)`,
|
|
1008
|
+
`removeByName(name)`, `clear()`, and it is iterable — `for (const detector of call.detectors)`.
|
|
1009
|
+
`instances` being a copy is deliberate: removing while iterating the live array would skip entries,
|
|
1010
|
+
and "drop the ones that look like X" is the most natural thing to want to write.
|
|
1011
|
+
|
|
1012
|
+
Two things worth knowing:
|
|
1013
|
+
|
|
1014
|
+
- **By name removes every instance under it**, not the first. A name can legitimately be registered
|
|
1015
|
+
more than once — `ClientPopulationIssueDetector` is meant to be added once per `groupBy` axis — and
|
|
1016
|
+
"remove whichever is first in the array" is not something a caller can predict from a name. Go via
|
|
1017
|
+
`detectors.getAll(name)` + `detectors.remove(instance)` when you mean one of them.
|
|
1018
|
+
- **`removeCallDetector` affects calls already open, by default.** Otherwise whether a detector runs
|
|
1019
|
+
would depend on when a call happened to join, which is not a state anyone can reason about. Pass
|
|
1020
|
+
`{ includeOpenCalls: false }` to change only what future calls are built with.
|
|
1021
|
+
|
|
1022
|
+
Every removal path calls the detector's `close()`, so it unsubscribes from the issue registry and
|
|
1023
|
+
drops any timers or bus listeners. A detector removed without closing would keep being fed matching
|
|
1024
|
+
issues for the life of the call — invisible, unbounded, and it would still look healthy if you
|
|
1025
|
+
inspected it.
|
|
1026
|
+
|
|
968
1027
|
Detectors are named by their kebab-case `NAME`, and the name types the config — an unknown name or a
|
|
969
1028
|
key that belongs to a different detector will not compile. Each detector owns its defaults in its own
|
|
970
1029
|
constructor, beside the doc explaining what each threshold means; there is no central table to keep
|
|
@@ -1093,6 +1152,23 @@ onDeploy(() => observer.addValidator('simulcast-receivers')); // check again
|
|
|
1093
1152
|
finishing. There is no revalidation timer: a deploy, not elapsed time, is what makes a structural
|
|
1094
1153
|
verdict stale, so re-checking means starting another.
|
|
1095
1154
|
|
|
1155
|
+
**Cancelling.** A check that has not decided can be stopped, by name or by instance:
|
|
1156
|
+
|
|
1157
|
+
```ts
|
|
1158
|
+
observer.cancelValidator('simulcast-receivers', 'sfu redeployed');
|
|
1159
|
+
|
|
1160
|
+
// or one specific instance — `observer.validators` holds what is running
|
|
1161
|
+
for (const validator of observer.validators) observer.cancelValidator(validator, 'shutting down');
|
|
1162
|
+
```
|
|
1163
|
+
|
|
1164
|
+
Cancelling is **not** silent discarding. The validator finishes `inconclusive` with your reason,
|
|
1165
|
+
emits `validation-ready` like any other completion, and removes itself. That matters twice over:
|
|
1166
|
+
anything waiting on the verdict would otherwise wait forever, and *"we stopped asking"* is a
|
|
1167
|
+
materially different outcome from *"we asked and learned nothing"* — which is exactly what an
|
|
1168
|
+
`inconclusive` carrying a reason records. Pass a real reason; the default tells the reader nothing
|
|
1169
|
+
they could not already infer. `observer.close()` cancels whatever is still running with
|
|
1170
|
+
`'observer closed'`.
|
|
1171
|
+
|
|
1096
1172
|
| Validator | `addValidator` name | Question | Also raises |
|
|
1097
1173
|
|-----------|---------------------|----------|-------------|
|
|
1098
1174
|
| `SimulcastReceiverValidator` 🔗 | `simulcast-receivers` | Does the SFU pick layers per receiver, or drag the publisher down to the worst one? | `WORST_RECEIVER_CONTAGION` |
|
package/dist/index.d.mts
CHANGED
|
@@ -2468,8 +2468,13 @@ interface Validator<S extends Record<string, unknown> = Record<string, unknown>>
|
|
|
2468
2468
|
onDone: (report: ValidationReport<S>) => void;
|
|
2469
2469
|
/** Gather evidence; decide if there is now enough. Called on every `observer.update()`. */
|
|
2470
2470
|
update(): void;
|
|
2471
|
-
/**
|
|
2472
|
-
|
|
2471
|
+
/**
|
|
2472
|
+
* Give up without a verdict. Finishes with `inconclusive`, so a caller waiting on it is freed.
|
|
2473
|
+
*
|
|
2474
|
+
* `reason` is carried into the report. Worth passing something specific — "cancelled" tells the
|
|
2475
|
+
* reader nothing, whereas "sfu redeployed" explains why a check that was running has no verdict.
|
|
2476
|
+
*/
|
|
2477
|
+
cancel: (reason?: string) => void;
|
|
2473
2478
|
}
|
|
2474
2479
|
/**
|
|
2475
2480
|
* The part of a validator the observer needs in order to drive it.
|
|
@@ -4412,11 +4417,48 @@ type AvailableDetectorsConfigs = AvailableObserverScopeDetectorsConfigs | Availa
|
|
|
4412
4417
|
declare class Detectors {
|
|
4413
4418
|
private _detectors;
|
|
4414
4419
|
constructor(...detectors: Detector[]);
|
|
4420
|
+
/**
|
|
4421
|
+
* Every registered detector, in registration order.
|
|
4422
|
+
*
|
|
4423
|
+
* This is **the** way to get hold of an instance: `addDetector` / `addObserverDetector` are
|
|
4424
|
+
* chainable and return the owning entity, so the registry is where instances live. Read it to
|
|
4425
|
+
* inspect a detector's state, or to pick one out and {@link remove} it.
|
|
4426
|
+
*
|
|
4427
|
+
* A copy, not the live array — a caller iterating this while removing would otherwise skip
|
|
4428
|
+
* entries, and that is exactly what "remove the ones that look like X" does.
|
|
4429
|
+
*/
|
|
4430
|
+
get instances(): Detector[];
|
|
4431
|
+
/** Iterate the registry directly: `for (const detector of call.detectors)`. */
|
|
4432
|
+
[Symbol.iterator](): IterableIterator<Detector>;
|
|
4433
|
+
/** The names in registration order. Duplicates are meaningful — see {@link getAll}. */
|
|
4415
4434
|
get listOfNames(): string[];
|
|
4416
4435
|
get size(): number;
|
|
4417
4436
|
add(detector: Detector): void;
|
|
4437
|
+
/** The first detector registered under `name`. See {@link getAll} when several can share one. */
|
|
4418
4438
|
get(name: string): Detector | undefined;
|
|
4419
|
-
|
|
4439
|
+
/**
|
|
4440
|
+
* Every detector registered under `name`.
|
|
4441
|
+
*
|
|
4442
|
+
* More than one is legitimate: `ClientPopulationIssueDetector` is meant to be added once per
|
|
4443
|
+
* `groupBy` axis, and two instances of it share a name.
|
|
4444
|
+
*/
|
|
4445
|
+
getAll(name: string): Detector[];
|
|
4446
|
+
has(name: string): boolean;
|
|
4447
|
+
/** Remove one specific instance. Returns `false` if it was not registered here. */
|
|
4448
|
+
remove(detector: Detector): boolean;
|
|
4449
|
+
/**
|
|
4450
|
+
* Remove **every** detector registered under `name`, returning how many were removed.
|
|
4451
|
+
*
|
|
4452
|
+
* All of them rather than the first, because a name can legitimately be registered more than once
|
|
4453
|
+
* (see {@link getAll}) and "remove the `client-population-issue-detector`" cannot sensibly mean
|
|
4454
|
+
* "remove whichever axis happens to be first in the array". Removing all of them is the only
|
|
4455
|
+
* behaviour that leaves the registry in a state the caller can predict from the name alone.
|
|
4456
|
+
*
|
|
4457
|
+
* Each removed detector gets `close()`, so trackers unsubscribe from the issue registry, bus
|
|
4458
|
+
* listeners drop, and timers clear — a detector removed without closing keeps being fed issues
|
|
4459
|
+
* forever.
|
|
4460
|
+
*/
|
|
4461
|
+
removeByName(name: string): number;
|
|
4420
4462
|
update(): void;
|
|
4421
4463
|
clear(): void;
|
|
4422
4464
|
private _close;
|
|
@@ -4565,7 +4607,32 @@ declare class ObservedCall<AppData extends Record<string, unknown> = Record<stri
|
|
|
4565
4607
|
constructor(settings: ObservedCallSettings<AppData>, observer: Observer, activeIssuesRegistry: ActiveIssuesRegistry);
|
|
4566
4608
|
get numberOfClients(): number;
|
|
4567
4609
|
get score(): number | undefined;
|
|
4610
|
+
/**
|
|
4611
|
+
* Build a call-scoped detector onto this call. Chainable.
|
|
4612
|
+
*
|
|
4613
|
+
* To get a handle on what was built — to inspect it, or to remove that exact instance later — read
|
|
4614
|
+
* it back off the registry: `call.detectors.getAll(name)`, or `call.detectors.instances`.
|
|
4615
|
+
*/
|
|
4568
4616
|
addDetector<K extends keyof AvailableCallScopeDetectorsConfigs>(name: K, config?: Partial<AvailableCallScopeDetectorsConfigs[K]>): this;
|
|
4617
|
+
/**
|
|
4618
|
+
* Remove a detector from **this call** by name, returning how many were removed.
|
|
4619
|
+
*
|
|
4620
|
+
* **Every** instance under the name goes — a name can legitimately be registered more than once.
|
|
4621
|
+
* When you want one of them specifically, go through the registry, which deals in instances:
|
|
4622
|
+
*
|
|
4623
|
+
* ```ts
|
|
4624
|
+
* const [ first ] = call.detectors.getAll('issue-fan-out-detector');
|
|
4625
|
+
*
|
|
4626
|
+
* call.detectors.remove(first);
|
|
4627
|
+
* ```
|
|
4628
|
+
*
|
|
4629
|
+
* Either route `close()`s the detector, so it unsubscribes from `activeIssuesRegistry` — without
|
|
4630
|
+
* that the registry keeps feeding a detector nobody is running any more, and its tracked set grows
|
|
4631
|
+
* for the life of the call.
|
|
4632
|
+
*
|
|
4633
|
+
* To stop building it on *future* calls too, use `observer.removeCallDetector(name)`.
|
|
4634
|
+
*/
|
|
4635
|
+
removeDetector(name: keyof AvailableCallScopeDetectorsConfigs): number;
|
|
4569
4636
|
/**
|
|
4570
4637
|
* Raise a call-level (server-side) finding; surfaced on the Observer bus as `call-issue`.
|
|
4571
4638
|
*
|
|
@@ -5113,6 +5180,12 @@ declare class Observer<AppData extends Record<string, unknown> = Record<string,
|
|
|
5113
5180
|
constructor(config?: Partial<ObserverConfig<AppData>>);
|
|
5114
5181
|
get numberOfCalls(): number;
|
|
5115
5182
|
get appData(): AppData | undefined;
|
|
5183
|
+
/**
|
|
5184
|
+
* Build a cross-call detector onto `observer.detectors`. Chainable.
|
|
5185
|
+
*
|
|
5186
|
+
* To get a handle on what was built — to inspect it, or to remove that exact instance later — read
|
|
5187
|
+
* it back off the registry: `observer.detectors.getAll(name)`, or `observer.detectors.instances`.
|
|
5188
|
+
*/
|
|
5116
5189
|
addObserverDetector<K extends keyof AvailableObserverScopeDetectorsConfigs>(name: K, config?: Partial<AvailableObserverScopeDetectorsConfigs[K]>): this;
|
|
5117
5190
|
/**
|
|
5118
5191
|
* Enable a call-scoped detector for calls created **from now on**.
|
|
@@ -5121,8 +5194,36 @@ declare class Observer<AppData extends Record<string, unknown> = Record<string,
|
|
|
5121
5194
|
* built with. To add one to an existing call, use `observedCall.addDetector(...)` directly.
|
|
5122
5195
|
*/
|
|
5123
5196
|
addCallDetector<K extends keyof AvailableCallScopeDetectorsConfigs>(name: K, config?: Partial<AvailableCallScopeDetectorsConfigs[K]>): this;
|
|
5124
|
-
/**
|
|
5125
|
-
|
|
5197
|
+
/**
|
|
5198
|
+
* Remove an observer-scoped detector by name, returning how many were removed.
|
|
5199
|
+
*
|
|
5200
|
+
* **Every** instance registered under the name goes, since a name can legitimately be registered
|
|
5201
|
+
* more than once (`ClientPopulationIssueDetector` is meant to be added once per `groupBy` axis).
|
|
5202
|
+
* When you want one of them specifically, go through the registry, which deals in instances:
|
|
5203
|
+
*
|
|
5204
|
+
* ```ts
|
|
5205
|
+
* const [ byBrowser, byOs ] = observer.detectors.getAll('client-population-issue-detector');
|
|
5206
|
+
*
|
|
5207
|
+
* observer.detectors.remove(byOs); // keeps the browser axis running
|
|
5208
|
+
* ```
|
|
5209
|
+
*
|
|
5210
|
+
* Either route `close()`s the detector, so it unsubscribes from the issue registry and drops any
|
|
5211
|
+
* timers or bus listeners it held.
|
|
5212
|
+
*/
|
|
5213
|
+
removeObserverDetector(name: keyof AvailableObserverScopeDetectorsConfigs): number;
|
|
5214
|
+
/**
|
|
5215
|
+
* Stop building `name` on calls created from now on.
|
|
5216
|
+
*
|
|
5217
|
+
* By default this also removes it from the calls **already open**, so that "remove this detector"
|
|
5218
|
+
* means the same thing whether you say it before or after a call started — the alternative leaves
|
|
5219
|
+
* a fleet where the detector is live on some calls and not others, decided by join time. Pass
|
|
5220
|
+
* `{ includeOpenCalls: false }` to change only what future calls are built with.
|
|
5221
|
+
*
|
|
5222
|
+
* Returns the number of live detector instances removed (`0` when only the config changed).
|
|
5223
|
+
*/
|
|
5224
|
+
removeCallDetector(name: keyof AvailableCallScopeDetectorsConfigs, { includeOpenCalls }?: {
|
|
5225
|
+
includeOpenCalls?: boolean;
|
|
5226
|
+
}): number;
|
|
5126
5227
|
/**
|
|
5127
5228
|
* Start a structural check. It runs on each `observer.update()` until it can decide, reports once
|
|
5128
5229
|
* on `validation-ready`, and removes itself.
|
|
@@ -5136,6 +5237,25 @@ declare class Observer<AppData extends Record<string, unknown> = Record<string,
|
|
|
5136
5237
|
* elapsed time is what makes a structural verdict stale.
|
|
5137
5238
|
*/
|
|
5138
5239
|
addValidator<K extends keyof AvailableValidatorConfigs>(name: K, config?: Partial<AvailableValidatorConfigs[K]>): this;
|
|
5240
|
+
/**
|
|
5241
|
+
* Stop a running validation, by name or by instance. Returns how many were cancelled.
|
|
5242
|
+
*
|
|
5243
|
+
* Cancelling is **not** silent discarding. The validator finishes with `inconclusive` and the given
|
|
5244
|
+
* `reason`, emits `validation-ready` like any other completion, and removes itself. That matters
|
|
5245
|
+
* because anything waiting on the verdict — a deploy gate, a dashboard, a promise — would otherwise
|
|
5246
|
+
* wait forever, and because "we stopped asking" is a materially different outcome from "we asked
|
|
5247
|
+
* and learned nothing", which is exactly what `inconclusive` with a reason records.
|
|
5248
|
+
*
|
|
5249
|
+
* ```ts
|
|
5250
|
+
* observer.cancelValidator('simulcast-receivers', 'sfu redeployed');
|
|
5251
|
+
*
|
|
5252
|
+
* // or one specific instance — `observer.validators` holds what is running
|
|
5253
|
+
* for (const validator of observer.validators) observer.cancelValidator(validator, 'shutting down');
|
|
5254
|
+
* ```
|
|
5255
|
+
*
|
|
5256
|
+
* Pass a real reason. The default tells the reader nothing they could not already infer.
|
|
5257
|
+
*/
|
|
5258
|
+
cancelValidator(target: keyof AvailableValidatorConfigs | RunningValidator, reason?: string): number;
|
|
5139
5259
|
getObservedCall<T extends Record<string, unknown> = Record<string, unknown>>(callId: string): ObservedCall<T> | undefined;
|
|
5140
5260
|
createObservedCall<T extends Record<string, unknown> = Record<string, unknown>>(settings: ObservedCallSettings<T>): ObservedCall<T> | undefined;
|
|
5141
5261
|
getOrCreateObservedCall<T extends Record<string, unknown> = Record<string, unknown>>(settings: ObservedCallSettings<T>): ObservedCall<T> | undefined;
|
package/dist/index.d.ts
CHANGED
|
@@ -2468,8 +2468,13 @@ interface Validator<S extends Record<string, unknown> = Record<string, unknown>>
|
|
|
2468
2468
|
onDone: (report: ValidationReport<S>) => void;
|
|
2469
2469
|
/** Gather evidence; decide if there is now enough. Called on every `observer.update()`. */
|
|
2470
2470
|
update(): void;
|
|
2471
|
-
/**
|
|
2472
|
-
|
|
2471
|
+
/**
|
|
2472
|
+
* Give up without a verdict. Finishes with `inconclusive`, so a caller waiting on it is freed.
|
|
2473
|
+
*
|
|
2474
|
+
* `reason` is carried into the report. Worth passing something specific — "cancelled" tells the
|
|
2475
|
+
* reader nothing, whereas "sfu redeployed" explains why a check that was running has no verdict.
|
|
2476
|
+
*/
|
|
2477
|
+
cancel: (reason?: string) => void;
|
|
2473
2478
|
}
|
|
2474
2479
|
/**
|
|
2475
2480
|
* The part of a validator the observer needs in order to drive it.
|
|
@@ -4412,11 +4417,48 @@ type AvailableDetectorsConfigs = AvailableObserverScopeDetectorsConfigs | Availa
|
|
|
4412
4417
|
declare class Detectors {
|
|
4413
4418
|
private _detectors;
|
|
4414
4419
|
constructor(...detectors: Detector[]);
|
|
4420
|
+
/**
|
|
4421
|
+
* Every registered detector, in registration order.
|
|
4422
|
+
*
|
|
4423
|
+
* This is **the** way to get hold of an instance: `addDetector` / `addObserverDetector` are
|
|
4424
|
+
* chainable and return the owning entity, so the registry is where instances live. Read it to
|
|
4425
|
+
* inspect a detector's state, or to pick one out and {@link remove} it.
|
|
4426
|
+
*
|
|
4427
|
+
* A copy, not the live array — a caller iterating this while removing would otherwise skip
|
|
4428
|
+
* entries, and that is exactly what "remove the ones that look like X" does.
|
|
4429
|
+
*/
|
|
4430
|
+
get instances(): Detector[];
|
|
4431
|
+
/** Iterate the registry directly: `for (const detector of call.detectors)`. */
|
|
4432
|
+
[Symbol.iterator](): IterableIterator<Detector>;
|
|
4433
|
+
/** The names in registration order. Duplicates are meaningful — see {@link getAll}. */
|
|
4415
4434
|
get listOfNames(): string[];
|
|
4416
4435
|
get size(): number;
|
|
4417
4436
|
add(detector: Detector): void;
|
|
4437
|
+
/** The first detector registered under `name`. See {@link getAll} when several can share one. */
|
|
4418
4438
|
get(name: string): Detector | undefined;
|
|
4419
|
-
|
|
4439
|
+
/**
|
|
4440
|
+
* Every detector registered under `name`.
|
|
4441
|
+
*
|
|
4442
|
+
* More than one is legitimate: `ClientPopulationIssueDetector` is meant to be added once per
|
|
4443
|
+
* `groupBy` axis, and two instances of it share a name.
|
|
4444
|
+
*/
|
|
4445
|
+
getAll(name: string): Detector[];
|
|
4446
|
+
has(name: string): boolean;
|
|
4447
|
+
/** Remove one specific instance. Returns `false` if it was not registered here. */
|
|
4448
|
+
remove(detector: Detector): boolean;
|
|
4449
|
+
/**
|
|
4450
|
+
* Remove **every** detector registered under `name`, returning how many were removed.
|
|
4451
|
+
*
|
|
4452
|
+
* All of them rather than the first, because a name can legitimately be registered more than once
|
|
4453
|
+
* (see {@link getAll}) and "remove the `client-population-issue-detector`" cannot sensibly mean
|
|
4454
|
+
* "remove whichever axis happens to be first in the array". Removing all of them is the only
|
|
4455
|
+
* behaviour that leaves the registry in a state the caller can predict from the name alone.
|
|
4456
|
+
*
|
|
4457
|
+
* Each removed detector gets `close()`, so trackers unsubscribe from the issue registry, bus
|
|
4458
|
+
* listeners drop, and timers clear — a detector removed without closing keeps being fed issues
|
|
4459
|
+
* forever.
|
|
4460
|
+
*/
|
|
4461
|
+
removeByName(name: string): number;
|
|
4420
4462
|
update(): void;
|
|
4421
4463
|
clear(): void;
|
|
4422
4464
|
private _close;
|
|
@@ -4565,7 +4607,32 @@ declare class ObservedCall<AppData extends Record<string, unknown> = Record<stri
|
|
|
4565
4607
|
constructor(settings: ObservedCallSettings<AppData>, observer: Observer, activeIssuesRegistry: ActiveIssuesRegistry);
|
|
4566
4608
|
get numberOfClients(): number;
|
|
4567
4609
|
get score(): number | undefined;
|
|
4610
|
+
/**
|
|
4611
|
+
* Build a call-scoped detector onto this call. Chainable.
|
|
4612
|
+
*
|
|
4613
|
+
* To get a handle on what was built — to inspect it, or to remove that exact instance later — read
|
|
4614
|
+
* it back off the registry: `call.detectors.getAll(name)`, or `call.detectors.instances`.
|
|
4615
|
+
*/
|
|
4568
4616
|
addDetector<K extends keyof AvailableCallScopeDetectorsConfigs>(name: K, config?: Partial<AvailableCallScopeDetectorsConfigs[K]>): this;
|
|
4617
|
+
/**
|
|
4618
|
+
* Remove a detector from **this call** by name, returning how many were removed.
|
|
4619
|
+
*
|
|
4620
|
+
* **Every** instance under the name goes — a name can legitimately be registered more than once.
|
|
4621
|
+
* When you want one of them specifically, go through the registry, which deals in instances:
|
|
4622
|
+
*
|
|
4623
|
+
* ```ts
|
|
4624
|
+
* const [ first ] = call.detectors.getAll('issue-fan-out-detector');
|
|
4625
|
+
*
|
|
4626
|
+
* call.detectors.remove(first);
|
|
4627
|
+
* ```
|
|
4628
|
+
*
|
|
4629
|
+
* Either route `close()`s the detector, so it unsubscribes from `activeIssuesRegistry` — without
|
|
4630
|
+
* that the registry keeps feeding a detector nobody is running any more, and its tracked set grows
|
|
4631
|
+
* for the life of the call.
|
|
4632
|
+
*
|
|
4633
|
+
* To stop building it on *future* calls too, use `observer.removeCallDetector(name)`.
|
|
4634
|
+
*/
|
|
4635
|
+
removeDetector(name: keyof AvailableCallScopeDetectorsConfigs): number;
|
|
4569
4636
|
/**
|
|
4570
4637
|
* Raise a call-level (server-side) finding; surfaced on the Observer bus as `call-issue`.
|
|
4571
4638
|
*
|
|
@@ -5113,6 +5180,12 @@ declare class Observer<AppData extends Record<string, unknown> = Record<string,
|
|
|
5113
5180
|
constructor(config?: Partial<ObserverConfig<AppData>>);
|
|
5114
5181
|
get numberOfCalls(): number;
|
|
5115
5182
|
get appData(): AppData | undefined;
|
|
5183
|
+
/**
|
|
5184
|
+
* Build a cross-call detector onto `observer.detectors`. Chainable.
|
|
5185
|
+
*
|
|
5186
|
+
* To get a handle on what was built — to inspect it, or to remove that exact instance later — read
|
|
5187
|
+
* it back off the registry: `observer.detectors.getAll(name)`, or `observer.detectors.instances`.
|
|
5188
|
+
*/
|
|
5116
5189
|
addObserverDetector<K extends keyof AvailableObserverScopeDetectorsConfigs>(name: K, config?: Partial<AvailableObserverScopeDetectorsConfigs[K]>): this;
|
|
5117
5190
|
/**
|
|
5118
5191
|
* Enable a call-scoped detector for calls created **from now on**.
|
|
@@ -5121,8 +5194,36 @@ declare class Observer<AppData extends Record<string, unknown> = Record<string,
|
|
|
5121
5194
|
* built with. To add one to an existing call, use `observedCall.addDetector(...)` directly.
|
|
5122
5195
|
*/
|
|
5123
5196
|
addCallDetector<K extends keyof AvailableCallScopeDetectorsConfigs>(name: K, config?: Partial<AvailableCallScopeDetectorsConfigs[K]>): this;
|
|
5124
|
-
/**
|
|
5125
|
-
|
|
5197
|
+
/**
|
|
5198
|
+
* Remove an observer-scoped detector by name, returning how many were removed.
|
|
5199
|
+
*
|
|
5200
|
+
* **Every** instance registered under the name goes, since a name can legitimately be registered
|
|
5201
|
+
* more than once (`ClientPopulationIssueDetector` is meant to be added once per `groupBy` axis).
|
|
5202
|
+
* When you want one of them specifically, go through the registry, which deals in instances:
|
|
5203
|
+
*
|
|
5204
|
+
* ```ts
|
|
5205
|
+
* const [ byBrowser, byOs ] = observer.detectors.getAll('client-population-issue-detector');
|
|
5206
|
+
*
|
|
5207
|
+
* observer.detectors.remove(byOs); // keeps the browser axis running
|
|
5208
|
+
* ```
|
|
5209
|
+
*
|
|
5210
|
+
* Either route `close()`s the detector, so it unsubscribes from the issue registry and drops any
|
|
5211
|
+
* timers or bus listeners it held.
|
|
5212
|
+
*/
|
|
5213
|
+
removeObserverDetector(name: keyof AvailableObserverScopeDetectorsConfigs): number;
|
|
5214
|
+
/**
|
|
5215
|
+
* Stop building `name` on calls created from now on.
|
|
5216
|
+
*
|
|
5217
|
+
* By default this also removes it from the calls **already open**, so that "remove this detector"
|
|
5218
|
+
* means the same thing whether you say it before or after a call started — the alternative leaves
|
|
5219
|
+
* a fleet where the detector is live on some calls and not others, decided by join time. Pass
|
|
5220
|
+
* `{ includeOpenCalls: false }` to change only what future calls are built with.
|
|
5221
|
+
*
|
|
5222
|
+
* Returns the number of live detector instances removed (`0` when only the config changed).
|
|
5223
|
+
*/
|
|
5224
|
+
removeCallDetector(name: keyof AvailableCallScopeDetectorsConfigs, { includeOpenCalls }?: {
|
|
5225
|
+
includeOpenCalls?: boolean;
|
|
5226
|
+
}): number;
|
|
5126
5227
|
/**
|
|
5127
5228
|
* Start a structural check. It runs on each `observer.update()` until it can decide, reports once
|
|
5128
5229
|
* on `validation-ready`, and removes itself.
|
|
@@ -5136,6 +5237,25 @@ declare class Observer<AppData extends Record<string, unknown> = Record<string,
|
|
|
5136
5237
|
* elapsed time is what makes a structural verdict stale.
|
|
5137
5238
|
*/
|
|
5138
5239
|
addValidator<K extends keyof AvailableValidatorConfigs>(name: K, config?: Partial<AvailableValidatorConfigs[K]>): this;
|
|
5240
|
+
/**
|
|
5241
|
+
* Stop a running validation, by name or by instance. Returns how many were cancelled.
|
|
5242
|
+
*
|
|
5243
|
+
* Cancelling is **not** silent discarding. The validator finishes with `inconclusive` and the given
|
|
5244
|
+
* `reason`, emits `validation-ready` like any other completion, and removes itself. That matters
|
|
5245
|
+
* because anything waiting on the verdict — a deploy gate, a dashboard, a promise — would otherwise
|
|
5246
|
+
* wait forever, and because "we stopped asking" is a materially different outcome from "we asked
|
|
5247
|
+
* and learned nothing", which is exactly what `inconclusive` with a reason records.
|
|
5248
|
+
*
|
|
5249
|
+
* ```ts
|
|
5250
|
+
* observer.cancelValidator('simulcast-receivers', 'sfu redeployed');
|
|
5251
|
+
*
|
|
5252
|
+
* // or one specific instance — `observer.validators` holds what is running
|
|
5253
|
+
* for (const validator of observer.validators) observer.cancelValidator(validator, 'shutting down');
|
|
5254
|
+
* ```
|
|
5255
|
+
*
|
|
5256
|
+
* Pass a real reason. The default tells the reader nothing they could not already infer.
|
|
5257
|
+
*/
|
|
5258
|
+
cancelValidator(target: keyof AvailableValidatorConfigs | RunningValidator, reason?: string): number;
|
|
5139
5259
|
getObservedCall<T extends Record<string, unknown> = Record<string, unknown>>(callId: string): ObservedCall<T> | undefined;
|
|
5140
5260
|
createObservedCall<T extends Record<string, unknown> = Record<string, unknown>>(settings: ObservedCallSettings<T>): ObservedCall<T> | undefined;
|
|
5141
5261
|
getOrCreateObservedCall<T extends Record<string, unknown> = Record<string, unknown>>(settings: ObservedCallSettings<T>): ObservedCall<T> | undefined;
|