@beignet/devtools 0.0.1 → 0.0.3
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 +31 -0
- package/README.md +2 -0
- package/dist/access.d.ts +20 -0
- package/dist/access.d.ts.map +1 -1
- package/dist/access.js +11 -0
- package/dist/access.js.map +1 -1
- package/dist/audit.d.ts +21 -0
- package/dist/audit.d.ts.map +1 -1
- package/dist/audit.js +6 -0
- package/dist/audit.js.map +1 -1
- package/dist/events.d.ts +19 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +6 -0
- package/dist/events.js.map +1 -1
- package/dist/instrumentation.d.ts +40 -0
- package/dist/instrumentation.d.ts.map +1 -1
- package/dist/instrumentation.js +10 -0
- package/dist/instrumentation.js.map +1 -1
- package/dist/persistence.d.ts +24 -0
- package/dist/persistence.d.ts.map +1 -1
- package/dist/persistence.js +6 -0
- package/dist/persistence.js.map +1 -1
- package/dist/provider-instrumentation.d.ts +18 -0
- package/dist/provider-instrumentation.d.ts.map +1 -1
- package/dist/provider-instrumentation.js +9 -0
- package/dist/provider-instrumentation.js.map +1 -1
- package/dist/provider.d.ts +42 -0
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +3 -0
- package/dist/provider.js.map +1 -1
- package/dist/redaction.d.ts +9 -0
- package/dist/redaction.d.ts.map +1 -1
- package/dist/redaction.js +9 -0
- package/dist/redaction.js.map +1 -1
- package/dist/routes.d.ts +12 -0
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js.map +1 -1
- package/dist/trace-context.d.ts +51 -0
- package/dist/trace-context.d.ts.map +1 -1
- package/dist/trace-context.js +18 -0
- package/dist/trace-context.js.map +1 -1
- package/dist/ui.js +12 -2
- package/dist/ui.js.map +1 -1
- package/dist/watchers.d.ts +43 -1
- package/dist/watchers.d.ts.map +1 -1
- package/dist/watchers.js +28 -0
- package/dist/watchers.js.map +1 -1
- package/package.json +1 -1
- package/src/access.ts +20 -0
- package/src/audit.ts +21 -0
- package/src/events.ts +25 -1
- package/src/instrumentation.ts +40 -0
- package/src/persistence.ts +24 -0
- package/src/provider-instrumentation.ts +20 -0
- package/src/provider.ts +42 -0
- package/src/redaction.ts +9 -0
- package/src/routes.ts +12 -0
- package/src/trace-context.ts +51 -0
- package/src/ui.ts +12 -2
- package/src/watchers.ts +58 -0
package/package.json
CHANGED
package/src/access.ts
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result returned by a devtools authorization check.
|
|
3
|
+
*/
|
|
1
4
|
export type DevtoolsAuthorizeResult = boolean | Response;
|
|
2
5
|
|
|
6
|
+
/**
|
|
7
|
+
* App-owned authorization callback for devtools routes.
|
|
8
|
+
*/
|
|
3
9
|
export type DevtoolsAuthorize = (
|
|
4
10
|
req: Request,
|
|
5
11
|
) => DevtoolsAuthorizeResult | Promise<DevtoolsAuthorizeResult>;
|
|
6
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Access control options for devtools HTTP routes.
|
|
15
|
+
*/
|
|
7
16
|
export interface DevtoolsRouteAccessOptions {
|
|
8
17
|
/**
|
|
9
18
|
* Whether the devtools route should be exposed.
|
|
@@ -21,16 +30,27 @@ export interface DevtoolsRouteAccessOptions {
|
|
|
21
30
|
authorize?: DevtoolsAuthorize;
|
|
22
31
|
}
|
|
23
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Resolve whether devtools routes should be exposed.
|
|
35
|
+
*
|
|
36
|
+
* Defaults to disabled in production and enabled elsewhere.
|
|
37
|
+
*/
|
|
24
38
|
export function isDevtoolsRouteEnabled(
|
|
25
39
|
options: DevtoolsRouteAccessOptions = {},
|
|
26
40
|
): boolean {
|
|
27
41
|
return options.enabled ?? process.env.NODE_ENV !== "production";
|
|
28
42
|
}
|
|
29
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Return the standard hidden devtools response.
|
|
46
|
+
*/
|
|
30
47
|
export function devtoolsNotFoundResponse(): Response {
|
|
31
48
|
return new Response("Not Found", { status: 404 });
|
|
32
49
|
}
|
|
33
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Authorize a devtools request and return a denial response when blocked.
|
|
53
|
+
*/
|
|
34
54
|
export async function authorizeDevtoolsRequest(
|
|
35
55
|
req: Request,
|
|
36
56
|
options: DevtoolsRouteAccessOptions = {},
|
package/src/audit.ts
CHANGED
|
@@ -7,10 +7,25 @@ import {
|
|
|
7
7
|
} from "@beignet/core/ports";
|
|
8
8
|
import type { DevtoolsPort } from "./index";
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Options for wrapping an audit log with devtools emission.
|
|
12
|
+
*/
|
|
10
13
|
export interface DevtoolsAuditLogOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Durable audit log to write first.
|
|
16
|
+
*/
|
|
11
17
|
audit: AuditLogPort;
|
|
18
|
+
/**
|
|
19
|
+
* Optional devtools port that receives audit events.
|
|
20
|
+
*/
|
|
12
21
|
devtools?: DevtoolsPort;
|
|
22
|
+
/**
|
|
23
|
+
* Whether to emit devtools events. Defaults to true.
|
|
24
|
+
*/
|
|
13
25
|
emit?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Optional app-owned redactor applied after Beignet's audit redaction.
|
|
28
|
+
*/
|
|
14
29
|
redact?: (entry: AuditLogEntry) => AuditLogEntry;
|
|
15
30
|
}
|
|
16
31
|
|
|
@@ -32,6 +47,12 @@ function auditSummary(entry: AuditLogEntry): string {
|
|
|
32
47
|
: `${entry.action} ${outcome}`;
|
|
33
48
|
}
|
|
34
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Wrap an audit log so durable audit writes can also appear in devtools.
|
|
52
|
+
*
|
|
53
|
+
* Devtools observer failures are ignored so audit persistence remains the
|
|
54
|
+
* source of truth.
|
|
55
|
+
*/
|
|
35
56
|
export function createDevtoolsAuditLog(
|
|
36
57
|
options: DevtoolsAuditLogOptions,
|
|
37
58
|
): AuditLogPort {
|
package/src/events.ts
CHANGED
|
@@ -110,7 +110,13 @@ export interface EventBusEvent extends BaseDevtoolsEvent {
|
|
|
110
110
|
export interface JobEvent extends BaseDevtoolsEvent {
|
|
111
111
|
type: "job";
|
|
112
112
|
jobName: string;
|
|
113
|
-
status:
|
|
113
|
+
status:
|
|
114
|
+
| "scheduled"
|
|
115
|
+
| "started"
|
|
116
|
+
| "completed"
|
|
117
|
+
| "failed"
|
|
118
|
+
| "retryScheduled"
|
|
119
|
+
| "deadLettered";
|
|
114
120
|
}
|
|
115
121
|
|
|
116
122
|
/**
|
|
@@ -156,14 +162,23 @@ export type DevtoolsEvent =
|
|
|
156
162
|
| ProviderEvent
|
|
157
163
|
| CustomDevtoolsEvent;
|
|
158
164
|
|
|
165
|
+
/**
|
|
166
|
+
* Input accepted by `createDevtoolsEvent(...)`.
|
|
167
|
+
*/
|
|
159
168
|
export type DevtoolsEventInput = DevtoolsEvent extends infer Event
|
|
160
169
|
? Event extends DevtoolsEvent
|
|
161
170
|
? Omit<Event, "id" | "timestamp"> & Partial<Pick<Event, "id" | "timestamp">>
|
|
162
171
|
: never
|
|
163
172
|
: never;
|
|
164
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Function that redacts a normalized devtools event before storage.
|
|
176
|
+
*/
|
|
165
177
|
export type DevtoolsRedactor = (event: DevtoolsEvent) => DevtoolsEvent;
|
|
166
178
|
|
|
179
|
+
/**
|
|
180
|
+
* Event emitted to live devtools subscribers.
|
|
181
|
+
*/
|
|
167
182
|
export type DevtoolsSubscriptionEvent =
|
|
168
183
|
| {
|
|
169
184
|
type: "record";
|
|
@@ -173,8 +188,14 @@ export type DevtoolsSubscriptionEvent =
|
|
|
173
188
|
type: "clear";
|
|
174
189
|
};
|
|
175
190
|
|
|
191
|
+
/**
|
|
192
|
+
* Devtools subscription listener.
|
|
193
|
+
*/
|
|
176
194
|
export type DevtoolsListener = (event: DevtoolsSubscriptionEvent) => void;
|
|
177
195
|
|
|
196
|
+
/**
|
|
197
|
+
* All built-in devtools event types.
|
|
198
|
+
*/
|
|
178
199
|
export const DEVTOOLS_EVENT_TYPES = [
|
|
179
200
|
"request",
|
|
180
201
|
"error",
|
|
@@ -186,6 +207,9 @@ export const DEVTOOLS_EVENT_TYPES = [
|
|
|
186
207
|
"custom",
|
|
187
208
|
] as const satisfies readonly DevtoolsEvent["type"][];
|
|
188
209
|
|
|
210
|
+
/**
|
|
211
|
+
* Check whether a string is a built-in devtools event type.
|
|
212
|
+
*/
|
|
189
213
|
export function isDevtoolsEventType(
|
|
190
214
|
value: string,
|
|
191
215
|
): value is DevtoolsEvent["type"] {
|
package/src/instrumentation.ts
CHANGED
|
@@ -32,6 +32,9 @@ type ContextWithPorts = {
|
|
|
32
32
|
};
|
|
33
33
|
};
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Options for server hooks that record request/error events to devtools.
|
|
37
|
+
*/
|
|
35
38
|
export interface DevtoolsHooksOptions<Ctx> {
|
|
36
39
|
/**
|
|
37
40
|
* Devtools route prefix. Requests under this path are ignored to avoid
|
|
@@ -41,6 +44,9 @@ export interface DevtoolsHooksOptions<Ctx> {
|
|
|
41
44
|
*/
|
|
42
45
|
basePath?: string;
|
|
43
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Resolve the request ID used for event correlation.
|
|
49
|
+
*/
|
|
44
50
|
getRequestId?: (args: {
|
|
45
51
|
req: HttpRequestLike;
|
|
46
52
|
ctx?: Ctx;
|
|
@@ -66,6 +72,9 @@ export interface DevtoolsHooksOptions<Ctx> {
|
|
|
66
72
|
*/
|
|
67
73
|
traceContextHeader?: string | false;
|
|
68
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Resolve a trace context from request/context/response data.
|
|
77
|
+
*/
|
|
69
78
|
getTraceContext?: (args: {
|
|
70
79
|
req: HttpRequestLike;
|
|
71
80
|
ctx?: Ctx;
|
|
@@ -78,6 +87,9 @@ export interface DevtoolsHooksOptions<Ctx> {
|
|
|
78
87
|
*/
|
|
79
88
|
redact?: DevtoolsRedactor;
|
|
80
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Decide whether to capture a completed request event.
|
|
92
|
+
*/
|
|
81
93
|
shouldCapture?: (args: {
|
|
82
94
|
req: HttpRequestLike;
|
|
83
95
|
ctx?: Ctx;
|
|
@@ -87,6 +99,9 @@ export interface DevtoolsHooksOptions<Ctx> {
|
|
|
87
99
|
}) => boolean;
|
|
88
100
|
}
|
|
89
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Use-case run event shape consumed by the devtools observer.
|
|
104
|
+
*/
|
|
90
105
|
export interface DevtoolsUseCaseRunEvent<Ctx> {
|
|
91
106
|
name: string;
|
|
92
107
|
kind: "command" | "query";
|
|
@@ -96,10 +111,25 @@ export interface DevtoolsUseCaseRunEvent<Ctx> {
|
|
|
96
111
|
ctx: Ctx;
|
|
97
112
|
}
|
|
98
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Options for `createDevtoolsUseCaseObserver(...)`.
|
|
116
|
+
*/
|
|
99
117
|
export interface DevtoolsUseCaseObserverOptions<Ctx> {
|
|
118
|
+
/**
|
|
119
|
+
* Resolve a devtools port from use-case context.
|
|
120
|
+
*/
|
|
100
121
|
getDevtools?: (ctx: Ctx) => DevtoolsPort | undefined;
|
|
122
|
+
/**
|
|
123
|
+
* Resolve the request ID used for event correlation.
|
|
124
|
+
*/
|
|
101
125
|
getRequestId?: (ctx: Ctx) => string | undefined;
|
|
126
|
+
/**
|
|
127
|
+
* Whether use-case error phases should also emit `error` events.
|
|
128
|
+
*/
|
|
102
129
|
logErrorEvents?: boolean;
|
|
130
|
+
/**
|
|
131
|
+
* Optional redactor applied before events are recorded.
|
|
132
|
+
*/
|
|
103
133
|
redact?: DevtoolsRedactor;
|
|
104
134
|
}
|
|
105
135
|
|
|
@@ -175,6 +205,13 @@ function isWatcherEnabled(
|
|
|
175
205
|
return devtools.isWatcherEnabled(name);
|
|
176
206
|
}
|
|
177
207
|
|
|
208
|
+
/**
|
|
209
|
+
* Create server hooks that record Beignet request and error activity.
|
|
210
|
+
*
|
|
211
|
+
* Devtools routes under `basePath` are ignored so polling and SSE traffic do not
|
|
212
|
+
* fill the event buffer. Request IDs and trace context are also written to
|
|
213
|
+
* response headers unless disabled.
|
|
214
|
+
*/
|
|
178
215
|
export function createDevtoolsHooks<
|
|
179
216
|
Ctx,
|
|
180
217
|
Ports extends PortsWithDevtools = PortsWithDevtools,
|
|
@@ -359,6 +396,9 @@ export function createDevtoolsHooks<
|
|
|
359
396
|
};
|
|
360
397
|
}
|
|
361
398
|
|
|
399
|
+
/**
|
|
400
|
+
* Create a use-case observer compatible with `createUseCase({ onRun })`.
|
|
401
|
+
*/
|
|
362
402
|
export function createDevtoolsUseCaseObserver<Ctx>(
|
|
363
403
|
options: DevtoolsUseCaseObserverOptions<Ctx> = {},
|
|
364
404
|
): (event: DevtoolsUseCaseRunEvent<Ctx>) => void {
|
package/src/persistence.ts
CHANGED
|
@@ -1,15 +1,33 @@
|
|
|
1
1
|
import type { DevtoolsEvent } from "./events";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Persistence interface for devtools event stores.
|
|
5
|
+
*/
|
|
3
6
|
export interface DevtoolsEventStore {
|
|
7
|
+
/**
|
|
8
|
+
* Store name used in diagnostics.
|
|
9
|
+
*/
|
|
4
10
|
name?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Load persisted events at startup.
|
|
13
|
+
*/
|
|
5
14
|
load?(): DevtoolsEvent[] | Promise<DevtoolsEvent[]>;
|
|
15
|
+
/**
|
|
16
|
+
* Append one event. The second argument is the current bounded buffer.
|
|
17
|
+
*/
|
|
6
18
|
append?(
|
|
7
19
|
event: DevtoolsEvent,
|
|
8
20
|
events: readonly DevtoolsEvent[],
|
|
9
21
|
): void | Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Clear persisted events.
|
|
24
|
+
*/
|
|
10
25
|
clear?(): void | Promise<void>;
|
|
11
26
|
}
|
|
12
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Options for the Node-only file-backed devtools store.
|
|
30
|
+
*/
|
|
13
31
|
export interface FileDevtoolsStoreOptions {
|
|
14
32
|
/**
|
|
15
33
|
* JSONL file used for persisted devtools events.
|
|
@@ -80,6 +98,12 @@ async function loadNodeModules(): Promise<{
|
|
|
80
98
|
return { fs, path };
|
|
81
99
|
}
|
|
82
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Create a JSONL file-backed devtools store.
|
|
103
|
+
*
|
|
104
|
+
* This store lazily imports Node fs/path modules and is intended for Node
|
|
105
|
+
* runtimes. It compacts periodically to keep the persisted file bounded.
|
|
106
|
+
*/
|
|
83
107
|
export function createFileDevtoolsStore(
|
|
84
108
|
options: FileDevtoolsStoreOptions = {},
|
|
85
109
|
): DevtoolsEventStore {
|
|
@@ -7,11 +7,25 @@ import {
|
|
|
7
7
|
} from "@beignet/core/providers";
|
|
8
8
|
import type { DevtoolsPort } from "./index";
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Provider devtools instrumentation options.
|
|
12
|
+
*/
|
|
10
13
|
export type ProviderDevtoolsOptions = ProviderInstrumentationOptions;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Custom provider event input accepted by provider devtools instrumentation.
|
|
17
|
+
*/
|
|
11
18
|
export type ProviderCustomDevtoolsEventInput =
|
|
12
19
|
ProviderCustomInstrumentationEventInput;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Provider instrumentation object backed by devtools.
|
|
23
|
+
*/
|
|
13
24
|
export type ProviderDevtools = ProviderInstrumentation;
|
|
14
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Check whether an unknown value implements the devtools port shape.
|
|
28
|
+
*/
|
|
15
29
|
export function isDevtoolsPort(value: unknown): value is DevtoolsPort {
|
|
16
30
|
return (
|
|
17
31
|
isProviderInstrumentationPort(value) &&
|
|
@@ -24,6 +38,9 @@ export function isDevtoolsPort(value: unknown): value is DevtoolsPort {
|
|
|
24
38
|
);
|
|
25
39
|
}
|
|
26
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Resolve a devtools port from a direct port or `{ devtools }` object.
|
|
43
|
+
*/
|
|
27
44
|
export function resolveDevtoolsPort(target: unknown): DevtoolsPort | undefined {
|
|
28
45
|
if (isDevtoolsPort(target)) return target;
|
|
29
46
|
|
|
@@ -39,6 +56,9 @@ export function resolveDevtoolsPort(target: unknown): DevtoolsPort | undefined {
|
|
|
39
56
|
return undefined;
|
|
40
57
|
}
|
|
41
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Create provider instrumentation backed by a devtools port when available.
|
|
61
|
+
*/
|
|
42
62
|
export function createProviderDevtools(
|
|
43
63
|
target: unknown,
|
|
44
64
|
options: ProviderDevtoolsOptions,
|
package/src/provider.ts
CHANGED
|
@@ -37,11 +37,29 @@ import {
|
|
|
37
37
|
*/
|
|
38
38
|
const MAX_EVENTS = 500;
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Options for `createInMemoryDevtools(...)`.
|
|
42
|
+
*/
|
|
40
43
|
export interface InMemoryDevtoolsOptions {
|
|
44
|
+
/**
|
|
45
|
+
* Maximum events kept in memory.
|
|
46
|
+
*/
|
|
41
47
|
maxEvents?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Custom redactor applied after the default devtools redactor.
|
|
50
|
+
*/
|
|
42
51
|
redact?: DevtoolsRedactor;
|
|
52
|
+
/**
|
|
53
|
+
* Watcher configuration.
|
|
54
|
+
*/
|
|
43
55
|
watchers?: DevtoolsWatchersOptions;
|
|
56
|
+
/**
|
|
57
|
+
* Events loaded into the buffer at startup.
|
|
58
|
+
*/
|
|
44
59
|
initialEvents?: readonly DevtoolsEvent[];
|
|
60
|
+
/**
|
|
61
|
+
* Optional persistence store.
|
|
62
|
+
*/
|
|
45
63
|
store?: DevtoolsEventStore;
|
|
46
64
|
}
|
|
47
65
|
|
|
@@ -52,6 +70,9 @@ function createEventId(): string {
|
|
|
52
70
|
return `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
53
71
|
}
|
|
54
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Normalize a devtools event by filling `id` and `timestamp`.
|
|
75
|
+
*/
|
|
55
76
|
export function createDevtoolsEvent(event: DevtoolsEventInput): DevtoolsEvent {
|
|
56
77
|
return {
|
|
57
78
|
...event,
|
|
@@ -247,13 +268,34 @@ const DevtoolsConfigSchema = z.object({
|
|
|
247
268
|
PERSIST_PATH: z.string().optional(),
|
|
248
269
|
});
|
|
249
270
|
|
|
271
|
+
/**
|
|
272
|
+
* Devtools provider config loaded from `DEVTOOLS_*` env vars.
|
|
273
|
+
*/
|
|
250
274
|
export type DevtoolsConfig = z.infer<typeof DevtoolsConfigSchema>;
|
|
251
275
|
|
|
276
|
+
/**
|
|
277
|
+
* Options for `createDevtoolsProvider(...)`.
|
|
278
|
+
*/
|
|
252
279
|
export interface DevtoolsProviderOptions {
|
|
280
|
+
/**
|
|
281
|
+
* Whether devtools are enabled. Defaults to non-production.
|
|
282
|
+
*/
|
|
253
283
|
enabled?: boolean;
|
|
284
|
+
/**
|
|
285
|
+
* Maximum events kept in memory.
|
|
286
|
+
*/
|
|
254
287
|
maxEvents?: number;
|
|
288
|
+
/**
|
|
289
|
+
* Custom redactor applied after the default redactor.
|
|
290
|
+
*/
|
|
255
291
|
redact?: DevtoolsRedactor;
|
|
292
|
+
/**
|
|
293
|
+
* Watcher configuration.
|
|
294
|
+
*/
|
|
256
295
|
watchers?: DevtoolsWatchersOptions;
|
|
296
|
+
/**
|
|
297
|
+
* Optional persistence store or async store factory.
|
|
298
|
+
*/
|
|
257
299
|
store?:
|
|
258
300
|
| DevtoolsEventStore
|
|
259
301
|
| (() => DevtoolsEventStore | Promise<DevtoolsEventStore>);
|
package/src/redaction.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { redactValue } from "@beignet/core/ports";
|
|
2
2
|
import type { DevtoolsEvent, DevtoolsRedactor } from "./events";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Default devtools event redactor.
|
|
6
|
+
*/
|
|
4
7
|
export const defaultDevtoolsRedactor: DevtoolsRedactor = (event) =>
|
|
5
8
|
redactValue(event);
|
|
6
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Apply default redaction and then an optional custom redactor.
|
|
12
|
+
*/
|
|
7
13
|
export function applyDevtoolsRedaction(
|
|
8
14
|
event: DevtoolsEvent,
|
|
9
15
|
redact?: DevtoolsRedactor,
|
|
@@ -13,6 +19,9 @@ export function applyDevtoolsRedaction(
|
|
|
13
19
|
return redact(redacted);
|
|
14
20
|
}
|
|
15
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Create a devtools error event when redaction fails.
|
|
24
|
+
*/
|
|
16
25
|
export function createRedactionFailureEvent(error: unknown): DevtoolsEvent {
|
|
17
26
|
return {
|
|
18
27
|
id: `${Date.now()}-${Math.random().toString(16).slice(2)}`,
|
package/src/routes.ts
CHANGED
|
@@ -13,6 +13,9 @@ import { isDevtoolsEventType } from "./events";
|
|
|
13
13
|
import type { DevtoolsFilter, DevtoolsPort } from "./index";
|
|
14
14
|
import { handleDevtoolsUIRequest } from "./ui";
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Devtools route options shared by route handlers.
|
|
18
|
+
*/
|
|
16
19
|
export interface DevtoolsRequestOptions extends DevtoolsRouteAccessOptions {
|
|
17
20
|
/**
|
|
18
21
|
* URL path prefix where devtools routes are mounted.
|
|
@@ -22,8 +25,17 @@ export interface DevtoolsRequestOptions extends DevtoolsRouteAccessOptions {
|
|
|
22
25
|
basePath: string;
|
|
23
26
|
}
|
|
24
27
|
|
|
28
|
+
/**
|
|
29
|
+
* GET/POST route handlers returned by `createDevtoolsRoute(...)`.
|
|
30
|
+
*/
|
|
25
31
|
export interface DevtoolsRouteHandlers {
|
|
32
|
+
/**
|
|
33
|
+
* Handle devtools GET requests.
|
|
34
|
+
*/
|
|
26
35
|
GET(req: Request): Promise<Response>;
|
|
36
|
+
/**
|
|
37
|
+
* Handle devtools POST requests.
|
|
38
|
+
*/
|
|
27
39
|
POST(req: Request): Promise<Response>;
|
|
28
40
|
}
|
|
29
41
|
|
package/src/trace-context.ts
CHANGED
|
@@ -1,19 +1,52 @@
|
|
|
1
1
|
const TRACEPARENT_PATTERN = /^00-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/;
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Devtools trace context used to correlate related events.
|
|
5
|
+
*/
|
|
3
6
|
export interface DevtoolsTraceContext {
|
|
7
|
+
/**
|
|
8
|
+
* W3C trace ID.
|
|
9
|
+
*/
|
|
4
10
|
traceId: string;
|
|
11
|
+
/**
|
|
12
|
+
* Current span ID.
|
|
13
|
+
*/
|
|
5
14
|
spanId: string;
|
|
15
|
+
/**
|
|
16
|
+
* Parent span ID when available.
|
|
17
|
+
*/
|
|
6
18
|
parentSpanId?: string;
|
|
19
|
+
/**
|
|
20
|
+
* W3C traceparent header value.
|
|
21
|
+
*/
|
|
7
22
|
traceparent: string;
|
|
8
23
|
}
|
|
9
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Parsed W3C traceparent header.
|
|
27
|
+
*/
|
|
10
28
|
export interface ParsedTraceparent {
|
|
29
|
+
/**
|
|
30
|
+
* W3C trace ID.
|
|
31
|
+
*/
|
|
11
32
|
traceId: string;
|
|
33
|
+
/**
|
|
34
|
+
* Span ID from the traceparent header.
|
|
35
|
+
*/
|
|
12
36
|
spanId: string;
|
|
37
|
+
/**
|
|
38
|
+
* Trace flags from the traceparent header.
|
|
39
|
+
*/
|
|
13
40
|
traceFlags: string;
|
|
41
|
+
/**
|
|
42
|
+
* Normalized traceparent header value.
|
|
43
|
+
*/
|
|
14
44
|
traceparent: string;
|
|
15
45
|
}
|
|
16
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Input accepted when creating devtools trace context.
|
|
49
|
+
*/
|
|
17
50
|
export interface DevtoolsTraceContextInput {
|
|
18
51
|
traceId?: string;
|
|
19
52
|
spanId?: string;
|
|
@@ -41,6 +74,9 @@ function isNonZeroHex(value: string): boolean {
|
|
|
41
74
|
return !/^0+$/.test(value);
|
|
42
75
|
}
|
|
43
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Create a non-zero W3C trace ID.
|
|
79
|
+
*/
|
|
44
80
|
export function createTraceId(): string {
|
|
45
81
|
let traceId = createHexId(32);
|
|
46
82
|
while (!isNonZeroHex(traceId)) {
|
|
@@ -49,6 +85,9 @@ export function createTraceId(): string {
|
|
|
49
85
|
return traceId;
|
|
50
86
|
}
|
|
51
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Create a non-zero W3C span ID.
|
|
90
|
+
*/
|
|
52
91
|
export function createSpanId(): string {
|
|
53
92
|
let spanId = createHexId(16);
|
|
54
93
|
while (!isNonZeroHex(spanId)) {
|
|
@@ -57,6 +96,9 @@ export function createSpanId(): string {
|
|
|
57
96
|
return spanId;
|
|
58
97
|
}
|
|
59
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Create a W3C traceparent header value.
|
|
101
|
+
*/
|
|
60
102
|
export function createTraceparent(args: {
|
|
61
103
|
traceId: string;
|
|
62
104
|
spanId: string;
|
|
@@ -65,6 +107,9 @@ export function createTraceparent(args: {
|
|
|
65
107
|
return `00-${args.traceId}-${args.spanId}-${args.traceFlags ?? "01"}`;
|
|
66
108
|
}
|
|
67
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Parse and validate a W3C traceparent header value.
|
|
112
|
+
*/
|
|
68
113
|
export function parseTraceparent(
|
|
69
114
|
value: string | null | undefined,
|
|
70
115
|
): ParsedTraceparent | undefined {
|
|
@@ -84,6 +129,9 @@ export function parseTraceparent(
|
|
|
84
129
|
};
|
|
85
130
|
}
|
|
86
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Create devtools trace context from explicit IDs or an existing traceparent.
|
|
134
|
+
*/
|
|
87
135
|
export function createDevtoolsTraceContext(
|
|
88
136
|
input: DevtoolsTraceContextInput = {},
|
|
89
137
|
): DevtoolsTraceContext {
|
|
@@ -104,6 +152,9 @@ export function createDevtoolsTraceContext(
|
|
|
104
152
|
};
|
|
105
153
|
}
|
|
106
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Create child trace context from a parent context.
|
|
157
|
+
*/
|
|
107
158
|
export function createChildDevtoolsTraceContext(
|
|
108
159
|
parent: DevtoolsTraceContextInput,
|
|
109
160
|
): DevtoolsTraceContext {
|
package/src/ui.ts
CHANGED
|
@@ -132,8 +132,10 @@ button.danger:hover{border-color:rgba(225,29,72,.24);background:#fff1f2}
|
|
|
132
132
|
.badge-cache{background:#f7fee7;color:#4d7c0f;border-color:#d9f99d}
|
|
133
133
|
.badge-db{background:#ecfeff;color:#0e7490;border-color:#cffafe}
|
|
134
134
|
.badge-mail{background:#fdf2f8;color:#be185d;border-color:#fbcfe8}
|
|
135
|
+
.badge-notifications{background:#f5f3ff;color:#6d28d9;border-color:#ddd6fe}
|
|
135
136
|
.badge-rateLimit{background:#fffbeb;color:#b45309;border-color:#fde68a}
|
|
136
137
|
.badge-storage{background:#f0fdf4;color:#15803d;border-color:#bbf7d0}
|
|
138
|
+
.badge-uploads{background:#f0f9ff;color:#0369a1;border-color:#bae6fd}
|
|
137
139
|
.request-id{font-family:var(--font-mono);font-size:10px;color:var(--text-muted);padding:2px 5px;background:var(--surface-2);border:1px solid var(--border);border-radius:4px}
|
|
138
140
|
.trace-id{font-family:var(--font-mono);font-size:10px;color:var(--accent-strong);padding:2px 5px;background:#eef2ff;border:1px solid rgba(79,70,229,.16);border-radius:4px}
|
|
139
141
|
@media(max-width:760px){
|
|
@@ -221,14 +223,16 @@ button.danger:hover{border-color:rgba(225,29,72,.24);background:#fff1f2}
|
|
|
221
223
|
{id:"db",label:"Database",type:"custom",watcher:"db",watcherName:"db"},
|
|
222
224
|
{id:"cache",label:"Cache",type:"custom",watcher:"cache",watcherName:"cache"},
|
|
223
225
|
{id:"storage",label:"Storage",type:"custom",watcher:"storage",watcherName:"storage"},
|
|
226
|
+
{id:"uploads",label:"Uploads",type:"custom",watcher:"uploads",watcherName:"uploads"},
|
|
224
227
|
{id:"mail",label:"Mail",type:"custom",watcher:"mail",watcherName:"mail"},
|
|
228
|
+
{id:"notifications",label:"Notifications",type:"custom",watcher:"notifications",watcherName:"notifications"},
|
|
225
229
|
{id:"auth",label:"Auth",type:"custom",watcher:"auth",watcherName:"auth"},
|
|
226
230
|
{id:"audit",label:"Audit",type:"custom",watcher:"audit",watcherName:"audit"},
|
|
227
231
|
{id:"rateLimit",label:"Rate limits",type:"custom",watcher:"rateLimit",watcherName:"rateLimit"},
|
|
228
232
|
{id:"custom",label:"Custom",type:"custom",watcher:"custom"}
|
|
229
233
|
];
|
|
230
|
-
const FOCUS_PANEL_TABS={events:true,jobs:true,schedules:true,db:true,cache:true,storage:true,mail:true,auth:true,audit:true,rateLimit:true};
|
|
231
|
-
const BUILT_IN_WATCHERS={requests:true,errors:true,useCases:true,eventBus:true,jobs:true,schedules:true,providers:true,db:true,cache:true,storage:true,mail:true,auth:true,audit:true,rateLimit:true,custom:true};
|
|
234
|
+
const FOCUS_PANEL_TABS={events:true,jobs:true,schedules:true,db:true,cache:true,storage:true,uploads:true,mail:true,notifications:true,auth:true,audit:true,rateLimit:true};
|
|
235
|
+
const BUILT_IN_WATCHERS={requests:true,errors:true,useCases:true,eventBus:true,jobs:true,schedules:true,providers:true,db:true,cache:true,storage:true,uploads:true,mail:true,notifications:true,auth:true,audit:true,rateLimit:true,custom:true};
|
|
232
236
|
let events=[];
|
|
233
237
|
let watchers=[];
|
|
234
238
|
let activeTab=localStorage.getItem("beignet-devtools-tab")||"timeline";
|
|
@@ -331,8 +335,10 @@ button.danger:hover{border-color:rgba(225,29,72,.24);background:#fff1f2}
|
|
|
331
335
|
case "cache": return "cache";
|
|
332
336
|
case "db": return "database";
|
|
333
337
|
case "mail": return "mail";
|
|
338
|
+
case "notifications": return "notifications";
|
|
334
339
|
case "rateLimit": return "rate limit";
|
|
335
340
|
case "storage": return "storage";
|
|
341
|
+
case "uploads": return "uploads";
|
|
336
342
|
default: return "custom";
|
|
337
343
|
}
|
|
338
344
|
}
|
|
@@ -347,8 +353,10 @@ button.danger:hover{border-color:rgba(225,29,72,.24);background:#fff1f2}
|
|
|
347
353
|
case "cache": return "cache";
|
|
348
354
|
case "db": return "db";
|
|
349
355
|
case "mail": return "mail";
|
|
356
|
+
case "notifications": return "notifications";
|
|
350
357
|
case "rateLimit": return "rateLimit";
|
|
351
358
|
case "storage": return "storage";
|
|
359
|
+
case "uploads": return "uploads";
|
|
352
360
|
default: return "custom";
|
|
353
361
|
}
|
|
354
362
|
}
|
|
@@ -564,7 +572,9 @@ button.danger:hover{border-color:rgba(225,29,72,.24);background:#fff1f2}
|
|
|
564
572
|
case "db": return {title:"Database",subtitle:"Queries and database provider diagnostics"};
|
|
565
573
|
case "cache": return {title:"Cache",subtitle:"Reads, writes, deletes, and hit rates"};
|
|
566
574
|
case "storage": return {title:"Storage",subtitle:"Object storage operations"};
|
|
575
|
+
case "uploads": return {title:"Uploads",subtitle:"Upload preparation, signing, and completion"};
|
|
567
576
|
case "mail": return {title:"Mail",subtitle:"Delivery attempts and provider results"};
|
|
577
|
+
case "notifications": return {title:"Notifications",subtitle:"Notification intent and channel delivery"};
|
|
568
578
|
case "auth": return {title:"Auth",subtitle:"Session and authentication activity"};
|
|
569
579
|
case "audit": return {title:"Audit",subtitle:"Durable activity records emitted by application code"};
|
|
570
580
|
case "rateLimit": return {title:"Rate limits",subtitle:"Allowed, blocked, and failed checks"};
|