@senrijs/senri 0.0.0-stage → 0.2.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 +21 -0
- package/README.md +377 -2
- package/dist/adapters/node/index.d.ts +14 -0
- package/dist/adapters/node/index.js +152 -0
- package/dist/adapters/serve/index.d.ts +28 -0
- package/dist/adapters/serve/index.js +137 -0
- package/dist/adapters/static/index.d.ts +9 -0
- package/dist/adapters/static/index.js +120 -0
- package/dist/src/app.d.ts +86 -0
- package/dist/src/app.js +325 -0
- package/dist/src/body.d.ts +3 -0
- package/dist/src/body.js +45 -0
- package/dist/src/compose.d.ts +22 -0
- package/dist/src/compose.js +80 -0
- package/dist/src/connection.d.ts +3 -0
- package/dist/src/connection.js +49 -0
- package/dist/src/context.d.ts +58 -0
- package/dist/src/context.js +235 -0
- package/dist/src/cookie.d.ts +18 -0
- package/dist/src/cookie.js +73 -0
- package/dist/src/dev/dashboard/access.d.ts +6 -0
- package/dist/src/dev/dashboard/access.js +46 -0
- package/dist/src/dev/dashboard/index.d.ts +27 -0
- package/dist/src/dev/dashboard/index.js +51 -0
- package/dist/src/dev/dashboard/links.d.ts +3 -0
- package/dist/src/dev/dashboard/links.js +8 -0
- package/dist/src/dev/dashboard/log.d.ts +20 -0
- package/dist/src/dev/dashboard/log.js +53 -0
- package/dist/src/dev/dashboard/mask.d.ts +4 -0
- package/dist/src/dev/dashboard/mask.js +43 -0
- package/dist/src/dev/dashboard/record.d.ts +37 -0
- package/dist/src/dev/dashboard/record.js +50 -0
- package/dist/src/dev/dashboard/routes.d.ts +7 -0
- package/dist/src/dev/dashboard/routes.js +121 -0
- package/dist/src/dev/dashboard/sse.d.ts +7 -0
- package/dist/src/dev/dashboard/sse.js +73 -0
- package/dist/src/dev/editor-url.d.ts +14 -0
- package/dist/src/dev/editor-url.js +61 -0
- package/dist/src/dev/editor.d.ts +6 -0
- package/dist/src/dev/editor.js +14 -0
- package/dist/src/dev/error-page.d.ts +3 -0
- package/dist/src/dev/error-page.js +69 -0
- package/dist/src/dev/explain.d.ts +2 -0
- package/dist/src/dev/explain.js +37 -0
- package/dist/src/dev/fallback.d.ts +3 -0
- package/dist/src/dev/fallback.js +40 -0
- package/dist/src/dev/hints.d.ts +6 -0
- package/dist/src/dev/hints.js +85 -0
- package/dist/src/dev/html.d.ts +1 -0
- package/dist/src/dev/html.js +14 -0
- package/dist/src/dev/index.d.ts +3 -0
- package/dist/src/dev/index.js +185 -0
- package/dist/src/dev/messages.d.ts +2 -0
- package/dist/src/dev/messages.js +6 -0
- package/dist/src/dev/not-found-page.d.ts +7 -0
- package/dist/src/dev/not-found-page.js +33 -0
- package/dist/src/dev/not-found.d.ts +3 -0
- package/dist/src/dev/not-found.js +15 -0
- package/dist/src/dev/page-data.d.ts +95 -0
- package/dist/src/dev/page-data.js +0 -0
- package/dist/src/dev/page.generated.d.ts +1 -0
- package/dist/src/dev/page.generated.js +3 -0
- package/dist/src/dev/pages.d.ts +4 -0
- package/dist/src/dev/pages.js +59 -0
- package/dist/src/dev/registry.d.ts +3 -0
- package/dist/src/dev/registry.js +7 -0
- package/dist/src/dev/render.d.ts +5 -0
- package/dist/src/dev/render.js +42 -0
- package/dist/src/dev/server-timing.d.ts +6 -0
- package/dist/src/dev/server-timing.js +45 -0
- package/dist/src/dev/source.d.ts +20 -0
- package/dist/src/dev/source.js +106 -0
- package/dist/src/dev/stack.d.ts +12 -0
- package/dist/src/dev/stack.js +93 -0
- package/dist/src/dev/suggest.d.ts +12 -0
- package/dist/src/dev/suggest.js +95 -0
- package/dist/src/dev/validation.d.ts +3 -0
- package/dist/src/dev/validation.js +47 -0
- package/dist/src/errors.d.ts +25 -0
- package/dist/src/errors.js +97 -0
- package/dist/src/group.d.ts +37 -0
- package/dist/src/group.js +75 -0
- package/dist/src/health.d.ts +9 -0
- package/dist/src/health.js +40 -0
- package/dist/src/index.d.ts +13 -0
- package/dist/src/index.js +12 -0
- package/dist/src/ip.d.ts +2 -0
- package/dist/src/ip.js +12 -0
- package/dist/src/logger.d.ts +18 -0
- package/dist/src/logger.js +110 -0
- package/dist/src/matcher.d.ts +16 -0
- package/dist/src/matcher.js +86 -0
- package/dist/src/middleware/body-limit.d.ts +6 -0
- package/dist/src/middleware/body-limit.js +47 -0
- package/dist/src/middleware/cors.d.ts +10 -0
- package/dist/src/middleware/cors.js +79 -0
- package/dist/src/middleware/response.d.ts +2 -0
- package/dist/src/middleware/response.js +28 -0
- package/dist/src/middleware/secure-headers.d.ts +19 -0
- package/dist/src/middleware/secure-headers.js +46 -0
- package/dist/src/router.d.ts +30 -0
- package/dist/src/router.js +177 -0
- package/dist/src/runtime.d.ts +5 -0
- package/dist/src/runtime.js +55 -0
- package/dist/src/validate.d.ts +46 -0
- package/dist/src/validate.js +55 -0
- package/package.json +90 -4
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { Context } from "../context.js";
|
|
2
|
+
import { compose } from "../compose.js";
|
|
3
|
+
import { defaultNotFound, defaultOnError, errorCode, HttpError } from "../errors.js";
|
|
4
|
+
import { compareRoutes } from "../router.js";
|
|
5
|
+
import * as runtime from "../runtime.js";
|
|
6
|
+
import { ValidationError } from "../validate.js";
|
|
7
|
+
import { createDashboard } from "./dashboard/index.js";
|
|
8
|
+
import { registerDashboard } from "./registry.js";
|
|
9
|
+
import { explain } from "./explain.js";
|
|
10
|
+
import { devNotFound } from "./not-found.js";
|
|
11
|
+
import { finishErrorPage, withErrorPage } from "./pages.js";
|
|
12
|
+
import { serverTimingHeader, withServerTiming } from "./server-timing.js";
|
|
13
|
+
import { checkSchemas, describeValidation } from "./validation.js";
|
|
14
|
+
// 計測で、いちばん内側の層に付ける名前(ダッシュボードの玉ねぎタイムラインに出る)
|
|
15
|
+
const INNER_NAME = {
|
|
16
|
+
route: "handler",
|
|
17
|
+
notFound: "notFound",
|
|
18
|
+
methodNotAllowed: "405",
|
|
19
|
+
options: "options",
|
|
20
|
+
badRequest: "400",
|
|
21
|
+
notImplemented: "501",
|
|
22
|
+
};
|
|
23
|
+
// 開発モードのフックを作る。本番(NODE_ENV=production)では undefined を返すので、開発モードにならない
|
|
24
|
+
// Deno で NODE_ENV を読む許可がないときは、本番かどうか分からないので、開発モードにしない(案内を1行出す)
|
|
25
|
+
export function devtools() {
|
|
26
|
+
if (!runtime.canReadEnv("NODE_ENV")) {
|
|
27
|
+
console.warn("[senri] devtools() is off: NODE_ENV is not readable. Run with --allow-env=NODE_ENV to enable it. / NODE_ENV を読めないので devtools() は無効です。--allow-env=NODE_ENV を付けると使えます");
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
if (runtime.env("NODE_ENV") === "production")
|
|
31
|
+
return undefined;
|
|
32
|
+
// Workers(nodejs_compat で process があっても、NODE_ENV はふつう入っていない)とブラウザは、本番かどうか分からない
|
|
33
|
+
const localOnly = runtime.isWorkers() || typeof globalThis.process === "undefined";
|
|
34
|
+
return { install: (core) => install(core, localOnly) };
|
|
35
|
+
}
|
|
36
|
+
// request.url のホスト名が localhost か(Host や X-Forwarded-Host などのヘッダーは見ない。だれでも付けられるので)
|
|
37
|
+
// - Workers: Cloudflare は登録されたドメインのリクエストしか渡さないので、本番に localhost は来ない。wrangler dev は localhost
|
|
38
|
+
// - ブラウザ: Service Worker が受けるリクエストの URL は、そのサイトのオリジン。開発中だけ localhost になる
|
|
39
|
+
export function isLocalUrl(url) {
|
|
40
|
+
let host;
|
|
41
|
+
try {
|
|
42
|
+
host = new URL(url).hostname.toLowerCase();
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
return (host === "localhost" ||
|
|
48
|
+
host.endsWith(".localhost") ||
|
|
49
|
+
host === "[::1]" ||
|
|
50
|
+
/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(host));
|
|
51
|
+
}
|
|
52
|
+
// パスが /_senri か /_senri/... なら、"_senri" より後ろのセグメント。そうでなければ null
|
|
53
|
+
function underDashboard(c) {
|
|
54
|
+
try {
|
|
55
|
+
return c.segments[0] === "_senri" ? c.segments.slice(1) : null;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// 層ごとの時間を c.timings に記録する(src/compose.ts の Trace)
|
|
62
|
+
function measure(c, origin, layers, innerName) {
|
|
63
|
+
const starts = [];
|
|
64
|
+
const inner = [];
|
|
65
|
+
return {
|
|
66
|
+
enter(i) {
|
|
67
|
+
starts[i] = performance.now();
|
|
68
|
+
inner[i] = 0;
|
|
69
|
+
},
|
|
70
|
+
down(i) {
|
|
71
|
+
const start = performance.now();
|
|
72
|
+
return () => (inner[i] += performance.now() - start);
|
|
73
|
+
},
|
|
74
|
+
leave(i, failed, stopped) {
|
|
75
|
+
const total = performance.now() - starts[i];
|
|
76
|
+
c.timings[i] = {
|
|
77
|
+
name: layers[i]?.name ?? innerName,
|
|
78
|
+
total,
|
|
79
|
+
self: total - inner[i],
|
|
80
|
+
failed,
|
|
81
|
+
start: starts[i] - origin,
|
|
82
|
+
stopped,
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
// 開発モードの既定の onError。本番の固定の文言の代わりに、詳しい文言を返す
|
|
88
|
+
// (ブラウザから開いた 5xx は、withErrorPage がエラーページにする)
|
|
89
|
+
const devOnError = (err, c) => {
|
|
90
|
+
if (err instanceof ValidationError)
|
|
91
|
+
return c.text(describeValidation(err));
|
|
92
|
+
if (err instanceof HttpError) {
|
|
93
|
+
switch (errorCode(err)) {
|
|
94
|
+
case "method_not_allowed":
|
|
95
|
+
return c.text(`405 Method Not Allowed: ${c.req.method} ${c.path}\n${err.headers.get("allow")} ならあります`);
|
|
96
|
+
case "query_no_body":
|
|
97
|
+
return c.text("400 Bad Request: QUERY リクエストにボディがありません\nQUERY は、検索の条件をボディに入れて送るメソッドです");
|
|
98
|
+
case "query_no_type":
|
|
99
|
+
return c.text("415 Unsupported Media Type: QUERY リクエストに Content-Type がありません\nボディの形式を指定してください(例: Content-Type: application/json)");
|
|
100
|
+
}
|
|
101
|
+
return c.text(err.message);
|
|
102
|
+
}
|
|
103
|
+
explain(err);
|
|
104
|
+
return c.text(`500 Internal Server Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
105
|
+
};
|
|
106
|
+
// localOnly: localhost へのリクエストのときだけ開発用の出力をする(Workers とブラウザ)
|
|
107
|
+
function install(core, localOnly) {
|
|
108
|
+
const { app } = core;
|
|
109
|
+
// 本番と同じに振る舞うリクエスト(localOnly で、localhost でないもの)
|
|
110
|
+
const quiet = new WeakSet();
|
|
111
|
+
// 一覧は優先順位の順(静的なセグメントが先)。本体は登録順に持っている
|
|
112
|
+
const sortedRoutes = () => [...core.routes()].sort(compareRoutes);
|
|
113
|
+
// ダッシュボード。Cloudflare Workers では作らない(接続元が分からず localhost 制限で開けないうえ、
|
|
114
|
+
// リクエストごとに別の isolate で動くことがあり、記録が残らない)。/_senri はふつうの 404 になる
|
|
115
|
+
const dashboard = runtime.isWorkers()
|
|
116
|
+
? null
|
|
117
|
+
: createDashboard({
|
|
118
|
+
routes: () => sortedRoutes().map((r) => ({ method: r.method, path: r.path })),
|
|
119
|
+
middlewares: () => app.middlewareList(),
|
|
120
|
+
});
|
|
121
|
+
if (dashboard)
|
|
122
|
+
registerDashboard(app, dashboard);
|
|
123
|
+
async function fetch(req, info, ctx) {
|
|
124
|
+
// 計測の基準になるリクエストの開始時刻
|
|
125
|
+
const origin = performance.now();
|
|
126
|
+
const time = Date.now();
|
|
127
|
+
const c = new Context(req, info, ctx);
|
|
128
|
+
if (localOnly && !isLocalUrl(req.url))
|
|
129
|
+
return quietFetch(c);
|
|
130
|
+
// /_senri 以下はダッシュボード。localhost から開いたときだけ、ミドルウェアを通さずに処理する
|
|
131
|
+
// (開けないときは普通の処理に回すので、/_senri はないものとして 404 になる)。どちらも記録はしない
|
|
132
|
+
const dashboardPath = underDashboard(c);
|
|
133
|
+
if (dashboard && dashboardPath && dashboard.canOpen(req, info)) {
|
|
134
|
+
return dashboard.handle(req, dashboardPath);
|
|
135
|
+
}
|
|
136
|
+
if (!dashboardPath)
|
|
137
|
+
dashboard?.begin(c, info);
|
|
138
|
+
// ミドルウェアより先にマッチして、c.route / c.params を見られるようにしておく
|
|
139
|
+
const resolved = core.resolve(c);
|
|
140
|
+
if (resolved.kind === "route") {
|
|
141
|
+
c.route = resolved.route.path;
|
|
142
|
+
c.params = resolved.params;
|
|
143
|
+
}
|
|
144
|
+
// ハンドラー・notFound・toResponse のどこで投げても、compose が onError に集める
|
|
145
|
+
const layers = core.layers(c);
|
|
146
|
+
const innerName = INNER_NAME[resolved.kind];
|
|
147
|
+
const res = await compose(layers, c, () => core.inner(c, resolved), (err) => core.fail(err, c), measure(c, origin, layers, innerName));
|
|
148
|
+
// ミドルウェアを全部通ったあとなので、ここなら「通った順番」が揃っている
|
|
149
|
+
const page = await finishErrorPage(c, res);
|
|
150
|
+
// 層ごとの時間を Server-Timing に載せる(ブラウザの開発者ツールで見られる)
|
|
151
|
+
const desc = resolved.kind === "route" ? `${resolved.route.method} ${resolved.route.path}` : innerName;
|
|
152
|
+
const final = withServerTiming(c.finalize(page), serverTimingHeader(c.timings, { name: innerName, desc }, performance.now() - origin));
|
|
153
|
+
// ダッシュボードに記録する(失敗してもレスポンスには影響しない)
|
|
154
|
+
await dashboard?.record(c, final, time, [
|
|
155
|
+
...layers.map((l) => ({ name: l.name, order: l.order ?? null })),
|
|
156
|
+
{ name: innerName, order: null },
|
|
157
|
+
]);
|
|
158
|
+
return final;
|
|
159
|
+
}
|
|
160
|
+
// 本番の fetch と同じ流れ(計測・記録・エラーページ・Server-Timing なし。onError / notFound も本番の既定になる)
|
|
161
|
+
function quietFetch(c) {
|
|
162
|
+
quiet.add(c);
|
|
163
|
+
const resolved = core.resolve(c);
|
|
164
|
+
if (resolved.kind === "route") {
|
|
165
|
+
c.route = resolved.route.path;
|
|
166
|
+
c.params = resolved.params;
|
|
167
|
+
}
|
|
168
|
+
return compose(core.layers(c), c, () => core.inner(c, resolved), (err) => core.fail(err, c)).then((res) => c.finalize(res));
|
|
169
|
+
}
|
|
170
|
+
const pageOnError = withErrorPage(devOnError, true);
|
|
171
|
+
return {
|
|
172
|
+
fetch,
|
|
173
|
+
onError: (err, c) => (quiet.has(c) ? defaultOnError(err, c) : pageOnError(err, c)),
|
|
174
|
+
notFound: (c) => (quiet.has(c) ? defaultNotFound(c) : devNotFound(c, sortedRoutes())),
|
|
175
|
+
checkRoute(method, path, schemas) {
|
|
176
|
+
if (schemas)
|
|
177
|
+
checkSchemas(path, schemas);
|
|
178
|
+
// /_senri 以下はダッシュボードが使うので、登録しても呼ばれない。気づけるようにエラーにする
|
|
179
|
+
if (dashboard && /^\/*_senri(\/|$)/.test(path)) {
|
|
180
|
+
throw new Error(`${method} ${path}: /_senri 以下は、開発モードではダッシュボード用に予約されています`);
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
explain,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { dashboardLinkOf } from "./dashboard/links.js";
|
|
2
|
+
import { notFoundFallback } from "./fallback.js";
|
|
3
|
+
import { PAGE_HTML } from "./page.generated.js";
|
|
4
|
+
import { renderPage } from "./render.js";
|
|
5
|
+
import { explainDifference, suggest } from "./suggest.js";
|
|
6
|
+
// ブラウザで開けるのは、パラメーターのない GET(と、どのメソッドでも動く all)だけ
|
|
7
|
+
// (":id" に何を入れるかは分からないので、パラメーターのあるものはリンクにしない)
|
|
8
|
+
const linkable = (r) => (r.method === "GET" || r.method === "ALL") && !r.path.includes(":");
|
|
9
|
+
export function notFoundPage(c, routes) {
|
|
10
|
+
// notFound に来るのはパスをデコードできたリクエストだけなので、c.path は読める
|
|
11
|
+
const path = c.path;
|
|
12
|
+
const closest = suggest(routes, path);
|
|
13
|
+
const data = {
|
|
14
|
+
kind: "notFound",
|
|
15
|
+
method: c.req.method,
|
|
16
|
+
path,
|
|
17
|
+
suggestion: closest
|
|
18
|
+
? {
|
|
19
|
+
method: closest.method,
|
|
20
|
+
path: closest.path,
|
|
21
|
+
link: linkable(closest),
|
|
22
|
+
...explainDifference(path, closest.path),
|
|
23
|
+
}
|
|
24
|
+
: null,
|
|
25
|
+
// showRoutes と同じく、パスの順 → 同じパスならメソッドの順
|
|
26
|
+
routes: [...routes]
|
|
27
|
+
.sort((a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method))
|
|
28
|
+
.map((r) => ({ method: r.method, path: r.path, link: linkable(r) })),
|
|
29
|
+
dashboardUrl: dashboardLinkOf(c),
|
|
30
|
+
};
|
|
31
|
+
const body = c.req.method === "HEAD" ? null : renderPage(PAGE_HTML, data, notFoundFallback(data));
|
|
32
|
+
return new Response(body, { status: 404, headers: { "content-type": "text/html; charset=utf-8" } });
|
|
33
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { notFoundPage } from "./not-found-page.js";
|
|
2
|
+
import { wantsHtml } from "./render.js";
|
|
3
|
+
import { suggest } from "./suggest.js";
|
|
4
|
+
export function devNotFound(c, routes) {
|
|
5
|
+
if (wantsHtml(c.req))
|
|
6
|
+
return notFoundPage(c, routes);
|
|
7
|
+
// ルートと同じく、デコード・正規化したパスで比べる(notFound に来るのはデコードできたリクエストだけ)
|
|
8
|
+
const path = c.path;
|
|
9
|
+
let message = `404 Not Found: ${c.req.method} ${path}`;
|
|
10
|
+
const closest = suggest(routes, path);
|
|
11
|
+
if (closest) {
|
|
12
|
+
message += `\nもしかして: ${closest.method} ${closest.path} ?`;
|
|
13
|
+
}
|
|
14
|
+
return c.text(message);
|
|
15
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { ConversionOrigin } from "../errors.ts";
|
|
2
|
+
import type { CodeFrame } from "./source.ts";
|
|
3
|
+
import type { EditorGuess } from "./editor.ts";
|
|
4
|
+
import type { Hint } from "./hints.ts";
|
|
5
|
+
import type { FrameKind } from "./stack.ts";
|
|
6
|
+
import type { Difference } from "./suggest.ts";
|
|
7
|
+
export type PageFrame = {
|
|
8
|
+
fn: string;
|
|
9
|
+
file: string;
|
|
10
|
+
display: string;
|
|
11
|
+
line: number;
|
|
12
|
+
col: number;
|
|
13
|
+
kind: FrameKind;
|
|
14
|
+
};
|
|
15
|
+
export type ErrorPageData = {
|
|
16
|
+
kind: "error";
|
|
17
|
+
status: number;
|
|
18
|
+
statusText: string;
|
|
19
|
+
durationMs: number | null;
|
|
20
|
+
error: {
|
|
21
|
+
name: string;
|
|
22
|
+
message: string;
|
|
23
|
+
};
|
|
24
|
+
origin: ConversionOrigin | null;
|
|
25
|
+
location: PageFrame | null;
|
|
26
|
+
code: CodeFrame | null;
|
|
27
|
+
hint: Hint | null;
|
|
28
|
+
frames: PageFrame[];
|
|
29
|
+
request: {
|
|
30
|
+
method: string;
|
|
31
|
+
path: string;
|
|
32
|
+
route: string | null;
|
|
33
|
+
params: Record<string, string>;
|
|
34
|
+
userAgent: string | null;
|
|
35
|
+
};
|
|
36
|
+
layers: {
|
|
37
|
+
name: string;
|
|
38
|
+
self: number;
|
|
39
|
+
total: number;
|
|
40
|
+
failed: boolean;
|
|
41
|
+
}[];
|
|
42
|
+
editor: EditorGuess;
|
|
43
|
+
root: string | null;
|
|
44
|
+
production: string;
|
|
45
|
+
dashboardUrl: string | null;
|
|
46
|
+
};
|
|
47
|
+
export type NotFoundPageData = {
|
|
48
|
+
kind: "notFound";
|
|
49
|
+
method: string;
|
|
50
|
+
path: string;
|
|
51
|
+
suggestion: (Difference & {
|
|
52
|
+
method: string;
|
|
53
|
+
path: string;
|
|
54
|
+
link: boolean;
|
|
55
|
+
}) | null;
|
|
56
|
+
routes: {
|
|
57
|
+
method: string;
|
|
58
|
+
path: string;
|
|
59
|
+
link: boolean;
|
|
60
|
+
}[];
|
|
61
|
+
dashboardUrl: string | null;
|
|
62
|
+
};
|
|
63
|
+
export type RequestSummary = {
|
|
64
|
+
id: string;
|
|
65
|
+
time: number;
|
|
66
|
+
method: string;
|
|
67
|
+
path: string;
|
|
68
|
+
status: number;
|
|
69
|
+
durationMs: number;
|
|
70
|
+
hasError: boolean;
|
|
71
|
+
};
|
|
72
|
+
export type RouteRow = {
|
|
73
|
+
method: string;
|
|
74
|
+
path: string;
|
|
75
|
+
middlewares: {
|
|
76
|
+
name: string;
|
|
77
|
+
applies: "yes" | "no" | "maybe";
|
|
78
|
+
}[];
|
|
79
|
+
};
|
|
80
|
+
export type MiddlewareRow = {
|
|
81
|
+
order: number;
|
|
82
|
+
name: string;
|
|
83
|
+
anonymous: boolean;
|
|
84
|
+
path: string | null;
|
|
85
|
+
runs: number;
|
|
86
|
+
avgSelfMs: number | null;
|
|
87
|
+
};
|
|
88
|
+
export type DashboardPageData = {
|
|
89
|
+
kind: "dashboard";
|
|
90
|
+
max: number;
|
|
91
|
+
requests: RequestSummary[];
|
|
92
|
+
routes: RouteRow[];
|
|
93
|
+
middlewares: MiddlewareRow[];
|
|
94
|
+
};
|
|
95
|
+
export type PageData = ErrorPageData | NotFoundPageData | DashboardPageData;
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const PAGE_HTML: string;
|