@zintrust/trace 0.4.81 → 0.4.82

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.
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@zintrust/trace",
3
- "version": "0.4.77",
4
- "buildDate": "2026-04-08T09:26:55.306Z",
3
+ "version": "0.4.82",
4
+ "buildDate": "2026-04-08T15:23:56.210Z",
5
5
  "buildEnvironment": {
6
6
  "node": "v22.22.1",
7
7
  "platform": "darwin",
8
8
  "arch": "arm64"
9
9
  },
10
10
  "git": {
11
- "commit": "f37a0c05",
11
+ "commit": "54ac1e60",
12
12
  "branch": "release"
13
13
  },
14
14
  "package": {
@@ -22,8 +22,8 @@
22
22
  },
23
23
  "files": {
24
24
  "build-manifest.json": {
25
- "size": 14438,
26
- "sha256": "967934d372f36722143e02cd66b98dae1904122d34754fa8267bf3133725e1b2"
25
+ "size": 14439,
26
+ "sha256": "b94e2cbd290c3992dabf8673fba6a501186ebe5386ee6bd2686a42f750881d00"
27
27
  },
28
28
  "cli-register.d.ts": {
29
29
  "size": 255,
@@ -38,8 +38,8 @@
38
38
  "sha256": "b034cbef0c71fb868071363624ef7a9f8d7acc20f8be8c895dd5db5a75e81f37"
39
39
  },
40
40
  "config.js": {
41
- "size": 5862,
42
- "sha256": "2692029ea79d7454f920fcdd6466cd6ee443a7029f36597a213585ed869f9af7"
41
+ "size": 5927,
42
+ "sha256": "24f446c25ca4aae4ebc48ab54824c46e2042f1afbf41dd88b769fde4dced5886"
43
43
  },
44
44
  "context.d.ts": {
45
45
  "size": 596,
@@ -70,16 +70,16 @@
70
70
  "sha256": "4862b41e0477f01afa0dbb446d4553b65c22ed774cd1e2db3489059ced392f94"
71
71
  },
72
72
  "dashboard/ui.js": {
73
- "size": 64493,
74
- "sha256": "061c304bad22631db4ad795457c0eb5064f060f534d5050e2e14c6a1c01a2c1b"
73
+ "size": 70368,
74
+ "sha256": "b9f67e7977a36b459ac8d37312547b2638fcab7fc9b6c3d0c072d02510fca62f"
75
75
  },
76
76
  "index.d.ts": {
77
- "size": 2370,
78
- "sha256": "4cfee97f40d9dd12f5ea37996bee548a5964ee2cbe45c4e844bf5a119d9470d3"
77
+ "size": 2470,
78
+ "sha256": "99c28d43f79dbb2b372bf6a8b611841c131f59f5066702b499915b874e9fa2b8"
79
79
  },
80
80
  "index.js": {
81
- "size": 3237,
82
- "sha256": "d610de7775e31196a8acca02425c407e64bfdb69977290443664d36eaa022426"
81
+ "size": 3255,
82
+ "sha256": "ab97f252e49aa760657c10b96ea3fc0857d626c128d70cbb423cf6b72d6a67fb"
83
83
  },
84
84
  "migrations/20260331000001_create_zin_trace_entries_table.d.ts": {
85
85
  "size": 304,
@@ -134,8 +134,8 @@
134
134
  "sha256": "71d366165dd36f1675aa253a76262b226fb6c62e5ab632746b8aea61c0c625fc"
135
135
  },
136
136
  "register.js": {
137
- "size": 10601,
138
- "sha256": "4e25cf1a5206578c0c1ad77febc258215d97d0468facc51d01a9259c0634757d"
137
+ "size": 11496,
138
+ "sha256": "46a85cc9e9fd14a48c66c0cdf348a84608ffc628e731c0d2cc129fc38d1fa9b6"
139
139
  },
140
140
  "storage/DebuggerStorage.d.ts": {
141
141
  "size": 517,
@@ -186,8 +186,8 @@
186
186
  "sha256": "d916e8e3abb1b1087f6b184851b0e6265e53380d7857b008e745d566aad15d44"
187
187
  },
188
188
  "types.d.ts": {
189
- "size": 7687,
190
- "sha256": "c1c5f5140d157d66355c65c774f055af2dccc470c9ee9fe403bbb96fbf4447d0"
189
+ "size": 8416,
190
+ "sha256": "03c17d7f3759890062d36089311d3c464ff833d90c1048dd8c35a07a33388862"
191
191
  },
192
192
  "types.js": {
193
193
  "size": 696,
@@ -266,12 +266,12 @@
266
266
  "sha256": "879a739de9ce2c3c5b57bdad73eae2ce3de94ccdc7e666ca52a50b45b0bc9bfd"
267
267
  },
268
268
  "watchers/CacheWatcher.d.ts": {
269
- "size": 265,
270
- "sha256": "cd30b7e8cd9ccd90a125073198dd2ad098706ab6e545f6152f94ccaeb616fa52"
269
+ "size": 314,
270
+ "sha256": "19b5f6fe4f0fc8f3df6762f4f46d36f198c7c7d7da8d3d23e5c8f124462e8cf7"
271
271
  },
272
272
  "watchers/CacheWatcher.js": {
273
- "size": 1518,
274
- "sha256": "1dae7cddf7940d7e6d478429d846390c9a862e30bd72bc68182b85f92b425dd0"
273
+ "size": 1956,
274
+ "sha256": "e1e1b79e85e7553d856ff0209d912e27d7b4fe665579de856c60e04bb7519aa1"
275
275
  },
276
276
  "watchers/CommandWatcher.d.ts": {
277
277
  "size": 267,
@@ -298,12 +298,12 @@
298
298
  "sha256": "ff32b44b48b6e313d15ba4370a845faf74ff8b03504a6336c19f183ff91a90dc"
299
299
  },
300
300
  "watchers/ExceptionWatcher.d.ts": {
301
- "size": 144,
302
- "sha256": "d1b5f5668a2fa3069b5b9f9c8852b66e1ef6489e5d1f47381bde582d6e406960"
301
+ "size": 311,
302
+ "sha256": "0f58c50fd77704151399ca6cb6ec7890a9aef86afe28235951971f8cc9c1d600"
303
303
  },
304
304
  "watchers/ExceptionWatcher.js": {
305
- "size": 3451,
306
- "sha256": "e4fe3d1df1c7d547b740bdf993503f43b80b6cc55aa151fe0cdc0f59b4442491"
305
+ "size": 3691,
306
+ "sha256": "d2ddd55f14730b0404cea53c17a6cfd4bd65ff3c17c8b068cc284de109da36d1"
307
307
  },
308
308
  "watchers/GateWatcher.d.ts": {
309
309
  "size": 262,
@@ -314,20 +314,20 @@
314
314
  "sha256": "f318cdeec954ce0bba97be1dc11a6dff935b081e6b6a417c614be1934fa47f04"
315
315
  },
316
316
  "watchers/HttpClientWatcher.d.ts": {
317
- "size": 283,
318
- "sha256": "1b48661bff79b2f72464c978ad7e6dcf011197ca4a3014e64e99b70437b3e5a1"
317
+ "size": 333,
318
+ "sha256": "08ab7e213c489ecc4fdd3166d7a121b9a5220ff9cbae9841d4787d9a804e11ce"
319
319
  },
320
320
  "watchers/HttpClientWatcher.js": {
321
- "size": 1627,
322
- "sha256": "65e910145f4d24f06643a847a1d75bc60034a629a5da61097ba9324d1d1c7ddd"
321
+ "size": 2414,
322
+ "sha256": "817c74e7a89bcd0c53c0344d713b422e0c9e51ec65e7b6c97f0c486116dda7a5"
323
323
  },
324
324
  "watchers/HttpWatcher.d.ts": {
325
325
  "size": 96,
326
326
  "sha256": "ce9a95a670f755193fd74ce721dbfa4b30f20c879a6566ebb35229b3b2435429"
327
327
  },
328
328
  "watchers/HttpWatcher.js": {
329
- "size": 5557,
330
- "sha256": "9f86966178485e2aab46ce03785c7c512feb641dbf5d0dda22d51f3f259d374e"
329
+ "size": 5916,
330
+ "sha256": "9b3fed08fd11f8a2bfe1f667293af437b00f11dcc66bfb545b2f77925394e611"
331
331
  },
332
332
  "watchers/JobWatcher.d.ts": {
333
333
  "size": 441,
@@ -342,16 +342,16 @@
342
342
  "sha256": "f3ddc5f8b58c6c86ac6b464dd48e5a55e79ab2bf2e735feacffc7480e4ccc0c4"
343
343
  },
344
344
  "watchers/LogWatcher.js": {
345
- "size": 1768,
346
- "sha256": "a7e769d504d5528068e9349f28d703606d21404d3976f7cdb131cee417420ff6"
345
+ "size": 2026,
346
+ "sha256": "c5d2227cd76ce10162993ac31f474b2460cd41264c36f01b5130152f14a0ad21"
347
347
  },
348
348
  "watchers/MailWatcher.d.ts": {
349
- "size": 214,
350
- "sha256": "aa4a677b61f231cc29c314d3091dfecf45cac30f164f6ac5e99f0f3d1e714154"
349
+ "size": 244,
350
+ "sha256": "5031b96ef8e64a6d376576e8cddf1c2560f22432a78f1d2be55f7cea6bff4547"
351
351
  },
352
352
  "watchers/MailWatcher.js": {
353
- "size": 1214,
354
- "sha256": "2bc8cb264822107cfc29d0341559f517186190492e594cb7ab1942b6ae28ea56"
353
+ "size": 1655,
354
+ "sha256": "0eb4f43c27a0c76cf290bb6507e71dc595bff3f8ab66c25249d9ac6027b351c5"
355
355
  },
356
356
  "watchers/MiddlewareWatcher.d.ts": {
357
357
  "size": 259,
@@ -370,20 +370,20 @@
370
370
  "sha256": "de3d1e379c7b1289167fe0b1dbf2aa5a54137b841df17c8d397ee656d9ce37fd"
371
371
  },
372
372
  "watchers/NotificationWatcher.d.ts": {
373
- "size": 237,
374
- "sha256": "02fdf20e32ab8c93c10ce5c93a88b2349e73a30e9eb391413b8a401899d1053e"
373
+ "size": 274,
374
+ "sha256": "a1d918122c5db9a7f27fdf78c0c14a61f6e1213748ee6f9b06f976f33589dc33"
375
375
  },
376
376
  "watchers/NotificationWatcher.js": {
377
- "size": 1242,
378
- "sha256": "e72cc2fcc622f44b907560ca664a3f88a388ac444c7a0c290c00e95899b123a3"
377
+ "size": 1697,
378
+ "sha256": "b0ecc1df6a49dc8c5ffeb8dff0f1e3594ba016c4de3424dc8b5d7832e2f4cd11"
379
379
  },
380
380
  "watchers/QueryWatcher.d.ts": {
381
381
  "size": 97,
382
382
  "sha256": "6832a282b1658398264ede770d41c6aa86cb13625a3a87dac27fbaf7d2f7be6a"
383
383
  },
384
384
  "watchers/QueryWatcher.js": {
385
- "size": 2642,
386
- "sha256": "76a7fdd42a340141810f541ed89845c303f5498db7bdcfa29d8d576cf36c8efb"
385
+ "size": 2899,
386
+ "sha256": "a5bb991149846b67edf9658ce448172ec0b6f2f1bfb1cab25d9b2831a5e19a64"
387
387
  },
388
388
  "watchers/RedisWatcher.d.ts": {
389
389
  "size": 294,
package/dist/config.js CHANGED
@@ -79,6 +79,8 @@ const DEFAULTS = Object.freeze({
79
79
  pruneAfterHours: 24,
80
80
  ignoreRoutes: ['/trace', '/health', '/ping'],
81
81
  slowQueryThreshold: 100,
82
+ captureCachePayloads: false,
83
+ captureQueryBindings: true,
82
84
  logMinLevel: 'info',
83
85
  watchers: {},
84
86
  redaction: {
@@ -41,7 +41,7 @@ const encodeSvgDataUri = (svg) => {
41
41
  const compactSvg = svg.replaceAll(/>\s+</g, '><').trim();
42
42
  return `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(compactSvg)}`;
43
43
  };
44
- const DASHBOARD_DOCUMENT = `<!DOCTYPE html>
44
+ const DASHBOARD_DOCUMENT = String.raw `<!DOCTYPE html>
45
45
  <html lang="en">
46
46
  <head>
47
47
  <meta charset="UTF-8">
@@ -74,7 +74,7 @@ const DASHBOARD_DOCUMENT = `<!DOCTYPE html>
74
74
  .tag{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:999px;background:rgba(56,189,248,.12);color:#bae6fd;font-size:.78rem;font-weight:800;margin:0 6px 6px 0;border:1px solid rgba(56,189,248,.18);text-decoration:none}button.tag{cursor:pointer}html[data-theme='light'] .tag{color:#075985}.tag.failed{background:rgba(239,68,68,.14);color:#fecaca;border-color:rgba(239,68,68,.2)}html[data-theme='light'] .tag.failed{color:#b91c1c}.tag.slow{background:rgba(245,158,11,.12);color:#fde68a;border-color:rgba(245,158,11,.18)}html[data-theme='light'] .tag.slow{color:#92400e}.type-pill{display:inline-flex;align-items:center;gap:6px;padding:6px 10px;border-radius:999px;font-size:.74rem;font-weight:900;text-transform:uppercase;letter-spacing:.08em;border:1px solid transparent}.pill-request{background:rgba(56,189,248,.14);color:#93c5fd}.pill-request.method-get{background:rgba(34,197,94,.16);color:#bbf7d0}.pill-request.method-post{background:rgba(59,130,246,.16);color:#bfdbfe}.pill-request.method-other{background:rgba(245,158,11,.16);color:#fde68a}.pill-query{background:rgba(34,197,94,.12);color:#86efac}.pill-exception{background:rgba(239,68,68,.14);color:#fecaca}.pill-log{background:rgba(168,85,247,.14);color:#ddd6fe}.pill-job,.pill-batch{background:rgba(245,158,11,.14);color:#fde68a}.pill-cache{background:rgba(20,184,166,.12);color:#99f6e4}.pill-schedule,.pill-command{background:rgba(14,165,233,.14);color:#bae6fd}.pill-mail,.pill-notification{background:rgba(236,72,153,.14);color:#fbcfe8}.pill-auth{background:rgba(148,163,184,.16);color:#e2e8f0}.pill-event,.pill-model{background:rgba(74,222,128,.14);color:#bbf7d0}.pill-redis{background:rgba(239,68,68,.12);color:#fecaca}.pill-gate{background:rgba(99,102,241,.14);color:#c7d2fe}.pill-middleware{background:rgba(45,212,191,.12);color:#ccfbf1}.pill-dump,.pill-view{background:rgba(148,163,184,.14);color:#e2e8f0}.pill-client-request{background:rgba(59,130,246,.14);color:#bfdbfe}html[data-theme='light'] .pill-request{color:#1d4ed8}html[data-theme='light'] .pill-request.method-get{color:#166534}html[data-theme='light'] .pill-request.method-post{color:#1d4ed8}html[data-theme='light'] .pill-request.method-other{color:#92400e}html[data-theme='light'] .pill-query{color:#166534}html[data-theme='light'] .pill-exception{color:#b91c1c}html[data-theme='light'] .pill-log{color:#6d28d9}html[data-theme='light'] .pill-job,html[data-theme='light'] .pill-batch{color:#92400e}html[data-theme='light'] .pill-cache{color:#115e59}html[data-theme='light'] .pill-schedule,html[data-theme='light'] .pill-command{color:#0c4a6e}html[data-theme='light'] .pill-mail,html[data-theme='light'] .pill-notification{color:#9d174d}html[data-theme='light'] .pill-auth,html[data-theme='light'] .pill-dump,html[data-theme='light'] .pill-view{color:#334155}html[data-theme='light'] .pill-event,html[data-theme='light'] .pill-model{color:#166534}html[data-theme='light'] .pill-redis{color:#991b1b}html[data-theme='light'] .pill-gate{color:#3730a3}html[data-theme='light'] .pill-middleware{color:#155e75}html[data-theme='light'] .pill-client-request{color:#1d4ed8}
75
75
  .monitoring-wrap{padding:0 24px 24px}.tag-list{display:flex;flex-wrap:wrap;gap:10px;margin-bottom:18px}.tag-item{display:inline-flex;align-items:center;gap:10px;padding:10px 14px;border-radius:999px;border:1px solid var(--line);background:var(--surface-strong)}.tag-remove{border:none;background:rgba(239,68,68,.14);color:var(--danger);border-radius:999px;width:24px;height:24px;cursor:pointer;font-size:1rem;line-height:1}.helper-text{color:var(--muted);line-height:1.6}
76
76
  .duration-chip{display:inline-flex;align-items:center;padding:5px 9px;border-radius:999px;border:1px solid transparent;font-size:.8rem;font-weight:700;color:var(--text);white-space:nowrap}.duration-chip.vfast{background:rgba(34,197,94,.14);border-color:rgba(34,197,94,.28);color:#bbf7d0}.duration-chip.fast{background:rgba(56,189,248,.12);border-color:rgba(56,189,248,.24);color:#bae6fd}.duration-chip.slow{background:rgba(245,158,11,.12);border-color:rgba(245,158,11,.22);color:#fde68a}.duration-chip.vslow{background:rgba(239,68,68,.14);border-color:rgba(239,68,68,.24);color:#fecaca}html[data-theme='light'] .duration-chip.vfast{color:#166534}html[data-theme='light'] .duration-chip.fast{color:#1d4ed8}html[data-theme='light'] .duration-chip.slow{color:#92400e}html[data-theme='light'] .duration-chip.vslow{color:#b91c1c}
77
- .code-card{border-radius:16px;border:1px solid var(--code-border);background:var(--surface-soft);overflow:hidden}.code-toolbar{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 14px;border-bottom:1px solid var(--line)}.code-label{font-size:.76rem;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);font-weight:800}.copy-button{display:inline-flex;align-items:center;justify-content:center;gap:8px;width:38px;height:38px;border-radius:12px;border:1px solid var(--line);background:var(--surface-strong);color:var(--text);cursor:pointer;transition:border-color .16s ease,color .16s ease}.copy-button:hover{border-color:rgba(56,189,248,.35);color:var(--accent)}.copy-button[data-copied='true']{color:var(--success);border-color:rgba(34,197,94,.28)}.copy-button svg{width:16px;height:16px;display:block}.code-block{margin:0;padding:18px 20px;background:var(--code-bg);color:#dbeafe;border:0;overflow:auto;white-space:pre;line-height:1.72;font-family:var(--mono);font-size:.92rem}.code-block code{font-family:inherit}.tok-key{color:#93c5fd}.tok-string{color:#86efac}.tok-number{color:#f9a8d4}.tok-boolean{color:#facc15}.tok-null{color:#fb7185}.tok-punctuation{color:#94a3b8}.tok-sql-keyword{color:#f472b6;font-weight:700}.tok-sql-identifier{color:#93c5fd}.tok-sql-string{color:#86efac}.tok-sql-number{color:#facc15}.tok-sql-comment{color:#64748b;font-style:italic}html[data-theme='light'] .code-block{color:#0f172a}html[data-theme='light'] .tok-key{color:#1d4ed8}html[data-theme='light'] .tok-string{color:#15803d}html[data-theme='light'] .tok-number{color:#c026d3}html[data-theme='light'] .tok-boolean{color:#b45309}html[data-theme='light'] .tok-null{color:#dc2626}html[data-theme='light'] .tok-punctuation{color:#64748b}html[data-theme='light'] .tok-sql-keyword{color:#db2777}html[data-theme='light'] .tok-sql-identifier{color:#2563eb}html[data-theme='light'] .tok-sql-string{color:#15803d}html[data-theme='light'] .tok-sql-number{color:#b45309}html[data-theme='light'] .tok-sql-comment{color:#6b7280}
77
+ .code-card{border-radius:16px;border:1px solid var(--code-border);background:var(--surface-soft);overflow:hidden}.code-toolbar{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 14px;border-bottom:1px solid var(--line)}.code-label{font-size:.76rem;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);font-weight:800}.copy-button{display:inline-flex;align-items:center;justify-content:center;gap:8px;width:38px;height:38px;border-radius:12px;border:1px solid var(--line);background:var(--surface-strong);color:var(--text);cursor:pointer;transition:border-color .16s ease,color .16s ease}.copy-button:hover{border-color:rgba(56,189,248,.35);color:var(--accent)}.copy-button[data-copied='true']{color:var(--success);border-color:rgba(34,197,94,.28)}.copy-button svg{width:16px;height:16px;display:block}.code-block{margin:0;padding:18px 20px;background:var(--code-bg);color:#dbeafe;border:0;overflow:auto;white-space:pre;line-height:1.72;font-family:var(--mono);font-size:.92rem}.code-block code{font-family:inherit}.html-preview-wrap{padding:14px;background:var(--surface-strong);border-top:1px solid var(--line)}.html-preview{display:block;width:100%;min-height:320px;border:1px solid var(--line);border-radius:14px;background:#fff}.tok-key{color:#93c5fd}.tok-string{color:#86efac}.tok-number{color:#f9a8d4}.tok-boolean{color:#facc15}.tok-null{color:#fb7185}.tok-punctuation{color:#94a3b8}.tok-sql-keyword{color:#f472b6;font-weight:700}.tok-sql-identifier{color:#93c5fd}.tok-sql-string{color:#86efac}.tok-sql-number{color:#facc15}.tok-sql-comment{color:#64748b;font-style:italic}html[data-theme='light'] .code-block{color:#0f172a}html[data-theme='light'] .tok-key{color:#1d4ed8}html[data-theme='light'] .tok-string{color:#15803d}html[data-theme='light'] .tok-number{color:#c026d3}html[data-theme='light'] .tok-boolean{color:#b45309}html[data-theme='light'] .tok-null{color:#dc2626}html[data-theme='light'] .tok-punctuation{color:#64748b}html[data-theme='light'] .tok-sql-keyword{color:#db2777}html[data-theme='light'] .tok-sql-identifier{color:#2563eb}html[data-theme='light'] .tok-sql-string{color:#15803d}html[data-theme='light'] .tok-sql-number{color:#b45309}html[data-theme='light'] .tok-sql-comment{color:#6b7280}
78
78
  @media (max-width:1120px){.content-grid{grid-template-columns:1fr}}@media (max-width:920px){.layout{grid-template-columns:1fr}.sidebar{position:static;height:auto;border-right:none;border-bottom:1px solid var(--line);padding:20px 16px 18px}.brand-row{padding:0 0 16px}.sidebar-status{margin:0 0 16px}.sidebar-group{padding:0}.main{padding:20px}}@media (max-width:640px){.stats-grid{grid-template-columns:1fr}.detail-card{padding:18px}.toolbar,.section-head,.pagination,.activity-list,.monitoring-wrap{padding-left:18px;padding-right:18px}.table-wrap{padding:0 8px 10px}.brand-row{align-items:stretch;gap:14px;padding:0 0 14px}.brand{width:100%;align-items:flex-start}.brand-copy{min-width:0}.brand-copy h1{font-size:1.18rem;line-height:1.12}.brand-copy p{font-size:.82rem;overflow-wrap:anywhere}.icon-button{align-self:flex-end}.sidebar-status{padding:12px}.nav-button{padding:11px 12px}.nav-title{font-size:.95rem}.nav-meta{font-size:.72rem}}@media (max-width:480px){.brand-row{flex-direction:column}.icon-button{align-self:flex-start}.nav-button{align-items:flex-start;flex-direction:column}.nav-meta{font-size:.7rem}}
79
79
  </style>
80
80
  </head>
@@ -196,6 +196,8 @@ const DASHBOARD_DOCUMENT = `<!DOCTYPE html>
196
196
  .replace(/"/g, '&quot;')
197
197
  .replace(/'/g, '&#39;');
198
198
 
199
+ const looksLikeHtml = (value) => new RegExp('</?(?:html|body|div|table)\\b|<!doctype\\b', 'i').test(String(value || ''));
200
+
199
201
  const api = async (path, opts) => {
200
202
  const response = await fetch(API + path, opts);
201
203
  if (!response.ok) {
@@ -335,6 +337,28 @@ const DASHBOARD_DOCUMENT = `<!DOCTYPE html>
335
337
  ].join('');
336
338
  };
337
339
 
340
+ const renderTextCard = (label, value) => {
341
+ const source = String(value ?? '');
342
+ return renderCodeCard(label, source, escapeHtml(source), 'language-text');
343
+ };
344
+
345
+ const renderHtmlPreview = (label, html) => {
346
+ const source = String(html ?? '');
347
+ const copyId = registerCopyPayload(source);
348
+ return [
349
+ '<section class="code-card">',
350
+ '<div class="code-toolbar">',
351
+ '<span class="code-label">' + escapeHtml(label) + '</span>',
352
+ '<button type="button" class="copy-button" data-action="copy-code" data-copy-id="' + escapeHtml(copyId) + '" title="Copy ' + escapeHtml(label) + '">',
353
+ COPY_ICON,
354
+ '</button>',
355
+ '</div>',
356
+ '<pre class="code-block language-html"><code>' + escapeHtml(source) + '</code></pre>',
357
+ '<div class="html-preview-wrap"><iframe class="html-preview" sandbox="allow-same-origin" srcdoc="' + escapeHtml(source) + '"></iframe></div>',
358
+ '</section>'
359
+ ].join('');
360
+ };
361
+
338
362
  const highlightJson = (value, label = 'JSON') => {
339
363
  const source = prettyJson(value);
340
364
  let output = '';
@@ -386,6 +410,14 @@ const DASHBOARD_DOCUMENT = `<!DOCTYPE html>
386
410
 
387
411
  const detailJson = (value, label = 'JSON') => highlightJson(value ?? {}, label);
388
412
 
413
+ const renderPayload = (label, value) => {
414
+ if (value === undefined) return '<p class="trace-note">No ' + escapeHtml(label.toLowerCase()) + ' was captured.</p>';
415
+ if (typeof value === 'string') {
416
+ return looksLikeHtml(value) ? renderHtmlPreview(label, value) : renderTextCard(label, value);
417
+ }
418
+ return detailJson(value, label);
419
+ };
420
+
389
421
  const entrySummaryText = (entry) => {
390
422
  const content = entry && entry.content ? entry.content : {};
391
423
  if (entry.type === 'request') return [content.responseStatus || '', content.method || '', content.uri || ''].filter(Boolean).join(' ');
@@ -393,20 +425,20 @@ const DASHBOARD_DOCUMENT = `<!DOCTYPE html>
393
425
  if (entry.type === 'exception') return [content.class || '', content.message || ''].filter(Boolean).join(': ');
394
426
  if (entry.type === 'log') return '[' + String(content.level || 'log') + '] ' + String(content.message || '').slice(0, 160);
395
427
  if (entry.type === 'job') return [content.name || '', content.status || 'queued'].filter(Boolean).join(' · ');
396
- if (entry.type === 'cache') return [content.operation || '', content.key || ''].filter(Boolean).join(' ');
428
+ if (entry.type === 'cache') return [content.operation || '', content.key || '', content.payloadLogged ? '' : '(payload off)'].filter(Boolean).join(' ');
397
429
  if (entry.type === 'schedule') return [content.name || '', content.status || 'ran'].filter(Boolean).join(' · ');
398
430
  if (entry.type === 'mail') return ['To ' + (content.to || 'unknown'), content.subject || 'No subject'].join(' · ');
399
431
  if (entry.type === 'auth') return [content.event || 'auth', content.userId ? '#' + content.userId : ''].filter(Boolean).join(' ');
400
432
  if (entry.type === 'event') return String(content.name || 'event');
401
433
  if (entry.type === 'model') return [content.action || '', content.model || ''].filter(Boolean).join(' ');
402
- if (entry.type === 'notification') return [content.notification || '', (content.channels || []).join(', ')].filter(Boolean).join(' -> ');
434
+ if (entry.type === 'notification') return [content.notification || '', content.message || (content.channels || []).join(', ')].filter(Boolean).join(' -> ');
403
435
  if (entry.type === 'redis') return String(content.command || 'redis');
404
436
  if (entry.type === 'gate') return [content.ability || '', content.result || ''].filter(Boolean).join(' · ');
405
437
  if (entry.type === 'middleware') return [content.name || '', content.event || ''].filter(Boolean).join(' · ');
406
438
  if (entry.type === 'command') return [content.name || '', content.exitCode !== undefined ? 'exit=' + content.exitCode : ''].filter(Boolean).join(' ');
407
439
  if (entry.type === 'batch') return [content.name || '', 'processed ' + (content.processed || 0) + '/' + (content.total || 0)].join(' · ');
408
440
  if (entry.type === 'view') return String(content.template || 'view');
409
- if (entry.type === 'client_request') return [content.method || '', content.url || ''].filter(Boolean).join(' ');
441
+ if (entry.type === 'client_request') return [content.method || '', content.url || '', content.responseStatus ? '[' + content.responseStatus + ']' : content.error ? '[failed]' : ''].filter(Boolean).join(' ');
410
442
  return JSON.stringify(content).slice(0, 160);
411
443
  };
412
444
 
@@ -442,6 +474,7 @@ const DASHBOARD_DOCUMENT = `<!DOCTYPE html>
442
474
  { label: 'Connection', value: escapeHtml(content.connection || 'default') },
443
475
  { label: 'Duration', value: escapeHtml(formatDuration(getEntryDuration(entry))) },
444
476
  { label: 'Slow', value: escapeHtml(content.slow ? 'Yes' : 'No') },
477
+ { label: 'Bindings', value: escapeHtml(content.bindingsIncluded === false ? 'Hidden' : 'Included') },
445
478
  { label: 'Hash', value: '<span class="mono">' + escapeHtml(content.hash || '') + '</span>' }
446
479
  ]),
447
480
  renderMetricBox('Runtime', [
@@ -449,7 +482,8 @@ const DASHBOARD_DOCUMENT = `<!DOCTYPE html>
449
482
  { label: 'Batch', value: '<span class="mono">' + escapeHtml(entry.batchId || '-') + '</span>' }
450
483
  ]),
451
484
  '</div>',
452
- highlightSql(content.sql || '')
485
+ highlightSql(content.sql || ''),
486
+ content.bindingsIncluded === false ? '<p class="trace-note">SQL bindings were hidden for this entry.</p>' : (Array.isArray(content.bindings) ? detailJson(content.bindings, 'Bindings Json') : '')
453
487
  ].join('');
454
488
  }
455
489
 
@@ -493,7 +527,34 @@ const DASHBOARD_DOCUMENT = `<!DOCTYPE html>
493
527
  renderMetricBox('Request', [
494
528
  { label: 'Method', value: escapeHtml(content.method || '') },
495
529
  { label: 'URL', value: '<span class="mono">' + escapeHtml(content.url || '') + '</span>' },
496
- { label: 'Status', value: escapeHtml(content.responseStatus || '') },
530
+ { label: 'Status', value: escapeHtml(content.responseStatus || (content.error ? 'Failed' : 'Pending')) },
531
+ { label: 'Duration', value: escapeHtml(formatDuration(getEntryDuration(entry))) }
532
+ ]),
533
+ renderMetricBox('Runtime', [
534
+ { label: 'Hostname', value: escapeHtml(content.hostname || '') },
535
+ { label: 'Batch', value: '<span class="mono">' + escapeHtml(entry.batchId || '-') + '</span>' },
536
+ { label: 'Error', value: escapeHtml(content.error || '-') }
537
+ ]),
538
+ '</div>',
539
+ '<div class="detail-stack">',
540
+ detailJson(content.requestHeaders || {}, 'Request Header Json'),
541
+ renderPayload('Request Body', content.requestBody),
542
+ detailJson(content.responseHeaders || {}, 'Response Header Json'),
543
+ renderPayload('Response Body', content.responseBody),
544
+ '</div>'
545
+ ].join('');
546
+ }
547
+
548
+ if (entry.type === 'cache') {
549
+ return [
550
+ '<div class="detail-grid">',
551
+ renderMetricBox('Cache', [
552
+ { label: 'Operation', value: escapeHtml(content.operation || '') },
553
+ { label: 'Key', value: '<span class="mono">' + escapeHtml(content.key || '') + '</span>' },
554
+ { label: 'Store', value: escapeHtml(content.store || 'default') },
555
+ { label: 'Hit', value: escapeHtml(content.hit === undefined ? '-' : (content.hit ? 'Yes' : 'No')) },
556
+ { label: 'Payload', value: escapeHtml(content.payloadLogged ? 'Captured' : 'Disabled') },
557
+ { label: 'TTL', value: escapeHtml(content.ttl === undefined ? '-' : String(content.ttl)) },
497
558
  { label: 'Duration', value: escapeHtml(formatDuration(getEntryDuration(entry))) }
498
559
  ]),
499
560
  renderMetricBox('Runtime', [
@@ -501,7 +562,41 @@ const DASHBOARD_DOCUMENT = `<!DOCTYPE html>
501
562
  { label: 'Batch', value: '<span class="mono">' + escapeHtml(entry.batchId || '-') + '</span>' }
502
563
  ]),
503
564
  '</div>',
504
- detailJson(content.requestHeaders || {})
565
+ content.payloadLogged ? renderPayload('Cache Payload', content.payload) : '<p class="trace-note">Cache payload logging is disabled. Set TRACE_CACHE_PAYLOADS=true to include values.</p>'
566
+ ].join('');
567
+ }
568
+
569
+ if (entry.type === 'mail') {
570
+ return [
571
+ '<div class="detail-grid">',
572
+ renderMetricBox('Mail', [
573
+ { label: 'To', value: escapeHtml(content.to || '') },
574
+ { label: 'Subject', value: escapeHtml(content.subject || '') },
575
+ { label: 'Template', value: escapeHtml(content.template || '-') },
576
+ { label: 'Hostname', value: escapeHtml(content.hostname || '') }
577
+ ]),
578
+ '</div>',
579
+ '<div class="detail-stack">',
580
+ renderPayload('Mail Text', content.text),
581
+ renderPayload('Mail Html', content.html),
582
+ '</div>'
583
+ ].join('');
584
+ }
585
+
586
+ if (entry.type === 'notification') {
587
+ return [
588
+ '<div class="detail-grid">',
589
+ renderMetricBox('Notification', [
590
+ { label: 'Notification', value: escapeHtml(content.notification || '') },
591
+ { label: 'Channels', value: escapeHtml((content.channels || []).join(', ') || '-') },
592
+ { label: 'Recipient', value: escapeHtml(content.notifiable || '-') },
593
+ { label: 'Hostname', value: escapeHtml(content.hostname || '') }
594
+ ]),
595
+ '</div>',
596
+ '<div class="detail-stack">',
597
+ renderPayload('Message', content.message),
598
+ content.payload === undefined ? '<p class="trace-note">No additional notification payload was captured.</p>' : detailJson(content.payload, 'Notification Payload Json'),
599
+ '</div>'
505
600
  ].join('');
506
601
  }
507
602
 
package/dist/register.js CHANGED
@@ -90,6 +90,16 @@ const parseEnvList = (rawValue) => {
90
90
  .map((entry) => entry.trim())
91
91
  .filter((entry) => entry !== '');
92
92
  };
93
+ const parseEnvBool = (rawValue) => {
94
+ const value = rawValue.trim().toLowerCase();
95
+ if (value === '')
96
+ return undefined;
97
+ if (['1', 'true', 'yes', 'on'].includes(value))
98
+ return true;
99
+ if (['0', 'false', 'no', 'off'].includes(value))
100
+ return false;
101
+ return undefined;
102
+ };
93
103
  const resolveTraceStartupOverrides = (core) => {
94
104
  const traceConfigFile = core.StartupConfigFile?.Trace;
95
105
  if (typeof traceConfigFile !== 'string' || traceConfigFile.trim() === '')
@@ -139,6 +149,8 @@ if (!traceAlreadyInitialized && Env) {
139
149
  const pruneAfterHoursRaw = Env.get('TRACE_PRUNE_HOURS', '').trim();
140
150
  const slowQueryThresholdRaw = Env.get('TRACE_SLOW_QUERY_MS', '').trim();
141
151
  const logMinLevelRaw = Env.get('TRACE_LOG_LEVEL', '').trim();
152
+ const captureCachePayloadsRaw = Env.get('TRACE_CACHE_PAYLOADS', '').trim();
153
+ const captureQueryBindingsRaw = Env.get('TRACE_QUERY_BINDINGS', '').trim();
142
154
  const redactionKeys = parseEnvList(Env.get('TRACE_REDACT_KEYS', ''));
143
155
  const redactionHeaders = parseEnvList(Env.get('TRACE_REDACT_HEADERS', ''));
144
156
  const redactionBody = parseEnvList(Env.get('TRACE_REDACT_BODY', ''));
@@ -151,6 +163,8 @@ if (!traceAlreadyInitialized && Env) {
151
163
  ? startupOverrides?.slowQueryThreshold
152
164
  : Number.parseInt(slowQueryThresholdRaw, 10);
153
165
  const logMinLevel = (logMinLevelRaw === '' ? startupOverrides?.logMinLevel : logMinLevelRaw);
166
+ const captureCachePayloads = parseEnvBool(captureCachePayloadsRaw) ?? startupOverrides?.captureCachePayloads;
167
+ const captureQueryBindings = parseEnvBool(captureQueryBindingsRaw) ?? startupOverrides?.captureQueryBindings;
154
168
  const redaction = buildTraceRedactionOverrides({
155
169
  startupOverrides,
156
170
  redactionBody,
@@ -168,6 +182,8 @@ if (!traceAlreadyInitialized && Env) {
168
182
  ...(typeof slowQueryThreshold === 'number' && Number.isFinite(slowQueryThreshold)
169
183
  ? { slowQueryThreshold }
170
184
  : {}),
185
+ ...(typeof captureCachePayloads === 'boolean' ? { captureCachePayloads } : {}),
186
+ ...(typeof captureQueryBindings === 'boolean' ? { captureQueryBindings } : {}),
171
187
  logMinLevel,
172
188
  ...(redaction === undefined ? {} : { redaction }),
173
189
  });
package/dist/types.d.ts CHANGED
@@ -43,6 +43,9 @@ export interface RequestContent {
43
43
  export interface QueryContent {
44
44
  connection: string;
45
45
  sql: string;
46
+ statement?: string;
47
+ bindings?: unknown[];
48
+ bindingsIncluded?: boolean;
46
49
  time: number;
47
50
  duration: number;
48
51
  slow: boolean;
@@ -91,6 +94,10 @@ export interface CacheContent {
91
94
  operation: 'get' | 'set' | 'delete' | 'clear' | 'has';
92
95
  key: string;
93
96
  hit?: boolean;
97
+ store?: string;
98
+ payload?: unknown;
99
+ payloadLogged?: boolean;
100
+ ttl?: number;
94
101
  duration: number;
95
102
  hostname: string;
96
103
  }
@@ -106,6 +113,8 @@ export interface MailContent {
106
113
  to: string;
107
114
  subject: string;
108
115
  template?: string;
116
+ text?: string;
117
+ html?: string;
109
118
  hostname: string;
110
119
  }
111
120
  export interface AuthContent {
@@ -130,6 +139,8 @@ export interface NotificationContent {
130
139
  channels: string[];
131
140
  notifiable?: string;
132
141
  notification: string;
142
+ message?: string;
143
+ payload?: unknown;
133
144
  hostname: string;
134
145
  }
135
146
  export interface RedisContent {
@@ -181,10 +192,25 @@ export interface ClientRequestContent {
181
192
  method: string;
182
193
  url: string;
183
194
  requestHeaders: Record<string, string>;
184
- responseStatus: number;
195
+ requestBody?: unknown;
196
+ responseStatus?: number;
197
+ responseHeaders?: Record<string, string>;
198
+ responseBody?: unknown;
199
+ error?: string;
185
200
  duration: number;
186
201
  hostname: string;
187
202
  }
203
+ export interface ClientRequestTraceInput {
204
+ method: string;
205
+ url: string;
206
+ requestHeaders: Record<string, string>;
207
+ responseStatus?: number;
208
+ duration: number;
209
+ requestBody?: unknown;
210
+ responseHeaders?: Record<string, string>;
211
+ responseBody?: unknown;
212
+ error?: string;
213
+ }
188
214
  export interface ITraceEntry<T = unknown> {
189
215
  uuid: string;
190
216
  batchId: string;
@@ -280,6 +306,8 @@ export interface ITraceConfig {
280
306
  pruneAfterHours: number;
281
307
  ignoreRoutes: string[];
282
308
  slowQueryThreshold: number;
309
+ captureCachePayloads: boolean;
310
+ captureQueryBindings: boolean;
283
311
  logMinLevel: 'debug' | 'info' | 'warn' | 'error' | 'fatal';
284
312
  watchers: WatcherToggles;
285
313
  redaction: RedactionConfig;
@@ -1,5 +1,5 @@
1
1
  import type { CacheContent, ITraceWatcher } from '../types';
2
- declare const emit: (operation: CacheContent["operation"], key: string, duration: number, hit?: boolean) => void;
2
+ declare const emit: (operation: CacheContent["operation"], key: string, duration: number, hit?: boolean, payload?: unknown, store?: string, ttl?: number) => void;
3
3
  export declare const CacheWatcher: ITraceWatcher & {
4
4
  emit: typeof emit;
5
5
  };
@@ -5,21 +5,27 @@
5
5
  import { TraceContext } from '../context.js';
6
6
  import { EntryType } from '../types.js';
7
7
  import { AuthTag } from '../utils/authTag.js';
8
- import { redactString } from '../utils/redact.js';
8
+ import { redactString, redactUnknown } from '../utils/redact.js';
9
9
  import { RequestFilter } from '../utils/requestFilter.js';
10
10
  let _storage = null;
11
+ let _config = null;
11
12
  let _redactionFields = [];
12
13
  let _ignoreRoutes = [];
13
- const emit = (operation, key, duration, hit) => {
14
+ const emit = (operation, key, duration, hit, payload, store, ttl) => {
14
15
  if (!_storage)
15
16
  return;
16
17
  if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes))
17
18
  return;
18
19
  const safeKey = redactString(key, _redactionFields);
20
+ const shouldLogPayload = _config?.captureCachePayloads === true;
19
21
  const content = {
20
22
  operation,
21
23
  key: safeKey,
22
24
  hit,
25
+ ...(typeof store === 'string' && store !== '' ? { store } : {}),
26
+ ...(typeof ttl === 'number' ? { ttl } : {}),
27
+ payloadLogged: shouldLogPayload,
28
+ ...(shouldLogPayload ? { payload: redactUnknown(payload, _redactionFields) } : {}),
23
29
  duration,
24
30
  hostname: TraceContext.getHostname(),
25
31
  };
@@ -41,10 +47,12 @@ export const CacheWatcher = Object.freeze({
41
47
  if (config.watchers.cache === false)
42
48
  return () => undefined;
43
49
  _storage = storage;
50
+ _config = config;
44
51
  _redactionFields = config.redaction.query;
45
52
  _ignoreRoutes = config.ignoreRoutes;
46
53
  return () => {
47
54
  _storage = null;
55
+ _config = null;
48
56
  _ignoreRoutes = [];
49
57
  };
50
58
  },
@@ -1,5 +1,5 @@
1
- import type { ITraceWatcher } from '../types';
2
- declare const emit: (method: string, url: string, requestHeaders: Record<string, string>, responseStatus: number, duration: number) => void;
1
+ import type { ClientRequestTraceInput, ITraceWatcher } from '../types';
2
+ declare const emit: ({ method, url, requestHeaders, responseStatus, duration, requestBody, responseHeaders, responseBody, error, }: ClientRequestTraceInput) => void;
3
3
  export declare const HttpClientWatcher: ITraceWatcher & {
4
4
  emit: typeof emit;
5
5
  };
@@ -1,24 +1,35 @@
1
1
  import { TraceContext } from '../context.js';
2
2
  import { EntryType } from '../types.js';
3
3
  import { AuthTag } from '../utils/authTag.js';
4
- import { redactHeaders } from '../utils/redact.js';
4
+ import { redactHeaders, redactUnknown } from '../utils/redact.js';
5
5
  import { RequestFilter } from '../utils/requestFilter.js';
6
6
  let _storage = null;
7
7
  let _redactHeaderNames = [];
8
+ let _redactBodyFields = [];
8
9
  let _ignoreRoutes = [];
9
- const emit = (method, url, requestHeaders, responseStatus, duration) => {
10
+ const emit = ({ method, url, requestHeaders, responseStatus, duration, requestBody, responseHeaders, responseBody, error, }) => {
10
11
  if (!_storage)
11
12
  return;
12
13
  if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes))
13
14
  return;
14
15
  const tags = AuthTag.append([method.toUpperCase()]);
15
- if (responseStatus >= 400)
16
+ if ((responseStatus ?? 0) >= 400 || error)
16
17
  tags.push('failed');
17
18
  const content = {
18
19
  method: method.toUpperCase(),
19
20
  url,
20
21
  requestHeaders: redactHeaders(requestHeaders, _redactHeaderNames),
21
- responseStatus,
22
+ ...(requestBody === undefined
23
+ ? {}
24
+ : { requestBody: redactUnknown(requestBody, _redactBodyFields) }),
25
+ ...(responseStatus === undefined ? {} : { responseStatus }),
26
+ ...(responseHeaders === undefined
27
+ ? {}
28
+ : { responseHeaders: redactHeaders(responseHeaders, _redactHeaderNames) }),
29
+ ...(responseBody === undefined
30
+ ? {}
31
+ : { responseBody: redactUnknown(responseBody, _redactBodyFields) }),
32
+ ...(typeof error === 'string' && error !== '' ? { error } : {}),
22
33
  duration,
23
34
  hostname: TraceContext.getHostname(),
24
35
  };
@@ -41,9 +52,11 @@ export const HttpClientWatcher = Object.freeze({
41
52
  return () => undefined;
42
53
  _storage = storage;
43
54
  _redactHeaderNames = [...(config.redaction?.keys ?? []), ...(config.redaction?.headers ?? [])];
55
+ _redactBodyFields = [...(config.redaction?.keys ?? []), ...(config.redaction?.body ?? [])];
44
56
  _ignoreRoutes = config.ignoreRoutes;
45
57
  return () => {
46
58
  _storage = null;
59
+ _redactBodyFields = [];
47
60
  _ignoreRoutes = [];
48
61
  };
49
62
  },
@@ -1,5 +1,5 @@
1
1
  import type { ITraceWatcher } from '../types';
2
- declare const emit: (to: string, subject: string, template?: string) => void;
2
+ declare const emit: (to: string, subject: string, template?: string, text?: string, html?: string) => void;
3
3
  export declare const MailWatcher: ITraceWatcher & {
4
4
  emit: typeof emit;
5
5
  };