@fedify/debugger 2.0.0-dev.0
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/LICENSE +20 -0
- package/dist/mod.cjs +969 -0
- package/dist/mod.d.cts +188 -0
- package/dist/mod.d.ts +189 -0
- package/dist/mod.js +945 -0
- package/package.json +54 -0
package/dist/mod.cjs
ADDED
|
@@ -0,0 +1,969 @@
|
|
|
1
|
+
|
|
2
|
+
const { Temporal } = require("@js-temporal/polyfill");
|
|
3
|
+
|
|
4
|
+
//#region rolldown:runtime
|
|
5
|
+
var __create = Object.create;
|
|
6
|
+
var __defProp = Object.defineProperty;
|
|
7
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
8
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
9
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
10
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
13
|
+
key = keys[i];
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
15
|
+
get: ((k) => from[k]).bind(null, key),
|
|
16
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
22
|
+
value: mod,
|
|
23
|
+
enumerable: true
|
|
24
|
+
}) : target, mod));
|
|
25
|
+
|
|
26
|
+
//#endregion
|
|
27
|
+
const __fedify_fedify_federation = __toESM(require("@fedify/fedify/federation"));
|
|
28
|
+
const __fedify_fedify_otel = __toESM(require("@fedify/fedify/otel"));
|
|
29
|
+
const __logtape_logtape = __toESM(require("@logtape/logtape"));
|
|
30
|
+
const __opentelemetry_api = __toESM(require("@opentelemetry/api"));
|
|
31
|
+
const __opentelemetry_context_async_hooks = __toESM(require("@opentelemetry/context-async-hooks"));
|
|
32
|
+
const __opentelemetry_core = __toESM(require("@opentelemetry/core"));
|
|
33
|
+
const __opentelemetry_sdk_trace_base = __toESM(require("@opentelemetry/sdk-trace-base"));
|
|
34
|
+
const node_async_hooks = __toESM(require("node:async_hooks"));
|
|
35
|
+
const hono = __toESM(require("hono"));
|
|
36
|
+
const hono_cookie = __toESM(require("hono/cookie"));
|
|
37
|
+
const node_crypto = __toESM(require("node:crypto"));
|
|
38
|
+
const hono_jsx_jsx_runtime = __toESM(require("hono/jsx/jsx-runtime"));
|
|
39
|
+
|
|
40
|
+
//#region src/log-store.ts
|
|
41
|
+
/**
|
|
42
|
+
* Persistent storage for log records grouped by trace ID, backed by a
|
|
43
|
+
* {@link KvStore}. When the same `KvStore` is shared across web and worker
|
|
44
|
+
* processes the dashboard can display logs produced by background tasks.
|
|
45
|
+
*/
|
|
46
|
+
var LogStore = class {
|
|
47
|
+
#kv;
|
|
48
|
+
#keyPrefix;
|
|
49
|
+
/** Chain of pending write promises for flush(). */
|
|
50
|
+
#pending = Promise.resolve();
|
|
51
|
+
constructor(kv, keyPrefix = [
|
|
52
|
+
"fedify",
|
|
53
|
+
"debugger",
|
|
54
|
+
"logs"
|
|
55
|
+
]) {
|
|
56
|
+
this.#kv = kv;
|
|
57
|
+
this.#keyPrefix = keyPrefix;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Enqueue a log record for writing. The write happens asynchronously;
|
|
61
|
+
* call {@link flush} to wait for all pending writes to complete.
|
|
62
|
+
*
|
|
63
|
+
* Keys use a timestamp + random suffix so that entries sort
|
|
64
|
+
* chronologically and never collide, even across multiple processes
|
|
65
|
+
* sharing the same {@link KvStore}.
|
|
66
|
+
*/
|
|
67
|
+
add(traceId, record) {
|
|
68
|
+
const key = [
|
|
69
|
+
...this.#keyPrefix,
|
|
70
|
+
traceId,
|
|
71
|
+
`${Date.now().toString(36).padStart(10, "0")}-${Math.random().toString(36).slice(2)}`
|
|
72
|
+
];
|
|
73
|
+
this.#pending = this.#pending.then(() => this.#kv.set(key, record)).catch(() => {});
|
|
74
|
+
}
|
|
75
|
+
/** Wait for all pending writes to complete. */
|
|
76
|
+
flush() {
|
|
77
|
+
return this.#pending;
|
|
78
|
+
}
|
|
79
|
+
async get(traceId) {
|
|
80
|
+
const prefix = [...this.#keyPrefix, traceId];
|
|
81
|
+
const logs = [];
|
|
82
|
+
for await (const entry of this.#kv.list(prefix)) logs.push(entry.value);
|
|
83
|
+
return logs;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Converts a {@link LogRecord} into a plain serializable object suitable
|
|
88
|
+
* for storage in a {@link KvStore}.
|
|
89
|
+
*/
|
|
90
|
+
function serializeLogRecord(record) {
|
|
91
|
+
const messageParts = [];
|
|
92
|
+
for (const part of record.message) if (typeof part === "string") messageParts.push(part);
|
|
93
|
+
else if (part == null) messageParts.push("");
|
|
94
|
+
else messageParts.push(String(part));
|
|
95
|
+
const { traceId: _t, spanId: _s,...properties } = record.properties;
|
|
96
|
+
return {
|
|
97
|
+
category: record.category,
|
|
98
|
+
level: record.level,
|
|
99
|
+
message: messageParts.join(""),
|
|
100
|
+
timestamp: record.timestamp,
|
|
101
|
+
properties
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Creates a LogTape {@link Sink} that writes log records into the given
|
|
106
|
+
* {@link LogStore}, grouped by their `traceId` property. Records without
|
|
107
|
+
* a `traceId` are silently discarded.
|
|
108
|
+
*/
|
|
109
|
+
function createLogSink(store) {
|
|
110
|
+
return (record) => {
|
|
111
|
+
const traceId = record.properties.traceId;
|
|
112
|
+
if (typeof traceId !== "string" || traceId.length === 0) return;
|
|
113
|
+
store.add(traceId, serializeLogRecord(record));
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/auth.ts
|
|
119
|
+
const SESSION_COOKIE_NAME = "__fedify_debug_session";
|
|
120
|
+
const SESSION_TOKEN = "authenticated";
|
|
121
|
+
async function generateHmacKey() {
|
|
122
|
+
return await crypto.subtle.generateKey({
|
|
123
|
+
name: "HMAC",
|
|
124
|
+
hash: "SHA-256"
|
|
125
|
+
}, false, ["sign", "verify"]);
|
|
126
|
+
}
|
|
127
|
+
function toHex(buffer) {
|
|
128
|
+
return [...new Uint8Array(buffer)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
129
|
+
}
|
|
130
|
+
function fromHex(hex) {
|
|
131
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
132
|
+
for (let i = 0; i < hex.length; i += 2) bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
|
|
133
|
+
return bytes.buffer;
|
|
134
|
+
}
|
|
135
|
+
async function signSession(key) {
|
|
136
|
+
const encoder = new TextEncoder();
|
|
137
|
+
const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(SESSION_TOKEN));
|
|
138
|
+
return toHex(signature);
|
|
139
|
+
}
|
|
140
|
+
async function verifySession(key, signature) {
|
|
141
|
+
try {
|
|
142
|
+
const encoder = new TextEncoder();
|
|
143
|
+
return await crypto.subtle.verify("HMAC", key, fromHex(signature), encoder.encode(SESSION_TOKEN));
|
|
144
|
+
} catch {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Constant-time string comparison to prevent timing attacks on credential
|
|
150
|
+
* checks. Uses {@link timingSafeEqual} from `node:crypto` under the hood.
|
|
151
|
+
*/
|
|
152
|
+
function constantTimeEqual(a, b) {
|
|
153
|
+
const encoder = new TextEncoder();
|
|
154
|
+
const bufA = encoder.encode(a);
|
|
155
|
+
const bufB = encoder.encode(b);
|
|
156
|
+
if (bufA.byteLength !== bufB.byteLength) {
|
|
157
|
+
(0, node_crypto.timingSafeEqual)(bufA, new Uint8Array(bufA.byteLength));
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
return (0, node_crypto.timingSafeEqual)(bufA, bufB);
|
|
161
|
+
}
|
|
162
|
+
async function checkAuth(auth, formData) {
|
|
163
|
+
if (auth.type === "password") {
|
|
164
|
+
if ("authenticate" in auth) return await auth.authenticate(formData.password);
|
|
165
|
+
return constantTimeEqual(formData.password, auth.password);
|
|
166
|
+
}
|
|
167
|
+
if (auth.type === "usernamePassword") {
|
|
168
|
+
if ("authenticate" in auth) return await auth.authenticate(formData.username ?? "", formData.password);
|
|
169
|
+
const usernameMatch = constantTimeEqual(formData.username ?? "", auth.username);
|
|
170
|
+
const passwordMatch = constantTimeEqual(formData.password, auth.password);
|
|
171
|
+
return usernameMatch && passwordMatch;
|
|
172
|
+
}
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
//#endregion
|
|
177
|
+
//#region src/views/logo.tsx
|
|
178
|
+
/**
|
|
179
|
+
* Inline SVG of the Fedify logo (mascot bird with fediverse connection nodes).
|
|
180
|
+
*/
|
|
181
|
+
const FedifyLogo = ({ size = 24 }) => {
|
|
182
|
+
return /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("svg", {
|
|
183
|
+
width: size,
|
|
184
|
+
height: size,
|
|
185
|
+
viewBox: "0 0 112 112",
|
|
186
|
+
role: "img",
|
|
187
|
+
"aria-label": "Fedify logo",
|
|
188
|
+
children: [
|
|
189
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("defs", { children: /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("clipPath", {
|
|
190
|
+
clipPathUnits: "userSpaceOnUse",
|
|
191
|
+
id: "fedify-logo-clip",
|
|
192
|
+
children: /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("ellipse", {
|
|
193
|
+
style: "fill: #000; stroke: #000; stroke-width: 3.02635; stroke-linejoin: miter; stroke-dasharray: none; stroke-dashoffset: 0; stroke-opacity: 1; paint-order: normal",
|
|
194
|
+
cx: "55.92646",
|
|
195
|
+
cy: "56.073448",
|
|
196
|
+
transform: "rotate(-0.07519647)",
|
|
197
|
+
rx: "54.486828",
|
|
198
|
+
ry: "54.486824"
|
|
199
|
+
})
|
|
200
|
+
}) }),
|
|
201
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("ellipse", {
|
|
202
|
+
style: "fill: #ffffff; stroke: none; stroke-width: 3.02635; stroke-linejoin: miter; stroke-dasharray: none; stroke-dashoffset: 0; stroke-opacity: 1; paint-order: normal",
|
|
203
|
+
cx: "55.92646",
|
|
204
|
+
cy: "56.073448",
|
|
205
|
+
transform: "rotate(-0.07519647)",
|
|
206
|
+
rx: "54.486828",
|
|
207
|
+
ry: "54.486824"
|
|
208
|
+
}),
|
|
209
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("g", {
|
|
210
|
+
"clip-path": "url(#fedify-logo-clip)",
|
|
211
|
+
children: [/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("g", { children: [
|
|
212
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("path", {
|
|
213
|
+
d: "M 77.4624,78.9593 C 78.2802,68.3428 73.7143,58.8833 71.3291,55.4806 L 87.6847,48.335 c 4.9066,1.6333 6.474,17.3537 6.6444,25.0098 0,0 -3.5778,0.5104 -5.6222,2.0416 -2.085,1.5616 -5.6222,5.1041 -11.2445,3.5729 z",
|
|
214
|
+
fill: "#ffffff",
|
|
215
|
+
stroke: "#84b5d9",
|
|
216
|
+
"stroke-width": "3",
|
|
217
|
+
"stroke-linecap": "round"
|
|
218
|
+
}),
|
|
219
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("path", {
|
|
220
|
+
d: "M 7.06239,52.159 C -5.55748,54.1782 -12.682,66.0659 -17.661,73.2769 c -0.8584,13.3918 -0.6181,41.1021 7.211,44.8111 7.82906,3.709 26.9553,1.545 35.5398,0 v 4.121 c 1.3736,0.515 5.0477,1.648 8.7562,2.06 3.7085,0.412 6.696,-1.202 7.7261,-2.06 v -9.787 c 0.5151,-0.343 2.9874,-1.957 8.7562,-5.666 7.211,-4.635 11.3315,-16.482 9.7863,-24.7229 -1.1589,-6.181 3.6055,-18.5427 6.1809,-26.7838 9.7863,2.0601 22.148,-1.0301 23.1781,-14.9369 C 90.1205,31.5801 80.7174,19.9868 63.2051,25.3752 45.6927,30.7636 48.268,52.159 41.5721,59.37 35.3913,53.1891 23.5446,49.5219 7.06239,52.159 Z",
|
|
221
|
+
fill: "#bae6fd",
|
|
222
|
+
stroke: "#0c4a6e",
|
|
223
|
+
"stroke-width": "3",
|
|
224
|
+
"stroke-linecap": "round"
|
|
225
|
+
}),
|
|
226
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("path", {
|
|
227
|
+
d: "M 66.2955,55.2493 C 64.5786,54.7342 60.9387,53.6011 60.1146,53.189",
|
|
228
|
+
stroke: "#0284c7",
|
|
229
|
+
"stroke-opacity": "0.37",
|
|
230
|
+
"stroke-width": "3",
|
|
231
|
+
"stroke-linecap": "round",
|
|
232
|
+
style: "opacity: 1; fill: none; stroke-width: 3; stroke-linejoin: miter; stroke-dasharray: none; paint-order: normal"
|
|
233
|
+
}),
|
|
234
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("path", {
|
|
235
|
+
d: "m 41.5721,59.3698 c -0.6868,0.8585 -2.6784,2.7814 -5.1507,3.6055",
|
|
236
|
+
stroke: "#0284c7",
|
|
237
|
+
"stroke-opacity": "0.37",
|
|
238
|
+
"stroke-width": "3",
|
|
239
|
+
"stroke-linecap": "round",
|
|
240
|
+
style: "fill: none"
|
|
241
|
+
}),
|
|
242
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("circle", {
|
|
243
|
+
cx: "68.870796",
|
|
244
|
+
cy: "42.8876",
|
|
245
|
+
r: "2.0602801",
|
|
246
|
+
fill: "#000000"
|
|
247
|
+
})
|
|
248
|
+
] }), /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("g", {
|
|
249
|
+
transform: "matrix(0.08160718,0,0,0.08160718,76.994732,53.205469)",
|
|
250
|
+
style: "display: inline",
|
|
251
|
+
children: [
|
|
252
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("path", {
|
|
253
|
+
style: "fill: #a730b8; fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 41.5748",
|
|
254
|
+
d: "m 181.13086,275.13672 a 68.892408,68.892408 0 0 1 -29.46484,29.32812 l 161.75781,162.38868 38.99805,-19.76368 z m 213.36328,214.1875 -38.99805,19.76367 81.96289,82.2832 a 68.892409,68.892409 0 0 1 29.47071,-29.33203 z",
|
|
255
|
+
transform: "matrix(0.26458333,0,0,0.26458333,-6.6789703,32.495842)"
|
|
256
|
+
}),
|
|
257
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("path", {
|
|
258
|
+
style: "fill: #5496be; fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 41.5748",
|
|
259
|
+
d: "m 581.64648,339.39062 -91.57617,46.41016 6.75196,43.18945 103.61523,-52.51367 A 68.892409,68.892409 0 0 1 581.64648,339.39062 Z M 436.9082,412.74219 220.38281,522.47656 a 68.892408,68.892408 0 0 1 18.79492,37.08985 L 443.66016,455.93359 Z",
|
|
260
|
+
transform: "matrix(0.26458333,0,0,0.26458333,-6.6789703,32.495842)"
|
|
261
|
+
}),
|
|
262
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("path", {
|
|
263
|
+
style: "fill: #ce3d1a; fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 41.5748",
|
|
264
|
+
d: "M 367.27539,142.4375 262.79492,346.4082 293.64258,377.375 404.26562,161.41797 A 68.892408,68.892408 0 0 1 367.27539,142.4375 Z m -131.6543,257.02148 -52.92187,103.31446 a 68.892409,68.892409 0 0 1 36.98633,18.97851 l 46.78125,-91.32812 z",
|
|
265
|
+
transform: "matrix(0.26458333,0,0,0.26458333,-6.6789703,32.495842)"
|
|
266
|
+
}),
|
|
267
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("path", {
|
|
268
|
+
style: "fill: #d0188f; fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 41.5748",
|
|
269
|
+
d: "m 150.76758,304.91797 a 68.892408,68.892408 0 0 1 -34.41602,7.19531 68.892408,68.892408 0 0 1 -6.65039,-0.69531 l 30.90235,197.66211 a 68.892409,68.892409 0 0 1 34.41601,-7.19531 68.892409,68.892409 0 0 1 6.64649,0.69531 z",
|
|
270
|
+
transform: "matrix(0.26458333,0,0,0.26458333,-6.6789703,32.495842)"
|
|
271
|
+
}),
|
|
272
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("path", {
|
|
273
|
+
style: "fill: #5b36e9; fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 41.5748",
|
|
274
|
+
d: "m 239.3418,560.54492 a 68.892408,68.892408 0 0 1 0.7207,13.87696 68.892408,68.892408 0 0 1 -7.26758,27.17968 l 197.62891,31.71289 a 68.892409,68.892409 0 0 1 -0.72266,-13.8789 68.892409,68.892409 0 0 1 7.26953,-27.17774 z",
|
|
275
|
+
transform: "matrix(0.26458333,0,0,0.26458333,-6.6789703,32.495842)"
|
|
276
|
+
}),
|
|
277
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("path", {
|
|
278
|
+
style: "fill: #30b873; fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 41.5748",
|
|
279
|
+
d: "m 601.13281,377.19922 -91.21875,178.08203 a 68.892408,68.892408 0 0 1 36.99414,18.98242 L 638.125,396.18359 a 68.892409,68.892409 0 0 1 -36.99219,-18.98437 z",
|
|
280
|
+
transform: "matrix(0.26458333,0,0,0.26458333,-6.6789703,32.495842)"
|
|
281
|
+
}),
|
|
282
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("path", {
|
|
283
|
+
style: "fill: #ebe305; fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 41.5748",
|
|
284
|
+
d: "m 476.72266,125.33008 a 68.892408,68.892408 0 0 1 -29.47071,29.33203 l 141.26563,141.81055 a 68.892409,68.892409 0 0 1 29.46875,-29.33204 z",
|
|
285
|
+
transform: "matrix(0.26458333,0,0,0.26458333,-6.6789703,32.495842)"
|
|
286
|
+
}),
|
|
287
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("path", {
|
|
288
|
+
style: "fill: #f47601; fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 41.5748",
|
|
289
|
+
d: "m 347.78711,104.63086 -178.57617,90.49805 a 68.892409,68.892409 0 0 1 18.79297,37.08593 l 178.57421,-90.50195 a 68.892408,68.892408 0 0 1 -18.79101,-37.08203 z",
|
|
290
|
+
transform: "matrix(0.26458333,0,0,0.26458333,-6.6789703,32.495842)"
|
|
291
|
+
}),
|
|
292
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("path", {
|
|
293
|
+
style: "fill: #57c115; fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 41.5748",
|
|
294
|
+
d: "m 446.92578,154.82617 a 68.892408,68.892408 0 0 1 -34.98242,7.48242 68.892408,68.892408 0 0 1 -6.0293,-0.63281 l 15.81836,101.29102 43.16211,6.92578 z m -16,167.02735 37.40039,239.48242 a 68.892409,68.892409 0 0 1 33.91406,-6.94336 68.892409,68.892409 0 0 1 7.20704,0.79101 L 474.08984,328.77734 Z",
|
|
295
|
+
transform: "matrix(0.26458333,0,0,0.26458333,-6.6789703,32.495842)"
|
|
296
|
+
}),
|
|
297
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("path", {
|
|
298
|
+
style: "fill: #dbb210; fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 41.5748",
|
|
299
|
+
d: "m 188.13086,232.97461 a 68.892408,68.892408 0 0 1 0.75781,14.0957 68.892408,68.892408 0 0 1 -7.16015,26.98242 l 101.36914,16.28125 19.92382,-38.9082 z m 173.73633,27.90039 -19.92578,38.91211 239.51367,38.4668 a 68.892409,68.892409 0 0 1 -0.69531,-13.71875 68.892409,68.892409 0 0 1 7.34961,-27.32422 z",
|
|
300
|
+
transform: "matrix(0.26458333,0,0,0.26458333,-6.6789703,32.495842)"
|
|
301
|
+
}),
|
|
302
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("circle", {
|
|
303
|
+
style: "fill: #ffca00; fill-opacity: 0.995968; stroke: none; stroke-width: 0.264583; stroke-opacity: 0.960784",
|
|
304
|
+
cx: "106.26596",
|
|
305
|
+
cy: "51.535553",
|
|
306
|
+
r: "16.570711",
|
|
307
|
+
transform: "rotate(3.1178174)"
|
|
308
|
+
}),
|
|
309
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("circle", {
|
|
310
|
+
style: "fill: #64ff00; fill-opacity: 0.995968; stroke: none; stroke-width: 0.264583; stroke-opacity: 0.960784",
|
|
311
|
+
cx: "171.42836",
|
|
312
|
+
cy: "110.19328",
|
|
313
|
+
r: "16.570711",
|
|
314
|
+
transform: "rotate(3.1178174)"
|
|
315
|
+
}),
|
|
316
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("circle", {
|
|
317
|
+
style: "fill: #00a3ff; fill-opacity: 0.995968; stroke: none; stroke-width: 0.264583; stroke-opacity: 0.960784",
|
|
318
|
+
cx: "135.76379",
|
|
319
|
+
cy: "190.27704",
|
|
320
|
+
r: "16.570711",
|
|
321
|
+
transform: "rotate(3.1178174)"
|
|
322
|
+
}),
|
|
323
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("circle", {
|
|
324
|
+
style: "fill: #9500ff; fill-opacity: 0.995968; stroke: none; stroke-width: 0.264583; stroke-opacity: 0.960784",
|
|
325
|
+
cx: "48.559471",
|
|
326
|
+
cy: "181.1138",
|
|
327
|
+
r: "16.570711",
|
|
328
|
+
transform: "rotate(3.1178174)"
|
|
329
|
+
}),
|
|
330
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("circle", {
|
|
331
|
+
style: "fill: #ff0000; fill-opacity: 0.995968; stroke: none; stroke-width: 0.264583; stroke-opacity: 0.960784",
|
|
332
|
+
cx: "30.328812",
|
|
333
|
+
cy: "95.366837",
|
|
334
|
+
r: "16.570711",
|
|
335
|
+
transform: "rotate(3.1178174)"
|
|
336
|
+
})
|
|
337
|
+
]
|
|
338
|
+
})]
|
|
339
|
+
}),
|
|
340
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("circle", {
|
|
341
|
+
style: "opacity: 1; fill: none; stroke: #84b5d9; stroke-width: 4.91342; stroke-linejoin: miter; stroke-dasharray: none; stroke-dashoffset: 0; stroke-opacity: 1; paint-order: normal",
|
|
342
|
+
cx: "55.926456",
|
|
343
|
+
cy: "56.073448",
|
|
344
|
+
transform: "rotate(-0.07519625)",
|
|
345
|
+
r: "53.543289"
|
|
346
|
+
})
|
|
347
|
+
]
|
|
348
|
+
});
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
//#endregion
|
|
352
|
+
//#region src/views/layout.tsx
|
|
353
|
+
/**
|
|
354
|
+
* Root HTML layout for the debug dashboard.
|
|
355
|
+
*/
|
|
356
|
+
const Layout = ({ title, pathPrefix, children }) => {
|
|
357
|
+
return /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("html", { children: [/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("head", { children: [
|
|
358
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("meta", { charset: "utf-8" }),
|
|
359
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("meta", {
|
|
360
|
+
name: "viewport",
|
|
361
|
+
content: "width=device-width, initial-scale=1"
|
|
362
|
+
}),
|
|
363
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("title", { children: [title != null ? `${title} — ` : "", "Fedify Debug Dashboard"] }),
|
|
364
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("style", { children: `
|
|
365
|
+
body {
|
|
366
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
367
|
+
max-width: 960px;
|
|
368
|
+
margin: 0 auto;
|
|
369
|
+
padding: 1rem;
|
|
370
|
+
color: #333;
|
|
371
|
+
}
|
|
372
|
+
header { border-bottom: 1px solid #ddd; padding-bottom: 0.5rem; margin-bottom: 1rem; }
|
|
373
|
+
header h1 { margin: 0; font-size: 1.25rem; }
|
|
374
|
+
header h1 a { color: inherit; text-decoration: none; display: inline-flex; align-items: center; gap: 0.5rem; }
|
|
375
|
+
table { width: 100%; border-collapse: collapse; }
|
|
376
|
+
th, td { text-align: left; padding: 0.5rem; border-bottom: 1px solid #eee; }
|
|
377
|
+
th { font-weight: 600; font-size: 0.875rem; color: #666; }
|
|
378
|
+
a { color: #0969da; }
|
|
379
|
+
code { background: #f0f0f0; padding: 0.15em 0.3em; border-radius: 3px; font-size: 0.875em; }
|
|
380
|
+
.badge { display: inline-block; background: #e0e0e0; color: #333; padding: 0.15em 0.5em; border-radius: 3px; font-size: 0.75rem; }
|
|
381
|
+
.badge-inbound { background: #ddf4ff; color: #0969da; }
|
|
382
|
+
.badge-outbound { background: #fff8c5; color: #9a6700; }
|
|
383
|
+
.detail-section { margin-bottom: 1.5rem; }
|
|
384
|
+
.detail-section h2 { font-size: 1rem; margin-bottom: 0.5rem; border-bottom: 1px solid #eee; padding-bottom: 0.25rem; }
|
|
385
|
+
pre { background: #f6f8fa; padding: 1rem; overflow-x: auto; border-radius: 6px; font-size: 0.8125rem; }
|
|
386
|
+
.empty { color: #888; font-style: italic; }
|
|
387
|
+
nav a { margin-right: 0.5rem; }
|
|
388
|
+
.log-table td { font-size: 0.8125rem; vertical-align: top; }
|
|
389
|
+
.log-table time { font-family: monospace; white-space: nowrap; }
|
|
390
|
+
.badge-debug { background: #e8e8e8; color: #666; }
|
|
391
|
+
.badge-info { background: #ddf4ff; color: #0969da; }
|
|
392
|
+
.badge-warning { background: #fff8c5; color: #9a6700; }
|
|
393
|
+
.badge-error { background: #ffebe9; color: #cf222e; }
|
|
394
|
+
.badge-fatal { background: #cf222e; color: #fff; }
|
|
395
|
+
.log-error td { background: #fff5f5; }
|
|
396
|
+
.log-fatal td { background: #ffebe9; }
|
|
397
|
+
|
|
398
|
+
@media (prefers-color-scheme: dark) {
|
|
399
|
+
body { background: #0d1117; color: #e6edf3; }
|
|
400
|
+
header { border-bottom-color: #30363d; }
|
|
401
|
+
th { color: #9198a1; }
|
|
402
|
+
th, td { border-bottom-color: #21262d; }
|
|
403
|
+
a { color: #58a6ff; }
|
|
404
|
+
code { background: #161b22; }
|
|
405
|
+
.badge { background: #30363d; color: #e6edf3; }
|
|
406
|
+
.badge-inbound { background: #122d42; color: #58a6ff; }
|
|
407
|
+
.badge-outbound { background: #2e2a1f; color: #d29922; }
|
|
408
|
+
.detail-section h2 { border-bottom-color: #21262d; }
|
|
409
|
+
pre { background: #161b22; }
|
|
410
|
+
.empty { color: #9198a1; }
|
|
411
|
+
.badge-debug { background: #21262d; color: #9198a1; }
|
|
412
|
+
.badge-info { background: #122d42; color: #58a6ff; }
|
|
413
|
+
.badge-warning { background: #2e2a1f; color: #d29922; }
|
|
414
|
+
.badge-error { background: #3d1f20; color: #f85149; }
|
|
415
|
+
.badge-fatal { background: #da3633; color: #fff; }
|
|
416
|
+
.log-error td { background: #2d1215; }
|
|
417
|
+
.log-fatal td { background: #3d1f20; }
|
|
418
|
+
}
|
|
419
|
+
` })
|
|
420
|
+
] }), /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("body", { children: [/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("header", { children: /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("h1", { children: /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("a", {
|
|
421
|
+
href: pathPrefix + "/",
|
|
422
|
+
children: [/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)(FedifyLogo, { size: 24 }), "Fedify Debug Dashboard"]
|
|
423
|
+
}) }) }), /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("main", { children })] })] });
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
//#endregion
|
|
427
|
+
//#region src/views/login.tsx
|
|
428
|
+
/**
|
|
429
|
+
* Login page for the debug dashboard.
|
|
430
|
+
*/
|
|
431
|
+
const LoginPage = ({ pathPrefix, showUsername, error }) => {
|
|
432
|
+
return /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)(Layout, {
|
|
433
|
+
title: "Login",
|
|
434
|
+
pathPrefix,
|
|
435
|
+
children: [
|
|
436
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("h2", { children: "Login Required" }),
|
|
437
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("p", {
|
|
438
|
+
class: "login-description",
|
|
439
|
+
children: "The debug dashboard requires authentication to access."
|
|
440
|
+
}),
|
|
441
|
+
error && /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("p", {
|
|
442
|
+
class: "login-error",
|
|
443
|
+
children: error
|
|
444
|
+
}),
|
|
445
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("form", {
|
|
446
|
+
method: "post",
|
|
447
|
+
action: pathPrefix + "/login",
|
|
448
|
+
class: "login-form",
|
|
449
|
+
children: [
|
|
450
|
+
showUsername && /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("div", {
|
|
451
|
+
class: "login-field",
|
|
452
|
+
children: [/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("label", {
|
|
453
|
+
for: "username",
|
|
454
|
+
children: "Username"
|
|
455
|
+
}), /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("input", {
|
|
456
|
+
type: "text",
|
|
457
|
+
id: "username",
|
|
458
|
+
name: "username",
|
|
459
|
+
required: true,
|
|
460
|
+
autocomplete: "username"
|
|
461
|
+
})]
|
|
462
|
+
}),
|
|
463
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("div", {
|
|
464
|
+
class: "login-field",
|
|
465
|
+
children: [/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("label", {
|
|
466
|
+
for: "password",
|
|
467
|
+
children: "Password"
|
|
468
|
+
}), /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("input", {
|
|
469
|
+
type: "password",
|
|
470
|
+
id: "password",
|
|
471
|
+
name: "password",
|
|
472
|
+
required: true,
|
|
473
|
+
autocomplete: "current-password"
|
|
474
|
+
})]
|
|
475
|
+
}),
|
|
476
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("button", {
|
|
477
|
+
type: "submit",
|
|
478
|
+
children: "Log in"
|
|
479
|
+
})
|
|
480
|
+
]
|
|
481
|
+
}),
|
|
482
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("style", { children: `
|
|
483
|
+
.login-form {
|
|
484
|
+
max-width: 320px;
|
|
485
|
+
}
|
|
486
|
+
.login-field {
|
|
487
|
+
margin-bottom: 0.75rem;
|
|
488
|
+
}
|
|
489
|
+
.login-field label {
|
|
490
|
+
display: block;
|
|
491
|
+
margin-bottom: 0.25rem;
|
|
492
|
+
font-weight: 600;
|
|
493
|
+
font-size: 0.875rem;
|
|
494
|
+
}
|
|
495
|
+
.login-field input {
|
|
496
|
+
width: 100%;
|
|
497
|
+
padding: 0.5rem;
|
|
498
|
+
border: 1px solid #ccc;
|
|
499
|
+
border-radius: 4px;
|
|
500
|
+
font-size: 0.875rem;
|
|
501
|
+
box-sizing: border-box;
|
|
502
|
+
}
|
|
503
|
+
.login-form button {
|
|
504
|
+
padding: 0.5rem 1.5rem;
|
|
505
|
+
background: #0969da;
|
|
506
|
+
color: #fff;
|
|
507
|
+
border: none;
|
|
508
|
+
border-radius: 4px;
|
|
509
|
+
font-size: 0.875rem;
|
|
510
|
+
cursor: pointer;
|
|
511
|
+
}
|
|
512
|
+
.login-form button:hover {
|
|
513
|
+
background: #0860c5;
|
|
514
|
+
}
|
|
515
|
+
.login-error {
|
|
516
|
+
color: #d1242f;
|
|
517
|
+
background: #ffebe9;
|
|
518
|
+
padding: 0.5rem 0.75rem;
|
|
519
|
+
border-radius: 4px;
|
|
520
|
+
font-size: 0.875rem;
|
|
521
|
+
}
|
|
522
|
+
.login-description {
|
|
523
|
+
color: #666;
|
|
524
|
+
font-size: 0.875rem;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
@media (prefers-color-scheme: dark) {
|
|
528
|
+
.login-field input {
|
|
529
|
+
background: #0d1117;
|
|
530
|
+
color: #e6edf3;
|
|
531
|
+
border-color: #30363d;
|
|
532
|
+
}
|
|
533
|
+
.login-form button {
|
|
534
|
+
background: #1f6feb;
|
|
535
|
+
}
|
|
536
|
+
.login-form button:hover {
|
|
537
|
+
background: #388bfd;
|
|
538
|
+
}
|
|
539
|
+
.login-error {
|
|
540
|
+
color: #f85149;
|
|
541
|
+
background: #3d1f20;
|
|
542
|
+
}
|
|
543
|
+
.login-description {
|
|
544
|
+
color: #9198a1;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
` })
|
|
548
|
+
]
|
|
549
|
+
});
|
|
550
|
+
};
|
|
551
|
+
|
|
552
|
+
//#endregion
|
|
553
|
+
//#region src/views/trace-detail.tsx
|
|
554
|
+
/**
|
|
555
|
+
* Safely formats a timestamp (milliseconds since epoch) as an ISO string.
|
|
556
|
+
* Returns `"(invalid)"` if the timestamp is not a finite number or produces
|
|
557
|
+
* an invalid `Date`.
|
|
558
|
+
*/
|
|
559
|
+
function safeISOString(timestamp) {
|
|
560
|
+
if (!Number.isFinite(timestamp)) return "(invalid)";
|
|
561
|
+
try {
|
|
562
|
+
return new Date(timestamp).toISOString();
|
|
563
|
+
} catch {
|
|
564
|
+
return "(invalid)";
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* The trace detail page of the debug dashboard.
|
|
569
|
+
*/
|
|
570
|
+
const TraceDetailPage = ({ traceId, activities, logs, pathPrefix }) => {
|
|
571
|
+
return /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)(Layout, {
|
|
572
|
+
pathPrefix,
|
|
573
|
+
title: `Trace ${traceId.slice(0, 8)}`,
|
|
574
|
+
children: [
|
|
575
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("nav", { children: /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("a", {
|
|
576
|
+
href: `${pathPrefix}/`,
|
|
577
|
+
children: "← Back to traces"
|
|
578
|
+
}) }),
|
|
579
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("h2", { children: ["Trace ", /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("code", { children: traceId.slice(0, 8) })] }),
|
|
580
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("p", { children: [
|
|
581
|
+
"Full ID: ",
|
|
582
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("code", { children: traceId }),
|
|
583
|
+
" —",
|
|
584
|
+
" ",
|
|
585
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("strong", { children: activities.length }),
|
|
586
|
+
" ",
|
|
587
|
+
"activit",
|
|
588
|
+
activities.length !== 1 ? "ies" : "y",
|
|
589
|
+
",",
|
|
590
|
+
" ",
|
|
591
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("strong", { children: logs.length }),
|
|
592
|
+
" log record",
|
|
593
|
+
logs.length !== 1 ? "s" : ""
|
|
594
|
+
] }),
|
|
595
|
+
activities.length === 0 ? /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("p", {
|
|
596
|
+
class: "empty",
|
|
597
|
+
children: "No activities found for this trace."
|
|
598
|
+
}) : activities.map((activity) => /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("div", {
|
|
599
|
+
class: "detail-section",
|
|
600
|
+
children: [
|
|
601
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("h2", { children: [
|
|
602
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("span", {
|
|
603
|
+
class: `badge ${activity.direction === "inbound" ? "badge-inbound" : "badge-outbound"}`,
|
|
604
|
+
children: activity.direction
|
|
605
|
+
}),
|
|
606
|
+
" ",
|
|
607
|
+
activity.activityType
|
|
608
|
+
] }),
|
|
609
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("table", { children: /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("tbody", { children: [
|
|
610
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("tr", { children: [/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("th", { children: "Span ID" }), /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("code", { children: activity.spanId }) })] }),
|
|
611
|
+
activity.parentSpanId != null && /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("tr", { children: [/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("th", { children: "Parent Span" }), /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("code", { children: activity.parentSpanId }) })] }),
|
|
612
|
+
activity.activityId != null && /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("tr", { children: [/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("th", { children: "Activity ID" }), /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("code", { children: activity.activityId }) })] }),
|
|
613
|
+
activity.actorId != null && /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("tr", { children: [/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("th", { children: "Actor" }), /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("code", { children: activity.actorId }) })] }),
|
|
614
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("tr", { children: [/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("th", { children: "Timestamp" }), /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("time", {
|
|
615
|
+
datetime: activity.timestamp,
|
|
616
|
+
children: activity.timestamp
|
|
617
|
+
}) })] }),
|
|
618
|
+
activity.direction === "outbound" && activity.inboxUrl != null && /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("tr", { children: [/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("th", { children: "Inbox URL" }), /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("code", { children: activity.inboxUrl }) })] }),
|
|
619
|
+
activity.direction === "inbound" && /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("tr", { children: [/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("th", { children: "Verified" }), /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("td", { children: activity.verified ? "Yes" : "No" })] }),
|
|
620
|
+
activity.signatureDetails != null && /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("tr", { children: [/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("th", { children: "Signature Details" }), /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("td", { children: [
|
|
621
|
+
"HTTP Signatures:",
|
|
622
|
+
" ",
|
|
623
|
+
activity.signatureDetails.httpSignaturesVerified ? "verified" : "not verified",
|
|
624
|
+
activity.signatureDetails.httpSignaturesKeyId != null && /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("span", { children: [
|
|
625
|
+
"\xA0(key:\xA0",
|
|
626
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("code", { children: activity.signatureDetails.httpSignaturesKeyId }),
|
|
627
|
+
")"
|
|
628
|
+
] }),
|
|
629
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("br", {}),
|
|
630
|
+
"LD Signatures:",
|
|
631
|
+
" ",
|
|
632
|
+
activity.signatureDetails.ldSignaturesVerified ? "verified" : "not verified"
|
|
633
|
+
] })] })
|
|
634
|
+
] }) }),
|
|
635
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("details", { children: [/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("summary", { children: "Activity JSON" }), /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("pre", { children: formatJson(activity.activityJson) })] })
|
|
636
|
+
]
|
|
637
|
+
}, activity.spanId)),
|
|
638
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("h2", { children: "Logs" }),
|
|
639
|
+
logs.length === 0 ? /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("p", {
|
|
640
|
+
class: "empty",
|
|
641
|
+
children: "No logs captured for this trace."
|
|
642
|
+
}) : /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("table", {
|
|
643
|
+
class: "log-table",
|
|
644
|
+
children: [/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("tr", { children: [
|
|
645
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("th", { children: "Time" }),
|
|
646
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("th", { children: "Level" }),
|
|
647
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("th", { children: "Category" }),
|
|
648
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("th", { children: "Message" })
|
|
649
|
+
] }) }), /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("tbody", { children: logs.map((log, i) => /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("tr", {
|
|
650
|
+
class: `log-${log.level}`,
|
|
651
|
+
children: [
|
|
652
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("time", {
|
|
653
|
+
datetime: safeISOString(log.timestamp),
|
|
654
|
+
children: (() => {
|
|
655
|
+
const iso = safeISOString(log.timestamp);
|
|
656
|
+
return iso === "(invalid)" ? iso : iso.slice(11, 23);
|
|
657
|
+
})()
|
|
658
|
+
}) }),
|
|
659
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("span", {
|
|
660
|
+
class: `badge badge-${log.level}`,
|
|
661
|
+
children: log.level
|
|
662
|
+
}) }),
|
|
663
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("code", { children: log.category.join(".") }) }),
|
|
664
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("td", { children: [log.message, Object.keys(log.properties).length > 0 && /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("details", { children: [/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("summary", { children: "Properties" }), /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("pre", { children: JSON.stringify(log.properties, null, 2) })] })] })
|
|
665
|
+
]
|
|
666
|
+
}, i)) })]
|
|
667
|
+
})
|
|
668
|
+
]
|
|
669
|
+
});
|
|
670
|
+
};
|
|
671
|
+
function formatJson(json) {
|
|
672
|
+
try {
|
|
673
|
+
return JSON.stringify(JSON.parse(json), null, 2);
|
|
674
|
+
} catch {
|
|
675
|
+
return json;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
//#endregion
|
|
680
|
+
//#region src/views/traces-list.tsx
|
|
681
|
+
/**
|
|
682
|
+
* The traces list page of the debug dashboard.
|
|
683
|
+
*/
|
|
684
|
+
const TracesListPage = ({ traces, pathPrefix }) => {
|
|
685
|
+
return /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)(Layout, {
|
|
686
|
+
pathPrefix,
|
|
687
|
+
children: [
|
|
688
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("p", { children: [
|
|
689
|
+
"Showing ",
|
|
690
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("strong", { children: traces.length }),
|
|
691
|
+
" ",
|
|
692
|
+
"trace",
|
|
693
|
+
traces.length !== 1 ? "s" : "",
|
|
694
|
+
"."
|
|
695
|
+
] }),
|
|
696
|
+
traces.length === 0 ? /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("p", {
|
|
697
|
+
class: "empty",
|
|
698
|
+
children: "No traces captured yet."
|
|
699
|
+
}) : /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("table", { children: [/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("tr", { children: [
|
|
700
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("th", { children: "Trace ID" }),
|
|
701
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("th", { children: "Activity Types" }),
|
|
702
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("th", { children: "Activities" }),
|
|
703
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("th", { children: "Timestamp" })
|
|
704
|
+
] }) }), /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("tbody", { children: traces.map((trace$1) => /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("tr", { children: [
|
|
705
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("a", {
|
|
706
|
+
href: `${pathPrefix}/traces/${trace$1.traceId}`,
|
|
707
|
+
children: /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("code", { children: trace$1.traceId.slice(0, 8) })
|
|
708
|
+
}) }),
|
|
709
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsxs)("td", { children: [trace$1.activityTypes.map((t) => /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("span", {
|
|
710
|
+
class: "badge",
|
|
711
|
+
children: t
|
|
712
|
+
}, t)), trace$1.activityTypes.length === 0 && /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("span", {
|
|
713
|
+
class: "empty",
|
|
714
|
+
children: "none"
|
|
715
|
+
})] }),
|
|
716
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("td", { children: trace$1.activityCount }),
|
|
717
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("time", {
|
|
718
|
+
datetime: trace$1.timestamp,
|
|
719
|
+
children: trace$1.timestamp
|
|
720
|
+
}) })
|
|
721
|
+
] }, trace$1.traceId)) })] }),
|
|
722
|
+
/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)("script", { dangerouslySetInnerHTML: { __html: `
|
|
723
|
+
(function() {
|
|
724
|
+
var interval = setInterval(function() {
|
|
725
|
+
fetch(${JSON.stringify(pathPrefix).replace(/</g, "\\u003c")} + "/api/traces")
|
|
726
|
+
.then(function(r) { return r.json(); })
|
|
727
|
+
.then(function(data) {
|
|
728
|
+
var countEl = document.querySelector("strong");
|
|
729
|
+
if (countEl) {
|
|
730
|
+
var current = parseInt(countEl.textContent, 10);
|
|
731
|
+
if (data.length !== current) {
|
|
732
|
+
location.reload();
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
})
|
|
736
|
+
.catch(function() {});
|
|
737
|
+
}, 3000);
|
|
738
|
+
window.addEventListener("beforeunload", function() {
|
|
739
|
+
clearInterval(interval);
|
|
740
|
+
});
|
|
741
|
+
})();
|
|
742
|
+
` } })
|
|
743
|
+
]
|
|
744
|
+
});
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
//#endregion
|
|
748
|
+
//#region src/routes.tsx
|
|
749
|
+
function createDebugApp(pathPrefix, exporter, logStore, auth) {
|
|
750
|
+
const app = new hono.Hono({ strict: false }).basePath(pathPrefix);
|
|
751
|
+
let hmacKeyPromise;
|
|
752
|
+
if (auth != null && auth.type !== "request") hmacKeyPromise = generateHmacKey();
|
|
753
|
+
if (auth != null) if (auth.type === "request") app.use("*", async (c, next) => {
|
|
754
|
+
const allowed = await auth.authenticate(c.req.raw);
|
|
755
|
+
if (!allowed) return c.text("Forbidden", 403);
|
|
756
|
+
await next();
|
|
757
|
+
});
|
|
758
|
+
else {
|
|
759
|
+
const showUsername = auth.type === "usernamePassword";
|
|
760
|
+
app.post("/login", async (c) => {
|
|
761
|
+
const body = await c.req.parseBody();
|
|
762
|
+
const password = typeof body.password === "string" ? body.password : "";
|
|
763
|
+
const username = typeof body.username === "string" ? body.username : void 0;
|
|
764
|
+
const ok = await checkAuth(auth, {
|
|
765
|
+
username,
|
|
766
|
+
password
|
|
767
|
+
});
|
|
768
|
+
if (!ok) return c.html(/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)(LoginPage, {
|
|
769
|
+
pathPrefix,
|
|
770
|
+
showUsername,
|
|
771
|
+
error: "Invalid credentials."
|
|
772
|
+
}), 401);
|
|
773
|
+
const key = await hmacKeyPromise;
|
|
774
|
+
const sig = await signSession(key);
|
|
775
|
+
const secure = new URL(c.req.url).protocol === "https:";
|
|
776
|
+
return new Response(null, {
|
|
777
|
+
status: 303,
|
|
778
|
+
headers: {
|
|
779
|
+
"Location": pathPrefix + "/",
|
|
780
|
+
"Set-Cookie": `${SESSION_COOKIE_NAME}=${sig}; Path=${pathPrefix}; HttpOnly; SameSite=Strict${secure ? "; Secure" : ""}`
|
|
781
|
+
}
|
|
782
|
+
});
|
|
783
|
+
});
|
|
784
|
+
app.get("/logout", (c) => {
|
|
785
|
+
const secure = new URL(c.req.url).protocol === "https:";
|
|
786
|
+
return new Response(null, {
|
|
787
|
+
status: 303,
|
|
788
|
+
headers: {
|
|
789
|
+
"Location": pathPrefix + "/",
|
|
790
|
+
"Set-Cookie": `${SESSION_COOKIE_NAME}=; Path=${pathPrefix}; HttpOnly; SameSite=Strict${secure ? "; Secure" : ""}; Max-Age=0`
|
|
791
|
+
}
|
|
792
|
+
});
|
|
793
|
+
});
|
|
794
|
+
app.use("*", async (c, next) => {
|
|
795
|
+
const path = new URL(c.req.url).pathname;
|
|
796
|
+
const loginPath = pathPrefix + "/login";
|
|
797
|
+
const logoutPath = pathPrefix + "/logout";
|
|
798
|
+
if (path === loginPath || path === logoutPath) {
|
|
799
|
+
await next();
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
const sessionValue = (0, hono_cookie.getCookie)(c, SESSION_COOKIE_NAME);
|
|
803
|
+
if (sessionValue) {
|
|
804
|
+
const key = await hmacKeyPromise;
|
|
805
|
+
const valid = await verifySession(key, sessionValue);
|
|
806
|
+
if (valid) {
|
|
807
|
+
await next();
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
return c.html(/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)(LoginPage, {
|
|
812
|
+
pathPrefix,
|
|
813
|
+
showUsername
|
|
814
|
+
}), 401);
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
app.get("/api/traces", async (c) => {
|
|
818
|
+
const traces = await exporter.getRecentTraces();
|
|
819
|
+
return c.json(traces);
|
|
820
|
+
});
|
|
821
|
+
app.get("/api/logs/:traceId", async (c) => {
|
|
822
|
+
const traceId = c.req.param("traceId");
|
|
823
|
+
await logStore.flush();
|
|
824
|
+
const logs = await logStore.get(traceId);
|
|
825
|
+
return c.json(logs);
|
|
826
|
+
});
|
|
827
|
+
app.get("/traces/:traceId", async (c) => {
|
|
828
|
+
const traceId = c.req.param("traceId");
|
|
829
|
+
await logStore.flush();
|
|
830
|
+
const activities = await exporter.getActivitiesByTraceId(traceId);
|
|
831
|
+
const logs = await logStore.get(traceId);
|
|
832
|
+
return c.html(/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)(TraceDetailPage, {
|
|
833
|
+
traceId,
|
|
834
|
+
activities,
|
|
835
|
+
logs,
|
|
836
|
+
pathPrefix
|
|
837
|
+
}));
|
|
838
|
+
});
|
|
839
|
+
app.get("/", async (c) => {
|
|
840
|
+
const traces = await exporter.getRecentTraces();
|
|
841
|
+
return c.html(/* @__PURE__ */ (0, hono_jsx_jsx_runtime.jsx)(TracesListPage, {
|
|
842
|
+
traces,
|
|
843
|
+
pathPrefix
|
|
844
|
+
}));
|
|
845
|
+
});
|
|
846
|
+
return app;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
//#endregion
|
|
850
|
+
//#region src/mod.tsx
|
|
851
|
+
/**
|
|
852
|
+
* Cached auto-setup state so that repeated calls to
|
|
853
|
+
* `createFederationDebugger()` without an explicit exporter reuse the same
|
|
854
|
+
* global OpenTelemetry tracer provider and exporter instead of registering
|
|
855
|
+
* duplicate providers and LogTape sinks.
|
|
856
|
+
*/
|
|
857
|
+
let _autoSetup;
|
|
858
|
+
/**
|
|
859
|
+
* Resets the internal auto-setup state. This is intended **only for tests**
|
|
860
|
+
* that need to exercise the auto-setup code path more than once within the
|
|
861
|
+
* same process.
|
|
862
|
+
*
|
|
863
|
+
* @internal
|
|
864
|
+
*/
|
|
865
|
+
function resetAutoSetup() {
|
|
866
|
+
_autoSetup = void 0;
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Validates and normalizes the path prefix for the debug dashboard.
|
|
870
|
+
*
|
|
871
|
+
* The path must start with `/`, must not end with `/` (unless it is exactly
|
|
872
|
+
* `"/"`), and must not contain control characters, semicolons, or commas
|
|
873
|
+
* (which are unsafe in HTTP headers like `Set-Cookie` and `Location`).
|
|
874
|
+
*
|
|
875
|
+
* @param path The path prefix to validate.
|
|
876
|
+
* @returns The normalized path prefix (trailing slash stripped).
|
|
877
|
+
* @throws {TypeError} If the path is invalid.
|
|
878
|
+
*/
|
|
879
|
+
function validatePathPrefix(path) {
|
|
880
|
+
if (path === "" || !path.startsWith("/")) throw new TypeError(`Invalid debug dashboard path: ${JSON.stringify(path)}. The path must start with '/'.`);
|
|
881
|
+
if (/[\x00-\x1f\x7f;,]/.test(path)) throw new TypeError(`Invalid debug dashboard path: ${JSON.stringify(path)}. The path must not contain control characters, semicolons, or commas.`);
|
|
882
|
+
if (path.length > 1 && path.endsWith("/")) return path.slice(0, -1);
|
|
883
|
+
return path;
|
|
884
|
+
}
|
|
885
|
+
function createFederationDebugger(federation, options) {
|
|
886
|
+
const pathPrefix = validatePathPrefix(options?.path ?? "/__debug__");
|
|
887
|
+
let exporter;
|
|
888
|
+
let logKv;
|
|
889
|
+
if (options != null && "exporter" in options) {
|
|
890
|
+
exporter = options.exporter;
|
|
891
|
+
logKv = options.kv;
|
|
892
|
+
} else if (_autoSetup != null) {
|
|
893
|
+
exporter = _autoSetup.exporter;
|
|
894
|
+
logKv = _autoSetup.kv;
|
|
895
|
+
} else {
|
|
896
|
+
const kv = new __fedify_fedify_federation.MemoryKvStore();
|
|
897
|
+
logKv = kv;
|
|
898
|
+
exporter = new __fedify_fedify_otel.FedifySpanExporter(kv);
|
|
899
|
+
const tracerProvider = new __opentelemetry_sdk_trace_base.BasicTracerProvider({ spanProcessors: [new __opentelemetry_sdk_trace_base.SimpleSpanProcessor(exporter)] });
|
|
900
|
+
__opentelemetry_api.trace.setGlobalTracerProvider(tracerProvider);
|
|
901
|
+
const contextManager = new __opentelemetry_context_async_hooks.AsyncLocalStorageContextManager();
|
|
902
|
+
__opentelemetry_api.context.setGlobalContextManager(contextManager);
|
|
903
|
+
__opentelemetry_api.propagation.setGlobalPropagator(new __opentelemetry_core.W3CTraceContextPropagator());
|
|
904
|
+
_autoSetup = {
|
|
905
|
+
exporter,
|
|
906
|
+
kv
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
const logStore = new LogStore(logKv);
|
|
910
|
+
const sink = createLogSink(logStore);
|
|
911
|
+
if (options == null || !("exporter" in options)) {
|
|
912
|
+
const existingConfig = (0, __logtape_logtape.getConfig)();
|
|
913
|
+
if (existingConfig != null) {
|
|
914
|
+
const sinks = {
|
|
915
|
+
...existingConfig.sinks,
|
|
916
|
+
__fedify_debugger__: sink
|
|
917
|
+
};
|
|
918
|
+
const loggers = existingConfig.loggers.map((l) => ({
|
|
919
|
+
...l,
|
|
920
|
+
sinks: Array.isArray(l.category) && l.category.length < 1 ? [...l.sinks ?? [], "__fedify_debugger__"] : l.sinks
|
|
921
|
+
}));
|
|
922
|
+
if (loggers.every((l) => typeof l.category === "string" || Array.isArray(l.category) && l.category.length > 0)) loggers.push({
|
|
923
|
+
category: [],
|
|
924
|
+
sinks: ["__fedify_debugger__"]
|
|
925
|
+
});
|
|
926
|
+
(0, __logtape_logtape.configureSync)({
|
|
927
|
+
...existingConfig,
|
|
928
|
+
contextLocalStorage: existingConfig.contextLocalStorage ?? new node_async_hooks.AsyncLocalStorage(),
|
|
929
|
+
reset: true,
|
|
930
|
+
sinks,
|
|
931
|
+
loggers
|
|
932
|
+
});
|
|
933
|
+
} else (0, __logtape_logtape.configureSync)({
|
|
934
|
+
sinks: { __fedify_debugger__: sink },
|
|
935
|
+
loggers: [{
|
|
936
|
+
category: [],
|
|
937
|
+
sinks: ["__fedify_debugger__"]
|
|
938
|
+
}],
|
|
939
|
+
contextLocalStorage: new node_async_hooks.AsyncLocalStorage()
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
const auth = options?.auth;
|
|
943
|
+
const app = createDebugApp(pathPrefix, exporter, logStore, auth);
|
|
944
|
+
const debugFetch = async (request, fetchOptions) => {
|
|
945
|
+
const url = new URL(request.url);
|
|
946
|
+
if (url.pathname === pathPrefix || url.pathname.startsWith(pathPrefix + "/")) return await app.fetch(request);
|
|
947
|
+
return await federation.fetch(request, fetchOptions);
|
|
948
|
+
};
|
|
949
|
+
const overrides = {
|
|
950
|
+
fetch: debugFetch,
|
|
951
|
+
sink
|
|
952
|
+
};
|
|
953
|
+
return new Proxy(federation, {
|
|
954
|
+
get(target, prop, receiver) {
|
|
955
|
+
if (prop in overrides) return overrides[prop];
|
|
956
|
+
const value = Reflect.get(target, prop, receiver);
|
|
957
|
+
if (typeof value === "function") return value.bind(target);
|
|
958
|
+
return value;
|
|
959
|
+
},
|
|
960
|
+
has(target, prop) {
|
|
961
|
+
if (prop in overrides) return true;
|
|
962
|
+
return Reflect.has(target, prop);
|
|
963
|
+
}
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
//#endregion
|
|
968
|
+
exports.createFederationDebugger = createFederationDebugger;
|
|
969
|
+
exports.resetAutoSetup = resetAutoSetup;
|