@hasna/logs 0.3.26 → 0.3.27

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 (130) hide show
  1. package/README.md +33 -10
  2. package/dashboard/dist/assets/index-C0wZYq1m.js +53 -0
  3. package/dashboard/dist/assets/index-DGNrK5qb.css +1 -0
  4. package/dashboard/dist/index.html +14 -0
  5. package/dist/cli/index.js +8511 -177
  6. package/dist/count-bmj4r2zb.js +10 -0
  7. package/dist/{diagnose-e0w5rwbc.js → diagnose-3q5cy9ra.js} +2 -2
  8. package/dist/{export-c3eqjste.js → export-cngdb9fh.js} +1 -1
  9. package/dist/{http-zm3ph78w.js → http-r0xc3d2s.js} +79 -8
  10. package/dist/index-931pbyn5.js +141 -0
  11. package/dist/index-b5c72f1p.js +7 -0
  12. package/dist/{index-gc0zvs88.js → index-bnr19y0h.js} +596 -37
  13. package/dist/{index-7w7v7hnr.js → index-by1pdzbr.js} +14 -5
  14. package/dist/{index-3dr7d80h.js → index-e1930v9b.js} +12 -8
  15. package/dist/{index-eh9bkbpa.js → index-e72k53yq.js} +10 -2
  16. package/dist/{index-edn08m6f.js → index-gcd14q2f.js} +9 -6
  17. package/dist/index-hq6kzaah.js +26 -0
  18. package/dist/index-j34f36wy.js +5672 -0
  19. package/dist/index-p4dbdzx4.js +1849 -0
  20. package/dist/{index-5qznfyah.js → index-q27bgpr1.js} +1086 -1646
  21. package/dist/index-t3x838zw.js +2583 -0
  22. package/dist/{index-ww5ggfv3.js → index-zkb3z95a.js} +12 -9
  23. package/dist/index.js +2982 -22
  24. package/dist/{jobs-ypmmc2ma.js → jobs-hsgyhfvm.js} +2 -1
  25. package/dist/mcp/index.js +1473 -4286
  26. package/dist/{query-7jwj05er.js → query-c5a43zx3.js} +3 -2
  27. package/dist/server/index.js +2944 -417
  28. package/dist/storage.js +50 -0
  29. package/package.json +27 -8
  30. package/biome.json +0 -13
  31. package/bun.lock +0 -376
  32. package/dashboard/README.md +0 -73
  33. package/dashboard/bun.lock +0 -526
  34. package/dashboard/eslint.config.js +0 -23
  35. package/dashboard/index.html +0 -13
  36. package/dashboard/package.json +0 -32
  37. package/dashboard/src/App.css +0 -184
  38. package/dashboard/src/App.tsx +0 -49
  39. package/dashboard/src/api.ts +0 -33
  40. package/dashboard/src/assets/hero.png +0 -0
  41. package/dashboard/src/assets/react.svg +0 -1
  42. package/dashboard/src/assets/vite.svg +0 -1
  43. package/dashboard/src/index.css +0 -111
  44. package/dashboard/src/main.tsx +0 -10
  45. package/dashboard/src/pages/Alerts.tsx +0 -69
  46. package/dashboard/src/pages/Issues.tsx +0 -50
  47. package/dashboard/src/pages/Perf.tsx +0 -75
  48. package/dashboard/src/pages/Projects.tsx +0 -67
  49. package/dashboard/src/pages/Summary.tsx +0 -67
  50. package/dashboard/src/pages/Tail.tsx +0 -65
  51. package/dashboard/tsconfig.app.json +0 -28
  52. package/dashboard/tsconfig.json +0 -7
  53. package/dashboard/tsconfig.node.json +0 -26
  54. package/dashboard/vite.config.ts +0 -14
  55. package/dist/count-x3n7qg3c.js +0 -9
  56. package/dist/index-997bkzr2.js +0 -15
  57. package/dist/index-pen6t0yc.js +0 -10794
  58. package/sdk/package.json +0 -27
  59. package/sdk/src/index.ts +0 -143
  60. package/sdk/src/types.ts +0 -56
  61. package/src/cli/entrypoints.test.ts +0 -63
  62. package/src/cli/index.ts +0 -471
  63. package/src/db/index.test.ts +0 -33
  64. package/src/db/index.ts +0 -189
  65. package/src/db/migrations/001_alert_rules.ts +0 -21
  66. package/src/db/migrations/002_issues.ts +0 -21
  67. package/src/db/migrations/003_retention.ts +0 -15
  68. package/src/db/migrations/004_page_auth.ts +0 -13
  69. package/src/db/pg-migrations.ts +0 -167
  70. package/src/index.ts +0 -1
  71. package/src/lib/alerts.test.ts +0 -67
  72. package/src/lib/alerts.ts +0 -117
  73. package/src/lib/browser-script.test.ts +0 -35
  74. package/src/lib/browser-script.ts +0 -31
  75. package/src/lib/compare.test.ts +0 -52
  76. package/src/lib/compare.ts +0 -85
  77. package/src/lib/count.test.ts +0 -44
  78. package/src/lib/count.ts +0 -55
  79. package/src/lib/diagnose.test.ts +0 -55
  80. package/src/lib/diagnose.ts +0 -91
  81. package/src/lib/export.test.ts +0 -66
  82. package/src/lib/export.ts +0 -65
  83. package/src/lib/github.ts +0 -38
  84. package/src/lib/health.test.ts +0 -48
  85. package/src/lib/health.ts +0 -51
  86. package/src/lib/ingest.test.ts +0 -57
  87. package/src/lib/ingest.ts +0 -78
  88. package/src/lib/issues.test.ts +0 -79
  89. package/src/lib/issues.ts +0 -70
  90. package/src/lib/jobs.test.ts +0 -69
  91. package/src/lib/jobs.ts +0 -63
  92. package/src/lib/lighthouse.ts +0 -65
  93. package/src/lib/package-meta.test.ts +0 -43
  94. package/src/lib/package-meta.ts +0 -80
  95. package/src/lib/page-auth.test.ts +0 -54
  96. package/src/lib/page-auth.ts +0 -48
  97. package/src/lib/parse-time.test.ts +0 -37
  98. package/src/lib/parse-time.ts +0 -14
  99. package/src/lib/perf.test.ts +0 -45
  100. package/src/lib/perf.ts +0 -46
  101. package/src/lib/projects.test.ts +0 -73
  102. package/src/lib/projects.ts +0 -69
  103. package/src/lib/query.test.ts +0 -104
  104. package/src/lib/query.ts +0 -84
  105. package/src/lib/retention.test.ts +0 -42
  106. package/src/lib/retention.ts +0 -62
  107. package/src/lib/rotate.test.ts +0 -37
  108. package/src/lib/rotate.ts +0 -27
  109. package/src/lib/scanner.ts +0 -131
  110. package/src/lib/scheduler.ts +0 -63
  111. package/src/lib/session-context.ts +0 -28
  112. package/src/lib/summarize.test.ts +0 -38
  113. package/src/lib/summarize.ts +0 -23
  114. package/src/mcp/http.test.ts +0 -92
  115. package/src/mcp/http.ts +0 -135
  116. package/src/mcp/index.test.ts +0 -27
  117. package/src/mcp/index.ts +0 -444
  118. package/src/server/index.ts +0 -61
  119. package/src/server/routes/alerts.ts +0 -32
  120. package/src/server/routes/issues.ts +0 -43
  121. package/src/server/routes/jobs.ts +0 -32
  122. package/src/server/routes/logs.ts +0 -113
  123. package/src/server/routes/perf.ts +0 -23
  124. package/src/server/routes/projects.ts +0 -67
  125. package/src/server/routes/stream.ts +0 -43
  126. package/src/server/server.test.ts +0 -194
  127. package/src/types/index.ts +0 -119
  128. package/tsconfig.json +0 -22
  129. /package/dashboard/{public → dist}/favicon.svg +0 -0
  130. /package/dashboard/{public → dist}/icons.svg +0 -0
package/dist/index.js CHANGED
@@ -2,34 +2,172 @@
2
2
  import"./index-re3ntm60.js";
3
3
 
4
4
  // sdk/src/index.ts
5
+ var expressCaptureSymbol = Symbol.for("@hasna/logs.express.capture");
5
6
  var DEFAULT_URL = "http://localhost:3460";
6
7
 
7
8
  class LogsClient {
8
9
  url;
9
10
  projectId;
11
+ source;
12
+ environment;
13
+ releaseId;
14
+ appId;
15
+ machineId;
16
+ repoId;
17
+ processId;
18
+ runId;
19
+ sessionId;
10
20
  headers;
21
+ writeHeaders;
22
+ hasBrowserToken;
11
23
  constructor(opts = {}) {
12
24
  this.url = (opts.url ?? DEFAULT_URL).replace(/\/$/, "");
13
25
  this.projectId = opts.projectId;
26
+ this.source = opts.source ?? "sdk";
27
+ this.environment = opts.environment;
28
+ this.releaseId = opts.releaseId;
29
+ this.appId = opts.appId;
30
+ this.machineId = opts.machineId;
31
+ this.repoId = opts.repoId;
32
+ this.processId = opts.processId;
33
+ this.runId = opts.runId;
34
+ this.sessionId = opts.sessionId;
35
+ this.hasBrowserToken = Boolean(opts.browserToken);
14
36
  this.headers = { "Content-Type": "application/json" };
15
37
  if (opts.apiKey)
16
38
  this.headers["Authorization"] = `Bearer ${opts.apiKey}`;
39
+ this.writeHeaders = { ...this.headers };
40
+ if (opts.browserToken)
41
+ this.writeHeaders["X-Logs-Browser-Token"] = opts.browserToken;
17
42
  }
18
43
  async push(entry) {
19
44
  const res = await fetch(`${this.url}/api/logs`, {
20
45
  method: "POST",
21
- headers: this.headers,
46
+ headers: this.writeHeaders,
22
47
  body: JSON.stringify({ project_id: this.projectId, ...entry })
23
48
  });
24
- return res.json();
49
+ return readJson(res);
25
50
  }
26
51
  async pushBatch(entries) {
27
52
  const res = await fetch(`${this.url}/api/logs`, {
28
53
  method: "POST",
29
- headers: this.headers,
54
+ headers: this.writeHeaders,
30
55
  body: JSON.stringify(entries.map((e) => ({ project_id: this.projectId, ...e })))
31
56
  });
32
- return res.json();
57
+ return readJson(res);
58
+ }
59
+ async pushEvent(event) {
60
+ const res = await fetch(`${this.url}/api/events`, {
61
+ method: "POST",
62
+ headers: this.writeHeaders,
63
+ body: JSON.stringify(this.withDefaultEventContext(event))
64
+ });
65
+ return readJson(res);
66
+ }
67
+ async pushEvents(events) {
68
+ const res = await fetch(`${this.url}/api/events`, {
69
+ method: "POST",
70
+ headers: this.writeHeaders,
71
+ body: JSON.stringify(events.map((event) => this.withDefaultEventContext(event)))
72
+ });
73
+ return readJson(res);
74
+ }
75
+ async pushStructuredLog(record, opts = {}) {
76
+ const url = `${this.url}/api/logs/structured${structuredLogQuery(this.withDefaultStructuredLogContext(opts))}`;
77
+ const res = await fetch(url, {
78
+ method: "POST",
79
+ headers: this.headers,
80
+ body: JSON.stringify(record)
81
+ });
82
+ return readJson(res);
83
+ }
84
+ async pushStructuredLogs(records, opts = {}) {
85
+ if (records.length === 0)
86
+ return { inserted: 0, events: [] };
87
+ const context = this.withDefaultStructuredLogContext(opts);
88
+ const url = `${this.url}/api/logs/structured${structuredLogQuery(context)}`;
89
+ const body = structuredLogBatchBody(records, context);
90
+ const res = await fetch(url, {
91
+ method: "POST",
92
+ headers: this.headers,
93
+ body: JSON.stringify(body)
94
+ });
95
+ return readJson(res);
96
+ }
97
+ async captureException(error, opts = {}) {
98
+ const normalized = normalizeError(error);
99
+ return this.pushEvent({
100
+ type: "exception",
101
+ severity: opts.severity ?? "error",
102
+ message: opts.message ?? normalized.message,
103
+ trace_id: opts.trace_id,
104
+ span_id: opts.span_id,
105
+ parent_span_id: opts.parent_span_id,
106
+ body: {
107
+ exception: {
108
+ type: normalized.type,
109
+ value: normalized.message,
110
+ stack_trace: normalized.stack,
111
+ handled: opts.handled ?? false,
112
+ mechanism: opts.mechanism ?? "sdk.captureException"
113
+ },
114
+ ...opts.body
115
+ },
116
+ attributes: {
117
+ exception_type: normalized.type,
118
+ stack_trace: normalized.stack,
119
+ handled: opts.handled ?? false,
120
+ mechanism: opts.mechanism ?? "sdk.captureException",
121
+ ...opts.attributes
122
+ }
123
+ });
124
+ }
125
+ async captureMetric(name, value, opts = {}) {
126
+ return this.pushEvent({
127
+ type: "metric",
128
+ severity: "info",
129
+ message: name,
130
+ trace_id: opts.trace_id,
131
+ span_id: opts.span_id,
132
+ body: {
133
+ name,
134
+ value,
135
+ kind: opts.kind ?? "gauge",
136
+ unit: opts.unit
137
+ },
138
+ attributes: {
139
+ name,
140
+ value,
141
+ metric_kind: opts.kind ?? "gauge",
142
+ unit: opts.unit,
143
+ ...opts.attributes
144
+ }
145
+ });
146
+ }
147
+ async captureSpan(span) {
148
+ return this.pushEvent({
149
+ type: "span",
150
+ severity: span.status === "error" ? "error" : "info",
151
+ message: span.name,
152
+ trace_id: span.trace_id,
153
+ span_id: span.span_id,
154
+ parent_span_id: span.parent_span_id,
155
+ body: {
156
+ name: span.name,
157
+ operation: span.operation,
158
+ status: span.status,
159
+ duration_ms: span.duration_ms
160
+ },
161
+ attributes: {
162
+ name: span.name,
163
+ operation: span.operation,
164
+ status: span.status,
165
+ started_at: span.started_at,
166
+ ended_at: span.ended_at,
167
+ duration_ms: span.duration_ms,
168
+ ...span.attributes
169
+ }
170
+ });
33
171
  }
34
172
  async search(query = {}) {
35
173
  const params = new URLSearchParams;
@@ -54,7 +192,7 @@ class LogsClient {
54
192
  if (query.fields)
55
193
  params.set("fields", query.fields.join(","));
56
194
  const res = await fetch(`${this.url}/api/logs?${params}`, { headers: this.headers });
57
- return res.json();
195
+ return readJson(res);
58
196
  }
59
197
  async tail(projectId, n = 50) {
60
198
  const params = new URLSearchParams({ n: String(n) });
@@ -62,7 +200,7 @@ class LogsClient {
62
200
  if (pid)
63
201
  params.set("project_id", pid);
64
202
  const res = await fetch(`${this.url}/api/logs/tail?${params}`, { headers: this.headers });
65
- return res.json();
203
+ return readJson(res);
66
204
  }
67
205
  async summary(projectId, since) {
68
206
  const params = new URLSearchParams;
@@ -72,11 +210,11 @@ class LogsClient {
72
210
  if (since)
73
211
  params.set("since", since);
74
212
  const res = await fetch(`${this.url}/api/logs/summary?${params}`, { headers: this.headers });
75
- return res.json();
213
+ return readJson(res);
76
214
  }
77
215
  async context(traceId) {
78
216
  const res = await fetch(`${this.url}/api/logs/${traceId}/context`, { headers: this.headers });
79
- return res.json();
217
+ return readJson(res);
80
218
  }
81
219
  async registerProject(name, githubRepo, baseUrl) {
82
220
  const res = await fetch(`${this.url}/api/projects`, {
@@ -84,7 +222,7 @@ class LogsClient {
84
222
  headers: this.headers,
85
223
  body: JSON.stringify({ name, github_repo: githubRepo, base_url: baseUrl })
86
224
  });
87
- return res.json();
225
+ return readJson(res);
88
226
  }
89
227
  async registerPage(projectId, url, path, name) {
90
228
  const res = await fetch(`${this.url}/api/projects/${projectId}/pages`, {
@@ -92,7 +230,7 @@ class LogsClient {
92
230
  headers: this.headers,
93
231
  body: JSON.stringify({ url, path, name })
94
232
  });
95
- return res.json();
233
+ return readJson(res);
96
234
  }
97
235
  async createScanJob(projectId, schedule, pageId) {
98
236
  const res = await fetch(`${this.url}/api/jobs`, {
@@ -100,14 +238,14 @@ class LogsClient {
100
238
  headers: this.headers,
101
239
  body: JSON.stringify({ project_id: projectId, schedule, page_id: pageId })
102
240
  });
103
- return res.json();
241
+ return readJson(res);
104
242
  }
105
243
  async perfSnapshot(projectId, pageId) {
106
244
  const params = new URLSearchParams({ project_id: projectId });
107
245
  if (pageId)
108
246
  params.set("page_id", pageId);
109
247
  const res = await fetch(`${this.url}/api/perf?${params}`, { headers: this.headers });
110
- return res.json();
248
+ return readJson(res);
111
249
  }
112
250
  async perfTrend(projectId, pageId, since, limit) {
113
251
  const params = new URLSearchParams({ project_id: projectId });
@@ -118,39 +256,2861 @@ class LogsClient {
118
256
  if (limit)
119
257
  params.set("limit", String(limit));
120
258
  const res = await fetch(`${this.url}/api/perf/trend?${params}`, { headers: this.headers });
121
- return res.json();
259
+ return readJson(res);
260
+ }
261
+ withDefaultEventContext(event) {
262
+ const identityContext = this.hasBrowserToken ? {} : {
263
+ project_id: this.projectId,
264
+ machine_id: this.machineId,
265
+ repo_id: this.repoId,
266
+ process_id: this.processId,
267
+ run_id: this.runId
268
+ };
269
+ const withContext = {
270
+ source: this.source,
271
+ environment: this.environment,
272
+ release_id: this.releaseId,
273
+ app_id: this.appId,
274
+ session_id: this.sessionId,
275
+ ...identityContext,
276
+ ...event,
277
+ attributes: {
278
+ sdk_name: "@hasna/logs-sdk",
279
+ sdk_runtime: runtimeName(),
280
+ ...event.attributes ?? {}
281
+ }
282
+ };
283
+ return this.hasBrowserToken ? stripBrowserUniversalEventIdentity(withContext) : withContext;
122
284
  }
285
+ withDefaultStructuredLogContext(opts) {
286
+ return {
287
+ projectId: this.projectId,
288
+ environment: this.environment,
289
+ releaseId: this.releaseId,
290
+ appId: this.appId,
291
+ machineId: this.machineId,
292
+ repoId: this.repoId,
293
+ processId: this.processId,
294
+ runId: this.runId,
295
+ sessionId: this.sessionId,
296
+ ...opts
297
+ };
298
+ }
299
+ }
300
+ function createPinoOpenLogsTransport(opts = {}) {
301
+ const queue = createStructuredLogQueue({ ...opts, format: opts.format ?? "pino" });
302
+ const decoder = new TextDecoder;
303
+ let pending = "";
304
+ let stopped = false;
305
+ const parseLine = (line) => {
306
+ const trimmed = line.trim();
307
+ if (!trimmed)
308
+ return;
309
+ try {
310
+ queue.enqueue(JSON.parse(trimmed));
311
+ return;
312
+ } catch (error) {
313
+ const normalized = toError(error);
314
+ opts.onError?.(normalized);
315
+ return normalized;
316
+ }
317
+ };
318
+ const drain = (text) => {
319
+ pending += text;
320
+ while (true) {
321
+ const newline = pending.indexOf(`
322
+ `);
323
+ if (newline < 0)
324
+ return;
325
+ const line = pending.slice(0, newline).replace(/\r$/, "");
326
+ pending = pending.slice(newline + 1);
327
+ const error = parseLine(line);
328
+ if (error)
329
+ return error;
330
+ }
331
+ };
332
+ const transport = {
333
+ write(chunk, encoding, callback) {
334
+ if (stopped) {
335
+ const error2 = new Error("open-logs Pino transport is stopped");
336
+ callbackFromArgs(encoding, callback)?.(error2);
337
+ return false;
338
+ }
339
+ const done = callbackFromArgs(encoding, callback);
340
+ const error = drain(chunkText(chunk, decoder));
341
+ if (error) {
342
+ done?.(error);
343
+ return false;
344
+ }
345
+ if (opts.waitForTelemetry && queue.shouldFlush()) {
346
+ queue.flush().then(() => done?.(), (err) => done?.(toError(err)));
347
+ } else {
348
+ if (queue.shouldFlush())
349
+ queue.flush().catch(opts.onError ?? noop);
350
+ done?.();
351
+ }
352
+ return true;
353
+ },
354
+ end(chunk, encoding, callback) {
355
+ let done;
356
+ if (typeof chunk === "function") {
357
+ done = chunk;
358
+ } else if (typeof encoding === "function") {
359
+ done = encoding;
360
+ } else {
361
+ done = callback;
362
+ }
363
+ if (chunk !== undefined && typeof chunk !== "function") {
364
+ const error = drain(chunkText(chunk, decoder));
365
+ if (error)
366
+ opts.onError?.(error);
367
+ }
368
+ const tail = decoder.decode();
369
+ if (tail)
370
+ pending += tail;
371
+ if (pending.trim()) {
372
+ const error = parseLine(pending);
373
+ if (error)
374
+ opts.onError?.(error);
375
+ }
376
+ pending = "";
377
+ stopped = true;
378
+ queue.flush().catch(opts.onError ?? noop).finally(() => {
379
+ queue.stop();
380
+ done?.();
381
+ });
382
+ },
383
+ flush: queue.flush,
384
+ stats: queue.stats,
385
+ stop() {
386
+ stopped = true;
387
+ queue.flush().catch(opts.onError ?? noop).finally(() => {
388
+ queue.stop();
389
+ });
390
+ }
391
+ };
392
+ return transport;
393
+ }
394
+ function createWinstonOpenLogsTransport(opts = {}) {
395
+ const queue = createStructuredLogQueue({ ...opts, format: opts.format ?? "winston" });
396
+ const listeners = new Map;
397
+ let closed = false;
398
+ const finish = () => {
399
+ if (closed)
400
+ return;
401
+ closed = true;
402
+ queue.stop();
403
+ transport.writable = false;
404
+ transport.emit("finish");
405
+ transport.emit("close");
406
+ };
407
+ const reportError = (error) => {
408
+ opts.onError?.(error);
409
+ transport.emit("error", error);
410
+ };
411
+ const transport = {
412
+ name: "open-logs",
413
+ level: opts.level,
414
+ silent: false,
415
+ writable: true,
416
+ _writableState: { objectMode: true },
417
+ pipe(destination) {
418
+ return destination ?? transport;
419
+ },
420
+ write(info, encoding, callback) {
421
+ const done = callbackFromUnknownArgs(encoding, callback);
422
+ transport.log(info, done);
423
+ return transport.writable;
424
+ },
425
+ end(info, encoding, callback) {
426
+ const done = callbackFromUnknownArgs(encoding, callback) ?? (typeof info === "function" ? info : undefined);
427
+ if (info && typeof info === "object")
428
+ transport.write(info);
429
+ queue.flush().catch(reportError).finally(() => {
430
+ finish();
431
+ done?.();
432
+ });
433
+ },
434
+ log(info, callback, ...legacyArgs) {
435
+ const normalized = normalizeWinstonLogArguments(info, callback, legacyArgs);
436
+ const done = normalized.callback;
437
+ if (transport.silent) {
438
+ done?.();
439
+ return;
440
+ }
441
+ queue.enqueue(normalized.info);
442
+ queueMicrotask(() => transport.emit("logged", normalized.info));
443
+ if (opts.waitForTelemetry && queue.shouldFlush()) {
444
+ queue.flush().then(() => done?.(), (error) => {
445
+ reportError(error);
446
+ done?.();
447
+ });
448
+ } else {
449
+ if (queue.shouldFlush())
450
+ queue.flush().catch((error) => {
451
+ reportError(error);
452
+ });
453
+ done?.();
454
+ }
455
+ },
456
+ flush: queue.flush,
457
+ stats: queue.stats,
458
+ close() {
459
+ queue.flush().catch(reportError).finally(finish);
460
+ },
461
+ stop() {
462
+ transport.close();
463
+ },
464
+ on(event, listener) {
465
+ let eventListeners = listeners.get(event);
466
+ if (!eventListeners) {
467
+ eventListeners = new Set;
468
+ listeners.set(event, eventListeners);
469
+ }
470
+ eventListeners.add(listener);
471
+ return transport;
472
+ },
473
+ once(event, listener) {
474
+ const onceListener = (...args) => {
475
+ transport.removeListener(event, onceListener);
476
+ listener(...args);
477
+ };
478
+ return transport.on(event, onceListener);
479
+ },
480
+ off(event, listener) {
481
+ return transport.removeListener(event, listener);
482
+ },
483
+ removeListener(event, listener) {
484
+ listeners.get(event)?.delete(listener);
485
+ return transport;
486
+ },
487
+ emit(event, ...args) {
488
+ const eventListeners = listeners.get(event);
489
+ if (!eventListeners?.size)
490
+ return false;
491
+ for (const listener of [...eventListeners])
492
+ listener(...args);
493
+ return true;
494
+ }
495
+ };
496
+ return transport;
497
+ }
498
+ async function captureHttpRequest(request, run, opts) {
499
+ const source = opts.source ?? opts.framework ?? runtimeName();
500
+ const client = opts.client ?? new LogsClient({ ...opts, source });
501
+ const startedAt = Date.now();
502
+ const startedIso = new Date(startedAt).toISOString();
503
+ const { traceId, parentSpanId } = traceContextFromRequest(request);
504
+ const spanId = randomHex(16);
505
+ const method = request.method || "GET";
506
+ const url = safeUrlParts(request.url);
507
+ const operation = opts.operation ?? "http.server";
508
+ try {
509
+ const result = await run();
510
+ const response = responseLike(result);
511
+ const durationMs = Date.now() - startedAt;
512
+ const statusCode = response?.status;
513
+ const route = routeForRequest(request, opts.route);
514
+ const name = `${method} ${route}`;
515
+ const event = httpServerSpanEvent({
516
+ source,
517
+ request,
518
+ response,
519
+ traceId,
520
+ spanId,
521
+ parentSpanId,
522
+ method,
523
+ route,
524
+ url,
525
+ operation,
526
+ name,
527
+ statusCode,
528
+ durationMs,
529
+ startedIso,
530
+ requestHeaderNames: opts.requestHeaderNames,
531
+ responseHeaderNames: opts.responseHeaderNames,
532
+ framework: opts.framework
533
+ });
534
+ await sendRequestTelemetry(client, [event], opts.waitForTelemetry);
535
+ return result;
536
+ } catch (error) {
537
+ const durationMs = Date.now() - startedAt;
538
+ const normalized = normalizeError(error);
539
+ const route = routeForRequest(request, opts.route);
540
+ const name = `${method} ${route}`;
541
+ const span = httpServerSpanEvent({
542
+ source,
543
+ request,
544
+ traceId,
545
+ spanId,
546
+ parentSpanId,
547
+ method,
548
+ route,
549
+ url,
550
+ operation,
551
+ name,
552
+ statusCode: 500,
553
+ durationMs,
554
+ startedIso,
555
+ requestHeaderNames: opts.requestHeaderNames,
556
+ framework: opts.framework,
557
+ errorType: normalized.type
558
+ });
559
+ const exception = {
560
+ type: "exception",
561
+ source,
562
+ severity: "error",
563
+ trace_id: traceId,
564
+ span_id: spanId,
565
+ parent_span_id: parentSpanId,
566
+ message: normalized.message,
567
+ body: {
568
+ exception: {
569
+ type: normalized.type,
570
+ value: normalized.message,
571
+ stack_trace: normalized.stack,
572
+ handled: false,
573
+ mechanism: `${opts.framework ?? "fetch"}.request`
574
+ }
575
+ },
576
+ attributes: {
577
+ exception_type: normalized.type,
578
+ stack_trace: normalized.stack,
579
+ handled: false,
580
+ mechanism: `${opts.framework ?? "fetch"}.request`,
581
+ operation,
582
+ method,
583
+ route,
584
+ url_scheme: url.scheme,
585
+ url_host: url.host,
586
+ url_path: url.path,
587
+ query_present: url.queryPresent,
588
+ framework: opts.framework,
589
+ duration_ms: durationMs
590
+ }
591
+ };
592
+ await sendRequestTelemetry(client, [span, exception], opts.waitForTelemetry);
593
+ throw error;
594
+ }
595
+ }
596
+ function instrumentFetchHandler(handler, opts) {
597
+ return (request, ...args) => captureHttpRequest(request, () => handler(request, ...args), opts);
598
+ }
599
+ function createHonoTelemetryMiddleware(opts) {
600
+ return async (c, next) => {
601
+ const route = opts.route ?? (() => c.req.routePath ?? c.req.path);
602
+ await captureHttpRequest(c.req.raw, async () => {
603
+ await next();
604
+ return c.res ?? new Response(null, { status: 204 });
605
+ }, { ...opts, route, framework: opts.framework ?? "hono" });
606
+ };
607
+ }
608
+ function captureNodeHttpRequest(request, response, opts) {
609
+ const startedAt = Date.now();
610
+ const startedIso = new Date(startedAt).toISOString();
611
+ let pending;
612
+ let completed = false;
613
+ let stopped = false;
614
+ const errorMonitor = nodeErrorMonitorEvent();
615
+ const listenerCleanups = [];
616
+ const finish = (error) => {
617
+ if (completed || stopped)
618
+ return;
619
+ completed = true;
620
+ cleanupNodeListeners(listenerCleanups);
621
+ pending = emitNodeHttpRequestTelemetry({
622
+ request,
623
+ response,
624
+ opts,
625
+ startedAt,
626
+ startedIso,
627
+ error
628
+ }).catch(() => {});
629
+ };
630
+ const onFinish = () => finish();
631
+ const onClose = () => {
632
+ if (!completed)
633
+ finish(new ResponseClosedError);
634
+ };
635
+ const onResponseError = (error) => finish(error);
636
+ const onRequestError = (error) => finish(error);
637
+ const onRequestAborted = () => finish(new ResponseClosedError);
638
+ listenerCleanups.push(addNodeListener(response, "finish", onFinish));
639
+ listenerCleanups.push(addNodeListener(response, "close", onClose));
640
+ if (errorMonitor)
641
+ listenerCleanups.push(addNodeListener(response, errorMonitor, onResponseError));
642
+ if (errorMonitor)
643
+ listenerCleanups.push(addNodeListener(request, errorMonitor, onRequestError));
644
+ listenerCleanups.push(addNodeListener(request, "aborted", onRequestAborted));
645
+ return {
646
+ finish,
647
+ async flush() {
648
+ await pending;
649
+ },
650
+ stop() {
651
+ stopped = true;
652
+ cleanupNodeListeners(listenerCleanups);
653
+ }
654
+ };
655
+ }
656
+ function createExpressTelemetryMiddleware(opts) {
657
+ return (request, response, next) => {
658
+ const capture = captureNodeHttpRequest(request, response, { ...opts, framework: opts.framework ?? "express" });
659
+ setExpressCapture(request, capture);
660
+ try {
661
+ next();
662
+ } catch (error) {
663
+ capture.finish(error);
664
+ clearExpressCapture(request);
665
+ throw error;
666
+ }
667
+ };
668
+ }
669
+ function createExpressErrorTelemetryMiddleware(opts) {
670
+ return (error, request, response, next) => {
671
+ const capture = getExpressCapture(request) ?? captureNodeHttpRequest(request, response, { ...opts, framework: opts.framework ?? "express" });
672
+ capture.finish(error);
673
+ clearExpressCapture(request);
674
+ next(error);
675
+ };
676
+ }
677
+ function createFastifyTelemetryHooks(opts) {
678
+ const captures = new WeakMap;
679
+ const onRequest = (request, reply, done) => {
680
+ captures.set(request, captureNodeHttpRequest(fastifyRequestLike(request), fastifyResponseLike(reply), { ...opts, framework: opts.framework ?? "fastify", route: opts.route ?? fastifyRoute }));
681
+ done?.();
682
+ };
683
+ const onResponse = (request, _reply, done) => {
684
+ const capture = captures.get(request);
685
+ captures.delete(request);
686
+ capture?.finish();
687
+ done?.();
688
+ };
689
+ const onError = (request, _reply, error, done) => {
690
+ const capture = captures.get(request);
691
+ captures.delete(request);
692
+ capture?.finish(error);
693
+ done?.();
694
+ };
695
+ return { onRequest, onResponse, onError };
123
696
  }
124
697
  function initLogs(opts) {
125
- if (typeof window === "undefined")
698
+ const browser = globalThis;
699
+ if (!browser.window || !browser.location)
126
700
  return;
127
701
  const serverUrl = (opts.url ?? DEFAULT_URL).replace(/\/$/, "");
128
- const client = new LogsClient({ url: serverUrl, projectId: opts.projectId });
702
+ const client = new LogsClient({ url: serverUrl, projectId: opts.projectId, browserToken: opts.browserToken, apiKey: opts.apiKey });
129
703
  const q = [];
130
704
  const flush = () => {
131
705
  if (q.length)
132
706
  client.pushBatch(q.splice(0)).catch(() => {});
133
707
  };
708
+ const currentHref = () => browser.location?.href ?? "";
134
709
  setInterval(flush, 2000);
135
710
  const _ce = console.error.bind(console);
136
711
  console.error = (...args) => {
137
712
  _ce(...args);
138
- q.push({ level: "error", message: args.map(String).join(" "), source: "script", url: location.href });
713
+ q.push({ level: "error", message: args.map(String).join(" "), source: "script", url: currentHref() });
139
714
  };
140
715
  const _cw = console.warn.bind(console);
141
716
  console.warn = (...args) => {
142
717
  _cw(...args);
143
- q.push({ level: "warn", message: args.map(String).join(" "), source: "script", url: location.href });
718
+ q.push({ level: "warn", message: args.map(String).join(" "), source: "script", url: currentHref() });
144
719
  };
145
- window.addEventListener("error", (e) => {
146
- q.push({ level: "error", message: e.message, stack_trace: e.error?.stack, source: "script", url: location.href });
720
+ browser.window.addEventListener("error", (e) => {
721
+ q.push({ level: "error", message: e.message ?? "Browser error", stack_trace: e.error?.stack, source: "script", url: currentHref() });
722
+ });
723
+ browser.window.addEventListener("unhandledrejection", (e) => {
724
+ const reason = typeof e.reason === "string" ? { message: e.reason } : e.reason;
725
+ q.push({ level: "error", message: `Unhandled: ${reason?.message ?? "promise rejection"}`, stack_trace: reason?.stack, source: "script", url: currentHref() });
147
726
  });
148
- window.addEventListener("unhandledrejection", (e) => {
149
- q.push({ level: "error", message: `Unhandled: ${e.reason?.message ?? e.reason}`, stack_trace: e.reason?.stack, source: "script", url: location.href });
727
+ browser.window.addEventListener("beforeunload", flush);
728
+ }
729
+ function initUniversalLogs(opts) {
730
+ const browser = globalThis;
731
+ if (browser.window && browser.location)
732
+ return initBrowserUniversalLogs(opts);
733
+ return initNodeLogs(opts);
734
+ }
735
+ function initNodeLogs(opts) {
736
+ const runtime = globalThis;
737
+ const processLike = runtime.process;
738
+ if (!processLike || !runtime.fetch)
739
+ return;
740
+ const source = opts.source ?? runtimeName();
741
+ const processId = opts.processId ?? defaultProcessId(processLike);
742
+ const client = new LogsClient({ ...opts, source, processId });
743
+ const q = [];
744
+ const maxBatchSize = opts.maxBatchSize ?? 20;
745
+ const collectorUrl = (opts.url ?? DEFAULT_URL).replace(/\/$/, "");
746
+ let stopped = false;
747
+ const enqueue = (event) => {
748
+ if (stopped)
749
+ return;
750
+ q.push({
751
+ event_time: new Date().toISOString(),
752
+ source,
753
+ process_id: processId,
754
+ ...event,
755
+ attributes: {
756
+ pid: processLike.pid,
757
+ runtime: runtimeName(),
758
+ ...event.attributes ?? {}
759
+ }
760
+ });
761
+ if (q.length >= maxBatchSize)
762
+ flush();
763
+ };
764
+ const flush = async () => {
765
+ if (!q.length)
766
+ return;
767
+ const batch = q.splice(0);
768
+ await client.pushEvents(batch).catch(() => {});
769
+ };
770
+ const interval = setInterval(() => {
771
+ flush();
772
+ }, opts.flushIntervalMs ?? 2000);
773
+ const restores = [() => clearInterval(interval)];
774
+ const removeProcessListener = (event, listener) => {
775
+ if (typeof processLike.off === "function")
776
+ processLike.off(event, listener);
777
+ else if (typeof processLike.removeListener === "function")
778
+ processLike.removeListener(event, listener);
779
+ };
780
+ if (opts.captureProcess !== false) {
781
+ enqueue({
782
+ type: "process",
783
+ severity: "info",
784
+ source_event_id: `${processId}:start`,
785
+ message: "Process started",
786
+ attributes: {
787
+ phase: "start",
788
+ argv: processLike.argv,
789
+ cwd: safeCall(processLike.cwd),
790
+ exec_path: processLike.execPath,
791
+ node_version: processLike.versions?.node ?? processLike.version,
792
+ bun_version: processLike.versions?.bun
793
+ }
794
+ });
795
+ const beforeExit = (code) => {
796
+ enqueue({
797
+ type: "process",
798
+ severity: code === 0 ? "info" : "error",
799
+ source_event_id: `${processId}:beforeExit:${String(code)}`,
800
+ message: `Process beforeExit ${String(code)}`,
801
+ attributes: { phase: "beforeExit", exit_code: code }
802
+ });
803
+ flush();
804
+ };
805
+ processLike.on?.("beforeExit", beforeExit);
806
+ restores.push(() => removeProcessListener("beforeExit", beforeExit));
807
+ }
808
+ if (opts.captureConsole !== false) {
809
+ const consoleRecord = console;
810
+ const consoleMethods = [
811
+ ["debug", "debug"],
812
+ ["log", "info"],
813
+ ["info", "info"],
814
+ ["warn", "warn"],
815
+ ["error", "error"]
816
+ ];
817
+ for (const [method, severity] of consoleMethods) {
818
+ const original = consoleRecord[method];
819
+ if (typeof original !== "function")
820
+ continue;
821
+ const bound = original.bind(console);
822
+ consoleRecord[method] = (...args) => {
823
+ bound(...args);
824
+ enqueue({
825
+ type: "log",
826
+ severity,
827
+ message: formatArgs(args),
828
+ attributes: {
829
+ console_method: method
830
+ }
831
+ });
832
+ };
833
+ restores.push(() => {
834
+ consoleRecord[method] = original;
835
+ });
836
+ }
837
+ }
838
+ if (opts.captureExceptions !== false) {
839
+ const uncaughtExceptionMonitor = (error, origin) => {
840
+ const normalized = normalizeError(error);
841
+ enqueue({
842
+ type: "exception",
843
+ severity: "fatal",
844
+ message: normalized.message,
845
+ body: {
846
+ exception: {
847
+ type: normalized.type,
848
+ value: normalized.message,
849
+ stack_trace: normalized.stack,
850
+ handled: false,
851
+ mechanism: "process.uncaughtExceptionMonitor"
852
+ }
853
+ },
854
+ attributes: {
855
+ exception_type: normalized.type,
856
+ stack_trace: normalized.stack,
857
+ handled: false,
858
+ mechanism: "process.uncaughtExceptionMonitor",
859
+ origin
860
+ }
861
+ });
862
+ flush();
863
+ };
864
+ processLike.on?.("uncaughtExceptionMonitor", uncaughtExceptionMonitor);
865
+ restores.push(() => removeProcessListener("uncaughtExceptionMonitor", uncaughtExceptionMonitor));
866
+ }
867
+ if (opts.captureRejections === true) {
868
+ const unhandledRejection = (reason) => {
869
+ const normalized = normalizeError(reason);
870
+ enqueue({
871
+ type: "exception",
872
+ severity: "error",
873
+ message: `Unhandled rejection: ${normalized.message}`,
874
+ body: {
875
+ exception: {
876
+ type: normalized.type,
877
+ value: normalized.message,
878
+ stack_trace: normalized.stack,
879
+ handled: false,
880
+ mechanism: "process.unhandledRejection"
881
+ }
882
+ },
883
+ attributes: {
884
+ exception_type: normalized.type,
885
+ stack_trace: normalized.stack,
886
+ handled: false,
887
+ mechanism: "process.unhandledRejection"
888
+ }
889
+ });
890
+ flush();
891
+ };
892
+ processLike.on?.("unhandledRejection", unhandledRejection);
893
+ restores.push(() => removeProcessListener("unhandledRejection", unhandledRejection));
894
+ }
895
+ if (opts.captureFetch !== false) {
896
+ const originalFetch = runtime.fetch;
897
+ const boundFetch = originalFetch.bind(globalThis);
898
+ runtime.fetch = async (input, init) => {
899
+ const requestUrl = requestUrlString(input);
900
+ const method = requestMethod(input, init);
901
+ const collectorRequest = isCollectorRequest(requestUrl, collectorUrl);
902
+ let fetchInput = input;
903
+ let fetchInit = init;
904
+ const existingTraceparent = traceparentInfoFromFetchInput(input, init);
905
+ let traceContext = existingTraceparent.context;
906
+ let traceparentInjected = false;
907
+ if (!existingTraceparent.present && !collectorRequest && shouldPropagateTrace(opts, requestUrl)) {
908
+ traceContext = { traceId: randomHex(32), spanId: randomHex(16) };
909
+ const tracedFetch = fetchInputWithTraceparent(input, init, traceparentFromContext(traceContext));
910
+ fetchInput = tracedFetch.input;
911
+ fetchInit = tracedFetch.init;
912
+ traceparentInjected = true;
913
+ }
914
+ const startedAt = Date.now();
915
+ try {
916
+ const response = await boundFetch(fetchInput, fetchInit);
917
+ if (!collectorRequest) {
918
+ enqueue({
919
+ type: "span",
920
+ severity: response.ok ? "info" : "error",
921
+ trace_id: traceContext?.traceId,
922
+ span_id: traceContext?.spanId ?? randomId("span"),
923
+ message: `${method} ${requestUrl}`,
924
+ body: {
925
+ name: `${method} ${requestUrl}`,
926
+ operation: "http.client",
927
+ status: response.ok ? "ok" : "error",
928
+ duration_ms: Date.now() - startedAt
929
+ },
930
+ attributes: {
931
+ name: `${method} ${requestUrl}`,
932
+ operation: "http.client",
933
+ method,
934
+ url: requestUrl,
935
+ status_code: response.status,
936
+ ok: response.ok,
937
+ duration_ms: Date.now() - startedAt,
938
+ traceparent_propagated: traceparentInjected || undefined,
939
+ traceparent_existing: existingTraceparent.present || undefined
940
+ }
941
+ });
942
+ }
943
+ return response;
944
+ } catch (error) {
945
+ if (!collectorRequest) {
946
+ const normalized = normalizeError(error);
947
+ enqueue({
948
+ type: "network",
949
+ severity: "error",
950
+ trace_id: traceContext?.traceId,
951
+ span_id: traceContext?.spanId ?? randomId("span"),
952
+ message: `${method} ${requestUrl} failed: ${normalized.message}`,
953
+ body: {
954
+ error: {
955
+ type: normalized.type,
956
+ value: normalized.message,
957
+ stack_trace: normalized.stack
958
+ }
959
+ },
960
+ attributes: {
961
+ operation: "http.client",
962
+ method,
963
+ url: requestUrl,
964
+ duration_ms: Date.now() - startedAt,
965
+ error_type: normalized.type,
966
+ stack_trace: normalized.stack,
967
+ traceparent_propagated: traceparentInjected || undefined,
968
+ traceparent_existing: existingTraceparent.present || undefined
969
+ }
970
+ });
971
+ }
972
+ throw error;
973
+ }
974
+ };
975
+ restores.push(() => {
976
+ runtime.fetch = originalFetch;
977
+ });
978
+ }
979
+ return {
980
+ flush,
981
+ stop() {
982
+ stopped = true;
983
+ while (restores.length)
984
+ restores.pop()?.();
985
+ }
986
+ };
987
+ }
988
+ function initBrowserUniversalLogs(opts) {
989
+ const browser = globalThis;
990
+ const runtime = globalThis;
991
+ if (!browser.window || !browser.location)
992
+ return;
993
+ const serverUrl = browserRequestUrlString(opts.url ?? DEFAULT_URL, browser.location.href).replace(/\/$/, "");
994
+ const client = new LogsClient({ ...opts, url: serverUrl, source: opts.source ?? "browser" });
995
+ const maxBatchSize = opts.maxBatchSize ?? 10;
996
+ const maxQueueSize = Math.max(1, opts.maxQueueSize ?? 1000);
997
+ const spool = createBrowserUniversalSpool(opts);
998
+ const loadedSpool = spool.load();
999
+ const q = loadedSpool.events.slice(-maxQueueSize).map((event) => ({
1000
+ event,
1001
+ spoolEvent: event
1002
+ }));
1003
+ let inFlightBatch;
1004
+ let flushPromise;
1005
+ let stopped = false;
1006
+ const currentHref = () => browser.location?.href ?? "";
1007
+ const allSpoolItems = () => [...inFlightBatch ?? [], ...q];
1008
+ const persistSpool = () => persistBrowserUniversalSpool(spool, allSpoolItems());
1009
+ const enforceQueueLimit = () => {
1010
+ const inFlightCount = inFlightBatch?.length ?? 0;
1011
+ while (q.length + inFlightCount > maxQueueSize)
1012
+ q.shift();
1013
+ };
1014
+ if (loadedSpool.hadStoredRecord)
1015
+ persistSpool();
1016
+ const enqueue = (event) => {
1017
+ if (stopped)
1018
+ return;
1019
+ const queued = {
1020
+ event_time: new Date().toISOString(),
1021
+ source: "browser",
1022
+ ...event,
1023
+ attributes: {
1024
+ url: currentHref(),
1025
+ ...event.attributes ?? {}
1026
+ }
1027
+ };
1028
+ q.push({
1029
+ event: queued,
1030
+ spoolEvent: redactBrowserUniversalEvent(queued)
1031
+ });
1032
+ enforceQueueLimit();
1033
+ persistSpool();
1034
+ if (q.length >= maxBatchSize)
1035
+ flush();
1036
+ };
1037
+ const flush = () => {
1038
+ if (flushPromise)
1039
+ return flushPromise;
1040
+ if (!q.length)
1041
+ return Promise.resolve();
1042
+ const batch = q.splice(0, maxBatchSize);
1043
+ inFlightBatch = batch;
1044
+ persistSpool();
1045
+ flushPromise = (async () => {
1046
+ try {
1047
+ await client.pushEvents(batch.map((item) => item.event));
1048
+ } catch {
1049
+ if (inFlightBatch === batch)
1050
+ inFlightBatch = undefined;
1051
+ q.unshift(...batch.map((item) => ({
1052
+ event: item.spoolEvent,
1053
+ spoolEvent: item.spoolEvent
1054
+ })));
1055
+ enforceQueueLimit();
1056
+ } finally {
1057
+ if (inFlightBatch === batch)
1058
+ inFlightBatch = undefined;
1059
+ flushPromise = undefined;
1060
+ persistSpool();
1061
+ if (!stopped && q.length >= maxBatchSize)
1062
+ flush();
1063
+ }
1064
+ })();
1065
+ return flushPromise;
1066
+ };
1067
+ const interval = setInterval(() => {
1068
+ flush();
1069
+ }, opts.flushIntervalMs ?? 2000);
1070
+ const restores = [() => clearInterval(interval)];
1071
+ if (opts.captureConsole !== false) {
1072
+ const consoleRecord = console;
1073
+ const consoleMethods = [
1074
+ ["debug", "debug"],
1075
+ ["log", "info"],
1076
+ ["info", "info"],
1077
+ ["warn", "warn"],
1078
+ ["error", "error"]
1079
+ ];
1080
+ for (const [method, severity] of consoleMethods) {
1081
+ const original = consoleRecord[method];
1082
+ if (typeof original !== "function")
1083
+ continue;
1084
+ const bound = original.bind(console);
1085
+ consoleRecord[method] = (...args) => {
1086
+ bound(...args);
1087
+ enqueue({
1088
+ type: "log",
1089
+ severity,
1090
+ message: formatArgs(args),
1091
+ attributes: {
1092
+ console_method: method
1093
+ }
1094
+ });
1095
+ };
1096
+ restores.push(() => {
1097
+ consoleRecord[method] = original;
1098
+ });
1099
+ }
1100
+ }
1101
+ if (opts.captureExceptions !== false) {
1102
+ const browserErrorListener = (e) => {
1103
+ enqueue({
1104
+ type: "exception",
1105
+ severity: "error",
1106
+ message: e.message ?? "Browser error",
1107
+ body: {
1108
+ exception: {
1109
+ value: e.message ?? "Browser error",
1110
+ stack_trace: e.error?.stack,
1111
+ handled: false,
1112
+ mechanism: "browser.onerror"
1113
+ }
1114
+ },
1115
+ attributes: {
1116
+ stack_trace: e.error?.stack,
1117
+ mechanism: "browser.onerror"
1118
+ }
1119
+ });
1120
+ };
1121
+ browser.window.addEventListener("error", browserErrorListener);
1122
+ restores.push(() => {
1123
+ browser.window?.removeEventListener?.("error", browserErrorListener);
1124
+ });
1125
+ }
1126
+ if (opts.captureRejections !== false) {
1127
+ const browserRejectionListener = (e) => {
1128
+ const reason = typeof e.reason === "string" ? { message: e.reason } : e.reason;
1129
+ enqueue({
1130
+ type: "exception",
1131
+ severity: "error",
1132
+ message: `Unhandled: ${reason?.message ?? "promise rejection"}`,
1133
+ body: {
1134
+ exception: {
1135
+ value: reason?.message ?? "promise rejection",
1136
+ stack_trace: reason?.stack,
1137
+ handled: false,
1138
+ mechanism: "browser.unhandledrejection"
1139
+ }
1140
+ },
1141
+ attributes: {
1142
+ stack_trace: reason?.stack,
1143
+ mechanism: "browser.unhandledrejection"
1144
+ }
1145
+ });
1146
+ };
1147
+ browser.window.addEventListener("unhandledrejection", browserRejectionListener);
1148
+ restores.push(() => {
1149
+ browser.window?.removeEventListener?.("unhandledrejection", browserRejectionListener);
1150
+ });
1151
+ }
1152
+ if (opts.captureNavigation) {
1153
+ let lastHref = currentHref();
1154
+ const enqueueNavigation = (navigationType, fromUrl, toUrl = currentHref()) => {
1155
+ enqueue({
1156
+ type: "span",
1157
+ severity: "info",
1158
+ span_id: randomId("span"),
1159
+ message: `NAVIGATION ${toUrl}`,
1160
+ body: {
1161
+ name: `NAVIGATION ${toUrl}`,
1162
+ operation: "browser.navigation",
1163
+ status: "ok"
1164
+ },
1165
+ attributes: {
1166
+ name: `NAVIGATION ${toUrl}`,
1167
+ operation: "browser.navigation",
1168
+ navigation_type: navigationType,
1169
+ from_url: fromUrl,
1170
+ to_url: toUrl
1171
+ }
1172
+ });
1173
+ };
1174
+ enqueueNavigation("page_load", undefined, lastHref);
1175
+ const emitRouteChange = (navigationType) => {
1176
+ const previousHref = lastHref;
1177
+ const nextHref = currentHref();
1178
+ if (nextHref === previousHref)
1179
+ return;
1180
+ lastHref = nextHref;
1181
+ enqueueNavigation(navigationType, previousHref, nextHref);
1182
+ };
1183
+ const popstateListener = () => emitRouteChange("popstate");
1184
+ const hashchangeListener = () => emitRouteChange("hashchange");
1185
+ browser.window.addEventListener("popstate", popstateListener);
1186
+ browser.window.addEventListener("hashchange", hashchangeListener);
1187
+ restores.push(() => {
1188
+ browser.window?.removeEventListener?.("popstate", popstateListener);
1189
+ browser.window?.removeEventListener?.("hashchange", hashchangeListener);
1190
+ });
1191
+ const originalPushState = browser.history?.pushState;
1192
+ if (typeof originalPushState === "function") {
1193
+ const wrappedPushState = function pushState(state, title, url) {
1194
+ originalPushState.call(this, state, title, url);
1195
+ emitRouteChange("pushState");
1196
+ };
1197
+ browser.history.pushState = wrappedPushState;
1198
+ restores.push(() => {
1199
+ if (browser.history?.pushState === wrappedPushState)
1200
+ browser.history.pushState = originalPushState;
1201
+ });
1202
+ }
1203
+ const originalReplaceState = browser.history?.replaceState;
1204
+ if (typeof originalReplaceState === "function") {
1205
+ const wrappedReplaceState = function replaceState(state, title, url) {
1206
+ originalReplaceState.call(this, state, title, url);
1207
+ emitRouteChange("replaceState");
1208
+ };
1209
+ browser.history.replaceState = wrappedReplaceState;
1210
+ restores.push(() => {
1211
+ if (browser.history?.replaceState === wrappedReplaceState)
1212
+ browser.history.replaceState = originalReplaceState;
1213
+ });
1214
+ }
1215
+ }
1216
+ if (opts.captureResourceTiming) {
1217
+ let resourceTimingEvents = 0;
1218
+ const configuredResourceLimit = opts.maxResourceTimingEvents;
1219
+ const maxResourceTimingEvents = Number.isFinite(configuredResourceLimit) ? Math.max(1, Math.floor(configuredResourceLimit)) : 100;
1220
+ const seenResourceTimings = new Set;
1221
+ const enqueueResourceTiming = (entry) => {
1222
+ if (resourceTimingEvents >= maxResourceTimingEvents)
1223
+ return;
1224
+ const resourceUrl = browserPerformanceEntryUrl(entry, currentHref());
1225
+ if (!resourceUrl || isCollectorRequest(resourceUrl, serverUrl))
1226
+ return;
1227
+ const durationMs = roundedNumber(entry.duration);
1228
+ const initiatorType = typeof entry.initiatorType === "string" ? entry.initiatorType : "resource";
1229
+ const resourceKey = browserPerformanceEntryKey(entry, resourceUrl, initiatorType);
1230
+ if (seenResourceTimings.has(resourceKey))
1231
+ return;
1232
+ seenResourceTimings.add(resourceKey);
1233
+ resourceTimingEvents += 1;
1234
+ enqueue({
1235
+ type: "span",
1236
+ severity: "info",
1237
+ span_id: randomId("span"),
1238
+ message: `RESOURCE ${resourceUrl}`,
1239
+ body: {
1240
+ name: `RESOURCE ${resourceUrl}`,
1241
+ operation: "browser.resource",
1242
+ status: "ok",
1243
+ duration_ms: durationMs
1244
+ },
1245
+ attributes: {
1246
+ name: `RESOURCE ${resourceUrl}`,
1247
+ operation: "browser.resource",
1248
+ url: resourceUrl,
1249
+ initiator_type: initiatorType,
1250
+ start_time_ms: roundedNumber(entry.startTime),
1251
+ duration_ms: durationMs,
1252
+ transfer_size: roundedNumber(entry.transferSize),
1253
+ encoded_body_size: roundedNumber(entry.encodedBodySize),
1254
+ decoded_body_size: roundedNumber(entry.decodedBodySize),
1255
+ response_status: roundedNumber(entry.responseStatus)
1256
+ }
1257
+ });
1258
+ };
1259
+ for (const entry of browser.performance?.getEntriesByType?.("resource") ?? []) {
1260
+ enqueueResourceTiming(entry);
1261
+ }
1262
+ if (runtime.PerformanceObserver) {
1263
+ try {
1264
+ const observer = new runtime.PerformanceObserver((list) => {
1265
+ for (const entry of list.getEntries())
1266
+ enqueueResourceTiming(entry);
1267
+ });
1268
+ let observing = false;
1269
+ try {
1270
+ observer.observe({ type: "resource", buffered: true });
1271
+ observing = true;
1272
+ } catch {
1273
+ try {
1274
+ observer.observe({ entryTypes: ["resource"] });
1275
+ observing = true;
1276
+ } catch {}
1277
+ }
1278
+ if (observing)
1279
+ restores.push(() => observer.disconnect());
1280
+ } catch {}
1281
+ }
1282
+ }
1283
+ if (opts.captureWebVitals) {
1284
+ let webVitalEvents = 0;
1285
+ let cumulativeLayoutShift = 0;
1286
+ let currentInp = 0;
1287
+ const configuredWebVitalLimit = opts.maxWebVitalEvents;
1288
+ const maxWebVitalEvents = Number.isFinite(configuredWebVitalLimit) ? Math.max(1, Math.floor(configuredWebVitalLimit)) : 50;
1289
+ const seenWebVitalEntries = new Set;
1290
+ const webVitalObserverDisconnects = [];
1291
+ let webVitalObservationStopped = false;
1292
+ const stopWebVitalObservation = () => {
1293
+ if (webVitalObservationStopped)
1294
+ return;
1295
+ webVitalObservationStopped = true;
1296
+ seenWebVitalEntries.clear();
1297
+ while (webVitalObserverDisconnects.length)
1298
+ webVitalObserverDisconnects.pop()?.();
1299
+ };
1300
+ const enqueueWebVital = (metricName, value, entry, extraAttributes = {}) => {
1301
+ if (webVitalObservationStopped || webVitalEvents >= maxWebVitalEvents) {
1302
+ stopWebVitalObservation();
1303
+ return;
1304
+ }
1305
+ const normalizedValue = normalizedWebVitalValue(metricName, value);
1306
+ if (normalizedValue === undefined)
1307
+ return;
1308
+ webVitalEvents += 1;
1309
+ const unit = metricName === "cls" ? "score" : "ms";
1310
+ const name = `browser.web_vital.${metricName}`;
1311
+ enqueue({
1312
+ type: "metric",
1313
+ severity: "info",
1314
+ message: `WEB_VITAL ${metricName} ${normalizedValue}`,
1315
+ body: {
1316
+ name,
1317
+ value: normalizedValue,
1318
+ kind: "gauge",
1319
+ unit
1320
+ },
1321
+ attributes: {
1322
+ name,
1323
+ operation: "browser.web_vital",
1324
+ web_vital: metricName,
1325
+ metric_kind: "gauge",
1326
+ value: normalizedValue,
1327
+ unit,
1328
+ rating: webVitalRating(metricName, normalizedValue),
1329
+ entry_type: entry.entryType,
1330
+ entry_name: entry.name,
1331
+ start_time_ms: roundedNumber(entry.startTime),
1332
+ duration_ms: roundedNumber(entry.duration),
1333
+ interaction_id: roundedNumber(entry.interactionId),
1334
+ ...extraAttributes
1335
+ }
1336
+ });
1337
+ if (webVitalEvents >= maxWebVitalEvents)
1338
+ stopWebVitalObservation();
1339
+ };
1340
+ const processWebVitalEntry = (entry) => {
1341
+ if (webVitalObservationStopped || webVitalEvents >= maxWebVitalEvents) {
1342
+ stopWebVitalObservation();
1343
+ return;
1344
+ }
1345
+ const entryType = entry.entryType;
1346
+ if (entryType === "paint" && entry.name === "first-contentful-paint") {
1347
+ if (normalizedWebVitalValue("fcp", entry.startTime) === undefined)
1348
+ return;
1349
+ const key = browserWebVitalEntryKey("fcp", entry);
1350
+ if (seenWebVitalEntries.has(key))
1351
+ return;
1352
+ seenWebVitalEntries.add(key);
1353
+ enqueueWebVital("fcp", entry.startTime, entry);
1354
+ return;
1355
+ }
1356
+ if (entryType === "largest-contentful-paint") {
1357
+ const value = entry.renderTime ?? entry.loadTime ?? entry.startTime;
1358
+ if (normalizedWebVitalValue("lcp", value) === undefined)
1359
+ return;
1360
+ const key = browserWebVitalEntryKey("lcp", entry);
1361
+ if (seenWebVitalEntries.has(key))
1362
+ return;
1363
+ seenWebVitalEntries.add(key);
1364
+ enqueueWebVital("lcp", value, entry);
1365
+ return;
1366
+ }
1367
+ if (entryType === "layout-shift") {
1368
+ if (entry.hadRecentInput)
1369
+ return;
1370
+ const delta = normalizedWebVitalValue("cls", entry.value);
1371
+ if (delta === undefined)
1372
+ return;
1373
+ const key = browserWebVitalEntryKey("cls", entry);
1374
+ if (seenWebVitalEntries.has(key))
1375
+ return;
1376
+ seenWebVitalEntries.add(key);
1377
+ cumulativeLayoutShift = normalizedWebVitalValue("cls", cumulativeLayoutShift + delta) ?? cumulativeLayoutShift;
1378
+ enqueueWebVital("cls", cumulativeLayoutShift, entry, { delta });
1379
+ return;
1380
+ }
1381
+ if (entryType === "first-input") {
1382
+ const value = typeof entry.processingStart === "number" && typeof entry.startTime === "number" ? entry.processingStart - entry.startTime : entry.duration;
1383
+ if (normalizedWebVitalValue("fid", value) === undefined)
1384
+ return;
1385
+ const key = browserWebVitalEntryKey("fid", entry);
1386
+ if (seenWebVitalEntries.has(key))
1387
+ return;
1388
+ seenWebVitalEntries.add(key);
1389
+ enqueueWebVital("fid", value, entry);
1390
+ return;
1391
+ }
1392
+ if (entryType === "event") {
1393
+ const value = typeof entry.duration === "number" ? entry.duration : undefined;
1394
+ const normalizedValue = normalizedWebVitalValue("inp", value);
1395
+ if (normalizedValue === undefined || normalizedValue <= currentInp)
1396
+ return;
1397
+ const key = browserWebVitalEntryKey("inp", entry);
1398
+ if (seenWebVitalEntries.has(key))
1399
+ return;
1400
+ seenWebVitalEntries.add(key);
1401
+ currentInp = normalizedValue;
1402
+ enqueueWebVital("inp", normalizedValue, entry);
1403
+ }
1404
+ };
1405
+ const processExistingWebVitalEntries = (entryType) => {
1406
+ for (const entry of browser.performance?.getEntriesByType?.(entryType) ?? []) {
1407
+ if (webVitalObservationStopped)
1408
+ break;
1409
+ processWebVitalEntry(entry);
1410
+ }
1411
+ };
1412
+ for (const entryType of ["paint", "largest-contentful-paint", "layout-shift", "first-input", "event"]) {
1413
+ if (webVitalObservationStopped)
1414
+ break;
1415
+ processExistingWebVitalEntries(entryType);
1416
+ }
1417
+ const observeWebVitalEntries = (entryType, observeOptions) => {
1418
+ if (webVitalObservationStopped || !runtime.PerformanceObserver)
1419
+ return;
1420
+ try {
1421
+ const observer = new runtime.PerformanceObserver((list) => {
1422
+ for (const entry of list.getEntries()) {
1423
+ if (webVitalObservationStopped || webVitalEvents >= maxWebVitalEvents) {
1424
+ stopWebVitalObservation();
1425
+ break;
1426
+ }
1427
+ processWebVitalEntry(entry);
1428
+ }
1429
+ });
1430
+ let observing = false;
1431
+ try {
1432
+ observer.observe(observeOptions);
1433
+ observing = true;
1434
+ } catch {
1435
+ try {
1436
+ observer.observe({ entryTypes: [entryType] });
1437
+ observing = true;
1438
+ } catch {}
1439
+ }
1440
+ if (observing) {
1441
+ let disconnected = false;
1442
+ const disconnectObserver = () => {
1443
+ if (disconnected)
1444
+ return;
1445
+ disconnected = true;
1446
+ observer.disconnect();
1447
+ };
1448
+ webVitalObserverDisconnects.push(disconnectObserver);
1449
+ restores.push(disconnectObserver);
1450
+ }
1451
+ } catch {}
1452
+ };
1453
+ observeWebVitalEntries("paint", { type: "paint", buffered: true });
1454
+ observeWebVitalEntries("largest-contentful-paint", { type: "largest-contentful-paint", buffered: true });
1455
+ observeWebVitalEntries("layout-shift", { type: "layout-shift", buffered: true });
1456
+ observeWebVitalEntries("first-input", { type: "first-input", buffered: true });
1457
+ observeWebVitalEntries("event", { type: "event", buffered: true, durationThreshold: 16 });
1458
+ }
1459
+ if (opts.captureFetch !== false && runtime.fetch) {
1460
+ const originalFetch = runtime.fetch;
1461
+ const boundFetch = originalFetch.bind(globalThis);
1462
+ runtime.fetch = async (input, init) => {
1463
+ const requestUrl = browserRequestUrlString(input, browser.location?.href);
1464
+ const method = requestMethod(input, init);
1465
+ const collectorRequest = isCollectorRequest(requestUrl, serverUrl);
1466
+ const traceparentSuppressed = browserTraceparentSuppressionReason(requestUrl, browserRequestMode(input, init));
1467
+ let fetchInput = input;
1468
+ let fetchInit = init;
1469
+ const existingTraceparent = traceparentSuppressed ? { present: false } : traceparentInfoFromFetchInput(input, init);
1470
+ let traceContext = existingTraceparent.context;
1471
+ let traceparentInjected = false;
1472
+ if (!traceparentSuppressed && !existingTraceparent.present && !collectorRequest && shouldPropagateTrace(opts, requestUrl, browser.location?.href)) {
1473
+ traceContext = { traceId: randomHex(32), spanId: randomHex(16) };
1474
+ const tracedFetch = fetchInputWithTraceparent(input, init, traceparentFromContext(traceContext));
1475
+ fetchInput = tracedFetch.input;
1476
+ fetchInit = tracedFetch.init;
1477
+ traceparentInjected = true;
1478
+ }
1479
+ const startedAt = Date.now();
1480
+ try {
1481
+ const response = await boundFetch(fetchInput, fetchInit);
1482
+ if (!collectorRequest) {
1483
+ enqueue({
1484
+ type: "span",
1485
+ severity: response.ok ? "info" : "error",
1486
+ trace_id: traceContext?.traceId,
1487
+ span_id: traceContext?.spanId ?? randomId("span"),
1488
+ message: `${method} ${requestUrl}`,
1489
+ body: {
1490
+ name: `${method} ${requestUrl}`,
1491
+ operation: "http.client",
1492
+ status: response.ok ? "ok" : "error",
1493
+ duration_ms: Date.now() - startedAt
1494
+ },
1495
+ attributes: {
1496
+ name: `${method} ${requestUrl}`,
1497
+ operation: "http.client",
1498
+ method,
1499
+ url: requestUrl,
1500
+ status_code: response.status,
1501
+ ok: response.ok,
1502
+ duration_ms: Date.now() - startedAt,
1503
+ traceparent_propagated: traceparentInjected || undefined,
1504
+ traceparent_existing: existingTraceparent.present || undefined,
1505
+ traceparent_suppressed: traceparentSuppressed
1506
+ }
1507
+ });
1508
+ }
1509
+ return response;
1510
+ } catch (error) {
1511
+ if (!collectorRequest) {
1512
+ const normalized = normalizeError(error);
1513
+ enqueue({
1514
+ type: "network",
1515
+ severity: "error",
1516
+ trace_id: traceContext?.traceId,
1517
+ span_id: traceContext?.spanId ?? randomId("span"),
1518
+ message: `${method} ${requestUrl} failed: ${normalized.message}`,
1519
+ body: {
1520
+ error: {
1521
+ type: normalized.type,
1522
+ value: normalized.message,
1523
+ stack_trace: normalized.stack
1524
+ }
1525
+ },
1526
+ attributes: {
1527
+ operation: "http.client",
1528
+ method,
1529
+ url: requestUrl,
1530
+ duration_ms: Date.now() - startedAt,
1531
+ error_type: normalized.type,
1532
+ stack_trace: normalized.stack,
1533
+ traceparent_propagated: traceparentInjected || undefined,
1534
+ traceparent_existing: existingTraceparent.present || undefined,
1535
+ traceparent_suppressed: traceparentSuppressed
1536
+ }
1537
+ });
1538
+ }
1539
+ throw error;
1540
+ }
1541
+ };
1542
+ restores.push(() => {
1543
+ runtime.fetch = originalFetch;
1544
+ });
1545
+ }
1546
+ const beforeUnloadListener = () => {
1547
+ flush();
1548
+ };
1549
+ browser.window.addEventListener("beforeunload", beforeUnloadListener);
1550
+ restores.push(() => {
1551
+ browser.window?.removeEventListener?.("beforeunload", beforeUnloadListener);
1552
+ });
1553
+ return {
1554
+ flush,
1555
+ stop() {
1556
+ stopped = true;
1557
+ while (restores.length)
1558
+ restores.pop()?.();
1559
+ }
1560
+ };
1561
+ }
1562
+ function createBrowserUniversalSpool(opts) {
1563
+ const runtime = globalThis;
1564
+ const storage = runtime.localStorage;
1565
+ const key = browserUniversalSpoolKey(opts);
1566
+ if (!storage || !key) {
1567
+ return {
1568
+ enabled: false,
1569
+ load: () => ({ hadStoredRecord: false, events: [] }),
1570
+ save: () => {}
1571
+ };
1572
+ }
1573
+ return {
1574
+ enabled: true,
1575
+ load() {
1576
+ let raw;
1577
+ try {
1578
+ raw = storage.getItem(key);
1579
+ } catch {
1580
+ return { hadStoredRecord: false, events: [] };
1581
+ }
1582
+ if (!raw)
1583
+ return { hadStoredRecord: false, events: [] };
1584
+ try {
1585
+ const parsed = JSON.parse(raw);
1586
+ if (!isObjectRecord(parsed) || parsed.version !== 1 || !Array.isArray(parsed.events)) {
1587
+ return { hadStoredRecord: true, events: [] };
1588
+ }
1589
+ const events = parsed.events.filter(isValidBrowserUniversalSpoolEvent).map((event) => redactBrowserUniversalEvent(event)).filter(isValidBrowserUniversalSpoolEvent);
1590
+ return { hadStoredRecord: true, events };
1591
+ } catch {
1592
+ return { hadStoredRecord: true, events: [] };
1593
+ }
1594
+ },
1595
+ save(events) {
1596
+ try {
1597
+ const validEvents = events.filter(isValidBrowserUniversalSpoolEvent).map(redactBrowserUniversalEvent).filter(isValidBrowserUniversalSpoolEvent);
1598
+ if (!validEvents.length) {
1599
+ storage.removeItem(key);
1600
+ return;
1601
+ }
1602
+ storage.setItem(key, JSON.stringify({
1603
+ version: 1,
1604
+ events: validEvents
1605
+ }));
1606
+ } catch {}
1607
+ }
1608
+ };
1609
+ }
1610
+ function browserUniversalSpoolKey(opts) {
1611
+ if (!opts.browserSpool && !opts.browserSpoolKey)
1612
+ return;
1613
+ if (opts.browserSpoolKey)
1614
+ return opts.browserSpoolKey;
1615
+ return [
1616
+ "open-logs",
1617
+ "browser-universal-spool",
1618
+ sanitizeSpoolPathPart(opts.projectId),
1619
+ sanitizeSpoolPathPart(opts.sessionId ?? "default")
1620
+ ].join(":");
1621
+ }
1622
+ function persistBrowserUniversalSpool(spool, q) {
1623
+ if (!spool.enabled)
1624
+ return;
1625
+ spool.save(q.map((item) => item.spoolEvent));
1626
+ }
1627
+ function redactBrowserUniversalEvent(event) {
1628
+ const redacted = redactSdkValue(event);
1629
+ return isObjectRecord(redacted) ? redacted : { type: "log", message: String(redacted) };
1630
+ }
1631
+ function stripBrowserUniversalEventIdentity(event) {
1632
+ const stripped = { ...event };
1633
+ for (const key of SDK_BROWSER_FORBIDDEN_IDENTITY_FIELDS)
1634
+ delete stripped[key];
1635
+ if (isObjectRecord(stripped.attributes)) {
1636
+ stripped.attributes = stripBrowserIdentityFields(stripped.attributes);
1637
+ }
1638
+ if (isObjectRecord(stripped.metadata)) {
1639
+ stripped.metadata = stripBrowserIdentityFields(stripped.metadata);
1640
+ }
1641
+ return stripped;
1642
+ }
1643
+ function stripBrowserIdentityFields(value) {
1644
+ const stripped = { ...value };
1645
+ for (const key of SDK_BROWSER_FORBIDDEN_IDENTITY_FIELDS)
1646
+ delete stripped[key];
1647
+ return stripped;
1648
+ }
1649
+ function isValidBrowserUniversalSpoolEvent(event) {
1650
+ if (!isObjectRecord(event))
1651
+ return false;
1652
+ for (const key of Object.keys(event)) {
1653
+ if (!SDK_BROWSER_UNIVERSAL_EVENT_FIELDS.has(key))
1654
+ return false;
1655
+ }
1656
+ if (!SDK_BROWSER_UNIVERSAL_EVENT_TYPES.has(event.type))
1657
+ return false;
1658
+ if (event.source !== undefined && event.source !== "browser")
1659
+ return false;
1660
+ if (event.schema_version !== undefined && (!Number.isInteger(event.schema_version) || Number(event.schema_version) < 1)) {
1661
+ return false;
1662
+ }
1663
+ const severity = event.severity ?? event.level;
1664
+ if (severity !== undefined && severity !== null) {
1665
+ if (typeof severity !== "string" || !SDK_BROWSER_SEVERITIES.has(severity))
1666
+ return false;
1667
+ }
1668
+ if (event.privacy !== undefined && event.privacy !== null) {
1669
+ if (typeof event.privacy !== "string" || !SDK_BROWSER_PRIVACY_CLASSES.has(event.privacy))
1670
+ return false;
1671
+ }
1672
+ for (const key of SDK_BROWSER_STRING_EVENT_FIELDS) {
1673
+ const item = event[key];
1674
+ if (item !== undefined && item !== null && typeof item !== "string")
1675
+ return false;
1676
+ }
1677
+ for (const key of SDK_BROWSER_TIMESTAMP_EVENT_FIELDS) {
1678
+ const item = event[key];
1679
+ if (typeof item === "string" && item.length > 0 && Number.isNaN(new Date(item).getTime())) {
1680
+ return false;
1681
+ }
1682
+ }
1683
+ for (const key of SDK_BROWSER_OBJECT_EVENT_FIELDS) {
1684
+ const item = event[key];
1685
+ if (item !== undefined && !isObjectRecord(item))
1686
+ return false;
1687
+ }
1688
+ if (hasBrowserIdentityFields(event.attributes) || hasBrowserIdentityFields(event.metadata))
1689
+ return false;
1690
+ for (const key of SDK_BROWSER_FORBIDDEN_IDENTITY_FIELDS) {
1691
+ if (event[key] !== undefined && event[key] !== null)
1692
+ return false;
1693
+ }
1694
+ return true;
1695
+ }
1696
+ function hasBrowserIdentityFields(value) {
1697
+ if (!isObjectRecord(value))
1698
+ return false;
1699
+ return SDK_BROWSER_FORBIDDEN_IDENTITY_FIELDS.some((key) => value[key] !== undefined && value[key] !== null);
1700
+ }
1701
+ var SDK_BROWSER_UNIVERSAL_EVENT_FIELDS = new Set([
1702
+ "schema_version",
1703
+ "event_id",
1704
+ "id",
1705
+ "source_event_id",
1706
+ "event_time",
1707
+ "timestamp",
1708
+ "type",
1709
+ "source",
1710
+ "severity",
1711
+ "level",
1712
+ "privacy",
1713
+ "project_id",
1714
+ "page_id",
1715
+ "machine_id",
1716
+ "repo_id",
1717
+ "app_id",
1718
+ "process_id",
1719
+ "run_id",
1720
+ "trace_id",
1721
+ "span_id",
1722
+ "parent_span_id",
1723
+ "session_id",
1724
+ "release_id",
1725
+ "environment",
1726
+ "artifact_id",
1727
+ "message",
1728
+ "body",
1729
+ "attributes",
1730
+ "metadata"
1731
+ ]);
1732
+ var SDK_BROWSER_UNIVERSAL_EVENT_TYPES = new Set([
1733
+ "log",
1734
+ "exception",
1735
+ "span",
1736
+ "metric",
1737
+ "network",
1738
+ "replay",
1739
+ "session"
1740
+ ]);
1741
+ var SDK_BROWSER_SEVERITIES = new Set(["debug", "info", "warn", "error", "fatal"]);
1742
+ var SDK_BROWSER_PRIVACY_CLASSES = new Set(["public", "internal", "sensitive", "secret", "pii"]);
1743
+ var SDK_BROWSER_STRING_EVENT_FIELDS = [
1744
+ "event_id",
1745
+ "id",
1746
+ "source_event_id",
1747
+ "event_time",
1748
+ "timestamp",
1749
+ "source",
1750
+ "severity",
1751
+ "level",
1752
+ "privacy",
1753
+ "project_id",
1754
+ "page_id",
1755
+ "machine_id",
1756
+ "repo_id",
1757
+ "app_id",
1758
+ "process_id",
1759
+ "run_id",
1760
+ "trace_id",
1761
+ "span_id",
1762
+ "parent_span_id",
1763
+ "session_id",
1764
+ "release_id",
1765
+ "environment",
1766
+ "artifact_id",
1767
+ "message"
1768
+ ];
1769
+ var SDK_BROWSER_TIMESTAMP_EVENT_FIELDS = ["event_time", "timestamp"];
1770
+ var SDK_BROWSER_OBJECT_EVENT_FIELDS = ["body", "attributes", "metadata"];
1771
+ var SDK_BROWSER_FORBIDDEN_IDENTITY_FIELDS = [
1772
+ "project_id",
1773
+ "page_id",
1774
+ "machine_id",
1775
+ "repo_id",
1776
+ "app_id",
1777
+ "process_id",
1778
+ "run_id",
1779
+ "artifact_id",
1780
+ "build_id",
1781
+ "agent_id"
1782
+ ];
1783
+ function normalizeError(error) {
1784
+ if (error instanceof Error) {
1785
+ return { type: error.name || "Error", message: error.message || String(error), stack: error.stack };
1786
+ }
1787
+ if (typeof error === "object" && error !== null) {
1788
+ const record = error;
1789
+ return {
1790
+ type: typeof record.name === "string" ? record.name : "Error",
1791
+ message: typeof record.message === "string" ? record.message : safeStringify(error),
1792
+ stack: typeof record.stack === "string" ? record.stack : undefined
1793
+ };
1794
+ }
1795
+ return { type: "Error", message: String(error) };
1796
+ }
1797
+ function formatArgs(args) {
1798
+ return args.map((arg) => {
1799
+ if (typeof arg === "string")
1800
+ return arg;
1801
+ if (arg instanceof Error)
1802
+ return `${arg.name}: ${arg.message}`;
1803
+ if (typeof arg === "object" && arg !== null)
1804
+ return safeStringify(arg);
1805
+ return String(arg);
1806
+ }).join(" ");
1807
+ }
1808
+ function requestUrlString(input) {
1809
+ if (typeof input === "string")
1810
+ return input;
1811
+ if (input instanceof URL)
1812
+ return input.toString();
1813
+ if (typeof Request !== "undefined" && input instanceof Request)
1814
+ return input.url;
1815
+ return String(input);
1816
+ }
1817
+ function browserRequestUrlString(input, baseHref) {
1818
+ const value = requestUrlString(input);
1819
+ if (!baseHref)
1820
+ return value;
1821
+ try {
1822
+ return new URL(value, baseHref).toString();
1823
+ } catch {
1824
+ return value;
1825
+ }
1826
+ }
1827
+ function browserRequestMode(input, init) {
1828
+ if (init?.mode)
1829
+ return init.mode;
1830
+ if (typeof Request !== "undefined" && input instanceof Request)
1831
+ return input.mode;
1832
+ return;
1833
+ }
1834
+ function browserTraceparentSuppressionReason(requestUrl, requestMode) {
1835
+ if (requestMode === "no-cors")
1836
+ return "no-cors";
1837
+ try {
1838
+ const protocol = new URL(requestUrl).protocol;
1839
+ if (protocol !== "http:" && protocol !== "https:")
1840
+ return "non-http";
1841
+ } catch {
1842
+ return;
1843
+ }
1844
+ return;
1845
+ }
1846
+ function browserPerformanceEntryUrl(entry, baseHref) {
1847
+ if (typeof entry.name !== "string" || !entry.name)
1848
+ return;
1849
+ try {
1850
+ return baseHref ? new URL(entry.name, baseHref).toString() : entry.name;
1851
+ } catch {
1852
+ return entry.name;
1853
+ }
1854
+ }
1855
+ function browserPerformanceEntryKey(entry, url, initiatorType) {
1856
+ return [
1857
+ url,
1858
+ initiatorType,
1859
+ roundedNumber(entry.startTime) ?? "",
1860
+ roundedNumber(entry.duration) ?? "",
1861
+ roundedNumber(entry.transferSize) ?? "",
1862
+ roundedNumber(entry.responseStatus) ?? ""
1863
+ ].join("|");
1864
+ }
1865
+ function browserWebVitalEntryKey(metricName, entry) {
1866
+ return [
1867
+ metricName,
1868
+ entry.entryType ?? "",
1869
+ entry.name ?? "",
1870
+ roundedNumber(entry.startTime) ?? "",
1871
+ roundedNumber(entry.duration) ?? "",
1872
+ normalizedWebVitalValue(metricName, entry.value) ?? "",
1873
+ roundedNumber(entry.processingStart) ?? "",
1874
+ roundedNumber(entry.interactionId) ?? "",
1875
+ roundedNumber(entry.renderTime) ?? "",
1876
+ roundedNumber(entry.loadTime) ?? ""
1877
+ ].join("|");
1878
+ }
1879
+ function normalizedWebVitalValue(metricName, value) {
1880
+ if (typeof value !== "number" || !Number.isFinite(value))
1881
+ return;
1882
+ return metricName === "cls" ? Math.round(value * 1000) / 1000 : Math.round(value);
1883
+ }
1884
+ function webVitalRating(metricName, value) {
1885
+ const thresholds = WEB_VITAL_THRESHOLDS[metricName];
1886
+ if (!thresholds)
1887
+ return;
1888
+ if (value <= thresholds.good)
1889
+ return "good";
1890
+ if (value <= thresholds.needsImprovement)
1891
+ return "needs_improvement";
1892
+ return "poor";
1893
+ }
1894
+ var WEB_VITAL_THRESHOLDS = {
1895
+ fcp: { good: 1800, needsImprovement: 3000 },
1896
+ lcp: { good: 2500, needsImprovement: 4000 },
1897
+ cls: { good: 0.1, needsImprovement: 0.25 },
1898
+ fid: { good: 100, needsImprovement: 300 },
1899
+ inp: { good: 200, needsImprovement: 500 }
1900
+ };
1901
+ function roundedNumber(value) {
1902
+ return typeof value === "number" && Number.isFinite(value) ? Math.round(value) : undefined;
1903
+ }
1904
+ function requestMethod(input, init) {
1905
+ if (init?.method)
1906
+ return init.method.toUpperCase();
1907
+ if (typeof Request !== "undefined" && input instanceof Request && input.method)
1908
+ return input.method.toUpperCase();
1909
+ return "GET";
1910
+ }
1911
+ function traceparentInfoFromFetchInput(input, init) {
1912
+ if (init?.headers !== undefined) {
1913
+ const header = fetchHeaderValue(init.headers, "traceparent");
1914
+ return {
1915
+ present: header.present,
1916
+ value: header.value,
1917
+ context: parseTraceparent(header.value)
1918
+ };
1919
+ }
1920
+ if (typeof Request !== "undefined" && input instanceof Request) {
1921
+ const value = input.headers.get("traceparent") ?? undefined;
1922
+ return {
1923
+ present: value !== undefined,
1924
+ value,
1925
+ context: parseTraceparent(value)
1926
+ };
1927
+ }
1928
+ return { present: false };
1929
+ }
1930
+ function fetchInputWithTraceparent(input, init, traceparent) {
1931
+ const headers = new Headers(init?.headers ?? (typeof Request !== "undefined" && input instanceof Request ? input.headers : undefined));
1932
+ headers.set("traceparent", traceparent);
1933
+ return { input, init: { ...init ?? {}, headers } };
1934
+ }
1935
+ function fetchHeaderValue(headers, name) {
1936
+ if (!headers)
1937
+ return { present: false };
1938
+ if (typeof Headers !== "undefined") {
1939
+ const value = new Headers(headers).get(name) ?? undefined;
1940
+ return { present: value !== undefined, value };
1941
+ }
1942
+ const normalizedName = name.toLowerCase();
1943
+ if (Array.isArray(headers)) {
1944
+ const pairs = headers.filter((entry) => entry[0]?.toLowerCase() === normalizedName);
1945
+ return pairs.length ? { present: true, value: pairs.map((entry) => entry[1]).join(", ") } : { present: false };
1946
+ }
1947
+ for (const [key, value] of Object.entries(headers)) {
1948
+ if (key.toLowerCase() === normalizedName)
1949
+ return { present: true, value };
1950
+ }
1951
+ return { present: false };
1952
+ }
1953
+ function shouldPropagateTrace(opts, requestUrl, baseHref) {
1954
+ if (opts.propagateTrace !== true)
1955
+ return false;
1956
+ const targets = opts.tracePropagationTargets ?? [];
1957
+ if (targets.length > 0) {
1958
+ return targets.some((target) => tracePropagationTargetMatches(target, requestUrl));
1959
+ }
1960
+ if (baseHref)
1961
+ return sameOrigin(requestUrl, baseHref);
1962
+ return true;
1963
+ }
1964
+ function tracePropagationTargetMatches(target, requestUrl) {
1965
+ if (typeof target === "string") {
1966
+ try {
1967
+ const parsedTarget = new URL(target);
1968
+ const parsedRequest = new URL(requestUrl);
1969
+ if (parsedRequest.origin !== parsedTarget.origin)
1970
+ return false;
1971
+ return `${parsedRequest.pathname}${parsedRequest.search}`.startsWith(`${parsedTarget.pathname}${parsedTarget.search}`);
1972
+ } catch {
1973
+ return requestUrl.startsWith(target);
1974
+ }
1975
+ }
1976
+ target.lastIndex = 0;
1977
+ return target.test(requestUrl);
1978
+ }
1979
+ function sameOrigin(requestUrl, baseHref) {
1980
+ try {
1981
+ return new URL(requestUrl, baseHref).origin === new URL(baseHref).origin;
1982
+ } catch {
1983
+ return false;
1984
+ }
1985
+ }
1986
+ function traceparentFromContext(context) {
1987
+ return `00-${context.traceId}-${context.spanId}-01`;
1988
+ }
1989
+ function routeForRequest(request, route) {
1990
+ if (typeof route === "function")
1991
+ return route(request) ?? safeUrlParts(request.url).path;
1992
+ if (route)
1993
+ return route;
1994
+ return safeUrlParts(request.url).path;
1995
+ }
1996
+ async function emitNodeHttpRequestTelemetry(input) {
1997
+ const source = input.opts.source ?? input.opts.framework ?? runtimeName();
1998
+ const client = input.opts.client ?? new LogsClient({ ...input.opts, source });
1999
+ const request = nodeRequestToRequest(input.request);
2000
+ const { traceId, parentSpanId } = traceContextFromRequest(request);
2001
+ const spanId = randomHex(16);
2002
+ const method = request.method || "GET";
2003
+ const url = safeUrlParts(request.url);
2004
+ const operation = input.opts.operation ?? "http.server";
2005
+ const durationMs = Date.now() - input.startedAt;
2006
+ const route = nodeRouteForRequest(input.request, input.opts.route);
2007
+ const name = `${method} ${route}`;
2008
+ const statusCode = nodeResponseStatus(input.response, input.error);
2009
+ const normalized = input.error ? normalizeError(input.error) : undefined;
2010
+ const response = nodeResponseToResponse(input.response, statusCode, input.opts.responseHeaderNames);
2011
+ const span = httpServerSpanEvent({
2012
+ source,
2013
+ request,
2014
+ response,
2015
+ traceId,
2016
+ spanId,
2017
+ parentSpanId,
2018
+ method,
2019
+ route,
2020
+ url,
2021
+ operation,
2022
+ name,
2023
+ statusCode,
2024
+ durationMs,
2025
+ startedIso: input.startedIso,
2026
+ requestHeaderNames: input.opts.requestHeaderNames,
2027
+ responseHeaderNames: input.opts.responseHeaderNames,
2028
+ framework: input.opts.framework,
2029
+ errorType: normalized?.type
2030
+ });
2031
+ if (!input.error || input.error instanceof ResponseClosedError) {
2032
+ await sendRequestTelemetry(client, [span], input.opts.waitForTelemetry);
2033
+ return;
2034
+ }
2035
+ const exception = {
2036
+ type: "exception",
2037
+ source,
2038
+ severity: "error",
2039
+ trace_id: traceId,
2040
+ span_id: spanId,
2041
+ parent_span_id: parentSpanId,
2042
+ message: normalized?.message ?? "Request failed",
2043
+ body: {
2044
+ exception: {
2045
+ type: normalized?.type,
2046
+ value: normalized?.message,
2047
+ stack_trace: normalized?.stack,
2048
+ handled: false,
2049
+ mechanism: `${input.opts.framework ?? "node-http"}.request`
2050
+ }
2051
+ },
2052
+ attributes: {
2053
+ exception_type: normalized?.type,
2054
+ stack_trace: normalized?.stack,
2055
+ handled: false,
2056
+ mechanism: `${input.opts.framework ?? "node-http"}.request`,
2057
+ operation,
2058
+ method,
2059
+ route,
2060
+ url_scheme: url.scheme,
2061
+ url_host: url.host,
2062
+ url_path: url.path,
2063
+ query_present: url.queryPresent,
2064
+ framework: input.opts.framework,
2065
+ duration_ms: durationMs
2066
+ }
2067
+ };
2068
+ await sendRequestTelemetry(client, [span, exception], input.opts.waitForTelemetry);
2069
+ }
2070
+ function nodeRequestToRequest(request) {
2071
+ return new Request(nodeRequestUrl(request), {
2072
+ method: nodeRequestMethod(request),
2073
+ headers: nodeHeaders(request)
150
2074
  });
151
- window.addEventListener("beforeunload", flush);
2075
+ }
2076
+ function nodeRequestUrl(request) {
2077
+ const input = request.originalUrl ?? request.url ?? request.path ?? "/";
2078
+ if (/^https?:\/\//i.test(input))
2079
+ return safeAbsoluteUrl(input) ?? "http://localhost/";
2080
+ const path = input.startsWith("/") ? input : `/${input}`;
2081
+ const host = safeNodeHost(firstHeaderValue(nodeRequestHeader(request, "host")) ?? request.hostname) ?? "localhost";
2082
+ const scheme = safeNodeScheme(request.protocol ?? firstHeaderValue(nodeRequestHeader(request, "x-forwarded-proto")));
2083
+ return safeAbsoluteUrl(`${scheme}://${host}${path}`) ?? `http://localhost${path}`;
2084
+ }
2085
+ function safeAbsoluteUrl(input) {
2086
+ try {
2087
+ return new URL(input).toString();
2088
+ } catch {
2089
+ return;
2090
+ }
2091
+ }
2092
+ function safeNodeHost(input) {
2093
+ const host = input?.trim();
2094
+ if (!host || /[\s/\\?#@]/.test(host))
2095
+ return;
2096
+ try {
2097
+ const parsed = new URL(`http://${host}/`);
2098
+ if (parsed.username || parsed.password || parsed.search || parsed.hash)
2099
+ return;
2100
+ if (parsed.host.toLowerCase() !== host.toLowerCase())
2101
+ return;
2102
+ return host;
2103
+ } catch {
2104
+ return;
2105
+ }
2106
+ }
2107
+ function safeNodeScheme(input) {
2108
+ const scheme = input?.split(",")[0]?.trim().replace(/:$/, "").toLowerCase();
2109
+ return scheme === "https" ? "https" : "http";
2110
+ }
2111
+ function nodeRequestMethod(request) {
2112
+ const method = request.method?.toUpperCase() ?? "GET";
2113
+ return /^[A-Z][A-Z0-9-]*$/.test(method) ? method : "GET";
2114
+ }
2115
+ function nodeHeaders(request) {
2116
+ const headers = new Headers;
2117
+ if (request.headers instanceof Headers) {
2118
+ request.headers.forEach((value, name) => headers.set(name, value));
2119
+ return headers;
2120
+ }
2121
+ for (const [name, value] of Object.entries(request.headers ?? {})) {
2122
+ setHeaderValue(headers, name, value);
2123
+ }
2124
+ for (const name of ["host", "traceparent", "tracestate", "x-forwarded-proto"]) {
2125
+ const value = firstHeaderValue(nodeRequestHeader(request, name));
2126
+ if (value && !headers.has(name))
2127
+ headers.set(name, value);
2128
+ }
2129
+ return headers;
2130
+ }
2131
+ function setHeaderValue(headers, name, value) {
2132
+ if (value === undefined)
2133
+ return;
2134
+ if (Array.isArray(value)) {
2135
+ headers.set(name, value.map(String).join(", "));
2136
+ return;
2137
+ }
2138
+ headers.set(name, String(value));
2139
+ }
2140
+ function nodeRequestHeader(request, name) {
2141
+ const getter = request.header ?? request.get;
2142
+ const direct = getter?.call(request, name);
2143
+ if (direct !== undefined)
2144
+ return direct;
2145
+ const headers = request.headers;
2146
+ if (!headers)
2147
+ return;
2148
+ if (headers instanceof Headers)
2149
+ return headers.get(name) ?? undefined;
2150
+ const lowerName = name.toLowerCase();
2151
+ for (const [headerName, value] of Object.entries(headers)) {
2152
+ if (headerName.toLowerCase() === lowerName)
2153
+ return value;
2154
+ }
2155
+ return;
2156
+ }
2157
+ function nodeRouteForRequest(request, route) {
2158
+ if (typeof route === "function")
2159
+ return route(request) ?? nodeRouteFallback(request);
2160
+ if (route)
2161
+ return route;
2162
+ return nodeRouteFallback(request);
2163
+ }
2164
+ function nodeRouteFallback(request) {
2165
+ const routePath = routePathValue(request.route);
2166
+ if (routePath)
2167
+ return joinRoutePath(request.baseUrl, routePath);
2168
+ if (request.routerPath)
2169
+ return request.routerPath;
2170
+ if (request.routeOptions?.url)
2171
+ return request.routeOptions.url;
2172
+ if (request.path)
2173
+ return request.path;
2174
+ return safeUrlParts(nodeRequestUrl(request)).path;
2175
+ }
2176
+ function routePathValue(route) {
2177
+ if (!route)
2178
+ return;
2179
+ if (typeof route === "string")
2180
+ return route;
2181
+ const path = route.path;
2182
+ if (!path)
2183
+ return;
2184
+ if (Array.isArray(path))
2185
+ return path.map((part) => String(part)).join("|");
2186
+ return String(path);
2187
+ }
2188
+ function joinRoutePath(baseUrl, routePath) {
2189
+ if (!baseUrl)
2190
+ return routePath;
2191
+ if (routePath === "/")
2192
+ return baseUrl || "/";
2193
+ return `${baseUrl.replace(/\/$/, "")}/${routePath.replace(/^\//, "")}`;
2194
+ }
2195
+ function nodeResponseStatus(response, error) {
2196
+ if (error instanceof ResponseClosedError)
2197
+ return 499;
2198
+ const status = response.statusCode ?? response.status;
2199
+ if (typeof status === "number" && status >= 100 && status <= 599) {
2200
+ if (error && status < 400)
2201
+ return 500;
2202
+ return status;
2203
+ }
2204
+ return error ? 500 : 200;
2205
+ }
2206
+ function nodeResponseToResponse(response, status, headerNames) {
2207
+ const headers = new Headers;
2208
+ for (const name of headerNames ?? []) {
2209
+ const value = firstHeaderValue(nodeResponseHeader(response, name));
2210
+ if (value !== undefined)
2211
+ headers.set(name, value);
2212
+ }
2213
+ return new Response(null, { status: status >= 200 ? status : 200, headers });
2214
+ }
2215
+ function nodeResponseHeader(response, name) {
2216
+ const direct = response.getHeader?.call(response, name);
2217
+ if (direct !== undefined)
2218
+ return direct;
2219
+ const headers = response.headers;
2220
+ if (!headers)
2221
+ return;
2222
+ if (headers instanceof Headers)
2223
+ return headers.get(name) ?? undefined;
2224
+ const lowerName = name.toLowerCase();
2225
+ for (const [headerName, value] of Object.entries(headers)) {
2226
+ if (headerName.toLowerCase() === lowerName)
2227
+ return value;
2228
+ }
2229
+ return;
2230
+ }
2231
+ function firstHeaderValue(value) {
2232
+ if (value === undefined)
2233
+ return;
2234
+ if (Array.isArray(value))
2235
+ return value.length ? String(value[0]) : undefined;
2236
+ return String(value);
2237
+ }
2238
+ function addNodeListener(target, event, listener) {
2239
+ if (typeof target.on === "function") {
2240
+ target.on(event, listener);
2241
+ return () => removeNodeListener(target, event, listener);
2242
+ }
2243
+ if (typeof target.once === "function") {
2244
+ const onceListener = (...args) => {
2245
+ removeNodeListener(target, event, onceListener);
2246
+ listener(...args);
2247
+ };
2248
+ target.once(event, onceListener);
2249
+ return () => removeNodeListener(target, event, onceListener);
2250
+ }
2251
+ return () => {};
2252
+ }
2253
+ function cleanupNodeListeners(cleanups) {
2254
+ while (cleanups.length)
2255
+ cleanups.pop()?.();
2256
+ }
2257
+ function removeNodeListener(target, event, listener) {
2258
+ if (typeof target.off === "function") {
2259
+ target.off(event, listener);
2260
+ return;
2261
+ }
2262
+ target.removeListener?.(event, listener);
2263
+ }
2264
+ function nodeErrorMonitorEvent() {
2265
+ try {
2266
+ const runtime = globalThis;
2267
+ const errorMonitor = runtime.process?.getBuiltinModule?.("node:events")?.errorMonitor;
2268
+ return typeof errorMonitor === "symbol" ? errorMonitor : undefined;
2269
+ } catch {
2270
+ return;
2271
+ }
2272
+ }
2273
+ function setExpressCapture(request, capture) {
2274
+ if (request && typeof request === "object") {
2275
+ request[expressCaptureSymbol] = capture;
2276
+ }
2277
+ }
2278
+ function getExpressCapture(request) {
2279
+ return request[expressCaptureSymbol];
2280
+ }
2281
+ function clearExpressCapture(request) {
2282
+ if (request && typeof request === "object") {
2283
+ delete request[expressCaptureSymbol];
2284
+ }
2285
+ }
2286
+ function fastifyRequestLike(request) {
2287
+ if (!request.raw)
2288
+ return request;
2289
+ return {
2290
+ ...request.raw,
2291
+ method: request.method ?? request.raw.method,
2292
+ url: request.url ?? request.raw.url,
2293
+ headers: request.headers ?? request.raw.headers,
2294
+ header: request.header?.bind(request) ?? request.raw.header?.bind(request.raw),
2295
+ get: request.get?.bind(request) ?? request.raw.get?.bind(request.raw),
2296
+ on: request.raw.on?.bind(request.raw),
2297
+ once: request.raw.once?.bind(request.raw),
2298
+ off: request.raw.off?.bind(request.raw),
2299
+ removeListener: request.raw.removeListener?.bind(request.raw),
2300
+ routeOptions: request.routeOptions ?? request.raw.routeOptions,
2301
+ routerPath: request.routerPath ?? request.raw.routerPath
2302
+ };
2303
+ }
2304
+ function fastifyResponseLike(reply) {
2305
+ if (!reply.raw)
2306
+ return reply;
2307
+ return {
2308
+ ...reply.raw,
2309
+ statusCode: reply.statusCode ?? reply.raw.statusCode,
2310
+ status: reply.status ?? reply.raw.status,
2311
+ headers: reply.headers ?? reply.raw.headers,
2312
+ getHeader: reply.getHeader?.bind(reply) ?? reply.raw.getHeader?.bind(reply.raw),
2313
+ on: reply.raw.on?.bind(reply.raw),
2314
+ once: reply.raw.once?.bind(reply.raw),
2315
+ off: reply.raw.off?.bind(reply.raw),
2316
+ removeListener: reply.raw.removeListener?.bind(reply.raw)
2317
+ };
2318
+ }
2319
+ function fastifyRoute(request) {
2320
+ return request.routeOptions?.url ?? request.routerPath;
2321
+ }
2322
+
2323
+ class ResponseClosedError extends Error {
2324
+ constructor() {
2325
+ super("Response closed before finish");
2326
+ this.name = "ResponseClosedError";
2327
+ }
2328
+ }
2329
+ function safeUrlParts(input) {
2330
+ try {
2331
+ const url = new URL(input);
2332
+ return {
2333
+ scheme: url.protocol.replace(/:$/, "") || null,
2334
+ host: url.host || null,
2335
+ path: url.pathname || "/",
2336
+ queryPresent: url.search.length > 0
2337
+ };
2338
+ } catch {
2339
+ return { scheme: null, host: null, path: input.split("?")[0] || "/", queryPresent: input.includes("?") };
2340
+ }
2341
+ }
2342
+ function traceContextFromRequest(request) {
2343
+ const parsed = parseTraceparent(request.headers.get("traceparent"));
2344
+ if (parsed)
2345
+ return { traceId: parsed.traceId, parentSpanId: parsed.spanId };
2346
+ return { traceId: randomHex(32) };
2347
+ }
2348
+ function parseTraceparent(value) {
2349
+ const match = value?.match(/^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-[0-9a-f]{2}$/);
2350
+ const version = match?.[1];
2351
+ const traceId = match?.[2];
2352
+ const spanId = match?.[3];
2353
+ if (version === "ff")
2354
+ return;
2355
+ if (!traceId || !spanId)
2356
+ return;
2357
+ if (/^0+$/.test(traceId) || /^0+$/.test(spanId))
2358
+ return;
2359
+ return { traceId, spanId };
2360
+ }
2361
+ function responseLike(value) {
2362
+ if (typeof Response !== "undefined" && value instanceof Response)
2363
+ return value;
2364
+ if (value && typeof value === "object" && "status" in value && typeof value.status === "number") {
2365
+ return value;
2366
+ }
2367
+ return;
2368
+ }
2369
+ function httpServerSpanEvent(input) {
2370
+ const status = input.statusCode ?? 0;
2371
+ const statusText = status >= 500 || input.errorType ? "error" : "ok";
2372
+ return {
2373
+ type: "span",
2374
+ source: input.source,
2375
+ severity: status >= 500 || input.errorType ? "error" : status >= 400 ? "warn" : "info",
2376
+ trace_id: input.traceId,
2377
+ span_id: input.spanId,
2378
+ parent_span_id: input.parentSpanId,
2379
+ message: input.name,
2380
+ body: {
2381
+ name: input.name,
2382
+ operation: input.operation,
2383
+ status: statusText,
2384
+ started_at: input.startedIso,
2385
+ duration_ms: input.durationMs
2386
+ },
2387
+ attributes: {
2388
+ name: input.name,
2389
+ operation: input.operation,
2390
+ method: input.method,
2391
+ route: input.route,
2392
+ framework: input.framework,
2393
+ status: statusText,
2394
+ status_code: input.statusCode,
2395
+ duration_ms: input.durationMs,
2396
+ started_at: input.startedIso,
2397
+ url_scheme: input.url.scheme,
2398
+ url_host: input.url.host,
2399
+ url_path: input.url.path,
2400
+ query_present: input.url.queryPresent,
2401
+ request_headers: pickHeaders(input.request.headers, input.requestHeaderNames),
2402
+ response_headers: input.response ? pickHeaders(input.response.headers, input.responseHeaderNames) : undefined,
2403
+ error_type: input.errorType
2404
+ }
2405
+ };
2406
+ }
2407
+ function pickHeaders(headers, names) {
2408
+ if (!names?.length)
2409
+ return;
2410
+ const picked = {};
2411
+ for (const name of names) {
2412
+ const value = headers.get(name);
2413
+ if (value !== null)
2414
+ picked[name.toLowerCase()] = value;
2415
+ }
2416
+ return Object.keys(picked).length ? picked : undefined;
2417
+ }
2418
+ function structuredLogQuery(opts) {
2419
+ const params = new URLSearchParams;
2420
+ setParam(params, "format", opts.format);
2421
+ setParam(params, "source", opts.source);
2422
+ setParam(params, "service", opts.service);
2423
+ setParam(params, "project_id", opts.projectId);
2424
+ setParam(params, "page_id", opts.pageId);
2425
+ setParam(params, "machine_id", opts.machineId);
2426
+ setParam(params, "repo_id", opts.repoId);
2427
+ setParam(params, "app_id", opts.appId);
2428
+ setParam(params, "process_id", opts.processId);
2429
+ setParam(params, "run_id", opts.runId);
2430
+ setParam(params, "trace_id", opts.traceId);
2431
+ setParam(params, "span_id", opts.spanId);
2432
+ setParam(params, "parent_span_id", opts.parentSpanId);
2433
+ setParam(params, "session_id", opts.sessionId);
2434
+ setParam(params, "release_id", opts.releaseId);
2435
+ setParam(params, "environment", opts.environment);
2436
+ setParam(params, "agent", opts.agent);
2437
+ setParam(params, "url", opts.logUrl);
2438
+ const query = params.toString();
2439
+ return query ? `?${query}` : "";
2440
+ }
2441
+ function setParam(params, key, value) {
2442
+ if (value !== undefined && value !== "")
2443
+ params.set(key, value);
2444
+ }
2445
+ function structuredLogBatchBody(records, opts) {
2446
+ if (!opts.metadata && !opts.sourceEventPrefix)
2447
+ return records;
2448
+ return {
2449
+ logs: records,
2450
+ metadata: opts.metadata,
2451
+ source_event_prefix: opts.sourceEventPrefix
2452
+ };
2453
+ }
2454
+ function createStructuredLogQueue(opts) {
2455
+ const client = new LogsClient(opts);
2456
+ const maxBatchSize = Math.max(1, opts.maxBatchSize ?? 20);
2457
+ const maxQueueSize = Math.max(1, opts.maxQueueSize ?? 1e4);
2458
+ const maxRetries = Math.max(0, opts.maxRetries ?? 3);
2459
+ const retryBaseDelayMs = Math.max(0, opts.retryBaseDelayMs ?? 250);
2460
+ const retryMaxDelayMs = Math.max(retryBaseDelayMs, opts.retryMaxDelayMs ?? 5000);
2461
+ const flushIntervalMs = opts.flushIntervalMs ?? 2000;
2462
+ const transportId = randomId(`${opts.format ?? "structured"}_transport`);
2463
+ const transportSendOptions = copyStructuredLogSendOptions(opts);
2464
+ const spooledTransportSendOptions = redactStructuredLogSendOptions(transportSendOptions);
2465
+ const spool = createStructuredLogSpool(opts);
2466
+ let q = [];
2467
+ const stats = {
2468
+ enqueued: 0,
2469
+ sent: 0,
2470
+ dropped: 0,
2471
+ retries: 0,
2472
+ failed_batches: 0,
2473
+ spool_loaded: 0,
2474
+ spool_dropped: 0,
2475
+ spool_errors: 0
2476
+ };
2477
+ let batchNumber = 0;
2478
+ let recordNumber = 0;
2479
+ let stopped = false;
2480
+ let inFlight;
2481
+ let activeBatch;
2482
+ const nextBatchPrefix = (sourceEventPrefix) => {
2483
+ batchNumber += 1;
2484
+ return `${redactSdkString(sourceEventPrefix ?? "sdk-structured-transport")}:${transportId}:${batchNumber}`;
2485
+ };
2486
+ const nextRecordId = () => {
2487
+ recordNumber += 1;
2488
+ return `${transportId}:record:${recordNumber}`;
2489
+ };
2490
+ const persistSpool = () => {
2491
+ if (!spool.enabled)
2492
+ return;
2493
+ try {
2494
+ spool.save(q);
2495
+ } catch (error) {
2496
+ stats.spool_errors += 1;
2497
+ opts.onError?.(error);
2498
+ }
2499
+ };
2500
+ const dropItem = (item, reason, error) => {
2501
+ stats.dropped += 1;
2502
+ if (spool.enabled)
2503
+ stats.spool_dropped += 1;
2504
+ opts.onDrop?.({ reason, record: item.record, attempts: item.attempts, error });
2505
+ };
2506
+ if (spool.enabled) {
2507
+ try {
2508
+ const loaded = spool.load();
2509
+ q = loaded.items;
2510
+ stats.spool_loaded = q.length;
2511
+ stats.spool_errors += loaded.errors;
2512
+ for (const item of loaded.dropped)
2513
+ dropItem(item, "queue_full");
2514
+ if (loaded.errors || loaded.dropped.length)
2515
+ persistSpool();
2516
+ } catch (error) {
2517
+ stats.spool_errors += 1;
2518
+ opts.onError?.(error);
2519
+ }
2520
+ }
2521
+ const makeQueueSpace = (incoming) => {
2522
+ while (q.length >= maxQueueSize) {
2523
+ const dropIndex = q.findIndex((item) => !activeBatch?.includes(item));
2524
+ if (dropIndex < 0) {
2525
+ dropItem({ record: incoming, attempts: 0 }, "queue_full");
2526
+ return false;
2527
+ }
2528
+ const dropped = q.splice(dropIndex, 1)[0];
2529
+ if (dropped)
2530
+ dropItem(dropped, "queue_full");
2531
+ persistSpool();
2532
+ }
2533
+ return true;
2534
+ };
2535
+ const retryDelay = (attempts) => {
2536
+ if (retryBaseDelayMs === 0)
2537
+ return 0;
2538
+ return Math.min(retryMaxDelayMs, retryBaseDelayMs * 2 ** Math.max(0, attempts - 1));
2539
+ };
2540
+ const recordForTransport = (item) => {
2541
+ if (!isObjectRecord(item.record) || hasStructuredProducerId(item.record))
2542
+ return item.record;
2543
+ return { ...item.record, _open_logs_event_id: item.eventId };
2544
+ };
2545
+ const flush = async () => {
2546
+ if (inFlight)
2547
+ return inFlight;
2548
+ inFlight = (async () => {
2549
+ let firstDroppedError;
2550
+ while (q.length) {
2551
+ const batch = nextStructuredLogBatch(q, maxBatchSize);
2552
+ const batchSendOptions = batch[0]?.sendOptions ?? transportSendOptions;
2553
+ const batchPrefix = batch[0]?.batchPrefix ?? nextBatchPrefix(batchSendOptions.sourceEventPrefix);
2554
+ for (const item of batch)
2555
+ item.batchPrefix = batchPrefix;
2556
+ persistSpool();
2557
+ activeBatch = batch;
2558
+ try {
2559
+ await client.pushStructuredLogs(batch.map(recordForTransport), {
2560
+ ...batchSendOptions,
2561
+ sourceEventPrefix: batchPrefix
2562
+ });
2563
+ q.splice(0, batch.length);
2564
+ stats.sent += batch.length;
2565
+ persistSpool();
2566
+ } catch (error) {
2567
+ stats.failed_batches += 1;
2568
+ for (const item of batch)
2569
+ item.attempts += 1;
2570
+ const exhausted = batch.filter((item) => item.attempts > maxRetries);
2571
+ if (spool.enabled && exhausted.length) {
2572
+ for (const item of exhausted)
2573
+ item.attempts = 0;
2574
+ opts.onError?.(error);
2575
+ firstDroppedError ??= error;
2576
+ persistSpool();
2577
+ break;
2578
+ }
2579
+ for (const item of exhausted) {
2580
+ const index = q.indexOf(item);
2581
+ if (index >= 0)
2582
+ q.splice(index, 1);
2583
+ dropItem(item, "retries_exhausted", error);
2584
+ }
2585
+ if (exhausted.length)
2586
+ opts.onError?.(error);
2587
+ if (exhausted.length)
2588
+ firstDroppedError ??= error;
2589
+ persistSpool();
2590
+ const retryable = batch.filter((item) => item.attempts <= maxRetries && q.includes(item));
2591
+ if (!retryable.length)
2592
+ continue;
2593
+ const attempts = Math.max(...retryable.map((item) => item.attempts));
2594
+ const nextDelayMs = retryDelay(attempts);
2595
+ stats.retries += 1;
2596
+ opts.onRetry?.({
2597
+ error,
2598
+ attempts,
2599
+ pending: q.length,
2600
+ next_delay_ms: nextDelayMs
2601
+ });
2602
+ if (nextDelayMs > 0)
2603
+ await sleep(nextDelayMs);
2604
+ } finally {
2605
+ activeBatch = undefined;
2606
+ }
2607
+ }
2608
+ if (firstDroppedError)
2609
+ throw toError(firstDroppedError);
2610
+ })().finally(() => {
2611
+ inFlight = undefined;
2612
+ });
2613
+ return inFlight;
2614
+ };
2615
+ const interval = setInterval(() => {
2616
+ if (!q.length || stopped)
2617
+ return;
2618
+ flush().catch(opts.onError ?? noop);
2619
+ }, flushIntervalMs);
2620
+ return {
2621
+ enqueue(record) {
2622
+ if (stopped)
2623
+ return;
2624
+ if (!makeQueueSpace(record))
2625
+ return;
2626
+ q.push({
2627
+ record,
2628
+ spoolRecord: redactStructuredSpoolRecord(record),
2629
+ sendOptions: transportSendOptions,
2630
+ spoolSendOptions: spooledTransportSendOptions,
2631
+ attempts: 0,
2632
+ eventId: nextRecordId(),
2633
+ createdAt: new Date().toISOString()
2634
+ });
2635
+ stats.enqueued += 1;
2636
+ persistSpool();
2637
+ },
2638
+ shouldFlush() {
2639
+ return q.length >= maxBatchSize;
2640
+ },
2641
+ async flush() {
2642
+ await flush();
2643
+ },
2644
+ stats() {
2645
+ return {
2646
+ pending: q.length,
2647
+ in_flight: Boolean(inFlight),
2648
+ enqueued: stats.enqueued,
2649
+ sent: stats.sent,
2650
+ dropped: stats.dropped,
2651
+ retries: stats.retries,
2652
+ failed_batches: stats.failed_batches,
2653
+ max_queue_size: maxQueueSize,
2654
+ spool_enabled: spool.enabled,
2655
+ spool_pending: spool.enabled ? q.length : 0,
2656
+ spool_loaded: stats.spool_loaded,
2657
+ spool_dropped: stats.spool_dropped,
2658
+ spool_errors: stats.spool_errors
2659
+ };
2660
+ },
2661
+ stop() {
2662
+ stopped = true;
2663
+ clearInterval(interval);
2664
+ }
2665
+ };
2666
+ }
2667
+ function createStructuredLogSpool(opts) {
2668
+ const filePath = structuredLogSpoolPath(opts);
2669
+ const builtins = nodeBuiltins();
2670
+ const spooledTransportSendOptions = redactStructuredLogSendOptions(copyStructuredLogSendOptions(opts));
2671
+ if (!filePath || !builtins) {
2672
+ return {
2673
+ enabled: false,
2674
+ load: () => ({ items: [], dropped: [], errors: 0 }),
2675
+ save: () => {}
2676
+ };
2677
+ }
2678
+ const { fs, path } = builtins;
2679
+ return {
2680
+ enabled: true,
2681
+ load() {
2682
+ if (!fs.existsSync(filePath))
2683
+ return { items: [], dropped: [], errors: 0 };
2684
+ const contents = fs.readFileSync(filePath, "utf8");
2685
+ const items = [];
2686
+ let errors = 0;
2687
+ for (const line of contents.split(/\n/)) {
2688
+ const trimmed = line.trim();
2689
+ if (!trimmed)
2690
+ continue;
2691
+ let parsed;
2692
+ try {
2693
+ const value = JSON.parse(trimmed);
2694
+ if (!isObjectRecord(value)) {
2695
+ errors += 1;
2696
+ continue;
2697
+ }
2698
+ parsed = value;
2699
+ } catch {
2700
+ errors += 1;
2701
+ continue;
2702
+ }
2703
+ if (parsed.version !== 1 || typeof parsed.event_id !== "string") {
2704
+ errors += 1;
2705
+ continue;
2706
+ }
2707
+ if (!isObjectRecord(parsed.record)) {
2708
+ errors += 1;
2709
+ continue;
2710
+ }
2711
+ const sendOptions = loadStructuredSpoolSendOptions(parsed.send_options, spooledTransportSendOptions);
2712
+ if (!sendOptions) {
2713
+ errors += 1;
2714
+ continue;
2715
+ }
2716
+ const record = redactStructuredSpoolRecord(parsed.record);
2717
+ items.push({
2718
+ record,
2719
+ spoolRecord: record,
2720
+ sendOptions,
2721
+ spoolSendOptions: sendOptions,
2722
+ attempts: typeof parsed.attempts === "number" && parsed.attempts >= 0 ? Math.floor(parsed.attempts) : 0,
2723
+ eventId: parsed.event_id,
2724
+ createdAt: typeof parsed.created_at === "string" ? parsed.created_at : new Date().toISOString(),
2725
+ batchPrefix: typeof parsed.batch_prefix === "string" ? redactStructuredBatchPrefix(parsed.batch_prefix) : undefined
2726
+ });
2727
+ }
2728
+ const maxItems = Math.max(1, opts.maxQueueSize ?? 1e4);
2729
+ const dropped = items.length > maxItems ? items.slice(0, items.length - maxItems) : [];
2730
+ return {
2731
+ items: items.slice(-maxItems),
2732
+ dropped,
2733
+ errors
2734
+ };
2735
+ },
2736
+ save(items) {
2737
+ if (!items.length) {
2738
+ if (fs.existsSync(filePath))
2739
+ fs.unlinkSync(filePath);
2740
+ return;
2741
+ }
2742
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
2743
+ const tmpPath = `${filePath}.${Date.now().toString(36)}.${Math.random().toString(36).slice(2)}.tmp`;
2744
+ const lines = items.map((item) => JSON.stringify({
2745
+ version: 1,
2746
+ record: redactStructuredSpoolRecord(item.spoolRecord),
2747
+ send_options: redactStructuredLogSendOptions(item.spoolSendOptions),
2748
+ event_id: item.eventId,
2749
+ attempts: item.attempts,
2750
+ created_at: item.createdAt,
2751
+ batch_prefix: item.batchPrefix
2752
+ }));
2753
+ fs.writeFileSync(tmpPath, `${lines.join(`
2754
+ `)}
2755
+ `, "utf8");
2756
+ fs.renameSync(tmpPath, filePath);
2757
+ }
2758
+ };
2759
+ }
2760
+ function nextStructuredLogBatch(queue, maxBatchSize) {
2761
+ const first = queue[0];
2762
+ if (!first)
2763
+ return [];
2764
+ const key = structuredLogSendOptionsKey(first.sendOptions);
2765
+ const batch = [];
2766
+ for (const item of queue.slice(0, maxBatchSize)) {
2767
+ if (batch.length && structuredLogSendOptionsKey(item.sendOptions) !== key)
2768
+ break;
2769
+ batch.push(item);
2770
+ }
2771
+ return batch;
2772
+ }
2773
+ function structuredLogSendOptionsKey(opts) {
2774
+ return safeStringify(opts);
2775
+ }
2776
+ function copyStructuredLogSendOptions(opts) {
2777
+ return {
2778
+ format: opts.format,
2779
+ source: opts.source,
2780
+ service: opts.service,
2781
+ projectId: opts.projectId,
2782
+ pageId: opts.pageId,
2783
+ machineId: opts.machineId,
2784
+ repoId: opts.repoId,
2785
+ appId: opts.appId,
2786
+ processId: opts.processId,
2787
+ runId: opts.runId,
2788
+ traceId: opts.traceId,
2789
+ spanId: opts.spanId,
2790
+ parentSpanId: opts.parentSpanId,
2791
+ sessionId: opts.sessionId,
2792
+ releaseId: opts.releaseId,
2793
+ environment: opts.environment,
2794
+ agent: opts.agent,
2795
+ logUrl: opts.logUrl,
2796
+ metadata: isObjectRecord(opts.metadata) ? { ...opts.metadata } : undefined,
2797
+ sourceEventPrefix: opts.sourceEventPrefix
2798
+ };
2799
+ }
2800
+ function redactStructuredLogSendOptions(opts) {
2801
+ const redacted = redactSdkValue(copyStructuredLogSendOptions(opts));
2802
+ return isObjectRecord(redacted) ? copyStructuredLogSendOptions(redacted) : {};
2803
+ }
2804
+ function redactStructuredBatchPrefix(value) {
2805
+ const generated = value.match(/^(.*)(:[A-Za-z0-9_-]+_transport_[A-Za-z0-9]+_[A-Za-z0-9]+:\d+)$/);
2806
+ const userPrefix = generated?.[1];
2807
+ const suffix = generated?.[2];
2808
+ if (userPrefix !== undefined && suffix !== undefined) {
2809
+ return `${redactSdkString(userPrefix)}${suffix}`;
2810
+ }
2811
+ return redactSdkString(value);
2812
+ }
2813
+ function loadStructuredSpoolSendOptions(value, fallback) {
2814
+ if (value === undefined)
2815
+ return fallback;
2816
+ if (!isObjectRecord(value))
2817
+ return;
2818
+ if (!isValidStructuredSpoolSendOptions(value))
2819
+ return;
2820
+ return redactStructuredLogSendOptions(value);
2821
+ }
2822
+ function isValidStructuredSpoolSendOptions(value) {
2823
+ if (value.format !== undefined && !SDK_STRUCTURED_LOG_FORMATS.has(value.format)) {
2824
+ return false;
2825
+ }
2826
+ if (value.source !== undefined && !SDK_STRUCTURED_LOG_SOURCES.has(value.source)) {
2827
+ return false;
2828
+ }
2829
+ for (const key of SDK_STRUCTURED_LOG_STRING_OPTION_KEYS) {
2830
+ if (value[key] !== undefined && typeof value[key] !== "string")
2831
+ return false;
2832
+ }
2833
+ return value.metadata === undefined || isObjectRecord(value.metadata);
2834
+ }
2835
+ var SDK_STRUCTURED_LOG_FORMATS = new Set(["auto", "pino", "winston", "json"]);
2836
+ var SDK_STRUCTURED_LOG_SOURCES = new Set([
2837
+ "sdk",
2838
+ "scanner",
2839
+ "node",
2840
+ "bun",
2841
+ "next",
2842
+ "vite",
2843
+ "cli",
2844
+ "build",
2845
+ "test",
2846
+ "mcp",
2847
+ "agent",
2848
+ "otel",
2849
+ "system",
2850
+ "pino",
2851
+ "winston",
2852
+ "structured"
2853
+ ]);
2854
+ var SDK_STRUCTURED_LOG_STRING_OPTION_KEYS = [
2855
+ "service",
2856
+ "projectId",
2857
+ "pageId",
2858
+ "machineId",
2859
+ "repoId",
2860
+ "appId",
2861
+ "processId",
2862
+ "runId",
2863
+ "traceId",
2864
+ "spanId",
2865
+ "parentSpanId",
2866
+ "sessionId",
2867
+ "releaseId",
2868
+ "environment",
2869
+ "agent",
2870
+ "logUrl",
2871
+ "sourceEventPrefix"
2872
+ ];
2873
+ function structuredLogSpoolPath(opts) {
2874
+ if (opts.spoolFile)
2875
+ return opts.spoolFile;
2876
+ if (!opts.spoolDirectory)
2877
+ return;
2878
+ const builtins = nodeBuiltins();
2879
+ const fileName = [
2880
+ "open-logs",
2881
+ sanitizeSpoolPathPart(opts.projectId ?? "default"),
2882
+ sanitizeSpoolPathPart(opts.format ?? "structured"),
2883
+ "structured-spool.jsonl"
2884
+ ].join("-");
2885
+ return builtins?.path.join(opts.spoolDirectory, fileName);
2886
+ }
2887
+ function nodeBuiltins() {
2888
+ const processLike = globalThis.process;
2889
+ const getBuiltinModule = processLike?.getBuiltinModule;
2890
+ if (!getBuiltinModule)
2891
+ return;
2892
+ const fs = getBuiltinModule.call(processLike, "node:fs");
2893
+ const path = getBuiltinModule.call(processLike, "node:path");
2894
+ if (!fs || !path || typeof fs.existsSync !== "function" || typeof fs.mkdirSync !== "function" || typeof fs.readFileSync !== "function" || typeof fs.renameSync !== "function" || typeof fs.unlinkSync !== "function" || typeof fs.writeFileSync !== "function" || typeof path.dirname !== "function" || typeof path.join !== "function") {
2895
+ return;
2896
+ }
2897
+ return { fs, path };
2898
+ }
2899
+ function sanitizeSpoolPathPart(value) {
2900
+ const cleaned = value.replace(/[^A-Za-z0-9._-]+/g, "_").replace(/^_+|_+$/g, "");
2901
+ return cleaned.slice(0, 80) || "default";
2902
+ }
2903
+ function redactStructuredSpoolRecord(value) {
2904
+ return redactSdkValue(value);
2905
+ }
2906
+ function redactSdkValue(value, depth = 0) {
2907
+ if (value === null || value === undefined)
2908
+ return value;
2909
+ if (typeof value === "string")
2910
+ return redactSdkString(value);
2911
+ if (typeof value !== "object" || depth >= 12)
2912
+ return value;
2913
+ if (Array.isArray(value)) {
2914
+ const output2 = [];
2915
+ let previousWasSensitiveFlag = false;
2916
+ for (const item of value) {
2917
+ if (previousWasSensitiveFlag && typeof item === "string") {
2918
+ output2.push("[REDACTED]");
2919
+ previousWasSensitiveFlag = false;
2920
+ continue;
2921
+ }
2922
+ const redacted = redactSdkValue(item, depth + 1);
2923
+ output2.push(redacted);
2924
+ previousWasSensitiveFlag = typeof redacted === "string" && SDK_SENSITIVE_FLAG.test(redacted);
2925
+ }
2926
+ return output2;
2927
+ }
2928
+ const output = {};
2929
+ for (const [key, child] of Object.entries(value)) {
2930
+ output[key] = SDK_SENSITIVE_KEY.test(key) && child !== null && child !== undefined ? "[REDACTED]" : redactSdkValue(child, depth + 1);
2931
+ }
2932
+ return output;
2933
+ }
2934
+ var SDK_SENSITIVE_KEY = /(?:authorization|cookie|set-cookie|api[_-]?key|token|secret|password|passwd|pwd|private[_-]?key|access[_-]?token|refresh[_-]?token|session[_-]?secret|client[_-]?secret)/i;
2935
+ var SDK_SENSITIVE_FLAG = /^(?:authorization|auth|api[-_]?key|token|secret|password|passwd|pwd|private[-_]?key|access[-_]?token|refresh[-_]?token|session[-_]?secret|client[-_]?secret|--[A-Za-z0-9._-]*(?:authorization|api[-_]?key|token|secret|password|passwd|pwd|private[-_]?key|access[-_]?token|refresh[-_]?token|session[-_]?secret|client[-_]?secret)[A-Za-z0-9._-]*)$/i;
2936
+ var SDK_STRING_PATTERNS = [
2937
+ {
2938
+ pattern: /\b(?:OPENLOGS|LOGS)[_-]?SECRET[_-]?CANARY[_-]?[A-Za-z0-9._-]*/gi,
2939
+ replacement: "[REDACTED]"
2940
+ },
2941
+ {
2942
+ pattern: /\bBearer\s+[A-Za-z0-9._~+/=-]+/gi,
2943
+ replacement: "Bearer [REDACTED]"
2944
+ },
2945
+ {
2946
+ pattern: /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9_]{20,}\b/g,
2947
+ replacement: "[REDACTED]"
2948
+ },
2949
+ {
2950
+ pattern: /\bgithub_pat_[A-Za-z0-9_]{20,}\b/g,
2951
+ replacement: "[REDACTED]"
2952
+ },
2953
+ {
2954
+ pattern: /\bsk-[A-Za-z0-9_-]{20,}\b/g,
2955
+ replacement: "[REDACTED]"
2956
+ },
2957
+ {
2958
+ pattern: /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g,
2959
+ replacement: "[REDACTED]"
2960
+ },
2961
+ {
2962
+ pattern: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g,
2963
+ replacement: "[REDACTED]"
2964
+ },
2965
+ {
2966
+ pattern: /\b(api[_-]?key|token|secret|password|passwd|pwd|access[_-]?token|refresh[_-]?token|client[_-]?secret)\s*[:=]\s*("[^"]*"|'[^']*'|[^\s,;&}]+)/gi,
2967
+ replacement: (_match, key) => `${key}=[REDACTED]`
2968
+ },
2969
+ {
2970
+ pattern: /(--[A-Za-z0-9._-]*(?:authorization|api[-_]?key|token|secret|password|passwd|pwd|private[-_]?key|access[-_]?token|refresh[-_]?token|session[-_]?secret|client[-_]?secret)[A-Za-z0-9._-]*\s+)(?:"[^"]*"|'[^']*'|[^\s,;&}]+)/gi,
2971
+ replacement: (_match, prefix) => `${prefix}[REDACTED]`
2972
+ },
2973
+ {
2974
+ pattern: /(--auth\s+)(?:"[^"]*"|'[^']*'|[^\s,;&}]+)/gi,
2975
+ replacement: (_match, prefix) => `${prefix}[REDACTED]`
2976
+ },
2977
+ {
2978
+ pattern: /([?&](?:api[_-]?key|token|secret|password|passwd|pwd|access[_-]?token|refresh[_-]?token|auth|code)=)[^&#\s]+/gi,
2979
+ replacement: (_match, prefix) => `${prefix}[REDACTED]`
2980
+ }
2981
+ ];
2982
+ function redactSdkString(input) {
2983
+ let output = input;
2984
+ for (const { pattern, replacement } of SDK_STRING_PATTERNS) {
2985
+ output = output.replace(pattern, (...args) => {
2986
+ if (typeof replacement === "function")
2987
+ return replacement(args[0] ?? "", ...args.slice(1));
2988
+ return replacement;
2989
+ });
2990
+ }
2991
+ return output;
2992
+ }
2993
+ function callbackFromArgs(encoding, callback) {
2994
+ return typeof encoding === "function" ? encoding : callback;
2995
+ }
2996
+ function callbackFromUnknownArgs(encoding, callback) {
2997
+ if (typeof callback === "function")
2998
+ return callback;
2999
+ if (typeof encoding === "function")
3000
+ return encoding;
3001
+ return;
3002
+ }
3003
+ function normalizeWinstonLogArguments(infoOrLevel, callbackOrMessage, legacyArgs) {
3004
+ if (infoOrLevel && typeof infoOrLevel === "object") {
3005
+ return {
3006
+ info: { ...infoOrLevel },
3007
+ callback: typeof callbackOrMessage === "function" ? callbackOrMessage : undefined
3008
+ };
3009
+ }
3010
+ const metadata = legacyArgs.find((arg) => arg && typeof arg === "object" && !Array.isArray(arg));
3011
+ let callback;
3012
+ for (let index = legacyArgs.length - 1;index >= 0; index -= 1) {
3013
+ const arg = legacyArgs[index];
3014
+ if (typeof arg === "function") {
3015
+ callback = arg;
3016
+ break;
3017
+ }
3018
+ }
3019
+ const normalized = metadata ? { ...metadata } : {};
3020
+ normalized.level = String(infoOrLevel);
3021
+ if (normalized.message === undefined && callbackOrMessage !== undefined) {
3022
+ normalized.message = callbackOrMessage instanceof Error ? callbackOrMessage.message : typeof callbackOrMessage === "string" ? callbackOrMessage : String(callbackOrMessage);
3023
+ }
3024
+ if (callbackOrMessage instanceof Error && normalized.stack === undefined) {
3025
+ normalized.stack = callbackOrMessage.stack;
3026
+ }
3027
+ return { info: normalized, callback };
3028
+ }
3029
+ function isObjectRecord(value) {
3030
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
3031
+ }
3032
+ function hasStructuredProducerId(value) {
3033
+ return typeof value.id === "string" || typeof value.event_id === "string" || typeof value.eventId === "string" || typeof value.source_event_id === "string" || typeof value.log_id === "string" || typeof value.logId === "string" || typeof value._open_logs_event_id === "string";
3034
+ }
3035
+ function chunkText(chunk, decoder) {
3036
+ if (typeof chunk === "string")
3037
+ return chunk;
3038
+ return decoder.decode(chunk, { stream: true });
3039
+ }
3040
+ function toError(error) {
3041
+ return error instanceof Error ? error : new Error(String(error));
3042
+ }
3043
+ function sleep(ms) {
3044
+ return new Promise((resolve) => setTimeout(resolve, ms));
3045
+ }
3046
+ function noop() {}
3047
+ async function sendRequestTelemetry(client, events, waitForTelemetry) {
3048
+ const send = events.length === 1 ? client.pushEvent(events[0]) : client.pushEvents(events);
3049
+ if (waitForTelemetry) {
3050
+ await send.catch(() => {});
3051
+ return;
3052
+ }
3053
+ send.catch(() => {});
3054
+ }
3055
+ function isCollectorRequest(requestUrl, collectorUrl) {
3056
+ return requestUrl === collectorUrl || requestUrl.startsWith(`${collectorUrl}/`);
3057
+ }
3058
+ function randomHex(length) {
3059
+ let output = "";
3060
+ while (output.length < length)
3061
+ output += Math.random().toString(16).slice(2);
3062
+ return output.slice(0, length);
3063
+ }
3064
+ function randomId(prefix) {
3065
+ return `${prefix}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
3066
+ }
3067
+ function defaultProcessId(processLike) {
3068
+ return `proc_${processLike.pid ?? "unknown"}_${Date.now().toString(36)}`;
3069
+ }
3070
+ function safeCall(fn) {
3071
+ try {
3072
+ return fn?.();
3073
+ } catch {
3074
+ return;
3075
+ }
3076
+ }
3077
+ function safeStringify(value) {
3078
+ try {
3079
+ return JSON.stringify(value) ?? String(value);
3080
+ } catch {
3081
+ return Object.prototype.toString.call(value);
3082
+ }
3083
+ }
3084
+ function runtimeName() {
3085
+ const runtime = globalThis;
3086
+ if (runtime.Bun)
3087
+ return "bun";
3088
+ if (runtime.window)
3089
+ return "browser";
3090
+ if (runtime.process?.versions?.node)
3091
+ return "node";
3092
+ return "unknown";
3093
+ }
3094
+ async function readJson(res) {
3095
+ const body = await res.json().catch(() => null);
3096
+ if (!res.ok) {
3097
+ const message = body && typeof body === "object" && "error" in body ? String(body.error) : `Request failed: ${res.status} ${res.statusText}`;
3098
+ throw new Error(message);
3099
+ }
3100
+ return body;
152
3101
  }
153
3102
  export {
3103
+ instrumentFetchHandler,
3104
+ initUniversalLogs,
3105
+ initNodeLogs,
154
3106
  initLogs,
3107
+ createWinstonOpenLogsTransport,
3108
+ createPinoOpenLogsTransport,
3109
+ createHonoTelemetryMiddleware,
3110
+ createFastifyTelemetryHooks,
3111
+ createExpressTelemetryMiddleware,
3112
+ createExpressErrorTelemetryMiddleware,
3113
+ captureNodeHttpRequest,
3114
+ captureHttpRequest,
155
3115
  LogsClient
156
3116
  };