stitchkit 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -4,9 +4,9 @@ import {
4
4
  emitResult,
5
5
  parseCliArgs,
6
6
  pollUntilDone
7
- } from "./index-f39j6twc.js";
7
+ } from "./index-eyc38rgc.js";
8
8
  import"./index-0ed3bx43.js";
9
- import"./index-jgpsd7dy.js";
9
+ import"./index-fq89tdex.js";
10
10
  import"./index-tm7dqzxc.js";
11
11
  export {
12
12
  pollUntilDone,
@@ -7,7 +7,7 @@ import {
7
7
  isWithinDir,
8
8
  normalizeError,
9
9
  validateHandlerOutput
10
- } from "./index-jgpsd7dy.js";
10
+ } from "./index-fq89tdex.js";
11
11
  import {
12
12
  isRecord,
13
13
  isUnsafeKey,
@@ -76,6 +76,13 @@ function formatZodError(error) {
76
76
  return lines.join(`
77
77
  `) + suffix;
78
78
  }
79
+ function errorCode(err) {
80
+ if (AppError.is(err))
81
+ return err.code;
82
+ if (err instanceof z2.ZodError)
83
+ return "VALIDATION_ERROR";
84
+ return;
85
+ }
79
86
  function normalizeError(err) {
80
87
  if (AppError.is(err))
81
88
  return err;
@@ -102,4 +109,4 @@ function isWithinDir(root, target) {
102
109
  return target === root || target === base || target.startsWith(base + sep);
103
110
  }
104
111
 
105
- export { AppError, notFound, badRequest, unauthorized, forbidden, conflict, rateLimited, STITCH_ERROR_STATUS, isStitchErrorCode, appError, formatZodError, normalizeError, validateHandlerOutput, isWithinDir };
112
+ export { AppError, notFound, badRequest, unauthorized, forbidden, conflict, rateLimited, STITCH_ERROR_STATUS, isStitchErrorCode, appError, formatZodError, errorCode, normalizeError, validateHandlerOutput, isWithinDir };
@@ -1,10 +1,11 @@
1
1
  import {
2
2
  AppError,
3
3
  badRequest,
4
+ errorCode,
4
5
  isWithinDir,
5
6
  normalizeError,
6
7
  validateHandlerOutput
7
- } from "./index-jgpsd7dy.js";
8
+ } from "./index-fq89tdex.js";
8
9
  import {
9
10
  extractIp,
10
11
  getClientInfo,
@@ -446,8 +447,8 @@ function levelForStatus(status) {
446
447
  return "warn";
447
448
  return "info";
448
449
  }
449
- function buildLogFields(method, path, status, durationMs, traceId, errorCode) {
450
- return { traceId, method, path, status, durationMs, ...errorCode && { errorCode } };
450
+ function buildLogFields(method, path, status, durationMs, traceId, errorCode2) {
451
+ return { traceId, method, path, status, durationMs, ...errorCode2 && { errorCode: errorCode2 } };
451
452
  }
452
453
  function formatMs(ms) {
453
454
  if (ms >= 1000)
@@ -491,20 +492,20 @@ function logIncoming(req, pathname, traceId, ipAddress) {
491
492
  }
492
493
  return log;
493
494
  }
494
- function logOutgoing(req, pathname, status, log, ipAddress, errorCode) {
495
+ function logOutgoing(req, pathname, status, log, ipAddress, errorCode2) {
495
496
  const ms = elapsedMs(log.startTime);
496
497
  if (isProd) {
497
498
  console.log(JSON.stringify({
498
499
  ts: new Date().toISOString(),
499
500
  level: levelForStatus(status),
500
501
  msg: `${req.method} ${pathname} ${status}`,
501
- ...buildLogFields(req.method, pathname, status, Math.round(ms), log.traceId, errorCode),
502
+ ...buildLogFields(req.method, pathname, status, Math.round(ms), log.traceId, errorCode2),
502
503
  ip: ipAddress
503
504
  }));
504
505
  return;
505
506
  }
506
507
  const mc = METHOD_COLOR[req.method] ?? c.dim;
507
- const code = errorCode ? ` ${c.red}${errorCode}${c.reset}` : "";
508
+ const code = errorCode2 ? ` ${c.red}${errorCode2}${c.reset}` : "";
508
509
  console.log(`${c.gray}[${timestamp()}]${c.reset} ${mc}${req.method}${c.reset} ${c.dim}${log.traceId}${c.reset} ${c.cyan}←${c.reset} ${safePath(pathname)} ${statusColor(status)}${status}${c.reset}${code} ${durationColor(ms)}${formatMs(ms)}${c.reset} ${ipLabel(ipAddress ?? "")}`);
509
510
  }
510
511
 
@@ -534,15 +535,15 @@ function createHandler(config) {
534
535
  });
535
536
  reqLog = { traceId, startTime: performance.now() };
536
537
  }
537
- const logDone = (status, errorCode) => {
538
+ const logDone = (status, errorCode2) => {
538
539
  if (!reqLog)
539
540
  return;
540
541
  if (useDefaultLog)
541
- logOutgoing(req, url.pathname, status, reqLog, ipAddress, errorCode);
542
+ logOutgoing(req, url.pathname, status, reqLog, ipAddress, errorCode2);
542
543
  if (customLogger) {
543
544
  const durationMs = Math.round(elapsedMs(reqLog.startTime));
544
545
  const level = levelForStatus(status);
545
- customLogger[level](`${req.method} ${url.pathname} ${status}${errorCode ? ` ${errorCode}` : ""} ${durationMs}ms`, buildLogFields(req.method, url.pathname, status, durationMs, reqLog.traceId, errorCode));
546
+ customLogger[level](`${req.method} ${url.pathname} ${status}${errorCode2 ? ` ${errorCode2}` : ""} ${durationMs}ms`, buildLogFields(req.method, url.pathname, status, durationMs, reqLog.traceId, errorCode2));
546
547
  }
547
548
  };
548
549
  const respondError = async (err, errCtx, endpoint) => {
@@ -551,7 +552,7 @@ function createHandler(config) {
551
552
  const response = await hooks.onError(errCtx ?? buildErrorContext(req, url, traceId, clientIp), err, endpoint);
552
553
  if (response instanceof Response) {
553
554
  const withCors = applyCors(response, cors, req);
554
- logDone(withCors.status);
555
+ logDone(withCors.status, errorCode(err));
555
556
  return withCors;
556
557
  }
557
558
  } catch {}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  forbidden,
3
3
  unauthorized
4
- } from "./index-jgpsd7dy.js";
4
+ } from "./index-fq89tdex.js";
5
5
  import {
6
6
  isRecord,
7
7
  isUnsafeKey,
@@ -1,6 +1,13 @@
1
1
  import { type ZodType, z } from 'zod';
2
2
  import { AppError } from '../contract';
3
3
  export declare function formatZodError(error: z.ZodError): string;
4
+ /**
5
+ * The stable error code for a thrown value — `AppError.code`, `VALIDATION_ERROR`
6
+ * for a `ZodError`, else `undefined`. Side-effect-free (unlike `normalizeError`,
7
+ * it never logs): for access-log attribution on a path where the response is
8
+ * produced elsewhere — a custom `onError` hook that returns its own `Response`.
9
+ */
10
+ export declare function errorCode(err: unknown): string | undefined;
4
11
  export declare function normalizeError(err: unknown): AppError;
5
12
  /**
6
13
  * Validate a handler's return value against the contract `output` schema. A
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/internal/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,wBAAgB,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,QAAQ,GAAG,MAAM,CASxD;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,QAAQ,CAYrD;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,OAAO,EACf,IAAI,EAAE,OAAO,GACZ;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAO9D"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/internal/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,wBAAgB,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,QAAQ,GAAG,MAAM,CASxD;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAI1D;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,QAAQ,CAYrD;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,OAAO,EACf,IAAI,EAAE,OAAO,GACZ;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAO9D"}
package/dist/node.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  createImplement,
4
4
  createSocketIOServer,
5
5
  implement
6
- } from "./index-renxz7c2.js";
6
+ } from "./index-kb7wxdq0.js";
7
7
  import {
8
8
  AppError,
9
9
  appError,
@@ -13,7 +13,7 @@ import {
13
13
  notFound,
14
14
  rateLimited,
15
15
  unauthorized
16
- } from "./index-jgpsd7dy.js";
16
+ } from "./index-fq89tdex.js";
17
17
  import"./index-fwqnkc90.js";
18
18
  import"./index-tm7dqzxc.js";
19
19
  // src/server/node.ts
@@ -1 +1 @@
1
- {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/observability/audit.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAc,MAAM,kBAAkB,CAAC;AAElE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAe,KAAK,eAAe,EAAmB,MAAM,YAAY,CAAC;AAGhF,oCAAoC;AACpC,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,KAAK,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,iFAAiF;IACjF,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC;IAC1C,yEAAyE;IACzE,QAAQ,CAAC,EAAE,eAAe,CAAC;CAC5B;AAED,qEAAqE;AACrE,MAAM,WAAW,SAAS;IACxB;;;;OAIG;IACH,IAAI,EAAE,CAAC,CAAC,EACN,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,KACpD,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpD;;;OAGG;IACH,QAAQ,EAAE,aAAa,CAAC;CACzB;AAmBD,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,CAsG9D"}
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/observability/audit.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAc,MAAM,kBAAkB,CAAC;AAElE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAe,KAAK,eAAe,EAAmB,MAAM,YAAY,CAAC;AAGhF,oCAAoC;AACpC,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,KAAK,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,iFAAiF;IACjF,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC;IAC1C,yEAAyE;IACzE,QAAQ,CAAC,EAAE,eAAe,CAAC;CAC5B;AAED,qEAAqE;AACrE,MAAM,WAAW,SAAS;IACxB;;;;OAIG;IACH,IAAI,EAAE,CAAC,CAAC,EACN,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,KACpD,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpD;;;OAGG;IACH,QAAQ,EAAE,aAAa,CAAC;CACzB;AAmBD,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,CA6G9D"}
@@ -1,5 +1,4 @@
1
1
  import type { TransportSource } from '../contract';
2
- import type { JsonValue } from './sanitize';
3
2
  import { type TraceContext } from './trace';
4
3
  /** Everything known about the request in flight. */
5
4
  export interface RequestContext {
@@ -33,11 +32,12 @@ export interface RequestContext {
33
32
  * `RequestEvent.dimensions`. Set via `setRequestDimensions`.
34
33
  */
35
34
  dimensions?: Record<string, string>;
36
- /** Error outcome — set late, by the error handler. */
35
+ /** Error outcome — set late, by the error handler. `details` is sanitised into
36
+ * `RequestEvent.errorDetail` at emit, so it is accepted loosely here. */
37
37
  error?: {
38
38
  code?: string;
39
39
  message?: string;
40
- details?: JsonValue;
40
+ details?: unknown;
41
41
  };
42
42
  }
43
43
  /** Run `fn` with `ctx` as the active request context. */
@@ -74,13 +74,15 @@ export declare function setRequestDimensions(dimensions: Record<string, string>)
74
74
  /**
75
75
  * Record the error outcome on the active context. Call this from the error
76
76
  * handler — the audit hook reads it when the request completes. Optional
77
- * `details` carries structure the message string flattens (e.g. the failing
78
- * Zod issues) onto `RequestEvent.errorDetail`.
77
+ * `details` carries the structure the message string flattens (e.g. the failing
78
+ * Zod issues, or an `AppError.details`); it is accepted as `unknown` and
79
+ * **sanitised** into `RequestEvent.errorDetail` at emit — pass it raw, no need to
80
+ * pre-launder the type.
79
81
  */
80
82
  export declare function setRequestError(error: {
81
83
  code?: string;
82
84
  message?: string;
83
- details?: JsonValue;
85
+ details?: unknown;
84
86
  }): void;
85
87
  /** Options for `wrapInRequestContext`. */
86
88
  export interface WrapRequestContextOptions {
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/observability/context.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,SAAS,CAAC;AAEjE,oDAAoD;AACpD,MAAM,WAAW,cAAc;IAC7B,sCAAsC;IACtC,KAAK,EAAE,YAAY,CAAC;IACpB,4CAA4C;IAC5C,MAAM,EAAE,eAAe,CAAC;IACxB,iBAAiB;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,sDAAsD;IACtD,KAAK,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,SAAS,CAAA;KAAE,CAAC;CAClE;AAID,yDAAyD;AACzD,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,GAAG,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAE5E;AAED,sEAAsE;AACtE,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,SAAS,CAE9D;AAED,mEAAmE;AACnE,wBAAgB,UAAU,IAAI,MAAM,GAAG,SAAS,CAE/C;AAED,qDAAqD;AACrD,wBAAgB,SAAS,IAAI,MAAM,GAAG,SAAS,CAE9C;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAGnD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAM5E;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAG7E;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB,GAAG,IAAI,CAGP;AAED,0CAA0C;AAC1C,MAAM,WAAW,yBAAyB;IACxC;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EACpC,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,EACvD,OAAO,GAAE,yBAA8B,GACtC,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,CAiBhD"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/observability/context.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,SAAS,CAAC;AAEjE,oDAAoD;AACpD,MAAM,WAAW,cAAc;IAC7B,sCAAsC;IACtC,KAAK,EAAE,YAAY,CAAC;IACpB,4CAA4C;IAC5C,MAAM,EAAE,eAAe,CAAC;IACxB,iBAAiB;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC;8EAC0E;IAC1E,KAAK,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAChE;AAID,yDAAyD;AACzD,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,GAAG,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAE5E;AAED,sEAAsE;AACtE,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,SAAS,CAE9D;AAED,mEAAmE;AACnE,wBAAgB,UAAU,IAAI,MAAM,GAAG,SAAS,CAE/C;AAED,qDAAqD;AACrD,wBAAgB,SAAS,IAAI,MAAM,GAAG,SAAS,CAE9C;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAGnD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAM5E;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAG7E;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,IAAI,CAGP;AAED,0CAA0C;AAC1C,MAAM,WAAW,yBAAyB;IACxC;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EACpC,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,EACvD,OAAO,GAAE,yBAA8B,GACtC,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,CAiBhD"}
@@ -1,4 +1,4 @@
1
- import type { TransportSource } from '../contract';
1
+ import type { HttpMethod, TransportSource } from '../contract';
2
2
  import type { JsonValue } from './sanitize';
3
3
  /**
4
4
  * A normalised audit event — one shape for a completed call on any surface
@@ -11,6 +11,13 @@ export interface RequestEvent {
11
11
  source: TransportSource;
12
12
  /** HTTP verb, or `TOOL` for a tool call. */
13
13
  method: string;
14
+ /**
15
+ * The operation's contract verb (`GET` / `POST` / …). Set on **tool** events
16
+ * (whose `method` is `TOOL`) so a single filter can tell a read from a write
17
+ * across HTTP and tool calls — `(event.httpMethod ?? event.method) !== 'GET'`.
18
+ * Omitted on HTTP events, where `method` already is the verb. → ADR 0030.
19
+ */
20
+ httpMethod?: HttpMethod;
14
21
  /** Request path — `/api/...` for HTTP, `/{source}/{tool}` for a tool call. */
15
22
  path: string;
16
23
  /**
@@ -46,9 +53,10 @@ export interface RequestEvent {
46
53
  /** Error message — failures only. */
47
54
  errorMessage?: string;
48
55
  /**
49
- * Structured error detail — failures only, when the error handler recorded it
50
- * via `setRequestError({ details })` (e.g. the failing validation issues the
51
- * `errorMessage` string flattens). HTTP path only.
56
+ * Structured error detail — failures only. On HTTP, what the error handler
57
+ * recorded via `setRequestError({ details })` (e.g. the failing validation
58
+ * issues the `errorMessage` string flattens); on a tool call, the failed
59
+ * `ToolResult.details` (sanitised).
52
60
  */
53
61
  errorDetail?: JsonValue;
54
62
  /** Sanitised request payload — the HTTP body or the tool arguments. */
@@ -1 +1 @@
1
- {"version":3,"file":"event.d.ts","sourceRoot":"","sources":["../../src/observability/event.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,mCAAmC;IACnC,MAAM,EAAE,eAAe,CAAC;IACxB,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,mCAAmC;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kCAAkC;IAClC,EAAE,EAAE,OAAO,CAAC;IACZ,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAC;IACnB,2BAA2B;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,uEAAuE;IACvE,OAAO,EAAE,SAAS,GAAG,IAAI,CAAC;IAC1B,mDAAmD;IACnD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,4CAA4C;IAC5C,aAAa,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yBAAyB;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6BAA6B;IAC7B,SAAS,EAAE,IAAI,CAAC;CACjB"}
1
+ {"version":3,"file":"event.d.ts","sourceRoot":"","sources":["../../src/observability/event.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,mCAAmC;IACnC,MAAM,EAAE,eAAe,CAAC;IACxB,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,mCAAmC;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kCAAkC;IAClC,EAAE,EAAE,OAAO,CAAC;IACZ,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAC;IACnB,2BAA2B;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,uEAAuE;IACvE,OAAO,EAAE,SAAS,GAAG,IAAI,CAAC;IAC1B,mDAAmD;IACnD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,4CAA4C;IAC5C,aAAa,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yBAAyB;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6BAA6B;IAC7B,SAAS,EAAE,IAAI,CAAC;CACjB"}
@@ -152,7 +152,9 @@ function createAuditHook(config) {
152
152
  durationMs,
153
153
  errorCode: ctx.error?.code,
154
154
  errorMessage: ctx.error?.message,
155
- ...ctx.error?.details !== undefined && { errorDetail: ctx.error.details },
155
+ ...ctx.error?.details !== undefined && {
156
+ errorDetail: sanitizePayload(ctx.error.details, sanitize)
157
+ },
156
158
  payload: sanitizePayload(body, sanitize),
157
159
  resultSize: null,
158
160
  responseBytes: 0,
@@ -174,6 +176,7 @@ function createAuditHook(config) {
174
176
  emit({
175
177
  source: context.source,
176
178
  method: "TOOL",
179
+ httpMethod: endpoint.method,
177
180
  path: `/${context.source}/${toolName}`,
178
181
  serviceName: endpoint.serviceName,
179
182
  action: endpoint.key,
@@ -187,6 +190,9 @@ function createAuditHook(config) {
187
190
  durationMs,
188
191
  errorCode: result.ok ? undefined : result.code,
189
192
  errorMessage: result.ok ? undefined : toolErrorMessage(result),
193
+ ...!result.ok && result.details !== undefined && {
194
+ errorDetail: sanitizePayload(result.details, sanitize)
195
+ },
190
196
  payload: sanitizePayload(args, sanitize),
191
197
  resultSize: measure.resultSize,
192
198
  responseBytes: measure.responseBytes,
@@ -1 +1 @@
1
- {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/server/create.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAEV,eAAe,EACf,aAAa,EAGd,MAAM,SAAS,CAAC;AAEjB,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAsNxF;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,uBAsBnD"}
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/server/create.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAEV,eAAe,EACf,aAAa,EAGd,MAAM,SAAS,CAAC;AAEjB,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAyNxF;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,uBAsBnD"}
@@ -12,7 +12,7 @@ import {
12
12
  socketIoLane,
13
13
  staticRoute,
14
14
  webSocketLane
15
- } from "../index-renxz7c2.js";
15
+ } from "../index-kb7wxdq0.js";
16
16
  import {
17
17
  createAuthHook,
18
18
  createBearerResolver,
@@ -25,7 +25,7 @@ import {
25
25
  signJwt,
26
26
  verifyJwt,
27
27
  verifyPkce
28
- } from "../index-9zrq8x5z.js";
28
+ } from "../index-msmy8ydw.js";
29
29
  import {
30
30
  jsonSchemaFields,
31
31
  toJsonSchema
@@ -42,7 +42,7 @@ import {
42
42
  notFound,
43
43
  rateLimited,
44
44
  unauthorized
45
- } from "../index-jgpsd7dy.js";
45
+ } from "../index-fq89tdex.js";
46
46
  import {
47
47
  extractIp,
48
48
  generateTraceId,
package/dist/tools.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  inputIsQuery,
3
3
  signJwt,
4
4
  verifyPkce
5
- } from "./index-9zrq8x5z.js";
5
+ } from "./index-msmy8ydw.js";
6
6
  import {
7
7
  coerceJsonArgs,
8
8
  collectTools,
@@ -14,14 +14,14 @@ import {
14
14
  pollUntil,
15
15
  readCapped,
16
16
  toolResultFromError
17
- } from "./index-f39j6twc.js";
17
+ } from "./index-eyc38rgc.js";
18
18
  import {
19
19
  toJsonSchema
20
20
  } from "./index-0ed3bx43.js";
21
21
  import {
22
22
  AppError,
23
23
  isWithinDir
24
- } from "./index-jgpsd7dy.js";
24
+ } from "./index-fq89tdex.js";
25
25
  import {
26
26
  isRecord,
27
27
  typedEntries
package/llms-full.txt CHANGED
@@ -2258,6 +2258,7 @@ queryable across all three:
2258
2258
  | `method` / `path` | the verb + path, or `TOOL` + `/{source}/{tool}` |
2259
2259
  | `serviceName` / `action` | stable contract identity of the operation (→ ADR 0022) — from the contract, not parsed from `path`; set on every surface, present even on a pre-handler 400 |
2260
2260
  | `toolName` | tool calls only |
2261
+ | `httpMethod` | the contract verb on **tool** events (their `method` is `TOOL`) — filter reads vs writes across both surfaces with `(event.httpMethod ?? event.method) !== 'GET'` |
2261
2262
  | `dimensions` | app-defined domain dimensions (tenant / project / entity id) — see [request context](#request-context) |
2262
2263
  | `traceId` / `spanId` / `parentSpanId` | [W3C trace context](#trace-context) |
2263
2264
  | `ok` / `statusCode` | outcome — real HTTP status, or `200`/`400` for a tool |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stitchkit",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "Contract-first backend framework — one defineContract() into an HTTP API, MCP tools, AI-agent tools and a typed client. Bun and Node.",
5
5
  "keywords": [
6
6
  "bun",