@tracepanel/js 0.0.0-stage → 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.
Files changed (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +271 -2
  3. package/dist/agent.d.ts +42 -0
  4. package/dist/agent.js +298 -0
  5. package/dist/agent.js.map +1 -0
  6. package/dist/api.d.ts +11 -0
  7. package/dist/api.js +34 -0
  8. package/dist/api.js.map +1 -0
  9. package/dist/browser.d.ts +25 -0
  10. package/dist/browser.js +438 -0
  11. package/dist/browser.js.map +1 -0
  12. package/dist/bullmq.d.ts +7 -0
  13. package/dist/bullmq.js +86 -0
  14. package/dist/bullmq.js.map +1 -0
  15. package/dist/bun.d.ts +15 -0
  16. package/dist/bun.js +114 -0
  17. package/dist/bun.js.map +1 -0
  18. package/dist/calls.d.ts +20 -0
  19. package/dist/calls.js +145 -0
  20. package/dist/calls.js.map +1 -0
  21. package/dist/cli.d.ts +2 -0
  22. package/dist/cli.js +39 -0
  23. package/dist/cli.js.map +1 -0
  24. package/dist/config.d.ts +29 -0
  25. package/dist/config.js +48 -0
  26. package/dist/config.js.map +1 -0
  27. package/dist/console.d.ts +6 -0
  28. package/dist/console.js +44 -0
  29. package/dist/console.js.map +1 -0
  30. package/dist/explain.d.ts +25 -0
  31. package/dist/explain.js +145 -0
  32. package/dist/explain.js.map +1 -0
  33. package/dist/express.d.ts +2 -0
  34. package/dist/express.js +77 -0
  35. package/dist/express.js.map +1 -0
  36. package/dist/frameworks.d.ts +2 -0
  37. package/dist/frameworks.js +39 -0
  38. package/dist/frameworks.js.map +1 -0
  39. package/dist/headers.d.ts +2 -0
  40. package/dist/headers.js +42 -0
  41. package/dist/headers.js.map +1 -0
  42. package/dist/index.d.ts +5 -0
  43. package/dist/index.js +3 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/logs.d.ts +8 -0
  46. package/dist/logs.js +54 -0
  47. package/dist/logs.js.map +1 -0
  48. package/dist/model.d.ts +68 -0
  49. package/dist/model.js +41 -0
  50. package/dist/model.js.map +1 -0
  51. package/dist/next-widget.d.ts +3 -0
  52. package/dist/next-widget.js +17 -0
  53. package/dist/next-widget.js.map +1 -0
  54. package/dist/next.d.ts +2 -0
  55. package/dist/next.js +16 -0
  56. package/dist/next.js.map +1 -0
  57. package/dist/nuxt-plugin.d.ts +24 -0
  58. package/dist/nuxt-plugin.js +39 -0
  59. package/dist/nuxt-plugin.js.map +1 -0
  60. package/dist/nuxt.d.ts +10 -0
  61. package/dist/nuxt.js +16 -0
  62. package/dist/nuxt.js.map +1 -0
  63. package/dist/processor.d.ts +105 -0
  64. package/dist/processor.js +491 -0
  65. package/dist/processor.js.map +1 -0
  66. package/dist/register.d.ts +1 -0
  67. package/dist/register.js +5 -0
  68. package/dist/register.js.map +1 -0
  69. package/dist/runtime.d.ts +17 -0
  70. package/dist/runtime.js +27 -0
  71. package/dist/runtime.js.map +1 -0
  72. package/dist/setup.d.ts +10 -0
  73. package/dist/setup.js +102 -0
  74. package/dist/setup.js.map +1 -0
  75. package/dist/sql.d.ts +3 -0
  76. package/dist/sql.js +18 -0
  77. package/dist/sql.js.map +1 -0
  78. package/dist/stack.d.ts +13 -0
  79. package/dist/stack.js +66 -0
  80. package/dist/stack.js.map +1 -0
  81. package/dist/vite.d.ts +36 -0
  82. package/dist/vite.js +139 -0
  83. package/dist/vite.js.map +1 -0
  84. package/dist/widget.d.ts +26 -0
  85. package/dist/widget.js +70 -0
  86. package/dist/widget.js.map +1 -0
  87. package/package.json +125 -4
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DevRealm OÜ
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,272 @@
1
- # Temporary Holding Version
1
+ # Tracepanel for Node.js and Bun
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![CI](https://github.com/tracepanel/tracepanel-js/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/tracepanel/tracepanel-js/actions/workflows/ci.yml)
4
+ [![npm](https://img.shields.io/npm/v/@tracepanel/js)](https://www.npmjs.com/package/@tracepanel/js)
5
+ [![Node.js](https://img.shields.io/node/v/@tracepanel/js)](https://www.npmjs.com/package/@tracepanel/js)
6
+
7
+ **See what your app does on every request.** Tracepanel records each request
8
+ of your Express, NestJS, Next.js, Nuxt or Bun app with its SQL queries, logs
9
+ and errors. It finds N+1 queries, duplicate queries and slow SQL, and names
10
+ the line of code to fix. You read it in a panel on your pages and in a
11
+ console.
12
+
13
+ It runs while you develop, and everything stays on your machine.
14
+
15
+ ## Setup
16
+
17
+ ```bash
18
+ npm install @tracepanel/js
19
+ ```
20
+
21
+ Tracepanel is on unless `NODE_ENV` is `production`. `TRACEPANEL_ENABLED=0`
22
+ turns it off and `TRACEPANEL_ENABLED=1` turns it on. It starts its agent on
23
+ the first request. The agent keeps the traces in `.tracepanel/` in your
24
+ project and serves the console. Open a page and click the badge in the
25
+ corner.
26
+
27
+ ### Express
28
+
29
+ Start the app with Tracepanel loaded before it, so OpenTelemetry can
30
+ instrument its HTTP server, Express and its database driver, and add the
31
+ middleware. It puts the badge on HTML pages and gives every response its
32
+ trace id:
33
+
34
+ ```bash
35
+ node --import @tracepanel/js/register server.js
36
+ ```
37
+
38
+ ```js
39
+ import express from "express";
40
+ import { tracepanel } from "@tracepanel/js/express";
41
+
42
+ const app = express();
43
+ app.use(tracepanel());
44
+ ```
45
+
46
+ The flag is how Tracepanel gets in before the app. An ES module's imports
47
+ all load before any of its code runs, so `import "@tracepanel/js/register"` at
48
+ the top of `server.js` comes too late for Express and the database driver.
49
+ Without the flag, either let the CLI add it:
50
+
51
+ ```json
52
+ { "scripts": { "dev": "tracepanel run node server.js" } }
53
+ ```
54
+
55
+ or start from a file that loads Tracepanel, then the app:
56
+
57
+ ```js
58
+ // main.js, run with `node main.js`
59
+ import "@tracepanel/js/register";
60
+ await import("./server.js");
61
+ ```
62
+
63
+ The agent starts by itself either way, on the app's first request.
64
+
65
+ ### NestJS
66
+
67
+ Nest runs on Express: load Tracepanel the same way and add the same
68
+ middleware in `main.ts`. Each request's handler is its controller method,
69
+ such as `BooksController.list`, and the Nest `Logger`'s records go to the
70
+ request's trace. An `HttpException` that answers below 500 is the response,
71
+ not an error.
72
+
73
+ ```bash
74
+ node --import @tracepanel/js/register dist/main.js
75
+ ```
76
+
77
+ ```ts
78
+ import { NestFactory } from "@nestjs/core";
79
+ import { tracepanel } from "@tracepanel/js/express";
80
+ import { AppModule } from "./app.module.js";
81
+
82
+ const app = await NestFactory.create(AppModule);
83
+ app.use(tracepanel());
84
+ await app.listen(3000);
85
+ ```
86
+
87
+ ### Next.js
88
+
89
+ Next.js starts its own server: Tracepanel goes in from the instrumentation
90
+ hook, and Next.js makes each request's root span itself. Keep the package out
91
+ of the bundles, and put the badge in the root layout:
92
+
93
+ ```ts
94
+ // instrumentation.ts
95
+ export async function register() {
96
+ if (process.env.NEXT_RUNTIME === "nodejs") (await import("@tracepanel/js/next")).register();
97
+ }
98
+ ```
99
+
100
+ ```ts
101
+ // next.config.ts
102
+ const config = { serverExternalPackages: ["@tracepanel/js"] };
103
+ export default config;
104
+ ```
105
+
106
+ ```tsx
107
+ // app/layout.tsx
108
+ import { Tracepanel } from "@tracepanel/js/next/widget";
109
+
110
+ export default function RootLayout({ children }) {
111
+ return (
112
+ <html lang="en">
113
+ <head>
114
+ <Tracepanel />
115
+ </head>
116
+ <body>{children}</body>
117
+ </html>
118
+ );
119
+ }
120
+ ```
121
+
122
+ A request's handler is its page or route handler, named by its route
123
+ (`/api/books/[id]`).
124
+
125
+ ### Nuxt
126
+
127
+ Add the module and start `nuxt dev` with Tracepanel loaded, so it hooks the
128
+ database driver as Nitro loads it:
129
+
130
+ ```ts
131
+ // nuxt.config.ts
132
+ export default defineNuxtConfig({ modules: ["@tracepanel/js/nuxt"] });
133
+ ```
134
+
135
+ ```bash
136
+ NODE_OPTIONS=--import=@tracepanel/js/register nuxt dev
137
+ ```
138
+
139
+ The module names each request by its route (`/api/books/:id`), puts the
140
+ badge on server-rendered pages, and records the errors h3 answers with a 500.
141
+
142
+ ### Bun
143
+
144
+ `serve()` is `Bun.serve` with each route traced under its pattern, and
145
+ `withTracepanel()` wraps any fetch handler, such as Hono's `app.fetch`:
146
+
147
+ ```bash
148
+ bun --preload @tracepanel/js/register server.ts
149
+ ```
150
+
151
+ ```ts
152
+ import { serve } from "@tracepanel/js/bun";
153
+
154
+ serve({
155
+ routes: {
156
+ "/api/books/:id": async (req) => Response.json(await findBook(req.params.id)),
157
+ },
158
+ });
159
+ ```
160
+
161
+ ### An SPA on Vite
162
+
163
+ A single-page app on Vite's dev server gets the badge from the plugin, whatever
164
+ serves its API: an app Tracepanel traces, in Node or in Python. It puts the
165
+ widget into `index.html` and serves the backend's console on Vite's origin,
166
+ from the backend project's `.tracepanel/`:
167
+
168
+ ```ts
169
+ // vite.config.ts
170
+ import { tracepanel } from "@tracepanel/js/vite";
171
+
172
+ export default { plugins: [tracepanel({ dir: "../backend" })] };
173
+ ```
174
+
175
+ When the page loads before the API's first request, the plugin starts the
176
+ backend's agent itself, as the SDK would. The console answers this machine
177
+ only, even with Vite on the network (`vite --host`): its traces and what it
178
+ lets a local caller do, such as switching the user, are the developer's.
179
+
180
+ An API on another origin exposes the trace's headers to the page on its CORS
181
+ responses, so the badge lists each call the app makes. A backend that serves
182
+ its own console (Django, FastAPI) is named with `console`:
183
+
184
+ ```ts
185
+ tracepanel({ console: "http://localhost:8000/__tracepanel__/" });
186
+ ```
187
+
188
+ The plugin also records the pages in the browser. Each view (the page's load,
189
+ or a route change) is a trace, and so is each action on it: a click or a
190
+ submit that called the API, named after the control (`click "Save"`), or
191
+ background calls nobody started. Each has its calls, its errors and its
192
+ console, with the line that logged each record, and lasts until its last
193
+ call answered. The console lists them in Pages, the page's view first and
194
+ its actions under it, and the badge's panel lists each action over the
195
+ requests it made.
196
+
197
+ A call to the page's own origin carries a W3C `traceparent`, so the console
198
+ links it to the API's trace. `propagateTo` names other origins, whose CORS
199
+ must allow the header:
200
+
201
+ ```ts
202
+ tracepanel({ dir: "../backend", browser: { service: "shop-web", propagateTo: ["http://127.0.0.1:8000"] } });
203
+ ```
204
+
205
+ `browser: false` leaves the recording out. Vite's own client logs (its
206
+ connection, hot reload) stay out of the pages' traces.
207
+
208
+ Without Vite, call `record()` from `@tracepanel/js/browser` in the page, with the
209
+ console on the page's origin (`api`, `/__tracepanel__/` by default).
210
+ `ignoreSources` takes the path prefixes of scripts whose console isn't the
211
+ page's, such as a dev server's client.
212
+
213
+ ## What it records
214
+
215
+ - HTTP requests, with the route and the handler that served them.
216
+ - SQL from `pg` and `mysql2` (and so Drizzle, Knex, Sequelize, TypeORM and
217
+ Prisma's driver adapters), with the line of your code that ran each
218
+ statement. Prisma's operations show as steps.
219
+ - Redis commands (`ioredis`, `redis`) and MongoDB commands, with their own
220
+ N+1 and duplicates.
221
+ - Outgoing HTTP calls: `fetch()`, and `node:http` clients such as axios.
222
+ - BullMQ: each job a worker runs is a task, each job the app adds a step.
223
+ - `console` calls made during a request, and Nest's `Logger`.
224
+ - Errors thrown in a handler, with your frames and their source.
225
+ - The caller's trace, when a request carries a W3C `traceparent`: the agent
226
+ links the two.
227
+
228
+ ## Your own steps and traces
229
+
230
+ ```js
231
+ import { span, trace } from "@tracepanel/js";
232
+
233
+ await trace("nightly cleanup", async () => {
234
+ await span("load stale rows", () => loadStaleRows());
235
+ });
236
+ ```
237
+
238
+ `trace()` makes a script or a scheduled job a trace of its own, and inside a
239
+ request it is a step. `span()` is a step of the current trace. Outside
240
+ development they run the function and nothing else. Run a script with
241
+ `npx tracepanel run node script.js`.
242
+
243
+ ## Settings
244
+
245
+ Environment variables, as in the Python SDK:
246
+
247
+ | Variable | Default | What it sets |
248
+ | --- | --- | --- |
249
+ | `TRACEPANEL_ENABLED` | on unless `NODE_ENV=production` | `0` turns Tracepanel off, `1` on |
250
+ | `TRACEPANEL_SERVICE` | `package.json`'s name | the app's name in the console |
251
+ | `TRACEPANEL_DIR` | `.tracepanel` | the project's store, where the agent keeps the traces |
252
+ | `TRACEPANEL_AGENT_BIN` | the one npm installed | the agent to start, such as a build of your own |
253
+ | `TRACEPANEL_AGENT` | `auto` | an agent elsewhere (`tcp:host:port`), never started |
254
+ | `TRACEPANEL_N_PLUS_ONE_THRESHOLD` | `3` | repeats that make an N+1 |
255
+ | `TRACEPANEL_N_PLUS_ONE_MIN_MS` | `5` | for Redis and other fast stores, the time repeats must take together |
256
+ | `TRACEPANEL_SLOW_SQL_MS` | `100` | a slow query, SQL or MongoDB |
257
+ | `TRACEPANEL_SLOW_HTTP_MS` | `500` | a slow outgoing HTTP call |
258
+ | `TRACEPANEL_LONG_TRANSACTION_MS` | `500` | a long transaction |
259
+ | `TRACEPANEL_QUEUE_WAIT_MS` | `1000` | a task that waited long for a worker |
260
+ | `TRACEPANEL_LARGE_RESULT_ROWS` | `1000` | a query without a LIMIT that read too many rows |
261
+
262
+ ## Command line
263
+
264
+ | Command | What it does |
265
+ | --- | --- |
266
+ | `tracepanel run <command>` | runs a Node command with Tracepanel on, such as `tracepanel run node server.js` |
267
+ | `tracepanel dev` | runs the agent in the foreground, with the console on http://127.0.0.1:8765/ |
268
+ | `tracepanel mcp` | gives an AI coding agent your traces over MCP |
269
+
270
+ ## License
271
+
272
+ Licensed under the [MIT License](LICENSE).
@@ -0,0 +1,42 @@
1
+ import type { Config } from "./config.js";
2
+ import type { Trace } from "./model.js";
3
+ /** Starts the project's agent when none answers, in its own session, serving the console on a free port. */
4
+ export declare class Starter {
5
+ private readonly storeDir;
6
+ private failedAt;
7
+ constructor(storeDir: string);
8
+ /**
9
+ * Whether an agent runs for the project: one already does, or this process starts it (RESTART_MS after a failure).
10
+ * `gone`: the agent at the project's address didn't answer. One that didn't exit cleanly (killed, a reboot) leaves
11
+ * its addresses: they go before a new one starts.
12
+ */
13
+ ensure(gone?: boolean): boolean;
14
+ private start;
15
+ }
16
+ /** Where the widget loads from and opens the panel: the console the project's agent serves, read every few seconds. */
17
+ export declare class ConsoleUrl {
18
+ private readonly storeDir;
19
+ private url;
20
+ private checked;
21
+ constructor(storeDir: string);
22
+ get(): string;
23
+ }
24
+ /** Batches traces to the agent in the background; a full queue drops a trace rather than slow a request down. */
25
+ export declare class Sender {
26
+ private readonly config;
27
+ private readonly starter;
28
+ private queue;
29
+ private timer;
30
+ private socket;
31
+ private connecting;
32
+ private retryAt;
33
+ private warned;
34
+ constructor(config: Config, starter: Starter);
35
+ submit(trace: Trace): void;
36
+ private flushSoon;
37
+ private flush;
38
+ private connection;
39
+ private open;
40
+ /** A connection the agent accepted after the hello line, or undefined. */
41
+ private handshake;
42
+ }
package/dist/agent.js ADDED
@@ -0,0 +1,298 @@
1
+ // Traces go to the project's agent, which finds their issues, keeps them and serves the console. Its addresses are in
2
+ // <store>/agent.addr and console.addr. The wire: a hello line, then frames of a 4-byte big-endian length and a JSON
3
+ // array of traces. The agent starts on demand, as the other SDKs start it, and outlives the app's restarts.
4
+ import { spawn } from "node:child_process";
5
+ import { closeSync, mkdirSync, openSync, readFileSync, rmSync } from "node:fs";
6
+ import { createRequire } from "node:module";
7
+ import { connect } from "node:net";
8
+ import { join } from "node:path";
9
+ import { explain } from "./explain.js";
10
+ const PROTOCOL = 1;
11
+ const QUEUE_SIZE = 1000;
12
+ const BATCH_SIZE = 50;
13
+ const BATCH_WAIT_MS = 50;
14
+ const RETRY_AFTER_MS = 5000;
15
+ const START_WAIT_MS = 1500;
16
+ const CONSOLE_CHECK_MS = 2000;
17
+ const IDLE_EXIT_S = "1800";
18
+ // An agent that exited (idle, killed, replaced by a new build) is started anew at once; one that couldn't start is
19
+ // tried again after this long, not on every trace.
20
+ const RESTART_MS = 30_000;
21
+ function readAddress(path) {
22
+ try {
23
+ return readFileSync(path, "utf8").trim();
24
+ }
25
+ catch {
26
+ return "";
27
+ }
28
+ }
29
+ /** TRACEPANEL_AGENT (an agent outside the app's container, tcp:host:port), or the address the project's agent wrote. */
30
+ function agentAddress(storeDir) {
31
+ const named = process.env.TRACEPANEL_AGENT;
32
+ if (named && named !== "auto")
33
+ return named;
34
+ return readAddress(join(storeDir, "agent.addr"));
35
+ }
36
+ function autoStart() {
37
+ const named = process.env.TRACEPANEL_AGENT;
38
+ return !named || named === "auto";
39
+ }
40
+ /** The agent's binary: TRACEPANEL_AGENT_BIN, or the one npm installed with @tracepanel/agent. */
41
+ function agentBinary() {
42
+ try {
43
+ const agent = createRequire(import.meta.url)("@tracepanel/agent");
44
+ return agent.binaryPath();
45
+ }
46
+ catch (error) {
47
+ warn(error instanceof Error ? error.message : String(error));
48
+ return undefined;
49
+ }
50
+ }
51
+ function warn(message) {
52
+ process.stderr.write(`tracepanel: ${message}\n`);
53
+ }
54
+ /** Starts the project's agent when none answers, in its own session, serving the console on a free port. */
55
+ export class Starter {
56
+ storeDir;
57
+ failedAt = Number.NEGATIVE_INFINITY;
58
+ constructor(storeDir) {
59
+ this.storeDir = storeDir;
60
+ }
61
+ /**
62
+ * Whether an agent runs for the project: one already does, or this process starts it (RESTART_MS after a failure).
63
+ * `gone`: the agent at the project's address didn't answer. One that didn't exit cleanly (killed, a reboot) leaves
64
+ * its addresses: they go before a new one starts.
65
+ */
66
+ ensure(gone = false) {
67
+ if (!gone && agentAddress(this.storeDir))
68
+ return true;
69
+ if (!autoStart() || Date.now() - this.failedAt < RESTART_MS)
70
+ return false;
71
+ if (gone)
72
+ for (const name of ["agent.addr", "console.addr"])
73
+ rmSync(join(this.storeDir, name), { force: true });
74
+ this.start();
75
+ const started = agentAddress(this.storeDir) !== "";
76
+ if (!started)
77
+ this.failedAt = Date.now();
78
+ return started;
79
+ }
80
+ start() {
81
+ const binary = agentBinary();
82
+ if (!binary)
83
+ return;
84
+ mkdirSync(this.storeDir, { recursive: true });
85
+ const log = openSync(join(this.storeDir, "agent.log"), "a");
86
+ try {
87
+ const child = spawn(binary, ["--dir", this.storeDir, "--serve", "127.0.0.1:0", "--idle-exit", IDLE_EXIT_S], {
88
+ detached: true,
89
+ stdio: ["ignore", log, log],
90
+ });
91
+ child.on("error", (error) => warn(`the agent didn't start: ${error.message}`));
92
+ child.unref();
93
+ }
94
+ finally {
95
+ closeSync(log);
96
+ }
97
+ const deadline = Date.now() + START_WAIT_MS;
98
+ while (Date.now() < deadline && !agentAddress(this.storeDir)) {
99
+ // The first trace waits a moment for the agent's address: it's the one place a request waits on it.
100
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 20);
101
+ }
102
+ if (!agentAddress(this.storeDir))
103
+ warn(`the agent didn't start (see ${join(this.storeDir, "agent.log")})`);
104
+ }
105
+ }
106
+ /** Where the widget loads from and opens the panel: the console the project's agent serves, read every few seconds. */
107
+ export class ConsoleUrl {
108
+ storeDir;
109
+ url = "";
110
+ checked = 0;
111
+ constructor(storeDir) {
112
+ this.storeDir = storeDir;
113
+ }
114
+ get() {
115
+ const configured = process.env.TRACEPANEL_CONSOLE_URL;
116
+ if (configured)
117
+ return configured.endsWith("/") ? configured : `${configured}/`;
118
+ if (!this.url || Date.now() - this.checked > CONSOLE_CHECK_MS) {
119
+ this.checked = Date.now();
120
+ this.url = readAddress(join(this.storeDir, "console.addr"));
121
+ }
122
+ return this.url;
123
+ }
124
+ }
125
+ /** Batches traces to the agent in the background; a full queue drops a trace rather than slow a request down. */
126
+ export class Sender {
127
+ config;
128
+ starter;
129
+ queue = [];
130
+ timer;
131
+ socket;
132
+ connecting;
133
+ retryAt = 0;
134
+ warned = false;
135
+ constructor(config, starter) {
136
+ this.config = config;
137
+ this.starter = starter;
138
+ }
139
+ submit(trace) {
140
+ if (this.queue.length >= QUEUE_SIZE)
141
+ return;
142
+ this.queue.push(trace);
143
+ if (this.queue.length >= BATCH_SIZE)
144
+ this.flushSoon(0);
145
+ else
146
+ this.flushSoon(BATCH_WAIT_MS);
147
+ }
148
+ flushSoon(delay) {
149
+ if (this.timer && delay > 0)
150
+ return;
151
+ clearTimeout(this.timer);
152
+ this.timer = setTimeout(() => {
153
+ this.timer = undefined;
154
+ void this.flush();
155
+ }, delay);
156
+ this.timer.unref();
157
+ }
158
+ async flush() {
159
+ const batch = this.queue.splice(0, BATCH_SIZE);
160
+ if (batch.length === 0)
161
+ return;
162
+ const socket = await this.connection();
163
+ if (!socket)
164
+ return;
165
+ const data = Buffer.from(JSON.stringify(batch));
166
+ const length = Buffer.alloc(4);
167
+ length.writeUInt32BE(data.length);
168
+ socket.write(Buffer.concat([length, data]));
169
+ if (this.queue.length > 0)
170
+ this.flushSoon(0);
171
+ }
172
+ async connection() {
173
+ if (this.socket && !this.socket.destroyed)
174
+ return this.socket;
175
+ if (Date.now() < this.retryAt)
176
+ return undefined;
177
+ this.connecting ??= this.open().finally(() => {
178
+ this.connecting = undefined;
179
+ });
180
+ const socket = await this.connecting;
181
+ if (!socket) {
182
+ this.retryAt = Date.now() + RETRY_AFTER_MS;
183
+ if (!this.warned && !autoStart()) {
184
+ this.warned = true;
185
+ warn(`no agent at ${agentAddress(this.config.storeDir)}: traces are dropped until it answers`);
186
+ }
187
+ }
188
+ return socket;
189
+ }
190
+ async open() {
191
+ let socket = await this.handshake(agentAddress(this.config.storeDir));
192
+ if (!socket && this.starter.ensure(true))
193
+ socket = await this.handshake(agentAddress(this.config.storeDir));
194
+ if (socket) {
195
+ this.socket = socket;
196
+ socket.unref();
197
+ socket.on("error", () => socket?.destroy());
198
+ socket.on("close", () => {
199
+ if (this.socket === socket)
200
+ this.socket = undefined;
201
+ });
202
+ }
203
+ return socket;
204
+ }
205
+ /** A connection the agent accepted after the hello line, or undefined. */
206
+ handshake(address) {
207
+ const target = address.startsWith("unix:")
208
+ ? { path: address.slice("unix:".length) }
209
+ : address.startsWith("tcp:")
210
+ ? hostPort(address.slice("tcp:".length))
211
+ : undefined;
212
+ if (!target)
213
+ return Promise.resolve(undefined);
214
+ return new Promise((done) => {
215
+ const socket = connect(target);
216
+ const fail = () => {
217
+ socket.destroy();
218
+ done(undefined);
219
+ };
220
+ socket.setTimeout(500, fail);
221
+ socket.once("error", fail);
222
+ socket.once("connect", () => {
223
+ // The agent finds the issues with this project's limits.
224
+ // It can EXPLAIN a query on the app's database for the console.
225
+ const hello = {
226
+ protocol: PROTOCOL,
227
+ max_traces: this.config.maxTraces,
228
+ thresholds: this.config.thresholds,
229
+ service: this.config.service,
230
+ capabilities: ["explain"],
231
+ };
232
+ socket.write(`${JSON.stringify(hello)}\n`);
233
+ });
234
+ let received = Buffer.alloc(0);
235
+ socket.on("data", function onData(chunk) {
236
+ received = Buffer.concat([received, chunk]);
237
+ const newline = received.indexOf(10);
238
+ if (newline < 0)
239
+ return;
240
+ socket.off("data", onData);
241
+ socket.setTimeout(0);
242
+ socket.removeListener("error", fail);
243
+ const reply = JSON.parse(received.subarray(0, newline).toString("utf8"));
244
+ if (reply.ok) {
245
+ // The agent's requests come down the same connection, a frame each (EXPLAIN on the app's database): what
246
+ // came with the answer is the start of the first.
247
+ readFrames(socket, received.subarray(newline + 1), (request) => void answer(socket, request));
248
+ done(socket);
249
+ }
250
+ else {
251
+ warn(`the agent refused: ${reply.error}`);
252
+ fail();
253
+ }
254
+ });
255
+ });
256
+ }
257
+ }
258
+ function hostPort(value) {
259
+ const at = value.lastIndexOf(":");
260
+ return { host: value.slice(0, at).replace(/^\[|\]$/g, ""), port: Number(value.slice(at + 1)) };
261
+ }
262
+ function readFrames(socket, start, onFrame) {
263
+ let pending = Buffer.alloc(0);
264
+ const read = (chunk) => {
265
+ pending = Buffer.concat([pending, chunk]);
266
+ while (pending.length >= 4 && pending.length >= 4 + pending.readUInt32BE(0)) {
267
+ const length = pending.readUInt32BE(0);
268
+ const body = pending.subarray(4, 4 + length).toString("utf8");
269
+ pending = pending.subarray(4 + length);
270
+ try {
271
+ onFrame(JSON.parse(body));
272
+ }
273
+ catch {
274
+ // A frame this SDK can't read: the agent times its request out.
275
+ }
276
+ }
277
+ };
278
+ socket.on("data", read);
279
+ read(start);
280
+ }
281
+ /** The agent's request run, and its answer sent back up: the rows, or what went wrong. */
282
+ async function answer(socket, request) {
283
+ if (!request.explain)
284
+ return;
285
+ let reply;
286
+ try {
287
+ reply = { answer: request.id, ...(await explain(request.explain)) };
288
+ }
289
+ catch (error) {
290
+ reply = { answer: request.id, error: error instanceof Error ? error.message : String(error) };
291
+ }
292
+ const data = Buffer.from(JSON.stringify(reply));
293
+ const length = Buffer.alloc(4);
294
+ length.writeUInt32BE(data.length);
295
+ if (!socket.destroyed)
296
+ socket.write(Buffer.concat([length, data]));
297
+ }
298
+ //# sourceMappingURL=agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
package/dist/api.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ import { type Attributes } from "@opentelemetry/api";
2
+ export interface TraceOptions {
3
+ /** `command` (a script, a cron job; the default) or `task` (a job a worker took). */
4
+ kind?: "command" | "task";
5
+ /** Attributes of the trace: task.queue, task.id and the like. */
6
+ attrs?: Attributes;
7
+ }
8
+ /** Runs the function as a trace of its own: a script, a scheduled job, a worker's job. Inside a trace, a step. */
9
+ export declare function trace<T>(name: string, fn: () => T | Promise<T>, options?: TraceOptions): Promise<Awaited<T>>;
10
+ /** Runs the function as a step of the current trace, with its SQL, calls and logs inside it. */
11
+ export declare function span<T>(name: string, fn: () => T | Promise<T>, attrs?: Attributes): Promise<Awaited<T>>;