@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.
- package/dist/build-manifest.json +10 -22
- package/dist/dashboard/ui.js +51 -12
- package/package.json +2 -2
- package/src/dashboard/ui.ts +51 -12
- package/dist/storage/DebuggerStorage.d.ts +0 -13
- package/dist/storage/DebuggerStorage.js +0 -195
package/dist/build-manifest.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zintrust/trace",
|
|
3
|
-
"version": "0.4.
|
|
4
|
-
"buildDate": "2026-04-
|
|
3
|
+
"version": "0.4.86",
|
|
4
|
+
"buildDate": "2026-04-09T08:08:55.865Z",
|
|
5
5
|
"buildEnvironment": {
|
|
6
|
-
"node": "
|
|
7
|
-
"platform": "
|
|
8
|
-
"arch": "
|
|
6
|
+
"node": "v20.20.2",
|
|
7
|
+
"platform": "linux",
|
|
8
|
+
"arch": "x64"
|
|
9
9
|
},
|
|
10
10
|
"git": {
|
|
11
|
-
"commit": "
|
|
12
|
-
"branch": "
|
|
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":
|
|
74
|
-
"sha256": "
|
|
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": "
|
|
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"
|
package/dist/dashboard/ui.js
CHANGED
|
@@ -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)
|
|
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.
|
|
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.
|
|
43
|
+
"@zintrust/core": "^0.4.84"
|
|
44
44
|
},
|
|
45
45
|
"publishConfig": {
|
|
46
46
|
"access": "public"
|
package/src/dashboard/ui.ts
CHANGED
|
@@ -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)
|
|
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 });
|