stitchkit 0.56.0 → 0.56.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/dist/{index-n4nfa7gh.js → index-75njxz7p.js} +66 -4
- package/dist/node.js +1 -1
- package/dist/observability/audit.d.ts +4 -0
- package/dist/observability/audit.d.ts.map +1 -1
- package/dist/observability/event.d.ts +5 -0
- package/dist/observability/event.d.ts.map +1 -1
- package/dist/observability/index.js +14 -8
- package/dist/server/create.d.ts.map +1 -1
- package/dist/server/error-hook.d.ts +1 -1
- package/dist/server/error-hook.d.ts.map +1 -1
- package/dist/server/index.js +3 -2
- package/dist/server/logger.d.ts.map +1 -1
- package/dist/server/request-body.d.ts.map +1 -1
- package/llms-full.txt +64 -8
- package/package.json +1 -1
|
@@ -466,12 +466,20 @@ async function parseMultipart(req, descriptor, fieldsSchema, receivers) {
|
|
|
466
466
|
}
|
|
467
467
|
|
|
468
468
|
// src/server/request-body.ts
|
|
469
|
+
function requestAbortReason(req) {
|
|
470
|
+
return req.signal.reason ?? new DOMException("The connection was closed", "AbortError");
|
|
471
|
+
}
|
|
472
|
+
function throwIfRequestAborted(req) {
|
|
473
|
+
if (req.signal.aborted)
|
|
474
|
+
throw requestAbortReason(req);
|
|
475
|
+
}
|
|
469
476
|
function assertJsonBodyLimit(maxBytes, owner) {
|
|
470
477
|
if (maxBytes !== undefined && (!Number.isSafeInteger(maxBytes) || maxBytes <= 0)) {
|
|
471
478
|
throw new Error(`${owner} must be a positive safe integer, received ${maxBytes}`);
|
|
472
479
|
}
|
|
473
480
|
}
|
|
474
481
|
async function readRequestText(req, maxBytes) {
|
|
482
|
+
throwIfRequestAborted(req);
|
|
475
483
|
if (maxBytes === undefined)
|
|
476
484
|
return req.text();
|
|
477
485
|
assertJsonBodyLimit(maxBytes, "maxJsonBodyBytes");
|
|
@@ -480,9 +488,25 @@ async function readRequestText(req, maxBytes) {
|
|
|
480
488
|
return "";
|
|
481
489
|
const chunks = [];
|
|
482
490
|
let total = 0;
|
|
491
|
+
let rejectAbortedRead = (_reason) => {
|
|
492
|
+
return;
|
|
493
|
+
};
|
|
494
|
+
const abortedRead = new Promise((_resolve, reject) => {
|
|
495
|
+
rejectAbortedRead = reject;
|
|
496
|
+
});
|
|
497
|
+
const onAbort = () => {
|
|
498
|
+
const reason = requestAbortReason(req);
|
|
499
|
+
rejectAbortedRead(reason);
|
|
500
|
+
reader.cancel(reason).catch(() => {
|
|
501
|
+
return;
|
|
502
|
+
});
|
|
503
|
+
};
|
|
504
|
+
req.signal.addEventListener("abort", onAbort, { once: true });
|
|
505
|
+
if (req.signal.aborted)
|
|
506
|
+
onAbort();
|
|
483
507
|
try {
|
|
484
508
|
while (true) {
|
|
485
|
-
const { done, value } = await reader.read();
|
|
509
|
+
const { done, value } = await Promise.race([reader.read(), abortedRead]);
|
|
486
510
|
if (done)
|
|
487
511
|
break;
|
|
488
512
|
total += value.byteLength;
|
|
@@ -493,7 +517,9 @@ async function readRequestText(req, maxBytes) {
|
|
|
493
517
|
chunks.push(value);
|
|
494
518
|
}
|
|
495
519
|
} finally {
|
|
496
|
-
|
|
520
|
+
req.signal.removeEventListener("abort", onAbort);
|
|
521
|
+
if (!req.signal.aborted)
|
|
522
|
+
reader.releaseLock();
|
|
497
523
|
}
|
|
498
524
|
const bytes = new Uint8Array(total);
|
|
499
525
|
let offset = 0;
|
|
@@ -630,6 +656,8 @@ function safePath(pathname) {
|
|
|
630
656
|
return out;
|
|
631
657
|
}
|
|
632
658
|
function levelForStatus(status) {
|
|
659
|
+
if (status === 499)
|
|
660
|
+
return "info";
|
|
633
661
|
if (status >= 500)
|
|
634
662
|
return "error";
|
|
635
663
|
if (status >= 400)
|
|
@@ -1037,6 +1065,29 @@ function validateRawRoutes(rawRoutes) {
|
|
|
1037
1065
|
|
|
1038
1066
|
// src/server/create.ts
|
|
1039
1067
|
var BODY_METHODS = new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
1068
|
+
var MAX_REQUEST_ABORT_CAUSE_DEPTH = 8;
|
|
1069
|
+
function containsRequestAbortReason(error, reason) {
|
|
1070
|
+
let current = error;
|
|
1071
|
+
const visited = new Set;
|
|
1072
|
+
for (let depth = 0;depth <= MAX_REQUEST_ABORT_CAUSE_DEPTH; depth += 1) {
|
|
1073
|
+
if (current === reason)
|
|
1074
|
+
return true;
|
|
1075
|
+
if (depth === MAX_REQUEST_ABORT_CAUSE_DEPTH || !isRecord(current))
|
|
1076
|
+
return false;
|
|
1077
|
+
if (visited.has(current))
|
|
1078
|
+
return false;
|
|
1079
|
+
visited.add(current);
|
|
1080
|
+
current = current.cause;
|
|
1081
|
+
}
|
|
1082
|
+
return false;
|
|
1083
|
+
}
|
|
1084
|
+
function isClientClosedRequest(req, error) {
|
|
1085
|
+
if (!req.signal.aborted)
|
|
1086
|
+
return false;
|
|
1087
|
+
if (isRecord(error) && error.name === "AbortError")
|
|
1088
|
+
return true;
|
|
1089
|
+
return req.signal.reason !== undefined && containsRequestAbortReason(error, req.signal.reason);
|
|
1090
|
+
}
|
|
1040
1091
|
function createHandler(config) {
|
|
1041
1092
|
const { cors, hooks, logging = false, observability, trustProxy = false } = config;
|
|
1042
1093
|
if (cors)
|
|
@@ -1100,7 +1151,7 @@ function createHandler(config) {
|
|
|
1100
1151
|
} catch {}
|
|
1101
1152
|
}
|
|
1102
1153
|
let completed = false;
|
|
1103
|
-
const complete = (status, errorCode2) => {
|
|
1154
|
+
const complete = (status, errorCode2, outcome) => {
|
|
1104
1155
|
if (completed)
|
|
1105
1156
|
return;
|
|
1106
1157
|
completed = true;
|
|
@@ -1148,11 +1199,22 @@ function createHandler(config) {
|
|
|
1148
1199
|
const context = getRequestContext();
|
|
1149
1200
|
if (observability && context) {
|
|
1150
1201
|
try {
|
|
1151
|
-
observability.complete({
|
|
1202
|
+
observability.complete({
|
|
1203
|
+
context,
|
|
1204
|
+
statusCode: status,
|
|
1205
|
+
durationMs,
|
|
1206
|
+
payload,
|
|
1207
|
+
...outcome !== undefined && { outcome }
|
|
1208
|
+
});
|
|
1152
1209
|
} catch {}
|
|
1153
1210
|
}
|
|
1154
1211
|
};
|
|
1155
1212
|
const respondError = async (err, errCtx, endpoint) => {
|
|
1213
|
+
if (isClientClosedRequest(req, err)) {
|
|
1214
|
+
const response = applyCors(new Response(null, { status: 499, statusText: "Client Closed Request" }), cors, req);
|
|
1215
|
+
complete(response.status, undefined, "cancelled");
|
|
1216
|
+
return response;
|
|
1217
|
+
}
|
|
1156
1218
|
const recordFailure = (normalized) => {
|
|
1157
1219
|
if (getRequestContext()?.error !== undefined)
|
|
1158
1220
|
return;
|
package/dist/node.js
CHANGED
|
@@ -34,6 +34,8 @@ export interface SinkDrop {
|
|
|
34
34
|
export interface RequestObservabilityConfig extends RequestEventSinkConfig {
|
|
35
35
|
/** Clone and record JSON request bodies. Default false. */
|
|
36
36
|
includePayload?: boolean;
|
|
37
|
+
/** Emit client-closed HTTP requests as `outcome: 'cancelled'`. Default false. */
|
|
38
|
+
includeCancelled?: boolean;
|
|
37
39
|
}
|
|
38
40
|
export interface ObservabilityConfig {
|
|
39
41
|
request?: RequestObservabilityConfig;
|
|
@@ -45,6 +47,8 @@ export interface HttpRequestCompletion {
|
|
|
45
47
|
statusCode: number;
|
|
46
48
|
durationMs: number;
|
|
47
49
|
payload?: Promise<unknown>;
|
|
50
|
+
/** Framework-owned neutral cancellation; currently paired with HTTP 499. */
|
|
51
|
+
outcome?: 'cancelled';
|
|
48
52
|
}
|
|
49
53
|
/** Server-facing request projection returned by `createObservability`. */
|
|
50
54
|
export interface HttpRequestObserver {
|
|
@@ -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;AAClE,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAe,KAAK,eAAe,EAAmB,MAAM,YAAY,CAAC;AAChF,OAAO,EAEL,KAAK,wBAAwB,EAI7B,KAAK,mBAAmB,EAEzB,MAAM,UAAU,CAAC;AAGlB,kEAAkE;AAClE,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;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;IAC3B,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2DAA2D;IAC3D,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,SAAS,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,8DAA8D;IAC9D,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD;AAED,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,QAAQ,CAAC;AAEnD,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,YAAY,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,0BAA2B,SAAQ,sBAAsB;IACxE,2DAA2D;IAC3D,cAAc,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/observability/audit.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAc,MAAM,kBAAkB,CAAC;AAClE,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAe,KAAK,eAAe,EAAmB,MAAM,YAAY,CAAC;AAChF,OAAO,EAEL,KAAK,wBAAwB,EAI7B,KAAK,mBAAmB,EAEzB,MAAM,UAAU,CAAC;AAGlB,kEAAkE;AAClE,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;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;IAC3B,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2DAA2D;IAC3D,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,SAAS,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,8DAA8D;IAC9D,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD;AAED,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,QAAQ,CAAC;AAEnD,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,YAAY,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,0BAA2B,SAAQ,sBAAsB;IACxE,2DAA2D;IAC3D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,iFAAiF;IACjF,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,0BAA0B,CAAC;IACrC,KAAK,CAAC,EAAE,sBAAsB,CAAC;CAChC;AAED,gEAAgE;AAChE,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,cAAc,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3B,4EAA4E;IAC5E,OAAO,CAAC,EAAE,WAAW,CAAC;CACvB;AAED,0EAA0E;AAC1E,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,qBAAqB,GAAG,IAAI,CAAC;CACnD;AAED,iFAAiF;AACjF,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,QAAQ,EAAE,aAAa,CAAC;IACxB,iDAAiD;IACjD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,2EAA2E;IAC3E,SAAS,IAAI,mBAAmB,CAAC;IACjC,4EAA4E;IAC5E,KAAK,IAAI,OAAO,CAAC,wBAAwB,CAAC,CAAC;CAC5C;AAkOD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,aAAa,CA8J9E"}
|
|
@@ -48,6 +48,11 @@ export interface RequestEvent {
|
|
|
48
48
|
parentSpanId?: string;
|
|
49
49
|
/** Whether the call succeeded. */
|
|
50
50
|
ok: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Explicit non-failure outcome. Present only for event classes a sink opted
|
|
53
|
+
* into; ordinary success/failure rows retain their released shape.
|
|
54
|
+
*/
|
|
55
|
+
outcome?: 'cancelled';
|
|
51
56
|
/** HTTP status — the real status for HTTP, `200` / `400` for a tool call. */
|
|
52
57
|
statusCode: number;
|
|
53
58
|
/** Wall-clock duration. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event.d.ts","sourceRoot":"","sources":["../../src/observability/event.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC/E,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,2EAA2E;IAC3E,SAAS,CAAC,EAAE,WAAW,GAAG,aAAa,CAAC;IACxC,6EAA6E;IAC7E,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,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;;;OAGG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,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,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC/E,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,2EAA2E;IAC3E,SAAS,CAAC,EAAE,WAAW,GAAG,aAAa,CAAC;IACxC,6EAA6E;IAC7E,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,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;;;OAGG;IACH,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,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;;;OAGG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,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"}
|
|
@@ -250,13 +250,16 @@ function createObservability(config) {
|
|
|
250
250
|
let closePromise;
|
|
251
251
|
const request = config.request ? {
|
|
252
252
|
includePayload: config.request.includePayload ?? false,
|
|
253
|
-
complete: ({ context, statusCode, durationMs, payload }) => {
|
|
253
|
+
complete: ({ context, statusCode, durationMs, payload, outcome }) => {
|
|
254
254
|
const requestConfig = config.request;
|
|
255
255
|
if (!requestConfig)
|
|
256
256
|
return;
|
|
257
|
+
const cancelled = outcome === "cancelled";
|
|
258
|
+
if (cancelled && !requestConfig.includeCancelled)
|
|
259
|
+
return;
|
|
257
260
|
requestManager?.submit(async () => {
|
|
258
261
|
let body;
|
|
259
|
-
if (payload) {
|
|
262
|
+
if (!cancelled && payload) {
|
|
260
263
|
try {
|
|
261
264
|
body = await payload;
|
|
262
265
|
} catch {
|
|
@@ -277,15 +280,18 @@ function createObservability(config) {
|
|
|
277
280
|
traceId: context.trace.traceId,
|
|
278
281
|
spanId: context.trace.spanId,
|
|
279
282
|
parentSpanId: context.trace.parentSpanId,
|
|
280
|
-
ok: statusCode < 400,
|
|
283
|
+
ok: !cancelled && statusCode < 400,
|
|
284
|
+
...cancelled && { outcome: "cancelled" },
|
|
281
285
|
statusCode,
|
|
282
286
|
durationMs,
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
+
...!cancelled && {
|
|
288
|
+
errorCode: context.error?.code,
|
|
289
|
+
errorMessage: context.error?.message,
|
|
290
|
+
...context.error?.details !== undefined && {
|
|
291
|
+
errorDetail: sanitizePayload(context.error.details, requestConfig.sanitize)
|
|
292
|
+
}
|
|
287
293
|
},
|
|
288
|
-
payload: sanitizePayload(body, requestConfig.sanitize),
|
|
294
|
+
payload: cancelled ? null : sanitizePayload(body, requestConfig.sanitize),
|
|
289
295
|
resultSize: null,
|
|
290
296
|
responseBytes: 0,
|
|
291
297
|
userId: context.userId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/server/create.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/server/create.ts"],"names":[],"mappings":"AAqDA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAA2B,MAAM,SAAS,CAAC;AA2BpF,wBAAgB,aAAa,CAAC,OAAO,GAAG,OAAO,EAC7C,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,GAC7B,YAAY,CAAC,OAAO,CAAC,CA8gBvB"}
|
|
@@ -55,7 +55,7 @@ export interface ErrorHookConfig<TWireCode extends string = string> {
|
|
|
55
55
|
* `satisfies Record<StitchErrorCode, …>` makes a new framework code a compile
|
|
56
56
|
* error here. Codes you threw yourself (not stitchkit's) pass through as-is.
|
|
57
57
|
*/
|
|
58
|
-
codeMap?: Record<StitchErrorCode, TWireCode
|
|
58
|
+
codeMap?: Partial<Record<StitchErrorCode, TWireCode>>;
|
|
59
59
|
/** Build the response body from the resolved error. */
|
|
60
60
|
/**
|
|
61
61
|
* Build the response body from the resolved error. `ctx` is the request's
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-hook.d.ts","sourceRoot":"","sources":["../../src/server/error-hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAEtE,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzD,uEAAuE;AACvE,MAAM,WAAW,aAAa;IAC5B,wFAAwF;IACxF,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,+FAA+F;IAC/F,OAAO,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,oCAAoC;AACpC,MAAM,WAAW,eAAe,CAAC,SAAS,SAAS,MAAM,GAAG,MAAM;IAChE;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"error-hook.d.ts","sourceRoot":"","sources":["../../src/server/error-hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAEtE,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzD,uEAAuE;AACvE,MAAM,WAAW,aAAa;IAC5B,wFAAwF;IACxF,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,+FAA+F;IAC/F,OAAO,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,oCAAoC;AACpC,MAAM,WAAW,eAAe,CAAC,SAAS,SAAS,MAAM,GAAG,MAAM;IAChE;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC,CAAC;IACtD,uDAAuD;IACvD;;;;;OAKG;IACH,MAAM,EAAE,CACN,IAAI,EAAE,aAAa,EACnB,GAAG,EAAE,cAAc,EACnB,QAAQ,CAAC,EAAE,SAAS,KACjB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAChC;;;;OAIG;IACH,OAAO,CAAC,EAAE,CACR,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,aAAa,EACnB,GAAG,EAAE,cAAc,EACnB,QAAQ,CAAC,EAAE,SAAS,KACjB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACjC;AAED,mEAAmE;AACnE,wBAAgB,eAAe,CAAC,SAAS,SAAS,MAAM,GAAG,MAAM,EAC/D,MAAM,EAAE,eAAe,CAAC,SAAS,CAAC,GACjC,WAAW,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,CAgCxC"}
|
package/dist/server/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
parseMultipart,
|
|
13
13
|
socketIoLane,
|
|
14
14
|
webSocketLane
|
|
15
|
-
} from "../index-
|
|
15
|
+
} from "../index-75njxz7p.js";
|
|
16
16
|
import {
|
|
17
17
|
composeAuthHooks,
|
|
18
18
|
createAuthHook,
|
|
@@ -349,7 +349,8 @@ function cacheHeaders(maxAge, scope = "public") {
|
|
|
349
349
|
function createErrorHook(config) {
|
|
350
350
|
return async (ctx, error, endpoint) => {
|
|
351
351
|
const appErr = normalizeError(error);
|
|
352
|
-
const
|
|
352
|
+
const mapped = config.codeMap && isStitchErrorCode(appErr.code) ? config.codeMap[appErr.code] : undefined;
|
|
353
|
+
const code = mapped ?? appErr.code;
|
|
353
354
|
const info = {
|
|
354
355
|
code,
|
|
355
356
|
status: appErr.status,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/server/logger.ts"],"names":[],"mappings":"AAqCA,0CAA0C;AAC1C,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE1C;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,CAAC,EAAE,SAAS,GAAG,SAAS,CAGlE;AAaD,6DAA6D;AAC7D,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEnD;AAoBD,yCAAyC;AACzC,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/server/logger.ts"],"names":[],"mappings":"AAqCA,0CAA0C;AAC1C,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE1C;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,CAAC,EAAE,SAAS,GAAG,SAAS,CAGlE;AAaD,6DAA6D;AAC7D,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEnD;AAoBD,yCAAyC;AACzC,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAKxE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM,GACjB;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAIA;AA0BD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;CACjB;AAID,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAGnE;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,SAAS,EACjB,SAAS,CAAC,EAAE,MAAM,GACjB,UAAU,CASZ;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,MAAM,CAMR;AAED,uDAAuD;AACvD,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,OAAO,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,UAAU,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,UAAU,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,MAAM,EAAE,SAAS,CAAC;IAClB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAyBzD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-body.d.ts","sourceRoot":"","sources":["../../src/server/request-body.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"request-body.d.ts","sourceRoot":"","sources":["../../src/server/request-body.ts"],"names":[],"mappings":"AAUA,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAIrF;AAED,6FAA6F;AAC7F,wBAAsB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAuDtF"}
|
package/llms-full.txt
CHANGED
|
@@ -989,6 +989,18 @@ createServer({
|
|
|
989
989
|
| `'pretty'` | two coloured lines per request — `→` on arrival, `←` on completion | no — a line sized for a terminal is not a record |
|
|
990
990
|
| `'json'` | one structured line per completed request | yes |
|
|
991
991
|
|
|
992
|
+
Status `499` has one framework-wide meaning: the client closed the request. It
|
|
993
|
+
is logged at `info`, not under the ordinary `4xx → warn` rule. A confirmed
|
|
994
|
+
disconnect is not sent through project `onError`, `normalizeError` or the
|
|
995
|
+
request-error recorder; an `AbortError` while the request signal is still active
|
|
996
|
+
remains an internal failure. A runtime abort reason may be preserved by identity
|
|
997
|
+
through at most eight cycle-safe standard `cause` links; error messages and codes
|
|
998
|
+
are never classifiers. The same rule applies when the disconnect happens while
|
|
999
|
+
Stitchkit is reading a JSON upload body: bounded reads race every pending stream
|
|
1000
|
+
read against the request signal and never parse a cancelled partial body. `499`
|
|
1001
|
+
is transport telemetry, not a response declared in the contract or generated
|
|
1002
|
+
OpenAPI document.
|
|
1003
|
+
|
|
992
1004
|
Unset, `format` follows `NODE_ENV`: `json` under `production`, `pretty`
|
|
993
1005
|
otherwise. That default is read **per request** — not at import, not when this
|
|
994
1006
|
package was built — so it reflects the environment your app actually runs in.
|
|
@@ -1159,7 +1171,9 @@ createServer({
|
|
|
1159
1171
|
- **`afterHandle`** — receives the handler result; return a replacement to
|
|
1160
1172
|
transform it.
|
|
1161
1173
|
- **`onError`** — receives any thrown error; return a `Response` to customise
|
|
1162
|
-
the error body. Without it, errors render through the standard envelope.
|
|
1174
|
+
the error body. Without it, errors render through the standard envelope. A
|
|
1175
|
+
confirmed client disconnect is a transport cancellation rather than an
|
|
1176
|
+
application error and deliberately bypasses this hook.
|
|
1163
1177
|
|
|
1164
1178
|
Hooks see `RuntimeContext` (loose types); handlers see `HandlerContext` (typed).
|
|
1165
1179
|
That split is deliberate — see [ADR 0003](../decisions/0003-two-context-types.md).
|
|
@@ -4878,9 +4892,11 @@ a request context, and event projections — and you usually touch only the last
|
|
|
4878
4892
|
### createObservability
|
|
4879
4893
|
|
|
4880
4894
|
`createObservability` configures request and tool projections independently.
|
|
4881
|
-
Every completed call is normalised into one `RequestEvent`; HTTP
|
|
4882
|
-
owned directly by `createHandler`, while MCP/Agent completion uses
|
|
4883
|
-
`ToolCallHooks` runner.
|
|
4895
|
+
Every completed application call is normalised into one `RequestEvent`; HTTP
|
|
4896
|
+
completion is owned directly by `createHandler`, while MCP/Agent completion uses
|
|
4897
|
+
the canonical `ToolCallHooks` runner. Confirmed HTTP client cancellation stays
|
|
4898
|
+
in the access log by default and becomes a structured event only when the
|
|
4899
|
+
request sink opts in. There is no nested HTTP audit wrapper.
|
|
4884
4900
|
|
|
4885
4901
|
```ts
|
|
4886
4902
|
import { createObservability } from 'stitchkit/observability'
|
|
@@ -4901,6 +4917,7 @@ export const observability = createObservability({
|
|
|
4901
4917
|
request: {
|
|
4902
4918
|
write,
|
|
4903
4919
|
includePayload: false, // default: no Request.clone(), payload is null
|
|
4920
|
+
includeCancelled: false, // default: keep client closes in access logs only
|
|
4904
4921
|
filter: (event) => event.method !== 'GET',
|
|
4905
4922
|
},
|
|
4906
4923
|
tools: {
|
|
@@ -4923,6 +4940,40 @@ Each sink runs fire-and-forget and fails independently: a slow or broken request
|
|
|
4923
4940
|
sink cannot block the response, suppress operational logging or break the tool
|
|
4924
4941
|
sink.
|
|
4925
4942
|
|
|
4943
|
+
#### Client cancellation
|
|
4944
|
+
|
|
4945
|
+
A physical client close is classified only when the request's own signal is
|
|
4946
|
+
aborted and the thrown value is either an `AbortError` or contains the exact
|
|
4947
|
+
`request.signal.reason` by identity at the top level or within at most eight
|
|
4948
|
+
standard `cause` links. Cause traversal is cycle-safe. No message or error-code
|
|
4949
|
+
matching is used; active requests and unrelated/deeper causes remain application
|
|
4950
|
+
failures. The access completion is always `499/info`, without application error
|
|
4951
|
+
fields or project `onError`.
|
|
4952
|
+
|
|
4953
|
+
Structured request sinks are default-preserving: existing sinks receive no row
|
|
4954
|
+
for this outcome. Opt in when cancellation frequency belongs in the durable
|
|
4955
|
+
stream:
|
|
4956
|
+
|
|
4957
|
+
```ts
|
|
4958
|
+
const observability = createObservability({
|
|
4959
|
+
request: {
|
|
4960
|
+
includeCancelled: true,
|
|
4961
|
+
write: (event) => {
|
|
4962
|
+
if (event.outcome === 'cancelled') return recordClientClose(event)
|
|
4963
|
+
return recordApplicationRequest(event)
|
|
4964
|
+
},
|
|
4965
|
+
},
|
|
4966
|
+
})
|
|
4967
|
+
```
|
|
4968
|
+
|
|
4969
|
+
An opted-in row has `outcome: 'cancelled'`, `ok: false`, `statusCode: 499`, the
|
|
4970
|
+
ordinary identity/trace/timing fields and no `errorCode`, `errorMessage` or
|
|
4971
|
+
`errorDetail`. `ok` remains the legacy success bit: branch on `outcome` first.
|
|
4972
|
+
Cancellation rows use the same filter, capacity, diagnostics, ordering,
|
|
4973
|
+
`flush()` and `close()` machinery as every request event. MCP protocol
|
|
4974
|
+
cancellation remains represented by `event.mcp.outcome`; Agent and CLI have no
|
|
4975
|
+
generic client-disconnect signal and are not inferred from error text.
|
|
4976
|
+
|
|
4926
4977
|
The fire-and-forget work has an explicit bounded lifecycle:
|
|
4927
4978
|
|
|
4928
4979
|
```ts
|
|
@@ -5003,7 +5054,8 @@ queryable across all three:
|
|
|
5003
5054
|
| `httpMethod` | the contract verb on **tool** events (their `method` is `TOOL`) — filter reads vs writes across both surfaces with `(event.httpMethod ?? event.method) !== 'GET'` |
|
|
5004
5055
|
| `dimensions` | app-defined domain dimensions (tenant / project / entity id) — see [request context](#request-context) |
|
|
5005
5056
|
| `traceId` / `spanId` / `parentSpanId` | [W3C trace context](#trace-context) |
|
|
5006
|
-
| `
|
|
5057
|
+
| `outcome` | optional `'cancelled'` on explicitly enabled HTTP client-close rows; ordinary rows omit it |
|
|
5058
|
+
| `ok` / `statusCode` | legacy success bit plus real HTTP status, or `200`/`400` for a tool; an opted-in cancellation is `false` / `499` |
|
|
5007
5059
|
| `durationMs` / `startedAt` | timing |
|
|
5008
5060
|
| `errorCode` / `errorMessage` / `errorDetail` | failures only — `errorDetail` carries the structure the message flattens (e.g. Zod issues) |
|
|
5009
5061
|
| `payload` | sanitised tool arguments; HTTP is `null` unless request `includePayload` is enabled |
|
|
@@ -5253,6 +5305,10 @@ success and error alike — carrying the tool name, the arguments, the result, t
|
|
|
5253
5305
|
duration, the call context, the endpoint identity, and (only when the call failed
|
|
5254
5306
|
by throwing) the raw thrown value.
|
|
5255
5307
|
|
|
5308
|
+
A confirmed HTTP client disconnect is the exception to the raw-hook table: it
|
|
5309
|
+
is transport cancellation, so neither `afterHandle` nor project `onError` runs.
|
|
5310
|
+
Use the `499/info` access completion or opt-in request cancellation event above.
|
|
5311
|
+
|
|
5256
5312
|
```ts
|
|
5257
5313
|
createMcpHandler({
|
|
5258
5314
|
serverInfo, auth, services,
|
|
@@ -7537,7 +7593,7 @@ audit event. See the [Observability guide](../guide/observability.md).
|
|
|
7537
7593
|
| Export | Kind | Summary |
|
|
7538
7594
|
|--------|------|---------|
|
|
7539
7595
|
| `createObservability` | function | configure framework-owned request completion and canonical tool event sinks — [guide](../guide/observability.md#createobservability) |
|
|
7540
|
-
| `RequestEvent` | _type_ | the normalised audit event handed to the sink |
|
|
7596
|
+
| `RequestEvent` | _type_ | the normalised audit event handed to the sink; opt-in HTTP cancellation rows carry `outcome: 'cancelled'` |
|
|
7541
7597
|
| `ObservabilityConfig` | _type_ | independent request and tool sink configuration |
|
|
7542
7598
|
| `Observability` | _type_ | `{ request?, toolCall, getStatus(), flush(), close() }` with bounded sink lifecycle |
|
|
7543
7599
|
| `ObservabilitySinkStatus` | _type_ | immutable counters for one bounded request/tool sink |
|
|
@@ -7545,11 +7601,11 @@ audit event. See the [Observability guide](../guide/observability.md).
|
|
|
7545
7601
|
| `ObservabilityDrainReport` | _type_ | final closed/drained snapshot plus duration |
|
|
7546
7602
|
| `ObservabilitySinkStatusSchema` / `ObservabilityStatusSchema` / `ObservabilityDrainReportSchema` | schema | runtime schemas for status/report integration boundaries |
|
|
7547
7603
|
| `RequestEventSinkConfig` | _type_ | `write`, filter/sanitisation, `maxPending`, `onSinkError` and `onDrop` |
|
|
7548
|
-
| `RequestObservabilityConfig` | _type_ | request sink plus opt-in payload capture |
|
|
7604
|
+
| `RequestObservabilityConfig` | _type_ | request sink plus opt-in payload capture and default-off `includeCancelled` rows |
|
|
7549
7605
|
| `SinkDropReason` | _type_ | `'capacity' \| 'closed'` |
|
|
7550
7606
|
| `SinkError` | _type_ | isolated sink/projection failure and optional event |
|
|
7551
7607
|
| `SinkDrop` | _type_ | rejected event, reason and current pending count |
|
|
7552
|
-
| `HttpRequestCompletion` | _type_ | the single framework-owned HTTP outcome projected to logging and request events |
|
|
7608
|
+
| `HttpRequestCompletion` | _type_ | the single framework-owned HTTP outcome, including optional neutral cancellation, projected to logging and request events |
|
|
7553
7609
|
| `HttpRequestObserver` | _type_ | server-facing projection consumed by `HandlerConfig.observability` |
|
|
7554
7610
|
|
|
7555
7611
|
### Request context
|
package/package.json
CHANGED