autotel-devtools 22.0.0 → 23.1.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.
Files changed (46) hide show
  1. package/README.md +183 -28
  2. package/dist/cli.cjs +80 -13
  3. package/dist/cli.js +80 -13
  4. package/dist/compile-JDFHUCo5.d.cts +177 -0
  5. package/dist/compile-JDFHUCo5.d.ts +177 -0
  6. package/dist/{error-aggregator-D8VKciLY.d.ts → error-aggregator-B52JI8jL.d.ts} +1 -1
  7. package/dist/{error-aggregator-DCEKMOm3.d.cts → error-aggregator-DSwUvgFF.d.cts} +1 -1
  8. package/dist/exporter-C88W22vY.d.ts +576 -0
  9. package/dist/exporter-OoWkSMCR.d.cts +576 -0
  10. package/dist/fullpage.global.js +39 -0
  11. package/dist/grpc-D0B3P9sI.cjs +82 -0
  12. package/dist/grpc-DY-C1jSU.js +77 -0
  13. package/dist/http-1afd_01N.cjs +3991 -0
  14. package/dist/http-gk1xapnA.js +3825 -0
  15. package/dist/index.cjs +25 -7
  16. package/dist/index.d.cts +30 -3
  17. package/dist/index.d.ts +30 -3
  18. package/dist/index.js +25 -7
  19. package/dist/{listen-DBfsfcdd.js → listen-D-lLgfro.js} +5 -2
  20. package/dist/{listen-CEJ3nYJf.cjs → listen-l09RRHht.cjs} +5 -2
  21. package/dist/parse-BRlosZft.cjs +642 -0
  22. package/dist/parse-D_RmPPQs.js +612 -0
  23. package/dist/query/index.cjs +8 -0
  24. package/dist/query/index.d.cts +16 -0
  25. package/dist/query/index.d.ts +16 -0
  26. package/dist/query/index.js +3 -0
  27. package/dist/server/exporter.d.cts +1 -1
  28. package/dist/server/exporter.d.ts +1 -1
  29. package/dist/server/index.cjs +6 -2
  30. package/dist/server/index.d.cts +19 -6
  31. package/dist/server/index.d.ts +18 -5
  32. package/dist/server/index.js +3 -2
  33. package/dist/types-B0tjwFqj.d.cts +107 -0
  34. package/dist/types-DM8y4A9Z.d.ts +107 -0
  35. package/dist/widget.global.js +15 -24
  36. package/dist/wire/index.cjs +7 -0
  37. package/dist/wire/index.d.cts +26 -0
  38. package/dist/wire/index.d.ts +26 -0
  39. package/dist/wire/index.js +3 -0
  40. package/dist/wire-2Rmfg6IT.js +51 -0
  41. package/dist/wire-CHU1PkMo.cjs +75 -0
  42. package/package.json +21 -5
  43. package/dist/exporter-Dt4kx128.d.cts +0 -207
  44. package/dist/exporter-Due9Rd4s.d.ts +0 -207
  45. package/dist/http-CNZMrnzv.js +0 -1453
  46. package/dist/http-CXSzX4ee.cjs +0 -1607
package/dist/cli.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { a as probePortHolder, f as DevtoolsServer, m as hostHeaderIsLoopback, r as resolveSourceRoot, t as attachDevtoolsRoutes } from "./http-CNZMrnzv.js";
3
- import { t as listenLoopbackDualStack } from "./listen-DBfsfcdd.js";
2
+ import { a as probePortHolder, g as hostHeaderIsLoopback, l as DevtoolsServer, r as resolveSourceRoot, t as attachDevtoolsRoutes } from "./http-gk1xapnA.js";
3
+ import { t as listenLoopbackDualStack } from "./listen-D-lLgfro.js";
4
+ import { t as startOtlpGrpcReceiver } from "./grpc-DY-C1jSU.js";
4
5
  import { createServer } from "node:http";
5
6
  import { readFileSync } from "node:fs";
6
7
  import { dirname, resolve } from "node:path";
@@ -9,7 +10,7 @@ import { spawn } from "node:child_process";
9
10
 
10
11
  //#region src/cli.ts
11
12
  function printHelp() {
12
- process.stdout.write("autotel-devtools - Standalone OTLP receiver with web devtools UI\n\nUsage: autotel-devtools [port] [options]\n autotel-devtools claude [claude args] [--print-env] [--log-prompts]\n\nSubcommands:\n claude Start the receiver AND launch Claude Code wired to it\n (HTTP/protobuf → this receiver, 1s intervals, session id on).\n Open the \"Agents\" tab to watch sessions, tokens, cost,\n tool / MCP / sub-agent / skill usage live.\n --print-env Print the telemetry env block and exit (don't launch)\n --log-prompts Capture prompt text (default: length only / private)\n\nArguments:\n port Port to listen on (shorthand for --port; must be a positive integer)\n\nOptions:\n -p, --port <port> Port to listen on (default: 4318, env: AUTOTEL_DEVTOOLS_PORT).\n If the port is taken, the next free port is used and a warning is shown.\n -H, --host <host> Host to bind to (default: 127.0.0.1, env: AUTOTEL_DEVTOOLS_HOST)\n -t, --title <title> Dashboard title, shown in the banner and browser tab\n (env: AUTOTEL_DEVTOOLS_TITLE)\n Env limits: AUTOTEL_MAX_TRACE_COUNT, AUTOTEL_MAX_LOG_COUNT, AUTOTEL_MAX_METRIC_COUNT\n -h, --help Show this help message\n -v, --version Show version number\n\nEndpoints:\n GET / Web devtools UI (fullpage)\n GET /widget.js Widget bundle (embed in your app)\n POST /v1/traces Receive OTLP JSON trace data\n GET /v1/traces Read back received traces (verify ingestion in tests)\n DELETE /v1/traces Clear captured telemetry (test reset)\n POST /v1/logs Receive OTLP JSON log data\n POST /v1/metrics Receive OTLP JSON metric data\n WS /ws WebSocket stream for real-time updates\n GET /healthz Health check\n\nExamples:\n npx autotel-devtools\n npx autotel-devtools 4319\n npx autotel-devtools -p 4319 -H 0.0.0.0\n npx autotel-devtools claude # watch Claude Code in the Agents tab\n npx autotel-devtools claude --print-env # just print the env (for MDM / VS Code)\n\nThen point your app:\n OTEL_EXPORTER_OTLP_PROTOCOL=http/json OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 node app.js\n\nView in browser:\n http://localhost:4318\n\nOr embed widget in your app:\n <script src=\"http://localhost:4318/widget.js\"><\/script>\n\n");
13
+ process.stdout.write("autotel-devtools - Standalone OTLP receiver with web devtools UI\n\nUsage: autotel-devtools [port] [options]\n autotel-devtools claude [claude args] [--print-env] [--log-prompts]\n\nSubcommands:\n claude Start the receiver AND launch Claude Code wired to it\n (HTTP/protobuf → this receiver, 1s intervals, session id on).\n Open the \"Agents\" tab to watch sessions, tokens, cost,\n tool / MCP / sub-agent / skill usage live.\n --print-env Print the telemetry env block and exit (don't launch)\n --log-prompts Capture prompt text (default: length only / private)\n\nArguments:\n port Port to listen on (shorthand for --port; must be a positive integer)\n\nOptions:\n -p, --port <port> Port to listen on (default: 4318, env: AUTOTEL_DEVTOOLS_PORT).\n If the port is taken, the next free port is used and a warning is shown.\n -H, --host <host> Host to bind to (default: 127.0.0.1, env: AUTOTEL_DEVTOOLS_HOST)\n -t, --title <title> Dashboard title, shown in the banner and browser tab\n -d, --db <path> Keep telemetry in a sqlite file so it survives restarts\n (default: in-memory, env: AUTOTEL_DEVTOOLS_DB)\n --max-traces <n> Traces retained before the oldest are pruned\n (default: 100000, env: AUTOTEL_DEVTOOLS_MAX_TRACES)\n --grpc-port <n> OTLP/gRPC port (default: 4317, env: AUTOTEL_DEVTOOLS_GRPC_PORT)\n --db-max-size <size> Logical store cap, e.g. 512mb or 2gb\n (default: 512mb memory / 2gb disk, env: AUTOTEL_DEVTOOLS_DB_MAX_SIZE)\n (env: AUTOTEL_DEVTOOLS_TITLE)\n Env limits: AUTOTEL_MAX_TRACE_COUNT, AUTOTEL_MAX_LOG_COUNT\n -h, --help Show this help message\n -v, --version Show version number\n\nEndpoints:\n GET / Web devtools UI (fullpage)\n GET /widget.js Widget bundle (embed in your app)\n POST /v1/traces Receive OTLP JSON trace data\n GET /v1/traces Read back received traces (verify ingestion in tests)\n DELETE /v1/traces Clear captured telemetry (test reset)\n POST /v1/logs Receive OTLP JSON log data\n POST /v1/metrics Receive OTLP JSON metric data\n gRPC :4317 Receive OTLP protobuf traces, logs and metrics\n WS /ws WebSocket stream for real-time updates\n GET /healthz Health check\n\nExamples:\n npx autotel-devtools\n npx autotel-devtools 4319\n npx autotel-devtools -p 4319 -H 0.0.0.0\n npx autotel-devtools claude # watch Claude Code in the Agents tab\n npx autotel-devtools claude --print-env # just print the env (for MDM / VS Code)\n\nThen point your app:\n OTEL_EXPORTER_OTLP_PROTOCOL=http/json OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 node app.js\n\nView in browser:\n http://localhost:4318\n\nOr embed widget in your app:\n <script src=\"http://localhost:4318/widget.js\"><\/script>\n\n");
13
14
  }
14
15
  function printVersion() {
15
16
  try {
@@ -24,7 +25,11 @@ function parseArgs(argv) {
24
25
  const options = {
25
26
  port: parsePort(process.env.AUTOTEL_DEVTOOLS_PORT || "4318"),
26
27
  host: process.env.AUTOTEL_DEVTOOLS_HOST || "127.0.0.1",
27
- title: process.env.AUTOTEL_DEVTOOLS_TITLE
28
+ title: process.env.AUTOTEL_DEVTOOLS_TITLE,
29
+ dbPath: process.env.AUTOTEL_DEVTOOLS_DB,
30
+ maxTraces: parseCount(process.env.AUTOTEL_DEVTOOLS_MAX_TRACES),
31
+ maxDbBytes: parseBytes(process.env.AUTOTEL_DEVTOOLS_DB_MAX_SIZE),
32
+ grpcPort: parsePort(process.env.AUTOTEL_DEVTOOLS_GRPC_PORT || "4317")
28
33
  };
29
34
  let portWasExplicit = false;
30
35
  let positionalPortConsumed = false;
@@ -55,6 +60,26 @@ function parseArgs(argv) {
55
60
  i++;
56
61
  continue;
57
62
  }
63
+ if ((arg === "--db" || arg === "-d") && next) {
64
+ options.dbPath = next;
65
+ i++;
66
+ continue;
67
+ }
68
+ if (arg === "--max-traces" && next) {
69
+ options.maxTraces = parseCount(next);
70
+ i++;
71
+ continue;
72
+ }
73
+ if (arg === "--grpc-port" && next) {
74
+ options.grpcPort = parsePort(next);
75
+ i++;
76
+ continue;
77
+ }
78
+ if (arg === "--db-max-size" && next) {
79
+ options.maxDbBytes = parseBytes(next);
80
+ i++;
81
+ continue;
82
+ }
58
83
  if (/^\d+$/.test(arg) && !positionalPortConsumed) {
59
84
  if (!portWasExplicit) options.port = parsePort(arg);
60
85
  positionalPortConsumed = true;
@@ -63,6 +88,28 @@ function parseArgs(argv) {
63
88
  }
64
89
  return options;
65
90
  }
91
+ /**
92
+ * A positive integer option, or undefined when unset.
93
+ *
94
+ * Unlike `parsePort` this does not exit on a bad value: a nonsense retention
95
+ * cap should fall back to the default rather than refuse to start a viewer.
96
+ */
97
+ function parseCount(value) {
98
+ if (!value) return void 0;
99
+ const n = Number(value);
100
+ return Number.isInteger(n) && n > 0 ? n : void 0;
101
+ }
102
+ function parseBytes(value) {
103
+ if (!value) return void 0;
104
+ const match = value.trim().toLowerCase().match(/^(\d+(?:\.\d+)?)\s*(b|kb|mb|gb)?$/);
105
+ if (!match) return void 0;
106
+ return Math.floor(Number(match[1]) * {
107
+ b: 1,
108
+ kb: 1024,
109
+ mb: 1024 ** 2,
110
+ gb: 1024 ** 3
111
+ }[match[2] || "b"]);
112
+ }
66
113
  function parsePort(value) {
67
114
  const n = Number(value);
68
115
  if (!Number.isInteger(n) || n < 0 || n > 65535) {
@@ -77,7 +124,10 @@ async function startReceiver(options) {
77
124
  const wsServer = new DevtoolsServer({
78
125
  server: httpServer,
79
126
  host: options.host,
80
- verbose: true
127
+ verbose: true,
128
+ dbPath: options.dbPath,
129
+ maxTraces: options.maxTraces,
130
+ maxDbBytes: options.maxDbBytes
81
131
  });
82
132
  const sourceRoot = resolveSourceRoot(process.env.AUTOTEL_DEVTOOLS_SOURCE_ROOT, process.cwd(), loopbackOnly);
83
133
  attachDevtoolsRoutes(httpServer, wsServer, {
@@ -89,22 +139,36 @@ async function startReceiver(options) {
89
139
  primary: httpServer,
90
140
  port: options.port,
91
141
  host: options.host,
92
- attachSecondary: (s) => attachDevtoolsRoutes(s, wsServer, {
93
- loopbackOnly,
94
- title: options.title,
95
- sourceRoot
96
- })
142
+ attachSecondary: (s) => {
143
+ attachDevtoolsRoutes(s, wsServer, {
144
+ loopbackOnly,
145
+ title: options.title,
146
+ sourceRoot
147
+ });
148
+ wsServer.attachWebSocket(s);
149
+ }
97
150
  });
98
151
  const { addresses, warnings, port: boundPort } = await listeners.ready;
152
+ const grpc = await startOtlpGrpcReceiver({
153
+ devtools: wsServer,
154
+ host: options.host,
155
+ port: options.grpcPort
156
+ });
157
+ if (grpc.port !== options.grpcPort) warnings.push(`OTLP/gRPC port ${options.grpcPort} was busy; using ${grpc.port}.`);
99
158
  if (boundPort !== options.port) if (await probePortHolder(options.host, options.port) === "autotel-devtools") warnings.push(`another autotel-devtools is already running on port ${options.port}; this instance is on ${boundPort}. Use the existing one, or stop it and restart here.`);
100
159
  else warnings.push(`port ${options.port} is held by another process that is NOT autotel-devtools. Anything exporting OTLP to :${options.port} is reaching that process, not this devtools. Point your exporter at :${boundPort}, or free :${options.port} and restart.`);
101
160
  return {
102
161
  wsServer,
103
- closeAll: () => Promise.all([wsServer.close(), listeners.closeSibling()]).then(() => void 0),
162
+ closeAll: () => Promise.all([
163
+ wsServer.close(),
164
+ listeners.closeSibling(),
165
+ grpc.close()
166
+ ]).then(() => void 0),
104
167
  addresses,
105
168
  warnings,
106
169
  boundPort,
107
- uiBase: `http://${options.host === "localhost" ? "127.0.0.1" : options.host}:${boundPort}`
170
+ uiBase: `http://${options.host === "localhost" ? "127.0.0.1" : options.host}:${boundPort}`,
171
+ grpcAddress: grpc.address
108
172
  };
109
173
  }
110
174
  function buildAgentEnv(uiBase, logPrompts) {
@@ -169,7 +233,8 @@ async function runClaudeSubcommand(argv) {
169
233
  }
170
234
  const receiver = await startReceiver({
171
235
  port: opts.port,
172
- host: opts.host
236
+ host: opts.host,
237
+ grpcPort: 4317
173
238
  });
174
239
  const env = buildAgentEnv(receiver.uiBase, opts.logPrompts);
175
240
  process.stdout.write(`\n autotel-devtools — Claude Code\n\n`);
@@ -214,6 +279,7 @@ async function main() {
214
279
  process.stdout.write(` Listening: ${addresses.join(" + ")}\n`);
215
280
  process.stdout.write(` UI: ${uiBase} (open in a browser)\n`);
216
281
  process.stdout.write(` OTLP: ${uiBase}/v1/traces\n`);
282
+ process.stdout.write(` OTLP/gRPC: ${receiver.grpcAddress}\n`);
217
283
  process.stdout.write(` WebSocket: ${uiBase.replace("http", "ws")}/ws\n\n`);
218
284
  process.stdout.write(` Embed in your app — paste into your HTML; a floating panel appears automatically:\n`);
219
285
  process.stdout.write(` <script src="${uiBase}/widget.js"><\/script>\n\n`);
@@ -222,6 +288,7 @@ async function main() {
222
288
  process.stdout.write(` Or point any OTLP exporter at this receiver:\n`);
223
289
  process.stdout.write(` OTEL_EXPORTER_OTLP_PROTOCOL=http/json\n`);
224
290
  process.stdout.write(` OTEL_EXPORTER_OTLP_ENDPOINT=${uiBase}\n\n`);
291
+ process.stdout.write(` Or use gRPC: OTEL_EXPORTER_OTLP_ENDPOINT=http://${receiver.grpcAddress}\n\n`);
225
292
  process.stdout.write(` Verify ingestion: curl -s ${uiBase}/v1/traces\n\n`);
226
293
  for (const w of warnings) process.stdout.write(` ⚠ ${w}\n`);
227
294
  if (warnings.length > 0) process.stdout.write("\n");
@@ -0,0 +1,177 @@
1
+ //#region src/query/ast.d.ts
2
+ /**
3
+ * AST and vocabulary for the telemetry query language.
4
+ *
5
+ * Shared by everything that touches a query: the parser produces these nodes,
6
+ * the SQL compiler consumes them, and the editor reads the same operator table
7
+ * to offer completions. One definition, so a new operator cannot be added to
8
+ * the language without the compiler and the editor both seeing it.
9
+ */
10
+ /** Half-open source range `[from, to)`, used for editor squiggles. */
11
+ interface Range {
12
+ from: number;
13
+ to: number;
14
+ }
15
+ /**
16
+ * Every operator the language accepts.
17
+ *
18
+ * Sigils and keywords are two spellings of one set — `=~` and `REGEXP` produce
19
+ * the same node — so downstream code branches on one canonical value.
20
+ */
21
+ declare const OPERATORS: {
22
+ readonly '=': {
23
+ readonly label: "equals";
24
+ readonly kind: "comparison";
25
+ };
26
+ readonly '!=': {
27
+ readonly label: "does not equal";
28
+ readonly kind: "comparison";
29
+ };
30
+ readonly '>': {
31
+ readonly label: "greater than";
32
+ readonly kind: "ordered";
33
+ };
34
+ readonly '<': {
35
+ readonly label: "less than";
36
+ readonly kind: "ordered";
37
+ };
38
+ readonly '>=': {
39
+ readonly label: "greater than or equal";
40
+ readonly kind: "ordered";
41
+ };
42
+ readonly '<=': {
43
+ readonly label: "less than or equal";
44
+ readonly kind: "ordered";
45
+ };
46
+ readonly '^': {
47
+ readonly label: "starts with";
48
+ readonly kind: "text";
49
+ };
50
+ readonly $: {
51
+ readonly label: "ends with";
52
+ readonly kind: "text";
53
+ };
54
+ readonly CONTAINS: {
55
+ readonly label: "contains";
56
+ readonly kind: "text";
57
+ };
58
+ readonly 'NOT CONTAINS': {
59
+ readonly label: "does not contain";
60
+ readonly kind: "text";
61
+ };
62
+ readonly REGEXP: {
63
+ readonly label: "matches regex";
64
+ readonly kind: "text";
65
+ };
66
+ readonly 'NOT REGEXP': {
67
+ readonly label: "does not match regex";
68
+ readonly kind: "text";
69
+ };
70
+ readonly IN: {
71
+ readonly label: "is one of";
72
+ readonly kind: "set";
73
+ };
74
+ readonly 'NOT IN': {
75
+ readonly label: "is not one of";
76
+ readonly kind: "set";
77
+ };
78
+ };
79
+ type Operator = keyof typeof OPERATORS;
80
+ /** Sigil spellings that map onto a keyword operator. */
81
+ declare const SIGIL_ALIASES: Readonly<Record<string, Operator>>;
82
+ type QueryValue = {
83
+ type: 'string';
84
+ value: string;
85
+ } | {
86
+ type: 'number';
87
+ value: number;
88
+ } | {
89
+ type: 'boolean';
90
+ value: boolean;
91
+ } | {
92
+ type: 'null';
93
+ } | {
94
+ type: 'array';
95
+ values: QueryValue[];
96
+ };
97
+ type QueryNode =
98
+ /** Empty query: matches everything. Distinct from an error. */
99
+ {
100
+ type: 'all';
101
+ } | {
102
+ type: 'and';
103
+ left: QueryNode;
104
+ right: QueryNode;
105
+ range: Range;
106
+ } | {
107
+ type: 'or';
108
+ left: QueryNode;
109
+ right: QueryNode;
110
+ range: Range;
111
+ } | {
112
+ type: 'comparison';
113
+ field: string;
114
+ op: Operator;
115
+ value: QueryValue;
116
+ range: Range;
117
+ } |
118
+ /** A bare word or phrase, matched across a signal's default text fields. */
119
+ {
120
+ type: 'freeText';
121
+ text: string;
122
+ range: Range;
123
+ };
124
+ interface QueryError {
125
+ message: string;
126
+ range: Range;
127
+ }
128
+ type ParseResult = {
129
+ ok: true;
130
+ node: QueryNode;
131
+ } | {
132
+ ok: false;
133
+ errors: QueryError[];
134
+ };
135
+ //#endregion
136
+ //#region src/query/compile.d.ts
137
+ interface ColumnSchema {
138
+ /** Physical column name. May differ from the name used in queries. */
139
+ column: string;
140
+ type: 'string' | 'number' | 'boolean';
141
+ }
142
+ interface SignalSchema {
143
+ /** Query field name → physical column. Anything absent is an attribute. */
144
+ columns: Record<string, ColumnSchema>;
145
+ /** Column holding the JSON attribute bag. */
146
+ attributesColumn: string;
147
+ /** Query fields a bare word is matched against. Must be keys of `columns`. */
148
+ freeTextColumns: string[];
149
+ /** Optional normalized attribute index used for exact equality probes. */
150
+ attributeIndex?: {
151
+ table: string;
152
+ signal: string;
153
+ /** Trusted SQL expression identifying the current row. */
154
+ entitySql: string;
155
+ };
156
+ /**
157
+ * Query fields that live in a child table rather than on the row itself, so
158
+ * `event.name = timeout` asks whether *any* event on the span matches.
159
+ *
160
+ * Every part is a trusted SQL fragment from the schema, never from the query
161
+ * text: `table` and `column` are identifiers, `joinSql` is the predicate
162
+ * tying a child row to the current one.
163
+ */
164
+ related?: Record<string, {
165
+ table: string;
166
+ column: string;
167
+ joinSql: string;
168
+ }>;
169
+ }
170
+ interface CompiledQuery {
171
+ /** A WHERE-clause fragment. `1` matches everything, `0` matches nothing. */
172
+ sql: string;
173
+ params: unknown[];
174
+ }
175
+ declare function compileWhere(node: QueryNode, schema: SignalSchema): CompiledQuery;
176
+ //#endregion
177
+ export { OPERATORS as a, QueryError as c, Range as d, SIGIL_ALIASES as f, compileWhere as i, QueryNode as l, CompiledQuery as n, Operator as o, SignalSchema as r, ParseResult as s, ColumnSchema as t, QueryValue as u };
@@ -0,0 +1,177 @@
1
+ //#region src/query/ast.d.ts
2
+ /**
3
+ * AST and vocabulary for the telemetry query language.
4
+ *
5
+ * Shared by everything that touches a query: the parser produces these nodes,
6
+ * the SQL compiler consumes them, and the editor reads the same operator table
7
+ * to offer completions. One definition, so a new operator cannot be added to
8
+ * the language without the compiler and the editor both seeing it.
9
+ */
10
+ /** Half-open source range `[from, to)`, used for editor squiggles. */
11
+ interface Range {
12
+ from: number;
13
+ to: number;
14
+ }
15
+ /**
16
+ * Every operator the language accepts.
17
+ *
18
+ * Sigils and keywords are two spellings of one set — `=~` and `REGEXP` produce
19
+ * the same node — so downstream code branches on one canonical value.
20
+ */
21
+ declare const OPERATORS: {
22
+ readonly '=': {
23
+ readonly label: "equals";
24
+ readonly kind: "comparison";
25
+ };
26
+ readonly '!=': {
27
+ readonly label: "does not equal";
28
+ readonly kind: "comparison";
29
+ };
30
+ readonly '>': {
31
+ readonly label: "greater than";
32
+ readonly kind: "ordered";
33
+ };
34
+ readonly '<': {
35
+ readonly label: "less than";
36
+ readonly kind: "ordered";
37
+ };
38
+ readonly '>=': {
39
+ readonly label: "greater than or equal";
40
+ readonly kind: "ordered";
41
+ };
42
+ readonly '<=': {
43
+ readonly label: "less than or equal";
44
+ readonly kind: "ordered";
45
+ };
46
+ readonly '^': {
47
+ readonly label: "starts with";
48
+ readonly kind: "text";
49
+ };
50
+ readonly $: {
51
+ readonly label: "ends with";
52
+ readonly kind: "text";
53
+ };
54
+ readonly CONTAINS: {
55
+ readonly label: "contains";
56
+ readonly kind: "text";
57
+ };
58
+ readonly 'NOT CONTAINS': {
59
+ readonly label: "does not contain";
60
+ readonly kind: "text";
61
+ };
62
+ readonly REGEXP: {
63
+ readonly label: "matches regex";
64
+ readonly kind: "text";
65
+ };
66
+ readonly 'NOT REGEXP': {
67
+ readonly label: "does not match regex";
68
+ readonly kind: "text";
69
+ };
70
+ readonly IN: {
71
+ readonly label: "is one of";
72
+ readonly kind: "set";
73
+ };
74
+ readonly 'NOT IN': {
75
+ readonly label: "is not one of";
76
+ readonly kind: "set";
77
+ };
78
+ };
79
+ type Operator = keyof typeof OPERATORS;
80
+ /** Sigil spellings that map onto a keyword operator. */
81
+ declare const SIGIL_ALIASES: Readonly<Record<string, Operator>>;
82
+ type QueryValue = {
83
+ type: 'string';
84
+ value: string;
85
+ } | {
86
+ type: 'number';
87
+ value: number;
88
+ } | {
89
+ type: 'boolean';
90
+ value: boolean;
91
+ } | {
92
+ type: 'null';
93
+ } | {
94
+ type: 'array';
95
+ values: QueryValue[];
96
+ };
97
+ type QueryNode =
98
+ /** Empty query: matches everything. Distinct from an error. */
99
+ {
100
+ type: 'all';
101
+ } | {
102
+ type: 'and';
103
+ left: QueryNode;
104
+ right: QueryNode;
105
+ range: Range;
106
+ } | {
107
+ type: 'or';
108
+ left: QueryNode;
109
+ right: QueryNode;
110
+ range: Range;
111
+ } | {
112
+ type: 'comparison';
113
+ field: string;
114
+ op: Operator;
115
+ value: QueryValue;
116
+ range: Range;
117
+ } |
118
+ /** A bare word or phrase, matched across a signal's default text fields. */
119
+ {
120
+ type: 'freeText';
121
+ text: string;
122
+ range: Range;
123
+ };
124
+ interface QueryError {
125
+ message: string;
126
+ range: Range;
127
+ }
128
+ type ParseResult = {
129
+ ok: true;
130
+ node: QueryNode;
131
+ } | {
132
+ ok: false;
133
+ errors: QueryError[];
134
+ };
135
+ //#endregion
136
+ //#region src/query/compile.d.ts
137
+ interface ColumnSchema {
138
+ /** Physical column name. May differ from the name used in queries. */
139
+ column: string;
140
+ type: 'string' | 'number' | 'boolean';
141
+ }
142
+ interface SignalSchema {
143
+ /** Query field name → physical column. Anything absent is an attribute. */
144
+ columns: Record<string, ColumnSchema>;
145
+ /** Column holding the JSON attribute bag. */
146
+ attributesColumn: string;
147
+ /** Query fields a bare word is matched against. Must be keys of `columns`. */
148
+ freeTextColumns: string[];
149
+ /** Optional normalized attribute index used for exact equality probes. */
150
+ attributeIndex?: {
151
+ table: string;
152
+ signal: string;
153
+ /** Trusted SQL expression identifying the current row. */
154
+ entitySql: string;
155
+ };
156
+ /**
157
+ * Query fields that live in a child table rather than on the row itself, so
158
+ * `event.name = timeout` asks whether *any* event on the span matches.
159
+ *
160
+ * Every part is a trusted SQL fragment from the schema, never from the query
161
+ * text: `table` and `column` are identifiers, `joinSql` is the predicate
162
+ * tying a child row to the current one.
163
+ */
164
+ related?: Record<string, {
165
+ table: string;
166
+ column: string;
167
+ joinSql: string;
168
+ }>;
169
+ }
170
+ interface CompiledQuery {
171
+ /** A WHERE-clause fragment. `1` matches everything, `0` matches nothing. */
172
+ sql: string;
173
+ params: unknown[];
174
+ }
175
+ declare function compileWhere(node: QueryNode, schema: SignalSchema): CompiledQuery;
176
+ //#endregion
177
+ export { OPERATORS as a, QueryError as c, Range as d, SIGIL_ALIASES as f, compileWhere as i, QueryNode as l, CompiledQuery as n, Operator as o, SignalSchema as r, ParseResult as s, ColumnSchema as t, QueryValue as u };
@@ -1,4 +1,4 @@
1
- import { a as ErrorGroup, o as ErrorOccurrence, u as TraceData } from "./exporter-Due9Rd4s.js";
1
+ import { n as ErrorGroup, o as TraceData, r as ErrorOccurrence } from "./types-DM8y4A9Z.js";
2
2
  //#region src/server/error-aggregator.d.ts
3
3
  /** How many errors the aggregator is holding, and what they are. */
4
4
  interface ErrorStats {
@@ -1,4 +1,4 @@
1
- import { a as ErrorGroup, o as ErrorOccurrence, u as TraceData } from "./exporter-Dt4kx128.cjs";
1
+ import { n as ErrorGroup, o as TraceData, r as ErrorOccurrence } from "./types-B0tjwFqj.cjs";
2
2
  //#region src/server/error-aggregator.d.ts
3
3
  /** How many errors the aggregator is holding, and what they are. */
4
4
  interface ErrorStats {