effect-inspect 0.1.1 → 0.2.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/README.md +68 -0
- package/app/dist/client/assets/{index-smV05cfr.js → index-T-bCzOWw.js} +2 -2
- package/app/dist/client/assets/routes-C2qO8k2W.js +5 -0
- package/app/dist/server/assets/{_tanstack-start-manifest_v-CFQ3DEVN.js → _tanstack-start-manifest_v-B2BMiICr.js} +3 -3
- package/app/dist/server/assets/{router-CN98Ramo.js → router-dMcw-pHq.js} +1 -1
- package/app/dist/server/assets/{routes-eZ4XqxE9.js → routes-BmnrEVoN.js} +203 -157
- package/app/dist/server/server.js +2 -2
- package/dist/cli/QueryCommands.d.ts +127 -0
- package/dist/cli/QueryCommands.js +768 -0
- package/dist/cli.d.ts +1 -2
- package/dist/cli.js +150 -8
- package/dist/client/Client.d.ts +42 -1
- package/dist/client/Client.js +88 -4
- package/dist/collector/QueryApi.d.ts +29 -0
- package/dist/collector/QueryApi.js +91 -0
- package/dist/collector/Server.d.ts +1 -1
- package/dist/collector/Server.js +15 -8
- package/dist/collector/Store.d.ts +56 -16
- package/dist/collector/Store.js +37 -11
- package/dist/protocol/Codec.d.ts +10 -0
- package/dist/protocol/Schema.d.ts +118 -1
- package/dist/protocol/Schema.js +53 -1
- package/dist/query/Client.d.ts +30 -0
- package/dist/query/Client.js +74 -0
- package/dist/query/Query.d.ts +599 -0
- package/dist/query/Query.js +876 -0
- package/dist/trace/Timing.d.ts +18 -0
- package/dist/trace/Timing.js +57 -0
- package/dist/trace/TraceFile.d.ts +54 -0
- package/dist/trace/TraceFile.js +86 -0
- package/dist/trace/TraceStore.d.ts +202 -0
- package/dist/trace/TraceStore.js +328 -0
- package/package.json +3 -1
- package/app/dist/client/assets/routes-TKgeFdSW.js +0 -5
|
@@ -1,139 +1,13 @@
|
|
|
1
|
-
import { a as CollapseButton, c as PanelHeader, i as themeAtom, l as usePanel, n as DEFAULT_THEME, o as CollapsedRail, r as resolvedThemeAtom, s as Empty$1, u as Button } from "./router-
|
|
1
|
+
import { a as CollapseButton, c as PanelHeader, i as themeAtom, l as usePanel, n as DEFAULT_THEME, o as CollapsedRail, r as resolvedThemeAtom, s as Empty$1, u as Button } from "./router-dMcw-pHq.js";
|
|
2
2
|
import { useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
3
3
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
import { FilterX, Monitor, Moon, MousePointerClick, PlugZap, Radio, SearchX, Sun, X } from "lucide-react";
|
|
5
5
|
import { RegistryContext, useAtom, useAtomMount, useAtomValue } from "@effect/atom-react";
|
|
6
6
|
import { Atom } from "effect/unstable/reactivity";
|
|
7
7
|
import { Data, Result, Schema } from "effect";
|
|
8
|
-
//#region
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
rows: [],
|
|
12
|
-
rowOf: /* @__PURE__ */ new Map(),
|
|
13
|
-
duration: 0,
|
|
14
|
-
ordered: []
|
|
15
|
-
};
|
|
16
|
-
/** Effective end of a span: open spans run to `now` (the trace's right edge). */
|
|
17
|
-
var spanEnd = (span, now) => span.end ?? now;
|
|
18
|
-
/**
|
|
19
|
-
* Returns a layout for the store's current version, reusing `previous` when
|
|
20
|
-
* nothing has changed.
|
|
21
|
-
*
|
|
22
|
-
* **A span's row is final once assigned.** `previous.rowOf` is carried over
|
|
23
|
-
* verbatim and only spans the previous layout never saw are packed. A late
|
|
24
|
-
* arrival that fits nowhere takes a new row rather than repacking what the
|
|
25
|
-
* user is already looking at — under a live trace, rows never reshuffle.
|
|
26
|
-
*
|
|
27
|
-
* The cost is deliberate and one-directional: a trace watched live can end up
|
|
28
|
-
* taller than the same trace loaded from scratch, because a gap that opens up
|
|
29
|
-
* later is never reclaimed. Visual stability beats vertical compactness.
|
|
30
|
-
*/
|
|
31
|
-
var layout = (store, previous = EMPTY$1) => {
|
|
32
|
-
if (previous.version === store.version) return previous;
|
|
33
|
-
const duration = store.stats().duration;
|
|
34
|
-
const ordered = [];
|
|
35
|
-
for (const ids of store.rows) for (const id of ids) {
|
|
36
|
-
const span = store.spans.get(id);
|
|
37
|
-
if (span !== void 0) ordered.push(span);
|
|
38
|
-
}
|
|
39
|
-
ordered.sort((a, b) => a.start - b.start || a.depth - b.depth);
|
|
40
|
-
const spansByRow = [];
|
|
41
|
-
const freeFrom = [];
|
|
42
|
-
const pinned = [];
|
|
43
|
-
const rowOf = /* @__PURE__ */ new Map();
|
|
44
|
-
const claim = (row, span, isPinned) => {
|
|
45
|
-
while (spansByRow.length <= row) {
|
|
46
|
-
spansByRow.push([]);
|
|
47
|
-
freeFrom.push(Number.NEGATIVE_INFINITY);
|
|
48
|
-
pinned.push([]);
|
|
49
|
-
}
|
|
50
|
-
const to = spanEnd(span, duration);
|
|
51
|
-
spansByRow[row].push(span);
|
|
52
|
-
if (isPinned) pinned[row].push({
|
|
53
|
-
from: span.start,
|
|
54
|
-
to
|
|
55
|
-
});
|
|
56
|
-
else if (to > freeFrom[row]) freeFrom[row] = to;
|
|
57
|
-
rowOf.set(span.spanId, row);
|
|
58
|
-
};
|
|
59
|
-
/** True when a span spanning `[from, to)` can be drawn on `row` untouched. */
|
|
60
|
-
const fits = (row, from, to) => {
|
|
61
|
-
if (row >= spansByRow.length) return true;
|
|
62
|
-
if (freeFrom[row] > from) return false;
|
|
63
|
-
return !pinned[row].some((i) => i.from < to && from < i.to);
|
|
64
|
-
};
|
|
65
|
-
if (ordered.length >= previous.ordered.length) for (const span of ordered) {
|
|
66
|
-
const pinnedRow = previous.rowOf.get(span.spanId);
|
|
67
|
-
if (pinnedRow !== void 0) claim(pinnedRow, span, true);
|
|
68
|
-
}
|
|
69
|
-
const rowFor = (span) => {
|
|
70
|
-
const parentRow = span.parentId === void 0 ? void 0 : rowOf.get(span.parentId);
|
|
71
|
-
const from = span.start;
|
|
72
|
-
const to = spanEnd(span, duration);
|
|
73
|
-
let row = parentRow === void 0 ? 0 : parentRow + 1;
|
|
74
|
-
while (!fits(row, from, to)) row++;
|
|
75
|
-
return row;
|
|
76
|
-
};
|
|
77
|
-
for (const span of ordered) {
|
|
78
|
-
if (rowOf.has(span.spanId)) continue;
|
|
79
|
-
claim(rowFor(span), span, false);
|
|
80
|
-
}
|
|
81
|
-
const rows = spansByRow.map((spans) => {
|
|
82
|
-
spans.sort((a, b) => a.start - b.start);
|
|
83
|
-
const maxEnd = new Float64Array(spans.length);
|
|
84
|
-
let running = Number.NEGATIVE_INFINITY;
|
|
85
|
-
for (let i = 0; i < spans.length; i++) {
|
|
86
|
-
const end = spanEnd(spans[i], duration);
|
|
87
|
-
if (end > running) running = end;
|
|
88
|
-
maxEnd[i] = running;
|
|
89
|
-
}
|
|
90
|
-
return {
|
|
91
|
-
spans,
|
|
92
|
-
maxEnd
|
|
93
|
-
};
|
|
94
|
-
});
|
|
95
|
-
return {
|
|
96
|
-
version: store.version,
|
|
97
|
-
rows,
|
|
98
|
-
rowOf,
|
|
99
|
-
duration,
|
|
100
|
-
ordered
|
|
101
|
-
};
|
|
102
|
-
};
|
|
103
|
-
var emptyLayout = () => EMPTY$1;
|
|
104
|
-
/**
|
|
105
|
-
* Index of the first span in `row` that can intersect `[from, to]`.
|
|
106
|
-
*
|
|
107
|
-
* Binary searches the prefix-maximum of end times: everything before the
|
|
108
|
-
* result ends strictly before `from`, so it is safely skipped. Returns
|
|
109
|
-
* `row.spans.length` when nothing intersects.
|
|
110
|
-
*/
|
|
111
|
-
var firstVisible = (row, from) => {
|
|
112
|
-
let lo = 0;
|
|
113
|
-
let hi = row.spans.length;
|
|
114
|
-
while (lo < hi) {
|
|
115
|
-
const mid = lo + hi >>> 1;
|
|
116
|
-
if (row.maxEnd[mid] < from) lo = mid + 1;
|
|
117
|
-
else hi = mid;
|
|
118
|
-
}
|
|
119
|
-
return lo;
|
|
120
|
-
};
|
|
121
|
-
/**
|
|
122
|
-
* Walks the spans of `row` intersecting `[from, to]`, in start order.
|
|
123
|
-
*
|
|
124
|
-
* Stops as soon as a span starts after `to` — the row is start-sorted, so
|
|
125
|
-
* everything after it starts later still.
|
|
126
|
-
*/
|
|
127
|
-
var forEachVisible = (row, from, to, now, visit) => {
|
|
128
|
-
for (let i = firstVisible(row, from); i < row.spans.length; i++) {
|
|
129
|
-
const span = row.spans[i];
|
|
130
|
-
if (span.start > to) return;
|
|
131
|
-
if (spanEnd(span, now) >= from) visit(span);
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
//#endregion
|
|
135
|
-
//#region app/src/chart/metrics.ts
|
|
136
|
-
/** Span timing derived the same way for the chart, the tooltip, the log and the drawer. */
|
|
8
|
+
//#region src/trace/Timing.ts
|
|
9
|
+
/** A span's end, or `now` while it is still open. */
|
|
10
|
+
var spanEnd$1 = (span, now) => span.end ?? now;
|
|
137
11
|
/**
|
|
138
12
|
* Time covered by the union of `span`'s direct children, clipped to `[from, to]`.
|
|
139
13
|
*
|
|
@@ -151,7 +25,7 @@ var childUnion = (store, span, now, from, to) => {
|
|
|
151
25
|
const child = store.spans.get(id);
|
|
152
26
|
if (child === void 0) continue;
|
|
153
27
|
const lo = Math.max(child.start, from);
|
|
154
|
-
const hi = Math.min(spanEnd(child, now), to);
|
|
28
|
+
const hi = Math.min(spanEnd$1(child, now), to);
|
|
155
29
|
if (hi > lo) intervals.push([lo, hi]);
|
|
156
30
|
}
|
|
157
31
|
if (intervals.length === 0) return 0;
|
|
@@ -179,7 +53,7 @@ var childUnion = (store, span, now, from, to) => {
|
|
|
179
53
|
*/
|
|
180
54
|
var timings = (store, span, now, from = -Infinity, to = Infinity) => {
|
|
181
55
|
const lo = Math.max(span.start, from);
|
|
182
|
-
const hi = Math.min(spanEnd(span, now), to);
|
|
56
|
+
const hi = Math.min(spanEnd$1(span, now), to);
|
|
183
57
|
if (hi <= lo) return {
|
|
184
58
|
total: 0,
|
|
185
59
|
self: 0
|
|
@@ -244,7 +118,7 @@ var memoryCollapsedAtom = Atom.make(false);
|
|
|
244
118
|
* Rebuilt when the viewport, the filter or `store.version` changes — never per
|
|
245
119
|
* frame. The drawer is not in the chart's draw path at all.
|
|
246
120
|
*/
|
|
247
|
-
var EMPTY = {
|
|
121
|
+
var EMPTY$1 = {
|
|
248
122
|
summary: [],
|
|
249
123
|
callTree: [],
|
|
250
124
|
bottomUp: [],
|
|
@@ -310,7 +184,7 @@ var aggregate = (store, from, to, filter, hides) => {
|
|
|
310
184
|
self
|
|
311
185
|
});
|
|
312
186
|
}
|
|
313
|
-
if (visible.length === 0) return EMPTY;
|
|
187
|
+
if (visible.length === 0) return EMPTY$1;
|
|
314
188
|
visible.sort((a, b) => a.span.start - b.span.start);
|
|
315
189
|
const byName = /* @__PURE__ */ new Map();
|
|
316
190
|
for (const timing of visible) add(descend(byName, "", timing.span.name, timing.span.spanId), timing, timing.self);
|
|
@@ -386,7 +260,12 @@ var Json = Schema.Union([
|
|
|
386
260
|
]);
|
|
387
261
|
/** Span attributes, log annotations, and metric/event attributes. */
|
|
388
262
|
var Attributes = Schema.Record(Schema.String, Json);
|
|
389
|
-
/**
|
|
263
|
+
/**
|
|
264
|
+
* Identifies one run of an instrumented program.
|
|
265
|
+
*
|
|
266
|
+
* Any string on the wire, so traces from older clients keep decoding. IDs a
|
|
267
|
+
* caller chooses are held to {@link isValidSessionId} by the client instead.
|
|
268
|
+
*/
|
|
390
269
|
var SessionId = Schema.String;
|
|
391
270
|
var sessionId = { sessionId: SessionId };
|
|
392
271
|
/** Mirrors `Tracer.SpanKind`. */
|
|
@@ -427,7 +306,14 @@ var Hello = Schema.Struct({
|
|
|
427
306
|
pid: Schema.Natural,
|
|
428
307
|
runtime: Schema.String,
|
|
429
308
|
protocolVersion: Schema.Natural,
|
|
430
|
-
clock: Clock
|
|
309
|
+
clock: Clock,
|
|
310
|
+
/**
|
|
311
|
+
* Random per client instance, fixed for its lifetime and re-sent on every
|
|
312
|
+
* reconnect. Tells a genuine reconnect (same instance) from an independent
|
|
313
|
+
* run that chose the same `sessionId`. Absent from older clients, which are
|
|
314
|
+
* treated as one instance per `sessionId`.
|
|
315
|
+
*/
|
|
316
|
+
instanceId: Schema.optional(Schema.String)
|
|
431
317
|
});
|
|
432
318
|
/**
|
|
433
319
|
* A span parent that lives outside this session's span tree — propagated from
|
|
@@ -624,7 +510,13 @@ var Session = Schema.Struct({
|
|
|
624
510
|
runtime: Schema.String,
|
|
625
511
|
clock: Clock,
|
|
626
512
|
active: Schema.Boolean,
|
|
627
|
-
endedAtEpochMillis: Schema.optional(Schema.Natural)
|
|
513
|
+
endedAtEpochMillis: Schema.optional(Schema.Natural),
|
|
514
|
+
/**
|
|
515
|
+
* Connections refused because a different client instance announced this
|
|
516
|
+
* `sessionId` — an ID collision. Their telemetry was discarded, so this
|
|
517
|
+
* session holds only the first instance's run. Absent when there were none.
|
|
518
|
+
*/
|
|
519
|
+
conflicts: Schema.optional(Schema.Natural)
|
|
628
520
|
});
|
|
629
521
|
/** Every session the collector holds. Sent on connect and on change. */
|
|
630
522
|
var SessionList = Schema.Struct({
|
|
@@ -684,6 +576,22 @@ var Unsubscribe = Schema.Struct({
|
|
|
684
576
|
/** What the webapp sends to the collector. */
|
|
685
577
|
var WebappRequest = Schema.Union([Subscribe, Unsubscribe]);
|
|
686
578
|
/**
|
|
579
|
+
* Collector-side loss counters for one session at snapshot time, carried in
|
|
580
|
+
* a trace file exported from the collector.
|
|
581
|
+
*/
|
|
582
|
+
var TraceCapture = Schema.Struct({
|
|
583
|
+
/** Messages evicted by the collector's per-session capacity bound. */
|
|
584
|
+
droppedMessages: Schema.Natural,
|
|
585
|
+
/** Lines received for the session that could not be decoded. */
|
|
586
|
+
skippedLines: Schema.Natural,
|
|
587
|
+
/**
|
|
588
|
+
* Whether the collector could refuse an independent run reusing this ID.
|
|
589
|
+
* `false` when the owning client predates instance IDs, so a reused ID
|
|
590
|
+
* would have merged silently and `session.conflicts` proves nothing.
|
|
591
|
+
*/
|
|
592
|
+
conflictDetection: Schema.Boolean
|
|
593
|
+
});
|
|
594
|
+
/**
|
|
687
595
|
* Line 1 of a saved trace file.
|
|
688
596
|
*
|
|
689
597
|
* The rest of the file is {@link ClientMessage} NDJSON, byte-identical to what
|
|
@@ -702,7 +610,14 @@ var TraceFileHeader = Schema.Struct({
|
|
|
702
610
|
/** {@link protocolVersion} at save time. Recorded for diagnosis, not enforced. */
|
|
703
611
|
protocolVersion: Schema.Natural,
|
|
704
612
|
session: Session,
|
|
705
|
-
savedAtEpochMillis: Schema.Natural
|
|
613
|
+
savedAtEpochMillis: Schema.Natural,
|
|
614
|
+
/**
|
|
615
|
+
* What the collector knew about loss when the file was exported from it.
|
|
616
|
+
* Absent from browser saves and older files: their capture completeness is
|
|
617
|
+
* unknown, not complete. Optional and additive, so older builds, which
|
|
618
|
+
* ignore unknown header keys, still read these files.
|
|
619
|
+
*/
|
|
620
|
+
capture: Schema.optional(TraceCapture)
|
|
706
621
|
});
|
|
707
622
|
//#endregion
|
|
708
623
|
//#region src/protocol/Codec.ts
|
|
@@ -754,7 +669,7 @@ var webappRequestCodec = make(WebappRequest);
|
|
|
754
669
|
/** Line 1 of a saved trace file; the rest of the file is {@link clientCodec} lines. */
|
|
755
670
|
var traceFileHeaderCodec = make(TraceFileHeader);
|
|
756
671
|
//#endregion
|
|
757
|
-
//#region
|
|
672
|
+
//#region src/trace/TraceFile.ts
|
|
758
673
|
/**
|
|
759
674
|
* Saving a trace to a file and loading it back.
|
|
760
675
|
*
|
|
@@ -776,38 +691,26 @@ var fail = (message) => Result.fail({
|
|
|
776
691
|
message
|
|
777
692
|
});
|
|
778
693
|
/**
|
|
779
|
-
* Prefix on a loaded session's id.
|
|
780
|
-
*
|
|
781
|
-
* Without it, loading a trace exported from the collector you are currently
|
|
782
|
-
* connected to would collide with the live session of the same id and the two
|
|
783
|
-
* would fight over the selection.
|
|
784
|
-
*/
|
|
785
|
-
var loadedSessionPrefix = "loaded:";
|
|
786
|
-
/** True for a session id produced by {@link parseTraceFile}. */
|
|
787
|
-
var isLoadedSession = (sessionId) => sessionId.startsWith(loadedSessionPrefix);
|
|
788
|
-
/**
|
|
789
694
|
* Serializes a session and its messages to trace-file text.
|
|
790
695
|
*
|
|
791
696
|
* `messages` is the raw protocol stream in arrival order — not a re-derivation
|
|
792
697
|
* from the rendered trace model, which would silently drop every message the
|
|
793
|
-
* model does not draw.
|
|
698
|
+
* model does not draw. `capture` is written only when the caller knows the
|
|
699
|
+
* collector's loss counters; omitting it marks completeness as unknown.
|
|
794
700
|
*/
|
|
795
|
-
var serializeTraceFile = (session, messages, savedAtEpochMillis) => {
|
|
701
|
+
var serializeTraceFile = (session, messages, savedAtEpochMillis, capture) => {
|
|
796
702
|
const header = traceFileHeaderCodec.encode({
|
|
797
703
|
_tag: "TraceFileHeader",
|
|
798
704
|
formatVersion: 1,
|
|
799
705
|
protocolVersion: 1,
|
|
800
706
|
session,
|
|
801
|
-
savedAtEpochMillis
|
|
707
|
+
savedAtEpochMillis,
|
|
708
|
+
...capture === void 0 ? {} : { capture }
|
|
802
709
|
});
|
|
803
710
|
const body = [];
|
|
804
711
|
for (const message of messages) body.push(clientCodec.encode(message));
|
|
805
712
|
return header + body.join("");
|
|
806
713
|
};
|
|
807
|
-
/** A filename safe on every platform, carrying the program and save time. */
|
|
808
|
-
var traceFileName = (session, savedAtEpochMillis) => {
|
|
809
|
-
return `${(session.program.split(/[/\\]/).pop() ?? "trace").replace(/[^\w.-]+/g, "-").replace(/^-+|-+$/g, "") || "trace"}-${new Date(savedAtEpochMillis).toISOString().replace(/[:.]/g, "-").slice(0, 19)}${traceFileExtension}`;
|
|
810
|
-
};
|
|
811
714
|
/**
|
|
812
715
|
* Parses trace-file text.
|
|
813
716
|
*
|
|
@@ -844,6 +747,22 @@ var parseTraceFile = (text) => {
|
|
|
844
747
|
truncatedLines
|
|
845
748
|
});
|
|
846
749
|
};
|
|
750
|
+
//#endregion
|
|
751
|
+
//#region app/src/trace/TraceFile.ts
|
|
752
|
+
/**
|
|
753
|
+
* Prefix on a loaded session's id.
|
|
754
|
+
*
|
|
755
|
+
* Without it, loading a trace exported from the collector you are currently
|
|
756
|
+
* connected to would collide with the live session of the same id and the two
|
|
757
|
+
* would fight over the selection.
|
|
758
|
+
*/
|
|
759
|
+
var loadedSessionPrefix = "loaded:";
|
|
760
|
+
/** True for a session id produced by {@link parseTraceFile}. */
|
|
761
|
+
var isLoadedSession = (sessionId) => sessionId.startsWith(loadedSessionPrefix);
|
|
762
|
+
/** A filename safe on every platform, carrying the program and save time. */
|
|
763
|
+
var traceFileName = (session, savedAtEpochMillis) => {
|
|
764
|
+
return `${(session.program.split(/[/\\]/).pop() ?? "trace").replace(/[^\w.-]+/g, "-").replace(/^-+|-+$/g, "") || "trace"}-${new Date(savedAtEpochMillis).toISOString().replace(/[:.]/g, "-").slice(0, 19)}${traceFileExtension}`;
|
|
765
|
+
};
|
|
847
766
|
/** The `Session` a loaded trace lists as: never active, id namespaced so it cannot collide. */
|
|
848
767
|
var loadedSession = (header) => ({
|
|
849
768
|
...header.session,
|
|
@@ -852,7 +771,7 @@ var loadedSession = (header) => ({
|
|
|
852
771
|
endedAtEpochMillis: header.session.endedAtEpochMillis ?? header.savedAtEpochMillis
|
|
853
772
|
});
|
|
854
773
|
//#endregion
|
|
855
|
-
//#region
|
|
774
|
+
//#region src/trace/TraceStore.ts
|
|
856
775
|
var NANOS_PER_MILLI = 1000000n;
|
|
857
776
|
/**
|
|
858
777
|
* Converts protocol nanos to milliseconds relative to `origin`, as a `number`.
|
|
@@ -1412,6 +1331,133 @@ var saveableMessages = (registry, sessionId) => {
|
|
|
1412
1331
|
return registry.get(loadedSessionsAtom).find((entry) => entry.session.sessionId === sessionId)?.messages ?? traceStore.raw;
|
|
1413
1332
|
};
|
|
1414
1333
|
//#endregion
|
|
1334
|
+
//#region app/src/chart/Layout.ts
|
|
1335
|
+
var EMPTY = {
|
|
1336
|
+
version: -1,
|
|
1337
|
+
rows: [],
|
|
1338
|
+
rowOf: /* @__PURE__ */ new Map(),
|
|
1339
|
+
duration: 0,
|
|
1340
|
+
ordered: []
|
|
1341
|
+
};
|
|
1342
|
+
/** Effective end of a span: open spans run to `now` (the trace's right edge). */
|
|
1343
|
+
var spanEnd = (span, now) => span.end ?? now;
|
|
1344
|
+
/**
|
|
1345
|
+
* Returns a layout for the store's current version, reusing `previous` when
|
|
1346
|
+
* nothing has changed.
|
|
1347
|
+
*
|
|
1348
|
+
* **A span's row is final once assigned.** `previous.rowOf` is carried over
|
|
1349
|
+
* verbatim and only spans the previous layout never saw are packed. A late
|
|
1350
|
+
* arrival that fits nowhere takes a new row rather than repacking what the
|
|
1351
|
+
* user is already looking at — under a live trace, rows never reshuffle.
|
|
1352
|
+
*
|
|
1353
|
+
* The cost is deliberate and one-directional: a trace watched live can end up
|
|
1354
|
+
* taller than the same trace loaded from scratch, because a gap that opens up
|
|
1355
|
+
* later is never reclaimed. Visual stability beats vertical compactness.
|
|
1356
|
+
*/
|
|
1357
|
+
var layout = (store, previous = EMPTY) => {
|
|
1358
|
+
if (previous.version === store.version) return previous;
|
|
1359
|
+
const duration = store.stats().duration;
|
|
1360
|
+
const ordered = [];
|
|
1361
|
+
for (const ids of store.rows) for (const id of ids) {
|
|
1362
|
+
const span = store.spans.get(id);
|
|
1363
|
+
if (span !== void 0) ordered.push(span);
|
|
1364
|
+
}
|
|
1365
|
+
ordered.sort((a, b) => a.start - b.start || a.depth - b.depth);
|
|
1366
|
+
const spansByRow = [];
|
|
1367
|
+
const freeFrom = [];
|
|
1368
|
+
const pinned = [];
|
|
1369
|
+
const rowOf = /* @__PURE__ */ new Map();
|
|
1370
|
+
const claim = (row, span, isPinned) => {
|
|
1371
|
+
while (spansByRow.length <= row) {
|
|
1372
|
+
spansByRow.push([]);
|
|
1373
|
+
freeFrom.push(Number.NEGATIVE_INFINITY);
|
|
1374
|
+
pinned.push([]);
|
|
1375
|
+
}
|
|
1376
|
+
const to = spanEnd(span, duration);
|
|
1377
|
+
spansByRow[row].push(span);
|
|
1378
|
+
if (isPinned) pinned[row].push({
|
|
1379
|
+
from: span.start,
|
|
1380
|
+
to
|
|
1381
|
+
});
|
|
1382
|
+
else if (to > freeFrom[row]) freeFrom[row] = to;
|
|
1383
|
+
rowOf.set(span.spanId, row);
|
|
1384
|
+
};
|
|
1385
|
+
/** True when a span spanning `[from, to)` can be drawn on `row` untouched. */
|
|
1386
|
+
const fits = (row, from, to) => {
|
|
1387
|
+
if (row >= spansByRow.length) return true;
|
|
1388
|
+
if (freeFrom[row] > from) return false;
|
|
1389
|
+
return !pinned[row].some((i) => i.from < to && from < i.to);
|
|
1390
|
+
};
|
|
1391
|
+
if (ordered.length >= previous.ordered.length) for (const span of ordered) {
|
|
1392
|
+
const pinnedRow = previous.rowOf.get(span.spanId);
|
|
1393
|
+
if (pinnedRow !== void 0) claim(pinnedRow, span, true);
|
|
1394
|
+
}
|
|
1395
|
+
const rowFor = (span) => {
|
|
1396
|
+
const parentRow = span.parentId === void 0 ? void 0 : rowOf.get(span.parentId);
|
|
1397
|
+
const from = span.start;
|
|
1398
|
+
const to = spanEnd(span, duration);
|
|
1399
|
+
let row = parentRow === void 0 ? 0 : parentRow + 1;
|
|
1400
|
+
while (!fits(row, from, to)) row++;
|
|
1401
|
+
return row;
|
|
1402
|
+
};
|
|
1403
|
+
for (const span of ordered) {
|
|
1404
|
+
if (rowOf.has(span.spanId)) continue;
|
|
1405
|
+
claim(rowFor(span), span, false);
|
|
1406
|
+
}
|
|
1407
|
+
const rows = spansByRow.map((spans) => {
|
|
1408
|
+
spans.sort((a, b) => a.start - b.start);
|
|
1409
|
+
const maxEnd = new Float64Array(spans.length);
|
|
1410
|
+
let running = Number.NEGATIVE_INFINITY;
|
|
1411
|
+
for (let i = 0; i < spans.length; i++) {
|
|
1412
|
+
const end = spanEnd(spans[i], duration);
|
|
1413
|
+
if (end > running) running = end;
|
|
1414
|
+
maxEnd[i] = running;
|
|
1415
|
+
}
|
|
1416
|
+
return {
|
|
1417
|
+
spans,
|
|
1418
|
+
maxEnd
|
|
1419
|
+
};
|
|
1420
|
+
});
|
|
1421
|
+
return {
|
|
1422
|
+
version: store.version,
|
|
1423
|
+
rows,
|
|
1424
|
+
rowOf,
|
|
1425
|
+
duration,
|
|
1426
|
+
ordered
|
|
1427
|
+
};
|
|
1428
|
+
};
|
|
1429
|
+
var emptyLayout = () => EMPTY;
|
|
1430
|
+
/**
|
|
1431
|
+
* Index of the first span in `row` that can intersect `[from, to]`.
|
|
1432
|
+
*
|
|
1433
|
+
* Binary searches the prefix-maximum of end times: everything before the
|
|
1434
|
+
* result ends strictly before `from`, so it is safely skipped. Returns
|
|
1435
|
+
* `row.spans.length` when nothing intersects.
|
|
1436
|
+
*/
|
|
1437
|
+
var firstVisible = (row, from) => {
|
|
1438
|
+
let lo = 0;
|
|
1439
|
+
let hi = row.spans.length;
|
|
1440
|
+
while (lo < hi) {
|
|
1441
|
+
const mid = lo + hi >>> 1;
|
|
1442
|
+
if (row.maxEnd[mid] < from) lo = mid + 1;
|
|
1443
|
+
else hi = mid;
|
|
1444
|
+
}
|
|
1445
|
+
return lo;
|
|
1446
|
+
};
|
|
1447
|
+
/**
|
|
1448
|
+
* Walks the spans of `row` intersecting `[from, to]`, in start order.
|
|
1449
|
+
*
|
|
1450
|
+
* Stops as soon as a span starts after `to` — the row is start-sorted, so
|
|
1451
|
+
* everything after it starts later still.
|
|
1452
|
+
*/
|
|
1453
|
+
var forEachVisible = (row, from, to, now, visit) => {
|
|
1454
|
+
for (let i = firstVisible(row, from); i < row.spans.length; i++) {
|
|
1455
|
+
const span = row.spans[i];
|
|
1456
|
+
if (span.start > to) return;
|
|
1457
|
+
if (spanEnd(span, now) >= from) visit(span);
|
|
1458
|
+
}
|
|
1459
|
+
};
|
|
1460
|
+
//#endregion
|
|
1415
1461
|
//#region app/src/chart/palette.ts
|
|
1416
1462
|
/**
|
|
1417
1463
|
* Resolves one custom property against `<html>`.
|
|
@@ -91,7 +91,7 @@ var HEADERS = { TSS_SHELL: "X-TSS_SHELL" };
|
|
|
91
91
|
* the dev styles URL for route-scoped CSS collection.
|
|
92
92
|
*/
|
|
93
93
|
async function getStartManifest(matchedRoutes) {
|
|
94
|
-
const { tsrStartManifest } = await import("./assets/_tanstack-start-manifest_v-
|
|
94
|
+
const { tsrStartManifest } = await import("./assets/_tanstack-start-manifest_v-B2BMiICr.js");
|
|
95
95
|
const startManifest = tsrStartManifest();
|
|
96
96
|
let routes = startManifest.routes;
|
|
97
97
|
routes[rootRouteId];
|
|
@@ -1356,7 +1356,7 @@ var getBaseManifest = getProdBaseManifest;
|
|
|
1356
1356
|
var createEarlyHintsForRequest = createEarlyHintsCollector;
|
|
1357
1357
|
async function loadEntries() {
|
|
1358
1358
|
const [routerEntry, startEntry, pluginAdapters] = await Promise.all([
|
|
1359
|
-
import("./assets/router-
|
|
1359
|
+
import("./assets/router-dMcw-pHq.js").then((n) => n.t),
|
|
1360
1360
|
import("./assets/start-5Z2QO8AU.js"),
|
|
1361
1361
|
import("./assets/empty-plugin-adapters-D9UWiqvJ.js")
|
|
1362
1362
|
]);
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The query commands of the `effect-inspect` CLI: `sessions`, `summary`,
|
|
3
|
+
* `spans`, `span`, `logs` and `export`.
|
|
4
|
+
*
|
|
5
|
+
* Every command prints exactly one JSON document on stdout (pretty, or compact
|
|
6
|
+
* with `--json`) and, on failure, one diagnostic on stderr; the exit code
|
|
7
|
+
* names the failure (see {@link exitCodes}). Live queries go through
|
|
8
|
+
* `query/Client.ts`, file queries through `Query.queryFile`, so both answer
|
|
9
|
+
* with the one contract of `query/Query.ts`.
|
|
10
|
+
*
|
|
11
|
+
* The help text here is the primary manual for agents driving the CLI, so it
|
|
12
|
+
* spells out the whole local contract: sources, flags, defaults, JSON shapes,
|
|
13
|
+
* errors and exit codes.
|
|
14
|
+
*/
|
|
15
|
+
import { Effect, Option } from 'effect';
|
|
16
|
+
import { FileSystem } from 'effect/FileSystem';
|
|
17
|
+
import * as Stdio from 'effect/Stdio';
|
|
18
|
+
import { Command } from 'effect/unstable/cli';
|
|
19
|
+
import * as Query from '../query/Query.ts';
|
|
20
|
+
/** Process exit code per outcome. `1` is reserved for unexpected internal errors. */
|
|
21
|
+
export declare const exitCodes: Readonly<Record<Query.ErrorTag, number>>;
|
|
22
|
+
declare const CliExit_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
23
|
+
readonly _tag: "CliExit";
|
|
24
|
+
} & Readonly<A>;
|
|
25
|
+
/** A handled failure: its JSON is already printed, only the exit code is left. */
|
|
26
|
+
declare class CliExit extends CliExit_base<{
|
|
27
|
+
readonly code: number;
|
|
28
|
+
}> {
|
|
29
|
+
}
|
|
30
|
+
/** `export`'s success document. */
|
|
31
|
+
export interface ExportResponse {
|
|
32
|
+
readonly ok: true;
|
|
33
|
+
readonly apiVersion: typeof Query.apiVersion;
|
|
34
|
+
readonly op: 'export';
|
|
35
|
+
readonly query: {
|
|
36
|
+
readonly op: 'export';
|
|
37
|
+
readonly sessionId: string;
|
|
38
|
+
readonly out: string;
|
|
39
|
+
readonly force: boolean;
|
|
40
|
+
};
|
|
41
|
+
readonly result: {
|
|
42
|
+
readonly sessionId: string;
|
|
43
|
+
readonly file: string;
|
|
44
|
+
readonly bytes: number;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
type Response = Query.QueryResponse | ExportResponse;
|
|
48
|
+
/**
|
|
49
|
+
* The exact stdout text for `response`, held to `Query.limits.responseBytes`
|
|
50
|
+
* as emitted: after formatting, trailing newline included. The query layer
|
|
51
|
+
* bounds compact JSON only, so indentation (or the newline) can still push a
|
|
52
|
+
* page over; that becomes a small fixed-shape `ResponseTooLarge` instead.
|
|
53
|
+
*/
|
|
54
|
+
export declare const renderBounded: (response: Response, json: boolean) => {
|
|
55
|
+
readonly response: Response;
|
|
56
|
+
readonly text: string;
|
|
57
|
+
};
|
|
58
|
+
/** The query subcommands, in investigation order. */
|
|
59
|
+
export declare const queryCommands: readonly [Command.Command<"summary", {
|
|
60
|
+
readonly session: Option.Option<string>;
|
|
61
|
+
readonly file: Option.Option<string>;
|
|
62
|
+
readonly url: Option.Option<string>;
|
|
63
|
+
readonly timeoutMs: Option.Option<number>;
|
|
64
|
+
readonly top: Option.Option<number>;
|
|
65
|
+
readonly json: boolean;
|
|
66
|
+
}, {}, CliExit, FileSystem | Stdio.Stdio>, Command.Command<"spans", {
|
|
67
|
+
readonly limit: Option.Option<number>;
|
|
68
|
+
readonly offset: Option.Option<number>;
|
|
69
|
+
readonly fromMs: Option.Option<number>;
|
|
70
|
+
readonly toMs: Option.Option<number>;
|
|
71
|
+
readonly session: Option.Option<string>;
|
|
72
|
+
readonly file: Option.Option<string>;
|
|
73
|
+
readonly url: Option.Option<string>;
|
|
74
|
+
readonly timeoutMs: Option.Option<number>;
|
|
75
|
+
readonly status: Option.Option<"any" | "defect" | "error" | "failed" | "interrupted" | "ok" | "open">;
|
|
76
|
+
readonly name: Option.Option<string>;
|
|
77
|
+
readonly minDurationMs: Option.Option<number>;
|
|
78
|
+
readonly sort: Option.Option<"duration" | "outsideChildren" | "start">;
|
|
79
|
+
readonly json: boolean;
|
|
80
|
+
}, {}, CliExit, FileSystem | Stdio.Stdio>, Command.Command<"span", {
|
|
81
|
+
readonly session: Option.Option<string>;
|
|
82
|
+
readonly file: Option.Option<string>;
|
|
83
|
+
readonly url: Option.Option<string>;
|
|
84
|
+
readonly timeoutMs: Option.Option<number>;
|
|
85
|
+
readonly span: string;
|
|
86
|
+
readonly children: Option.Option<number>;
|
|
87
|
+
readonly events: Option.Option<number>;
|
|
88
|
+
readonly json: boolean;
|
|
89
|
+
}, {}, CliExit, FileSystem | Stdio.Stdio>, Command.Command<"logs", {
|
|
90
|
+
readonly limit: Option.Option<number>;
|
|
91
|
+
readonly offset: Option.Option<number>;
|
|
92
|
+
readonly fromMs: Option.Option<number>;
|
|
93
|
+
readonly toMs: Option.Option<number>;
|
|
94
|
+
readonly session: Option.Option<string>;
|
|
95
|
+
readonly file: Option.Option<string>;
|
|
96
|
+
readonly url: Option.Option<string>;
|
|
97
|
+
readonly timeoutMs: Option.Option<number>;
|
|
98
|
+
readonly span: Option.Option<string>;
|
|
99
|
+
readonly scope: Option.Option<"span" | "subtree">;
|
|
100
|
+
readonly minLevel: Option.Option<"Debug" | "Error" | "Fatal" | "Info" | "Trace" | "Warn">;
|
|
101
|
+
readonly json: boolean;
|
|
102
|
+
}, {}, CliExit, FileSystem | Stdio.Stdio>, Command.Command<"export", {
|
|
103
|
+
readonly session: Option.Option<string>;
|
|
104
|
+
readonly out: Option.Option<string>;
|
|
105
|
+
readonly force: boolean;
|
|
106
|
+
readonly url: Option.Option<string>;
|
|
107
|
+
readonly timeoutMs: Option.Option<number>;
|
|
108
|
+
readonly json: boolean;
|
|
109
|
+
}, {}, CliExit, FileSystem | Stdio.Stdio>, Command.Command<"sessions", {
|
|
110
|
+
readonly limit: Option.Option<number>;
|
|
111
|
+
readonly offset: Option.Option<number>;
|
|
112
|
+
readonly file: Option.Option<string>;
|
|
113
|
+
readonly url: Option.Option<string>;
|
|
114
|
+
readonly timeoutMs: Option.Option<number>;
|
|
115
|
+
readonly json: boolean;
|
|
116
|
+
}, {}, CliExit, FileSystem | Stdio.Stdio>];
|
|
117
|
+
/**
|
|
118
|
+
* Runs `cli` on `args` and resolves to the process exit code.
|
|
119
|
+
*
|
|
120
|
+
* For a query command (without `--help`/`--version`) the parser's own help dump
|
|
121
|
+
* on a usage error is suppressed: its errors become one `InvalidRequest` JSON
|
|
122
|
+
* on stdout, a stderr diagnostic and exit 2, so stdout stays machine-readable.
|
|
123
|
+
* Other invocations (`start`, the root, help) render exactly as the framework
|
|
124
|
+
* does.
|
|
125
|
+
*/
|
|
126
|
+
export declare const runCli: <Name extends string, Input, E, R, ContextInput>(cli: Command.Command<Name, Input, ContextInput, E, R>, version: string, args: ReadonlyArray<string>) => Effect.Effect<number, Exclude<E, import("effect/Terminal").QuitError>, Exclude<R, never> | Command.Environment>;
|
|
127
|
+
export {};
|