@zintrust/trace 0.5.4 → 0.5.7
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/config.js +2 -0
- package/dist/dashboard/ui.js +4 -4
- package/dist/storage/TraceContentBudget.js +47 -85
- package/dist/types.d.ts +1 -0
- package/dist/utils/requestFilter.d.ts +7 -2
- package/dist/utils/requestFilter.js +30 -4
- package/dist/watchers/AuthWatcher.js +4 -1
- package/dist/watchers/BatchWatcher.js +4 -1
- package/dist/watchers/CacheWatcher.js +4 -1
- package/dist/watchers/CommandWatcher.js +4 -1
- package/dist/watchers/DumpWatcher.js +4 -1
- package/dist/watchers/EventWatcher.js +4 -1
- package/dist/watchers/ExceptionWatcher.js +5 -2
- package/dist/watchers/GateWatcher.js +4 -1
- package/dist/watchers/HttpClientWatcher.js +4 -1
- package/dist/watchers/HttpWatcher.js +1 -1
- package/dist/watchers/JobWatcher.js +4 -1
- package/dist/watchers/LogWatcher.js +1 -1
- package/dist/watchers/MailWatcher.js +4 -1
- package/dist/watchers/MiddlewareWatcher.js +4 -1
- package/dist/watchers/ModelWatcher.js +4 -1
- package/dist/watchers/NotificationWatcher.js +4 -1
- package/dist/watchers/QueryWatcher.js +1 -1
- package/dist/watchers/RedisWatcher.js +4 -1
- package/dist/watchers/ScheduleWatcher.js +4 -1
- package/dist/watchers/ViewWatcher.js +4 -1
- package/package.json +2 -2
- package/src/config.ts +2 -0
- package/src/dashboard/ui.ts +4 -4
- package/src/storage/TraceContentBudget.ts +57 -118
- package/src/types.ts +1 -0
- package/src/utils/requestFilter.ts +57 -11
- package/src/watchers/AuthWatcher.ts +4 -1
- package/src/watchers/BatchWatcher.ts +4 -1
- package/src/watchers/CacheWatcher.ts +4 -1
- package/src/watchers/CommandWatcher.ts +4 -1
- package/src/watchers/DumpWatcher.ts +4 -1
- package/src/watchers/EventWatcher.ts +4 -1
- package/src/watchers/ExceptionWatcher.ts +5 -2
- package/src/watchers/GateWatcher.ts +4 -1
- package/src/watchers/HttpClientWatcher.ts +4 -1
- package/src/watchers/HttpWatcher.ts +1 -1
- package/src/watchers/JobWatcher.ts +4 -1
- package/src/watchers/LogWatcher.ts +2 -1
- package/src/watchers/MailWatcher.ts +4 -1
- package/src/watchers/MiddlewareWatcher.ts +4 -1
- package/src/watchers/ModelWatcher.ts +4 -1
- package/src/watchers/NotificationWatcher.ts +4 -1
- package/src/watchers/QueryWatcher.ts +1 -1
- package/src/watchers/RedisWatcher.ts +4 -1
- package/src/watchers/ScheduleWatcher.ts +4 -1
- package/src/watchers/ViewWatcher.ts +4 -1
|
@@ -8,10 +8,11 @@ import { RequestFilter } from '../utils/requestFilter.js';
|
|
|
8
8
|
let _storage = null;
|
|
9
9
|
let _redactionFields = [];
|
|
10
10
|
let _ignoreRoutes = [];
|
|
11
|
+
let _ignorePath = [];
|
|
11
12
|
const emit = (to, subject, template, text, html) => {
|
|
12
13
|
if (!_storage)
|
|
13
14
|
return;
|
|
14
|
-
if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes))
|
|
15
|
+
if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes, _ignorePath))
|
|
15
16
|
return;
|
|
16
17
|
const content = {
|
|
17
18
|
to,
|
|
@@ -45,10 +46,12 @@ export const MailWatcher = Object.freeze({
|
|
|
45
46
|
_storage = storage;
|
|
46
47
|
_redactionFields = [...config.redaction.keys, ...config.redaction.body];
|
|
47
48
|
_ignoreRoutes = config.ignoreRoutes;
|
|
49
|
+
_ignorePath = config.ignorePath;
|
|
48
50
|
return () => {
|
|
49
51
|
_storage = null;
|
|
50
52
|
_redactionFields = [];
|
|
51
53
|
_ignoreRoutes = [];
|
|
54
|
+
_ignorePath = [];
|
|
52
55
|
};
|
|
53
56
|
},
|
|
54
57
|
});
|
|
@@ -3,10 +3,11 @@ import { EntryType } from '../types.js';
|
|
|
3
3
|
import { RequestFilter } from '../utils/requestFilter.js';
|
|
4
4
|
let _storage = null;
|
|
5
5
|
let _ignoreRoutes = [];
|
|
6
|
+
let _ignorePath = [];
|
|
6
7
|
const emit = (name, event, duration) => {
|
|
7
8
|
if (!_storage)
|
|
8
9
|
return;
|
|
9
|
-
if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes))
|
|
10
|
+
if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes, _ignorePath))
|
|
10
11
|
return;
|
|
11
12
|
const content = {
|
|
12
13
|
name,
|
|
@@ -33,6 +34,7 @@ export const MiddlewareWatcher = Object.freeze({
|
|
|
33
34
|
return () => undefined;
|
|
34
35
|
_storage = storage;
|
|
35
36
|
_ignoreRoutes = config.ignoreRoutes;
|
|
37
|
+
_ignorePath = config.ignorePath;
|
|
36
38
|
globalThis.__zintrust_trace_middleware_emit__ = emit;
|
|
37
39
|
return () => {
|
|
38
40
|
const globalState = globalThis;
|
|
@@ -41,6 +43,7 @@ export const MiddlewareWatcher = Object.freeze({
|
|
|
41
43
|
}
|
|
42
44
|
_storage = null;
|
|
43
45
|
_ignoreRoutes = [];
|
|
46
|
+
_ignorePath = [];
|
|
44
47
|
};
|
|
45
48
|
},
|
|
46
49
|
});
|
|
@@ -3,10 +3,11 @@ import { EntryType } from '../types.js';
|
|
|
3
3
|
import { RequestFilter } from '../utils/requestFilter.js';
|
|
4
4
|
let _storage = null;
|
|
5
5
|
let _ignoreRoutes = [];
|
|
6
|
+
let _ignorePath = [];
|
|
6
7
|
const emit = (action, model, id, changes) => {
|
|
7
8
|
if (!_storage)
|
|
8
9
|
return;
|
|
9
|
-
if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes))
|
|
10
|
+
if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes, _ignorePath))
|
|
10
11
|
return;
|
|
11
12
|
const content = {
|
|
12
13
|
action,
|
|
@@ -34,6 +35,7 @@ export const ModelWatcher = Object.freeze({
|
|
|
34
35
|
return () => undefined;
|
|
35
36
|
_storage = storage;
|
|
36
37
|
_ignoreRoutes = config.ignoreRoutes;
|
|
38
|
+
_ignorePath = config.ignorePath;
|
|
37
39
|
globalThis.__zintrust_trace_model_emit__ = emit;
|
|
38
40
|
return () => {
|
|
39
41
|
const globalState = globalThis;
|
|
@@ -42,6 +44,7 @@ export const ModelWatcher = Object.freeze({
|
|
|
42
44
|
}
|
|
43
45
|
_storage = null;
|
|
44
46
|
_ignoreRoutes = [];
|
|
47
|
+
_ignorePath = [];
|
|
45
48
|
};
|
|
46
49
|
},
|
|
47
50
|
});
|
|
@@ -6,10 +6,11 @@ import { RequestFilter } from '../utils/requestFilter.js';
|
|
|
6
6
|
let _storage = null;
|
|
7
7
|
let _redactionFields = [];
|
|
8
8
|
let _ignoreRoutes = [];
|
|
9
|
+
let _ignorePath = [];
|
|
9
10
|
const emit = (notification, channels, notifiable, message, payload) => {
|
|
10
11
|
if (!_storage)
|
|
11
12
|
return;
|
|
12
|
-
if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes))
|
|
13
|
+
if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes, _ignorePath))
|
|
13
14
|
return;
|
|
14
15
|
const content = {
|
|
15
16
|
notification,
|
|
@@ -41,10 +42,12 @@ export const NotificationWatcher = Object.freeze({
|
|
|
41
42
|
_storage = storage;
|
|
42
43
|
_redactionFields = [...config.redaction.keys, ...config.redaction.body];
|
|
43
44
|
_ignoreRoutes = config.ignoreRoutes;
|
|
45
|
+
_ignorePath = config.ignorePath;
|
|
44
46
|
return () => {
|
|
45
47
|
_storage = null;
|
|
46
48
|
_redactionFields = [];
|
|
47
49
|
_ignoreRoutes = [];
|
|
50
|
+
_ignorePath = [];
|
|
48
51
|
};
|
|
49
52
|
},
|
|
50
53
|
});
|
|
@@ -29,7 +29,7 @@ const isTraceStorageQuery = (sql) => {
|
|
|
29
29
|
const emit = (query, params, duration, connection = 'default') => {
|
|
30
30
|
if (_storage === null || _config === null)
|
|
31
31
|
return;
|
|
32
|
-
if (RequestFilter.shouldIgnoreCurrentRequest(_config.ignoreRoutes))
|
|
32
|
+
if (RequestFilter.shouldIgnoreCurrentRequest(_config.ignoreRoutes, _config.ignorePath))
|
|
33
33
|
return;
|
|
34
34
|
if (isTraceStorageQuery(query))
|
|
35
35
|
return;
|
|
@@ -4,11 +4,12 @@ import { AuthTag } from '../utils/authTag.js';
|
|
|
4
4
|
import { RequestFilter } from '../utils/requestFilter.js';
|
|
5
5
|
let _storage = null;
|
|
6
6
|
let _ignoreRoutes = [];
|
|
7
|
+
let _ignorePath = [];
|
|
7
8
|
/** Emit a redis command trace. Key/value payload is intentionally omitted for security. */
|
|
8
9
|
const emit = (command, duration) => {
|
|
9
10
|
if (!_storage)
|
|
10
11
|
return;
|
|
11
|
-
if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes))
|
|
12
|
+
if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes, _ignorePath))
|
|
12
13
|
return;
|
|
13
14
|
const content = { command, duration, hostname: TraceContext.getHostname() };
|
|
14
15
|
_storage
|
|
@@ -30,9 +31,11 @@ export const RedisWatcher = Object.freeze({
|
|
|
30
31
|
return () => undefined;
|
|
31
32
|
_storage = storage;
|
|
32
33
|
_ignoreRoutes = config.ignoreRoutes;
|
|
34
|
+
_ignorePath = config.ignorePath;
|
|
33
35
|
return () => {
|
|
34
36
|
_storage = null;
|
|
35
37
|
_ignoreRoutes = [];
|
|
38
|
+
_ignorePath = [];
|
|
36
39
|
};
|
|
37
40
|
},
|
|
38
41
|
});
|
|
@@ -6,10 +6,11 @@ import { EntryType } from '../types.js';
|
|
|
6
6
|
import { RequestFilter } from '../utils/requestFilter.js';
|
|
7
7
|
let _storage = null;
|
|
8
8
|
let _ignoreRoutes = [];
|
|
9
|
+
let _ignorePath = [];
|
|
9
10
|
const emit = (name, expression, status, duration, output) => {
|
|
10
11
|
if (!_storage)
|
|
11
12
|
return;
|
|
12
|
-
if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes))
|
|
13
|
+
if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes, _ignorePath))
|
|
13
14
|
return;
|
|
14
15
|
const content = {
|
|
15
16
|
name,
|
|
@@ -38,9 +39,11 @@ export const ScheduleWatcher = Object.freeze({
|
|
|
38
39
|
return () => undefined;
|
|
39
40
|
_storage = storage;
|
|
40
41
|
_ignoreRoutes = config.ignoreRoutes;
|
|
42
|
+
_ignorePath = config.ignorePath;
|
|
41
43
|
return () => {
|
|
42
44
|
_storage = null;
|
|
43
45
|
_ignoreRoutes = [];
|
|
46
|
+
_ignorePath = [];
|
|
44
47
|
};
|
|
45
48
|
},
|
|
46
49
|
});
|
|
@@ -3,10 +3,11 @@ import { EntryType } from '../types.js';
|
|
|
3
3
|
import { RequestFilter } from '../utils/requestFilter.js';
|
|
4
4
|
let _storage = null;
|
|
5
5
|
let _ignoreRoutes = [];
|
|
6
|
+
let _ignorePath = [];
|
|
6
7
|
const emit = (template, duration) => {
|
|
7
8
|
if (!_storage)
|
|
8
9
|
return;
|
|
9
|
-
if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes))
|
|
10
|
+
if (RequestFilter.shouldIgnoreCurrentRequest(_ignoreRoutes, _ignorePath))
|
|
10
11
|
return;
|
|
11
12
|
const content = { template, duration, hostname: TraceContext.getHostname() };
|
|
12
13
|
_storage
|
|
@@ -28,9 +29,11 @@ export const ViewWatcher = Object.freeze({
|
|
|
28
29
|
return () => undefined;
|
|
29
30
|
_storage = storage;
|
|
30
31
|
_ignoreRoutes = config.ignoreRoutes;
|
|
32
|
+
_ignorePath = config.ignorePath;
|
|
31
33
|
return () => {
|
|
32
34
|
_storage = null;
|
|
33
35
|
_ignoreRoutes = [];
|
|
36
|
+
_ignorePath = [];
|
|
34
37
|
};
|
|
35
38
|
},
|
|
36
39
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zintrust/trace",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.7",
|
|
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.5.
|
|
43
|
+
"@zintrust/core": "^0.5.5"
|
|
44
44
|
},
|
|
45
45
|
"publishConfig": {
|
|
46
46
|
"access": "public"
|
package/src/config.ts
CHANGED
|
@@ -259,6 +259,7 @@ const DEFAULTS: ITraceConfig = Object.freeze({
|
|
|
259
259
|
observeConnection: undefined,
|
|
260
260
|
pruneAfterHours: 24,
|
|
261
261
|
ignoreRoutes: ['/trace', '/health', '/ping'],
|
|
262
|
+
ignorePath: [],
|
|
262
263
|
slowQueryThreshold: 100,
|
|
263
264
|
captureCachePayloads: false,
|
|
264
265
|
captureQueryBindings: true,
|
|
@@ -350,6 +351,7 @@ export const TraceConfig = Object.freeze({
|
|
|
350
351
|
query: mergeStringLists(DEFAULTS.redaction.query, overrides.redaction?.query),
|
|
351
352
|
},
|
|
352
353
|
ignoreRoutes: overrides.ignoreRoutes ?? DEFAULTS.ignoreRoutes,
|
|
354
|
+
ignorePath: overrides.ignorePath ?? DEFAULTS.ignorePath,
|
|
353
355
|
});
|
|
354
356
|
},
|
|
355
357
|
|
package/src/dashboard/ui.ts
CHANGED
|
@@ -76,13 +76,13 @@ const DASHBOARD_DOCUMENT = String.raw`<!DOCTYPE html>
|
|
|
76
76
|
.main{padding:28px;min-width:0}.shell{max-width:1320px;margin:0 auto;display:grid;gap:18px}
|
|
77
77
|
.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}
|
|
78
78
|
.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)}
|
|
79
|
-
.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
|
+
.table-wrap{overflow:auto;padding:0 12px 12px}.table-wrap 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}
|
|
80
80
|
.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}
|
|
81
81
|
.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}
|
|
82
82
|
.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}
|
|
83
83
|
.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}
|
|
84
84
|
.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}
|
|
85
|
-
@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,.
|
|
85
|
+
@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:760px){.main{padding:16px}.shell{gap:14px}.stat-value{font-size:1.9rem}.section-head{flex-direction:column;align-items:stretch}.section-head .btn{width:100%}.toolbar{display:grid;grid-template-columns:1fr;padding-left:18px;padding-right:18px}.toolbar .btn,.toolbar input,.toolbar select{width:100%;flex:none}.pagination{padding-left:18px;padding-right:18px}.pagination,.pagination-controls{width:100%}.pagination-controls{justify-content:space-between}.pagination button{flex:1 1 0}.detail-meta{display:grid;gap:8px}.trace-tabs{flex-wrap:nowrap;overflow:auto;padding-bottom:4px}.trace-tab{white-space:nowrap}.code-toolbar{flex-wrap:wrap}.copy-button{width:100%}.html-preview{min-height:220px}.table-wrap{padding:0 14px 14px;overflow:visible}.table-wrap table{min-width:0;border-spacing:0}.table-wrap thead{display:none}.table-wrap tbody{display:grid;gap:12px}.table-wrap tr{display:block;border:1px solid var(--line);border-radius:16px;background:var(--surface-soft);overflow:hidden}.table-wrap td{display:grid;grid-template-columns:minmax(84px,108px) minmax(0,1fr);gap:10px;align-items:start;padding:12px 14px;border-bottom:1px solid var(--line)}.table-wrap td::before{content:attr(data-label);font-size:.72rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase;color:var(--muted)}.table-wrap td:last-child{border-bottom:none}.table-wrap td[data-label='Summary'],.table-wrap td[data-label='Tags']{grid-template-columns:1fr}.table-wrap td[data-label='Summary']::before,.table-wrap td[data-label='Tags']::before{margin-bottom:2px}.row-button:hover td{background:transparent}.row-button:hover{box-shadow:inset 0 0 0 1px rgba(56,189,248,.18)}}@media (max-width:640px){.stats-grid{grid-template-columns:1fr}.detail-card{padding:18px}.toolbar,.section-head,.activity-list,.monitoring-wrap{padding-left:18px;padding-right:18px}.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}}
|
|
86
86
|
</style>
|
|
87
87
|
</head>
|
|
88
88
|
<body>
|
|
@@ -845,7 +845,7 @@ const DASHBOARD_DOCUMENT = String.raw`<!DOCTYPE html>
|
|
|
845
845
|
const recentRows = recent.data || [];
|
|
846
846
|
const recentTable = recentRows.length === 0
|
|
847
847
|
? '<div class="empty">No trace entries recorded.</div>'
|
|
848
|
-
: '<div class="table-wrap"><table><thead><tr><th>Type</th><th>Summary</th><th>Tags</th><th>Duration</th><th>Happened</th></tr></thead><tbody>' + recentRows.map((entry) => '<tr class="row-button" data-action="show-detail" data-uuid="' + escapeHtml(entry.uuid) + '"><td><span class="' + typeClass(entry) + '">' + escapeHtml(entryTypeLabel(entry)) + '</span></td><td>' + entrySummaryHtml(entry) + '</td><td>' + tagsHtml(entry.tags) + '</td><td>' + durationHtml(entry) + '</td><td class="activity-time">' + escapeHtml(timeSince(entry.createdAt)) + '</td></tr>').join('') + '</tbody></table></div>';
|
|
848
|
+
: '<div class="table-wrap"><table><thead><tr><th>Type</th><th>Summary</th><th>Tags</th><th>Duration</th><th>Happened</th></tr></thead><tbody>' + recentRows.map((entry) => '<tr class="row-button" data-action="show-detail" data-uuid="' + escapeHtml(entry.uuid) + '"><td data-label="Type"><span class="' + typeClass(entry) + '">' + escapeHtml(entryTypeLabel(entry)) + '</span></td><td data-label="Summary">' + entrySummaryHtml(entry) + '</td><td data-label="Tags">' + tagsHtml(entry.tags) + '</td><td data-label="Duration">' + durationHtml(entry) + '</td><td data-label="Happened" class="activity-time">' + escapeHtml(timeSince(entry.createdAt)) + '</td></tr>').join('') + '</tbody></table></div>';
|
|
849
849
|
const activityList = recentRows.length === 0
|
|
850
850
|
? '<div class="empty">No recent activity.</div>'
|
|
851
851
|
: '<ul class="activity-list">' + recentRows.slice(0, 5).map((entry) => '<li class="activity-item"><div class="activity-head"><span class="' + typeClass(entry) + '">' + escapeHtml(entryTypeLabel(entry)) + '</span>' + durationHtml(entry) + '<span class="activity-time">' + escapeHtml(timeSince(entry.createdAt)) + '</span></div><div class="activity-summary">' + escapeHtml(entrySummaryText(entry)) + '</div></li>').join('') + '</ul>';
|
|
@@ -892,7 +892,7 @@ const DASHBOARD_DOCUMENT = String.raw`<!DOCTYPE html>
|
|
|
892
892
|
const total = Number(response.total || 0);
|
|
893
893
|
const perPage = Number(response.perPage || 50);
|
|
894
894
|
const totalPages = Math.max(1, Math.ceil(total / perPage));
|
|
895
|
-
const rows = data.map((entry) => '<tr class="row-button" data-action="show-detail" data-uuid="' + escapeHtml(entry.uuid) + '"><td><span class="' + typeClass(entry) + '">' + escapeHtml(entryTypeLabel(entry)) + '</span></td><td>' + entrySummaryHtml(entry) + '</td><td>' + tagsHtml(entry.tags) + '</td><td>' + durationHtml(entry) + '</td><td class="mono">' + batchSnippet(entry.batchId) + '</td><td class="activity-time">' + escapeHtml(timeSince(entry.createdAt)) + '</td></tr>').join('');
|
|
895
|
+
const rows = data.map((entry) => '<tr class="row-button" data-action="show-detail" data-uuid="' + escapeHtml(entry.uuid) + '"><td data-label="Type"><span class="' + typeClass(entry) + '">' + escapeHtml(entryTypeLabel(entry)) + '</span></td><td data-label="Summary">' + entrySummaryHtml(entry) + '</td><td data-label="Tags">' + tagsHtml(entry.tags) + '</td><td data-label="Duration">' + durationHtml(entry) + '</td><td data-label="Batch" class="mono">' + batchSnippet(entry.batchId) + '</td><td data-label="Happened" class="activity-time">' + escapeHtml(timeSince(entry.createdAt)) + '</td></tr>').join('');
|
|
896
896
|
|
|
897
897
|
main.innerHTML = [
|
|
898
898
|
'<section class="panel">',
|
|
@@ -28,87 +28,6 @@ const describeValueType = (value: unknown): string => {
|
|
|
28
28
|
return typeof value;
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
-
type TracePathSegment = string | number;
|
|
32
|
-
|
|
33
|
-
type TracePathCandidate = {
|
|
34
|
-
path: TracePathSegment[];
|
|
35
|
-
size: number;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const chooseLargerCandidate = (
|
|
39
|
-
left: TracePathCandidate | null,
|
|
40
|
-
right: TracePathCandidate | null
|
|
41
|
-
): TracePathCandidate | null => {
|
|
42
|
-
if (left === null) return right;
|
|
43
|
-
if (right === null) return left;
|
|
44
|
-
return right.size > left.size ? right : left;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const fallbackCandidate = (value: unknown, path: TracePathSegment[]): TracePathCandidate | null => {
|
|
48
|
-
return path.length === 0 ? null : { path, size: serializedSize(value) };
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
const findLargestDroppablePathInArray = (
|
|
52
|
-
value: unknown[],
|
|
53
|
-
path: TracePathSegment[]
|
|
54
|
-
): TracePathCandidate | null => {
|
|
55
|
-
let best: TracePathCandidate | null = null;
|
|
56
|
-
|
|
57
|
-
for (const [index, item] of value.entries()) {
|
|
58
|
-
best = chooseLargerCandidate(best, findLargestDroppablePath(item, [...path, index]));
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return best ?? fallbackCandidate(value, path);
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
const findLargestDroppablePathInObject = (
|
|
65
|
-
value: Record<string, unknown>,
|
|
66
|
-
path: TracePathSegment[]
|
|
67
|
-
): TracePathCandidate | null => {
|
|
68
|
-
let best: TracePathCandidate | null = null;
|
|
69
|
-
|
|
70
|
-
for (const [key, entryValue] of Object.entries(value)) {
|
|
71
|
-
if (key === '__traceNotice') continue;
|
|
72
|
-
best = chooseLargerCandidate(best, findLargestDroppablePath(entryValue, [...path, key]));
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return best ?? fallbackCandidate(value, path);
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const findLargestDroppablePath = (
|
|
79
|
-
value: unknown,
|
|
80
|
-
path: TracePathSegment[] = []
|
|
81
|
-
): TracePathCandidate | null => {
|
|
82
|
-
if (Array.isArray(value)) return findLargestDroppablePathInArray(value, path);
|
|
83
|
-
if (typeof value === 'object' && value !== null) {
|
|
84
|
-
return findLargestDroppablePathInObject(value as Record<string, unknown>, path);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return fallbackCandidate(value, path);
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const replaceAtPath = (value: unknown, path: TracePathSegment[], replacement: unknown): unknown => {
|
|
91
|
-
if (path.length === 0) return replacement;
|
|
92
|
-
|
|
93
|
-
const [segment, ...rest] = path;
|
|
94
|
-
|
|
95
|
-
if (Array.isArray(value) && typeof segment === 'number') {
|
|
96
|
-
const next = value.slice();
|
|
97
|
-
next[segment] = replaceAtPath(next[segment], rest, replacement);
|
|
98
|
-
return next;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (typeof value === 'object' && value !== null && typeof segment === 'string') {
|
|
102
|
-
const current = value as Record<string, unknown>;
|
|
103
|
-
return {
|
|
104
|
-
...current,
|
|
105
|
-
[segment]: replaceAtPath(current[segment], rest, replacement),
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return value;
|
|
110
|
-
};
|
|
111
|
-
|
|
112
31
|
const compactValue = (value: unknown, depth: number): unknown => {
|
|
113
32
|
if (depth >= DEFAULT_MAX_DEPTH) {
|
|
114
33
|
return DROPPED_FIELD_MESSAGE;
|
|
@@ -152,18 +71,34 @@ const compactValue = (value: unknown, depth: number): unknown => {
|
|
|
152
71
|
};
|
|
153
72
|
|
|
154
73
|
const compactStructuredValueToBudget = (value: unknown): unknown => {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
74
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
75
|
+
return value;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const compacted: Record<string, unknown> = {
|
|
79
|
+
...(value as Record<string, unknown>),
|
|
80
|
+
__traceNotice: COMPACTED_CONTENT_MESSAGE,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const topLevelCandidates = Object.entries(compacted)
|
|
84
|
+
.filter(([key]) => key !== '__traceNotice')
|
|
85
|
+
.map(([key, entryValue]) => ({ key, size: serializedSize(entryValue) }))
|
|
86
|
+
.sort((left, right) => right.size - left.size);
|
|
87
|
+
|
|
88
|
+
let droppedCount = 0;
|
|
89
|
+
|
|
90
|
+
for (const candidate of topLevelCandidates) {
|
|
91
|
+
if (serializedSize(compacted) <= DEFAULT_MAX_ENTRY_BYTES) {
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
162
94
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
95
|
+
compacted[candidate.key] = DROPPED_FIELD_MESSAGE;
|
|
96
|
+
droppedCount += 1;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (droppedCount > 0) {
|
|
100
|
+
compacted['__traceNotice'] =
|
|
101
|
+
`${COMPACTED_CONTENT_MESSAGE} ${String(droppedCount)} top-level field(s) were dropped.`;
|
|
167
102
|
}
|
|
168
103
|
|
|
169
104
|
return compacted;
|
|
@@ -270,26 +205,28 @@ const closePort = (port: MessagePort): void => {
|
|
|
270
205
|
}
|
|
271
206
|
};
|
|
272
207
|
|
|
273
|
-
const scheduleTask = (task: () => Promise<void>): void => {
|
|
274
|
-
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
channel.port1.onmessage = (): void => {
|
|
278
|
-
channel.port1.onmessage = null;
|
|
279
|
-
closePort(channel.port1);
|
|
280
|
-
closePort(channel.port2);
|
|
281
|
-
void task().catch(() => undefined);
|
|
208
|
+
const scheduleTask = async (task: () => Promise<void>): Promise<void> => {
|
|
209
|
+
return await new Promise<void>((resolve, reject) => {
|
|
210
|
+
const runTask = (): void => {
|
|
211
|
+
void task().then(resolve).catch(reject);
|
|
282
212
|
};
|
|
283
213
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
214
|
+
if (typeof MessageChannel === 'function') {
|
|
215
|
+
const channel = new MessageChannel();
|
|
216
|
+
|
|
217
|
+
channel.port1.onmessage = (): void => {
|
|
218
|
+
channel.port1.onmessage = null;
|
|
219
|
+
closePort(channel.port1);
|
|
220
|
+
closePort(channel.port2);
|
|
221
|
+
runTask();
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
channel.port2.postMessage(undefined);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
287
227
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
void task().catch(() => undefined);
|
|
291
|
-
})
|
|
292
|
-
.catch(() => undefined);
|
|
228
|
+
Promise.resolve().then(runTask).catch(reject);
|
|
229
|
+
});
|
|
293
230
|
};
|
|
294
231
|
|
|
295
232
|
const getReplacementContent = (content: unknown): Record<string, unknown> => {
|
|
@@ -442,7 +379,7 @@ const startInternalDispatchWorker = (
|
|
|
442
379
|
if (startedWorkerKeys.has(key)) return;
|
|
443
380
|
startedWorkerKeys.add(key);
|
|
444
381
|
|
|
445
|
-
scheduleTask(async () => {
|
|
382
|
+
void scheduleTask(async () => {
|
|
446
383
|
const workersApi = runtime?.queueWorkerApi ?? (await getQueueWorkerApi());
|
|
447
384
|
if (workersApi === null) {
|
|
448
385
|
startedWorkerKeys.delete(key);
|
|
@@ -487,16 +424,18 @@ const startInternalDispatchWorker = (
|
|
|
487
424
|
void runWorker();
|
|
488
425
|
}, intervalMs)
|
|
489
426
|
);
|
|
427
|
+
}).catch(() => {
|
|
428
|
+
startedWorkerKeys.delete(key);
|
|
490
429
|
});
|
|
491
430
|
};
|
|
492
431
|
|
|
493
|
-
const dispatchWrite = (
|
|
432
|
+
const dispatchWrite = async (
|
|
494
433
|
storage: ITraceStorage,
|
|
495
434
|
config: ITraceConfig,
|
|
496
435
|
entry: ITraceEntry,
|
|
497
436
|
runtime?: TraceContentBudgetRuntime
|
|
498
|
-
): void => {
|
|
499
|
-
scheduleTask(async () => {
|
|
437
|
+
): Promise<void> => {
|
|
438
|
+
await scheduleTask(async () => {
|
|
500
439
|
if (hasQueueDispatch(config)) {
|
|
501
440
|
const enqueued = await enqueueTraceDispatch(config, { operation: 'write', entry }, runtime);
|
|
502
441
|
if (enqueued) return;
|
|
@@ -506,14 +445,14 @@ const dispatchWrite = (
|
|
|
506
445
|
});
|
|
507
446
|
};
|
|
508
447
|
|
|
509
|
-
const dispatchUpdate = (
|
|
448
|
+
const dispatchUpdate = async (
|
|
510
449
|
storage: ITraceStorage,
|
|
511
450
|
config: ITraceConfig,
|
|
512
451
|
uuid: string,
|
|
513
452
|
patch: Partial<Pick<ITraceEntry, 'content' | 'isLatest'>>,
|
|
514
453
|
runtime?: TraceContentBudgetRuntime
|
|
515
|
-
): void => {
|
|
516
|
-
scheduleTask(async () => {
|
|
454
|
+
): Promise<void> => {
|
|
455
|
+
await scheduleTask(async () => {
|
|
517
456
|
if (hasQueueDispatch(config)) {
|
|
518
457
|
const enqueued = await enqueueTraceDispatch(
|
|
519
458
|
config,
|
|
@@ -538,13 +477,13 @@ export const TraceContentBudget = Object.freeze({
|
|
|
538
477
|
return Object.freeze({
|
|
539
478
|
...storage,
|
|
540
479
|
writeEntry: async (entry: ITraceEntry): Promise<void> => {
|
|
541
|
-
dispatchWrite(storage, config, entry, runtime);
|
|
480
|
+
await dispatchWrite(storage, config, entry, runtime);
|
|
542
481
|
},
|
|
543
482
|
updateEntry: async (
|
|
544
483
|
uuid: string,
|
|
545
484
|
patch: Partial<Pick<ITraceEntry, 'content' | 'isLatest'>>
|
|
546
485
|
): Promise<void> => {
|
|
547
|
-
dispatchUpdate(storage, config, uuid, patch, runtime);
|
|
486
|
+
await dispatchUpdate(storage, config, uuid, patch, runtime);
|
|
548
487
|
},
|
|
549
488
|
});
|
|
550
489
|
},
|
package/src/types.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { TraceContext } from '../context';
|
|
2
2
|
|
|
3
|
+
type RequestIgnoreRules = {
|
|
4
|
+
ignoreRoutes?: string[];
|
|
5
|
+
ignorePath?: string[];
|
|
6
|
+
};
|
|
7
|
+
|
|
3
8
|
const normalizePath = (input: string): string => {
|
|
4
9
|
const trimmed = input.trim();
|
|
5
10
|
const [pathOnly] = trimmed.split('?');
|
|
@@ -7,24 +12,65 @@ const normalizePath = (input: string): string => {
|
|
|
7
12
|
return pathOnly.startsWith('/') ? pathOnly : `/${pathOnly}`;
|
|
8
13
|
};
|
|
9
14
|
|
|
10
|
-
const
|
|
15
|
+
const normalizeContainsPattern = (input: string): string => {
|
|
16
|
+
const trimmed = input.trim();
|
|
17
|
+
const [pathOnly] = trimmed.split('?');
|
|
18
|
+
return pathOnly ?? '';
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const resolveRules = (
|
|
22
|
+
ignoreRoutesOrRules: string[] | RequestIgnoreRules,
|
|
23
|
+
ignorePath?: string[]
|
|
24
|
+
): Required<RequestIgnoreRules> => {
|
|
25
|
+
if (Array.isArray(ignoreRoutesOrRules)) {
|
|
26
|
+
return {
|
|
27
|
+
ignoreRoutes: ignoreRoutesOrRules,
|
|
28
|
+
ignorePath: ignorePath ?? [],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
ignoreRoutes: ignoreRoutesOrRules.ignoreRoutes ?? [],
|
|
34
|
+
ignorePath: ignoreRoutesOrRules.ignorePath ?? [],
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const matchesIgnoredPath = (
|
|
39
|
+
path: string,
|
|
40
|
+
ignoreRoutesOrRules: string[] | RequestIgnoreRules,
|
|
41
|
+
ignorePath?: string[]
|
|
42
|
+
): boolean => {
|
|
11
43
|
const normalizedPath = normalizePath(path);
|
|
44
|
+
const rules = resolveRules(ignoreRoutesOrRules, ignorePath);
|
|
45
|
+
|
|
46
|
+
if (
|
|
47
|
+
rules.ignoreRoutes.some((route) => {
|
|
48
|
+
const normalizedRoute = normalizePath(route);
|
|
49
|
+
return (
|
|
50
|
+
normalizedPath === normalizedRoute ||
|
|
51
|
+
normalizedPath.startsWith(
|
|
52
|
+
normalizedRoute.endsWith('/') ? normalizedRoute : `${normalizedRoute}/`
|
|
53
|
+
)
|
|
54
|
+
);
|
|
55
|
+
})
|
|
56
|
+
) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
12
59
|
|
|
13
|
-
return
|
|
14
|
-
const
|
|
15
|
-
return
|
|
16
|
-
|
|
17
|
-
normalizedPath.startsWith(
|
|
18
|
-
normalizedRoute.endsWith('/') ? normalizedRoute : `${normalizedRoute}/`
|
|
19
|
-
)
|
|
20
|
-
);
|
|
60
|
+
return rules.ignorePath.some((route) => {
|
|
61
|
+
const containsPattern = normalizeContainsPattern(route);
|
|
62
|
+
if (containsPattern === '') return false;
|
|
63
|
+
return normalizedPath.includes(containsPattern);
|
|
21
64
|
});
|
|
22
65
|
};
|
|
23
66
|
|
|
24
|
-
const shouldIgnoreCurrentRequest = (
|
|
67
|
+
const shouldIgnoreCurrentRequest = (
|
|
68
|
+
ignoreRoutesOrRules: string[] | RequestIgnoreRules,
|
|
69
|
+
ignorePath?: string[]
|
|
70
|
+
): boolean => {
|
|
25
71
|
const currentPath = TraceContext.getRequestPath();
|
|
26
72
|
if (typeof currentPath !== 'string' || currentPath === '') return false;
|
|
27
|
-
return matchesIgnoredPath(currentPath,
|
|
73
|
+
return matchesIgnoredPath(currentPath, ignoreRoutesOrRules, ignorePath);
|
|
28
74
|
};
|
|
29
75
|
|
|
30
76
|
export const RequestFilter = Object.freeze({
|