@tracepanel/js 0.3.3 → 0.4.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 (74) hide show
  1. package/README.md +71 -4
  2. package/dist/agent.d.ts +25 -2
  3. package/dist/agent.js +75 -24
  4. package/dist/agent.js.map +1 -1
  5. package/dist/body.d.ts +32 -0
  6. package/dist/body.js +314 -0
  7. package/dist/body.js.map +1 -0
  8. package/dist/browser.d.ts +2 -2
  9. package/dist/browser.js +15 -7
  10. package/dist/browser.js.map +1 -1
  11. package/dist/bullmq.d.ts +76 -1
  12. package/dist/bullmq.js +215 -6
  13. package/dist/bullmq.js.map +1 -1
  14. package/dist/bun.js +81 -11
  15. package/dist/bun.js.map +1 -1
  16. package/dist/config.d.ts +20 -0
  17. package/dist/config.js +13 -0
  18. package/dist/config.js.map +1 -1
  19. package/dist/console.d.ts +5 -0
  20. package/dist/console.js +16 -0
  21. package/dist/console.js.map +1 -1
  22. package/dist/express.js +94 -23
  23. package/dist/express.js.map +1 -1
  24. package/dist/headers.js +54 -8
  25. package/dist/headers.js.map +1 -1
  26. package/dist/inspector.d.ts +9 -0
  27. package/dist/inspector.js +47 -0
  28. package/dist/inspector.js.map +1 -0
  29. package/dist/locals.d.ts +51 -0
  30. package/dist/locals.js +269 -0
  31. package/dist/locals.js.map +1 -0
  32. package/dist/loggers.d.ts +50 -0
  33. package/dist/loggers.js +201 -0
  34. package/dist/loggers.js.map +1 -0
  35. package/dist/model.d.ts +23 -2
  36. package/dist/model.js.map +1 -1
  37. package/dist/next-widget.d.ts +13 -2
  38. package/dist/next-widget.js +16 -6
  39. package/dist/next-widget.js.map +1 -1
  40. package/dist/next.d.ts +6 -0
  41. package/dist/next.js +63 -2
  42. package/dist/next.js.map +1 -1
  43. package/dist/nuxt-plugin.d.ts +1 -0
  44. package/dist/nuxt-plugin.js +6 -0
  45. package/dist/nuxt-plugin.js.map +1 -1
  46. package/dist/processor.d.ts +23 -2
  47. package/dist/processor.js +65 -17
  48. package/dist/processor.js.map +1 -1
  49. package/dist/profile.d.ts +63 -0
  50. package/dist/profile.js +300 -0
  51. package/dist/profile.js.map +1 -0
  52. package/dist/proxy.d.ts +22 -0
  53. package/dist/proxy.js +72 -0
  54. package/dist/proxy.js.map +1 -0
  55. package/dist/request.d.ts +61 -0
  56. package/dist/request.js +244 -0
  57. package/dist/request.js.map +1 -0
  58. package/dist/runtime.d.ts +9 -0
  59. package/dist/runtime.js.map +1 -1
  60. package/dist/session.d.ts +10 -0
  61. package/dist/session.js +45 -0
  62. package/dist/session.js.map +1 -0
  63. package/dist/setup.js +10 -1
  64. package/dist/setup.js.map +1 -1
  65. package/dist/stack.d.ts +27 -3
  66. package/dist/stack.js +106 -9
  67. package/dist/stack.js.map +1 -1
  68. package/dist/vite.d.ts +1 -1
  69. package/dist/vite.js +2 -45
  70. package/dist/vite.js.map +1 -1
  71. package/dist/widget.d.ts +24 -0
  72. package/dist/widget.js +66 -3
  73. package/dist/widget.js.map +1 -1
  74. package/package.json +17 -5
@@ -0,0 +1,244 @@
1
+ // What a request carried, as attributes of its trace: headers, query, cookies, the body the app read, the response's
2
+ // headers; and the cookie that tells the widget which requests redirected to the page it is on.
3
+ //
4
+ // They stay on this machine (the agent keeps them out of what it sends anywhere), and the values under a sensitive
5
+ // name are masked before they are recorded. A body is only what the app read itself, up to MAX_BODY characters: a
6
+ // body parser's `req.body`, or the bytes the app's own reads took from the request's stream (body.ts). Tracepanel
7
+ // never reads a request's stream for the app.
8
+ /** How much of a body is kept, in characters. */
9
+ export const MAX_BODY = 16_384;
10
+ export const MASK = "********";
11
+ const SENSITIVE = /pass(word|wd)?|secret|token|api_?key|auth|cookie|session|csrf|signature|private/i;
12
+ /** A response's cookies are a session's keys, whatever their name. */
13
+ const ALWAYS_MASKED = new Set(["set-cookie"]);
14
+ /** A form as people read it: encoded, but its spaces, masks and file notes as they are. */
15
+ const READABLE = /^[A-Za-z0-9_.~*<>,. /:@-]$/;
16
+ /** A query's values: encoded as a form's, `+` for a space, masks and paths as they are. */
17
+ const QUERY_SAFE = /^[A-Za-z0-9_.~*/:-]$/;
18
+ /** A name as one segment of an attribute's key (content-type -> content_type), as the trace format has keys. */
19
+ export function key(name) {
20
+ return name.toLowerCase().replace(/[^a-z0-9_]/g, "_") || "_";
21
+ }
22
+ /** Whether a secret goes under the name: x-api-key, csrftoken, sessionid, password. */
23
+ export function sensitive(name) {
24
+ return SENSITIVE.test(name.replaceAll("-", "_"));
25
+ }
26
+ /** A header's value as one string: several values joined, as Node joins them. */
27
+ export function headerValue(value) {
28
+ if (value === undefined || value === null)
29
+ return undefined;
30
+ return Array.isArray(value) ? value.map(String).join(", ") : String(value);
31
+ }
32
+ function headers(prefix, entries) {
33
+ const found = {};
34
+ for (const [name, raw] of entries) {
35
+ const value = headerValue(raw);
36
+ if (!value)
37
+ continue;
38
+ const lowered = name.toLowerCase();
39
+ const attr = `${prefix}${key(lowered)}`;
40
+ if (ALWAYS_MASKED.has(lowered) || sensitive(lowered))
41
+ found[attr] = MASK;
42
+ else
43
+ found[attr] = attr in found ? `${found[attr]}, ${value}` : value;
44
+ }
45
+ return found;
46
+ }
47
+ /** http.request.header.<name>, the name as a key; Cookie is read as cookies instead. */
48
+ export function requestHeaders(entries) {
49
+ return headers("http.request.header.", [...entries].filter(([name]) => name.toLowerCase() !== "cookie"));
50
+ }
51
+ /** http.response.header.<name>: several values joined, Set-Cookie masked. */
52
+ export function responseHeaders(entries) {
53
+ return headers("http.response.header.", entries);
54
+ }
55
+ /** Percent-encoded but for the characters `safe` keeps; a space as `+` when `plus`. */
56
+ function quote(text, safe, plus = false) {
57
+ let out = "";
58
+ for (const char of text) {
59
+ if (plus && char === " ")
60
+ out += "+";
61
+ else if (safe.test(char))
62
+ out += char;
63
+ else
64
+ for (const byte of Buffer.from(char))
65
+ out += `%${byte.toString(16).toUpperCase().padStart(2, "0")}`;
66
+ }
67
+ return out;
68
+ }
69
+ /** url.query: the query string, the values of sensitive parameters masked. */
70
+ export function query(search) {
71
+ const raw = search.startsWith("?") ? search.slice(1) : search;
72
+ if (!raw)
73
+ return {};
74
+ const pairs = [...new URLSearchParams(raw)].map(([name, value]) => `${quote(name, QUERY_SAFE, true)}=${quote(sensitive(name) ? MASK : value, QUERY_SAFE, true)}`);
75
+ return { "url.query": pairs.join("&") };
76
+ }
77
+ /** The Cookie header's cookies by name, as they came. */
78
+ export function parseCookies(header) {
79
+ const found = [];
80
+ for (const part of header.split(";")) {
81
+ const at = part.indexOf("=");
82
+ const name = part.slice(0, at).trim();
83
+ if (at >= 0 && name)
84
+ found.push([name, part.slice(at + 1).trim()]);
85
+ }
86
+ return found;
87
+ }
88
+ /** http.request.cookie.<name>, from the Cookie header; a session id or a CSRF token masked. */
89
+ export function cookies(header) {
90
+ return Object.fromEntries(parseCookies(header).map(([name, value]) => [`http.request.cookie.${key(name)}`, sensitive(name) ? MASK : value]));
91
+ }
92
+ /** What the request carried before the app read it: its headers, query and cookies. */
93
+ export function requestAttrs(requested, search) {
94
+ return {
95
+ ...requestHeaders(Object.entries(requested)),
96
+ ...query(search),
97
+ ...cookies(headerValue(requested.cookie) ?? ""),
98
+ };
99
+ }
100
+ /** The query string of a request's URL (/books?page=2 -> ?page=2). */
101
+ export function searchOf(url) {
102
+ const at = url.indexOf("?");
103
+ return at < 0 ? "" : url.slice(at);
104
+ }
105
+ export function cut(text) {
106
+ if (text.length <= MAX_BODY)
107
+ return { "http.request.body": text };
108
+ return { "http.request.body": text.slice(0, MAX_BODY), "http.request.body.truncated": true };
109
+ }
110
+ /** A JSON value with the values under sensitive keys masked, at any depth. */
111
+ export function masked(value) {
112
+ if (Array.isArray(value))
113
+ return value.map(masked);
114
+ if (value !== null && typeof value === "object") {
115
+ return Object.fromEntries(Object.entries(value).map(([name, item]) => [name, sensitive(name) ? MASK : masked(item)]));
116
+ }
117
+ return value;
118
+ }
119
+ /** A form's fields as they would have been sent, sensitive values masked, then its files by name and size. */
120
+ export function formText(fields, files = []) {
121
+ const pairs = [];
122
+ for (const [name, value] of fields) {
123
+ if (sensitive(name))
124
+ pairs.push([name, MASK]);
125
+ else if (Array.isArray(value))
126
+ for (const item of value)
127
+ pairs.push([name, plain(item)]);
128
+ else
129
+ pairs.push([name, plain(value)]);
130
+ }
131
+ for (const [field, name, size] of files)
132
+ pairs.push([field, `<file ${name}, ${size} bytes>`]);
133
+ return pairs.map(([name, value]) => `${quote(name, READABLE)}=${quote(value, READABLE)}`).join("&");
134
+ }
135
+ /** A form's value as text: a nested one (qs's `a[b]=c`) as JSON, its secrets masked. */
136
+ function plain(value) {
137
+ return value !== null && typeof value === "object" ? JSON.stringify(masked(value)) : String(value ?? "");
138
+ }
139
+ export function kindOf(contentType) {
140
+ return (contentType.split(";", 1)[0] ?? "").trim().toLowerCase();
141
+ }
142
+ function isJson(kind) {
143
+ return kind === "application/json" || kind.endsWith("+json");
144
+ }
145
+ function isForm(kind) {
146
+ return kind === "application/x-www-form-urlencoded" || kind === "multipart/form-data";
147
+ }
148
+ /** A body read as text (a text or raw parser's): JSON and forms with secrets masked, text as it is, binary not. */
149
+ function textBody(contentType, text) {
150
+ const kind = kindOf(contentType);
151
+ if (!text)
152
+ return {};
153
+ if (isJson(kind)) {
154
+ try {
155
+ return cut(JSON.stringify(masked(JSON.parse(text))));
156
+ }
157
+ catch {
158
+ return cut(text);
159
+ }
160
+ }
161
+ if (kind === "application/x-www-form-urlencoded")
162
+ return cut(formText(new URLSearchParams(text)));
163
+ if (kind.startsWith("text/") || kind === "application/xml" || kind === "application/graphql")
164
+ return cut(text);
165
+ return {};
166
+ }
167
+ /**
168
+ * http.request.body from the bytes the app read, as far as it read them: JSON and forms with secrets masked, text as
169
+ * it is, binary not. `complete` is false when only the start of the body is at hand: JSON or a form can't be read
170
+ * (nor its secrets masked) from a part of it.
171
+ */
172
+ export function readBody(contentType, data, complete) {
173
+ const kind = kindOf(contentType);
174
+ if (data.length === 0 || (!complete && (isJson(kind) || isForm(kind))))
175
+ return {};
176
+ return textBody(contentType, Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString("utf8"));
177
+ }
178
+ function uploads(file, files) {
179
+ const all = [];
180
+ if (file)
181
+ all.push(file);
182
+ if (Array.isArray(files))
183
+ all.push(...files);
184
+ else if (files && typeof files === "object")
185
+ for (const list of Object.values(files))
186
+ all.push(...[list].flat());
187
+ return all
188
+ .filter((item) => item !== null && typeof item === "object")
189
+ .map((item) => [String(item.fieldname ?? ""), String(item.originalname ?? ""), Number(item.size ?? 0)]);
190
+ }
191
+ /**
192
+ * http.request.body from what the app's body parser read: JSON and forms with secrets masked, text as it is, an
193
+ * upload's files by name and size. Nothing when no parser read it.
194
+ */
195
+ export function parsedBody({ contentType, body, file, files }) {
196
+ const kind = kindOf(contentType);
197
+ const sent = uploads(file, files);
198
+ if (typeof body === "string")
199
+ return textBody(contentType, body);
200
+ if (body instanceof Uint8Array)
201
+ return textBody(contentType, Buffer.from(body).toString("utf8"));
202
+ if (body === undefined || body === null)
203
+ return sent.length > 0 ? cut(formText([], sent)) : {};
204
+ if (typeof body !== "object")
205
+ return cut(JSON.stringify(body));
206
+ if (isForm(kind)) {
207
+ const text = formText(Object.entries(body), sent);
208
+ return text ? cut(text) : {};
209
+ }
210
+ return cut(JSON.stringify(masked(body)));
211
+ }
212
+ /** Whether a request came with a body: one declared by its size, or sent in chunks. */
213
+ export function hasBody(requested) {
214
+ const length = Number(headerValue(requested["content-length"]) ?? 0);
215
+ return length > 0 || (headerValue(requested["transfer-encoding"]) ?? "").toLowerCase().includes("chunked");
216
+ }
217
+ /**
218
+ * The requests that answered a page's navigation with a redirect, by id, on the way to the page the widget will see:
219
+ * its panel lists them before that page (a form's POST, then the page it redirected to).
220
+ */
221
+ export const REDIRECTS = "tracepanel_redirect";
222
+ /** How many redirects in a row are kept: a loop of them doesn't grow the cookie without end. */
223
+ const MAX_REDIRECTS = 5;
224
+ /** Whether the browser asked for a page to show (a link, a form): not fetch() or an image. */
225
+ export function navigated(requested) {
226
+ const mode = headerValue(requested["sec-fetch-mode"]);
227
+ return mode !== undefined ? mode === "navigate" : (headerValue(requested.accept) ?? "").includes("text/html");
228
+ }
229
+ /** The Set-Cookie naming this request too, for a redirect that answers a page's navigation; undefined otherwise. */
230
+ export function redirectCookie(id, status, requested) {
231
+ if (status < 300 || status >= 400 || !navigated(requested))
232
+ return undefined;
233
+ const sent = parseCookies(headerValue(requested.cookie) ?? "").find(([name]) => name === REDIRECTS)?.[1] ?? "";
234
+ let before;
235
+ try {
236
+ before = decodeURIComponent(sent).split(",").filter(Boolean);
237
+ }
238
+ catch {
239
+ before = [];
240
+ }
241
+ const value = encodeURIComponent([...before, id].slice(-MAX_REDIRECTS).join(","));
242
+ return `${REDIRECTS}=${value}; Max-Age=30; Path=/; SameSite=Lax`;
243
+ }
244
+ //# sourceMappingURL=request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request.js","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"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"}
package/dist/runtime.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { ConsoleUrl, Starter } from "./agent.js";
2
+ import type { BrowserOptions } from "./browser.js";
2
3
  import { type Config } from "./config.js";
3
4
  import { TracepanelProcessor } from "./processor.js";
4
5
  import { type Badge, type BadgeRequest } from "./widget.js";
@@ -12,6 +13,14 @@ export interface Runtime {
12
13
  * OpenTelemetry (a Next.js layout) can read it without importing any of it.
13
14
  */
14
15
  badge(request: BadgeRequest): Badge | undefined;
16
+ /**
17
+ * The console on the app's own origin (Next.js serves it at /__tracepanel__/): where the widget loads from, and the
18
+ * page's recording as an inline script. Unset where the widget loads from the agent's own address.
19
+ */
20
+ onOrigin?: {
21
+ prefix: string;
22
+ recording(options: BrowserOptions): string;
23
+ };
15
24
  }
16
25
  /** The process's Tracepanel, made on first use; undefined outside development. */
17
26
  export declare function runtime(): Runtime | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAE3F,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,EAAe,UAAU,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAiC,KAAK,EAAE,MAAM,aAAa,CAAC;AAcnE,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;AAG7C,kFAAkF;AAClF,MAAM,UAAU,OAAO;IACrB,MAAM,MAAM,GAAG,UAAoB,CAAC;IACpC,IAAI,MAAM,CAAC,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;IACpC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IACtC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAG,IAAI,mBAAmB,CAAC,MAAM,EAAE,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC/E,MAAM,IAAI,GAAY;QACpB,MAAM;QACN,SAAS;QACT,OAAO;QACP,OAAO,EAAE,IAAI,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC;QACxC,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC;KACzC,CAAC;IACF,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;IACnB,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAE3F,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAEzD,OAAO,EAAe,UAAU,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAiC,KAAK,EAAE,MAAM,aAAa,CAAC;AAmBnE,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;AAG7C,kFAAkF;AAClF,MAAM,UAAU,OAAO;IACrB,MAAM,MAAM,GAAG,UAAoB,CAAC;IACpC,IAAI,MAAM,CAAC,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;IACpC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IACtC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAG,IAAI,mBAAmB,CAAC,MAAM,EAAE,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC/E,MAAM,IAAI,GAAY;QACpB,MAAM;QACN,SAAS;QACT,OAAO;QACP,OAAO,EAAE,IAAI,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC;QACxC,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC;KACzC,CAAC;IACF,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;IACnB,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { Attrs } from "./model.js";
2
+ /** session.<key> of a session's entries, under a prefix of its own when there are several (session.<name>.<key>). */
3
+ export declare function sessionAttrs(entries: Iterable<[string, unknown]>, prefix?: string): Attrs;
4
+ /**
5
+ * The session express-session or cookie-session put on an Express request, as the handler left it; none without one.
6
+ * express-session's `cookie` is the session cookie's settings, not what the app keeps in it.
7
+ */
8
+ export declare function expressSession(session: unknown): Attrs;
9
+ /** h3's sessions as the request left them (`event.context.sessions`), by name: those `useSession()` read. */
10
+ export declare function h3Sessions(sessions: unknown): Attrs;
@@ -0,0 +1,45 @@
1
+ // The request's session as the app left it, for the panel's Request tab, as the Python SDK records it: session.<key>,
2
+ // each value as text, cut as an error's locals are, masked under a sensitive name and at any depth below one.
3
+ //
4
+ // Only a session the app already read is recorded, from where its framework keeps it decoded: express-session's and
5
+ // cookie-session's `req.session` in Express and Nest, h3's `useSession()` in Nuxt. Nothing here decodes a cookie: a
6
+ // sealed one (iron-session, Auth.js, h3's own) takes the app's secret.
7
+ import { repr } from "./locals.js";
8
+ import { key, MASK, masked } from "./request.js";
9
+ // Keys that say who is signed in, kept as they are: Passport's `{ user }`, under a name that reads as a password's.
10
+ const PLAIN = new Set(["passport"]);
11
+ // Keys that hold what only the server may see, whatever is inside: nuxt-auth-utils' `secure`.
12
+ const MASKED = new Set(["secure"]);
13
+ function value(name, item) {
14
+ if (MASKED.has(name))
15
+ return MASK;
16
+ return repr(PLAIN.has(name) ? "" : name, masked(item));
17
+ }
18
+ /** session.<key> of a session's entries, under a prefix of its own when there are several (session.<name>.<key>). */
19
+ export function sessionAttrs(entries, prefix = "session.") {
20
+ const found = {};
21
+ for (const [name, item] of entries) {
22
+ if (typeof item !== "function")
23
+ found[`${prefix}${key(name)}`] = value(name, item);
24
+ }
25
+ return found;
26
+ }
27
+ /**
28
+ * The session express-session or cookie-session put on an Express request, as the handler left it; none without one.
29
+ * express-session's `cookie` is the session cookie's settings, not what the app keeps in it.
30
+ */
31
+ export function expressSession(session) {
32
+ if (session === null || typeof session !== "object")
33
+ return {};
34
+ const own = typeof session.regenerate === "function";
35
+ return sessionAttrs(Object.entries(session).filter(([name]) => !(own && name === "cookie")));
36
+ }
37
+ /** h3's sessions as the request left them (`event.context.sessions`), by name: those `useSession()` read. */
38
+ export function h3Sessions(sessions) {
39
+ if (sessions === null || typeof sessions !== "object")
40
+ return {};
41
+ const read = Object.entries(sessions).filter((entry) => typeof entry[1]?.data === "object" && entry[1].data !== null);
42
+ const named = read.length > 1;
43
+ return Object.assign({}, ...read.map(([name, session]) => sessionAttrs(Object.entries(session.data), named ? `session.${key(name)}.` : "session.")));
44
+ }
45
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,sHAAsH;AACtH,8GAA8G;AAC9G,EAAE;AACF,oHAAoH;AACpH,oHAAoH;AACpH,uEAAuE;AAEvE,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAEnC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEjD,oHAAoH;AACpH,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;AACpC,8FAA8F;AAC9F,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;AAEnC,SAAS,KAAK,CAAC,IAAY,EAAE,IAAa;IACxC,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAClC,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;AACzD,CAAC;AAED,qHAAqH;AACrH,MAAM,UAAU,YAAY,CAAC,OAAoC,EAAE,MAAM,GAAG,UAAU;IACpF,MAAM,KAAK,GAAU,EAAE,CAAC;IACxB,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,OAAO,EAAE,CAAC;QACnC,IAAI,OAAO,IAAI,KAAK,UAAU;YAAE,KAAK,CAAC,GAAG,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACrF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,OAAgB;IAC7C,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IAC/D,MAAM,GAAG,GAAG,OAAQ,OAAoC,CAAC,UAAU,KAAK,UAAU,CAAC;IACnF,OAAO,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC;AAC/F,CAAC;AAED,6GAA6G;AAC7G,MAAM,UAAU,UAAU,CAAC,QAAiB;IAC1C,IAAI,QAAQ,KAAK,IAAI,IAAI,OAAO,QAAQ,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IACjE,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,CAC1C,CAAC,KAAK,EAAuC,EAAE,CAAC,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAC7G,CAAC;IACF,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IAC9B,OAAO,MAAM,CAAC,MAAM,CAClB,EAAE,EACF,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE,CAC9B,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,WAAW,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CACzF,CACF,CAAC;AACJ,CAAC"}
package/dist/setup.js CHANGED
@@ -20,8 +20,10 @@ import { BullMQInstrumentation } from "./bullmq.js";
20
20
  import { captureConsole } from "./console.js";
21
21
  import { DatabasesInstrumentation } from "./explain.js";
22
22
  import { traceFetch } from "./fetch.js";
23
+ import { PinoLogs, WinstonLogs } from "./loggers.js";
23
24
  import { NestLogs } from "./logs.js";
24
25
  import { traceBunCalls } from "./outgoing.js";
26
+ import { ATTRIBUTE as PROFILE, requested } from "./profile.js";
25
27
  import { runtime } from "./runtime.js";
26
28
  import { TransactionsInstrumentation } from "./transactions.js";
27
29
  const STARTED = Symbol.for("tracepanel.started");
@@ -80,6 +82,8 @@ export function start(options = {}) {
80
82
  // A Nest app runs on Express: Nest's spans name the controller and method that handled the request.
81
83
  nest(),
82
84
  new NestLogs(tracepanel.processor),
85
+ new PinoLogs(tracepanel.processor),
86
+ new WinstonLogs(tracepanel.processor),
83
87
  new PgInstrumentation({ enhancedDatabaseReporting: true }),
84
88
  new MySQL2Instrumentation(),
85
89
  new IORedisInstrumentation(),
@@ -89,7 +93,8 @@ export function start(options = {}) {
89
93
  new PrismaInstrumentation(),
90
94
  // BEGIN … COMMIT on a connection as a transaction span: after pg's and mysql2's, so its statements are inside.
91
95
  new TransactionsInstrumentation(),
92
- new BullMQInstrumentation(),
96
+ // Its jobs' task events too, for the console's Live list.
97
+ new BullMQInstrumentation(tracepanel.processor),
93
98
  // The databases the app queries, for the console's EXPLAIN.
94
99
  new DatabasesInstrumentation(),
95
100
  // fetch(): Node's own, through undici (Bun's: traceBunCalls() below).
@@ -101,6 +106,10 @@ export function start(options = {}) {
101
106
  // A CORS preflight (the browser asking before a call with a traceparent) is the browser's, not the app's work.
102
107
  ignoreIncomingRequestHook: (request) => options.httpServer === false ||
103
108
  (request.method === "OPTIONS" && request.headers["access-control-request-method"] !== undefined),
109
+ // A request that asks to be profiled says so on its span, as it starts.
110
+ startIncomingSpanHook: (request) => tracepanel.config.profileEnabled && "headers" in request && requested(request.headers)
111
+ ? { [PROFILE]: true }
112
+ : {},
104
113
  }),
105
114
  ];
106
115
  registerInstrumentations({ instrumentations });
package/dist/setup.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"setup.js","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"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"}
package/dist/stack.d.ts CHANGED
@@ -1,13 +1,37 @@
1
1
  import type { Frame } from "./model.js";
2
+ /** Whether a file is this package's own. */
3
+ export declare function ownFile(file: string): boolean;
2
4
  export interface StackFrame {
3
5
  function: string;
4
6
  file: string;
5
7
  line: number;
6
8
  }
7
- export declare function parseStack(stack: string | undefined): StackFrame[];
9
+ /**
10
+ * The frames of a V8 stack, innermost first. `mapped`: a bundle's frames as the app's sources, through its source
11
+ * map; without it, where the code that ran is, as the debugger sees it.
12
+ */
13
+ export declare function parseStack(stack: string | undefined, mapped?: boolean): StackFrame[];
14
+ /** Whether a line of a V8 stack is an `async` one: a caller waiting for the frame above, not running under it. */
15
+ export declare function asyncFrames(stack: string | undefined): boolean[];
16
+ /** Where a function is in the app's source: a file URL or path and a line and column (1-based) of the code that ran. */
17
+ export declare function place(url: string, line: number, column: number): StackFrame;
8
18
  /** The project's own code: under the root, not a dependency, not a bundle, not Node's, not Tracepanel's. */
9
19
  export declare function inApp(file: string, root: string): boolean;
10
20
  /** `file:line in function` of the innermost frame of the app's own code on the current stack, or null. */
11
21
  export declare function origin(root: string): string | null;
12
- /** An error's frames, outermost first as the console shows every SDK's, with the source of the app's own. */
13
- export declare function frames(stack: string | undefined, root: string): Frame[];
22
+ /**
23
+ * Where the call being made now was made. `origin`: `file:line in function` of the innermost frame of the app's own
24
+ * code. Without one, `caller`: the library code that asked for it, the first frame past the driver, its ORM and
25
+ * Tracepanel (`connect-pg-simple/index.js:… in PGStore.get`), as the Python SDK's code.caller names it.
26
+ */
27
+ export declare function madeAt(root: string): {
28
+ origin: string | null;
29
+ caller: string | null;
30
+ };
31
+ /**
32
+ * An error's frames, outermost first as the console shows every SDK's, with the source of the app's own. `locals`:
33
+ * the local variables of the app's frames, by the frame's place in the stack (innermost first), when they were read.
34
+ */
35
+ export declare function frames(stack: string | undefined, root: string, locals?: readonly (Record<string, string> | undefined)[]): Frame[];
36
+ /** A library's file as its package names it (express/lib/router/index.js), Node's own as Node names them. */
37
+ export declare function libraryPath(file: string): string;
package/dist/stack.js CHANGED
@@ -1,4 +1,5 @@
1
- // The app's own code in a V8 stack: where a query was made (code.origin), and an error's frames with their source.
1
+ // The app's own code in a V8 stack: where a query was made (code.origin), or the library code that asked for one
2
+ // none of the app's code made (code.caller); and an error's frames with their source.
2
3
  import { readFileSync } from "node:fs";
3
4
  import nodeModule, {} from "node:module";
4
5
  import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
@@ -8,7 +9,15 @@ const CONTEXT_LINES = 5;
8
9
  const FRAME = /^\s*at (?:async )?(?:(.+?) \()?(.+?):(\d+):(\d+)\)?$/;
9
10
  // This package's own files are never the app's.
10
11
  const OWN = fileURLToPath(new URL(".", import.meta.url));
11
- export function parseStack(stack) {
12
+ /** Whether a file is this package's own. */
13
+ export function ownFile(file) {
14
+ return file.startsWith(OWN);
15
+ }
16
+ /**
17
+ * The frames of a V8 stack, innermost first. `mapped`: a bundle's frames as the app's sources, through its source
18
+ * map; without it, where the code that ran is, as the debugger sees it.
19
+ */
20
+ export function parseStack(stack, mapped = true) {
12
21
  const frames = [];
13
22
  for (const line of (stack ?? "").split("\n").slice(1)) {
14
23
  const match = FRAME.exec(line);
@@ -16,10 +25,19 @@ export function parseStack(stack) {
16
25
  continue;
17
26
  const [, fn, location, lineno, column] = match;
18
27
  const file = location?.startsWith("file://") ? fileURLToPath(location) : (location ?? "");
19
- frames.push(original({ function: fn ?? "<anonymous>", file, line: Number(lineno) }, Number(column)));
28
+ const frame = { function: fn ?? "<anonymous>", file, line: Number(lineno) };
29
+ frames.push(mapped ? original(frame, Number(column)) : frame);
20
30
  }
21
31
  return frames;
22
32
  }
33
+ /** Whether a line of a V8 stack is an `async` one: a caller waiting for the frame above, not running under it. */
34
+ export function asyncFrames(stack) {
35
+ return (stack ?? "")
36
+ .split("\n")
37
+ .slice(1)
38
+ .filter((line) => FRAME.test(line))
39
+ .map((line) => /^\s*at async /.test(line));
40
+ }
23
41
  // A dev server's bundle (`next dev`'s .next/dev/server/chunks/*.js, `nuxt dev`'s .nuxt/dev/index.mjs) runs the app's
24
42
  // code: its frames are mapped back to the app's sources through the bundle's source map. The frames of a bundle
25
43
  // without one are the bundler's glue, never the app's.
@@ -75,6 +93,12 @@ function sourcePath(source, mapFile, sourceRoot) {
75
93
  return resolve(process.cwd(), webpack[1] ?? "");
76
94
  return isAbsolute(source) ? source : resolve(dirname(mapFile), sourceRoot, source);
77
95
  }
96
+ /** Where a function is in the app's source: a file URL or path and a line and column (1-based) of the code that ran. */
97
+ export function place(url, line, column) {
98
+ const file = url.startsWith("file://") ? fileURLToPath(url) : url;
99
+ // This package's own code stays where it is: its source map leads out of it, to its TypeScript.
100
+ return ownFile(file) ? { function: "", file, line } : original({ function: "", file, line }, column);
101
+ }
78
102
  /** The frame in the app's source, when its file is a bundle with a source map. */
79
103
  function original(frame, column) {
80
104
  if (!frame.file || !isAbsolute(frame.file) || frame.file.includes(`${sep}node_modules${sep}`))
@@ -121,8 +145,73 @@ function deepStack() {
121
145
  }
122
146
  /** `file:line in function` of the innermost frame of the app's own code on the current stack, or null. */
123
147
  export function origin(root) {
124
- const frame = parseStack(deepStack()).find((candidate) => inApp(candidate.file, root));
125
- return frame ? `${relative(root, frame.file)}:${frame.line} in ${shortName(frame.function)}` : null;
148
+ return madeAt(root).origin;
149
+ }
150
+ // What makes a call rather than asks for one: the drivers, the ORMs and query builders on them, their pools, the
151
+ // instrumentation around them and the hooks that load it. Past these is the library code that wanted the call made
152
+ // (a session store, an admin panel, a job queue).
153
+ const CALL_LAYERS = new Set([
154
+ "@opentelemetry",
155
+ "@prisma",
156
+ "@mikro-orm",
157
+ "@redis",
158
+ "@tracepanel",
159
+ "drizzle-orm",
160
+ "generic-pool",
161
+ "import-in-the-middle",
162
+ "ioredis",
163
+ "knex",
164
+ "kysely",
165
+ "mongodb",
166
+ "mongoose",
167
+ "mysql",
168
+ "mysql2",
169
+ "objection",
170
+ "pg",
171
+ "pg-cursor",
172
+ "pg-pool",
173
+ "pg-protocol",
174
+ "prisma",
175
+ "redis",
176
+ "require-in-the-middle",
177
+ "sequelize",
178
+ "shimmer",
179
+ "tarn",
180
+ "tslib",
181
+ "typeorm",
182
+ ]);
183
+ /** The installed package a file is in (pg, @prisma/client's scope @prisma), or null for a file in none. */
184
+ function packageOf(file) {
185
+ const at = file.lastIndexOf(`${sep}node_modules${sep}`);
186
+ if (at < 0)
187
+ return null;
188
+ return file.slice(at + `${sep}node_modules${sep}`.length).split(sep)[0] ?? null;
189
+ }
190
+ /** Whether a frame is library code that may have asked for a call: not a driver's, Node's, Tracepanel's or glue. */
191
+ function asks(file, root) {
192
+ if (!isAbsolute(file) || ownFile(file))
193
+ return false;
194
+ const name = packageOf(file);
195
+ if (name !== null)
196
+ return !CALL_LAYERS.has(name);
197
+ // A bundle's frame that no source map led out of is the bundler's glue.
198
+ return !BUNDLES.some((dir) => file.startsWith(root + dir));
199
+ }
200
+ /**
201
+ * Where the call being made now was made. `origin`: `file:line in function` of the innermost frame of the app's own
202
+ * code. Without one, `caller`: the library code that asked for it, the first frame past the driver, its ORM and
203
+ * Tracepanel (`connect-pg-simple/index.js:… in PGStore.get`), as the Python SDK's code.caller names it.
204
+ */
205
+ export function madeAt(root) {
206
+ const stack = parseStack(deepStack());
207
+ const own = stack.find((frame) => inApp(frame.file, root));
208
+ if (own)
209
+ return { origin: `${relative(root, own.file)}:${own.line} in ${shortName(own.function)}`, caller: null };
210
+ const asked = stack.find((frame) => asks(frame.file, root));
211
+ return {
212
+ origin: null,
213
+ caller: asked ? `${libraryPath(asked.file)}:${asked.line} in ${shortName(asked.function)}` : null,
214
+ };
126
215
  }
127
216
  function shortName(fn) {
128
217
  return fn.replace(/^Object\.|^Module\./, "");
@@ -137,10 +226,13 @@ function sourceAround(file, line) {
137
226
  return { start: null, context: [] };
138
227
  }
139
228
  }
140
- /** An error's frames, outermost first as the console shows every SDK's, with the source of the app's own. */
141
- export function frames(stack, root) {
229
+ /**
230
+ * An error's frames, outermost first as the console shows every SDK's, with the source of the app's own. `locals`:
231
+ * the local variables of the app's frames, by the frame's place in the stack (innermost first), when they were read.
232
+ */
233
+ export function frames(stack, root, locals) {
142
234
  return parseStack(stack)
143
- .map((frame) => {
235
+ .map((frame, index) => {
144
236
  const own = inApp(frame.file, root);
145
237
  const { start, context } = own ? sourceAround(frame.file, frame.line) : { start: null, context: [] };
146
238
  return {
@@ -151,9 +243,14 @@ export function frames(stack, root) {
151
243
  in_app: own,
152
244
  context_start: start,
153
245
  context,
154
- locals: {},
246
+ locals: (own && locals?.[index]) || null,
155
247
  };
156
248
  })
157
249
  .reverse();
158
250
  }
251
+ /** A library's file as its package names it (express/lib/router/index.js), Node's own as Node names them. */
252
+ export function libraryPath(file) {
253
+ const at = file.lastIndexOf(`${sep}node_modules${sep}`);
254
+ return at >= 0 ? file.slice(at + `${sep}node_modules${sep}`.length) : file;
255
+ }
159
256
  //# sourceMappingURL=stack.js.map