@forinda/kickjs-devtools 2.0.0 → 2.1.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/dist/index.js DELETED
@@ -1,205 +0,0 @@
1
- import "reflect-metadata";
2
- import { Router as v, static as A } from "express";
3
- import { dirname as b, join as c } from "node:path";
4
- import { fileURLToPath as R } from "node:url";
5
- import { existsSync as y, readFileSync as E } from "node:fs";
6
- import { randomBytes as D } from "node:crypto";
7
- import { METADATA as f, computed as p, createLogger as S, reactive as C, ref as l, watch as m } from "@forinda/kickjs";
8
- var h = S("DevTools"), j = class {
9
- name = "DevToolsAdapter";
10
- basePath;
11
- enabled;
12
- exposeConfig;
13
- configPrefixes;
14
- errorRateThreshold;
15
- secret;
16
- requestCount;
17
- errorCount;
18
- clientErrorCount;
19
- startedAt;
20
- errorRate;
21
- uptimeSeconds;
22
- routeLatency;
23
- routes = [];
24
- container = null;
25
- appRef = null;
26
- adapterStatuses = {};
27
- stopErrorWatch = null;
28
- peerAdapters = [];
29
- constructor(s = {}) {
30
- if (this.basePath = s.basePath ?? "/_debug", this.enabled = s.enabled ?? process.env.NODE_ENV !== "production", this.exposeConfig = s.exposeConfig ?? !1, this.configPrefixes = s.configPrefixes ?? ["APP_", "NODE_ENV"], this.errorRateThreshold = s.errorRateThreshold ?? 0.5, this.peerAdapters = s.adapters ?? [], s.secret === !1 ? this.secret = !1 : s.secret ? this.secret = s.secret : this.secret = D(16).toString("hex"), this.requestCount = l(0), this.errorCount = l(0), this.clientErrorCount = l(0), this.startedAt = l(Date.now()), this.routeLatency = C({}), this.errorRate = p(() => this.requestCount.value > 0 ? this.errorCount.value / this.requestCount.value : 0), this.uptimeSeconds = p(() => Math.floor((Date.now() - this.startedAt.value) / 1e3)), s.onErrorRateExceeded) {
31
- const n = s.onErrorRateExceeded, r = this.errorRateThreshold;
32
- this.stopErrorWatch = m(this.errorRate, (i) => {
33
- i > r && n(i);
34
- });
35
- } else this.stopErrorWatch = m(this.errorRate, (n) => {
36
- n > this.errorRateThreshold && h.warn(`Error rate elevated: ${(n * 100).toFixed(1)}%`);
37
- });
38
- }
39
- getPeerAdapters() {
40
- const s = this.appRef?.__kickApp;
41
- return s && typeof s.getAdapters == "function" ? s.getAdapters() : this.peerAdapters;
42
- }
43
- beforeMount({ app: s, container: n }) {
44
- if (!this.enabled) return;
45
- this.appRef = s, this.container = n, this.startedAt.value = Date.now(), this.routes = [], this.adapterStatuses[this.name] = "running";
46
- const r = v();
47
- if (this.secret !== !1) {
48
- const o = this.secret;
49
- r.use((e, t, a) => {
50
- if ((e.headers["x-devtools-token"] ?? e.query?.token) === o || e.path === "/" && e.method === "GET" && !e.query?.token || e.path.endsWith(".js") || e.path.endsWith(".css")) return a();
51
- t.status(403).json({ error: "Forbidden — invalid or missing devtools token" });
52
- });
53
- }
54
- r.get("/routes", (o, e) => {
55
- e.json({ routes: this.routes });
56
- }), r.get("/container", (o, e) => {
57
- const t = this.container?.getRegistrations() ?? [];
58
- e.json({
59
- registrations: t,
60
- count: t.length
61
- });
62
- }), r.get("/metrics", (o, e) => {
63
- e.json({
64
- requests: this.requestCount.value,
65
- serverErrors: this.errorCount.value,
66
- clientErrors: this.clientErrorCount.value,
67
- errorRate: this.errorRate.value,
68
- uptimeSeconds: this.uptimeSeconds.value,
69
- startedAt: new Date(this.startedAt.value).toISOString(),
70
- routeLatency: this.routeLatency
71
- });
72
- }), r.get("/health", (o, e) => {
73
- const t = this.errorRate.value < this.errorRateThreshold, a = t ? "healthy" : "degraded";
74
- e.status(t ? 200 : 503).json({
75
- status: a,
76
- errorRate: this.errorRate.value,
77
- uptime: this.uptimeSeconds.value,
78
- adapters: this.adapterStatuses
79
- });
80
- }), r.get("/state", (o, e) => {
81
- const t = this.getPeerAdapters().find((a) => a.name === "WsAdapter" && typeof a.getStats == "function");
82
- e.json({
83
- reactive: {
84
- requestCount: this.requestCount.value,
85
- errorCount: this.errorCount.value,
86
- clientErrorCount: this.clientErrorCount.value,
87
- errorRate: this.errorRate.value,
88
- uptimeSeconds: this.uptimeSeconds.value,
89
- startedAt: new Date(this.startedAt.value).toISOString()
90
- },
91
- routes: this.routes.length,
92
- container: this.container?.getRegistrations().length ?? 0,
93
- routeLatency: this.routeLatency,
94
- ...t ? { ws: t.getStats() } : {}
95
- });
96
- }), r.get("/ws", (o, e) => {
97
- const t = this.getPeerAdapters().find((a) => a.name === "WsAdapter" && typeof a.getStats == "function");
98
- if (!t) {
99
- e.json({
100
- enabled: !1,
101
- message: "WsAdapter not found"
102
- });
103
- return;
104
- }
105
- e.json({
106
- enabled: !0,
107
- ...t.getStats()
108
- });
109
- }), r.get("/queues", async (o, e) => {
110
- const t = this.getPeerAdapters().find((a) => a.name === "QueueAdapter" && typeof a.getQueueNames == "function");
111
- if (!t) {
112
- e.json({
113
- enabled: !1,
114
- message: "QueueAdapter not found"
115
- });
116
- return;
117
- }
118
- try {
119
- const a = t.getQueueNames?.() ?? [], u = [];
120
- for (const d of a) {
121
- const g = await t.getQueueStats?.(d);
122
- u.push({
123
- name: d,
124
- ...g
125
- });
126
- }
127
- e.json({
128
- enabled: !0,
129
- queues: u
130
- });
131
- } catch {
132
- e.json({
133
- enabled: !0,
134
- queues: [],
135
- error: "Failed to fetch queue stats"
136
- });
137
- }
138
- }), this.exposeConfig && r.get("/config", (o, e) => {
139
- const t = {};
140
- for (const [a, u] of Object.entries(process.env))
141
- u !== void 0 && (t[a] = this.configPrefixes.some((d) => a.startsWith(d)) ? u : "[REDACTED]");
142
- e.json({ config: t });
143
- });
144
- const i = this.resolvePublicDir();
145
- if (i) {
146
- r.use(A(i));
147
- const o = E(c(i, "index.html"), "utf-8");
148
- r.get("/", (e, t) => {
149
- const a = o.replace("<body", `<body data-base="${this.basePath}"`);
150
- t.type("html").send(a);
151
- });
152
- } else r.get("/", (o, e) => {
153
- e.type("html").send("<h1>DevTools: public directory not found</h1>");
154
- });
155
- s.use(this.basePath, r), this.secret ? (h.info(`DevTools mounted at ${this.basePath} [token: ${this.secret}]`), h.info(`Access: ${this.basePath}?token=${this.secret}`)) : h.info(`DevTools mounted at ${this.basePath} [no guard]`);
156
- }
157
- middleware() {
158
- return this.enabled ? [{
159
- handler: (s, n, r) => {
160
- const i = Date.now();
161
- this.requestCount.value++, n.on("finish", () => {
162
- n.statusCode >= 500 ? this.errorCount.value++ : n.statusCode >= 400 && this.clientErrorCount.value++;
163
- const o = `${s.method} ${s.route?.path ?? s.path}`, e = Date.now() - i;
164
- this.routeLatency[o] || (this.routeLatency[o] = {
165
- count: 0,
166
- totalMs: 0,
167
- minMs: 1 / 0,
168
- maxMs: 0
169
- });
170
- const t = this.routeLatency[o];
171
- t.count++, t.totalMs += e, t.minMs = Math.min(t.minMs, e), t.maxMs = Math.max(t.maxMs, e);
172
- }), r();
173
- },
174
- phase: "beforeGlobal"
175
- }] : [];
176
- }
177
- onRouteMount(s, n) {
178
- if (!this.enabled) return;
179
- const r = Reflect.getMetadata(f.ROUTES, s) ?? [], i = Reflect.getMetadata(f.CLASS_MIDDLEWARES, s) ?? [];
180
- for (const o of r) {
181
- const e = Reflect.getMetadata(f.METHOD_MIDDLEWARES, s.prototype, o.handlerName) ?? [];
182
- this.routes.push({
183
- method: o.method.toUpperCase(),
184
- path: `${n}${o.path === "/" ? "" : o.path}`,
185
- controller: s.name,
186
- handler: o.handlerName,
187
- middleware: [...i.map((t) => t.name || "anonymous"), ...e.map((t) => t.name || "anonymous")]
188
- });
189
- }
190
- }
191
- afterStart({}) {
192
- this.enabled && h.info(`DevTools ready — ${this.routes.length} routes tracked, ${this.container?.getRegistrations().length ?? 0} DI bindings`);
193
- }
194
- shutdown() {
195
- this.stopErrorWatch?.(), this.adapterStatuses[this.name] = "stopped";
196
- }
197
- resolvePublicDir() {
198
- const s = b(R(import.meta.url)), n = [c(s, "..", "public", "devtools"), c(s, "..", "..", "public", "devtools")];
199
- for (const r of n) if (y(c(r, "index.html"))) return r;
200
- return null;
201
- }
202
- };
203
- export {
204
- j as DevToolsAdapter
205
- };