@warlock.js/devtools 5.25.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/CHANGELOG.md +11 -0
- package/LICENSE +21 -0
- package/README.md +35 -0
- package/cjs/index.cjs +694 -0
- package/cjs/index.cjs.map +1 -0
- package/cjs/server-DHYSVive.cjs +286 -0
- package/cjs/server-DHYSVive.cjs.map +1 -0
- package/esm/caller-location.mjs +33 -0
- package/esm/caller-location.mjs.map +1 -0
- package/esm/collector.mjs +185 -0
- package/esm/collector.mjs.map +1 -0
- package/esm/current-request-id.mjs +24 -0
- package/esm/current-request-id.mjs.map +1 -0
- package/esm/devtools-connector.d.mts +26 -0
- package/esm/devtools-connector.d.mts.map +1 -0
- package/esm/devtools-connector.mjs +112 -0
- package/esm/devtools-connector.mjs.map +1 -0
- package/esm/index.d.mts +3 -0
- package/esm/index.mjs +3 -0
- package/esm/n-plus-one.mjs +36 -0
- package/esm/n-plus-one.mjs.map +1 -0
- package/esm/options.mjs +19 -0
- package/esm/options.mjs.map +1 -0
- package/esm/query-shape.mjs +27 -0
- package/esm/query-shape.mjs.map +1 -0
- package/esm/safe-value.mjs +63 -0
- package/esm/safe-value.mjs.map +1 -0
- package/esm/server/background-routes.mjs +9 -0
- package/esm/server/background-routes.mjs.map +1 -0
- package/esm/server/explain-route.mjs +37 -0
- package/esm/server/explain-route.mjs.map +1 -0
- package/esm/server/guard.mjs +30 -0
- package/esm/server/guard.mjs.map +1 -0
- package/esm/server/index.mjs +37 -0
- package/esm/server/index.mjs.map +1 -0
- package/esm/server/log-routes.mjs +12 -0
- package/esm/server/log-routes.mjs.map +1 -0
- package/esm/server/mail-routes.mjs +27 -0
- package/esm/server/mail-routes.mjs.map +1 -0
- package/esm/server/requests-routes.mjs +21 -0
- package/esm/server/requests-routes.mjs.map +1 -0
- package/esm/server/route-routes.mjs +9 -0
- package/esm/server/route-routes.mjs.map +1 -0
- package/esm/server/stream-route.mjs +32 -0
- package/esm/server/stream-route.mjs.map +1 -0
- package/esm/server/ui-assets.mjs +38 -0
- package/esm/server/ui-assets.mjs.map +1 -0
- package/esm/sources/cache-source.mjs +51 -0
- package/esm/sources/cache-source.mjs.map +1 -0
- package/esm/sources/log-source.mjs +46 -0
- package/esm/sources/log-source.mjs.map +1 -0
- package/esm/sources/mail-source.mjs +48 -0
- package/esm/sources/mail-source.mjs.map +1 -0
- package/esm/sources/query-source.mjs +52 -0
- package/esm/sources/query-source.mjs.map +1 -0
- package/esm/sources/tracing-source.mjs +47 -0
- package/esm/sources/tracing-source.mjs.map +1 -0
- package/esm/types.d.mts +213 -0
- package/esm/types.d.mts.map +1 -0
- package/esm/ui-root.mjs +40 -0
- package/esm/ui-root.mjs.map +1 -0
- package/llms-full.txt +78 -0
- package/llms.txt +9 -0
- package/package.json +52 -0
- package/skills/devtools-overview/SKILL.md +68 -0
- package/ui/app.css +354 -0
- package/ui/app.js +520 -0
- package/ui/index.html +24 -0
package/esm/types.d.mts
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
//#region ../devtools/src/types.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The records the devtools collector keeps and the dashboard API returns.
|
|
4
|
+
*
|
|
5
|
+
* Every timestamp is epoch milliseconds (fractional allowed); every duration
|
|
6
|
+
* is milliseconds. Nothing here is persisted — the collector is an in-memory
|
|
7
|
+
* ring buffer that is lost on restart.
|
|
8
|
+
*/
|
|
9
|
+
/** Options accepted by `devtoolsConnector()`. */
|
|
10
|
+
type DevtoolsOptions = {
|
|
11
|
+
/**
|
|
12
|
+
* URL prefix the dashboard and its API are served under.
|
|
13
|
+
*
|
|
14
|
+
* @default "/__warlock"
|
|
15
|
+
*/
|
|
16
|
+
path?: string;
|
|
17
|
+
/**
|
|
18
|
+
* How many requests the ring buffer keeps; the oldest is evicted first.
|
|
19
|
+
*
|
|
20
|
+
* @default 200
|
|
21
|
+
*/
|
|
22
|
+
maxRequests?: number;
|
|
23
|
+
/**
|
|
24
|
+
* How many times one query shape may run inside a single request before it
|
|
25
|
+
* is reported as an N+1.
|
|
26
|
+
*
|
|
27
|
+
* @default 5
|
|
28
|
+
*/
|
|
29
|
+
nPlusOneThreshold?: number;
|
|
30
|
+
};
|
|
31
|
+
/** `DevtoolsOptions` with every default applied. */
|
|
32
|
+
type ResolvedDevtoolsOptions = Required<DevtoolsOptions>;
|
|
33
|
+
/** A source file position, as shown next to an N+1 warning. */
|
|
34
|
+
type DevtoolsCallerLocation = {
|
|
35
|
+
/** Absolute path, forward slashes. */file: string;
|
|
36
|
+
line: number;
|
|
37
|
+
column: number;
|
|
38
|
+
};
|
|
39
|
+
/** One timed phase of a request (router middleware, loader, render, …). */
|
|
40
|
+
type DevtoolsPhase = {
|
|
41
|
+
name: string;
|
|
42
|
+
startedAt: number;
|
|
43
|
+
durationMs: number;
|
|
44
|
+
attrs?: Record<string, unknown>;
|
|
45
|
+
};
|
|
46
|
+
/** One database query, Postgres or Mongo. */
|
|
47
|
+
type DevtoolsQuery = {
|
|
48
|
+
/** Unique within the collector. */id: string;
|
|
49
|
+
connection: string;
|
|
50
|
+
driver: "postgres" | "mongodb";
|
|
51
|
+
sql?: string; /** Redacted through the logger's redaction rules before storage. */
|
|
52
|
+
bindings?: unknown[];
|
|
53
|
+
collection?: string;
|
|
54
|
+
command?: string;
|
|
55
|
+
pipeline?: unknown;
|
|
56
|
+
startedAt: number;
|
|
57
|
+
durationMs: number;
|
|
58
|
+
rowCount?: number; /** The error message when the query failed. */
|
|
59
|
+
error?: string;
|
|
60
|
+
/**
|
|
61
|
+
* The query with its values stripped — SQL text without bindings, or the
|
|
62
|
+
* Mongo command with every leaf value replaced by `?`. Two queries with the
|
|
63
|
+
* same shape are "the same query" for N+1 detection.
|
|
64
|
+
*/
|
|
65
|
+
shape: string; /** The first application frame that issued the query, when resolvable. */
|
|
66
|
+
caller?: DevtoolsCallerLocation;
|
|
67
|
+
};
|
|
68
|
+
/** One cache operation. */
|
|
69
|
+
type DevtoolsCacheOperation = {
|
|
70
|
+
type: "hit" | "miss" | "set" | "removed" | "invalidated" | "flushed";
|
|
71
|
+
driver: string;
|
|
72
|
+
key?: string;
|
|
73
|
+
tags?: string[];
|
|
74
|
+
keys?: string[];
|
|
75
|
+
at: number;
|
|
76
|
+
durationMs?: number;
|
|
77
|
+
};
|
|
78
|
+
/** One log entry. */
|
|
79
|
+
type DevtoolsLogEntry = {
|
|
80
|
+
level: "debug" | "info" | "warn" | "error" | "success";
|
|
81
|
+
module: string;
|
|
82
|
+
action: string;
|
|
83
|
+
message: string;
|
|
84
|
+
at: number; /** Redacted through the logger's redaction rules before storage. */
|
|
85
|
+
context?: unknown;
|
|
86
|
+
};
|
|
87
|
+
/** A mail captured in development (see core's `mailEvents.onCaptured`). */
|
|
88
|
+
type DevtoolsMail = {
|
|
89
|
+
id: string;
|
|
90
|
+
requestId?: string;
|
|
91
|
+
capturedAt: number;
|
|
92
|
+
from?: string;
|
|
93
|
+
to: string[];
|
|
94
|
+
cc: string[];
|
|
95
|
+
bcc: string[];
|
|
96
|
+
subject: string;
|
|
97
|
+
html?: string;
|
|
98
|
+
text?: string;
|
|
99
|
+
headers: Record<string, string>;
|
|
100
|
+
attachments: DevtoolsMailAttachment[];
|
|
101
|
+
};
|
|
102
|
+
/** Attachment metadata only — the bytes are never stored. */
|
|
103
|
+
type DevtoolsMailAttachment = {
|
|
104
|
+
filename: string;
|
|
105
|
+
contentType?: string;
|
|
106
|
+
size?: number;
|
|
107
|
+
};
|
|
108
|
+
/** A problem devtools noticed about a request. */
|
|
109
|
+
type DevtoolsWarning = {
|
|
110
|
+
type: "n+1";
|
|
111
|
+
message: string;
|
|
112
|
+
shape: string;
|
|
113
|
+
count: number;
|
|
114
|
+
caller?: DevtoolsCallerLocation;
|
|
115
|
+
};
|
|
116
|
+
/** Everything recorded for one HTTP request. */
|
|
117
|
+
type DevtoolsRequest = {
|
|
118
|
+
/** The framework's `request.id`. */id: string;
|
|
119
|
+
method: string;
|
|
120
|
+
path: string; /** Matched route pattern, e.g. `/posts/:slug`. */
|
|
121
|
+
route?: string;
|
|
122
|
+
status?: number;
|
|
123
|
+
startedAt: number; /** Undefined while the request is still in flight. */
|
|
124
|
+
durationMs?: number;
|
|
125
|
+
error?: string;
|
|
126
|
+
phases: DevtoolsPhase[];
|
|
127
|
+
queries: DevtoolsQuery[];
|
|
128
|
+
cache: DevtoolsCacheOperation[];
|
|
129
|
+
logs: DevtoolsLogEntry[]; /** Ids of mails captured during this request (see `DevtoolsMail`). */
|
|
130
|
+
mails: string[];
|
|
131
|
+
warnings: DevtoolsWarning[];
|
|
132
|
+
};
|
|
133
|
+
/** The row the request list shows; `GET <path>/api/requests` returns these. */
|
|
134
|
+
type DevtoolsRequestSummary = {
|
|
135
|
+
id: string;
|
|
136
|
+
method: string;
|
|
137
|
+
path: string;
|
|
138
|
+
route?: string;
|
|
139
|
+
status?: number;
|
|
140
|
+
startedAt: number;
|
|
141
|
+
durationMs?: number;
|
|
142
|
+
queryCount: number;
|
|
143
|
+
warningCount: number;
|
|
144
|
+
};
|
|
145
|
+
/** The row the mailbox list shows; `GET <path>/api/mails` returns these. */
|
|
146
|
+
type DevtoolsMailSummary = {
|
|
147
|
+
id: string;
|
|
148
|
+
requestId?: string;
|
|
149
|
+
capturedAt: number;
|
|
150
|
+
to: string[];
|
|
151
|
+
subject: string;
|
|
152
|
+
};
|
|
153
|
+
/**
|
|
154
|
+
* Work that happened outside any request (boot, a scheduler tick, a queue
|
|
155
|
+
* job): kept separately so it never pollutes a request's timeline.
|
|
156
|
+
*/
|
|
157
|
+
type DevtoolsBackground = {
|
|
158
|
+
queries: DevtoolsQuery[];
|
|
159
|
+
cache: DevtoolsCacheOperation[];
|
|
160
|
+
logs: DevtoolsLogEntry[];
|
|
161
|
+
};
|
|
162
|
+
/** Server-sent events on `GET <path>/api/stream`. */
|
|
163
|
+
type DevtoolsStreamEvent = {
|
|
164
|
+
type: "request";
|
|
165
|
+
data: DevtoolsRequestSummary;
|
|
166
|
+
} | {
|
|
167
|
+
type: "mail";
|
|
168
|
+
data: DevtoolsMailSummary;
|
|
169
|
+
} | {
|
|
170
|
+
type: "log";
|
|
171
|
+
data: DevtoolsLogEntry;
|
|
172
|
+
};
|
|
173
|
+
/** Unsubscribes a listener or detaches a source. */
|
|
174
|
+
type DevtoolsDisposer = () => void;
|
|
175
|
+
/**
|
|
176
|
+
* The in-memory store every source writes to and the dashboard API reads
|
|
177
|
+
* from. `requestId` is the framework's `request.id`; `undefined` means the
|
|
178
|
+
* work happened outside a request and goes to the background bucket.
|
|
179
|
+
*/
|
|
180
|
+
type DevtoolsCollector = {
|
|
181
|
+
startRequest(request: {
|
|
182
|
+
id: string;
|
|
183
|
+
method: string;
|
|
184
|
+
path: string;
|
|
185
|
+
route?: string;
|
|
186
|
+
startedAt: number;
|
|
187
|
+
}): void; /** Settles the request, runs N+1 detection and emits a `request` stream event. */
|
|
188
|
+
endRequest(id: string, result: {
|
|
189
|
+
status?: number;
|
|
190
|
+
durationMs: number;
|
|
191
|
+
error?: string;
|
|
192
|
+
route?: string;
|
|
193
|
+
}): void;
|
|
194
|
+
addPhase(requestId: string, phase: DevtoolsPhase): void; /** Computes `id` and `shape` itself. */
|
|
195
|
+
addQuery(requestId: string | undefined, query: Omit<DevtoolsQuery, "id" | "shape">): DevtoolsQuery;
|
|
196
|
+
addCacheOperation(requestId: string | undefined, operation: DevtoolsCacheOperation): void;
|
|
197
|
+
addLog(requestId: string | undefined, entry: DevtoolsLogEntry): void;
|
|
198
|
+
addMail(mail: DevtoolsMail): void;
|
|
199
|
+
listRequests(): DevtoolsRequestSummary[];
|
|
200
|
+
getRequest(id: string): DevtoolsRequest | undefined;
|
|
201
|
+
listMails(): DevtoolsMailSummary[];
|
|
202
|
+
getMail(id: string): DevtoolsMail | undefined;
|
|
203
|
+
listLogs(filter?: {
|
|
204
|
+
level?: DevtoolsLogEntry["level"];
|
|
205
|
+
module?: string;
|
|
206
|
+
}): DevtoolsLogEntry[];
|
|
207
|
+
getBackground(): DevtoolsBackground;
|
|
208
|
+
subscribe(listener: (event: DevtoolsStreamEvent) => void): DevtoolsDisposer;
|
|
209
|
+
clear(): void;
|
|
210
|
+
};
|
|
211
|
+
//#endregion
|
|
212
|
+
export { DevtoolsBackground, DevtoolsCacheOperation, DevtoolsCallerLocation, DevtoolsCollector, DevtoolsDisposer, DevtoolsLogEntry, DevtoolsMail, DevtoolsMailAttachment, DevtoolsMailSummary, DevtoolsOptions, DevtoolsPhase, DevtoolsQuery, DevtoolsRequest, DevtoolsRequestSummary, DevtoolsStreamEvent, DevtoolsWarning, ResolvedDevtoolsOptions };
|
|
213
|
+
//# sourceMappingURL=types.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../devtools/src/types.ts"],"mappings":";;AASA;;;;;;;KAAY,eAAA;EAmBO;AAInB;;;;EAjBE,IAAA;EAoBU;;;;;EAdV,WAAA;EAkBA;;AAAM;AAIR;;;EAfE,iBAAA;AAAA;;KAIU,uBAAA,GAA0B,QAAQ,CAAC,eAAA;;KAGnC,sBAAA;EAYI,sCAVd,IAAA;EACA,IAAA;EACA,MAAA;AAAA;;KAIU,aAAA;EACV,IAAA;EACA,SAAA;EACA,UAAA;EACA,KAAA,GAAQ,MAAM;AAAA;;KAIJ,aAAA;EAWV,mCATA,EAAA;EACA,UAAA;EACA,MAAA;EACA,GAAA,WAkBA;EAhBA,QAAA;EACA,UAAA;EACA,OAAA;EACA,QAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA,WAeA;EAbA,KAAA;EAeA;;;;;EATA,KAAA,UAaU;EAXV,MAAA,GAAS,sBAAsB;AAAA;;KAIrB,sBAAA;EACV,IAAA;EACA,MAAA;EACA,GAAA;EACA,IAAA;EACA,IAAA;EACA,EAAA;EACA,UAAA;AAAA;AAeF;AAAA,KAXY,gBAAA;EACV,KAAA;EACA,MAAA;EACA,MAAA;EACA,OAAA;EACA,EAAA,UAUA;EARA,OAAA;AAAA;;KAIU,YAAA;EACV,EAAA;EACA,SAAA;EACA,UAAA;EACA,IAAA;EACA,EAAA;EACA,EAAA;EACA,GAAA;EACA,OAAA;EACA,IAAA;EACA,IAAA;EACA,OAAA,EAAS,MAAA;EACT,WAAA,EAAa,sBAAsB;AAAA;;KAIzB,sBAAA;EACV,QAAA;EACA,WAAA;EACA,IAAA;AAAA;;KAIU,eAAA;EACV,IAAA;EACA,OAAA;EACA,KAAA;EACA,KAAA;EACA,MAAA,GAAS,sBAAsB;AAAA;;KAIrB,eAAA;EAAA,oCAEV,EAAA;EACA,MAAA;EACA,IAAA,UASS;EAPT,KAAA;EACA,MAAA;EACA,SAAA,UAUyB;EARzB,UAAA;EACA,KAAA;EACA,MAAA,EAAQ,aAAA;EACR,OAAA,EAAS,aAAA;EACT,KAAA,EAAO,sBAAA;EACP,IAAA,EAAM,gBAAA,IAPN;EASA,KAAA;EACA,QAAA,EAAU,eAAA;AAAA;;KAIA,sBAAA;EACV,EAAA;EACA,MAAA;EACA,IAAA;EACA,KAAA;EACA,MAAA;EACA,SAAA;EACA,UAAA;EACA,UAAA;EACA,YAAA;AAAA;AATF;AAAA,KAaY,mBAAA;EACV,EAAA;EACA,SAAA;EACA,UAAA;EACA,EAAA;EACA,OAAA;AAAA;;;;;KAOU,kBAAA;EACV,OAAA,EAAS,aAAA;EACT,KAAA,EAAO,sBAAA;EACP,IAAA,EAAM,gBAAA;AAAA;;KAII,mBAAA;EACN,IAAA;EAAiB,IAAA,EAAM,sBAAA;AAAA;EACvB,IAAA;EAAc,IAAA,EAAM,mBAAA;AAAA;EACpB,IAAA;EAAa,IAAA,EAAM,gBAAA;AAAA;;KAGb,gBAAA;;;;;;KAOA,iBAAA;EACV,YAAA,CAAa,OAAA;IACX,EAAA;IACA,MAAA;IACA,IAAA;IACA,KAAA;IACA,SAAA;EAAA,UAnB2B;EAsB7B,UAAA,CACE,EAAA,UACA,MAAA;IAAU,MAAA;IAAiB,UAAA;IAAoB,KAAA;IAAgB,KAAA;EAAA;EAEjE,QAAA,CAAS,SAAA,UAAmB,KAAA,EAAO,aAAA,SAzBd;EA2BrB,QAAA,CACE,SAAA,sBACA,KAAA,EAAO,IAAA,CAAK,aAAA,oBACX,aAAA;EACH,iBAAA,CAAkB,SAAA,sBAA+B,SAAA,EAAW,sBAAA;EAC5D,MAAA,CAAO,SAAA,sBAA+B,KAAA,EAAO,gBAAA;EAC7C,OAAA,CAAQ,IAAA,EAAM,YAAA;EACd,YAAA,IAAgB,sBAAA;EAChB,UAAA,CAAW,EAAA,WAAa,eAAA;EACxB,SAAA,IAAa,mBAAA;EACb,OAAA,CAAQ,EAAA,WAAa,YAAA;EACrB,QAAA,CAAS,MAAA;IAAW,KAAA,GAAQ,gBAAA;IAA2B,MAAA;EAAA,IAAoB,gBAAA;EAC3E,aAAA,IAAiB,kBAAA;EACjB,SAAA,CAAU,QAAA,GAAW,KAAA,EAAO,mBAAA,YAA+B,gBAAA;EAC3D,KAAA;AAAA"}
|
package/esm/ui-root.mjs
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
//#region ../devtools/src/ui-root.ts
|
|
6
|
+
let cachedUiRoot;
|
|
7
|
+
/** Whether `dir` is this package's root: it has `ui/index.html` beside a `package.json` named `@warlock.js/devtools`. */
|
|
8
|
+
function isPackageRoot(dir) {
|
|
9
|
+
const indexHtml = path.join(dir, "ui", "index.html");
|
|
10
|
+
const packageJson = path.join(dir, "package.json");
|
|
11
|
+
if (!existsSync(indexHtml) || !existsSync(packageJson)) return false;
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(readFileSync(packageJson, "utf8")).name === "@warlock.js/devtools";
|
|
14
|
+
} catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Locate this package's `ui/` directory by walking up from the current
|
|
20
|
+
* module's directory. Works from `src/` in development and from the built
|
|
21
|
+
* `esm/`/`cjs/` output, since both sit beside `ui/` and `package.json` at
|
|
22
|
+
* the package root.
|
|
23
|
+
*/
|
|
24
|
+
function resolveUiRoot() {
|
|
25
|
+
if (cachedUiRoot) return cachedUiRoot;
|
|
26
|
+
let dir = path.dirname(fileURLToPath(import.meta.url));
|
|
27
|
+
while (true) {
|
|
28
|
+
if (isPackageRoot(dir)) {
|
|
29
|
+
cachedUiRoot = path.join(dir, "ui");
|
|
30
|
+
return cachedUiRoot;
|
|
31
|
+
}
|
|
32
|
+
const parent = path.dirname(dir);
|
|
33
|
+
if (parent === dir) throw new Error("Could not locate the devtools UI directory: no ancestor of this module has a ui/index.html beside a package.json named @warlock.js/devtools.");
|
|
34
|
+
dir = parent;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
//#endregion
|
|
39
|
+
export { resolveUiRoot };
|
|
40
|
+
//# sourceMappingURL=ui-root.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-root.mjs","names":[],"sources":["../../../../../../devtools/src/ui-root.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nlet cachedUiRoot: string | undefined;\n\n/** Whether `dir` is this package's root: it has `ui/index.html` beside a `package.json` named `@warlock.js/devtools`. */\nfunction isPackageRoot(dir: string): boolean {\n const indexHtml = path.join(dir, \"ui\", \"index.html\");\n const packageJson = path.join(dir, \"package.json\");\n\n if (!existsSync(indexHtml) || !existsSync(packageJson)) {\n return false;\n }\n\n try {\n const parsed = JSON.parse(readFileSync(packageJson, \"utf8\")) as { name?: string };\n return parsed.name === \"@warlock.js/devtools\";\n } catch {\n return false;\n }\n}\n\n/**\n * Locate this package's `ui/` directory by walking up from the current\n * module's directory. Works from `src/` in development and from the built\n * `esm/`/`cjs/` output, since both sit beside `ui/` and `package.json` at\n * the package root.\n */\nexport function resolveUiRoot(): string {\n if (cachedUiRoot) {\n return cachedUiRoot;\n }\n\n let dir = path.dirname(fileURLToPath(import.meta.url));\n\n while (true) {\n if (isPackageRoot(dir)) {\n cachedUiRoot = path.join(dir, \"ui\");\n return cachedUiRoot;\n }\n\n const parent = path.dirname(dir);\n if (parent === dir) {\n throw new Error(\n \"Could not locate the devtools UI directory: no ancestor of \" +\n \"this module has a ui/index.html beside a package.json named \" +\n \"@warlock.js/devtools.\",\n );\n }\n\n dir = parent;\n }\n}\n"],"mappings":";;;;;AAIA,IAAI;;AAGJ,SAAS,cAAc,KAAsB;CAC3C,MAAM,YAAY,KAAK,KAAK,KAAK,MAAM,YAAY;CACnD,MAAM,cAAc,KAAK,KAAK,KAAK,cAAc;CAEjD,IAAI,CAAC,WAAW,SAAS,KAAK,CAAC,WAAW,WAAW,GACnD,OAAO;CAGT,IAAI;EAEF,OADe,KAAK,MAAM,aAAa,aAAa,MAAM,CAC9C,CAAC,CAAC,SAAS;CACzB,QAAQ;EACN,OAAO;CACT;AACF;;;;;;;AAQA,SAAgB,gBAAwB;CACtC,IAAI,cACF,OAAO;CAGT,IAAI,MAAM,KAAK,QAAQ,cAAc,OAAO,KAAK,GAAG,CAAC;CAErD,OAAO,MAAM;EACX,IAAI,cAAc,GAAG,GAAG;GACtB,eAAe,KAAK,KAAK,KAAK,IAAI;GAClC,OAAO;EACT;EAEA,MAAM,SAAS,KAAK,QAAQ,GAAG;EAC/B,IAAI,WAAW,KACb,MAAM,IAAI,MACR,8IAGF;EAGF,MAAM;CACR;AACF"}
|
package/llms-full.txt
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Warlock Devtools — full skills
|
|
2
|
+
|
|
3
|
+
> Package: `@warlock.js/devtools`
|
|
4
|
+
|
|
5
|
+
> Generated artifact. Concatenates every SKILL.md and reference file under `@warlock.js/devtools/skills/`. Re-run `node scripts/generate-llms.mjs` after any change.
|
|
6
|
+
|
|
7
|
+
## devtools-overview `@warlock.js/devtools/devtools-overview/SKILL.md`
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
name: devtools-overview
|
|
11
|
+
description: 'Development-only dashboard for Warlock.js apps at `/__warlock`: request timeline (router + page middleware, loaders, render, deferred values), every SQL/Mongo query with EXPLAIN, N+1 warnings naming the file and line, a dev mailbox, logs, cache operations and the route map. Use for: "why is this page slow", "which queries does this request run", "find N+1 queries", "see the email my app just sent in dev", "open the dashboard". Install with `warlock add devtools`; never registered in warlock.config.ts. Not this package: production monitoring/APM or OpenTelemetry export — use `http.tracing` hooks in @warlock.js/core; slow-query logging in production — subscribe to cascade `dataSourceRegistry.on("query")` yourself.'
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# `@warlock.js/devtools` — overview
|
|
15
|
+
|
|
16
|
+
A dashboard for **development only**, served by your own app at `/__warlock`.
|
|
17
|
+
It shows what each request did: its timing waterfall, the queries it ran, the
|
|
18
|
+
cache operations, the logs, and any mail it sent.
|
|
19
|
+
|
|
20
|
+
## Install: one command, no config
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
warlock add devtools # adds @warlock.js/devtools as a DEV dependency
|
|
24
|
+
warlock dev # open http://localhost:<port>/__warlock
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
There's nothing to register. `warlock dev` loads devtools automatically when
|
|
28
|
+
the package is installed.
|
|
29
|
+
|
|
30
|
+
**Do not** add `devtoolsConnector()` to `warlock.config.ts`. Devtools is a
|
|
31
|
+
dev dependency, so a production install doesn't have it, and a config import
|
|
32
|
+
of it would fail to resolve there.
|
|
33
|
+
|
|
34
|
+
## Why it can't leak into production
|
|
35
|
+
|
|
36
|
+
1. **It isn't installed there.** Production installs omit dev dependencies.
|
|
37
|
+
2. **It refuses to run outside development.** If it's present anyway, it logs
|
|
38
|
+
one warning (`@warlock.js/devtools only runs in development; it is not mounted`)
|
|
39
|
+
and mounts nothing.
|
|
40
|
+
3. **It only answers loopback clients.** Every dashboard and API route answers
|
|
41
|
+
404 unless the connection comes from `127.0.0.1` / `::1`. It checks the
|
|
42
|
+
socket address, not `X-Forwarded-For`, so a forwarded-for header can't
|
|
43
|
+
unlock it.
|
|
44
|
+
|
|
45
|
+
## What each panel shows
|
|
46
|
+
|
|
47
|
+
| Panel | What you see | Where it comes from |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| **Requests** | Each request with its status, duration and query count. Click one for a waterfall: router middleware, `page.middleware`, loaders, `page.cache` hit/miss, `render.shell`, `defer.settle`, with queries and cache ops placed on the same time axis. | core `registerTracingHooks()` |
|
|
50
|
+
| **Queries** | SQL + bindings or the Mongo command, duration, rows, and an **Explain** button (Postgres `SELECT` only; runs `EXPLAIN (FORMAT JSON)`, never `ANALYZE`) | cascade `dataSourceRegistry.on("query")` |
|
|
51
|
+
| **N+1** | `⚠ N+1: 12× "select … where post_id = $1" — src/app/posts/services/list-posts.service.ts:18`, shown on the request and printed in the terminal | same query shape ≥ 5 times in one request |
|
|
52
|
+
| **Mail** | Every mail sent in development: headers, HTML (sandboxed) and text, attachment names | core `mailEvents.onCaptured()` |
|
|
53
|
+
| **Logs** | Log entries per request, filterable by level and module | a logger channel |
|
|
54
|
+
| **Cache** | hit / miss / set / removed / `invalidated` (tags, keys removed) | cache `cache.on(...)` |
|
|
55
|
+
| **Routes** | Every HTTP route with its method, path, name and middleware | core router |
|
|
56
|
+
|
|
57
|
+
Work outside a request (boot, scheduler ticks, queue jobs) goes to a separate
|
|
58
|
+
background list, so it never clutters a request's timeline.
|
|
59
|
+
|
|
60
|
+
## Options
|
|
61
|
+
|
|
62
|
+
`warlock dev` uses the defaults: path `/__warlock`, the last 200 requests
|
|
63
|
+
kept, and an N+1 threshold of 5. Everything lives in memory and is gone on
|
|
64
|
+
restart.
|
|
65
|
+
|
|
66
|
+
## Secrets
|
|
67
|
+
|
|
68
|
+
Query bindings and log context go through the logger's redaction rules
|
|
69
|
+
before they're stored. The data is still real request data, which is exactly
|
|
70
|
+
why the dashboard is development-only and loopback-only.
|
|
71
|
+
|
|
72
|
+
## Related
|
|
73
|
+
|
|
74
|
+
- Building your own tooling on the same hooks: core `registerTracingHooks()`,
|
|
75
|
+
cascade `dataSourceRegistry.on("query")`, core `mailEvents.onCaptured()` and
|
|
76
|
+
the cache `invalidated` event are public APIs that also work without devtools.
|
|
77
|
+
|
|
78
|
+
|
package/llms.txt
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Warlock Devtools
|
|
2
|
+
|
|
3
|
+
> Package: `@warlock.js/devtools`
|
|
4
|
+
|
|
5
|
+
> Development-only dashboard for Warlock.js apps at /__warlock: request timeline, queries with EXPLAIN, N+1 detection, dev mailbox, logs, cache operations and the route map. Refuses to run outside development.
|
|
6
|
+
|
|
7
|
+
## Skills
|
|
8
|
+
|
|
9
|
+
- [devtools-overview](@warlock.js/devtools/devtools-overview/SKILL.md): Development-only dashboard for Warlock.js apps at `/__warlock`: request timeline (router + page middleware, loaders, render, deferred values), every SQL/Mongo query with EXPLAIN, N+1 warnings naming the file and line, a dev mailbox, logs, cache operations and the route map. Use for: "why is this page slow", "which queries does this request run", "find N+1 queries", "see the email my app just sent in dev", "open the dashboard". Install with `warlock add devtools`; never registered in warlock.config.ts. Not this package: production monitoring/APM or OpenTelemetry export — use `http.tracing` hooks in @warlock.js/core; slow-query logging in production — subscribe to cascade `dataSourceRegistry.on("query")` yourself.
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@warlock.js/devtools",
|
|
3
|
+
"description": "Development-only dashboard for Warlock.js apps at /__warlock: request timeline, queries with EXPLAIN, N+1 detection, dev mailbox, logs, cache operations and the route map. Refuses to run outside development.",
|
|
4
|
+
"warlock": {
|
|
5
|
+
"environment": "server"
|
|
6
|
+
},
|
|
7
|
+
"peerDependencies": {
|
|
8
|
+
"@warlock.js/cache": "5.25.0",
|
|
9
|
+
"@warlock.js/cascade": "5.25.0",
|
|
10
|
+
"@warlock.js/core": "5.25.0",
|
|
11
|
+
"@warlock.js/logger": "5.25.0"
|
|
12
|
+
},
|
|
13
|
+
"peerDependenciesMeta": {
|
|
14
|
+
"@warlock.js/cache": {
|
|
15
|
+
"optional": true
|
|
16
|
+
},
|
|
17
|
+
"@warlock.js/cascade": {
|
|
18
|
+
"optional": true
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/warlockjs/devtools"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"warlock.js",
|
|
27
|
+
"devtools",
|
|
28
|
+
"debugging",
|
|
29
|
+
"profiler",
|
|
30
|
+
"n+1",
|
|
31
|
+
"timeline",
|
|
32
|
+
"mailbox"
|
|
33
|
+
],
|
|
34
|
+
"author": "hassanzohdy",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"version": "5.25.0",
|
|
37
|
+
"main": "./cjs/index.cjs",
|
|
38
|
+
"module": "./esm/index.mjs",
|
|
39
|
+
"types": "./esm/index.d.mts",
|
|
40
|
+
"exports": {
|
|
41
|
+
".": {
|
|
42
|
+
"import": {
|
|
43
|
+
"types": "./esm/index.d.mts",
|
|
44
|
+
"default": "./esm/index.mjs"
|
|
45
|
+
},
|
|
46
|
+
"require": {
|
|
47
|
+
"types": "./esm/index.d.mts",
|
|
48
|
+
"default": "./cjs/index.cjs"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: devtools-overview
|
|
3
|
+
description: 'Development-only dashboard for Warlock.js apps at `/__warlock`: request timeline (router + page middleware, loaders, render, deferred values), every SQL/Mongo query with EXPLAIN, N+1 warnings naming the file and line, a dev mailbox, logs, cache operations and the route map. Use for: "why is this page slow", "which queries does this request run", "find N+1 queries", "see the email my app just sent in dev", "open the dashboard". Install with `warlock add devtools`; never registered in warlock.config.ts. Not this package: production monitoring/APM or OpenTelemetry export — use `http.tracing` hooks in @warlock.js/core; slow-query logging in production — subscribe to cascade `dataSourceRegistry.on("query")` yourself.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# `@warlock.js/devtools` — overview
|
|
7
|
+
|
|
8
|
+
A dashboard for **development only**, served by your own app at `/__warlock`.
|
|
9
|
+
It shows what each request did: its timing waterfall, the queries it ran, the
|
|
10
|
+
cache operations, the logs, and any mail it sent.
|
|
11
|
+
|
|
12
|
+
## Install: one command, no config
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
warlock add devtools # adds @warlock.js/devtools as a DEV dependency
|
|
16
|
+
warlock dev # open http://localhost:<port>/__warlock
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
There's nothing to register. `warlock dev` loads devtools automatically when
|
|
20
|
+
the package is installed.
|
|
21
|
+
|
|
22
|
+
**Do not** add `devtoolsConnector()` to `warlock.config.ts`. Devtools is a
|
|
23
|
+
dev dependency, so a production install doesn't have it, and a config import
|
|
24
|
+
of it would fail to resolve there.
|
|
25
|
+
|
|
26
|
+
## Why it can't leak into production
|
|
27
|
+
|
|
28
|
+
1. **It isn't installed there.** Production installs omit dev dependencies.
|
|
29
|
+
2. **It refuses to run outside development.** If it's present anyway, it logs
|
|
30
|
+
one warning (`@warlock.js/devtools only runs in development; it is not mounted`)
|
|
31
|
+
and mounts nothing.
|
|
32
|
+
3. **It only answers loopback clients.** Every dashboard and API route answers
|
|
33
|
+
404 unless the connection comes from `127.0.0.1` / `::1`. It checks the
|
|
34
|
+
socket address, not `X-Forwarded-For`, so a forwarded-for header can't
|
|
35
|
+
unlock it.
|
|
36
|
+
|
|
37
|
+
## What each panel shows
|
|
38
|
+
|
|
39
|
+
| Panel | What you see | Where it comes from |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| **Requests** | Each request with its status, duration and query count. Click one for a waterfall: router middleware, `page.middleware`, loaders, `page.cache` hit/miss, `render.shell`, `defer.settle`, with queries and cache ops placed on the same time axis. | core `registerTracingHooks()` |
|
|
42
|
+
| **Queries** | SQL + bindings or the Mongo command, duration, rows, and an **Explain** button (Postgres `SELECT` only; runs `EXPLAIN (FORMAT JSON)`, never `ANALYZE`) | cascade `dataSourceRegistry.on("query")` |
|
|
43
|
+
| **N+1** | `⚠ N+1: 12× "select … where post_id = $1" — src/app/posts/services/list-posts.service.ts:18`, shown on the request and printed in the terminal | same query shape ≥ 5 times in one request |
|
|
44
|
+
| **Mail** | Every mail sent in development: headers, HTML (sandboxed) and text, attachment names | core `mailEvents.onCaptured()` |
|
|
45
|
+
| **Logs** | Log entries per request, filterable by level and module | a logger channel |
|
|
46
|
+
| **Cache** | hit / miss / set / removed / `invalidated` (tags, keys removed) | cache `cache.on(...)` |
|
|
47
|
+
| **Routes** | Every HTTP route with its method, path, name and middleware | core router |
|
|
48
|
+
|
|
49
|
+
Work outside a request (boot, scheduler ticks, queue jobs) goes to a separate
|
|
50
|
+
background list, so it never clutters a request's timeline.
|
|
51
|
+
|
|
52
|
+
## Options
|
|
53
|
+
|
|
54
|
+
`warlock dev` uses the defaults: path `/__warlock`, the last 200 requests
|
|
55
|
+
kept, and an N+1 threshold of 5. Everything lives in memory and is gone on
|
|
56
|
+
restart.
|
|
57
|
+
|
|
58
|
+
## Secrets
|
|
59
|
+
|
|
60
|
+
Query bindings and log context go through the logger's redaction rules
|
|
61
|
+
before they're stored. The data is still real request data, which is exactly
|
|
62
|
+
why the dashboard is development-only and loopback-only.
|
|
63
|
+
|
|
64
|
+
## Related
|
|
65
|
+
|
|
66
|
+
- Building your own tooling on the same hooks: core `registerTracingHooks()`,
|
|
67
|
+
cascade `dataSourceRegistry.on("query")`, core `mailEvents.onCaptured()` and
|
|
68
|
+
the cache `invalidated` event are public APIs that also work without devtools.
|