@adonis-agora/telescope 0.2.0 → 0.3.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 +30 -0
- package/dist/providers/telescope_provider.d.ts +16 -0
- package/dist/providers/telescope_provider.d.ts.map +1 -1
- package/dist/providers/telescope_provider.js +33 -2
- package/dist/providers/telescope_provider.js.map +1 -1
- package/dist/providers/telescope_ui_provider.d.ts.map +1 -1
- package/dist/providers/telescope_ui_provider.js +87 -1
- package/dist/providers/telescope_ui_provider.js.map +1 -1
- package/dist/providers/telescope_watchers_provider.d.ts +11 -0
- package/dist/providers/telescope_watchers_provider.d.ts.map +1 -1
- package/dist/providers/telescope_watchers_provider.js +58 -0
- package/dist/providers/telescope_watchers_provider.js.map +1 -1
- package/dist/src/define_config.d.ts +50 -0
- package/dist/src/define_config.d.ts.map +1 -1
- package/dist/src/define_config.js +9 -0
- package/dist/src/define_config.js.map +1 -1
- package/dist/src/index.d.ts +23 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +17 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/metrics/metrics_service.d.ts +63 -0
- package/dist/src/metrics/metrics_service.d.ts.map +1 -0
- package/dist/src/metrics/metrics_service.js +116 -0
- package/dist/src/metrics/metrics_service.js.map +1 -0
- package/dist/src/metrics/rollup.d.ts +61 -0
- package/dist/src/metrics/rollup.d.ts.map +1 -0
- package/dist/src/metrics/rollup.js +114 -0
- package/dist/src/metrics/rollup.js.map +1 -0
- package/dist/src/metrics/stats.d.ts +112 -0
- package/dist/src/metrics/stats.d.ts.map +1 -0
- package/dist/src/metrics/stats.js +255 -0
- package/dist/src/metrics/stats.js.map +1 -0
- package/dist/src/metrics/timeseries.d.ts +22 -0
- package/dist/src/metrics/timeseries.d.ts.map +1 -0
- package/dist/src/metrics/timeseries.js +34 -0
- package/dist/src/metrics/timeseries.js.map +1 -0
- package/dist/src/metrics/traces.d.ts +27 -0
- package/dist/src/metrics/traces.d.ts.map +1 -0
- package/dist/src/metrics/traces.js +71 -0
- package/dist/src/metrics/traces.js.map +1 -0
- package/dist/src/metrics/waterfall.d.ts +44 -0
- package/dist/src/metrics/waterfall.d.ts.map +1 -0
- package/dist/src/metrics/waterfall.js +99 -0
- package/dist/src/metrics/waterfall.js.map +1 -0
- package/dist/src/query/n_plus_one.d.ts +60 -0
- package/dist/src/query/n_plus_one.d.ts.map +1 -0
- package/dist/src/query/n_plus_one.js +102 -0
- package/dist/src/query/n_plus_one.js.map +1 -0
- package/dist/src/registry.d.ts +9 -0
- package/dist/src/registry.d.ts.map +1 -1
- package/dist/src/registry.js +6 -0
- package/dist/src/registry.js.map +1 -1
- package/dist/src/sampling/sampling.d.ts +52 -0
- package/dist/src/sampling/sampling.d.ts.map +1 -0
- package/dist/src/sampling/sampling.js +111 -0
- package/dist/src/sampling/sampling.js.map +1 -0
- package/dist/src/sampling/sampling_store.d.ts +33 -0
- package/dist/src/sampling/sampling_store.d.ts.map +1 -0
- package/dist/src/sampling/sampling_store.js +65 -0
- package/dist/src/sampling/sampling_store.js.map +1 -0
- package/dist/src/stream/entry_events.d.ts +45 -0
- package/dist/src/stream/entry_events.d.ts.map +1 -0
- package/dist/src/stream/entry_events.js +56 -0
- package/dist/src/stream/entry_events.js.map +1 -0
- package/dist/src/stream/index.d.ts +6 -0
- package/dist/src/stream/index.d.ts.map +1 -0
- package/dist/src/stream/index.js +5 -0
- package/dist/src/stream/index.js.map +1 -0
- package/dist/src/stream/stream_handler.d.ts +42 -0
- package/dist/src/stream/stream_handler.d.ts.map +1 -0
- package/dist/src/stream/stream_handler.js +48 -0
- package/dist/src/stream/stream_handler.js.map +1 -0
- package/dist/src/stream/streaming_store.d.ts +31 -0
- package/dist/src/stream/streaming_store.d.ts.map +1 -0
- package/dist/src/stream/streaming_store.js +49 -0
- package/dist/src/stream/streaming_store.js.map +1 -0
- package/dist/src/ui/api.d.ts +39 -1
- package/dist/src/ui/api.d.ts.map +1 -1
- package/dist/src/ui/api.js +116 -1
- package/dist/src/ui/api.js.map +1 -1
- package/dist/src/ui/define_config.d.ts +27 -0
- package/dist/src/ui/define_config.d.ts.map +1 -1
- package/dist/src/ui/define_config.js +6 -0
- package/dist/src/ui/define_config.js.map +1 -1
- package/dist/src/ui/http.d.ts +40 -0
- package/dist/src/ui/http.d.ts.map +1 -1
- package/dist/src/ui/http.js +43 -0
- package/dist/src/ui/http.js.map +1 -1
- package/dist/src/ui/index.d.ts +5 -3
- package/dist/src/ui/index.d.ts.map +1 -1
- package/dist/src/ui/index.js +3 -1
- package/dist/src/ui/index.js.map +1 -1
- package/dist/src/ui/request_replay.d.ts +56 -0
- package/dist/src/ui/request_replay.d.ts.map +1 -0
- package/dist/src/ui/request_replay.js +120 -0
- package/dist/src/ui/request_replay.js.map +1 -0
- package/dist/src/watchers/define_config.d.ts +17 -1
- package/dist/src/watchers/define_config.d.ts.map +1 -1
- package/dist/src/watchers/define_config.js +13 -0
- package/dist/src/watchers/define_config.js.map +1 -1
- package/dist/src/watchers/events_watcher.d.ts +63 -0
- package/dist/src/watchers/events_watcher.d.ts.map +1 -0
- package/dist/src/watchers/events_watcher.js +81 -0
- package/dist/src/watchers/events_watcher.js.map +1 -0
- package/dist/src/watchers/index.d.ts +6 -0
- package/dist/src/watchers/index.d.ts.map +1 -1
- package/dist/src/watchers/index.js +6 -0
- package/dist/src/watchers/index.js.map +1 -1
- package/dist/src/watchers/queue_watcher.d.ts +97 -0
- package/dist/src/watchers/queue_watcher.d.ts.map +1 -0
- package/dist/src/watchers/queue_watcher.js +114 -0
- package/dist/src/watchers/queue_watcher.js.map +1 -0
- package/dist/src/watchers/redis_watcher.d.ts +103 -0
- package/dist/src/watchers/redis_watcher.d.ts.map +1 -0
- package/dist/src/watchers/redis_watcher.js +161 -0
- package/dist/src/watchers/redis_watcher.js.map +1 -0
- package/dist/stubs/config/telescope.stub +9 -0
- package/dist/stubs/config/telescope_ui.stub +13 -0
- package/dist/stubs/config/telescope_watchers.stub +14 -3
- package/package.json +1 -1
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type Entry } from '../entry.js';
|
|
2
|
+
/** A flat family-count N+1 insight — "query template X ran N times". */
|
|
3
|
+
export interface NPlusOneInsight {
|
|
4
|
+
familyHash: string;
|
|
5
|
+
count: number;
|
|
6
|
+
sql: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Flat N+1 detection — ported from `nestjs-telescope`'s `query/n-plus-one.ts`.
|
|
10
|
+
* Group query entries by `familyHash`; report templates that ran `>= threshold`
|
|
11
|
+
* times. Pure; order follows insertion (first-seen family first).
|
|
12
|
+
*/
|
|
13
|
+
export declare function detectNPlusOne(entries: Entry[], threshold: number): NPlusOneInsight[];
|
|
14
|
+
/**
|
|
15
|
+
* A detected N+1 LOOP pattern within a single request/trace: one driving "parent"
|
|
16
|
+
* query (the SELECT that produced the rows being iterated) followed by N
|
|
17
|
+
* similarly-shaped "child" queries (the per-row lookups). Richer than the flat
|
|
18
|
+
* {@link detectNPlusOne} family-count: it attributes a likely parent and weights
|
|
19
|
+
* by the total time WASTED in the loop, so the worst offenders rank first.
|
|
20
|
+
*/
|
|
21
|
+
export interface NPlusOnePattern {
|
|
22
|
+
/** The repeated child query's family hash. */
|
|
23
|
+
childFamilyHash: string;
|
|
24
|
+
/** A representative child SQL (template/text). */
|
|
25
|
+
childSql: string;
|
|
26
|
+
/** How many times the child query ran in the trace (>= threshold). */
|
|
27
|
+
count: number;
|
|
28
|
+
/** Sum of the child queries' durations (ms) — the "wasted" time, the rank key. */
|
|
29
|
+
totalDurationMs: number;
|
|
30
|
+
/**
|
|
31
|
+
* The family hash of the query that most likely drove the loop — the distinct
|
|
32
|
+
* query immediately preceding the loop in record order — or `null` when the
|
|
33
|
+
* loop is the first thing in the trace (no identifiable parent).
|
|
34
|
+
*/
|
|
35
|
+
parentFamilyHash: string | null;
|
|
36
|
+
/** The likely-parent SQL, or `null` when there is no identifiable parent. */
|
|
37
|
+
parentSql: string | null;
|
|
38
|
+
/** A representative child entry id (deep-link / hydration seam). */
|
|
39
|
+
representativeId: string;
|
|
40
|
+
/** The trace the pattern was found in. */
|
|
41
|
+
traceId: string;
|
|
42
|
+
}
|
|
43
|
+
export interface NPlusOnePatternOptions {
|
|
44
|
+
/** Minimum repetitions of one child template to flag a loop. */
|
|
45
|
+
threshold: number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Detect N+1 loop patterns in a request/trace's query entries. Ported from
|
|
49
|
+
* `nestjs-telescope`'s `query/n-plus-one-pattern.ts` (NestJS `batchId` becomes
|
|
50
|
+
* Adonis `traceId`). For each query family that repeats `>= threshold` times we
|
|
51
|
+
* emit a pattern weighted by the loop's total duration and attribute the likely
|
|
52
|
+
* driving parent (the distinct query that ran just before the loop began). Pure;
|
|
53
|
+
* ordered by total wasted duration desc.
|
|
54
|
+
*
|
|
55
|
+
* NOTE: callers pass entries in OLDEST-FIRST record order (ascending sequence) so
|
|
56
|
+
* "the query immediately preceding the loop" is meaningful. The store returns
|
|
57
|
+
* newest-first, so the service reverses before calling.
|
|
58
|
+
*/
|
|
59
|
+
export declare function detectNPlusOnePatterns(entries: Entry[], options: NPlusOnePatternOptions): NPlusOnePattern[];
|
|
60
|
+
//# sourceMappingURL=n_plus_one.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"n_plus_one.d.ts","sourceRoot":"","sources":["../../../src/query/n_plus_one.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAa,MAAM,aAAa,CAAC;AAEpD,wEAAwE;AACxE,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAWD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,eAAe,EAAE,CAkBrF;AAED;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,8CAA8C;IAC9C,eAAe,EAAE,MAAM,CAAC;IACxB,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,eAAe,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,6EAA6E;IAC7E,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,oEAAoE;IACpE,gBAAgB,EAAE,MAAM,CAAC;IACzB,0CAA0C;IAC1C,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,sBAAsB;IACrC,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;CACnB;AAWD;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,KAAK,EAAE,EAChB,OAAO,EAAE,sBAAsB,GAC9B,eAAe,EAAE,CA8CnB"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { EntryType } from '../entry.js';
|
|
2
|
+
/** Extract the `sql` string from a query entry's content, or `''`. */
|
|
3
|
+
function sqlOf(entry) {
|
|
4
|
+
const record = typeof entry.content === 'object' && entry.content !== null
|
|
5
|
+
? entry.content
|
|
6
|
+
: null;
|
|
7
|
+
return record !== null && typeof record.sql === 'string' ? record.sql : '';
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Flat N+1 detection — ported from `nestjs-telescope`'s `query/n-plus-one.ts`.
|
|
11
|
+
* Group query entries by `familyHash`; report templates that ran `>= threshold`
|
|
12
|
+
* times. Pure; order follows insertion (first-seen family first).
|
|
13
|
+
*/
|
|
14
|
+
export function detectNPlusOne(entries, threshold) {
|
|
15
|
+
const groups = new Map();
|
|
16
|
+
for (const entry of entries) {
|
|
17
|
+
if (entry.type !== EntryType.Query || entry.familyHash === null)
|
|
18
|
+
continue;
|
|
19
|
+
const existing = groups.get(entry.familyHash);
|
|
20
|
+
if (existing) {
|
|
21
|
+
existing.count += 1;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
groups.set(entry.familyHash, { count: 1, sql: sqlOf(entry) });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const insights = [];
|
|
28
|
+
for (const [familyHash, group] of groups) {
|
|
29
|
+
if (group.count >= threshold) {
|
|
30
|
+
insights.push({ familyHash, count: group.count, sql: group.sql });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return insights;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Detect N+1 loop patterns in a request/trace's query entries. Ported from
|
|
37
|
+
* `nestjs-telescope`'s `query/n-plus-one-pattern.ts` (NestJS `batchId` becomes
|
|
38
|
+
* Adonis `traceId`). For each query family that repeats `>= threshold` times we
|
|
39
|
+
* emit a pattern weighted by the loop's total duration and attribute the likely
|
|
40
|
+
* driving parent (the distinct query that ran just before the loop began). Pure;
|
|
41
|
+
* ordered by total wasted duration desc.
|
|
42
|
+
*
|
|
43
|
+
* NOTE: callers pass entries in OLDEST-FIRST record order (ascending sequence) so
|
|
44
|
+
* "the query immediately preceding the loop" is meaningful. The store returns
|
|
45
|
+
* newest-first, so the service reverses before calling.
|
|
46
|
+
*/
|
|
47
|
+
export function detectNPlusOnePatterns(entries, options) {
|
|
48
|
+
const queries = entries.filter((entry) => entry.type === EntryType.Query && entry.familyHash !== null);
|
|
49
|
+
const groups = new Map();
|
|
50
|
+
queries.forEach((entry, index) => {
|
|
51
|
+
const familyHash = entry.familyHash;
|
|
52
|
+
const duration = typeof entry.durationMs === 'number' ? entry.durationMs : 0;
|
|
53
|
+
const existing = groups.get(familyHash);
|
|
54
|
+
if (existing) {
|
|
55
|
+
existing.count += 1;
|
|
56
|
+
existing.totalDurationMs += duration;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
groups.set(familyHash, {
|
|
60
|
+
count: 1,
|
|
61
|
+
totalDurationMs: duration,
|
|
62
|
+
representativeId: entry.id,
|
|
63
|
+
sql: sqlOf(entry),
|
|
64
|
+
firstIndex: index,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
const patterns = [];
|
|
69
|
+
for (const [childFamilyHash, group] of groups) {
|
|
70
|
+
if (group.count < options.threshold)
|
|
71
|
+
continue;
|
|
72
|
+
const parent = findParent(queries, group.firstIndex, childFamilyHash);
|
|
73
|
+
patterns.push({
|
|
74
|
+
childFamilyHash,
|
|
75
|
+
childSql: group.sql,
|
|
76
|
+
count: group.count,
|
|
77
|
+
totalDurationMs: group.totalDurationMs,
|
|
78
|
+
parentFamilyHash: parent?.familyHash ?? null,
|
|
79
|
+
parentSql: parent === null ? null : sqlOf(parent),
|
|
80
|
+
representativeId: group.representativeId,
|
|
81
|
+
traceId: queries[group.firstIndex]?.traceId ?? '',
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return patterns.sort((a, b) => b.totalDurationMs - a.totalDurationMs ||
|
|
85
|
+
b.count - a.count ||
|
|
86
|
+
a.childFamilyHash.localeCompare(b.childFamilyHash));
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The likely driving parent for a loop whose first child is at `firstIndex`:
|
|
90
|
+
* walking BACKWARDS from just before the loop, the first query of a DIFFERENT
|
|
91
|
+
* family. Returns `null` when none precedes it (the loop is the trace's start).
|
|
92
|
+
*/
|
|
93
|
+
function findParent(queries, firstIndex, childFamilyHash) {
|
|
94
|
+
for (let i = firstIndex - 1; i >= 0; i--) {
|
|
95
|
+
const candidate = queries[i];
|
|
96
|
+
if (candidate !== undefined && candidate.familyHash !== childFamilyHash) {
|
|
97
|
+
return candidate;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=n_plus_one.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"n_plus_one.js","sourceRoot":"","sources":["../../../src/query/n_plus_one.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,SAAS,EAAE,MAAM,aAAa,CAAC;AASpD,sEAAsE;AACtE,SAAS,KAAK,CAAC,KAAY;IACzB,MAAM,MAAM,GACV,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,KAAK,IAAI;QACzD,CAAC,CAAE,KAAK,CAAC,OAAmC;QAC5C,CAAC,CAAC,IAAI,CAAC;IACX,OAAO,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;AAC7E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,OAAgB,EAAE,SAAiB;IAChE,MAAM,MAAM,GAAG,IAAI,GAAG,EAA0C,CAAC;IACjE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,KAAK,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI;YAAE,SAAS;QAC1E,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAC9C,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,KAAK,IAAI,CAAC,CAAC;QACtB,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,GAAsB,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;QACzC,IAAI,KAAK,CAAC,KAAK,IAAI,SAAS,EAAE,CAAC;YAC7B,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AA8CD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,sBAAsB,CACpC,OAAgB,EAChB,OAA+B;IAE/B,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAC5B,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,KAAK,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI,CACvE,CAAC;IAEF,MAAM,MAAM,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC7C,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QAC/B,MAAM,UAAU,GAAG,KAAK,CAAC,UAAoB,CAAC;QAC9C,MAAM,QAAQ,GAAG,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7E,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACxC,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,KAAK,IAAI,CAAC,CAAC;YACpB,QAAQ,CAAC,eAAe,IAAI,QAAQ,CAAC;QACvC,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE;gBACrB,KAAK,EAAE,CAAC;gBACR,eAAe,EAAE,QAAQ;gBACzB,gBAAgB,EAAE,KAAK,CAAC,EAAE;gBAC1B,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC;gBACjB,UAAU,EAAE,KAAK;aAClB,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAsB,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,eAAe,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;QAC9C,IAAI,KAAK,CAAC,KAAK,GAAG,OAAO,CAAC,SAAS;YAAE,SAAS;QAC9C,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;QACtE,QAAQ,CAAC,IAAI,CAAC;YACZ,eAAe;YACf,QAAQ,EAAE,KAAK,CAAC,GAAG;YACnB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,eAAe,EAAE,KAAK,CAAC,eAAe;YACtC,gBAAgB,EAAE,MAAM,EAAE,UAAU,IAAI,IAAI;YAC5C,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC;YACjD,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;YACxC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,OAAO,IAAI,EAAE;SAClD,CAAC,CAAC;IACL,CAAC;IAED,OAAO,QAAQ,CAAC,IAAI,CAClB,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACP,CAAC,CAAC,eAAe,GAAG,CAAC,CAAC,eAAe;QACrC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK;QACjB,CAAC,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC,CAAC,eAAe,CAAC,CACrD,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,UAAU,CAAC,OAAgB,EAAE,UAAkB,EAAE,eAAuB;IAC/E,KAAK,IAAI,CAAC,GAAG,UAAU,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QAC7B,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,CAAC,UAAU,KAAK,eAAe,EAAE,CAAC;YACxE,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/dist/src/registry.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ExtensionRegistry } from './extension/registry.js';
|
|
2
2
|
import type { TelescopeStore } from './store.js';
|
|
3
|
+
import type { EntryEvents } from './stream/entry_events.js';
|
|
3
4
|
/**
|
|
4
5
|
* The live runtime telescope handles, published on a cross-copy-stable global slot
|
|
5
6
|
* so the request middleware can reach the active store WITHOUT constructor wiring
|
|
@@ -14,6 +15,12 @@ export interface TelescopeRuntime {
|
|
|
14
15
|
requestWatcherEnabled: boolean;
|
|
15
16
|
/** The booted extension registry (entry types / dashboards / data providers), or `null`. */
|
|
16
17
|
registry: ExtensionRegistry | null;
|
|
18
|
+
/**
|
|
19
|
+
* The live SSE entry-events bus the store's write path publishes persisted
|
|
20
|
+
* entries to and the UI stream route subscribes to, or `null` when live
|
|
21
|
+
* streaming is disabled / not booted.
|
|
22
|
+
*/
|
|
23
|
+
entryEvents: EntryEvents | null;
|
|
17
24
|
}
|
|
18
25
|
/** The shared runtime handle. */
|
|
19
26
|
export declare function getTelescopeRuntime(): TelescopeRuntime;
|
|
@@ -21,6 +28,8 @@ export declare function getTelescopeRuntime(): TelescopeRuntime;
|
|
|
21
28
|
export declare function setTelescopeRuntime(store: TelescopeStore, requestWatcherEnabled: boolean): void;
|
|
22
29
|
/** Publish the booted extension registry so the UI can serve its dashboards + providers. */
|
|
23
30
|
export declare function setTelescopeExtensionRegistry(registry: ExtensionRegistry | null): void;
|
|
31
|
+
/** Publish the SSE entry-events bus so the UI stream route can subscribe to it. */
|
|
32
|
+
export declare function setTelescopeEntryEvents(entryEvents: EntryEvents | null): void;
|
|
24
33
|
/** Tear down the runtime (called by the provider at shutdown). */
|
|
25
34
|
export declare function resetTelescopeRuntime(): void;
|
|
26
35
|
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,2EAA2E;IAC3E,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;IAC7B,iDAAiD;IACjD,qBAAqB,EAAE,OAAO,CAAC;IAC/B,4FAA4F;IAC5F,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACnC;;;;OAIG;IACH,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;CACjC;AAaD,iCAAiC;AACjC,wBAAgB,mBAAmB,IAAI,gBAAgB,CAEtD;AAED,yEAAyE;AACzE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,OAAO,GAAG,IAAI,CAG/F;AAED,4FAA4F;AAC5F,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI,GAAG,IAAI,CAEtF;AAED,mFAAmF;AACnF,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,CAE7E;AAED,kEAAkE;AAClE,wBAAgB,qBAAqB,IAAI,IAAI,CAK5C"}
|
package/dist/src/registry.js
CHANGED
|
@@ -4,6 +4,7 @@ const runtime = globalStore[RUNTIME_KEY] ?? {
|
|
|
4
4
|
store: null,
|
|
5
5
|
requestWatcherEnabled: false,
|
|
6
6
|
registry: null,
|
|
7
|
+
entryEvents: null,
|
|
7
8
|
};
|
|
8
9
|
globalStore[RUNTIME_KEY] = runtime;
|
|
9
10
|
/** The shared runtime handle. */
|
|
@@ -19,10 +20,15 @@ export function setTelescopeRuntime(store, requestWatcherEnabled) {
|
|
|
19
20
|
export function setTelescopeExtensionRegistry(registry) {
|
|
20
21
|
runtime.registry = registry;
|
|
21
22
|
}
|
|
23
|
+
/** Publish the SSE entry-events bus so the UI stream route can subscribe to it. */
|
|
24
|
+
export function setTelescopeEntryEvents(entryEvents) {
|
|
25
|
+
runtime.entryEvents = entryEvents;
|
|
26
|
+
}
|
|
22
27
|
/** Tear down the runtime (called by the provider at shutdown). */
|
|
23
28
|
export function resetTelescopeRuntime() {
|
|
24
29
|
runtime.store = null;
|
|
25
30
|
runtime.requestWatcherEnabled = false;
|
|
26
31
|
runtime.registry = null;
|
|
32
|
+
runtime.entryEvents = null;
|
|
27
33
|
}
|
|
28
34
|
//# sourceMappingURL=registry.js.map
|
package/dist/src/registry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/registry.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/registry.ts"],"names":[],"mappings":"AA0BA,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;AAC3D,MAAM,WAAW,GAAG,UAAsE,CAAC;AAE3F,MAAM,OAAO,GAAqB,WAAW,CAAC,WAAW,CAAC,IAAI;IAC5D,KAAK,EAAE,IAAI;IACX,qBAAqB,EAAE,KAAK;IAC5B,QAAQ,EAAE,IAAI;IACd,WAAW,EAAE,IAAI;CAClB,CAAC;AACF,WAAW,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC;AAEnC,iCAAiC;AACjC,MAAM,UAAU,mBAAmB;IACjC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,mBAAmB,CAAC,KAAqB,EAAE,qBAA8B;IACvF,OAAO,CAAC,KAAK,GAAG,KAAK,CAAC;IACtB,OAAO,CAAC,qBAAqB,GAAG,qBAAqB,CAAC;AACxD,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,6BAA6B,CAAC,QAAkC;IAC9E,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC;AAC9B,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,uBAAuB,CAAC,WAA+B;IACrE,OAAO,CAAC,WAAW,GAAG,WAAW,CAAC;AACpC,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,qBAAqB;IACnC,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;IACrB,OAAO,CAAC,qBAAqB,GAAG,KAAK,CAAC;IACtC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { RecordInput } from '../entry.js';
|
|
2
|
+
/**
|
|
3
|
+
* Tail-sampling rule for a single entry type. Keeps `rate` of the noise but
|
|
4
|
+
* always retains the entries that matter — errors and slow ones.
|
|
5
|
+
*/
|
|
6
|
+
export interface SamplingRule {
|
|
7
|
+
/** Base keep-rate 0–1 applied to ordinary entries of this type. */
|
|
8
|
+
rate: number;
|
|
9
|
+
/** When true, always keep entries that look like errors (see {@link isErrorEntry}). */
|
|
10
|
+
keepErrors?: boolean;
|
|
11
|
+
/** When set, always keep entries whose `durationMs` is at least this value. */
|
|
12
|
+
keepSlowMs?: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Per-type sampling configuration. Each type maps to either a bare keep-rate
|
|
16
|
+
* (uniform down-sampling) or a {@link SamplingRule} object (tail-sampling: keep a
|
|
17
|
+
* fraction but always retain errors / slow entries). The reserved `default` key
|
|
18
|
+
* applies to every entry type that lacks a specific rate override.
|
|
19
|
+
*/
|
|
20
|
+
export type SamplingConfig = Record<string, number | SamplingRule>;
|
|
21
|
+
/**
|
|
22
|
+
* Pragmatic, cheap structural error check used by tail-sampling's `keepErrors`.
|
|
23
|
+
* Reads only shallow, already-present fields — no deep walk — so it stays on the
|
|
24
|
+
* hot path. An entry "looks like an error" when:
|
|
25
|
+
* - its `tags` include `'failed'`, OR
|
|
26
|
+
* - `content.failed === true`, OR
|
|
27
|
+
* - `content.statusCode >= 500`, OR
|
|
28
|
+
* - `content.level` is `'warn'`, `'error'`, or `'fatal'` (a Log entry).
|
|
29
|
+
*/
|
|
30
|
+
export declare function isErrorEntry(input: RecordInput): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Projects a {@link SamplingConfig} down to bare per-type keep-rates for the
|
|
33
|
+
* meta/dashboard contract, which only surfaces the headline rate.
|
|
34
|
+
*/
|
|
35
|
+
export declare function samplingRates(sampling: SamplingConfig): Record<string, number>;
|
|
36
|
+
/**
|
|
37
|
+
* Tail-sampling decision for one input. Plain-number per-type config behaves
|
|
38
|
+
* exactly as a uniform keep-rate. A {@link SamplingRule} additionally always
|
|
39
|
+
* keeps errors (`keepErrors`) and slow entries (`durationMs >= keepSlowMs`)
|
|
40
|
+
* before falling back to the base rate. `random` is the injected 0–1 source.
|
|
41
|
+
*
|
|
42
|
+
* Default-neutral: a type with no rule (and no `default`) is always kept, so an
|
|
43
|
+
* unset `sampling` config records everything exactly as before.
|
|
44
|
+
*/
|
|
45
|
+
export declare function passesSampling(sampling: SamplingConfig, input: RecordInput, random: () => number): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Normalize the author-facing `sampling` option (a bare number → `{ default }`,
|
|
48
|
+
* or a {@link SamplingConfig} as-is, or `undefined` → `{}` meaning record
|
|
49
|
+
* everything) into a {@link SamplingConfig}.
|
|
50
|
+
*/
|
|
51
|
+
export declare function resolveSampling(sampling?: number | SamplingConfig): SamplingConfig;
|
|
52
|
+
//# sourceMappingURL=sampling.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sampling.d.ts","sourceRoot":"","sources":["../../../src/sampling/sampling.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAgB/C;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC;IACb,uFAAuF;IACvF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAAC,CAAC;AAEnE;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAwBxD;AAUD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAM9E;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,MAAM,MAAM,GACnB,OAAO,CA4BT;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,cAAc,GAAG,cAAc,CAIlF"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tail-sampling for the WRITE path. Ported from `nestjs-telescope`'s
|
|
3
|
+
* `config/sampling.ts`: a per-entry-type keep `rate` plus `keepErrors` /
|
|
4
|
+
* `keepSlowMs` overrides so a host can sample noisy types aggressively while
|
|
5
|
+
* never dropping the entries that matter (errors, slow operations).
|
|
6
|
+
*
|
|
7
|
+
* Everything here is a PURE function with an injected RNG, so the sampling
|
|
8
|
+
* decision is deterministic in tests (no flaky `Math.random()` on the hot path).
|
|
9
|
+
*/
|
|
10
|
+
/** Log levels that count as an error for `keepErrors` — a `warn` / `error` /
|
|
11
|
+
* `fatal` line is exactly what you never want sampled away. */
|
|
12
|
+
const ERROR_LOG_LEVELS = new Set(['warn', 'error', 'fatal']);
|
|
13
|
+
/**
|
|
14
|
+
* Pragmatic, cheap structural error check used by tail-sampling's `keepErrors`.
|
|
15
|
+
* Reads only shallow, already-present fields — no deep walk — so it stays on the
|
|
16
|
+
* hot path. An entry "looks like an error" when:
|
|
17
|
+
* - its `tags` include `'failed'`, OR
|
|
18
|
+
* - `content.failed === true`, OR
|
|
19
|
+
* - `content.statusCode >= 500`, OR
|
|
20
|
+
* - `content.level` is `'warn'`, `'error'`, or `'fatal'` (a Log entry).
|
|
21
|
+
*/
|
|
22
|
+
export function isErrorEntry(input) {
|
|
23
|
+
if (input.tags?.includes('failed')) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
const { content } = input;
|
|
27
|
+
if (content === null || typeof content !== 'object') {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
if ('failed' in content && content.failed === true) {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
if ('statusCode' in content) {
|
|
34
|
+
const statusCode = content.statusCode;
|
|
35
|
+
if (typeof statusCode === 'number' && statusCode >= 500) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if ('level' in content) {
|
|
40
|
+
const level = content.level;
|
|
41
|
+
if (typeof level === 'string' && ERROR_LOG_LEVELS.has(level)) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Resolves the keep-rate for a sampling entry. Discriminated purely by `typeof`
|
|
49
|
+
* — a number is a bare rate, an object is a {@link SamplingRule}.
|
|
50
|
+
*/
|
|
51
|
+
function ruleRate(rule) {
|
|
52
|
+
return typeof rule === 'number' ? rule : rule.rate;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Projects a {@link SamplingConfig} down to bare per-type keep-rates for the
|
|
56
|
+
* meta/dashboard contract, which only surfaces the headline rate.
|
|
57
|
+
*/
|
|
58
|
+
export function samplingRates(sampling) {
|
|
59
|
+
const rates = {};
|
|
60
|
+
for (const [type, rule] of Object.entries(sampling)) {
|
|
61
|
+
rates[type] = ruleRate(rule);
|
|
62
|
+
}
|
|
63
|
+
return rates;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Tail-sampling decision for one input. Plain-number per-type config behaves
|
|
67
|
+
* exactly as a uniform keep-rate. A {@link SamplingRule} additionally always
|
|
68
|
+
* keeps errors (`keepErrors`) and slow entries (`durationMs >= keepSlowMs`)
|
|
69
|
+
* before falling back to the base rate. `random` is the injected 0–1 source.
|
|
70
|
+
*
|
|
71
|
+
* Default-neutral: a type with no rule (and no `default`) is always kept, so an
|
|
72
|
+
* unset `sampling` config records everything exactly as before.
|
|
73
|
+
*/
|
|
74
|
+
export function passesSampling(sampling, input, random) {
|
|
75
|
+
const rule = sampling[input.type] ?? sampling.default;
|
|
76
|
+
if (rule === undefined) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
if (typeof rule === 'object') {
|
|
80
|
+
if (rule.keepErrors === true && isErrorEntry(input)) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
if (rule.keepSlowMs !== undefined &&
|
|
84
|
+
input.durationMs !== undefined &&
|
|
85
|
+
input.durationMs !== null &&
|
|
86
|
+
input.durationMs >= rule.keepSlowMs) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const rate = ruleRate(rule);
|
|
91
|
+
if (rate >= 1) {
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
if (rate <= 0) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
return random() < rate;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Normalize the author-facing `sampling` option (a bare number → `{ default }`,
|
|
101
|
+
* or a {@link SamplingConfig} as-is, or `undefined` → `{}` meaning record
|
|
102
|
+
* everything) into a {@link SamplingConfig}.
|
|
103
|
+
*/
|
|
104
|
+
export function resolveSampling(sampling) {
|
|
105
|
+
if (sampling === undefined)
|
|
106
|
+
return {};
|
|
107
|
+
if (typeof sampling === 'number')
|
|
108
|
+
return { default: sampling };
|
|
109
|
+
return sampling;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=sampling.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sampling.js","sourceRoot":"","sources":["../../../src/sampling/sampling.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Entry, RecordInput } from '../entry.js';
|
|
2
|
+
import type { EntryQuery, TelescopeStore } from '../store.js';
|
|
3
|
+
import { type SamplingConfig } from './sampling.js';
|
|
4
|
+
/**
|
|
5
|
+
* A {@link TelescopeStore} decorator that applies tail-sampling on the WRITE
|
|
6
|
+
* path: a dropped entry is NEVER persisted (its `record()` short-circuits before
|
|
7
|
+
* delegating to the inner store). Wiring sampling at the store boundary — the one
|
|
8
|
+
* place every watcher records through — guarantees no watcher can bypass it.
|
|
9
|
+
*
|
|
10
|
+
* The decision is made by the pure {@link passesSampling} using only the shallow
|
|
11
|
+
* fields already on the {@link RecordInput} (no deep walk), so the hot path stays
|
|
12
|
+
* cheap. The RNG is injected so the decision is deterministic in tests.
|
|
13
|
+
*
|
|
14
|
+
* Default-neutral: with an empty {@link SamplingConfig} (the default when the
|
|
15
|
+
* host sets no `sampling` option) every entry passes, so behavior is unchanged.
|
|
16
|
+
*
|
|
17
|
+
* A DROPPED entry still resolves to a synthetic {@link Entry} (never persisted,
|
|
18
|
+
* `sequence: -1`) so the fire-and-forget `record()` contract — callers `void` the
|
|
19
|
+
* promise — is honoured without surfacing the drop as an error.
|
|
20
|
+
*/
|
|
21
|
+
export declare class SamplingTelescopeStore implements TelescopeStore {
|
|
22
|
+
private readonly inner;
|
|
23
|
+
private readonly sampling;
|
|
24
|
+
private readonly random;
|
|
25
|
+
constructor(inner: TelescopeStore, sampling: SamplingConfig, random?: () => number);
|
|
26
|
+
record<TContent>(input: RecordInput<TContent>): Promise<Entry<TContent>>;
|
|
27
|
+
get(id: string): Promise<Entry | null>;
|
|
28
|
+
list(query?: EntryQuery): Promise<Entry[]>;
|
|
29
|
+
count(): Promise<number>;
|
|
30
|
+
prune(olderThan: Date, keepLast?: number): Promise<number>;
|
|
31
|
+
clear(): Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=sampling_store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sampling_store.d.ts","sourceRoot":"","sources":["../../../src/sampling/sampling_store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,KAAK,cAAc,EAAkB,MAAM,eAAe,CAAC;AAEpE;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,sBAAuB,YAAW,cAAc;IAEzD,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAFN,KAAK,EAAE,cAAc,EACrB,QAAQ,EAAE,cAAc,EACxB,MAAM,GAAE,MAAM,MAAoB;IAG/C,MAAM,CAAC,QAAQ,EAAE,KAAK,EAAE,WAAW,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAO9E,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;IAItC,IAAI,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAI1C,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAIxB,KAAK,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAI1D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAGvB"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { passesSampling } from './sampling.js';
|
|
2
|
+
/**
|
|
3
|
+
* A {@link TelescopeStore} decorator that applies tail-sampling on the WRITE
|
|
4
|
+
* path: a dropped entry is NEVER persisted (its `record()` short-circuits before
|
|
5
|
+
* delegating to the inner store). Wiring sampling at the store boundary — the one
|
|
6
|
+
* place every watcher records through — guarantees no watcher can bypass it.
|
|
7
|
+
*
|
|
8
|
+
* The decision is made by the pure {@link passesSampling} using only the shallow
|
|
9
|
+
* fields already on the {@link RecordInput} (no deep walk), so the hot path stays
|
|
10
|
+
* cheap. The RNG is injected so the decision is deterministic in tests.
|
|
11
|
+
*
|
|
12
|
+
* Default-neutral: with an empty {@link SamplingConfig} (the default when the
|
|
13
|
+
* host sets no `sampling` option) every entry passes, so behavior is unchanged.
|
|
14
|
+
*
|
|
15
|
+
* A DROPPED entry still resolves to a synthetic {@link Entry} (never persisted,
|
|
16
|
+
* `sequence: -1`) so the fire-and-forget `record()` contract — callers `void` the
|
|
17
|
+
* promise — is honoured without surfacing the drop as an error.
|
|
18
|
+
*/
|
|
19
|
+
export class SamplingTelescopeStore {
|
|
20
|
+
inner;
|
|
21
|
+
sampling;
|
|
22
|
+
random;
|
|
23
|
+
constructor(inner, sampling, random = Math.random) {
|
|
24
|
+
this.inner = inner;
|
|
25
|
+
this.sampling = sampling;
|
|
26
|
+
this.random = random;
|
|
27
|
+
}
|
|
28
|
+
async record(input) {
|
|
29
|
+
if (!passesSampling(this.sampling, input, this.random)) {
|
|
30
|
+
return droppedEntry(input);
|
|
31
|
+
}
|
|
32
|
+
return this.inner.record(input);
|
|
33
|
+
}
|
|
34
|
+
get(id) {
|
|
35
|
+
return this.inner.get(id);
|
|
36
|
+
}
|
|
37
|
+
list(query) {
|
|
38
|
+
return this.inner.list(query);
|
|
39
|
+
}
|
|
40
|
+
count() {
|
|
41
|
+
return this.inner.count();
|
|
42
|
+
}
|
|
43
|
+
prune(olderThan, keepLast) {
|
|
44
|
+
return this.inner.prune(olderThan, keepLast);
|
|
45
|
+
}
|
|
46
|
+
clear() {
|
|
47
|
+
return this.inner.clear();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** A non-persisted placeholder for a sampled-away entry (`sequence: -1`). */
|
|
51
|
+
function droppedEntry(input) {
|
|
52
|
+
return {
|
|
53
|
+
id: '',
|
|
54
|
+
type: input.type,
|
|
55
|
+
familyHash: input.familyHash ?? null,
|
|
56
|
+
content: input.content,
|
|
57
|
+
tags: input.tags ?? [],
|
|
58
|
+
sequence: -1,
|
|
59
|
+
durationMs: input.durationMs ?? null,
|
|
60
|
+
origin: input.origin ?? 'manual',
|
|
61
|
+
traceId: input.traceId ?? null,
|
|
62
|
+
createdAt: new Date(),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=sampling_store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sampling_store.js","sourceRoot":"","sources":["../../../src/sampling/sampling_store.ts"],"names":[],"mappings":"AAEA,OAAO,EAAuB,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpE;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,sBAAsB;IAEd;IACA;IACA;IAHnB,YACmB,KAAqB,EACrB,QAAwB,EACxB,SAAuB,IAAI,CAAC,MAAM;QAFlC,UAAK,GAAL,KAAK,CAAgB;QACrB,aAAQ,GAAR,QAAQ,CAAgB;QACxB,WAAM,GAAN,MAAM,CAA4B;IAClD,CAAC;IAEJ,KAAK,CAAC,MAAM,CAAW,KAA4B;QACjD,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YACvD,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;IAED,GAAG,CAAC,EAAU;QACZ,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC5B,CAAC;IAED,IAAI,CAAC,KAAkB;QACrB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC;IAED,KAAK;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,SAAe,EAAE,QAAiB;QACtC,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAC/C,CAAC;IAED,KAAK;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IAC5B,CAAC;CACF;AAED,6EAA6E;AAC7E,SAAS,YAAY,CAAW,KAA4B;IAC1D,OAAO;QACL,EAAE,EAAE,EAAE;QACN,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,IAAI;QACpC,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE;QACtB,QAAQ,EAAE,CAAC,CAAC;QACZ,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,IAAI;QACpC,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,QAAQ;QAChC,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,IAAI;QAC9B,SAAS,EAAE,IAAI,IAAI,EAAE;KACtB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Entry } from '../entry.js';
|
|
2
|
+
/**
|
|
3
|
+
* A subscriber notified of each newly-persisted {@link Entry}. Run synchronously
|
|
4
|
+
* from {@link EntryEvents.publish}; it must not throw (the emitter swallows it
|
|
5
|
+
* regardless) and should not block — the SSE route's subscriber just enqueues a
|
|
6
|
+
* frame.
|
|
7
|
+
*/
|
|
8
|
+
export type EntrySubscriber = (entry: Entry) => void;
|
|
9
|
+
/**
|
|
10
|
+
* Unsubscribe handle returned by {@link EntryEvents.subscribe}. Idempotent: calling
|
|
11
|
+
* it twice is a no-op.
|
|
12
|
+
*/
|
|
13
|
+
export type Unsubscribe = () => void;
|
|
14
|
+
/**
|
|
15
|
+
* Process-local pub/sub of "an entry was just persisted". Fed from the store's
|
|
16
|
+
* write path (the {@link import('./streaming_store.js').StreamingTelescopeStore}
|
|
17
|
+
* decorator) AFTER redaction + sampling, so only entries that actually got stored
|
|
18
|
+
* — already scrubbed, never raw — are published. Consumed by the SSE stream route
|
|
19
|
+
* to push live entries to the dashboard.
|
|
20
|
+
*
|
|
21
|
+
* Port of the NestJS `EntryEvents` (an RxJS `Subject`), reshaped to a dependency-free
|
|
22
|
+
* synchronous emitter matching the Agora idiom (no RxJS). Stateless and best-effort:
|
|
23
|
+
* `publish` never throws into the flush path, and is a cheap no-op when there are no
|
|
24
|
+
* subscribers (`subscriberCount === 0`), so the hot write path pays nothing while the
|
|
25
|
+
* dashboard is closed.
|
|
26
|
+
*/
|
|
27
|
+
export declare class EntryEvents {
|
|
28
|
+
private readonly subscribers;
|
|
29
|
+
/**
|
|
30
|
+
* Register a subscriber and get back an idempotent unsubscribe handle. The SSE
|
|
31
|
+
* route subscribes on connect and unsubscribes on client disconnect.
|
|
32
|
+
*/
|
|
33
|
+
subscribe(subscriber: EntrySubscriber): Unsubscribe;
|
|
34
|
+
/**
|
|
35
|
+
* Publish a freshly-persisted entry to every subscriber. Best-effort: a throwing
|
|
36
|
+
* subscriber is isolated (logged-free swallow) so observability can never break
|
|
37
|
+
* the flush path. A no-op when there are no subscribers.
|
|
38
|
+
*/
|
|
39
|
+
publish(entry: Entry): void;
|
|
40
|
+
/** Number of active subscribers — `0` means `publish` is a cheap no-op. */
|
|
41
|
+
get subscriberCount(): number;
|
|
42
|
+
/** Drop every subscriber (called by the provider at shutdown). */
|
|
43
|
+
clear(): void;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=entry_events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry_events.d.ts","sourceRoot":"","sources":["../../../src/stream/entry_events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;AAErD;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC;AAErC;;;;;;;;;;;;GAYG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA8B;IAE1D;;;OAGG;IACH,SAAS,CAAC,UAAU,EAAE,eAAe,GAAG,WAAW;IAUnD;;;;OAIG;IACH,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAW3B,2EAA2E;IAC3E,IAAI,eAAe,IAAI,MAAM,CAE5B;IAED,kEAAkE;IAClE,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-local pub/sub of "an entry was just persisted". Fed from the store's
|
|
3
|
+
* write path (the {@link import('./streaming_store.js').StreamingTelescopeStore}
|
|
4
|
+
* decorator) AFTER redaction + sampling, so only entries that actually got stored
|
|
5
|
+
* — already scrubbed, never raw — are published. Consumed by the SSE stream route
|
|
6
|
+
* to push live entries to the dashboard.
|
|
7
|
+
*
|
|
8
|
+
* Port of the NestJS `EntryEvents` (an RxJS `Subject`), reshaped to a dependency-free
|
|
9
|
+
* synchronous emitter matching the Agora idiom (no RxJS). Stateless and best-effort:
|
|
10
|
+
* `publish` never throws into the flush path, and is a cheap no-op when there are no
|
|
11
|
+
* subscribers (`subscriberCount === 0`), so the hot write path pays nothing while the
|
|
12
|
+
* dashboard is closed.
|
|
13
|
+
*/
|
|
14
|
+
export class EntryEvents {
|
|
15
|
+
subscribers = new Set();
|
|
16
|
+
/**
|
|
17
|
+
* Register a subscriber and get back an idempotent unsubscribe handle. The SSE
|
|
18
|
+
* route subscribes on connect and unsubscribes on client disconnect.
|
|
19
|
+
*/
|
|
20
|
+
subscribe(subscriber) {
|
|
21
|
+
this.subscribers.add(subscriber);
|
|
22
|
+
let active = true;
|
|
23
|
+
return () => {
|
|
24
|
+
if (!active)
|
|
25
|
+
return;
|
|
26
|
+
active = false;
|
|
27
|
+
this.subscribers.delete(subscriber);
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Publish a freshly-persisted entry to every subscriber. Best-effort: a throwing
|
|
32
|
+
* subscriber is isolated (logged-free swallow) so observability can never break
|
|
33
|
+
* the flush path. A no-op when there are no subscribers.
|
|
34
|
+
*/
|
|
35
|
+
publish(entry) {
|
|
36
|
+
if (this.subscribers.size === 0)
|
|
37
|
+
return;
|
|
38
|
+
for (const subscriber of this.subscribers) {
|
|
39
|
+
try {
|
|
40
|
+
subscriber(entry);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// observability must never break the flush
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** Number of active subscribers — `0` means `publish` is a cheap no-op. */
|
|
48
|
+
get subscriberCount() {
|
|
49
|
+
return this.subscribers.size;
|
|
50
|
+
}
|
|
51
|
+
/** Drop every subscriber (called by the provider at shutdown). */
|
|
52
|
+
clear() {
|
|
53
|
+
this.subscribers.clear();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=entry_events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry_events.js","sourceRoot":"","sources":["../../../src/stream/entry_events.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,WAAW;IACL,WAAW,GAAG,IAAI,GAAG,EAAmB,CAAC;IAE1D;;;OAGG;IACH,SAAS,CAAC,UAA2B;QACnC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACjC,IAAI,MAAM,GAAG,IAAI,CAAC;QAClB,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,MAAM;gBAAE,OAAO;YACpB,MAAM,GAAG,KAAK,CAAC;YACf,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACtC,CAAC,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,OAAO,CAAC,KAAY;QAClB,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC;YAAE,OAAO;QACxC,KAAK,MAAM,UAAU,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YAC1C,IAAI,CAAC;gBACH,UAAU,CAAC,KAAK,CAAC,CAAC;YACpB,CAAC;YAAC,MAAM,CAAC;gBACP,2CAA2C;YAC7C,CAAC;QACH,CAAC;IACH,CAAC;IAED,2EAA2E;IAC3E,IAAI,eAAe;QACjB,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;IAC/B,CAAC;IAED,kEAAkE;IAClE,KAAK;QACH,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;IAC3B,CAAC;CACF"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { EntryEvents } from './entry_events.js';
|
|
2
|
+
export type { EntrySubscriber, Unsubscribe } from './entry_events.js';
|
|
3
|
+
export { StreamingTelescopeStore } from './streaming_store.js';
|
|
4
|
+
export { DEFAULT_HEARTBEAT_MS, streamEntries, } from './stream_handler.js';
|
|
5
|
+
export type { StreamOptions, StreamSession } from './stream_handler.js';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/stream/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EACL,oBAAoB,EACpB,aAAa,GACd,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC"}
|