@zintrust/trace 0.4.84 → 0.4.86

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,15 +1,15 @@
1
1
  {
2
2
  "name": "@zintrust/trace",
3
- "version": "0.4.84",
4
- "buildDate": "2026-04-08T17:51:22.444Z",
3
+ "version": "0.4.86",
4
+ "buildDate": "2026-04-09T08:08:55.865Z",
5
5
  "buildEnvironment": {
6
- "node": "v22.22.1",
7
- "platform": "darwin",
8
- "arch": "arm64"
6
+ "node": "v20.20.2",
7
+ "platform": "linux",
8
+ "arch": "x64"
9
9
  },
10
10
  "git": {
11
- "commit": "d4c31a0d",
12
- "branch": "release"
11
+ "commit": "a456e7ec",
12
+ "branch": "master"
13
13
  },
14
14
  "package": {
15
15
  "engines": {
@@ -21,10 +21,6 @@
21
21
  ]
22
22
  },
23
23
  "files": {
24
- "build-manifest.json": {
25
- "size": 14440,
26
- "sha256": "04b7ac17fd2a9abf208db4df6218eea2f97a4ce4e1dc6ce8d826036924e7ab84"
27
- },
28
24
  "cli-register.d.ts": {
29
25
  "size": 255,
30
26
  "sha256": "da8d689fe5ef32e97e755f28017e4d3cb1aa63489073a71907ea41ad5761ede9"
@@ -70,8 +66,8 @@
70
66
  "sha256": "4862b41e0477f01afa0dbb446d4553b65c22ed774cd1e2db3489059ced392f94"
71
67
  },
72
68
  "dashboard/ui.js": {
73
- "size": 70368,
74
- "sha256": "b9f67e7977a36b459ac8d37312547b2638fcab7fc9b6c3d0c072d02510fca62f"
69
+ "size": 73577,
70
+ "sha256": "2903901d8c0c5076118aa691727daa79be7abe87fdb393c5b389a2b1a8fce170"
75
71
  },
76
72
  "index.d.ts": {
77
73
  "size": 2470,
@@ -79,7 +75,7 @@
79
75
  },
80
76
  "index.js": {
81
77
  "size": 3255,
82
- "sha256": "f95401dc3b5ad76937e32978770c9568cf90e453bb1e77d0ecc8611e664b5ce0"
78
+ "sha256": "faba7327d333f46340e9f6933510a88cb2439d14af9651cd8868a38782a44388"
83
79
  },
84
80
  "migrations/20260331000001_create_zin_trace_entries_table.d.ts": {
85
81
  "size": 304,
@@ -137,14 +133,6 @@
137
133
  "size": 12522,
138
134
  "sha256": "66bccec7fac4d669702fa18e439788325d36ce17f228639005ffcde2cd968d70"
139
135
  },
140
- "storage/DebuggerStorage.d.ts": {
141
- "size": 517,
142
- "sha256": "c9c215aaa414f7b0c1fec6c82b054fc52bdf97af58f96f35c7f96672fb859c31"
143
- },
144
- "storage/DebuggerStorage.js": {
145
- "size": 7442,
146
- "sha256": "5ecce0fcfcf695df587a7b90a7a5c7efd2e64ad13c9f2d104b392f89f34f0dc4"
147
- },
148
136
  "storage/TraceContentRedaction.d.ts": {
149
137
  "size": 207,
150
138
  "sha256": "2fd7b317c5ee87d8ecaa6467a75959499673ef374fec3ecfaa7911c4f3d5c83c"
@@ -70,11 +70,11 @@ const DASHBOARD_DOCUMENT = String.raw `<!DOCTYPE html>
70
70
  .panel{border-radius:var(--radius);border:1px solid var(--line);background:var(--surface);box-shadow:var(--shadow);backdrop-filter:blur(16px)}.stats-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:16px;margin-bottom:18px}.stat-card{padding:20px;position:relative;overflow:hidden}.stat-card::after{content:'';position:absolute;right:-18px;bottom:-26px;width:92px;height:92px;border-radius:28px;background:linear-gradient(135deg,rgba(56,189,248,.16),rgba(34,197,94,.08));transform:rotate(18deg)}.stat-label{font-size:.74rem;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);font-weight:800;margin-bottom:12px}.stat-value{font-size:2.25rem;font-weight:800;line-height:1}.stat-meta{margin-top:10px;color:var(--muted);font-size:.9rem}.content-grid{display:grid;grid-template-columns:minmax(0,1.65fr) minmax(320px,.95fr);gap:18px}.side-stack{display:grid;gap:18px}
71
71
  .section-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;padding:22px 24px 16px}.section-head h3{margin:0;font-size:1.04rem}.section-head p{margin:6px 0 0;color:var(--muted);font-size:.92rem}.toolbar{display:flex;flex-wrap:wrap;gap:10px;padding:0 24px 18px}.control,.toolbar input,.toolbar select{height:44px;border-radius:13px;border:1px solid var(--line);background:var(--surface-strong);color:var(--text);padding:0 14px;min-width:0}.toolbar input,.toolbar select{flex:1 1 180px}.toolbar input::placeholder{color:var(--muted)}.btn{height:44px;border:none;border-radius:13px;padding:0 16px;cursor:pointer;font-weight:800}.btn-primary{background:linear-gradient(135deg,var(--accent-strong),var(--accent));color:#fff}.btn-danger{background:rgba(239,68,68,.12);color:var(--danger);border:1px solid rgba(239,68,68,.18)}.btn-ghost{background:var(--surface-soft);color:var(--text);border:1px solid var(--line)}
72
72
  .table-wrap{overflow:auto;padding:0 12px 12px}table{width:100%;border-collapse:separate;border-spacing:0;min-width:880px}th{padding:14px;color:var(--muted);font-size:.74rem;font-weight:800;letter-spacing:.12em;text-transform:uppercase;text-align:left;border-bottom:1px solid var(--line)}td{padding:15px 14px;border-bottom:1px solid var(--line);vertical-align:top}.row-button{cursor:pointer}.row-button:hover td{background:rgba(56,189,248,.05)}.summary{font-size:.93rem;font-weight:700;line-height:1.4;color:var(--text)}.summary-sub{margin-top:6px;color:var(--muted);font-size:.82rem;line-height:1.4}.mono{font-family:var(--mono)}.empty{padding:44px 24px;color:var(--muted);line-height:1.65;text-align:center}.pagination{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:0 24px 24px;color:var(--muted);flex-wrap:wrap}.pagination-controls{display:flex;gap:8px}.pagination button{height:40px;min-width:92px;padding:0 14px;border-radius:12px;border:1px solid var(--line);background:var(--surface-strong);color:var(--text);cursor:pointer}.pagination button:disabled{opacity:.45;cursor:not-allowed}
73
- .activity-list{list-style:none;margin:0;padding:0 24px 24px}.activity-item{padding:14px 0;border-top:1px solid var(--line)}.activity-item:first-child{border-top:none}.activity-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.activity-time{color:var(--muted);font-size:.85rem}.activity-summary{margin-top:8px;color:var(--text);line-height:1.48}.back-link{display:inline-flex;align-items:center;gap:8px;margin:0 0 14px;color:var(--accent);font-weight:800;cursor:pointer}.detail-card{padding:24px}.detail-meta{display:flex;flex-wrap:wrap;gap:10px;margin:14px 0 20px;color:var(--muted);font-size:.9rem}.detail-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px}.detail-stack{display:grid;gap:16px;margin-top:18px}.detail-box{padding:16px;border-radius:16px;background:var(--surface-soft);border:1px solid var(--line)}.detail-box h4{margin:0 0 10px;font-size:.92rem}.detail-box dl{margin:0;display:grid;gap:8px}.detail-box dt{font-size:.76rem;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);font-weight:800}.detail-box dd{margin:0;color:var(--text);line-height:1.45}.trace-tabs{display:flex;gap:10px;flex-wrap:wrap;margin:20px 0 16px}.trace-tab{border:none;border-radius:12px;padding:10px 12px;background:transparent;color:var(--muted);cursor:pointer;box-shadow:inset 0 0 0 1px var(--line);font-weight:800}.trace-tab.active{background:rgba(56,189,248,.12);color:var(--text);box-shadow:inset 0 0 0 1px rgba(56,189,248,.28)}.trace-panel{display:grid;gap:14px}.trace-item{padding:18px;border-radius:16px;background:var(--surface-soft);border:1px solid var(--line)}.trace-item-head{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.trace-item-summary{margin-top:10px;display:grid;gap:10px}.trace-note{color:var(--muted);line-height:1.6}
73
+ .activity-list{list-style:none;margin:0;padding:0 24px 24px}.activity-item{padding:14px 0;border-top:1px solid var(--line)}.activity-item:first-child{border-top:none}.activity-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.activity-time{color:var(--muted);font-size:.85rem}.activity-summary{margin-top:8px;color:var(--text);line-height:1.48}.back-link{display:inline-flex;align-items:center;gap:8px;margin:0 0 14px;color:var(--accent);font-weight:800;cursor:pointer}.detail-card{padding:24px}.detail-meta{display:flex;flex-wrap:wrap;gap:10px;margin:14px 0 20px;color:var(--muted);font-size:.9rem;overflow-wrap:anywhere}.detail-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px}.detail-stack{display:grid;gap:16px;margin-top:18px}.detail-box{padding:16px;border-radius:16px;background:var(--surface-soft);border:1px solid var(--line)}.detail-box h4{margin:0 0 10px;font-size:.92rem}.detail-box dl{margin:0;display:grid;gap:8px}.detail-box dt{font-size:.76rem;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);font-weight:800}.detail-box dd{margin:0;color:var(--text);line-height:1.45;overflow-wrap:anywhere}.trace-tabs{display:flex;gap:10px;flex-wrap:wrap;margin:20px 0 16px}.trace-tab{border:none;border-radius:12px;padding:10px 12px;background:transparent;color:var(--muted);cursor:pointer;box-shadow:inset 0 0 0 1px var(--line);font-weight:800}.trace-tab.active{background:rgba(56,189,248,.12);color:var(--text);box-shadow:inset 0 0 0 1px rgba(56,189,248,.28)}.trace-panel{display:grid;gap:14px}.trace-item{padding:18px;border-radius:16px;background:var(--surface-soft);border:1px solid var(--line)}.trace-item-head{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.trace-item-summary{margin-top:10px;display:grid;gap:10px}.trace-note{color:var(--muted);line-height:1.6}.trace-disclosure{padding:0;overflow:hidden}.trace-disclosure[open]{padding-bottom:18px}.trace-disclosure .trace-item-summary{margin-top:0}.trace-disclosure-body{display:grid;gap:12px;padding:0 18px}.trace-summary{list-style:none;cursor:pointer;padding:18px}.trace-summary::-webkit-details-marker{display:none}.trace-summary-main{display:grid;gap:10px;min-width:0;flex:1}.trace-summary-copy{display:grid;gap:6px;min-width:0}.trace-summary-copy .summary,.trace-summary-copy .summary-sub{display:block;overflow-wrap:anywhere}.trace-disclosure-body .summary-sub{overflow-wrap:anywhere}
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}.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}
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.wrap{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word}.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}.inline-collapse{margin:0;border-top:1px solid var(--line);background:var(--surface-strong)}.inline-collapse summary{cursor:pointer;list-style:none;padding:14px 16px;font-size:.82rem;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);font-weight:800}.inline-collapse summary::-webkit-details-marker{display:none}.inline-collapse[open] summary{border-bottom:1px solid var(--line)}.inline-collapse .code-block{border-top:none}.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>
@@ -322,8 +322,9 @@ const DASHBOARD_DOCUMENT = String.raw `<!DOCTYPE html>
322
322
  return id;
323
323
  };
324
324
 
325
- const renderCodeCard = (label, rawText, highlightedHtml, languageClass) => {
325
+ const renderCodeCard = (label, rawText, highlightedHtml, languageClass, options = {}) => {
326
326
  const copyId = registerCopyPayload(rawText);
327
+ const wrapClass = options.wrap === true ? ' wrap' : '';
327
328
  return [
328
329
  '<section class="code-card">',
329
330
  '<div class="code-toolbar">',
@@ -332,19 +333,23 @@ const DASHBOARD_DOCUMENT = String.raw `<!DOCTYPE html>
332
333
  COPY_ICON,
333
334
  '</button>',
334
335
  '</div>',
335
- '<pre class="code-block ' + escapeHtml(languageClass) + '"><code>' + highlightedHtml + '</code></pre>',
336
+ '<pre class="code-block ' + escapeHtml(languageClass) + wrapClass + '"><code>' + highlightedHtml + '</code></pre>',
336
337
  '</section>'
337
338
  ].join('');
338
339
  };
339
340
 
340
341
  const renderTextCard = (label, value) => {
341
342
  const source = String(value ?? '');
342
- return renderCodeCard(label, source, escapeHtml(source), 'language-text');
343
+ return renderCodeCard(label, source, escapeHtml(source), 'language-text', { wrap: true });
343
344
  };
344
345
 
345
- const renderHtmlPreview = (label, html) => {
346
+ const renderHtmlPreview = (label, html, options = {}) => {
346
347
  const source = String(html ?? '');
347
348
  const copyId = registerCopyPayload(source);
349
+ const rawHtmlBlock = '<pre class="code-block language-html wrap"><code>' + escapeHtml(source) + '</code></pre>';
350
+ const rawHtmlSection = options.collapseSource === true
351
+ ? '<details class="inline-collapse"><summary>View raw HTML source</summary>' + rawHtmlBlock + '</details>'
352
+ : rawHtmlBlock;
348
353
  return [
349
354
  '<section class="code-card">',
350
355
  '<div class="code-toolbar">',
@@ -353,8 +358,8 @@ const DASHBOARD_DOCUMENT = String.raw `<!DOCTYPE html>
353
358
  COPY_ICON,
354
359
  '</button>',
355
360
  '</div>',
356
- '<pre class="code-block language-html"><code>' + escapeHtml(source) + '</code></pre>',
357
361
  '<div class="html-preview-wrap"><iframe class="html-preview" sandbox="allow-same-origin" srcdoc="' + escapeHtml(source) + '"></iframe></div>',
362
+ rawHtmlSection,
358
363
  '</section>'
359
364
  ].join('');
360
365
  };
@@ -378,7 +383,7 @@ const DASHBOARD_DOCUMENT = String.raw `<!DOCTYPE html>
378
383
  }
379
384
 
380
385
  output += escapeHtml(source.slice(lastIndex));
381
- return renderCodeCard(label, source, output, 'language-json');
386
+ return renderCodeCard(label, source, output, 'language-json', { wrap: true });
382
387
  };
383
388
 
384
389
  const highlightSql = (sql) => {
@@ -413,7 +418,9 @@ const DASHBOARD_DOCUMENT = String.raw `<!DOCTYPE html>
413
418
  const renderPayload = (label, value) => {
414
419
  if (value === undefined) return '<p class="trace-note">No ' + escapeHtml(label.toLowerCase()) + ' was captured.</p>';
415
420
  if (typeof value === 'string') {
416
- return looksLikeHtml(value) ? renderHtmlPreview(label, value) : renderTextCard(label, value);
421
+ return looksLikeHtml(value)
422
+ ? renderHtmlPreview(label, value, { collapseSource: true })
423
+ : renderTextCard(label, value);
417
424
  }
418
425
  return detailJson(value, label);
419
426
  };
@@ -453,6 +460,17 @@ const DASHBOARD_DOCUMENT = String.raw `<!DOCTYPE html>
453
460
  return '<div class="summary">' + summary + '</div><div class="summary-sub">' + escapeHtml(secondary) + '</div>';
454
461
  };
455
462
 
463
+ const entrySummaryInlineHtml = (entry) => {
464
+ const summary = escapeHtml(entrySummaryText(entry) || 'No summary available');
465
+ const secondary = [
466
+ entry.type === 'request' ? 'Incoming request' : '',
467
+ entry.type === 'query' ? 'Database query' : '',
468
+ entry.type === 'exception' ? 'Unhandled error' : '',
469
+ entry.type === 'client_request' ? 'Outbound HTTP call' : ''
470
+ ].find(Boolean) || 'Trace record';
471
+ return '<span class="summary">' + summary + '</span><span class="summary-sub">' + escapeHtml(secondary) + '</span>';
472
+ };
473
+
456
474
  const renderMetricBox = (title, items) => {
457
475
  return [
458
476
  '<section class="detail-box">',
@@ -577,8 +595,8 @@ const DASHBOARD_DOCUMENT = String.raw `<!DOCTYPE html>
577
595
  ]),
578
596
  '</div>',
579
597
  '<div class="detail-stack">',
580
- renderPayload('Mail Text', content.text),
581
598
  renderPayload('Mail Html', content.html),
599
+ renderPayload('Mail Text', content.text),
582
600
  '</div>'
583
601
  ].join('');
584
602
  }
@@ -603,10 +621,31 @@ const DASHBOARD_DOCUMENT = String.raw `<!DOCTYPE html>
603
621
  return detailJson(content);
604
622
  };
605
623
 
606
- const renderTraceItems = (entries) => {
624
+ const renderTraceItems = (entries, options = {}) => {
607
625
  if (entries.length === 0) return '<p class="trace-note">No related entries captured.</p>';
608
626
 
627
+ const collapsible = options.collapsible === true;
628
+ const isInitiallyOpen = options.collapsed !== true;
629
+
609
630
  return '<div class="trace-panel">' + entries.map((entry) => {
631
+ if (collapsible) {
632
+ return [
633
+ '<details class="trace-item trace-disclosure"' + (isInitiallyOpen ? ' open' : '') + '>',
634
+ '<summary class="trace-item-head trace-summary">',
635
+ '<span class="trace-summary-main">',
636
+ '<span><span class="' + typeClass(entry) + '">' + escapeHtml(entry.type) + '</span></span>',
637
+ '<span class="trace-summary-copy">' + entrySummaryInlineHtml(entry) + '</span>',
638
+ '</span>',
639
+ '<span class="activity-head">' + durationHtml(entry) + '<span class="activity-time">' + escapeHtml(timeSince(entry.createdAt)) + '</span></span>',
640
+ '</summary>',
641
+ '<div class="trace-disclosure-body">',
642
+ '<div>' + tagsHtml(entry.tags) + '</div>',
643
+ renderEntryBody(entry),
644
+ '</div>',
645
+ '</details>'
646
+ ].join('');
647
+ }
648
+
610
649
  return [
611
650
  '<section class="trace-item">',
612
651
  '<div class="trace-item-head">',
@@ -665,7 +704,7 @@ const DASHBOARD_DOCUMENT = String.raw `<!DOCTYPE html>
665
704
  payload: detailJson(content.payload || {}, 'Payload Json'),
666
705
  headers: '<div class="detail-stack">' + detailJson(content.headers || {}, 'Request Header Json') + detailJson(content.responseHeaders || {}, 'Response Header Json') + '</div>',
667
706
  response: '<div class="detail-stack"><div class="detail-grid">' + renderMetricBox('Status', [{ label: 'Response status', value: escapeHtml(content.responseStatus || '') }, { label: 'Duration', value: escapeHtml(formatDuration(getEntryDuration(entry))) }]) + '</div>' + (content.responseBody === undefined ? '<p class="trace-note">No response body was captured for this request.</p>' : detailJson(content.responseBody, 'Response Body Json')) + detailJson(content.responseHeaders || {}, 'Response Header Json') + '</div>',
668
- queries: renderTraceItems(batchEntriesByType('query')),
707
+ queries: renderTraceItems(batchEntriesByType('query'), { collapsible: true, collapsed: true }),
669
708
  logs: renderTraceItems(batchEntriesByType('log')),
670
709
  exceptions: renderTraceItems(batchEntriesByType('exception')),
671
710
  http: renderTraceItems(batchEntriesByType('client_request')),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zintrust/trace",
3
- "version": "0.4.84",
3
+ "version": "0.4.86",
4
4
  "description": "Trace assistant for ZinTrust: logs requests, queries, exceptions, jobs, and more.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -40,7 +40,7 @@
40
40
  "node": ">=20.0.0"
41
41
  },
42
42
  "peerDependencies": {
43
- "@zintrust/core": "^0.4.83"
43
+ "@zintrust/core": "^0.4.84"
44
44
  },
45
45
  "publishConfig": {
46
46
  "access": "public"
@@ -76,11 +76,11 @@ const DASHBOARD_DOCUMENT = String.raw`<!DOCTYPE html>
76
76
  .panel{border-radius:var(--radius);border:1px solid var(--line);background:var(--surface);box-shadow:var(--shadow);backdrop-filter:blur(16px)}.stats-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:16px;margin-bottom:18px}.stat-card{padding:20px;position:relative;overflow:hidden}.stat-card::after{content:'';position:absolute;right:-18px;bottom:-26px;width:92px;height:92px;border-radius:28px;background:linear-gradient(135deg,rgba(56,189,248,.16),rgba(34,197,94,.08));transform:rotate(18deg)}.stat-label{font-size:.74rem;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);font-weight:800;margin-bottom:12px}.stat-value{font-size:2.25rem;font-weight:800;line-height:1}.stat-meta{margin-top:10px;color:var(--muted);font-size:.9rem}.content-grid{display:grid;grid-template-columns:minmax(0,1.65fr) minmax(320px,.95fr);gap:18px}.side-stack{display:grid;gap:18px}
77
77
  .section-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;padding:22px 24px 16px}.section-head h3{margin:0;font-size:1.04rem}.section-head p{margin:6px 0 0;color:var(--muted);font-size:.92rem}.toolbar{display:flex;flex-wrap:wrap;gap:10px;padding:0 24px 18px}.control,.toolbar input,.toolbar select{height:44px;border-radius:13px;border:1px solid var(--line);background:var(--surface-strong);color:var(--text);padding:0 14px;min-width:0}.toolbar input,.toolbar select{flex:1 1 180px}.toolbar input::placeholder{color:var(--muted)}.btn{height:44px;border:none;border-radius:13px;padding:0 16px;cursor:pointer;font-weight:800}.btn-primary{background:linear-gradient(135deg,var(--accent-strong),var(--accent));color:#fff}.btn-danger{background:rgba(239,68,68,.12);color:var(--danger);border:1px solid rgba(239,68,68,.18)}.btn-ghost{background:var(--surface-soft);color:var(--text);border:1px solid var(--line)}
78
78
  .table-wrap{overflow:auto;padding:0 12px 12px}table{width:100%;border-collapse:separate;border-spacing:0;min-width:880px}th{padding:14px;color:var(--muted);font-size:.74rem;font-weight:800;letter-spacing:.12em;text-transform:uppercase;text-align:left;border-bottom:1px solid var(--line)}td{padding:15px 14px;border-bottom:1px solid var(--line);vertical-align:top}.row-button{cursor:pointer}.row-button:hover td{background:rgba(56,189,248,.05)}.summary{font-size:.93rem;font-weight:700;line-height:1.4;color:var(--text)}.summary-sub{margin-top:6px;color:var(--muted);font-size:.82rem;line-height:1.4}.mono{font-family:var(--mono)}.empty{padding:44px 24px;color:var(--muted);line-height:1.65;text-align:center}.pagination{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:0 24px 24px;color:var(--muted);flex-wrap:wrap}.pagination-controls{display:flex;gap:8px}.pagination button{height:40px;min-width:92px;padding:0 14px;border-radius:12px;border:1px solid var(--line);background:var(--surface-strong);color:var(--text);cursor:pointer}.pagination button:disabled{opacity:.45;cursor:not-allowed}
79
- .activity-list{list-style:none;margin:0;padding:0 24px 24px}.activity-item{padding:14px 0;border-top:1px solid var(--line)}.activity-item:first-child{border-top:none}.activity-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.activity-time{color:var(--muted);font-size:.85rem}.activity-summary{margin-top:8px;color:var(--text);line-height:1.48}.back-link{display:inline-flex;align-items:center;gap:8px;margin:0 0 14px;color:var(--accent);font-weight:800;cursor:pointer}.detail-card{padding:24px}.detail-meta{display:flex;flex-wrap:wrap;gap:10px;margin:14px 0 20px;color:var(--muted);font-size:.9rem}.detail-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px}.detail-stack{display:grid;gap:16px;margin-top:18px}.detail-box{padding:16px;border-radius:16px;background:var(--surface-soft);border:1px solid var(--line)}.detail-box h4{margin:0 0 10px;font-size:.92rem}.detail-box dl{margin:0;display:grid;gap:8px}.detail-box dt{font-size:.76rem;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);font-weight:800}.detail-box dd{margin:0;color:var(--text);line-height:1.45}.trace-tabs{display:flex;gap:10px;flex-wrap:wrap;margin:20px 0 16px}.trace-tab{border:none;border-radius:12px;padding:10px 12px;background:transparent;color:var(--muted);cursor:pointer;box-shadow:inset 0 0 0 1px var(--line);font-weight:800}.trace-tab.active{background:rgba(56,189,248,.12);color:var(--text);box-shadow:inset 0 0 0 1px rgba(56,189,248,.28)}.trace-panel{display:grid;gap:14px}.trace-item{padding:18px;border-radius:16px;background:var(--surface-soft);border:1px solid var(--line)}.trace-item-head{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.trace-item-summary{margin-top:10px;display:grid;gap:10px}.trace-note{color:var(--muted);line-height:1.6}
79
+ .activity-list{list-style:none;margin:0;padding:0 24px 24px}.activity-item{padding:14px 0;border-top:1px solid var(--line)}.activity-item:first-child{border-top:none}.activity-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.activity-time{color:var(--muted);font-size:.85rem}.activity-summary{margin-top:8px;color:var(--text);line-height:1.48}.back-link{display:inline-flex;align-items:center;gap:8px;margin:0 0 14px;color:var(--accent);font-weight:800;cursor:pointer}.detail-card{padding:24px}.detail-meta{display:flex;flex-wrap:wrap;gap:10px;margin:14px 0 20px;color:var(--muted);font-size:.9rem;overflow-wrap:anywhere}.detail-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px}.detail-stack{display:grid;gap:16px;margin-top:18px}.detail-box{padding:16px;border-radius:16px;background:var(--surface-soft);border:1px solid var(--line)}.detail-box h4{margin:0 0 10px;font-size:.92rem}.detail-box dl{margin:0;display:grid;gap:8px}.detail-box dt{font-size:.76rem;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);font-weight:800}.detail-box dd{margin:0;color:var(--text);line-height:1.45;overflow-wrap:anywhere}.trace-tabs{display:flex;gap:10px;flex-wrap:wrap;margin:20px 0 16px}.trace-tab{border:none;border-radius:12px;padding:10px 12px;background:transparent;color:var(--muted);cursor:pointer;box-shadow:inset 0 0 0 1px var(--line);font-weight:800}.trace-tab.active{background:rgba(56,189,248,.12);color:var(--text);box-shadow:inset 0 0 0 1px rgba(56,189,248,.28)}.trace-panel{display:grid;gap:14px}.trace-item{padding:18px;border-radius:16px;background:var(--surface-soft);border:1px solid var(--line)}.trace-item-head{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.trace-item-summary{margin-top:10px;display:grid;gap:10px}.trace-note{color:var(--muted);line-height:1.6}.trace-disclosure{padding:0;overflow:hidden}.trace-disclosure[open]{padding-bottom:18px}.trace-disclosure .trace-item-summary{margin-top:0}.trace-disclosure-body{display:grid;gap:12px;padding:0 18px}.trace-summary{list-style:none;cursor:pointer;padding:18px}.trace-summary::-webkit-details-marker{display:none}.trace-summary-main{display:grid;gap:10px;min-width:0;flex:1}.trace-summary-copy{display:grid;gap:6px;min-width:0}.trace-summary-copy .summary,.trace-summary-copy .summary-sub{display:block;overflow-wrap:anywhere}.trace-disclosure-body .summary-sub{overflow-wrap:anywhere}
80
80
  .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}
81
81
  .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}
82
82
  .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}
83
- .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}
83
+ .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.wrap{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word}.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}.inline-collapse{margin:0;border-top:1px solid var(--line);background:var(--surface-strong)}.inline-collapse summary{cursor:pointer;list-style:none;padding:14px 16px;font-size:.82rem;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);font-weight:800}.inline-collapse summary::-webkit-details-marker{display:none}.inline-collapse[open] summary{border-bottom:1px solid var(--line)}.inline-collapse .code-block{border-top:none}.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}
84
84
  @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}}
85
85
  </style>
86
86
  </head>
@@ -328,8 +328,9 @@ const DASHBOARD_DOCUMENT = String.raw`<!DOCTYPE html>
328
328
  return id;
329
329
  };
330
330
 
331
- const renderCodeCard = (label, rawText, highlightedHtml, languageClass) => {
331
+ const renderCodeCard = (label, rawText, highlightedHtml, languageClass, options = {}) => {
332
332
  const copyId = registerCopyPayload(rawText);
333
+ const wrapClass = options.wrap === true ? ' wrap' : '';
333
334
  return [
334
335
  '<section class="code-card">',
335
336
  '<div class="code-toolbar">',
@@ -338,19 +339,23 @@ const DASHBOARD_DOCUMENT = String.raw`<!DOCTYPE html>
338
339
  COPY_ICON,
339
340
  '</button>',
340
341
  '</div>',
341
- '<pre class="code-block ' + escapeHtml(languageClass) + '"><code>' + highlightedHtml + '</code></pre>',
342
+ '<pre class="code-block ' + escapeHtml(languageClass) + wrapClass + '"><code>' + highlightedHtml + '</code></pre>',
342
343
  '</section>'
343
344
  ].join('');
344
345
  };
345
346
 
346
347
  const renderTextCard = (label, value) => {
347
348
  const source = String(value ?? '');
348
- return renderCodeCard(label, source, escapeHtml(source), 'language-text');
349
+ return renderCodeCard(label, source, escapeHtml(source), 'language-text', { wrap: true });
349
350
  };
350
351
 
351
- const renderHtmlPreview = (label, html) => {
352
+ const renderHtmlPreview = (label, html, options = {}) => {
352
353
  const source = String(html ?? '');
353
354
  const copyId = registerCopyPayload(source);
355
+ const rawHtmlBlock = '<pre class="code-block language-html wrap"><code>' + escapeHtml(source) + '</code></pre>';
356
+ const rawHtmlSection = options.collapseSource === true
357
+ ? '<details class="inline-collapse"><summary>View raw HTML source</summary>' + rawHtmlBlock + '</details>'
358
+ : rawHtmlBlock;
354
359
  return [
355
360
  '<section class="code-card">',
356
361
  '<div class="code-toolbar">',
@@ -359,8 +364,8 @@ const DASHBOARD_DOCUMENT = String.raw`<!DOCTYPE html>
359
364
  COPY_ICON,
360
365
  '</button>',
361
366
  '</div>',
362
- '<pre class="code-block language-html"><code>' + escapeHtml(source) + '</code></pre>',
363
367
  '<div class="html-preview-wrap"><iframe class="html-preview" sandbox="allow-same-origin" srcdoc="' + escapeHtml(source) + '"></iframe></div>',
368
+ rawHtmlSection,
364
369
  '</section>'
365
370
  ].join('');
366
371
  };
@@ -384,7 +389,7 @@ const DASHBOARD_DOCUMENT = String.raw`<!DOCTYPE html>
384
389
  }
385
390
 
386
391
  output += escapeHtml(source.slice(lastIndex));
387
- return renderCodeCard(label, source, output, 'language-json');
392
+ return renderCodeCard(label, source, output, 'language-json', { wrap: true });
388
393
  };
389
394
 
390
395
  const highlightSql = (sql) => {
@@ -419,7 +424,9 @@ const DASHBOARD_DOCUMENT = String.raw`<!DOCTYPE html>
419
424
  const renderPayload = (label, value) => {
420
425
  if (value === undefined) return '<p class="trace-note">No ' + escapeHtml(label.toLowerCase()) + ' was captured.</p>';
421
426
  if (typeof value === 'string') {
422
- return looksLikeHtml(value) ? renderHtmlPreview(label, value) : renderTextCard(label, value);
427
+ return looksLikeHtml(value)
428
+ ? renderHtmlPreview(label, value, { collapseSource: true })
429
+ : renderTextCard(label, value);
423
430
  }
424
431
  return detailJson(value, label);
425
432
  };
@@ -459,6 +466,17 @@ const DASHBOARD_DOCUMENT = String.raw`<!DOCTYPE html>
459
466
  return '<div class="summary">' + summary + '</div><div class="summary-sub">' + escapeHtml(secondary) + '</div>';
460
467
  };
461
468
 
469
+ const entrySummaryInlineHtml = (entry) => {
470
+ const summary = escapeHtml(entrySummaryText(entry) || 'No summary available');
471
+ const secondary = [
472
+ entry.type === 'request' ? 'Incoming request' : '',
473
+ entry.type === 'query' ? 'Database query' : '',
474
+ entry.type === 'exception' ? 'Unhandled error' : '',
475
+ entry.type === 'client_request' ? 'Outbound HTTP call' : ''
476
+ ].find(Boolean) || 'Trace record';
477
+ return '<span class="summary">' + summary + '</span><span class="summary-sub">' + escapeHtml(secondary) + '</span>';
478
+ };
479
+
462
480
  const renderMetricBox = (title, items) => {
463
481
  return [
464
482
  '<section class="detail-box">',
@@ -583,8 +601,8 @@ const DASHBOARD_DOCUMENT = String.raw`<!DOCTYPE html>
583
601
  ]),
584
602
  '</div>',
585
603
  '<div class="detail-stack">',
586
- renderPayload('Mail Text', content.text),
587
604
  renderPayload('Mail Html', content.html),
605
+ renderPayload('Mail Text', content.text),
588
606
  '</div>'
589
607
  ].join('');
590
608
  }
@@ -609,10 +627,31 @@ const DASHBOARD_DOCUMENT = String.raw`<!DOCTYPE html>
609
627
  return detailJson(content);
610
628
  };
611
629
 
612
- const renderTraceItems = (entries) => {
630
+ const renderTraceItems = (entries, options = {}) => {
613
631
  if (entries.length === 0) return '<p class="trace-note">No related entries captured.</p>';
614
632
 
633
+ const collapsible = options.collapsible === true;
634
+ const isInitiallyOpen = options.collapsed !== true;
635
+
615
636
  return '<div class="trace-panel">' + entries.map((entry) => {
637
+ if (collapsible) {
638
+ return [
639
+ '<details class="trace-item trace-disclosure"' + (isInitiallyOpen ? ' open' : '') + '>',
640
+ '<summary class="trace-item-head trace-summary">',
641
+ '<span class="trace-summary-main">',
642
+ '<span><span class="' + typeClass(entry) + '">' + escapeHtml(entry.type) + '</span></span>',
643
+ '<span class="trace-summary-copy">' + entrySummaryInlineHtml(entry) + '</span>',
644
+ '</span>',
645
+ '<span class="activity-head">' + durationHtml(entry) + '<span class="activity-time">' + escapeHtml(timeSince(entry.createdAt)) + '</span></span>',
646
+ '</summary>',
647
+ '<div class="trace-disclosure-body">',
648
+ '<div>' + tagsHtml(entry.tags) + '</div>',
649
+ renderEntryBody(entry),
650
+ '</div>',
651
+ '</details>'
652
+ ].join('');
653
+ }
654
+
616
655
  return [
617
656
  '<section class="trace-item">',
618
657
  '<div class="trace-item-head">',
@@ -671,7 +710,7 @@ const DASHBOARD_DOCUMENT = String.raw`<!DOCTYPE html>
671
710
  payload: detailJson(content.payload || {}, 'Payload Json'),
672
711
  headers: '<div class="detail-stack">' + detailJson(content.headers || {}, 'Request Header Json') + detailJson(content.responseHeaders || {}, 'Response Header Json') + '</div>',
673
712
  response: '<div class="detail-stack"><div class="detail-grid">' + renderMetricBox('Status', [{ label: 'Response status', value: escapeHtml(content.responseStatus || '') }, { label: 'Duration', value: escapeHtml(formatDuration(getEntryDuration(entry))) }]) + '</div>' + (content.responseBody === undefined ? '<p class="trace-note">No response body was captured for this request.</p>' : detailJson(content.responseBody, 'Response Body Json')) + detailJson(content.responseHeaders || {}, 'Response Header Json') + '</div>',
674
- queries: renderTraceItems(batchEntriesByType('query')),
713
+ queries: renderTraceItems(batchEntriesByType('query'), { collapsible: true, collapsed: true }),
675
714
  logs: renderTraceItems(batchEntriesByType('log')),
676
715
  exceptions: renderTraceItems(batchEntriesByType('exception')),
677
716
  http: renderTraceItems(batchEntriesByType('client_request')),
@@ -1,13 +0,0 @@
1
- /**
2
- * TraceStorage — sealed namespace wrapping the D1/SQLite driver.
3
- * Resolves the correct IDatabase from the app config, then delegates all
4
- * read/write operations to the trace storage facade.
5
- */
6
- import type { IDatabase } from '@zintrust/core';
7
- import type { ITraceStorage } from '../types';
8
- export declare const TraceStorage: Readonly<{
9
- resolveStorage: (db: IDatabase) => ITraceStorage;
10
- reset: () => void;
11
- familyHash: (input: string) => string;
12
- }>;
13
- export { type ITraceStorage } from '../types';
@@ -1,195 +0,0 @@
1
- import { familyHash } from '../utils/familyHash.js';
2
- const TABLE_ENTRIES = 'zin_trace_entries';
3
- const TABLE_TAGS = 'zin_trace_entries_tags';
4
- const TABLE_MONITORING = 'zin_trace_monitoring';
5
- const generateUuid = () => crypto.randomUUID();
6
- const rowToEntry = (row, tags) => ({
7
- uuid: row.uuid,
8
- batchId: row.batch_id,
9
- familyHash: row.family_hash ?? undefined,
10
- type: row.type,
11
- content: JSON.parse(row.content),
12
- tags,
13
- isLatest: Boolean(row.is_latest),
14
- createdAt: row.created_at,
15
- });
16
- const insertTags = async (db, uuid, tags) => {
17
- if (tags.length === 0)
18
- return;
19
- await Promise.all(tags.map(async (tag) => {
20
- await db.execute(`INSERT OR IGNORE INTO ${TABLE_TAGS} (entry_uuid, tag) VALUES (?, ?)`, [
21
- uuid,
22
- tag,
23
- ]);
24
- }));
25
- };
26
- const buildEntryFilters = (opts) => {
27
- const conditions = [];
28
- const params = [];
29
- if (opts.type) {
30
- conditions.push('e.type = ?');
31
- params.push(opts.type);
32
- }
33
- if (opts.batchId) {
34
- conditions.push('e.batch_id = ?');
35
- params.push(opts.batchId);
36
- }
37
- if (opts.from) {
38
- conditions.push('e.created_at >= ?');
39
- params.push(opts.from);
40
- }
41
- if (opts.to) {
42
- conditions.push('e.created_at <= ?');
43
- params.push(opts.to);
44
- }
45
- let joinClause = '';
46
- if (opts.tag) {
47
- joinClause = `INNER JOIN ${TABLE_TAGS} t ON t.entry_uuid = e.uuid AND t.tag = ?`;
48
- params.unshift(opts.tag);
49
- }
50
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
51
- const countParams = opts.tag ? [opts.tag, ...params.slice(1)] : [...params];
52
- return { joinClause, whereClause, params, countParams };
53
- };
54
- const loadTagsByUuid = async (db, uuids) => {
55
- const tagsByUuid = new Map();
56
- if (uuids.length === 0)
57
- return tagsByUuid;
58
- const tagRows = (await db.query(`SELECT entry_uuid, tag FROM ${TABLE_TAGS} WHERE entry_uuid IN (${uuids.map(() => '?').join(',')})`, uuids));
59
- for (const tagRow of tagRows) {
60
- const tags = tagsByUuid.get(tagRow.entry_uuid) ?? [];
61
- tags.push(tagRow.tag);
62
- tagsByUuid.set(tagRow.entry_uuid, tags);
63
- }
64
- return tagsByUuid;
65
- };
66
- // The storage facade intentionally groups related DB operations in one factory.
67
- // eslint-disable-next-line max-lines-per-function
68
- const createStorage = (db) => {
69
- const writeEntry = async (entry) => {
70
- const uuid = entry.uuid || generateUuid();
71
- await db.execute(`INSERT INTO ${TABLE_ENTRIES} (uuid, batch_id, family_hash, type, content, is_latest, created_at)
72
- VALUES (?, ?, ?, ?, ?, ?, ?)`, [
73
- uuid,
74
- entry.batchId,
75
- entry.familyHash ?? null,
76
- entry.type,
77
- JSON.stringify(entry.content),
78
- entry.isLatest ? 1 : 0,
79
- entry.createdAt,
80
- ]);
81
- await insertTags(db, uuid, entry.tags);
82
- };
83
- const updateEntry = async (uuid, patch) => {
84
- const sets = [];
85
- const params = [];
86
- if (patch.content !== undefined) {
87
- sets.push('content = ?');
88
- params.push(JSON.stringify(patch.content));
89
- }
90
- if (patch.isLatest !== undefined) {
91
- sets.push('is_latest = ?');
92
- params.push(patch.isLatest ? 1 : 0);
93
- }
94
- if (sets.length === 0)
95
- return;
96
- params.push(uuid);
97
- await db.execute(`UPDATE ${TABLE_ENTRIES} SET ${sets.join(', ')} WHERE uuid = ?`, params);
98
- };
99
- const markFamilyStale = async (hash, exceptUuid) => {
100
- await db.execute(`UPDATE ${TABLE_ENTRIES} SET is_latest = 0
101
- WHERE family_hash = ? AND uuid != ? AND is_latest = 1`, [hash, exceptUuid]);
102
- };
103
- const queryEntries = async (opts) => {
104
- const page = opts.page ?? 1;
105
- const perPage = opts.perPage ?? 50;
106
- const offset = (page - 1) * perPage;
107
- const { joinClause, whereClause, params, countParams } = buildEntryFilters(opts);
108
- const countResult = (await db.queryOne(`SELECT COUNT(*) as cnt FROM ${TABLE_ENTRIES} e ${joinClause} ${whereClause}`, countParams));
109
- const total = countResult?.cnt ?? 0;
110
- const rows = (await db.query(`SELECT e.id, e.uuid, e.batch_id, e.family_hash, e.type, e.content, e.is_latest, e.created_at
111
- FROM ${TABLE_ENTRIES} e ${joinClause} ${whereClause}
112
- ORDER BY e.created_at DESC, e.id DESC
113
- LIMIT ? OFFSET ?`, [...params, perPage, offset]));
114
- const tagsByUuid = await loadTagsByUuid(db, rows.map((row) => row.uuid));
115
- return {
116
- data: rows.map((row) => rowToEntry(row, tagsByUuid.get(row.uuid) ?? [])),
117
- total,
118
- };
119
- };
120
- const getEntry = async (uuid) => {
121
- const row = (await db.queryOne(`SELECT id, uuid, batch_id, family_hash, type, content, is_latest, created_at
122
- FROM ${TABLE_ENTRIES}
123
- WHERE uuid = ?`, [uuid]));
124
- if (!row)
125
- return null;
126
- const tags = (await db.query(`SELECT tag FROM ${TABLE_TAGS} WHERE entry_uuid = ?`, [
127
- uuid,
128
- ]));
129
- return rowToEntry(row, tags.map((tag) => tag.tag));
130
- };
131
- const getBatch = async (batchId) => {
132
- const rows = (await db.query(`SELECT id, uuid, batch_id, family_hash, type, content, is_latest, created_at
133
- FROM ${TABLE_ENTRIES}
134
- WHERE batch_id = ?
135
- ORDER BY created_at ASC, id ASC`, [batchId]));
136
- if (rows.length === 0)
137
- return [];
138
- const tagsByUuid = await loadTagsByUuid(db, rows.map((row) => row.uuid));
139
- return rows.map((row) => rowToEntry(row, tagsByUuid.get(row.uuid) ?? []));
140
- };
141
- const prune = async (olderThanMs, keepExceptions = false) => {
142
- const countResult = (await db.queryOne(`SELECT COUNT(*) as cnt FROM ${TABLE_ENTRIES}
143
- WHERE created_at < ?
144
- ${keepExceptions ? "AND type != 'exception'" : ''}`, [olderThanMs]));
145
- const deleted = countResult?.cnt ?? 0;
146
- if (deleted === 0)
147
- return 0;
148
- await db.execute(`DELETE FROM ${TABLE_ENTRIES}
149
- WHERE created_at < ?
150
- ${keepExceptions ? "AND type != 'exception'" : ''}`, [olderThanMs]);
151
- return deleted;
152
- };
153
- const clear = async () => {
154
- await db.execute(`DELETE FROM ${TABLE_ENTRIES}`, []);
155
- };
156
- const getMonitoring = async () => {
157
- const rows = (await db.query(`SELECT tag FROM ${TABLE_MONITORING}`, []));
158
- return rows.map((row) => row.tag);
159
- };
160
- const addMonitoring = async (tag) => {
161
- await db.execute(`INSERT OR IGNORE INTO ${TABLE_MONITORING} (tag) VALUES (?)`, [tag]);
162
- };
163
- const removeMonitoring = async (tag) => {
164
- await db.execute(`DELETE FROM ${TABLE_MONITORING} WHERE tag = ?`, [tag]);
165
- };
166
- const stats = async () => {
167
- const rows = (await db.query(`SELECT type, COUNT(*) as cnt FROM ${TABLE_ENTRIES} GROUP BY type`, []));
168
- const output = {};
169
- for (const row of rows) {
170
- output[row.type] = row.cnt;
171
- }
172
- return output;
173
- };
174
- return {
175
- writeEntry,
176
- updateEntry,
177
- markFamilyStale,
178
- queryEntries,
179
- getEntry,
180
- getBatch,
181
- prune,
182
- clear,
183
- getMonitoring,
184
- addMonitoring,
185
- removeMonitoring,
186
- stats,
187
- };
188
- };
189
- const resolveStorage = (db) => {
190
- return createStorage(db);
191
- };
192
- const reset = () => {
193
- return;
194
- };
195
- export const TraceStorage = Object.freeze({ resolveStorage, reset, familyHash });