@fixback/node 0.3.3 → 0.4.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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  The Fixback **backend error SDK** for Node servers. Capture unhandled crashes and
4
4
  manually-reported errors and file them into the same Fixback pipeline your frontend
5
- uses — one queue for your whole product (ADR-0026/0027/0028, spec #224).
5
+ uses — one queue for your whole product (ADR-0061/0062/0028, spec #224).
6
6
 
7
7
  Backend errors authenticate with the Project **secret key**, so they arrive
8
8
  **Internal** tier and tagged `platform: node`. Fleet-wide crash-loops collapse into a
package/dist/client.d.ts CHANGED
@@ -44,6 +44,10 @@ export declare class FixbackClient {
44
44
  get enabled(): boolean;
45
45
  /** The header the request-context middleware reads a correlation id from. */
46
46
  get requestIdHeader(): string;
47
+ /** Whether the request/response bodies are captured (the `captureBodies` opt-in). */
48
+ get captureBodies(): boolean;
49
+ /** The size cap (bytes) for a captured body when {@link captureBodies} is on. */
50
+ get maxBodyBytes(): number;
47
51
  /**
48
52
  * Report a caught or manually-observed error. `handled` defaults to `true`; the
49
53
  * process handlers pass `handled: false` for an uncaught crash.
package/dist/client.js CHANGED
@@ -62,6 +62,14 @@ class FixbackClient {
62
62
  get requestIdHeader() {
63
63
  return this.config.requestIdHeader;
64
64
  }
65
+ /** Whether the request/response bodies are captured (the `captureBodies` opt-in). */
66
+ get captureBodies() {
67
+ return this.config.captureBodies;
68
+ }
69
+ /** The size cap (bytes) for a captured body when {@link captureBodies} is on. */
70
+ get maxBodyBytes() {
71
+ return this.config.maxBodyBytes;
72
+ }
65
73
  /**
66
74
  * Report a caught or manually-observed error. `handled` defaults to `true`; the
67
75
  * process handlers pass `handled: false` for an uncaught crash.
@@ -137,7 +145,13 @@ class FixbackClient {
137
145
  }
138
146
  }
139
147
  exports.FixbackClient = FixbackClient;
140
- /** The built-in default scrub: re-sweep frame URLs through the shared-core scrubber. */
148
+ /**
149
+ * The built-in default scrub: re-sweep frame URLs through the shared-core scrubber,
150
+ * and — under the `captureBodies` opt-in — redact PII (emails, bearer tokens, long
151
+ * digit runs) from the captured request/response bodies before they leave the
152
+ * process. Masking is the SDK's job (spec 0003 §C); ingest re-scrubs the same fields
153
+ * as defence-in-depth.
154
+ */
141
155
  function applyDefaultScrub(event) {
142
156
  if (event.errorFrames && event.errorFrames.length > 0) {
143
157
  event.errorFrames = event.errorFrames.map((frame) => ({
@@ -145,6 +159,19 @@ function applyDefaultScrub(event) {
145
159
  file: (0, sdk_core_1.scrubUrl)(frame.file),
146
160
  }));
147
161
  }
162
+ const server = event.server;
163
+ if (server &&
164
+ (typeof server.requestBody === "string" || typeof server.responseBody === "string")) {
165
+ event.server = {
166
+ ...server,
167
+ ...(typeof server.requestBody === "string"
168
+ ? { requestBody: (0, sdk_core_1.redactPii)(server.requestBody) }
169
+ : {}),
170
+ ...(typeof server.responseBody === "string"
171
+ ? { responseBody: (0, sdk_core_1.redactPii)(server.responseBody) }
172
+ : {}),
173
+ };
174
+ }
148
175
  }
149
176
  /** Project the event onto the wire payload, dropping the event-only fields. */
150
177
  function toPayload(event) {
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;AAsOH,oBAqBC;AAGD,8BAEC;AAGD,4CAEC;AAGD,wCAEC;AAGD,sBAEC;AAGD,sBAOC;AAvRD,gDAAqG;AAErG,qCAAgF;AAChF,uCAAiD;AACjD,uCAAqE;AACrE,mCAAuC;AACvC,2CAA+D;AA+B/D,wFAAwF;AACxF,SAAS,kBAAkB,CACzB,IAA+B,EAC/B,QAAmC;IAEnC,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAC;IACzC,MAAM,MAAM,GAA4B,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,CAAC;IACjF,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,SAAS;YAAE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAE,MAAwB,CAAC,CAAC,CAAC,SAAS,CAAC;AAChF,CAAC;AAED,MAAa,aAAa;IACP,MAAM,CAAiB;IACvB,SAAS,CAA0B;IAEpD,YAAY,MAAsB,EAAE,OAAmB,EAAE;QACvD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,SAAS;YACZ,IAAI,CAAC,SAAS;gBACd,CAAC,MAAM,CAAC,OAAO;oBACb,CAAC,CAAC,IAAI,qBAAS,CAAC;wBACZ,QAAQ,EAAE,MAAM,CAAC,cAAc;wBAC/B,SAAS,EAAE,MAAM,CAAC,SAAS;wBAC3B,YAAY,EAAE,MAAM,CAAC,YAAY;wBACjC,YAAY,EAAE,MAAM,CAAC,YAAY;wBACjC,eAAe,EAAE,MAAM,CAAC,eAAe;wBACvC,SAAS,EAAE,MAAM,CAAC,SAAS;qBAC5B,CAAC;oBACJ,CAAC,CAAC,IAAI,CAAC,CAAC;IACd,CAAC;IAED,qFAAqF;IACrF,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;IAC7B,CAAC;IAED,6EAA6E;IAC7E,IAAI,eAAe;QACjB,OAAO,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC;IACrC,CAAC;IAED,qFAAqF;IACrF,IAAI,aAAa;QACf,OAAO,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC;IACnC,CAAC;IAED,iFAAiF;IACjF,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC;IAClC,CAAC;IAED;;;OAGG;IACH,gBAAgB,CAAC,KAAc,EAAE,UAA0B,EAAE;QAC3D,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,IAAA,oBAAY,EAAC,KAAK,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,IAAA,kCAAuB,EAAC,KAAK,CAAC,CAAC;QAC9C,IAAI,CAAC,QAAQ,CACX;YACE,cAAc,EAAE,IAAA,6BAAkB,EAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC;YACtD,IAAI;YACJ,KAAK;YACL,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,IAAI;YAChC,WAAW,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;SACpD,EACD,OAAO,CACR,CAAC;IACJ,CAAC;IAED,4EAA4E;IAC5E,cAAc,CAAC,OAAe,EAAE,QAAkB,MAAM,EAAE,UAA0B,EAAE;QACpF,MAAM,KAAK,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACtE,IAAI,CAAC,QAAQ,CACX;YACE,cAAc,EAAE,IAAA,6BAAkB,EAAC,SAAS,EAAE,KAAK,CAAC;YACpD,IAAI,EAAE,SAAS;YACf,KAAK;YACL,KAAK;YACL,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,IAAI;SACjC,EACD,OAAO,CACR,CAAC;IACJ,CAAC;IAED,gFAAgF;IAChF,KAAK;QACH,OAAO,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IACtD,CAAC;IAED,6DAA6D;IAC7D,KAAK;QACH,OAAO,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IACtD,CAAC;IAED,+EAA+E;IACvE,QAAQ,CACd,OAC2D,EAC3D,OAAuB;QAEvB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,OAAO;QACpD,IAAI,CAAC;YACH,MAAM,KAAK,GAAsB;gBAC/B,cAAc,EAAE,OAAO,CAAC,cAAc;gBACtC,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW;gBACpC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO;gBAC5B,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,MAAM,EAAE,kBAAkB,CAAC,IAAA,8BAAoB,GAAE,EAAE,OAAO,CAAC,MAAM,CAAC;aACnE,CAAC;YACF,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK;gBAAE,iBAAiB,CAAC,KAAK,CAAC,CAAC;YAChD,MAAM,SAAS,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;YAC5C,IAAI,CAAC,SAAS;gBAAE,OAAO,CAAC,4CAA4C;YACpE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC;QAC/C,CAAC;QAAC,MAAM,CAAC;YACP,qDAAqD;QACvD,CAAC;IACH,CAAC;IAED,oFAAoF;IAC5E,aAAa,CAAC,KAAwB;QAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;QACpC,IAAI,CAAC,IAAI;YAAE,OAAO,KAAK,CAAC;QACxB,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC,CAAC,sEAAsE;QACtF,CAAC;IACH,CAAC;CACF;AA1HD,sCA0HC;AAED;;;;;;GAMG;AACH,SAAS,iBAAiB,CAAC,KAAwB;IACjD,IAAI,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtD,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACpD,GAAG,KAAK;YACR,IAAI,EAAE,IAAA,mBAAQ,EAAC,KAAK,CAAC,IAAI,CAAC;SAC3B,CAAC,CAAC,CAAC;IACN,CAAC;IACD,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAC5B,IACE,MAAM;QACN,CAAC,OAAO,MAAM,CAAC,WAAW,KAAK,QAAQ,IAAI,OAAO,MAAM,CAAC,YAAY,KAAK,QAAQ,CAAC,EACnF,CAAC;QACD,KAAK,CAAC,MAAM,GAAG;YACb,GAAG,MAAM;YACT,GAAG,CAAC,OAAO,MAAM,CAAC,WAAW,KAAK,QAAQ;gBACxC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAA,oBAAS,EAAC,MAAM,CAAC,WAAW,CAAC,EAAE;gBAChD,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,OAAO,MAAM,CAAC,YAAY,KAAK,QAAQ;gBACzC,CAAC,CAAC,EAAE,YAAY,EAAE,IAAA,oBAAS,EAAC,MAAM,CAAC,YAAY,CAAC,EAAE;gBAClD,CAAC,CAAC,EAAE,CAAC;SACR,CAAC;IACJ,CAAC;AACH,CAAC;AAED,+EAA+E;AAC/E,SAAS,SAAS,CAAC,KAAwB;IACzC,MAAM,OAAO,GAAmB,EAAE,cAAc,EAAE,KAAK,CAAC,cAAc,EAAE,CAAC;IACzE,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS;QAAE,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IACjE,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS;QAAE,OAAO,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC;IAC7E,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS;QAAE,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IACjE,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS;QAAE,OAAO,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC;IAC7E,IAAI,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC;IAC/F,IAAI,KAAK,CAAC,MAAM;QAAE,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAChD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,gFAAgF;AAEhF,IAAI,YAAY,GAAyB,IAAI,CAAC;AAC9C,IAAI,eAAe,GAAwB,IAAI,CAAC;AAEhD;;;;;GAKG;AACH,SAAgB,IAAI,CAAC,OAAoB,EAAE,IAAiB;IAC1D,MAAM,cAAc,GAAG,YAAY,CAAC;IACpC,MAAM,iBAAiB,GAAG,eAAe,CAAC;IAC1C,IAAI,iBAAiB;QAAE,iBAAiB,EAAE,CAAC;IAC3C,IAAI,cAAc;QAAE,KAAK,cAAc,CAAC,KAAK,EAAE,CAAC;IAEhD,MAAM,MAAM,GAAG,IAAA,sBAAa,EAAC,OAAO,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,IAAI,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC/C,YAAY,GAAG,MAAM,CAAC;IACtB,eAAe;QACb,MAAM,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,wBAAwB,IAAI,MAAM,CAAC,yBAAyB,CAAC;YACrF,CAAC,CAAC,IAAA,gCAAsB,EACpB,MAAM,EACN;gBACE,wBAAwB,EAAE,MAAM,CAAC,wBAAwB;gBACzD,yBAAyB,EAAE,MAAM,CAAC,yBAAyB;aAC5D,EACD,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY,EAAE,CAC7D;YACH,CAAC,CAAC,IAAI,CAAC;IACX,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,gFAAgF;AAChF,SAAgB,SAAS;IACvB,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,oFAAoF;AACpF,SAAgB,gBAAgB,CAAC,KAAc,EAAE,OAAwB;IACvE,YAAY,EAAE,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AACjD,CAAC;AAED,wEAAwE;AACxE,SAAgB,cAAc,CAAC,OAAe,EAAE,KAAgB,EAAE,OAAwB;IACxF,YAAY,EAAE,cAAc,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;AACxD,CAAC;AAED,8DAA8D;AAC9D,SAAgB,KAAK;IACnB,OAAO,YAAY,EAAE,KAAK,EAAE,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;AACpD,CAAC;AAED,8FAA8F;AAC9F,SAAgB,KAAK;IACnB,MAAM,OAAO,GAAG,YAAY,CAAC;IAC7B,MAAM,SAAS,GAAG,eAAe,CAAC;IAClC,YAAY,GAAG,IAAI,CAAC;IACpB,eAAe,GAAG,IAAI,CAAC;IACvB,IAAI,SAAS;QAAE,SAAS,EAAE,CAAC;IAC3B,OAAO,OAAO,EAAE,KAAK,EAAE,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;AAC/C,CAAC"}
package/dist/config.d.ts CHANGED
@@ -50,6 +50,22 @@ export interface InitOptions extends Omit<CommonInitOptions<FixbackErrorEvent>,
50
50
  readonly timeoutMs?: number;
51
51
  /** The header a correlation id is read from. Defaults to `x-request-id`. */
52
52
  readonly requestIdHeader?: string;
53
+ /**
54
+ * Capture the failing request's **request and response bodies** onto the error's
55
+ * Server context (ADR-2026-09-16). **Defaults to `false`** — this relaxes the
56
+ * private-by-default posture (VISION §5), so it is an explicit opt-in a backend
57
+ * turns on for its own internal/staging debugging. When off, nothing is captured
58
+ * or wrapped and behaviour is byte-for-byte unchanged. Bodies are serialized,
59
+ * PII-redacted at the scrub choke point, and dropped when they exceed
60
+ * {@link maxBodyBytes} (an oversized body degrades to exactly the off behaviour).
61
+ */
62
+ readonly captureBodies?: boolean;
63
+ /**
64
+ * The size cap (bytes) for a captured body when {@link captureBodies} is on; a body
65
+ * whose serialized form exceeds it is dropped rather than truncated. Defaults to
66
+ * `16384`, clamped to `[0, 65536]`.
67
+ */
68
+ readonly maxBodyBytes?: number;
53
69
  }
54
70
  /** The validated, defaulted configuration a {@link FixbackClient} runs on. */
55
71
  export interface ResolvedConfig {
@@ -68,6 +84,8 @@ export interface ResolvedConfig {
68
84
  readonly maxQueueSize: number;
69
85
  readonly timeoutMs: number;
70
86
  readonly requestIdHeader: string;
87
+ readonly captureBodies: boolean;
88
+ readonly maxBodyBytes: number;
71
89
  }
72
90
  /**
73
91
  * Resolve raw {@link InitOptions} into a validated {@link ResolvedConfig}. Never
package/dist/config.js CHANGED
@@ -20,6 +20,10 @@ const DEFAULT_FLUSH_INTERVAL_MS = 5_000;
20
20
  const DEFAULT_MAX_QUEUE_SIZE = 1_024;
21
21
  const DEFAULT_TIMEOUT_MS = 30_000;
22
22
  const DEFAULT_REQUEST_ID_HEADER = "x-request-id";
23
+ /** The default per-body size cap (bytes) when `captureBodies` is on — a larger body is dropped. */
24
+ const DEFAULT_MAX_BODY_BYTES = 16_384;
25
+ /** A hard ceiling on the body cap so a misconfigured knob can't buffer unbounded bodies. */
26
+ const MAX_BODY_BYTES_CEILING = 65_536;
23
27
  /** Trim a string option, returning `undefined` for a blank or non-string value. */
24
28
  function trimmed(value) {
25
29
  if (typeof value !== "string")
@@ -68,6 +72,10 @@ function resolveConfig(options) {
68
72
  maxQueueSize: clampInt(options.maxQueueSize, DEFAULT_MAX_QUEUE_SIZE, 1, Number.MAX_SAFE_INTEGER),
69
73
  timeoutMs: clampInt(options.timeoutMs, DEFAULT_TIMEOUT_MS, 0, Number.MAX_SAFE_INTEGER),
70
74
  requestIdHeader: (trimmed(options.requestIdHeader) ?? DEFAULT_REQUEST_ID_HEADER).toLowerCase(),
75
+ // Body capture is a strict opt-in (only an explicit `true` turns it on), so a
76
+ // truthy-but-not-true value never relaxes the private-by-default posture.
77
+ captureBodies: options.captureBodies === true,
78
+ maxBodyBytes: clampInt(options.maxBodyBytes, DEFAULT_MAX_BODY_BYTES, 0, MAX_BODY_BYTES_CEILING),
71
79
  };
72
80
  }
73
81
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/dist/context.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Per-request correlation via Node **AsyncLocalStorage** (spec §D9, ADR-0025) — so a
2
+ * Per-request correlation via Node **AsyncLocalStorage** (spec §D9, ADR-0060) — so a
3
3
  * captured error carries the in-flight request's context without the developer
4
4
  * threading a context object through every call.
5
5
  *
@@ -19,6 +19,20 @@ export interface RequestStore {
19
19
  readonly requestId?: string;
20
20
  /** The app-supplied user ref, written by {@link setRequestUser}. Never scraped. */
21
21
  user?: string;
22
+ /**
23
+ * Whether the request/response bodies are being captured for this request
24
+ * (the `captureBodies` opt-in, ADR-2026-09-16). When unset, {@link deriveServerContext}
25
+ * carries no bodies — the private-by-default path.
26
+ */
27
+ captureBodies?: boolean;
28
+ /** The size cap (bytes) for a captured body; a larger body is dropped. */
29
+ maxBodyBytes?: number;
30
+ /**
31
+ * The raw response body recorded by the wrapped `res.json`/`res.send` — the first
32
+ * one the request resolved with. Serialized and capped at read time in
33
+ * {@link deriveServerContext}. Only ever set when {@link captureBodies} is on.
34
+ */
35
+ responseBody?: unknown;
22
36
  }
23
37
  /** The active request store, or `undefined` outside a request scope. */
24
38
  export declare function getRequestStore(): RequestStore | undefined;
@@ -30,11 +44,22 @@ export declare function runWithRequestStore<T>(store: RequestStore, fn: () => T)
30
44
  * throws). The SDK never derives identity from the request — only what you pass here.
31
45
  */
32
46
  export declare function setRequestUser(user: string): void;
47
+ /**
48
+ * Serialize a captured body to a string within a byte cap, or `undefined` when there
49
+ * is nothing usable to store (ADR-2026-09-16). A string rides as-is; a plain object is
50
+ * JSON-serialized; binary (a `Buffer` or typed array) and non-body primitives are
51
+ * dropped. An empty result, an unserialisable value (a circular object), or one whose
52
+ * serialized form exceeds `maxBytes` all degrade to `undefined` — exactly the
53
+ * body-absent path, so an oversized body renders like no body at all.
54
+ */
55
+ export declare function serializeBody(value: unknown, maxBytes: number): string | undefined;
33
56
  /**
34
57
  * Derive the private-by-default {@link ServerContext} from a request store: the HTTP
35
58
  * method, the route **pattern**, the resolved status, the correlation id, and the
36
59
  * app-supplied user ref. Returns `undefined` when there is no store or nothing
37
- * resolvable — never the concrete path, a body, a header, or a query value.
60
+ * resolvable — never the concrete path, a header, or a query value. The request and
61
+ * response **bodies** are added only when the store opted into `captureBodies`
62
+ * (ADR-2026-09-16), each serialized and dropped if it exceeds the store's cap.
38
63
  */
39
64
  export declare function deriveServerContext(store: RequestStore | undefined): ServerContext | undefined;
40
65
  /** The {@link ServerContext} for the active request, or `undefined` outside one. */
@@ -43,11 +68,21 @@ export declare function currentServerContext(): ServerContext | undefined;
43
68
  export interface RequestContextOptions {
44
69
  /** The header a correlation id is read from. Defaults to `x-request-id`. */
45
70
  readonly requestIdHeader?: string;
71
+ /**
72
+ * Record the request/response bodies onto the store (the `captureBodies` opt-in,
73
+ * ADR-2026-09-16). Off by default; when on, the middleware also wraps the response
74
+ * writers to record the response body. Resolved from the active client's config by
75
+ * the adapters, never asked of the app here.
76
+ */
77
+ readonly captureBodies?: boolean;
78
+ /** The size cap (bytes) for a captured body when {@link captureBodies} is on. */
79
+ readonly maxBodyBytes?: number;
46
80
  }
47
81
  /**
48
82
  * Build the Express-style request-context middleware: it opens an ALS store for the
49
83
  * request (adopting an inbound correlation id, or minting one) and runs the rest of
50
84
  * the request within it. Shared by the Express adapter and the NestJS module so both
51
- * frameworks get per-request correlation the same way.
85
+ * frameworks get per-request correlation the same way. Under `captureBodies` it also
86
+ * wraps the response writers so the response body can be recorded (ADR-2026-09-16).
52
87
  */
53
88
  export declare function createRequestContextMiddleware(options?: RequestContextOptions): (req: HttpRequestLike, res: HttpResponseLike, next: NextFunction) => void;
package/dist/context.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /**
3
- * Per-request correlation via Node **AsyncLocalStorage** (spec §D9, ADR-0025) — so a
3
+ * Per-request correlation via Node **AsyncLocalStorage** (spec §D9, ADR-0060) — so a
4
4
  * captured error carries the in-flight request's context without the developer
5
5
  * threading a context object through every call.
6
6
  *
@@ -14,12 +14,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
14
14
  exports.getRequestStore = getRequestStore;
15
15
  exports.runWithRequestStore = runWithRequestStore;
16
16
  exports.setRequestUser = setRequestUser;
17
+ exports.serializeBody = serializeBody;
17
18
  exports.deriveServerContext = deriveServerContext;
18
19
  exports.currentServerContext = currentServerContext;
19
20
  exports.createRequestContextMiddleware = createRequestContextMiddleware;
20
21
  const node_async_hooks_1 = require("node:async_hooks");
21
22
  const node_crypto_1 = require("node:crypto");
22
23
  const http_1 = require("./http");
24
+ /** The fallback body cap used when a store opts into body capture without naming one. */
25
+ const FALLBACK_MAX_BODY_BYTES = 16_384;
23
26
  const storage = new node_async_hooks_1.AsyncLocalStorage();
24
27
  /** The active request store, or `undefined` outside a request scope. */
25
28
  function getRequestStore() {
@@ -39,11 +42,80 @@ function setRequestUser(user) {
39
42
  if (store && typeof user === "string" && user.length > 0)
40
43
  store.user = user;
41
44
  }
45
+ /**
46
+ * Serialize a captured body to a string within a byte cap, or `undefined` when there
47
+ * is nothing usable to store (ADR-2026-09-16). A string rides as-is; a plain object is
48
+ * JSON-serialized; binary (a `Buffer` or typed array) and non-body primitives are
49
+ * dropped. An empty result, an unserialisable value (a circular object), or one whose
50
+ * serialized form exceeds `maxBytes` all degrade to `undefined` — exactly the
51
+ * body-absent path, so an oversized body renders like no body at all.
52
+ */
53
+ function serializeBody(value, maxBytes) {
54
+ if (value === undefined || value === null)
55
+ return undefined;
56
+ let text;
57
+ if (typeof value === "string") {
58
+ text = value;
59
+ }
60
+ else if (typeof value === "object") {
61
+ if (Buffer.isBuffer(value) || ArrayBuffer.isView(value))
62
+ return undefined;
63
+ try {
64
+ text = JSON.stringify(value);
65
+ }
66
+ catch {
67
+ return undefined; // circular / unserialisable — degrade to absent
68
+ }
69
+ if (typeof text !== "string")
70
+ return undefined; // e.g. a value whose toJSON returns undefined
71
+ }
72
+ else {
73
+ return undefined; // number / boolean / bigint / symbol / function — not a body
74
+ }
75
+ if (text.length === 0)
76
+ return undefined;
77
+ if (Buffer.byteLength(text, "utf8") > maxBytes)
78
+ return undefined; // oversized — degrade to absent
79
+ return text;
80
+ }
81
+ /**
82
+ * Wrap a response's `json`/`send` writers so the **first** body the request resolves
83
+ * with is recorded onto the store (for {@link deriveServerContext} to read). Installed
84
+ * by the request-context middleware only under the `captureBodies` opt-in. Recording
85
+ * never breaks the response: a throw while recording is swallowed and the original
86
+ * writer always runs; a frozen (non-writable) response is left untouched, so the body
87
+ * simply degrades to absent.
88
+ */
89
+ function installResponseBodyCapture(res, store) {
90
+ for (const method of ["json", "send"]) {
91
+ const original = res[method];
92
+ if (typeof original !== "function")
93
+ continue;
94
+ const wrapped = function (...args) {
95
+ try {
96
+ if (store.responseBody === undefined && args.length > 0)
97
+ store.responseBody = args[0];
98
+ }
99
+ catch {
100
+ /* recording must never break the response */
101
+ }
102
+ return original.apply(this, args);
103
+ };
104
+ try {
105
+ res[method] = wrapped;
106
+ }
107
+ catch {
108
+ /* a frozen response object — skip; the body degrades to absent */
109
+ }
110
+ }
111
+ }
42
112
  /**
43
113
  * Derive the private-by-default {@link ServerContext} from a request store: the HTTP
44
114
  * method, the route **pattern**, the resolved status, the correlation id, and the
45
115
  * app-supplied user ref. Returns `undefined` when there is no store or nothing
46
- * resolvable — never the concrete path, a body, a header, or a query value.
116
+ * resolvable — never the concrete path, a header, or a query value. The request and
117
+ * response **bodies** are added only when the store opted into `captureBodies`
118
+ * (ADR-2026-09-16), each serialized and dropped if it exceeds the store's cap.
47
119
  */
48
120
  function deriveServerContext(store) {
49
121
  if (!store)
@@ -62,6 +134,15 @@ function deriveServerContext(store) {
62
134
  ctx.requestId = store.requestId;
63
135
  if (store.user)
64
136
  ctx.user = store.user;
137
+ if (store.captureBodies) {
138
+ const maxBytes = store.maxBodyBytes ?? FALLBACK_MAX_BODY_BYTES;
139
+ const requestBody = serializeBody(store.req.body, maxBytes);
140
+ if (requestBody !== undefined)
141
+ ctx.requestBody = requestBody;
142
+ const responseBody = serializeBody(store.responseBody, maxBytes);
143
+ if (responseBody !== undefined)
144
+ ctx.responseBody = responseBody;
145
+ }
65
146
  return Object.keys(ctx).length > 0 ? ctx : undefined;
66
147
  }
67
148
  /** The {@link ServerContext} for the active request, or `undefined` outside one. */
@@ -81,16 +162,25 @@ function readRequestId(req, header) {
81
162
  * Build the Express-style request-context middleware: it opens an ALS store for the
82
163
  * request (adopting an inbound correlation id, or minting one) and runs the rest of
83
164
  * the request within it. Shared by the Express adapter and the NestJS module so both
84
- * frameworks get per-request correlation the same way.
165
+ * frameworks get per-request correlation the same way. Under `captureBodies` it also
166
+ * wraps the response writers so the response body can be recorded (ADR-2026-09-16).
85
167
  */
86
168
  function createRequestContextMiddleware(options = {}) {
87
169
  const header = (options.requestIdHeader ?? "x-request-id").toLowerCase();
170
+ const captureBodies = options.captureBodies === true;
171
+ const maxBodyBytes = options.maxBodyBytes;
88
172
  return function fixbackRequestContext(req, res, next) {
89
173
  const store = {
90
174
  req,
91
175
  res,
92
176
  requestId: readRequestId(req, header) ?? (0, node_crypto_1.randomUUID)(),
93
177
  };
178
+ if (captureBodies) {
179
+ store.captureBodies = true;
180
+ if (typeof maxBodyBytes === "number")
181
+ store.maxBodyBytes = maxBodyBytes;
182
+ installResponseBodyCapture(res, store);
183
+ }
94
184
  runWithRequestStore(store, () => next());
95
185
  };
96
186
  }
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
package/dist/express.js CHANGED
@@ -25,8 +25,14 @@ const http_1 = require("./http");
25
25
  * `app.use(fixbackRequestContext())`.
26
26
  */
27
27
  function fixbackRequestContext(options = {}) {
28
- const header = options.requestIdHeader ?? (0, client_1.getClient)()?.requestIdHeader;
29
- return (0, context_1.createRequestContextMiddleware)(header ? { requestIdHeader: header } : {});
28
+ const client = (0, client_1.getClient)();
29
+ const header = options.requestIdHeader ?? client?.requestIdHeader;
30
+ return (0, context_1.createRequestContextMiddleware)({
31
+ ...(header ? { requestIdHeader: header } : {}),
32
+ // The `captureBodies` opt-in lives on `init`, not on this factory — resolve it
33
+ // from the active client so the middleware wraps the response writers (ADR-2026-09-16).
34
+ ...(client?.captureBodies ? { captureBodies: true, maxBodyBytes: client.maxBodyBytes } : {}),
35
+ });
30
36
  }
31
37
  /**
32
38
  * Build the error-handling middleware. Mount it after your routes:
@@ -38,14 +44,27 @@ function fixbackErrorHandler(options = {}) {
38
44
  captureException: (error, context) => (0, client_1.captureException)(error, context),
39
45
  };
40
46
  return function fixbackErrorHandlerMiddleware(error, req, res, next) {
41
- try {
42
- const server = { ...(0, http_1.serverContextFromRequest)(req), statusCode: (0, http_1.errorStatus)(error, res) };
43
- client.captureException(error, { handled: true, server });
47
+ const capture = () => {
48
+ try {
49
+ const server = { ...(0, http_1.serverContextFromRequest)(req), statusCode: (0, http_1.errorStatus)(error, res) };
50
+ client.captureException(error, { handled: true, server });
51
+ }
52
+ catch {
53
+ // Capture must never break the request pipeline — always forward the error.
54
+ }
55
+ };
56
+ // Under the `captureBodies` opt-in (ADR-2026-09-16) the response body is only
57
+ // available once the app's own error handler has sent the response, so forward
58
+ // first and capture after. Otherwise keep the original order — capture, then
59
+ // forward — so behaviour is byte-for-byte unchanged when the opt-in is off.
60
+ if ((0, client_1.getClient)()?.captureBodies) {
61
+ next(error);
62
+ capture();
44
63
  }
45
- catch {
46
- // Capture must never break the request pipeline — always forward the error.
64
+ else {
65
+ capture();
66
+ next(error);
47
67
  }
48
- next(error);
49
68
  };
50
69
  }
51
70
  //# sourceMappingURL=express.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"express.js","sourceRoot":"","sources":["../src/express.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;AA+CH,sDAGC;AAOD,kDAaC;AApED,qCAIkB;AAClB,uCAA2D;AAC3D,iCAMgB;AA6BhB;;;GAGG;AACH,SAAgB,qBAAqB,CAAC,UAAiC,EAAE;IACvE,MAAM,MAAM,GAAG,OAAO,CAAC,eAAe,IAAI,IAAA,kBAAS,GAAE,EAAE,eAAe,CAAC;IACvE,OAAO,IAAA,wCAA8B,EAAC,MAAM,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AACnF,CAAC;AAED;;;;GAIG;AACH,SAAgB,mBAAmB,CAAC,UAA+B,EAAE;IACnE,MAAM,MAAM,GAAkB,OAAO,CAAC,MAAM,IAAI;QAC9C,gBAAgB,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,IAAA,yBAAsB,EAAC,KAAK,EAAE,OAAO,CAAC;KAC7E,CAAC;IACF,OAAO,SAAS,6BAA6B,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI;QACjE,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,EAAE,GAAG,IAAA,+BAAwB,EAAC,GAAG,CAAC,EAAE,UAAU,EAAE,IAAA,kBAAW,EAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC;YACzF,MAAM,CAAC,gBAAgB,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QAC5D,CAAC;QAAC,MAAM,CAAC;YACP,4EAA4E;QAC9E,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,CAAC;IACd,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"express.js","sourceRoot":"","sources":["../src/express.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;AA+CH,sDASC;AAOD,kDAyBC;AAtFD,qCAIkB;AAClB,uCAA2D;AAC3D,iCAMgB;AA6BhB;;;GAGG;AACH,SAAgB,qBAAqB,CAAC,UAAiC,EAAE;IACvE,MAAM,MAAM,GAAG,IAAA,kBAAS,GAAE,CAAC;IAC3B,MAAM,MAAM,GAAG,OAAO,CAAC,eAAe,IAAI,MAAM,EAAE,eAAe,CAAC;IAClE,OAAO,IAAA,wCAA8B,EAAC;QACpC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9C,+EAA+E;QAC/E,wFAAwF;QACxF,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC7F,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,SAAgB,mBAAmB,CAAC,UAA+B,EAAE;IACnE,MAAM,MAAM,GAAkB,OAAO,CAAC,MAAM,IAAI;QAC9C,gBAAgB,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,IAAA,yBAAsB,EAAC,KAAK,EAAE,OAAO,CAAC;KAC7E,CAAC;IACF,OAAO,SAAS,6BAA6B,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI;QACjE,MAAM,OAAO,GAAG,GAAS,EAAE;YACzB,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,EAAE,GAAG,IAAA,+BAAwB,EAAC,GAAG,CAAC,EAAE,UAAU,EAAE,IAAA,kBAAW,EAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC;gBACzF,MAAM,CAAC,gBAAgB,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;YAC5D,CAAC;YAAC,MAAM,CAAC;gBACP,4EAA4E;YAC9E,CAAC;QACH,CAAC,CAAC;QACF,8EAA8E;QAC9E,+EAA+E;QAC/E,6EAA6E;QAC7E,4EAA4E;QAC5E,IAAI,IAAA,kBAAS,GAAE,EAAE,aAAa,EAAE,CAAC;YAC/B,IAAI,CAAC,KAAK,CAAC,CAAC;YACZ,OAAO,EAAE,CAAC;QACZ,CAAC;aAAM,CAAC;YACN,OAAO,EAAE,CAAC;YACV,IAAI,CAAC,KAAK,CAAC,CAAC;QACd,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * **Host identity** minting (ADR-0032; ticket #251) — the escape hatch for a backend
2
+ * **Host identity** minting (ADR-0066; ticket #251) — the escape hatch for a backend
3
3
  * that already knows who its users are.
4
4
  *
5
5
  * A Host identity is a compact `HS256` JWT the customer's own server mints with the
@@ -43,7 +43,7 @@ export interface MintHostIdentityOptions {
43
43
  /**
44
44
  * How long the token is valid, in seconds. Defaults to one hour. `exp` is
45
45
  * mandatory — an identity assertion that never expires is exactly what an
46
- * escape-hatch credential must not be (ADR-0032) — so this is always set.
46
+ * escape-hatch credential must not be (ADR-0066) — so this is always set.
47
47
  */
48
48
  readonly expiresInSeconds?: number;
49
49
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /**
3
- * **Host identity** minting (ADR-0032; ticket #251) — the escape hatch for a backend
3
+ * **Host identity** minting (ADR-0066; ticket #251) — the escape hatch for a backend
4
4
  * that already knows who its users are.
5
5
  *
6
6
  * A Host identity is a compact `HS256` JWT the customer's own server mints with the
package/dist/http.d.ts CHANGED
@@ -8,7 +8,14 @@
8
8
  * the private-by-default fields are read here — never bodies, headers beyond the
9
9
  * correlation id, query values, or env.
10
10
  */
11
- /** The request slice the adapters read — never its body, headers (bar the id), or query. */
11
+ /** A framework response writer (`res.json` / `res.send`) — wrapped under `captureBodies`. */
12
+ export type ResponseWriter = (this: unknown, ...args: unknown[]) => unknown;
13
+ /**
14
+ * The request slice the adapters read — headers (bar the correlation id) and query are
15
+ * never read. The parsed **body** is read only under the `captureBodies` opt-in
16
+ * (default off, ADR-2026-09-16); it is whatever the app's body parser already put on
17
+ * `req.body`, so Fixback parses nothing itself.
18
+ */
12
19
  export interface HttpRequestLike {
13
20
  readonly method?: string;
14
21
  readonly baseUrl?: string;
@@ -18,10 +25,19 @@ export interface HttpRequestLike {
18
25
  readonly path?: string | RegExp;
19
26
  } | undefined;
20
27
  readonly headers?: Readonly<Record<string, string | string[] | undefined>>;
28
+ /** The parsed request body the app's body parser set — read only under `captureBodies`. */
29
+ readonly body?: unknown;
21
30
  }
22
- /** The response slice the adapters read — only the resolved status. */
31
+ /**
32
+ * The response slice the adapters read — the resolved status, plus the `json`/`send`
33
+ * writers the request-context middleware wraps (only under `captureBodies`) to record
34
+ * the response body. The writers are optional and non-readonly so the wrapper can
35
+ * replace them; a frozen response simply keeps its own and the body degrades to absent.
36
+ */
23
37
  export interface HttpResponseLike {
24
38
  readonly statusCode?: number;
39
+ json?: ResponseWriter;
40
+ send?: ResponseWriter;
25
41
  }
26
42
  /** The Express/Nest `next` callback — `next(err)` forwards to the error pipeline. */
27
43
  export type NextFunction = (err?: unknown) => void;
package/dist/http.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;AA0BH,wCASC;AAUD,4DAMC;AAGD,sCAOC;AAMD,kCAMC;AArDD;;;;;GAKG;AACH,SAAgB,cAAc,CAAC,GAAoB;IACjD,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC;IAClC,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACpE,MAAM,OAAO,GACX,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,IAAI,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;IACtF,MAAM,IAAI,GAAG,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IAChE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IACpE,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC;IACtC,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,EAAE,EAAE,CAAC;AACvE,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,wBAAwB,CAAC,GAAoB;IAC3D,MAAM,GAAG,GAAwC,EAAE,CAAC;IACpD,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;IACrF,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,KAAK;QAAE,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC;IAC7B,OAAO,GAAG,CAAC;AACb,CAAC;AAED,gGAAgG;AAChG,SAAgB,aAAa,CAAC,KAAc;IAC1C,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvC,MAAM,CAAC,GAAG,KAAmD,CAAC;QAC9D,MAAM,GAAG,GAAG,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;QACnE,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC;IAClE,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,SAAgB,WAAW,CAAC,KAAc,EAAE,GAAqB;IAC/D,MAAM,SAAS,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,SAAS,IAAI,SAAS,IAAI,GAAG;QAAE,OAAO,SAAS,CAAC;IACpD,MAAM,YAAY,GAAG,GAAG,CAAC,UAAU,CAAC;IACpC,IAAI,OAAO,YAAY,KAAK,QAAQ,IAAI,YAAY,IAAI,GAAG;QAAE,OAAO,YAAY,CAAC;IACjF,OAAO,GAAG,CAAC;AACb,CAAC"}
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;AA2CH,wCASC;AAUD,4DAMC;AAGD,sCAOC;AAMD,kCAMC;AArDD;;;;;GAKG;AACH,SAAgB,cAAc,CAAC,GAAoB;IACjD,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC;IAClC,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACpE,MAAM,OAAO,GACX,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,IAAI,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;IACtF,MAAM,IAAI,GAAG,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IAChE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IACpE,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC;IACtC,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,EAAE,EAAE,CAAC;AACvE,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,wBAAwB,CAAC,GAAoB;IAC3D,MAAM,GAAG,GAAwC,EAAE,CAAC;IACpD,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;IACrF,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,KAAK;QAAE,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC;IAC7B,OAAO,GAAG,CAAC;AACb,CAAC;AAED,gGAAgG;AAChG,SAAgB,aAAa,CAAC,KAAc;IAC1C,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvC,MAAM,CAAC,GAAG,KAAmD,CAAC;QAC9D,MAAM,GAAG,GAAG,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;QACnE,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC;IAClE,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,SAAgB,WAAW,CAAC,KAAc,EAAE,GAAqB;IAC/D,MAAM,SAAS,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,SAAS,IAAI,SAAS,IAAI,GAAG;QAAE,OAAO,SAAS,CAAC;IACpD,MAAM,YAAY,GAAG,GAAG,CAAC,UAAU,CAAC;IACpC,IAAI,OAAO,YAAY,KAAK,QAAQ,IAAI,YAAY,IAAI,GAAG;QAAE,OAAO,YAAY,CAAC;IACjF,OAAO,GAAG,CAAC;AACb,CAAC"}
package/dist/index.js CHANGED
@@ -32,7 +32,7 @@ Object.defineProperty(exports, "setUser", { enumerable: true, get: function () {
32
32
  // Configuration.
33
33
  var config_1 = require("./config");
34
34
  Object.defineProperty(exports, "DEFAULT_API_URL", { enumerable: true, get: function () { return config_1.DEFAULT_API_URL; } });
35
- // Host identity (ADR-0032) — mint a server-signed identity JWT a capture SDK presents
35
+ // Host identity (ADR-0066) — mint a server-signed identity JWT a capture SDK presents
36
36
  // as its `hostIdentity`, so a backend can tier its own users without Connect.
37
37
  var host_identity_1 = require("./host-identity");
38
38
  Object.defineProperty(exports, "mintHostIdentity", { enumerable: true, get: function () { return host_identity_1.mintHostIdentity; } });
package/dist/nestjs.js CHANGED
@@ -80,8 +80,20 @@ function captureNestException(exception, host, client = defaultClient) {
80
80
  */
81
81
  let FixbackExceptionFilter = class FixbackExceptionFilter extends core_1.BaseExceptionFilter {
82
82
  catch(exception, host) {
83
- captureNestException(exception, host);
84
- super.catch(exception, host);
83
+ // Under the `captureBodies` opt-in (ADR-2026-09-16) the response body is recorded
84
+ // by the request-context middleware's wrapped writers only once Nest has produced
85
+ // the response, so let `super.catch` run first and capture after — this is how the
86
+ // framework's own payload (e.g. a `ValidationPipe` array) reaches the Server
87
+ // context. With the opt-in off, keep the original order — capture, then respond —
88
+ // so behaviour is unchanged.
89
+ if ((0, client_1.getClient)()?.captureBodies) {
90
+ super.catch(exception, host);
91
+ captureNestException(exception, host);
92
+ }
93
+ else {
94
+ captureNestException(exception, host);
95
+ super.catch(exception, host);
96
+ }
85
97
  }
86
98
  };
87
99
  exports.FixbackExceptionFilter = FixbackExceptionFilter;
@@ -101,9 +113,17 @@ let FixbackModule = FixbackModule_1 = class FixbackModule {
101
113
  };
102
114
  }
103
115
  configure(consumer) {
104
- const requestIdHeader = (0, client_1.getClient)()?.requestIdHeader;
116
+ const client = (0, client_1.getClient)();
117
+ const requestIdHeader = client?.requestIdHeader;
105
118
  consumer
106
- .apply((0, context_1.createRequestContextMiddleware)(requestIdHeader ? { requestIdHeader } : {}))
119
+ .apply((0, context_1.createRequestContextMiddleware)({
120
+ ...(requestIdHeader ? { requestIdHeader } : {}),
121
+ // The `captureBodies` opt-in is resolved from `init`, so the middleware wraps
122
+ // the response writers to record the response body (ADR-2026-09-16).
123
+ ...(client?.captureBodies
124
+ ? { captureBodies: true, maxBodyBytes: client.maxBodyBytes }
125
+ : {}),
126
+ }))
107
127
  .forRoutes("*");
108
128
  }
109
129
  };
@@ -1 +1 @@
1
- {"version":3,"file":"nestjs.js","sourceRoot":"","sources":["../src/nestjs.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"nestjs.js","sourceRoot":"","sources":["../src/nestjs.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * The **batched secret-key transport** (ADR-0026): the SDK buffers captured errors
2
+ * The **batched secret-key transport** (ADR-0061): the SDK buffers captured errors
3
3
  * and flushes them to `POST /api/errors` as a JSON batch authenticated with the
4
4
  * Project secret key as an `Authorization: Bearer` token.
5
5
  *
@@ -7,7 +7,7 @@
7
7
  * (on the batch cap, on an interval, on demand, and on `close`).
8
8
  * - **Coalesced:** repeats of one fingerprint in a flush window fold into a single
9
9
  * item with a summed occurrence count — the client half of the crash-loop defence
10
- * (the server folds across flushes, ADR-0027).
10
+ * (the server folds across flushes, ADR-0062).
11
11
  * - **Backed off:** a `429` opens the shared-core {@link AutoReportBackoff} window
12
12
  * (honouring `Retry-After`); while it is open captures are shed without a request.
13
13
  * - **Fail-quiet (story 34):** an unreachable, timing-out, or refusing endpoint never
package/dist/transport.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /**
3
- * The **batched secret-key transport** (ADR-0026): the SDK buffers captured errors
3
+ * The **batched secret-key transport** (ADR-0061): the SDK buffers captured errors
4
4
  * and flushes them to `POST /api/errors` as a JSON batch authenticated with the
5
5
  * Project secret key as an `Authorization: Bearer` token.
6
6
  *
@@ -8,7 +8,7 @@
8
8
  * (on the batch cap, on an interval, on demand, and on `close`).
9
9
  * - **Coalesced:** repeats of one fingerprint in a flush window fold into a single
10
10
  * item with a summed occurrence count — the client half of the crash-loop defence
11
- * (the server folds across flushes, ADR-0027).
11
+ * (the server folds across flushes, ADR-0062).
12
12
  * - **Backed off:** a `429` opens the shared-core {@link AutoReportBackoff} window
13
13
  * (honouring `Retry-After`); while it is open captures are shed without a request.
14
14
  * - **Fail-quiet (story 34):** an unreachable, timing-out, or refusing endpoint never
package/dist/version.d.ts CHANGED
@@ -10,4 +10,4 @@
10
10
  * ever drift. Do not hand-edit this line to a value other than `package.json`'s
11
11
  * version.
12
12
  */
13
- export declare const NODE_SDK_VERSION = "0.3.3";
13
+ export declare const NODE_SDK_VERSION = "0.4.0";
package/dist/version.js CHANGED
@@ -13,5 +13,5 @@ exports.NODE_SDK_VERSION = void 0;
13
13
  * ever drift. Do not hand-edit this line to a value other than `package.json`'s
14
14
  * version.
15
15
  */
16
- exports.NODE_SDK_VERSION = "0.3.3";
16
+ exports.NODE_SDK_VERSION = "0.4.0";
17
17
  //# sourceMappingURL=version.js.map
package/dist/wire.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * `@fixback/node` **vendors** this contract (the ticket #47 convention, reaffirmed by
7
7
  * ADR-0028: the shared core carries pure logic and value shapes, not the ingest
8
8
  * envelope). It is kept in lock-step with the server endpoint that parses it —
9
- * `POST /api/errors` (`apps/api/src/errors/errors.controller.ts`, ADR-0026/0027).
9
+ * `POST /api/errors` (`apps/api/src/errors/errors.controller.ts`, ADR-0061/0062).
10
10
  * `CapturedFrame` (the structured stack-frame shape) is the one type reused from the
11
11
  * shared core, so the browser and node SDKs describe a frame identically.
12
12
  */
@@ -14,11 +14,18 @@ import type { CapturedFrame } from "@fixback/sdk-core";
14
14
  /** A severity for {@link FixbackErrorEvent} messages — Sentry's level vocabulary. */
15
15
  export type Severity = "fatal" | "error" | "warning" | "info" | "debug";
16
16
  /**
17
- * The **server context** captured for a backend error (ADR-0025, spec #224).
17
+ * The **server context** captured for a backend error (ADR-0060, spec #224).
18
18
  * Private-by-default: the route **pattern** (never the concrete path with values),
19
19
  * the HTTP method, the resolved status, a correlation id, and an **app-supplied**
20
- * user ref only. Never request/response bodies, never `Authorization`/`Cookie`
21
- * headers, never env vars, never query-string values.
20
+ * user ref are always carried. Never `Authorization`/`Cookie` headers, never env
21
+ * vars, never query-string values.
22
+ *
23
+ * The request/response **bodies** are the one relaxation — captured only when the
24
+ * project opts into `init({ captureBodies: true })` (default off, ADR-2026-09-16),
25
+ * already serialized to a string and size-capped, and PII-redacted at the scrub
26
+ * choke point. They ride to the trusted secret-key server door, which re-scrubs them
27
+ * as defence-in-depth; the browser (publishable-key) door has no server-context slot,
28
+ * so a page can never smuggle one in.
22
29
  */
23
30
  export interface ServerContext {
24
31
  /** The HTTP method (`GET`, `POST`, …). */
@@ -31,6 +38,17 @@ export interface ServerContext {
31
38
  readonly requestId?: string;
32
39
  /** An **app-supplied** user reference (via `setUser`) — never scraped from the request. */
33
40
  readonly user?: string;
41
+ /**
42
+ * The failing request's **body**, serialized and size-capped — present only under
43
+ * the `captureBodies` opt-in (default off). Absent when off, when no body was
44
+ * captured, or when it exceeded the cap (degrades to exactly the off behaviour).
45
+ */
46
+ readonly requestBody?: string;
47
+ /**
48
+ * The **response body** the request resolved with, serialized and size-capped —
49
+ * present only under the `captureBodies` opt-in, on the same terms as {@link requestBody}.
50
+ */
51
+ readonly responseBody?: string;
34
52
  }
35
53
  /**
36
54
  * The assembled event a capture produces, handed to the project's optional
@@ -49,7 +67,7 @@ export interface FixbackErrorEvent {
49
67
  type: string;
50
68
  /** The error message (or the message text for a `captureMessage`). */
51
69
  value: string;
52
- /** `false` = an uncaught process crash, `true` = a caught/manual capture (ADR-0025). */
70
+ /** `false` = an uncaught process crash, `true` = a caught/manual capture (ADR-0060). */
53
71
  handled?: boolean;
54
72
  /** The severity of a `captureMessage` (event-only; unset for a `captureException`). */
55
73
  level?: Severity;
@@ -73,10 +91,11 @@ export interface FixbackErrorEvent {
73
91
  export type BeforeSend = (event: FixbackErrorEvent) => FixbackErrorEvent | null;
74
92
  /**
75
93
  * One captured backend error **on the wire** — the payload item the SDK posts.
76
- * `errorSignature` is required (the ADR-0027 fold key); everything else is optional.
77
- * Mirrors the server's `errorItemBody`. `server` rides along for forward
78
- * compatibility — the current endpoint strips unknown keys, so it is harmless today
79
- * and ready for when the server persists it.
94
+ * `errorSignature` is required (the ADR-0062 fold key); everything else is optional.
95
+ * Mirrors the server's `errorItemBody`. `server` carries the failing request's
96
+ * context, which the endpoint persists onto the error's Server context (route /
97
+ * method / status, and — under the `captureBodies` opt-in — the request/response
98
+ * bodies); any slot the endpoint does not model is stripped there.
80
99
  */
81
100
  export interface ServerErrorPayload {
82
101
  readonly errorSignature: string;
@@ -93,7 +112,7 @@ export interface ServerErrorPayload {
93
112
  * against the endpoint's contract rather than assembled inline.
94
113
  *
95
114
  * The endpoint's *response* is deliberately not modelled: the transport reads only
96
- * the status and `Retry-After` (ADR-0026's partial-failure results are for the
115
+ * the status and `Retry-After` (ADR-0061's partial-failure results are for the
97
116
  * dashboard, not the SDK), and an unread shape would be a contract to keep in
98
117
  * lock-step for nothing.
99
118
  */
package/dist/wire.js CHANGED
@@ -7,7 +7,7 @@
7
7
  * `@fixback/node` **vendors** this contract (the ticket #47 convention, reaffirmed by
8
8
  * ADR-0028: the shared core carries pure logic and value shapes, not the ingest
9
9
  * envelope). It is kept in lock-step with the server endpoint that parses it —
10
- * `POST /api/errors` (`apps/api/src/errors/errors.controller.ts`, ADR-0026/0027).
10
+ * `POST /api/errors` (`apps/api/src/errors/errors.controller.ts`, ADR-0061/0062).
11
11
  * `CapturedFrame` (the structured stack-frame shape) is the one type reused from the
12
12
  * shared core, so the browser and node SDKs describe a frame identically.
13
13
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fixback/node",
3
- "version": "0.3.3",
3
+ "version": "0.4.0",
4
4
  "description": "The Fixback backend error SDK for Node servers — one-call init, batched secret-key capture, process handlers, and Express + NestJS adapters.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -50,7 +50,7 @@
50
50
  "access": "public"
51
51
  },
52
52
  "dependencies": {
53
- "@fixback/sdk-core": "0.4.2"
53
+ "@fixback/sdk-core": "0.6.0"
54
54
  },
55
55
  "peerDependencies": {
56
56
  "@nestjs/common": ">=10.0.0",