@beignet/devtools 0.0.1
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 +5 -0
- package/README.md +464 -0
- package/dist/access.d.ts +21 -0
- package/dist/access.d.ts.map +1 -0
- package/dist/access.js +20 -0
- package/dist/access.js.map +1 -0
- package/dist/audit.d.ts +10 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +49 -0
- package/dist/audit.js.map +1 -0
- package/dist/events.d.ts +143 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +20 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +114 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +44 -0
- package/dist/index.js.map +1 -0
- package/dist/instrumentation.d.ts +74 -0
- package/dist/instrumentation.d.ts.map +1 -0
- package/dist/instrumentation.js +293 -0
- package/dist/instrumentation.js.map +1 -0
- package/dist/persistence.d.ts +30 -0
- package/dist/persistence.d.ts.map +1 -0
- package/dist/persistence.js +100 -0
- package/dist/persistence.js.map +1 -0
- package/dist/provider-instrumentation.d.ts +9 -0
- package/dist/provider-instrumentation.d.ts.map +1 -0
- package/dist/provider-instrumentation.js +25 -0
- package/dist/provider-instrumentation.js.map +1 -0
- package/dist/provider.d.ts +79 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +293 -0
- package/dist/provider.js.map +1 -0
- package/dist/redaction.d.ts +5 -0
- package/dist/redaction.d.ts.map +1 -0
- package/dist/redaction.js +20 -0
- package/dist/redaction.js.map +1 -0
- package/dist/routes.d.ts +113 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +247 -0
- package/dist/routes.js.map +1 -0
- package/dist/trace-context.d.ts +29 -0
- package/dist/trace-context.d.ts.map +1 -0
- package/dist/trace-context.js +74 -0
- package/dist/trace-context.js.map +1 -0
- package/dist/ui.d.ts +14 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +795 -0
- package/dist/ui.js.map +1 -0
- package/dist/watchers.d.ts +22 -0
- package/dist/watchers.d.ts.map +1 -0
- package/dist/watchers.js +171 -0
- package/dist/watchers.js.map +1 -0
- package/package.json +66 -0
- package/src/access.ts +52 -0
- package/src/audit.ts +71 -0
- package/src/events.ts +193 -0
- package/src/index.ts +136 -0
- package/src/instrumentation.ts +451 -0
- package/src/persistence.ts +163 -0
- package/src/provider-instrumentation.ts +50 -0
- package/src/provider.ts +375 -0
- package/src/redaction.ts +26 -0
- package/src/routes.ts +317 -0
- package/src/trace-context.ts +115 -0
- package/src/ui.ts +807 -0
- package/src/watchers.ts +235 -0
package/dist/provider.js
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Devtools provider implementation
|
|
3
|
+
*
|
|
4
|
+
* Provides an in-memory event buffer that can be attached to ctx.ports.devtools.
|
|
5
|
+
* The provider is disabled by default in production environments.
|
|
6
|
+
*/
|
|
7
|
+
import { createProvider } from "@beignet/core/providers";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { createFileDevtoolsStore, } from "./persistence";
|
|
10
|
+
import { applyDevtoolsRedaction, createRedactionFailureEvent, } from "./redaction";
|
|
11
|
+
import { isDevtoolsEventEnabled, isDevtoolsWatcherEnabled, resolveDevtoolsWatchers, } from "./watchers";
|
|
12
|
+
/**
|
|
13
|
+
* Maximum number of events to keep in the buffer.
|
|
14
|
+
* When this limit is reached, the oldest events are removed.
|
|
15
|
+
*/
|
|
16
|
+
const MAX_EVENTS = 500;
|
|
17
|
+
function createEventId() {
|
|
18
|
+
if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
|
|
19
|
+
return crypto.randomUUID();
|
|
20
|
+
}
|
|
21
|
+
return `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
22
|
+
}
|
|
23
|
+
export function createDevtoolsEvent(event) {
|
|
24
|
+
return {
|
|
25
|
+
...event,
|
|
26
|
+
id: event.id ?? createEventId(),
|
|
27
|
+
timestamp: event.timestamp ?? new Date().toISOString(),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Create an in-memory devtools implementation.
|
|
32
|
+
*
|
|
33
|
+
* This implementation stores events in a bounded buffer (max 500 events).
|
|
34
|
+
* When the buffer is full, the oldest events are removed to make room for new ones.
|
|
35
|
+
*
|
|
36
|
+
* @returns A DevtoolsPort implementation
|
|
37
|
+
*/
|
|
38
|
+
export function createInMemoryDevtools(options = {}) {
|
|
39
|
+
const listeners = new Set();
|
|
40
|
+
const maxEvents = options.maxEvents ?? MAX_EVENTS;
|
|
41
|
+
const watchers = resolveDevtoolsWatchers(options.watchers);
|
|
42
|
+
const events = [];
|
|
43
|
+
function notify(event) {
|
|
44
|
+
for (const listener of listeners) {
|
|
45
|
+
try {
|
|
46
|
+
listener(event);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Subscribers are observers; they must not affect devtools storage.
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function getErrorMessage(error) {
|
|
54
|
+
if (error instanceof Error)
|
|
55
|
+
return error.message;
|
|
56
|
+
if (typeof error === "string")
|
|
57
|
+
return error;
|
|
58
|
+
return "Unknown error";
|
|
59
|
+
}
|
|
60
|
+
function persistEvent(event) {
|
|
61
|
+
if (!options.store?.append)
|
|
62
|
+
return;
|
|
63
|
+
void Promise.resolve(options.store.append(event, [...events])).catch((error) => {
|
|
64
|
+
storeInternalError({
|
|
65
|
+
type: "error",
|
|
66
|
+
message: "Devtools persistence failed",
|
|
67
|
+
details: {
|
|
68
|
+
message: getErrorMessage(error),
|
|
69
|
+
store: options.store?.name,
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
async function clearPersistedEvents() {
|
|
75
|
+
if (!options.store?.clear)
|
|
76
|
+
return;
|
|
77
|
+
try {
|
|
78
|
+
await options.store.clear();
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
storeInternalError({
|
|
82
|
+
type: "error",
|
|
83
|
+
message: "Devtools persistence clear failed",
|
|
84
|
+
details: {
|
|
85
|
+
message: getErrorMessage(error),
|
|
86
|
+
store: options.store?.name,
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function store(event, storeOptions = {}) {
|
|
92
|
+
events.push(event);
|
|
93
|
+
if (events.length > maxEvents) {
|
|
94
|
+
events.splice(0, events.length - maxEvents);
|
|
95
|
+
}
|
|
96
|
+
if (storeOptions.notify !== false) {
|
|
97
|
+
notify({ type: "record", event });
|
|
98
|
+
}
|
|
99
|
+
if (storeOptions.persist !== false) {
|
|
100
|
+
persistEvent(event);
|
|
101
|
+
}
|
|
102
|
+
return event;
|
|
103
|
+
}
|
|
104
|
+
function redact(event) {
|
|
105
|
+
try {
|
|
106
|
+
return applyDevtoolsRedaction(event, options.redact);
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
return createRedactionFailureEvent(error);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function storeInternalError(event) {
|
|
113
|
+
const normalized = createDevtoolsEvent(event);
|
|
114
|
+
const redacted = redact(normalized);
|
|
115
|
+
if (!isDevtoolsEventEnabled(watchers, redacted))
|
|
116
|
+
return redacted;
|
|
117
|
+
return store(redacted, { persist: false });
|
|
118
|
+
}
|
|
119
|
+
for (const event of options.initialEvents ?? []) {
|
|
120
|
+
const redacted = redact(event);
|
|
121
|
+
if (isDevtoolsEventEnabled(watchers, redacted)) {
|
|
122
|
+
store(redacted, { notify: false, persist: false });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function log(event) {
|
|
126
|
+
const redacted = redact(event);
|
|
127
|
+
if (!isDevtoolsEventEnabled(watchers, redacted))
|
|
128
|
+
return;
|
|
129
|
+
store(redacted);
|
|
130
|
+
}
|
|
131
|
+
function record(event) {
|
|
132
|
+
const normalized = createDevtoolsEvent(event);
|
|
133
|
+
const redacted = redact(normalized);
|
|
134
|
+
if (!isDevtoolsEventEnabled(watchers, redacted))
|
|
135
|
+
return redacted;
|
|
136
|
+
return store(redacted);
|
|
137
|
+
}
|
|
138
|
+
function getEvents(filter) {
|
|
139
|
+
let result = [...events];
|
|
140
|
+
if (filter?.type) {
|
|
141
|
+
result = result.filter((e) => e.type === filter.type);
|
|
142
|
+
}
|
|
143
|
+
if (filter?.requestId) {
|
|
144
|
+
result = result.filter((e) => e.requestId === filter.requestId);
|
|
145
|
+
}
|
|
146
|
+
if (filter?.traceId) {
|
|
147
|
+
result = result.filter((e) => e.traceId === filter.traceId);
|
|
148
|
+
}
|
|
149
|
+
const limit = filter?.limit ?? 200;
|
|
150
|
+
if (result.length > limit) {
|
|
151
|
+
result = result.slice(result.length - limit);
|
|
152
|
+
}
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
155
|
+
async function clear() {
|
|
156
|
+
events.length = 0;
|
|
157
|
+
notify({ type: "clear" });
|
|
158
|
+
await clearPersistedEvents();
|
|
159
|
+
}
|
|
160
|
+
function getWatchers() {
|
|
161
|
+
return watchers.map((watcher) => ({ ...watcher }));
|
|
162
|
+
}
|
|
163
|
+
function isWatcherEnabled(name) {
|
|
164
|
+
return isDevtoolsWatcherEnabled(watchers, name);
|
|
165
|
+
}
|
|
166
|
+
function subscribe(listener) {
|
|
167
|
+
listeners.add(listener);
|
|
168
|
+
return () => {
|
|
169
|
+
listeners.delete(listener);
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
log,
|
|
174
|
+
record,
|
|
175
|
+
subscribe,
|
|
176
|
+
getEvents,
|
|
177
|
+
clear,
|
|
178
|
+
getWatchers,
|
|
179
|
+
isWatcherEnabled,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Configuration schema for the devtools provider.
|
|
184
|
+
*/
|
|
185
|
+
const DevtoolsConfigSchema = z.object({
|
|
186
|
+
ENABLED: z
|
|
187
|
+
.string()
|
|
188
|
+
.optional()
|
|
189
|
+
.transform((v) => (v === undefined ? undefined : v === "true")),
|
|
190
|
+
MAX_EVENTS: z.coerce.number().int().positive().optional(),
|
|
191
|
+
PERSIST: z
|
|
192
|
+
.string()
|
|
193
|
+
.optional()
|
|
194
|
+
.transform((v) => (v === undefined ? undefined : v === "true")),
|
|
195
|
+
PERSIST_PATH: z.string().optional(),
|
|
196
|
+
});
|
|
197
|
+
/**
|
|
198
|
+
* Devtools service provider.
|
|
199
|
+
*
|
|
200
|
+
* This provider attaches a DevtoolsPort to ctx.ports.devtools.
|
|
201
|
+
* It can be enabled/disabled via the DEVTOOLS_ENABLED environment variable.
|
|
202
|
+
* By default, it's enabled in non-production environments and disabled in production.
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```ts
|
|
206
|
+
* import { createDevtoolsProvider } from "@beignet/devtools";
|
|
207
|
+
* import { createServer } from "@beignet/core/server";
|
|
208
|
+
*
|
|
209
|
+
* const server = await createServer({
|
|
210
|
+
* ports,
|
|
211
|
+
* providers: [createDevtoolsProvider()],
|
|
212
|
+
* });
|
|
213
|
+
* ```
|
|
214
|
+
*
|
|
215
|
+
* To explicitly enable/disable:
|
|
216
|
+
* ```bash
|
|
217
|
+
* DEVTOOLS_ENABLED=true npm run dev
|
|
218
|
+
* DEVTOOLS_ENABLED=false npm run dev
|
|
219
|
+
* ```
|
|
220
|
+
*/
|
|
221
|
+
export function createDevtoolsProvider(options = {}) {
|
|
222
|
+
return createProvider({
|
|
223
|
+
name: "devtools",
|
|
224
|
+
config: {
|
|
225
|
+
schema: DevtoolsConfigSchema,
|
|
226
|
+
envPrefix: "DEVTOOLS_",
|
|
227
|
+
},
|
|
228
|
+
async setup({ config }) {
|
|
229
|
+
const enabled = options.enabled ??
|
|
230
|
+
config?.ENABLED ??
|
|
231
|
+
process.env.NODE_ENV !== "production";
|
|
232
|
+
if (!enabled) {
|
|
233
|
+
const watchers = resolveDevtoolsWatchers(options.watchers).map((watcher) => ({
|
|
234
|
+
...watcher,
|
|
235
|
+
enabled: false,
|
|
236
|
+
}));
|
|
237
|
+
// Provide a no-op implementation so callers don't need null checks
|
|
238
|
+
const noop = {
|
|
239
|
+
log: () => { },
|
|
240
|
+
record: (event) => createDevtoolsEvent(event),
|
|
241
|
+
subscribe: () => () => { },
|
|
242
|
+
getEvents: () => [],
|
|
243
|
+
clear: () => { },
|
|
244
|
+
getWatchers: () => watchers.map((watcher) => ({ ...watcher })),
|
|
245
|
+
isWatcherEnabled: () => false,
|
|
246
|
+
};
|
|
247
|
+
return { ports: { devtools: noop } };
|
|
248
|
+
}
|
|
249
|
+
const maxEvents = config?.MAX_EVENTS ?? options.maxEvents ?? MAX_EVENTS;
|
|
250
|
+
const configuredStore = typeof options.store === "function"
|
|
251
|
+
? await options.store()
|
|
252
|
+
: options.store;
|
|
253
|
+
let store = configuredStore ??
|
|
254
|
+
(config?.PERSIST || config?.PERSIST_PATH
|
|
255
|
+
? createFileDevtoolsStore({
|
|
256
|
+
filePath: config.PERSIST_PATH,
|
|
257
|
+
maxEvents,
|
|
258
|
+
})
|
|
259
|
+
: undefined);
|
|
260
|
+
let initialEvents = [];
|
|
261
|
+
let loadError;
|
|
262
|
+
if (store?.load) {
|
|
263
|
+
try {
|
|
264
|
+
initialEvents = await store.load();
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
loadError = error;
|
|
268
|
+
store = undefined;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
const devtools = createInMemoryDevtools({
|
|
272
|
+
maxEvents,
|
|
273
|
+
redact: options.redact,
|
|
274
|
+
watchers: options.watchers,
|
|
275
|
+
initialEvents,
|
|
276
|
+
store,
|
|
277
|
+
});
|
|
278
|
+
if (loadError) {
|
|
279
|
+
devtools.record({
|
|
280
|
+
type: "error",
|
|
281
|
+
message: "Devtools persistence load failed",
|
|
282
|
+
details: {
|
|
283
|
+
message: loadError instanceof Error
|
|
284
|
+
? loadError.message
|
|
285
|
+
: String(loadError),
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
return { ports: { devtools } };
|
|
290
|
+
},
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { DevtoolsEvent, DevtoolsRedactor } from "./events";
|
|
2
|
+
export declare const defaultDevtoolsRedactor: DevtoolsRedactor;
|
|
3
|
+
export declare function applyDevtoolsRedaction(event: DevtoolsEvent, redact?: DevtoolsRedactor): DevtoolsEvent;
|
|
4
|
+
export declare function createRedactionFailureEvent(error: unknown): DevtoolsEvent;
|
|
5
|
+
//# sourceMappingURL=redaction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redaction.d.ts","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEhE,eAAO,MAAM,uBAAuB,EAAE,gBAClB,CAAC;AAErB,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,aAAa,EACpB,MAAM,CAAC,EAAE,gBAAgB,GACxB,aAAa,CAIf;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CAUzE"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { redactValue } from "@beignet/core/ports";
|
|
2
|
+
export const defaultDevtoolsRedactor = (event) => redactValue(event);
|
|
3
|
+
export function applyDevtoolsRedaction(event, redact) {
|
|
4
|
+
const redacted = defaultDevtoolsRedactor(event);
|
|
5
|
+
if (!redact)
|
|
6
|
+
return redacted;
|
|
7
|
+
return redact(redacted);
|
|
8
|
+
}
|
|
9
|
+
export function createRedactionFailureEvent(error) {
|
|
10
|
+
return {
|
|
11
|
+
id: `${Date.now()}-${Math.random().toString(16).slice(2)}`,
|
|
12
|
+
timestamp: new Date().toISOString(),
|
|
13
|
+
type: "error",
|
|
14
|
+
message: "Devtools redactor failed",
|
|
15
|
+
details: {
|
|
16
|
+
message: error instanceof Error ? error.message : String(error),
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=redaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redaction.js","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGlD,MAAM,CAAC,MAAM,uBAAuB,GAAqB,CAAC,KAAK,EAAE,EAAE,CACjE,WAAW,CAAC,KAAK,CAAC,CAAC;AAErB,MAAM,UAAU,sBAAsB,CACpC,KAAoB,EACpB,MAAyB;IAEzB,MAAM,QAAQ,GAAG,uBAAuB,CAAC,KAAK,CAAC,CAAC;IAChD,IAAI,CAAC,MAAM;QAAE,OAAO,QAAQ,CAAC;IAC7B,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,KAAc;IACxD,OAAO;QACL,EAAE,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;QAC1D,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,IAAI,EAAE,OAAO;QACb,OAAO,EAAE,0BAA0B;QACnC,OAAO,EAAE;YACP,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SAChE;KACF,CAAC;AACJ,CAAC"}
|
package/dist/routes.d.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP handlers for devtools API
|
|
3
|
+
*
|
|
4
|
+
* These handlers can be used to expose devtools data via HTTP endpoints
|
|
5
|
+
* in Next.js or any other framework that uses the Fetch API Request/Response.
|
|
6
|
+
*/
|
|
7
|
+
import { type DevtoolsRouteAccessOptions } from "./access";
|
|
8
|
+
import type { DevtoolsPort } from "./index";
|
|
9
|
+
export interface DevtoolsRequestOptions extends DevtoolsRouteAccessOptions {
|
|
10
|
+
/**
|
|
11
|
+
* URL path prefix where devtools routes are mounted.
|
|
12
|
+
*
|
|
13
|
+
* @example "/api/devtools"
|
|
14
|
+
*/
|
|
15
|
+
basePath: string;
|
|
16
|
+
}
|
|
17
|
+
export interface DevtoolsRouteHandlers {
|
|
18
|
+
GET(req: Request): Promise<Response>;
|
|
19
|
+
POST(req: Request): Promise<Response>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Handle GET requests to list devtools events with optional filtering.
|
|
23
|
+
*
|
|
24
|
+
* Query parameters:
|
|
25
|
+
* - type: Filter by event type (request, error, usecase, eventBus, job, schedule, provider)
|
|
26
|
+
* - requestId: Filter by request correlation ID
|
|
27
|
+
* - traceId: Filter by W3C trace ID
|
|
28
|
+
* - limit: Maximum number of events to return (default: 200)
|
|
29
|
+
*
|
|
30
|
+
* @param req - The incoming HTTP request
|
|
31
|
+
* @param devtools - The DevtoolsPort instance to query
|
|
32
|
+
* @returns JSON response with events array
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* // In a Next.js route handler:
|
|
37
|
+
* // app/api/devtools/core/events/route.ts
|
|
38
|
+
* import { handleDevtoolsEventsRequest } from "@beignet/devtools";
|
|
39
|
+
*
|
|
40
|
+
* export async function GET(req: Request) {
|
|
41
|
+
* const devtools = getAppDevtoolsPort(); // app-specific helper
|
|
42
|
+
* return handleDevtoolsEventsRequest(req, devtools);
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export declare function handleDevtoolsEventsRequest(req: Request, devtools: DevtoolsPort, options?: DevtoolsRouteAccessOptions): Promise<Response>;
|
|
47
|
+
/**
|
|
48
|
+
* Handle POST requests to clear all devtools events.
|
|
49
|
+
*
|
|
50
|
+
* This endpoint requires a POST request and will clear the in-memory event buffer.
|
|
51
|
+
*
|
|
52
|
+
* @param req - The incoming HTTP request
|
|
53
|
+
* @param devtools - The DevtoolsPort instance to clear
|
|
54
|
+
* @returns JSON response with success status
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* // In a Next.js route handler:
|
|
59
|
+
* // app/api/devtools/clear/route.ts
|
|
60
|
+
* import { handleDevtoolsClearRequest } from "@beignet/devtools";
|
|
61
|
+
*
|
|
62
|
+
* export async function POST(req: Request) {
|
|
63
|
+
* const devtools = getAppDevtoolsPort(); // app-specific helper
|
|
64
|
+
* return handleDevtoolsClearRequest(req, devtools);
|
|
65
|
+
* }
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export declare function handleDevtoolsClearRequest(req: Request, devtools: DevtoolsPort, options?: DevtoolsRouteAccessOptions): Promise<Response>;
|
|
69
|
+
/**
|
|
70
|
+
* Handle GET requests for a live Server-Sent Events stream.
|
|
71
|
+
*
|
|
72
|
+
* The stream emits:
|
|
73
|
+
* - `snapshot` with the current event buffer
|
|
74
|
+
* - `event` when a new event is recorded
|
|
75
|
+
* - `clear` when the buffer is cleared
|
|
76
|
+
*/
|
|
77
|
+
export declare function handleDevtoolsStreamRequest(req: Request, devtools: DevtoolsPort, options?: DevtoolsRouteAccessOptions): Promise<Response>;
|
|
78
|
+
/**
|
|
79
|
+
* Unified devtools request handler.
|
|
80
|
+
*
|
|
81
|
+
* Routes requests to the appropriate handler based on the URL path:
|
|
82
|
+
* - `{basePath}/core/events` → event list (GET)
|
|
83
|
+
* - `{basePath}/stream` → live event stream (GET)
|
|
84
|
+
* - `{basePath}/clear` → clear buffer (POST)
|
|
85
|
+
* - `{basePath}` → dashboard UI (GET)
|
|
86
|
+
*
|
|
87
|
+
* This lets you wire up a single catch-all route instead of three separate ones.
|
|
88
|
+
*
|
|
89
|
+
* @param req - The incoming HTTP request
|
|
90
|
+
* @param devtools - The DevtoolsPort instance
|
|
91
|
+
* @param options - Devtools route options including base path and access policy
|
|
92
|
+
* @returns Response for the matched sub-route
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```ts
|
|
96
|
+
* // Next.js catch-all route: app/api/devtools/[[...path]]/route.ts
|
|
97
|
+
* import { createDevtoolsRoute } from "@beignet/devtools";
|
|
98
|
+
* import { getDevtools } from "@/server";
|
|
99
|
+
*
|
|
100
|
+
* export const { GET, POST } = createDevtoolsRoute(getDevtools(), {
|
|
101
|
+
* basePath: "/api/devtools",
|
|
102
|
+
* });
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
export declare function handleDevtoolsRequest(req: Request, devtools: DevtoolsPort, options: DevtoolsRequestOptions): Promise<Response>;
|
|
106
|
+
/**
|
|
107
|
+
* Create GET and POST handlers for a devtools catch-all route.
|
|
108
|
+
*
|
|
109
|
+
* This is the recommended integration for frameworks such as Next.js that
|
|
110
|
+
* export HTTP method functions from route modules.
|
|
111
|
+
*/
|
|
112
|
+
export declare function createDevtoolsRoute(devtools: DevtoolsPort, options: DevtoolsRequestOptions): DevtoolsRouteHandlers;
|
|
113
|
+
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAEL,KAAK,0BAA0B,EAChC,MAAM,UAAU,CAAC;AAElB,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,SAAS,CAAC;AAG5D,MAAM,WAAW,sBAAuB,SAAQ,0BAA0B;IACxE;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,GAAG,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IACrC,IAAI,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CACvC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,2BAA2B,CAC/C,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,YAAY,EACtB,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,QAAQ,CAAC,CAkDnB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,YAAY,EACtB,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,QAAQ,CAAC,CAiBnB;AAQD;;;;;;;GAOG;AACH,wBAAsB,2BAA2B,CAC/C,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,YAAY,EACtB,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,QAAQ,CAAC,CA+DnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,QAAQ,CAAC,CA6BnB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,sBAAsB,GAC9B,qBAAqB,CAQvB"}
|