@beignet/core 0.0.27 → 0.0.28

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.
@@ -340,6 +340,26 @@ export type BeforeSendHook<
340
340
  native?: boolean;
341
341
  }) => MaybePromise<HttpResponseLike | undefined>;
342
342
 
343
+ /**
344
+ * Per-stage timing breakdown of one request, in milliseconds.
345
+ *
346
+ * Stages are measured around the pipeline phases in execution order:
347
+ * `onRequest` hooks, request parsing/validation, context creation, route
348
+ * hooks plus `beforeHandle` hooks, the route handler, and response
349
+ * preparation (`beforeSend`, response validation, and finalizers). Stages
350
+ * that did not run for a request — parsing on raw routes, the handler after
351
+ * a hook short-circuit — report `0`. The stages do not sum exactly to the
352
+ * request `durationMs`; routing and bookkeeping live in the gaps.
353
+ */
354
+ export type RequestStageTimings = {
355
+ onRequestMs: number;
356
+ parseMs: number;
357
+ contextMs: number;
358
+ beforeHandleMs: number;
359
+ handlerMs: number;
360
+ sendMs: number;
361
+ };
362
+
343
363
  /**
344
364
  * Hook that runs after the response has been prepared.
345
365
  *
@@ -360,6 +380,10 @@ export type AfterSendHook<
360
380
  response: HttpResponseLike;
361
381
  error?: unknown;
362
382
  durationMs: number;
383
+ /**
384
+ * Per-stage timing breakdown of this request.
385
+ */
386
+ stages: RequestStageTimings;
363
387
  }) => MaybePromise<void>;
364
388
 
365
389
  /**
@@ -402,10 +426,12 @@ export type ServerUnhandledErrorMapper<
402
426
  /**
403
427
  * Server lifecycle hook collection.
404
428
  *
405
- * Hooks run in the order they are registered. `onRequest` can short-circuit
406
- * before context creation; `beforeHandle` can replace context or short-circuit;
407
- * `beforeSend` can replace the outgoing response; `afterSend` observes the
408
- * final response.
429
+ * Hooks run in the order they are registered within each server-hook phase.
430
+ * `onRequest` can short-circuit before context creation; route hooks resolve
431
+ * route-scoped context before server `beforeHandle`; `beforeHandle` can replace
432
+ * context or short-circuit; `beforeSend` can replace the outgoing response, and
433
+ * route-owned replacements are contract-validated before send; `afterSend`
434
+ * observes the final response.
409
435
  */
410
436
  export interface ServerHook<Ctx, Ports extends AnyPorts = AnyPorts> {
411
437
  /**
@@ -427,7 +453,7 @@ export interface ServerHook<Ctx, Ports extends AnyPorts = AnyPorts> {
427
453
  */
428
454
  onRequest?: OnRequestHook<Ports>;
429
455
  /**
430
- * Runs after request parsing and context creation.
456
+ * Runs after request parsing, context creation, and route hook resolution.
431
457
  */
432
458
  beforeHandle?: BeforeHandleHook<Ctx>;
433
459
  /**
@@ -69,6 +69,8 @@ export {
69
69
  export type {
70
70
  CreateServerOptions,
71
71
  HandlerRouteDef,
72
+ RawRouteBuilder,
73
+ RawRouteInit,
72
74
  RouteDef,
73
75
  RouteGroup,
74
76
  ServerInstance,
@@ -371,7 +371,15 @@ export function createServerInstrumentation<Ctx>(
371
371
  },
372
372
  };
373
373
  },
374
- afterSend: ({ req, ctx, contract, response, error, durationMs }) => {
374
+ afterSend: ({
375
+ req,
376
+ ctx,
377
+ contract,
378
+ response,
379
+ error,
380
+ durationMs,
381
+ stages,
382
+ }) => {
375
383
  try {
376
384
  if (!port) return;
377
385
 
@@ -414,6 +422,7 @@ export function createServerInstrumentation<Ctx>(
414
422
  responseOwner,
415
423
  status: response.status,
416
424
  durationMs,
425
+ stages,
417
426
  details: {
418
427
  headers: requestHeadersToRecord(req.headers),
419
428
  route: {
@@ -0,0 +1,49 @@
1
+ import type { HttpContractConfig } from "../contracts/index.js";
2
+ import type {
3
+ HttpRequestLike,
4
+ HttpResponseLike,
5
+ MaybePromise,
6
+ } from "./http.js";
7
+
8
+ export const responseFinalizerHook = Symbol(
9
+ "@beignet/core/server/responseFinalizer",
10
+ );
11
+
12
+ export type ResponseFinalizerResponseOwner =
13
+ | "route"
14
+ | "framework"
15
+ | "transport";
16
+
17
+ export type ResponseFinalizerValidationState =
18
+ | "validated"
19
+ | "disabled"
20
+ | "not-applicable";
21
+
22
+ export type ResponseFinalizerHookArgs<Ctx> = {
23
+ req: HttpRequestLike;
24
+ ctx?: Ctx;
25
+ contract: HttpContractConfig;
26
+ path?: unknown;
27
+ query?: unknown;
28
+ headers?: unknown;
29
+ body?: unknown;
30
+ response: HttpResponseLike;
31
+ error?: unknown;
32
+ native?: boolean;
33
+ owner: ResponseFinalizerResponseOwner;
34
+ responseValidation: ResponseFinalizerValidationState;
35
+ };
36
+
37
+ export type ResponseFinalizerHook<Ctx> = (
38
+ args: ResponseFinalizerHookArgs<Ctx>,
39
+ ) => MaybePromise<void>;
40
+
41
+ export type ResponseFinalizerServerHook<Ctx> = {
42
+ [responseFinalizerHook]?: ResponseFinalizerHook<Ctx>;
43
+ };
44
+
45
+ export function getResponseFinalizerHook<Ctx>(
46
+ hook: unknown,
47
+ ): ResponseFinalizerHook<Ctx> | undefined {
48
+ return (hook as ResponseFinalizerServerHook<Ctx>)[responseFinalizerHook];
49
+ }