@osqd/bothandlerjs 0.5.0 → 0.7.0
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/CHANGELOG.md +227 -1
- package/README.md +18 -10
- package/dist/adapters/fastify.d.ts +10 -0
- package/dist/adapters/index.cjs +38 -10
- package/dist/adapters/index.cjs.map +1 -1
- package/dist/adapters/index.js +38 -10
- package/dist/adapters/index.js.map +1 -1
- package/dist/challenge/index.d.ts +40 -0
- package/dist/cli.cjs +2256 -103
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2256 -103
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +55 -0
- package/dist/core.d.ts +48 -21
- package/dist/corpus/index.cjs +365 -7
- package/dist/corpus/index.cjs.map +1 -1
- package/dist/corpus/index.js +365 -7
- package/dist/corpus/index.js.map +1 -1
- package/dist/corpus/schema.d.ts +33 -0
- package/dist/crawler-ranges.d.ts +31 -0
- package/dist/dashboard/client/actions.d.ts +1 -1
- package/dist/dashboard/client/app.d.ts +9 -2
- package/dist/dashboard/client/boot.d.ts +32 -3
- package/dist/dashboard/client/query.d.ts +72 -12
- package/dist/dashboard/client/registry.d.ts +25 -0
- package/dist/dashboard/client/saved.d.ts +29 -0
- package/dist/dashboard/client/store.d.ts +16 -2
- package/dist/dashboard/client/types.d.ts +2 -0
- package/dist/dashboard/client.generated.d.ts +1 -1
- package/dist/dashboard/types.d.ts +15 -0
- package/dist/detectors/blended-identity.d.ts +34 -0
- package/dist/detectors/challenge-integrity.d.ts +26 -0
- package/dist/detectors/challenge-reaction.d.ts +39 -0
- package/dist/detectors/clearance.d.ts +1 -23
- package/dist/detectors/id-enumeration.d.ts +31 -0
- package/dist/detectors/index.d.ts +24 -1
- package/dist/detectors/known-bots.d.ts +11 -0
- package/dist/detectors/marker.d.ts +106 -0
- package/dist/detectors/parameter-sweep.d.ts +39 -0
- package/dist/detectors/probe-signature.d.ts +27 -0
- package/dist/detectors/probe-volume.d.ts +26 -0
- package/dist/detectors/site-baseline.d.ts +135 -0
- package/dist/detectors/target-integrity.d.ts +16 -0
- package/dist/detectors/transport-coherence.d.ts +31 -0
- package/dist/detectors/trap.d.ts +10 -3
- package/dist/detectors/types.d.ts +17 -0
- package/dist/element/index.cjs +730 -80
- package/dist/element/index.cjs.map +1 -1
- package/dist/element/index.js +730 -80
- package/dist/element/index.js.map +1 -1
- package/dist/index.cjs +2044 -123
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.js +2025 -123
- package/dist/index.js.map +1 -1
- package/dist/internal/async.d.ts +0 -3
- package/dist/internal/ip.d.ts +18 -0
- package/dist/internal/text.d.ts +28 -0
- package/dist/metrics.d.ts +18 -0
- package/dist/probe/index.d.ts +153 -0
- package/dist/probe/marker.d.ts +119 -0
- package/dist/site/index.d.ts +122 -0
- package/dist/state.d.ts +205 -0
- package/dist/stores/redis.d.ts +24 -1
- package/dist/types.d.ts +106 -0
- package/docs/course/05-detectors.md +9 -4
- package/docs/course/06-identity.md +1 -1
- package/docs/course/16-proving-it.md +15 -9
- package/docs/course/index.md +1 -1
- package/docs/design/decisions.md +1 -1
- package/docs/detection/correlation.md +284 -0
- package/docs/detection/detectors.md +259 -1
- package/docs/detection/index.md +2 -1
- package/docs/detection/shadow-mode.md +147 -0
- package/docs/detection/signatures.md +10 -2
- package/docs/index.md +3 -2
- package/docs/integration/client-ip.md +16 -0
- package/docs/operations/dashboard.md +40 -1
- package/docs/operations/filters.md +143 -0
- package/docs/operations/index.md +1 -0
- package/docs/operations/metrics.md +18 -0
- package/docs/policy/presets.md +1 -1
- package/docs/start/choosing-a-policy.md +1 -1
- package/docs/start/first-integration.md +1 -1
- package/docs/start/installation.md +2 -2
- package/docs/testing/cli.md +7 -1
- package/docs/testing/corpus.md +12 -8
- package/docs/testing/index.md +1 -1
- package/docs/testing/try-it.md +1 -1
- package/package.json +4 -1
package/dist/corpus/schema.d.ts
CHANGED
|
@@ -61,6 +61,14 @@ export interface CaseRequest {
|
|
|
61
61
|
tlsFingerprint?: string;
|
|
62
62
|
/** Milliseconds after the case's start time. Drives the behavioural detectors. */
|
|
63
63
|
atMs?: number;
|
|
64
|
+
/**
|
|
65
|
+
* What the application answered, if the case is about that.
|
|
66
|
+
*
|
|
67
|
+
* The engine decides before a response exists, so this is reported back afterwards the
|
|
68
|
+
* way an adapter reports it. Only cases about response shape need it — a scan that is
|
|
69
|
+
* almost all misses being the one that matters.
|
|
70
|
+
*/
|
|
71
|
+
status?: number;
|
|
64
72
|
/** The source could not supply the full header set. See `RequestFacts.partialHeaders`. */
|
|
65
73
|
partialHeaders?: boolean;
|
|
66
74
|
}
|
|
@@ -133,6 +141,31 @@ export interface TrafficCase {
|
|
|
133
141
|
* from the request.
|
|
134
142
|
*/
|
|
135
143
|
clearance?: "pow" | "interaction" | "operator";
|
|
144
|
+
/**
|
|
145
|
+
* Answers this actor has already given to challenges, established before the case runs.
|
|
146
|
+
*
|
|
147
|
+
* Declared rather than performed, for the same reason `clearance` is: the corpus
|
|
148
|
+
* replays *requests*, and these are outcomes of the challenge verification endpoint,
|
|
149
|
+
* which is deliberately not part of the request path. A case cannot submit a solution
|
|
150
|
+
* any more than it can sign its own clearance token, so it says what has already
|
|
151
|
+
* happened and the runner tells the handler.
|
|
152
|
+
*/
|
|
153
|
+
/**
|
|
154
|
+
* Whether this client stores the cookies it is given. Default: inferred.
|
|
155
|
+
*
|
|
156
|
+
* The runner returns cookies the handler set to any case that sends a cookie of its
|
|
157
|
+
* own, because that is what having a jar means and modelling it otherwise made every
|
|
158
|
+
* browsing session look like a client that threw its marker away. A case sets this
|
|
159
|
+
* explicitly to say something the requests cannot: `false` models a client that sends
|
|
160
|
+
* one cookie it was configured with and stores nothing new.
|
|
161
|
+
*/
|
|
162
|
+
keepsCookies?: boolean;
|
|
163
|
+
challengeHistory?: {
|
|
164
|
+
/** Solutions submitted for challenges that had already been solved. */
|
|
165
|
+
replayedSolutions?: number;
|
|
166
|
+
/** Solutions returned faster than the proof of work can be computed in a browser. */
|
|
167
|
+
implausibleSolves?: number;
|
|
168
|
+
};
|
|
136
169
|
/**
|
|
137
170
|
* A person whose *client software* declares itself automated, and why.
|
|
138
171
|
*
|
package/dist/crawler-ranges.d.ts
CHANGED
|
@@ -81,6 +81,37 @@ export interface RefreshResult {
|
|
|
81
81
|
* an error rather than a guess.
|
|
82
82
|
*/
|
|
83
83
|
export declare function fetchCrawlerRanges(source: PublishedRangeSource, options?: RefreshOptions): Promise<string[]>;
|
|
84
|
+
/** Where a reputation or hosting-provider address list is published. */
|
|
85
|
+
export interface AddressListSource {
|
|
86
|
+
/** The range set to install it as. `denylist` blocks; `datacenter` corroborates. */
|
|
87
|
+
id: "denylist" | "datacenter" | "allowlist";
|
|
88
|
+
url: string;
|
|
89
|
+
}
|
|
90
|
+
export interface AddressListOptions extends RefreshOptions {
|
|
91
|
+
/** Entries to accept before refusing the list. Default 100,000. */
|
|
92
|
+
maxPrefixes?: number;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Fetches a published address list — a reputation feed, a hosting provider's own ranges.
|
|
96
|
+
*
|
|
97
|
+
* The same hardened path as {@link fetchCrawlerRanges}: HTTPS only, a size cap, comments
|
|
98
|
+
* stripped, JSON `prefixes` documents or one prefix per line, and a list that is empty,
|
|
99
|
+
* oversized or contains a block big enough to matter is refused **whole** rather than in
|
|
100
|
+
* part. It is a separate entry point because the limits differ — a reputation feed is
|
|
101
|
+
* thousands of entries where a crawler's is hundreds, and calling one a crawler's list in
|
|
102
|
+
* an error message helps nobody.
|
|
103
|
+
*
|
|
104
|
+
* It does not install anything. Hand the result to `updateRanges("denylist", …)` when you
|
|
105
|
+
* are ready, which is the same two-step the crawler path takes and for the same reason:
|
|
106
|
+
* fetching is the part that can fail, and installing is the part that changes behaviour.
|
|
107
|
+
*
|
|
108
|
+
* **A denylist entry is `certain`.** It does not corroborate anything — it decides, and it
|
|
109
|
+
* blocks people. A feed is somebody else's judgement about an address, refreshed on
|
|
110
|
+
* somebody else's schedule, and an address that was a bot last month may be a customer's
|
|
111
|
+
* home connection this month. Read what you are subscribing to, and prefer `datacenter`
|
|
112
|
+
* for anything you have not decided about yourself.
|
|
113
|
+
*/
|
|
114
|
+
export declare function fetchAddressList(source: AddressListSource, options?: AddressListOptions): Promise<string[]>;
|
|
84
115
|
/**
|
|
85
116
|
* Fetches every published list and installs it.
|
|
86
117
|
*
|
|
@@ -14,4 +14,4 @@
|
|
|
14
14
|
*/
|
|
15
15
|
export type AfterAction = () => void;
|
|
16
16
|
export declare function isConfirming(): boolean;
|
|
17
|
-
export declare function actorActions(key: string, after: AfterAction): HTMLElement[];
|
|
17
|
+
export declare function actorActions(key: string, after: AfterAction, current?: string): HTMLElement[];
|
|
@@ -19,8 +19,15 @@ export interface App {
|
|
|
19
19
|
replace?: boolean;
|
|
20
20
|
push?: boolean;
|
|
21
21
|
}) => void;
|
|
22
|
-
/**
|
|
23
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Puts the feed's filter state and the Actors scope into the URL, so a view can be
|
|
24
|
+
* sent to somebody. `replace: false` for a discrete choice a click made, which the
|
|
25
|
+
* back button should undo; the default replaces, because a history entry per keystroke
|
|
26
|
+
* of the search box is not history.
|
|
27
|
+
*/
|
|
28
|
+
syncUrl: (options?: {
|
|
29
|
+
replace?: boolean;
|
|
30
|
+
}) => void;
|
|
24
31
|
}
|
|
25
32
|
export declare const app: App;
|
|
26
33
|
/**
|
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
import type { Boot } from "./types.js";
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Reassignable, and that is the fix for a real bug rather than a style choice.
|
|
4
|
+
*
|
|
5
|
+
* On the served page this module evaluates after the nonced script has stamped the
|
|
6
|
+
* global, so reading it once was right. Embedded, the element sets the global and *then*
|
|
7
|
+
* imports the client — correct, and not something it can actually guarantee, because any
|
|
8
|
+
* other code path that reaches a client module first evaluates this one first.
|
|
9
|
+
*
|
|
10
|
+
* One did. `disconnectedCallback` imports `stream.js` to close the stream, `stream.js`
|
|
11
|
+
* imports this, and a host page that mounts, unmounts and remounts the element before the
|
|
12
|
+
* first bootstrap fetch returns — React 18's development double-mount, exactly — ran that
|
|
13
|
+
* import while the global was still undefined. `base` then froze at `""` for the life of
|
|
14
|
+
* the page, so every request the dashboard made went to the host application's own origin
|
|
15
|
+
* root instead of to `src`: no dashboard, and a stray `/api/stream` arriving at somebody
|
|
16
|
+
* else's router.
|
|
17
|
+
*
|
|
18
|
+
* These are `let` and {@link applyBoot} replaces them, so being evaluated early costs
|
|
19
|
+
* nothing. ES module bindings are live and esbuild keeps them so — importers read the
|
|
20
|
+
* variable, not a copy of it — which is what makes this work without every consumer
|
|
21
|
+
* changing to a getter.
|
|
22
|
+
*/
|
|
23
|
+
export declare let BOOT: Boot;
|
|
3
24
|
/** Every request the page makes is relative to the mount path. */
|
|
4
|
-
export declare
|
|
5
|
-
export declare
|
|
25
|
+
export declare let API: string;
|
|
26
|
+
export declare let SECTIONS: Required<import("./types.js").DashboardSections>;
|
|
27
|
+
/**
|
|
28
|
+
* Points this module at the payload the element fetched.
|
|
29
|
+
*
|
|
30
|
+
* Called before the client is imported. Ordering still matters for *drawing* — the client
|
|
31
|
+
* builds its tab strip from these — but no longer for correctness of the mount path,
|
|
32
|
+
* which is the part that was silently wrong.
|
|
33
|
+
*/
|
|
34
|
+
export declare function applyBoot(next: Boot): void;
|
|
@@ -7,19 +7,32 @@ import type { DashboardEntry } from "./types.js";
|
|
|
7
7
|
* question in the two cases somebody actually reaches for the box: an address that
|
|
8
8
|
* also appears inside a User-Agent, and a path that is a prefix of ten others.
|
|
9
9
|
*
|
|
10
|
-
* So terms
|
|
10
|
+
* So terms name a field, and combine:
|
|
11
11
|
*
|
|
12
12
|
* ```text
|
|
13
|
-
* actor:203.0.113.4 -path:/health
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* actor:203.0.113.4 -path:/health that address, except its health checks
|
|
14
|
+
* verdict:human $or verdict:unknown either verdict
|
|
15
|
+
* $not path:/health the same as -path:/health, spelled out
|
|
16
|
+
* action:$in(block, drop) one of a set
|
|
17
|
+
* action:$notin(allow, tag) none of a set
|
|
18
|
+
* (verdict:human $or score:<20) $and $not path:/health
|
|
19
|
+
* "GET /api/v2/orders" a phrase, spaces and all
|
|
17
20
|
* ```
|
|
18
21
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
22
|
+
* **Adjacent terms still mean AND**, which is what narrowing means and what every
|
|
23
|
+
* existing query and saved filter relies on. `$and` is available for people who would
|
|
24
|
+
* rather write it than rely on juxtaposition; it parses to the same thing. `$or` binds
|
|
25
|
+
* more loosely than `$and`, and `$not` more tightly than either, so
|
|
26
|
+
* `a $or b $and c` reads as `a $or (b $and c)` — the conventional precedence, and the
|
|
27
|
+
* reason parentheses exist for the times it is not what you meant.
|
|
28
|
+
*
|
|
29
|
+
* Operator names are prefixed with `$` for one reason: a bare `or` is a word that
|
|
30
|
+
* appears in User-Agents and paths, and a language where an ordinary search word
|
|
31
|
+
* silently becomes an operator is a language that lies about what it matched.
|
|
32
|
+
*
|
|
33
|
+
* **Nothing here throws.** It backs a live search box, so half-typed input is the
|
|
34
|
+
* normal state rather than an error: an unclosed parenthesis, a dangling `$or`, a
|
|
35
|
+
* `$in(` with nothing after it all parse to the best reading available.
|
|
23
36
|
*
|
|
24
37
|
* Kept free of the DOM so it can be unit-tested, which is the point: this is the piece
|
|
25
38
|
* of the page most likely to be wrong in a way nobody notices.
|
|
@@ -31,12 +44,59 @@ export interface Term {
|
|
|
31
44
|
negated: boolean;
|
|
32
45
|
/** For numeric fields: how `value` should be compared. */
|
|
33
46
|
compare?: "<" | ">" | "=" | undefined;
|
|
47
|
+
/** For `$in` and `$notin`: the set. `value` holds the first, so a term always has one. */
|
|
48
|
+
values?: readonly string[] | undefined;
|
|
34
49
|
}
|
|
35
|
-
|
|
50
|
+
/**
|
|
51
|
+
* A parsed query.
|
|
52
|
+
*
|
|
53
|
+
* A tree rather than a list, because `$or` cannot be expressed in a list — which is
|
|
54
|
+
* what the flat version of this quietly told people by ignoring it.
|
|
55
|
+
*/
|
|
56
|
+
export type Filter = {
|
|
57
|
+
kind: "all";
|
|
58
|
+
} | {
|
|
59
|
+
kind: "term";
|
|
60
|
+
term: Term;
|
|
61
|
+
} | {
|
|
62
|
+
kind: "not";
|
|
63
|
+
of: Filter;
|
|
64
|
+
} | {
|
|
65
|
+
kind: "and";
|
|
66
|
+
parts: readonly Filter[];
|
|
67
|
+
} | {
|
|
68
|
+
kind: "or";
|
|
69
|
+
parts: readonly Filter[];
|
|
70
|
+
};
|
|
71
|
+
/** Every field name the language accepts, including the aliases. For suggestions. */
|
|
72
|
+
export declare const FIELD_NAMES: readonly string[];
|
|
73
|
+
/**
|
|
74
|
+
* Values worth offering for the fields that have a fixed set of them.
|
|
75
|
+
*
|
|
76
|
+
* Only where the set really is closed. `rule`, `identity` and `path` take anything, and
|
|
77
|
+
* offering a guess there would be inventing options rather than completing them.
|
|
78
|
+
*/
|
|
79
|
+
export declare const FIELD_VALUES: Readonly<Record<string, readonly string[]>>;
|
|
80
|
+
/**
|
|
81
|
+
* What to offer for the token the caret is sitting in.
|
|
82
|
+
*
|
|
83
|
+
* Returns the completions and the span they would replace, so the caller can put one in
|
|
84
|
+
* without disturbing the rest of the query. A token that already names a field with a
|
|
85
|
+
* closed value set completes the value; anything else completes the field name.
|
|
86
|
+
*/
|
|
87
|
+
export declare function suggestFor(input: string, caret: number): {
|
|
88
|
+
options: readonly string[];
|
|
89
|
+
from: number;
|
|
90
|
+
to: number;
|
|
91
|
+
};
|
|
92
|
+
/** The operator words the language understands. Offered by the completions too. */
|
|
93
|
+
export declare const OPERATORS: readonly string[];
|
|
94
|
+
/** Parses a query. An empty or unparseable one matches everything. */
|
|
95
|
+
export declare function parseFilter(input: string): Filter;
|
|
36
96
|
/** Everything about a request, as one lower-case string. What a free term is matched against. */
|
|
37
97
|
export declare function searchableText(entry: DashboardEntry): string;
|
|
38
|
-
/** True when
|
|
39
|
-
export declare function
|
|
98
|
+
/** True when the query is satisfied. An empty one matches everything. */
|
|
99
|
+
export declare function matches(filter: Filter, entry: DashboardEntry, haystack: string): boolean;
|
|
40
100
|
/** The named filter buttons, which are a second, independent narrowing. */
|
|
41
101
|
export type FilterName = "all" | "proven" | "suspected" | "human" | "guard" | "deny" | "mitigate" | "allow";
|
|
42
102
|
export declare function matchesFilter(filter: FilterName, entry: DashboardEntry): boolean;
|
|
@@ -1,4 +1,29 @@
|
|
|
1
|
+
import type { ActorRow } from "./types.js";
|
|
1
2
|
export declare function loadActors(): Promise<void>;
|
|
2
3
|
/** Polls while the screen is showing, and stops the moment it is not. */
|
|
3
4
|
export declare function trackActors(): void;
|
|
5
|
+
/**
|
|
6
|
+
* The actors visible in the feed you are looking at.
|
|
7
|
+
*
|
|
8
|
+
* The registry answers "who is hitting me hardest", which is a different question from
|
|
9
|
+
* "who is in *this*" — and once a filter is on, the second one is usually what somebody
|
|
10
|
+
* has in mind. Derived from the rows the feed is already showing, so it narrows with the
|
|
11
|
+
* filter, the timeframe and the search without another request.
|
|
12
|
+
*
|
|
13
|
+
* Three columns are left blank on purpose. `Per min`, `Cadence` and `Unsolved` are
|
|
14
|
+
* properties of the whole actor as the engine sees it, and the feed's ring holds a few
|
|
15
|
+
* hundred requests rather than a client's history — computing them from that slice would
|
|
16
|
+
* put a confident number under a heading that means something else. A dash says "ask the
|
|
17
|
+
* registry", which is the button next to it.
|
|
18
|
+
*/
|
|
19
|
+
export declare function feedActors(): ActorRow[];
|
|
20
|
+
/**
|
|
21
|
+
* Moves the toggle to `scope` and redraws, without touching the URL.
|
|
22
|
+
*
|
|
23
|
+
* Separate from the click handler because the URL is also an input: opening a link and
|
|
24
|
+
* pressing Back both arrive here, and neither should write the address they just read.
|
|
25
|
+
*/
|
|
26
|
+
export declare function applyActorScope(scope: "tracked" | "feed"): void;
|
|
27
|
+
/** Wires the tracked/shown toggle. Called once. */
|
|
28
|
+
export declare function initActorScope(): void;
|
|
4
29
|
export declare function drawActors(): void;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filters somebody saved.
|
|
3
|
+
*
|
|
4
|
+
* They live in this browser rather than on the handler, and that is a decision worth
|
|
5
|
+
* stating. A saved filter is a personal working note — the view one operator returns to
|
|
6
|
+
* during an incident — not configuration the whole team shares. Keeping it here means it
|
|
7
|
+
* needs no write endpoint, no auth decision about who may change whose, and no migration
|
|
8
|
+
* when the handler restarts. The cost is that it does not follow you to another machine,
|
|
9
|
+
* which is the right trade for a bookmark.
|
|
10
|
+
*
|
|
11
|
+
* Hidden traffic used to live here too, as a second list. It does not any more: `$not`
|
|
12
|
+
* in the query says the same thing and, unlike a list in one person's browser, travels
|
|
13
|
+
* in the URL — so a view with the noise taken out is something you can send somebody.
|
|
14
|
+
*
|
|
15
|
+
* Every read and write is wrapped: a private window, cleared site data or a browser set to
|
|
16
|
+
* refuse storage all throw on access rather than returning nothing, and a dashboard that
|
|
17
|
+
* cannot save a bookmark must still show the traffic.
|
|
18
|
+
*/
|
|
19
|
+
export interface SavedFilter {
|
|
20
|
+
name: string;
|
|
21
|
+
/** The query text, exactly as typed. */
|
|
22
|
+
query: string;
|
|
23
|
+
/** The chip it was saved with, because a filter is usually both. */
|
|
24
|
+
filter: string;
|
|
25
|
+
}
|
|
26
|
+
export declare function savedFilters(): SavedFilter[];
|
|
27
|
+
/** Saves under a name, replacing any filter already using it. */
|
|
28
|
+
export declare function saveFilter(entry: SavedFilter): SavedFilter[];
|
|
29
|
+
export declare function deleteFilter(name: string): SavedFilter[];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { EditorRule } from "./draft.js";
|
|
2
|
-
import type {
|
|
2
|
+
import type { Filter, FilterName } from "./query.js";
|
|
3
3
|
import type { ActorRow, DashboardEntry, Policy, Row, Snapshot, TabName } from "./types.js";
|
|
4
4
|
/** Rows built into the table at once. Beyond this a feed is scrolled past, not read. */
|
|
5
5
|
export declare const FEED_LIMIT = 300;
|
|
@@ -11,10 +11,13 @@ export interface State {
|
|
|
11
11
|
/** The Actors screen's list, fetched rather than streamed. See `registry.ts`. */
|
|
12
12
|
actors: ActorRow[];
|
|
13
13
|
actorsTracked: number;
|
|
14
|
+
/** Which population the Actors screen is listing. See `registry.feedActors`. */
|
|
15
|
+
actorScope: "tracked" | "feed";
|
|
14
16
|
paused: boolean;
|
|
15
17
|
filter: FilterName;
|
|
16
18
|
search: string;
|
|
17
|
-
|
|
19
|
+
/** The parsed search. A tree, so `$or` means something. */
|
|
20
|
+
query: Filter;
|
|
18
21
|
tab: TabName;
|
|
19
22
|
open: Set<string>;
|
|
20
23
|
actor: string | undefined;
|
|
@@ -49,6 +52,15 @@ export interface State {
|
|
|
49
52
|
/** How many rows a page of each table holds. Chosen in the page, not configured. */
|
|
50
53
|
feedPageSize: number;
|
|
51
54
|
actorsPageSize: number;
|
|
55
|
+
/**
|
|
56
|
+
* The window somebody asked to look at, as absolute instants.
|
|
57
|
+
*
|
|
58
|
+
* Either end may be left open, which is what makes one control answer all three of the
|
|
59
|
+
* questions people actually ask: from an incident until now, from the beginning until
|
|
60
|
+
* something stopped, or between two moments.
|
|
61
|
+
*/
|
|
62
|
+
fromMs: number | undefined;
|
|
63
|
+
toMs: number | undefined;
|
|
52
64
|
/**
|
|
53
65
|
* How many skipped entries have already been fetched back and merged.
|
|
54
66
|
*
|
|
@@ -70,6 +82,8 @@ export declare const state: State;
|
|
|
70
82
|
export declare function ingest(entry: DashboardEntry): void;
|
|
71
83
|
export declare function clearFeed(): void;
|
|
72
84
|
export declare function setSearch(value: string): void;
|
|
85
|
+
/** Sets the window somebody is looking at. Either end may be left open. */
|
|
86
|
+
export declare function setTimeframe(from: number | undefined, to: number | undefined): void;
|
|
73
87
|
/** Back to the live first page, thawed. Called whenever what matches changes. */
|
|
74
88
|
export declare function resetPaging(): void;
|
|
75
89
|
/**
|
|
@@ -55,6 +55,8 @@ export interface Snapshot extends DashboardSnapshot {
|
|
|
55
55
|
* of the store came to fail on `HTMLInputElement`.
|
|
56
56
|
*/
|
|
57
57
|
export interface ActorRow {
|
|
58
|
+
/** A name somebody gave this actor. Never read by detection. */
|
|
59
|
+
label?: string | undefined;
|
|
58
60
|
key: string;
|
|
59
61
|
requests: number;
|
|
60
62
|
recentRate: number;
|