@c9up/ream 0.2.25 → 0.2.27
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/README.md +2 -0
- package/dist/Codemods.d.ts +12 -0
- package/dist/Codemods.d.ts.map +1 -1
- package/dist/Codemods.js +114 -23
- package/dist/Codemods.js.map +1 -1
- package/dist/HttpKernel.d.ts +5 -1
- package/dist/HttpKernel.d.ts.map +1 -1
- package/dist/HttpKernel.js +6 -6
- package/dist/HttpKernel.js.map +1 -1
- package/dist/Ignitor.d.ts.map +1 -1
- package/dist/Ignitor.js +87 -14
- package/dist/Ignitor.js.map +1 -1
- package/dist/console/Kernel.d.ts.map +1 -1
- package/dist/console/Kernel.js +20 -1
- package/dist/console/Kernel.js.map +1 -1
- package/dist/console/cliui.d.ts +9 -245
- package/dist/console/cliui.d.ts.map +1 -1
- package/dist/console/cliui.js +9 -583
- package/dist/console/cliui.js.map +1 -1
- package/dist/console/make/commands.d.ts +19 -0
- package/dist/console/make/commands.d.ts.map +1 -0
- package/dist/console/make/commands.js +252 -0
- package/dist/console/make/commands.js.map +1 -0
- package/dist/console/make/eject.d.ts +28 -0
- package/dist/console/make/eject.d.ts.map +1 -0
- package/dist/console/make/eject.js +161 -0
- package/dist/console/make/eject.js.map +1 -0
- package/dist/console/make/flush.d.ts +51 -0
- package/dist/console/make/flush.d.ts.map +1 -0
- package/dist/console/make/flush.js +93 -0
- package/dist/console/make/flush.js.map +1 -0
- package/dist/console/make/generators.d.ts +36 -0
- package/dist/console/make/generators.d.ts.map +1 -0
- package/dist/console/make/generators.js +148 -0
- package/dist/console/make/generators.js.map +1 -0
- package/dist/console/make/naming.d.ts +32 -0
- package/dist/console/make/naming.d.ts.map +1 -0
- package/dist/console/make/naming.js +82 -0
- package/dist/console/make/naming.js.map +1 -0
- package/dist/console/make/resolve.d.ts +46 -0
- package/dist/console/make/resolve.d.ts.map +1 -0
- package/dist/console/make/resolve.js +113 -0
- package/dist/console/make/resolve.js.map +1 -0
- package/dist/console/prompts.d.ts.map +1 -1
- package/dist/console/prompts.js +34 -46
- package/dist/console/prompts.js.map +1 -1
- package/dist/console/selection.d.ts +54 -0
- package/dist/console/selection.d.ts.map +1 -0
- package/dist/console/selection.js +235 -0
- package/dist/console/selection.js.map +1 -0
- package/dist/console/ui.d.ts +9 -15
- package/dist/console/ui.d.ts.map +1 -1
- package/dist/console/ui.js +11 -24
- package/dist/console/ui.js.map +1 -1
- package/dist/dev/fullReload.d.ts +35 -10
- package/dist/dev/fullReload.d.ts.map +1 -1
- package/dist/dev/fullReload.js +55 -14
- package/dist/dev/fullReload.js.map +1 -1
- package/dist/dev/hotWatcher.d.ts +19 -3
- package/dist/dev/hotWatcher.d.ts.map +1 -1
- package/dist/dev/hotWatcher.js +168 -18
- package/dist/dev/hotWatcher.js.map +1 -1
- package/dist/dev/readyBanner.d.ts +45 -0
- package/dist/dev/readyBanner.d.ts.map +1 -0
- package/dist/dev/readyBanner.js +72 -0
- package/dist/dev/readyBanner.js.map +1 -0
- package/dist/dev/shortcuts.d.ts +41 -0
- package/dist/dev/shortcuts.d.ts.map +1 -0
- package/dist/dev/shortcuts.js +142 -0
- package/dist/dev/shortcuts.js.map +1 -0
- package/dist/errors/prettyPrintError.d.ts.map +1 -1
- package/dist/errors/prettyPrintError.js +5 -8
- package/dist/errors/prettyPrintError.js.map +1 -1
- package/dist/errors/renderError.d.ts +46 -0
- package/dist/errors/renderError.d.ts.map +1 -0
- package/dist/errors/renderError.js +197 -0
- package/dist/errors/renderError.js.map +1 -0
- package/dist/hot.js +12 -2
- package/dist/hot.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/profiler/MemoryProfileStore.d.ts +23 -0
- package/dist/profiler/MemoryProfileStore.d.ts.map +1 -0
- package/dist/profiler/MemoryProfileStore.js +70 -0
- package/dist/profiler/MemoryProfileStore.js.map +1 -0
- package/dist/profiler/Profiler.d.ts +56 -0
- package/dist/profiler/Profiler.d.ts.map +1 -0
- package/dist/profiler/Profiler.js +162 -0
- package/dist/profiler/Profiler.js.map +1 -0
- package/dist/profiler/collectors.d.ts +105 -0
- package/dist/profiler/collectors.d.ts.map +1 -0
- package/dist/profiler/collectors.js +164 -0
- package/dist/profiler/collectors.js.map +1 -0
- package/dist/profiler/endpoint.d.ts +27 -0
- package/dist/profiler/endpoint.d.ts.map +1 -0
- package/dist/profiler/endpoint.js +79 -0
- package/dist/profiler/endpoint.js.map +1 -0
- package/dist/profiler/index.d.ts +26 -0
- package/dist/profiler/index.d.ts.map +1 -0
- package/dist/profiler/index.js +41 -0
- package/dist/profiler/index.js.map +1 -0
- package/dist/profiler/middleware.d.ts +17 -0
- package/dist/profiler/middleware.d.ts.map +1 -0
- package/dist/profiler/middleware.js +79 -0
- package/dist/profiler/middleware.js.map +1 -0
- package/dist/profiler/render.d.ts +23 -0
- package/dist/profiler/render.d.ts.map +1 -0
- package/dist/profiler/render.js +141 -0
- package/dist/profiler/render.js.map +1 -0
- package/dist/profiler/toolbar.d.ts +34 -0
- package/dist/profiler/toolbar.d.ts.map +1 -0
- package/dist/profiler/toolbar.js +104 -0
- package/dist/profiler/toolbar.js.map +1 -0
- package/dist/profiler/types.d.ts +70 -0
- package/dist/profiler/types.d.ts.map +1 -0
- package/dist/profiler/types.js +11 -0
- package/dist/profiler/types.js.map +1 -0
- package/dist/router/Router.d.ts +11 -0
- package/dist/router/Router.d.ts.map +1 -1
- package/dist/router/Router.js +16 -0
- package/dist/router/Router.js.map +1 -1
- package/dist/stubs/index.d.ts +11 -0
- package/dist/stubs/index.d.ts.map +1 -0
- package/dist/stubs/index.js +12 -0
- package/dist/stubs/index.js.map +1 -0
- package/dist/stubs/template.d.ts +63 -0
- package/dist/stubs/template.d.ts.map +1 -0
- package/dist/stubs/template.js +209 -0
- package/dist/stubs/template.js.map +1 -0
- package/events.win32-x64-msvc.node +0 -0
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +7 -1
- package/scheduler.win32-x64-msvc.node +0 -0
- package/src/Codemods.ts +153 -29
- package/src/HttpKernel.ts +12 -8
- package/src/Ignitor.ts +95 -14
- package/src/console/Kernel.ts +21 -1
- package/src/console/cliui.ts +44 -754
- package/src/console/make/commands.ts +270 -0
- package/src/console/make/eject.ts +163 -0
- package/src/console/make/flush.ts +125 -0
- package/src/console/make/generators.ts +176 -0
- package/src/console/make/naming.ts +85 -0
- package/src/console/make/resolve.ts +134 -0
- package/src/console/prompts.ts +45 -64
- package/src/console/selection.ts +274 -0
- package/src/console/ui.ts +12 -24
- package/src/dev/fullReload.ts +57 -15
- package/src/dev/hotWatcher.ts +165 -21
- package/src/dev/readyBanner.ts +84 -0
- package/src/dev/shortcuts.ts +158 -0
- package/src/errors/prettyPrintError.ts +5 -5
- package/src/errors/renderError.ts +230 -0
- package/src/hot.ts +12 -2
- package/src/index.ts +23 -3
- package/src/profiler/MemoryProfileStore.ts +75 -0
- package/src/profiler/Profiler.ts +182 -0
- package/src/profiler/collectors.ts +230 -0
- package/src/profiler/endpoint.ts +98 -0
- package/src/profiler/index.ts +56 -0
- package/src/profiler/middleware.ts +83 -0
- package/src/profiler/render.ts +173 -0
- package/src/profiler/toolbar.ts +106 -0
- package/src/profiler/types.ts +75 -0
- package/src/router/Router.ts +16 -0
- package/src/stubs/index.ts +14 -0
- package/src/stubs/template.ts +262 -0
- package/stubs/make/command.stub +29 -0
- package/stubs/make/controller.stub +30 -0
- package/stubs/make/entity.stub +11 -0
- package/stubs/make/event.stub +13 -0
- package/stubs/make/listener.stub +13 -0
- package/stubs/make/middleware.stub +16 -0
- package/stubs/make/provider.stub +26 -0
- package/stubs/make/service.stub +27 -0
- package/stubs/make/validator.stub +10 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The profiler: one profile per request, and the token that finds it again.
|
|
3
|
+
*
|
|
4
|
+
* The hard part is attribution. A SQL query is reported by a listener the
|
|
5
|
+
* database package calls, with no idea which request caused it — and in a
|
|
6
|
+
* server answering three requests at once, "the current request" is not a
|
|
7
|
+
* variable anything can hold. `AsyncLocalStorage` is what makes it one: the
|
|
8
|
+
* request runs inside a store, and whatever a collector does during it, however
|
|
9
|
+
* deep in the await chain, lands in the right bag.
|
|
10
|
+
*
|
|
11
|
+
* Dev only, and hard-gated: a profiler that kept every request in production
|
|
12
|
+
* would be a memory leak wearing a feature's name.
|
|
13
|
+
*/
|
|
14
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
15
|
+
import { randomBytes } from 'node:crypto';
|
|
16
|
+
/** Where the toolbar and the profiler page are served. */
|
|
17
|
+
export const PROFILER_PATH = '/__ream/profiler';
|
|
18
|
+
/** The header carrying the token back, so a client can ask for the profile. */
|
|
19
|
+
export const PROFILER_TOKEN_HEADER = 'x-debug-token';
|
|
20
|
+
export class Profiler {
|
|
21
|
+
#storage = new AsyncLocalStorage();
|
|
22
|
+
#collectors;
|
|
23
|
+
#store;
|
|
24
|
+
constructor(collectors, store) {
|
|
25
|
+
this.#collectors = collectors;
|
|
26
|
+
this.#store = store;
|
|
27
|
+
}
|
|
28
|
+
get store() {
|
|
29
|
+
return this.#store;
|
|
30
|
+
}
|
|
31
|
+
get collectors() {
|
|
32
|
+
return this.#collectors;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Run one request inside a profile.
|
|
36
|
+
*
|
|
37
|
+
* `work` is handed the scope so it can stamp the token on the response
|
|
38
|
+
* before anything else runs: a request that throws unwinds past its caller,
|
|
39
|
+
* and a token written after the await would be missing on exactly the
|
|
40
|
+
* request worth looking at.
|
|
41
|
+
*
|
|
42
|
+
* `describe` is called AFTER the work, because the status is only known
|
|
43
|
+
* then — asking for it up front would record the request as it arrived
|
|
44
|
+
* rather than as it was answered. It is given whatever was thrown, since a
|
|
45
|
+
* handler that threw has not had its status written yet.
|
|
46
|
+
*/
|
|
47
|
+
async profile(request, work, describe) {
|
|
48
|
+
const token = newToken();
|
|
49
|
+
const startedAt = Date.now();
|
|
50
|
+
const started = performance.now();
|
|
51
|
+
const active = { scope: { token, startedAt }, bags: new Map() };
|
|
52
|
+
for (const collector of this.#collectors) {
|
|
53
|
+
// A collector that throws while starting must not fail the request it was
|
|
54
|
+
// only meant to watch.
|
|
55
|
+
try {
|
|
56
|
+
active.bags.set(collector.name, collector.start(active.scope));
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
active.bags.set(collector.name, undefined);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// A request that threw is the profile you most want to look at, so it is
|
|
63
|
+
// recorded like any other and the error is re-thrown afterwards. Losing it
|
|
64
|
+
// here would mean the profiler goes quiet exactly when something broke.
|
|
65
|
+
let result;
|
|
66
|
+
let thrown;
|
|
67
|
+
let failed = false;
|
|
68
|
+
try {
|
|
69
|
+
result = await this.#storage.run(active, () => work(active.scope));
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
thrown = error;
|
|
73
|
+
failed = true;
|
|
74
|
+
recordException(active, error);
|
|
75
|
+
}
|
|
76
|
+
const profile = {
|
|
77
|
+
token,
|
|
78
|
+
time: startedAt,
|
|
79
|
+
method: request.method,
|
|
80
|
+
url: request.url,
|
|
81
|
+
status: safeStatus(describe, thrown),
|
|
82
|
+
duration: performance.now() - started,
|
|
83
|
+
data: {},
|
|
84
|
+
};
|
|
85
|
+
for (const collector of this.#collectors) {
|
|
86
|
+
try {
|
|
87
|
+
profile.data[collector.name] = collector.finish(active.bags.get(collector.name), active.scope);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// Same rule on the way out: one broken panel, not a broken response.
|
|
91
|
+
profile.data[collector.name] = {};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
this.#store.save(profile);
|
|
95
|
+
if (failed)
|
|
96
|
+
throw thrown;
|
|
97
|
+
// Only reachable when the work resolved, which is when `result` is set.
|
|
98
|
+
return { result: result, token };
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The bag of the collector named, for the request running right now.
|
|
102
|
+
*
|
|
103
|
+
* `undefined` outside a request — a query from a console command, a boot-time
|
|
104
|
+
* warm-up — and a collector that writes into it must accept that.
|
|
105
|
+
*/
|
|
106
|
+
current(collector) {
|
|
107
|
+
const active = this.#storage.getStore();
|
|
108
|
+
if (active === undefined)
|
|
109
|
+
return undefined;
|
|
110
|
+
const bag = active.bags.get(collector);
|
|
111
|
+
return bag === undefined ? undefined : bag;
|
|
112
|
+
}
|
|
113
|
+
/** Whether a request is being profiled right now. */
|
|
114
|
+
get active() {
|
|
115
|
+
return this.#storage.getStore() !== undefined;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Put the error where the exception panel reads it.
|
|
120
|
+
*
|
|
121
|
+
* The collector's own bag, by name: the framework ships that collector, and a
|
|
122
|
+
* profiler that could not say what broke would miss the reason most people
|
|
123
|
+
* open it.
|
|
124
|
+
*/
|
|
125
|
+
function recordException(active, error) {
|
|
126
|
+
const bag = active.bags.get('exception');
|
|
127
|
+
if (typeof bag !== 'object' || bag === null)
|
|
128
|
+
return;
|
|
129
|
+
const target = Object.assign(bag);
|
|
130
|
+
if (error instanceof Error) {
|
|
131
|
+
target.name = error.name;
|
|
132
|
+
target.message = error.message;
|
|
133
|
+
if (error.stack !== undefined)
|
|
134
|
+
target.stack = error.stack;
|
|
135
|
+
const code = Reflect.get(error, 'code');
|
|
136
|
+
if (typeof code === 'string')
|
|
137
|
+
target.code = code;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
target.name = 'Error';
|
|
141
|
+
target.message = String(error);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* A token, short enough to read aloud and random enough not to collide.
|
|
146
|
+
*
|
|
147
|
+
* Six bytes of hex, as Symfony's is six characters: it identifies a request
|
|
148
|
+
* within one dev session, not across the internet.
|
|
149
|
+
*/
|
|
150
|
+
function newToken() {
|
|
151
|
+
return randomBytes(6).toString('hex');
|
|
152
|
+
}
|
|
153
|
+
function safeStatus(describe, error) {
|
|
154
|
+
try {
|
|
155
|
+
const { status } = describe(error);
|
|
156
|
+
return Number.isInteger(status) ? status : 0;
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return 0;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=Profiler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Profiler.js","sourceRoot":"","sources":["../../src/profiler/Profiler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the profiler watches.
|
|
3
|
+
*
|
|
4
|
+
* Each one is independent and may be replaced: the database collector is the
|
|
5
|
+
* interesting case, because the data does not come from the request at all. It
|
|
6
|
+
* comes from `db:query` events the data layer emits with no idea which request
|
|
7
|
+
* caused them, and the profiler's async store is what puts each one back where
|
|
8
|
+
* it belongs.
|
|
9
|
+
*
|
|
10
|
+
* NO import of atlas. The collector listens on the application's emitter for
|
|
11
|
+
* `db:query`, which is the shape atlas documents for bridging its own registry
|
|
12
|
+
* (`onDbQuery((event) => emitter.emit('db:query', event))`). A project on
|
|
13
|
+
* another data layer emits the same event and gets the same panel.
|
|
14
|
+
*/
|
|
15
|
+
import type { CollectedData, ProfilerCollector } from './types.js';
|
|
16
|
+
/** One SQL statement, as the panel shows it. */
|
|
17
|
+
export interface RecordedQuery {
|
|
18
|
+
sql: string;
|
|
19
|
+
bindings: readonly unknown[];
|
|
20
|
+
/** Milliseconds. */
|
|
21
|
+
duration: number;
|
|
22
|
+
connection?: string;
|
|
23
|
+
model?: string;
|
|
24
|
+
method?: string;
|
|
25
|
+
inTransaction?: boolean;
|
|
26
|
+
/** The message, when the statement threw. */
|
|
27
|
+
error?: string;
|
|
28
|
+
}
|
|
29
|
+
/** What the emitter must give this collector. Structural: no package imported. */
|
|
30
|
+
export interface QueryEventSource {
|
|
31
|
+
on(event: string, listener: (payload: unknown) => void): unknown;
|
|
32
|
+
}
|
|
33
|
+
export declare function databaseCollector(events: QueryEventSource, read: () => QueryBag | undefined): ProfilerCollector<QueryBag>;
|
|
34
|
+
export interface QueryBag {
|
|
35
|
+
total: number;
|
|
36
|
+
duration: number;
|
|
37
|
+
queries: RecordedQuery[];
|
|
38
|
+
}
|
|
39
|
+
/** The request and what it answered. */
|
|
40
|
+
export declare function requestCollector(): ProfilerCollector<RequestBag>;
|
|
41
|
+
export interface RequestBag {
|
|
42
|
+
route?: string;
|
|
43
|
+
controller?: string;
|
|
44
|
+
userId?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* What went wrong, if anything.
|
|
48
|
+
*
|
|
49
|
+
* The one panel worth opening by itself: an exception that reached the handler
|
|
50
|
+
* is reported here so it shows up in the page rather than only in the terminal
|
|
51
|
+
* someone may not be looking at.
|
|
52
|
+
*/
|
|
53
|
+
export declare function exceptionCollector(): ProfilerCollector<ExceptionBag>;
|
|
54
|
+
export interface ExceptionBag {
|
|
55
|
+
name?: string;
|
|
56
|
+
message?: string;
|
|
57
|
+
stack?: string;
|
|
58
|
+
/** Where it was reported from, when the framework knows. */
|
|
59
|
+
code?: string;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Lines logged while the request ran.
|
|
63
|
+
*
|
|
64
|
+
* Same arrangement as the database panel, and for the same reason: a logger
|
|
65
|
+
* writes a line with no idea which request caused it. It comes off the
|
|
66
|
+
* application's emitter as `log:line`, which is what `@c9up/spectrum` bridges
|
|
67
|
+
* its own registry to (`onLog((entry) => emitter.emit('log:line', entry))`),
|
|
68
|
+
* so a project on another logger emits the same event and gets the same panel.
|
|
69
|
+
*/
|
|
70
|
+
export declare function logCollector(events: LogEventSource, read: () => LogBag | undefined): {
|
|
71
|
+
name: string;
|
|
72
|
+
badge: (data: CollectedData) => string | undefined;
|
|
73
|
+
start: () => {
|
|
74
|
+
total: number;
|
|
75
|
+
lines: never[];
|
|
76
|
+
};
|
|
77
|
+
finish: (bag: LogBag) => {
|
|
78
|
+
total?: undefined;
|
|
79
|
+
lines?: undefined;
|
|
80
|
+
} | {
|
|
81
|
+
total: number;
|
|
82
|
+
lines: {
|
|
83
|
+
level: string;
|
|
84
|
+
message: string;
|
|
85
|
+
time?: string;
|
|
86
|
+
}[];
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
/** What the emitter must give the log collector. Structural, as above. */
|
|
90
|
+
export interface LogEventSource {
|
|
91
|
+
on(event: string, listener: (payload: unknown) => void): unknown;
|
|
92
|
+
}
|
|
93
|
+
export interface LogBag {
|
|
94
|
+
total: number;
|
|
95
|
+
lines: Array<{
|
|
96
|
+
level: string;
|
|
97
|
+
message: string;
|
|
98
|
+
time?: string;
|
|
99
|
+
}>;
|
|
100
|
+
}
|
|
101
|
+
/** Peak heap while the request ran, which is what a leak shows up in. */
|
|
102
|
+
export declare function memoryCollector(): ProfilerCollector<{
|
|
103
|
+
start: number;
|
|
104
|
+
}>;
|
|
105
|
+
//# sourceMappingURL=collectors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collectors.d.ts","sourceRoot":"","sources":["../../src/profiler/collectors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAElE,gDAAgD;AAChD,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAA;IAC5B,oBAAoB;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,kFAAkF;AAClF,MAAM,WAAW,gBAAgB;IAC/B,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAA;CACjE;AAWD,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,MAAM,QAAQ,GAAG,SAAS,GAC/B,iBAAiB,CAAC,QAAQ,CAAC,CA8B7B;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,aAAa,EAAE,CAAA;CACzB;AAED,wCAAwC;AACxC,wBAAgB,gBAAgB,IAAI,iBAAiB,CAAC,UAAU,CAAC,CAOhE;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,IAAI,iBAAiB,CAAC,YAAY,CAAC,CAOpE;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,MAAM,GAAG,SAAS;;;;;;;;;;;;;mBAkC3D,MAAM;qBAAW,MAAM;mBAAS,MAAM;;;EAZ7D;AAED,0EAA0E;AAC1E,MAAM,WAAW,cAAc;IAC7B,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAA;CACjE;AAKD,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAChE;AAWD,yEAAyE;AACzE,wBAAgB,eAAe,IAAI,iBAAiB,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAatE"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the profiler watches.
|
|
3
|
+
*
|
|
4
|
+
* Each one is independent and may be replaced: the database collector is the
|
|
5
|
+
* interesting case, because the data does not come from the request at all. It
|
|
6
|
+
* comes from `db:query` events the data layer emits with no idea which request
|
|
7
|
+
* caused them, and the profiler's async store is what puts each one back where
|
|
8
|
+
* it belongs.
|
|
9
|
+
*
|
|
10
|
+
* NO import of atlas. The collector listens on the application's emitter for
|
|
11
|
+
* `db:query`, which is the shape atlas documents for bridging its own registry
|
|
12
|
+
* (`onDbQuery((event) => emitter.emit('db:query', event))`). A project on
|
|
13
|
+
* another data layer emits the same event and gets the same panel.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* How many statements one request may record.
|
|
17
|
+
*
|
|
18
|
+
* A runaway loop issuing ten thousand queries is exactly the bug this panel
|
|
19
|
+
* exists to show, and keeping all ten thousand would take the dev server down
|
|
20
|
+
* with it. The count stays true past the cap; the list stops.
|
|
21
|
+
*/
|
|
22
|
+
const QUERY_CAP = 500;
|
|
23
|
+
export function databaseCollector(events, read) {
|
|
24
|
+
// Subscribed once, at construction: a listener added per request would leak
|
|
25
|
+
// one per request, and removing it on the way out would miss a query still
|
|
26
|
+
// in flight.
|
|
27
|
+
events.on('db:query', (payload) => {
|
|
28
|
+
const bag = read();
|
|
29
|
+
if (bag === undefined)
|
|
30
|
+
return;
|
|
31
|
+
bag.total += 1;
|
|
32
|
+
bag.duration += durationOf(payload);
|
|
33
|
+
if (bag.queries.length < QUERY_CAP)
|
|
34
|
+
bag.queries.push(toQuery(payload));
|
|
35
|
+
});
|
|
36
|
+
return {
|
|
37
|
+
name: 'database',
|
|
38
|
+
badge: (data) => {
|
|
39
|
+
const total = typeof data.total === 'number' ? data.total : 0;
|
|
40
|
+
const duration = typeof data.duration === 'number' ? data.duration : 0;
|
|
41
|
+
return `${total} ${total === 1 ? 'query' : 'queries'} · ${round(duration)} ms`;
|
|
42
|
+
},
|
|
43
|
+
start: () => ({ total: 0, duration: 0, queries: [] }),
|
|
44
|
+
finish: (bag) => bag === undefined
|
|
45
|
+
? { total: 0, duration: 0, queries: [], capped: false }
|
|
46
|
+
: {
|
|
47
|
+
total: bag.total,
|
|
48
|
+
duration: round(bag.duration),
|
|
49
|
+
queries: bag.queries,
|
|
50
|
+
capped: bag.total > bag.queries.length,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** The request and what it answered. */
|
|
55
|
+
export function requestCollector() {
|
|
56
|
+
return {
|
|
57
|
+
name: 'request',
|
|
58
|
+
badge: (data) => (typeof data.route === 'string' ? data.route : undefined),
|
|
59
|
+
start: () => ({}),
|
|
60
|
+
finish: (bag) => ({ ...(bag ?? {}) }),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* What went wrong, if anything.
|
|
65
|
+
*
|
|
66
|
+
* The one panel worth opening by itself: an exception that reached the handler
|
|
67
|
+
* is reported here so it shows up in the page rather than only in the terminal
|
|
68
|
+
* someone may not be looking at.
|
|
69
|
+
*/
|
|
70
|
+
export function exceptionCollector() {
|
|
71
|
+
return {
|
|
72
|
+
name: 'exception',
|
|
73
|
+
badge: (data) => (typeof data.message === 'string' ? 'error' : undefined),
|
|
74
|
+
start: () => ({}),
|
|
75
|
+
finish: (bag) => ({ ...(bag ?? {}) }),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Lines logged while the request ran.
|
|
80
|
+
*
|
|
81
|
+
* Same arrangement as the database panel, and for the same reason: a logger
|
|
82
|
+
* writes a line with no idea which request caused it. It comes off the
|
|
83
|
+
* application's emitter as `log:line`, which is what `@c9up/spectrum` bridges
|
|
84
|
+
* its own registry to (`onLog((entry) => emitter.emit('log:line', entry))`),
|
|
85
|
+
* so a project on another logger emits the same event and gets the same panel.
|
|
86
|
+
*/
|
|
87
|
+
export function logCollector(events, read) {
|
|
88
|
+
// Subscribed once, at construction, for the same reason as the queries: a
|
|
89
|
+
// listener per request leaks one per request.
|
|
90
|
+
events.on('log:line', (payload) => {
|
|
91
|
+
const bag = read();
|
|
92
|
+
if (bag === undefined)
|
|
93
|
+
return;
|
|
94
|
+
if (bag.lines.length < LINE_CAP)
|
|
95
|
+
bag.lines.push(toLine(payload));
|
|
96
|
+
bag.total += 1;
|
|
97
|
+
});
|
|
98
|
+
return {
|
|
99
|
+
name: 'logs',
|
|
100
|
+
badge: (data) => {
|
|
101
|
+
const total = typeof data.total === 'number' ? data.total : 0;
|
|
102
|
+
return total === 0 ? undefined : `${total} ${total === 1 ? 'log' : 'logs'}`;
|
|
103
|
+
},
|
|
104
|
+
start: () => ({ total: 0, lines: [] }),
|
|
105
|
+
// Nothing logged is nothing to show: an empty bag renders no panel at all,
|
|
106
|
+
// rather than a heading over an empty list.
|
|
107
|
+
finish: (bag) => bag === undefined || bag.total === 0 ? {} : { total: bag.total, lines: bag.lines },
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/** As many lines as are worth scrolling; the count stays true past it. */
|
|
111
|
+
const LINE_CAP = 200;
|
|
112
|
+
function toLine(payload) {
|
|
113
|
+
const entry = asRecord(payload);
|
|
114
|
+
return {
|
|
115
|
+
level: typeof entry.level === 'string' ? entry.level : 'info',
|
|
116
|
+
message: typeof entry.message === 'string' ? entry.message : String(entry.message ?? ''),
|
|
117
|
+
...(typeof entry.timestamp === 'string' ? { time: entry.timestamp } : {}),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/** Peak heap while the request ran, which is what a leak shows up in. */
|
|
121
|
+
export function memoryCollector() {
|
|
122
|
+
return {
|
|
123
|
+
name: 'memory',
|
|
124
|
+
badge: (data) => (typeof data.usedMb === 'number' ? `${data.usedMb.toFixed(1)} MB` : undefined),
|
|
125
|
+
start: () => ({ start: process.memoryUsage().heapUsed }),
|
|
126
|
+
finish: (bag) => {
|
|
127
|
+
const used = process.memoryUsage().heapUsed;
|
|
128
|
+
return {
|
|
129
|
+
usedMb: used / 1024 / 1024,
|
|
130
|
+
deltaMb: bag === undefined ? 0 : (used - bag.start) / 1024 / 1024,
|
|
131
|
+
};
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function toQuery(payload) {
|
|
136
|
+
const event = asRecord(payload);
|
|
137
|
+
return {
|
|
138
|
+
sql: typeof event.sql === 'string' ? event.sql : String(event.sql ?? ''),
|
|
139
|
+
bindings: Array.isArray(event.bindings) ? event.bindings : [],
|
|
140
|
+
duration: durationOf(payload),
|
|
141
|
+
...pickString(event, 'connection'),
|
|
142
|
+
...pickString(event, 'model'),
|
|
143
|
+
...pickString(event, 'method'),
|
|
144
|
+
...(event.inTransaction === true ? { inTransaction: true } : {}),
|
|
145
|
+
...(event.error instanceof Error ? { error: event.error.message } : {}),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function durationOf(payload) {
|
|
149
|
+
const value = asRecord(payload).duration;
|
|
150
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : 0;
|
|
151
|
+
}
|
|
152
|
+
function pickString(event, key) {
|
|
153
|
+
const value = event[key];
|
|
154
|
+
return typeof value === 'string' && value !== '' ? { [key]: value } : {};
|
|
155
|
+
}
|
|
156
|
+
function asRecord(value) {
|
|
157
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
158
|
+
? Object.fromEntries(Object.entries(value))
|
|
159
|
+
: {};
|
|
160
|
+
}
|
|
161
|
+
function round(value) {
|
|
162
|
+
return Math.round(value * 100) / 100;
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=collectors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collectors.js","sourceRoot":"","sources":["../../src/profiler/collectors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the toolbar and the profiler read from.
|
|
3
|
+
*
|
|
4
|
+
* Three routes, all under `/__ream/profiler` and all development-only:
|
|
5
|
+
*
|
|
6
|
+
* /__ream/profiler the history, newest first
|
|
7
|
+
* /__ream/profiler/<token> one request, every panel
|
|
8
|
+
* /__ream/profiler/<token>.json the same, for the bar in the page
|
|
9
|
+
*
|
|
10
|
+
* Server middleware rather than routes, like the hot-reload endpoint: it
|
|
11
|
+
* answers before the application's own middleware, so a profiler request does
|
|
12
|
+
* not run the authentication stack and end up in its own history.
|
|
13
|
+
*/
|
|
14
|
+
import type { Profiler } from './Profiler.js';
|
|
15
|
+
/** The slice of the context this endpoint uses. */
|
|
16
|
+
export interface ProfilerContext {
|
|
17
|
+
request: {
|
|
18
|
+
url: () => string;
|
|
19
|
+
};
|
|
20
|
+
response: {
|
|
21
|
+
header: (name: string, value: string) => unknown;
|
|
22
|
+
send: (body: unknown) => unknown;
|
|
23
|
+
status: (code: number) => unknown;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export declare function profilerEndpoint(profiler: Profiler): (ctx: ProfilerContext, next: () => Promise<void>) => Promise<void>;
|
|
27
|
+
//# sourceMappingURL=endpoint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"endpoint.d.ts","sourceRoot":"","sources":["../../src/profiler/endpoint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAI7C,mDAAmD;AACnD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,MAAM,CAAA;KAAE,CAAA;IAC9B,QAAQ,EAAE;QACR,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAA;QAChD,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAA;QAChC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAA;KAClC,CAAA;CACF;AAED,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,QAAQ,GACjB,CAAC,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAgDpE"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the toolbar and the profiler read from.
|
|
3
|
+
*
|
|
4
|
+
* Three routes, all under `/__ream/profiler` and all development-only:
|
|
5
|
+
*
|
|
6
|
+
* /__ream/profiler the history, newest first
|
|
7
|
+
* /__ream/profiler/<token> one request, every panel
|
|
8
|
+
* /__ream/profiler/<token>.json the same, for the bar in the page
|
|
9
|
+
*
|
|
10
|
+
* Server middleware rather than routes, like the hot-reload endpoint: it
|
|
11
|
+
* answers before the application's own middleware, so a profiler request does
|
|
12
|
+
* not run the authentication stack and end up in its own history.
|
|
13
|
+
*/
|
|
14
|
+
import { PROFILER_PATH } from './Profiler.js';
|
|
15
|
+
import { renderProfileList, renderProfilePage } from './render.js';
|
|
16
|
+
export function profilerEndpoint(profiler) {
|
|
17
|
+
return async (ctx, next) => {
|
|
18
|
+
const path = ctx.request.url().split('?')[0] ?? '';
|
|
19
|
+
if (path !== PROFILER_PATH && !path.startsWith(`${PROFILER_PATH}/`)) {
|
|
20
|
+
await next();
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (path === PROFILER_PATH) {
|
|
24
|
+
ctx.response.header('content-type', 'text/html; charset=utf-8');
|
|
25
|
+
ctx.response.send(renderProfileList(profiler.store.recent(50)));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const rest = path.slice(`${PROFILER_PATH}/`.length);
|
|
29
|
+
const asJson = rest.endsWith('.json');
|
|
30
|
+
const token = asJson ? rest.slice(0, -'.json'.length) : rest;
|
|
31
|
+
const profile = profiler.store.find(token);
|
|
32
|
+
if (profile === undefined) {
|
|
33
|
+
// 404 rather than an empty page: a token the store has dropped is a real
|
|
34
|
+
// answer, and the bar reads the status to stop asking.
|
|
35
|
+
ctx.response.status(404);
|
|
36
|
+
if (asJson) {
|
|
37
|
+
ctx.response.header('content-type', 'application/json');
|
|
38
|
+
ctx.response.send({ error: 'no profile for this token' });
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
ctx.response.header('content-type', 'text/html; charset=utf-8');
|
|
42
|
+
ctx.response.send(renderProfileList(profiler.store.recent(50), token));
|
|
43
|
+
}
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (asJson) {
|
|
47
|
+
ctx.response.header('content-type', 'application/json');
|
|
48
|
+
// No cache: the token is single-use in practice, and a cached profile
|
|
49
|
+
// would show the previous request's numbers after a reload.
|
|
50
|
+
ctx.response.header('cache-control', 'no-store');
|
|
51
|
+
ctx.response.send({
|
|
52
|
+
...profile,
|
|
53
|
+
badges: badgesFor(profiler, profile.data),
|
|
54
|
+
});
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
ctx.response.header('content-type', 'text/html; charset=utf-8');
|
|
58
|
+
ctx.response.send(renderProfilePage(profile, badgesFor(profiler, profile.data)));
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/** Each collector's headline, asked of the collector rather than guessed. */
|
|
62
|
+
function badgesFor(profiler, data) {
|
|
63
|
+
const badges = {};
|
|
64
|
+
for (const collector of profiler.collectors) {
|
|
65
|
+
const panel = data[collector.name];
|
|
66
|
+
if (panel === undefined || collector.badge === undefined)
|
|
67
|
+
continue;
|
|
68
|
+
try {
|
|
69
|
+
const badge = collector.badge(panel);
|
|
70
|
+
if (badge !== undefined)
|
|
71
|
+
badges[collector.name] = badge;
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// A badge that throws costs its own label, nothing more.
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return badges;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=endpoint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"endpoint.js","sourceRoot":"","sources":["../../src/profiler/endpoint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The profiler, assembled.
|
|
3
|
+
*
|
|
4
|
+
* One place that knows which collectors ship, so the Ignitor wires a profiler
|
|
5
|
+
* rather than a list — and an application that wants its own panel adds a
|
|
6
|
+
* collector here instead of rebuilding the set.
|
|
7
|
+
*/
|
|
8
|
+
import { type QueryEventSource } from './collectors.js';
|
|
9
|
+
import { Profiler } from './Profiler.js';
|
|
10
|
+
import type { ProfilerCollector } from './types.js';
|
|
11
|
+
export * from './collectors.js';
|
|
12
|
+
export { profilerEndpoint } from './endpoint.js';
|
|
13
|
+
export { MemoryProfileStore } from './MemoryProfileStore.js';
|
|
14
|
+
export { profileRequest } from './middleware.js';
|
|
15
|
+
export { PROFILER_PATH, PROFILER_TOKEN_HEADER, Profiler } from './Profiler.js';
|
|
16
|
+
export { toolbarScript } from './toolbar.js';
|
|
17
|
+
export * from './types.js';
|
|
18
|
+
/**
|
|
19
|
+
* The profiler a dev server runs with.
|
|
20
|
+
*
|
|
21
|
+
* `events` is the application's emitter. The database panel comes from
|
|
22
|
+
* `db:query` on it, which is how a data layer reports a statement without the
|
|
23
|
+
* profiler importing one.
|
|
24
|
+
*/
|
|
25
|
+
export declare function createProfiler(events: QueryEventSource, extra?: readonly ProfilerCollector[]): Profiler;
|
|
26
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/profiler/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAOL,KAAK,gBAAgB,EAEtB,MAAM,iBAAiB,CAAA;AAExB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAEnD,cAAc,iBAAiB,CAAA;AAC/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC9E,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,cAAc,YAAY,CAAA;AAE1B;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,gBAAgB,EACxB,KAAK,GAAE,SAAS,iBAAiB,EAAO,GACvC,QAAQ,CAeV"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The profiler, assembled.
|
|
3
|
+
*
|
|
4
|
+
* One place that knows which collectors ship, so the Ignitor wires a profiler
|
|
5
|
+
* rather than a list — and an application that wants its own panel adds a
|
|
6
|
+
* collector here instead of rebuilding the set.
|
|
7
|
+
*/
|
|
8
|
+
import { databaseCollector, exceptionCollector, logCollector, memoryCollector, requestCollector, } from './collectors.js';
|
|
9
|
+
import { MemoryProfileStore } from './MemoryProfileStore.js';
|
|
10
|
+
import { Profiler } from './Profiler.js';
|
|
11
|
+
export * from './collectors.js';
|
|
12
|
+
export { profilerEndpoint } from './endpoint.js';
|
|
13
|
+
export { MemoryProfileStore } from './MemoryProfileStore.js';
|
|
14
|
+
export { profileRequest } from './middleware.js';
|
|
15
|
+
export { PROFILER_PATH, PROFILER_TOKEN_HEADER, Profiler } from './Profiler.js';
|
|
16
|
+
export { toolbarScript } from './toolbar.js';
|
|
17
|
+
export * from './types.js';
|
|
18
|
+
/**
|
|
19
|
+
* The profiler a dev server runs with.
|
|
20
|
+
*
|
|
21
|
+
* `events` is the application's emitter. The database panel comes from
|
|
22
|
+
* `db:query` on it, which is how a data layer reports a statement without the
|
|
23
|
+
* profiler importing one.
|
|
24
|
+
*/
|
|
25
|
+
export function createProfiler(events, extra = []) {
|
|
26
|
+
// Built before the profiler so the database collector can read the bag of
|
|
27
|
+
// whichever request is running — the reference is filled in a line later,
|
|
28
|
+
// and nothing reads it until a request arrives.
|
|
29
|
+
let profiler;
|
|
30
|
+
const collectors = [
|
|
31
|
+
requestCollector(),
|
|
32
|
+
databaseCollector(events, () => profiler.current('database')),
|
|
33
|
+
exceptionCollector(),
|
|
34
|
+
logCollector(events, () => profiler.current('logs')),
|
|
35
|
+
memoryCollector(),
|
|
36
|
+
...extra,
|
|
37
|
+
];
|
|
38
|
+
profiler = new Profiler(collectors, new MemoryProfileStore(collectors));
|
|
39
|
+
return profiler;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/profiler/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAElB,YAAY,EACZ,eAAe,EAGf,gBAAgB,GACjB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAGxC,cAAc,iBAAiB,CAAA;AAC/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC9E,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,cAAc,YAAY,CAAA;AAE1B;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAC5B,MAAwB,EACxB,QAAsC,EAAE;IAExC,0EAA0E;IAC1E,0EAA0E;IAC1E,gDAAgD;IAChD,IAAI,QAAkB,CAAA;IACtB,MAAM,UAAU,GAAwB;QACtC,gBAAgB,EAAE;QAClB,iBAAiB,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAW,UAAU,CAAC,CAAC;QACvE,kBAAkB,EAAE;QACpB,YAAY,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAS,MAAM,CAAC,CAAC;QAC5D,eAAe,EAAE;QACjB,GAAG,KAAK;KACT,CAAA;IACD,QAAQ,GAAG,IAAI,QAAQ,CAAC,UAAU,EAAE,IAAI,kBAAkB,CAAC,UAAU,CAAC,CAAC,CAAA;IACvE,OAAO,QAAQ,CAAA;AACjB,CAAC"}
|