stitchkit 0.31.0 → 0.33.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 +3 -3
- package/dist/{index-zza375qp.js → index-enc7t99e.js} +25 -9
- package/dist/{index-zj1s68tb.js → index-jr8vf7g8.js} +10 -10
- package/dist/{index-3hsagjqf.js → index-k7ysesv0.js} +27 -12
- package/dist/{index-jvescqgr.js → index-nmq59nae.js} +3 -5
- package/dist/{index-9g9d6r1h.js → index-wya8bkme.js} +1 -1
- package/dist/index-x3fcszf8.js +8 -0
- package/dist/internal/errors.d.ts +15 -0
- package/dist/internal/errors.d.ts.map +1 -1
- package/dist/node.js +4 -4
- package/dist/observability/audit.d.ts.map +1 -1
- package/dist/observability/index.js +9 -5
- package/dist/server/create.d.ts.map +1 -1
- package/dist/server/index.js +15 -15
- package/dist/tools/execute.d.ts +12 -1
- package/dist/tools/execute.d.ts.map +1 -1
- package/dist/tools.js +8 -8
- package/llms-full.txt +47 -26
- package/package.json +3 -2
- package/dist/index-c7nyw0yt.js +0 -20
package/dist/cli.js
CHANGED
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
emitResult,
|
|
5
5
|
parseCliArgs,
|
|
6
6
|
pollUntilDone
|
|
7
|
-
} from "./index-
|
|
7
|
+
} from "./index-jr8vf7g8.js";
|
|
8
8
|
import"./index-0ed3bx43.js";
|
|
9
|
-
import"./index-
|
|
10
|
-
import"./index-
|
|
9
|
+
import"./index-x3fcszf8.js";
|
|
10
|
+
import"./index-enc7t99e.js";
|
|
11
11
|
export {
|
|
12
12
|
pollUntilDone,
|
|
13
13
|
parseCliArgs,
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
} from "./index-c7nyw0yt.js";
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
4
3
|
|
|
5
4
|
// src/contract/errors.ts
|
|
6
5
|
var APP_ERROR_BRAND = Symbol.for("stitchkit.AppError");
|
|
@@ -77,6 +76,13 @@ function mergeMeta(contractMeta, endpointMeta) {
|
|
|
77
76
|
return { ...contractMeta };
|
|
78
77
|
return { ...contractMeta, ...endpointMeta };
|
|
79
78
|
}
|
|
79
|
+
// src/internal/typed.ts
|
|
80
|
+
function typedEntries(value) {
|
|
81
|
+
return Object.entries(value);
|
|
82
|
+
}
|
|
83
|
+
function isRecord(value) {
|
|
84
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
85
|
+
}
|
|
80
86
|
// src/contract/pagination.ts
|
|
81
87
|
import { z } from "zod";
|
|
82
88
|
|
|
@@ -123,6 +129,15 @@ function errorCode(err) {
|
|
|
123
129
|
return "VALIDATION_ERROR";
|
|
124
130
|
return;
|
|
125
131
|
}
|
|
132
|
+
function recordedErrorMessage(code, envelopeMessage, thrown) {
|
|
133
|
+
if (code === "INTERNAL_SERVER_ERROR" && thrown !== undefined) {
|
|
134
|
+
if (thrown instanceof Error)
|
|
135
|
+
return thrown.message;
|
|
136
|
+
if (typeof thrown === "string")
|
|
137
|
+
return thrown;
|
|
138
|
+
}
|
|
139
|
+
return envelopeMessage;
|
|
140
|
+
}
|
|
126
141
|
function normalizeError(err) {
|
|
127
142
|
if (AppError.is(err))
|
|
128
143
|
return err;
|
|
@@ -169,11 +184,12 @@ function validateHandlerOutput(schema, data, onStripped) {
|
|
|
169
184
|
};
|
|
170
185
|
}
|
|
171
186
|
|
|
172
|
-
// src/internal/
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
187
|
+
// src/internal/safe-json.ts
|
|
188
|
+
function isUnsafeKey(key) {
|
|
189
|
+
return key === "__proto__";
|
|
190
|
+
}
|
|
191
|
+
function safeJsonParse(text) {
|
|
192
|
+
return JSON.parse(text, (key, value) => isUnsafeKey(key) ? undefined : value);
|
|
177
193
|
}
|
|
178
194
|
|
|
179
|
-
export { mergeMeta, AppError, notFound, badRequest, unauthorized, forbidden, conflict, rateLimited, STITCH_ERROR_STATUS, isStitchErrorCode, appError, bytesToBase64Url, base64UrlToBytes, formatZodError, zodIssues, errorCode, normalizeError, validateHandlerOutput,
|
|
195
|
+
export { __require, mergeMeta, AppError, notFound, badRequest, unauthorized, forbidden, conflict, rateLimited, STITCH_ERROR_STATUS, isStitchErrorCode, appError, typedEntries, isRecord, bytesToBase64Url, base64UrlToBytes, formatZodError, zodIssues, errorCode, recordedErrorMessage, normalizeError, validateHandlerOutput, isUnsafeKey, safeJsonParse };
|
|
@@ -3,16 +3,16 @@ import {
|
|
|
3
3
|
toJsonSchema
|
|
4
4
|
} from "./index-0ed3bx43.js";
|
|
5
5
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
normalizeError,
|
|
9
|
-
validateHandlerOutput
|
|
10
|
-
} from "./index-zza375qp.js";
|
|
6
|
+
isWithinDir
|
|
7
|
+
} from "./index-x3fcszf8.js";
|
|
11
8
|
import {
|
|
9
|
+
formatZodError,
|
|
12
10
|
isRecord,
|
|
13
11
|
isUnsafeKey,
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
normalizeError,
|
|
13
|
+
safeJsonParse,
|
|
14
|
+
validateHandlerOutput
|
|
15
|
+
} from "./index-enc7t99e.js";
|
|
16
16
|
|
|
17
17
|
// src/tools/coerce.ts
|
|
18
18
|
import { z } from "zod";
|
|
@@ -348,8 +348,8 @@ function toolResultFromError(err) {
|
|
|
348
348
|
}
|
|
349
349
|
async function executeToolMethod(method, toolName, rawArgs, context, hooks, lifecycle, coerceJson = false, onOutputStrip) {
|
|
350
350
|
const startedAt = Date.now();
|
|
351
|
-
const finish = async (result) => {
|
|
352
|
-
await hooks?.afterToolCall?.(toolName, rawArgs, result, Date.now() - startedAt, context, method);
|
|
351
|
+
const finish = async (result, thrown) => {
|
|
352
|
+
await hooks?.afterToolCall?.(toolName, rawArgs, result, Date.now() - startedAt, context, method, thrown);
|
|
353
353
|
return result;
|
|
354
354
|
};
|
|
355
355
|
if (hooks?.beforeToolCall) {
|
|
@@ -430,7 +430,7 @@ async function executeToolMethod(method, toolName, rawArgs, context, hooks, life
|
|
|
430
430
|
console.error("[stitchkit] onToolError hook failed:", hookErr);
|
|
431
431
|
}
|
|
432
432
|
}
|
|
433
|
-
return finish(toolResultFromError(err));
|
|
433
|
+
return finish(toolResultFromError(err), err);
|
|
434
434
|
}
|
|
435
435
|
}
|
|
436
436
|
|
|
@@ -4,14 +4,8 @@ import {
|
|
|
4
4
|
corsPreflightResponse
|
|
5
5
|
} from "./index-czmqks7r.js";
|
|
6
6
|
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
errorCode,
|
|
10
|
-
isWithinDir,
|
|
11
|
-
mergeMeta,
|
|
12
|
-
normalizeError,
|
|
13
|
-
validateHandlerOutput
|
|
14
|
-
} from "./index-zza375qp.js";
|
|
7
|
+
isWithinDir
|
|
8
|
+
} from "./index-x3fcszf8.js";
|
|
15
9
|
import {
|
|
16
10
|
extractIp,
|
|
17
11
|
getClientInfo,
|
|
@@ -19,15 +13,23 @@ import {
|
|
|
19
13
|
parseQueryParams,
|
|
20
14
|
resolveSocketIp,
|
|
21
15
|
resolveTraceId,
|
|
22
|
-
setRequestEndpoint
|
|
23
|
-
|
|
16
|
+
setRequestEndpoint,
|
|
17
|
+
setRequestError
|
|
18
|
+
} from "./index-wya8bkme.js";
|
|
24
19
|
import {
|
|
20
|
+
AppError,
|
|
25
21
|
__require,
|
|
22
|
+
badRequest,
|
|
23
|
+
errorCode,
|
|
26
24
|
isRecord,
|
|
27
25
|
isUnsafeKey,
|
|
26
|
+
mergeMeta,
|
|
27
|
+
normalizeError,
|
|
28
|
+
recordedErrorMessage,
|
|
28
29
|
safeJsonParse,
|
|
29
|
-
typedEntries
|
|
30
|
-
|
|
30
|
+
typedEntries,
|
|
31
|
+
validateHandlerOutput
|
|
32
|
+
} from "./index-enc7t99e.js";
|
|
31
33
|
|
|
32
34
|
// src/server/multipart.ts
|
|
33
35
|
var DEFAULT_MAX_UPLOAD_BYTES = 25 * 1024 * 1024;
|
|
@@ -646,10 +648,22 @@ function createHandler(config) {
|
|
|
646
648
|
} catch {}
|
|
647
649
|
};
|
|
648
650
|
const respondError = async (err, errCtx, endpoint) => {
|
|
651
|
+
const recordFailure = (normalized) => {
|
|
652
|
+
if (getRequestContext()?.error !== undefined)
|
|
653
|
+
return;
|
|
654
|
+
const known = AppError.is(err) ? err : normalized;
|
|
655
|
+
const code = known?.code ?? errorCode(err) ?? "INTERNAL_SERVER_ERROR";
|
|
656
|
+
setRequestError({
|
|
657
|
+
code,
|
|
658
|
+
message: recordedErrorMessage(code, known?.message, err),
|
|
659
|
+
details: known?.details
|
|
660
|
+
});
|
|
661
|
+
};
|
|
649
662
|
if (hooks?.onError) {
|
|
650
663
|
try {
|
|
651
664
|
const response = await hooks.onError(errCtx ?? buildErrorContext(req, url, traceId, clientIp), err, endpoint);
|
|
652
665
|
if (response instanceof Response) {
|
|
666
|
+
recordFailure();
|
|
653
667
|
const withCors = applyCors(response, cors, req);
|
|
654
668
|
logDone(withCors.status, errorCode(err));
|
|
655
669
|
return withCors;
|
|
@@ -657,6 +671,7 @@ function createHandler(config) {
|
|
|
657
671
|
} catch {}
|
|
658
672
|
}
|
|
659
673
|
const appErr = normalizeError(err);
|
|
674
|
+
recordFailure(appErr);
|
|
660
675
|
logDone(appErr.status, appErr.code);
|
|
661
676
|
return json(appErr.toJSON(), appErr.status, cors, req);
|
|
662
677
|
};
|
|
@@ -2,13 +2,11 @@ import {
|
|
|
2
2
|
base64UrlToBytes,
|
|
3
3
|
bytesToBase64Url,
|
|
4
4
|
forbidden,
|
|
5
|
-
unauthorized
|
|
6
|
-
} from "./index-zza375qp.js";
|
|
7
|
-
import {
|
|
8
5
|
isRecord,
|
|
9
6
|
isUnsafeKey,
|
|
10
|
-
safeJsonParse
|
|
11
|
-
|
|
7
|
+
safeJsonParse,
|
|
8
|
+
unauthorized
|
|
9
|
+
} from "./index-enc7t99e.js";
|
|
12
10
|
|
|
13
11
|
// src/server/middleware/cookies.ts
|
|
14
12
|
function parseCookies(header) {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// src/internal/within-dir.ts
|
|
2
|
+
import { sep } from "node:path";
|
|
3
|
+
function isWithinDir(root, target) {
|
|
4
|
+
const base = root.endsWith(sep) ? root.slice(0, -sep.length) : root;
|
|
5
|
+
return target === root || target === base || target.startsWith(base + sep);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export { isWithinDir };
|
|
@@ -24,6 +24,21 @@ export declare function zodIssues(error: z.ZodError): ZodIssueSummary[];
|
|
|
24
24
|
* produced elsewhere — a custom `onError` hook that returns its own `Response`.
|
|
25
25
|
*/
|
|
26
26
|
export declare function errorCode(err: unknown): string | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* The message a **server-side record** should carry for a failure — an audit
|
|
29
|
+
* row, not a response.
|
|
30
|
+
*
|
|
31
|
+
* Normally the envelope's: it is truthful for an `AppError` or a `ZodError`, and
|
|
32
|
+
* it is what the caller was told, so the record and the response agree. The
|
|
33
|
+
* exception is the scrubbed one — an unexpected throw becomes
|
|
34
|
+
* `INTERNAL_SERVER_ERROR` / "Internal server error", which tells a later reader
|
|
35
|
+
* nothing at all, and there the raw message goes in instead.
|
|
36
|
+
*
|
|
37
|
+
* The line this holds is not "the framework never touches a raw message" but
|
|
38
|
+
* **"a raw message never crosses to the caller"**. Shared by the HTTP and tool
|
|
39
|
+
* paths so that line is one rule in one place. → ADR 0042.
|
|
40
|
+
*/
|
|
41
|
+
export declare function recordedErrorMessage(code: string, envelopeMessage: string | undefined, thrown: unknown): string | undefined;
|
|
27
42
|
export declare function normalizeError(err: unknown): AppError;
|
|
28
43
|
/**
|
|
29
44
|
* 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;AAQvC,wBAAgB,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,QAAQ,GAAG,MAAM,CAMxD;AAED,qFAAqF;AACrF,MAAM,WAAW,eAAe;IAC9B,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC,QAAQ,GAAG,eAAe,EAAE,CAM9D;AAKD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAI1D;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,QAAQ,CAgBrD;AA4BD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,OAAO,EACf,IAAI,EAAE,OAAO,EACb,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,GACrC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAe9D"}
|
|
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;AAQvC,wBAAgB,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,QAAQ,GAAG,MAAM,CAMxD;AAED,qFAAqF;AACrF,MAAM,WAAW,eAAe;IAC9B,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC,QAAQ,GAAG,eAAe,EAAE,CAM9D;AAKD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAI1D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,eAAe,EAAE,MAAM,GAAG,SAAS,EACnC,MAAM,EAAE,OAAO,GACd,MAAM,GAAG,SAAS,CAMpB;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,QAAQ,CAgBrD;AA4BD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,OAAO,EACf,IAAI,EAAE,OAAO,EACb,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,GACrC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAe9D"}
|
package/dist/node.js
CHANGED
|
@@ -3,8 +3,10 @@ import {
|
|
|
3
3
|
createImplement,
|
|
4
4
|
createSocketIOServer,
|
|
5
5
|
implement
|
|
6
|
-
} from "./index-
|
|
6
|
+
} from "./index-k7ysesv0.js";
|
|
7
7
|
import"./index-czmqks7r.js";
|
|
8
|
+
import"./index-x3fcszf8.js";
|
|
9
|
+
import"./index-wya8bkme.js";
|
|
8
10
|
import {
|
|
9
11
|
AppError,
|
|
10
12
|
appError,
|
|
@@ -14,9 +16,7 @@ import {
|
|
|
14
16
|
notFound,
|
|
15
17
|
rateLimited,
|
|
16
18
|
unauthorized
|
|
17
|
-
} from "./index-
|
|
18
|
-
import"./index-9g9d6r1h.js";
|
|
19
|
-
import"./index-c7nyw0yt.js";
|
|
19
|
+
} from "./index-enc7t99e.js";
|
|
20
20
|
// src/server/node.ts
|
|
21
21
|
import { serve } from "srvx";
|
|
22
22
|
async function serveNode(config) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/observability/audit.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/observability/audit.ts"],"names":[],"mappings":"AAQA,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;AA8BD,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,CA6G9D"}
|
|
@@ -13,11 +13,12 @@ import {
|
|
|
13
13
|
setRequestError,
|
|
14
14
|
setRequestUser,
|
|
15
15
|
wrapInRequestContext
|
|
16
|
-
} from "../index-
|
|
16
|
+
} from "../index-wya8bkme.js";
|
|
17
17
|
import {
|
|
18
18
|
isRecord,
|
|
19
|
-
isUnsafeKey
|
|
20
|
-
|
|
19
|
+
isUnsafeKey,
|
|
20
|
+
recordedErrorMessage
|
|
21
|
+
} from "../index-enc7t99e.js";
|
|
21
22
|
|
|
22
23
|
// src/observability/sanitize.ts
|
|
23
24
|
var DEFAULT_SENSITIVE_KEYS = /(password|passwd|pwd|secret|token|apikey|api[-_ ]?key|auth|authorization|bearer|session|cookie|init[-_ ]?data|credential|private[-_ ]?key)/i;
|
|
@@ -109,6 +110,9 @@ function toolErrorMessage(result) {
|
|
|
109
110
|
}
|
|
110
111
|
return hint;
|
|
111
112
|
}
|
|
113
|
+
function auditErrorMessage(result, thrown) {
|
|
114
|
+
return recordedErrorMessage(result.code, toolErrorMessage(result), thrown);
|
|
115
|
+
}
|
|
112
116
|
function readString(value) {
|
|
113
117
|
return typeof value === "string" ? value : undefined;
|
|
114
118
|
}
|
|
@@ -169,7 +173,7 @@ function createAuditHook(config) {
|
|
|
169
173
|
};
|
|
170
174
|
};
|
|
171
175
|
const toolCall = {
|
|
172
|
-
afterToolCall: (toolName, args, result, durationMs, context, endpoint) => {
|
|
176
|
+
afterToolCall: (toolName, args, result, durationMs, context, endpoint, thrown) => {
|
|
173
177
|
const requestCtx = getRequestContext();
|
|
174
178
|
const span = requestCtx ? childSpan(requestCtx.trace) : createTraceContext();
|
|
175
179
|
const measure = result.ok ? measureSize(result.data) : { resultSize: null, responseBytes: 0 };
|
|
@@ -189,7 +193,7 @@ function createAuditHook(config) {
|
|
|
189
193
|
statusCode: result.ok ? 200 : 400,
|
|
190
194
|
durationMs,
|
|
191
195
|
errorCode: result.ok ? undefined : result.code,
|
|
192
|
-
errorMessage: result.ok ? undefined :
|
|
196
|
+
errorMessage: result.ok ? undefined : auditErrorMessage(result, thrown),
|
|
193
197
|
...!result.ok && result.details !== undefined && {
|
|
194
198
|
errorDetail: sanitizePayload(result.details, sanitize)
|
|
195
199
|
},
|
|
@@ -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":"AA2CA,OAAO,KAAK,EAEV,eAAe,EACf,YAAY,EACZ,aAAa,EAGd,MAAM,SAAS,CAAC;AAEjB,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,YAAY,CAoZjE;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,uBAuBnD"}
|
package/dist/server/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
socketIoLane,
|
|
11
11
|
staticRoute,
|
|
12
12
|
webSocketLane
|
|
13
|
-
} from "../index-
|
|
13
|
+
} from "../index-k7ysesv0.js";
|
|
14
14
|
import {
|
|
15
15
|
createAuthHook,
|
|
16
16
|
createBearerResolver,
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
signJwt,
|
|
24
24
|
verifyJwt,
|
|
25
25
|
verifyPkce
|
|
26
|
-
} from "../index-
|
|
26
|
+
} from "../index-nmq59nae.js";
|
|
27
27
|
import {
|
|
28
28
|
DEFAULT_CORS_ALLOW_HEADERS,
|
|
29
29
|
DEFAULT_CORS_EXPOSE_HEADERS,
|
|
@@ -34,6 +34,17 @@ import {
|
|
|
34
34
|
jsonSchemaFields,
|
|
35
35
|
toJsonSchema
|
|
36
36
|
} from "../index-0ed3bx43.js";
|
|
37
|
+
import {
|
|
38
|
+
isWithinDir
|
|
39
|
+
} from "../index-x3fcszf8.js";
|
|
40
|
+
import {
|
|
41
|
+
extractIp,
|
|
42
|
+
generateTraceId,
|
|
43
|
+
getClientInfo,
|
|
44
|
+
getTraceId,
|
|
45
|
+
resolveSocketIp,
|
|
46
|
+
resolveTraceId
|
|
47
|
+
} from "../index-wya8bkme.js";
|
|
37
48
|
import {
|
|
38
49
|
AppError,
|
|
39
50
|
STITCH_ERROR_STATUS,
|
|
@@ -43,25 +54,14 @@ import {
|
|
|
43
54
|
errorCode,
|
|
44
55
|
forbidden,
|
|
45
56
|
formatZodError,
|
|
57
|
+
isRecord,
|
|
46
58
|
isStitchErrorCode,
|
|
47
|
-
isWithinDir,
|
|
48
59
|
normalizeError,
|
|
49
60
|
notFound,
|
|
50
61
|
rateLimited,
|
|
51
62
|
unauthorized,
|
|
52
63
|
zodIssues
|
|
53
|
-
} from "../index-
|
|
54
|
-
import {
|
|
55
|
-
extractIp,
|
|
56
|
-
generateTraceId,
|
|
57
|
-
getClientInfo,
|
|
58
|
-
getTraceId,
|
|
59
|
-
resolveSocketIp,
|
|
60
|
-
resolveTraceId
|
|
61
|
-
} from "../index-9g9d6r1h.js";
|
|
62
|
-
import {
|
|
63
|
-
isRecord
|
|
64
|
-
} from "../index-c7nyw0yt.js";
|
|
64
|
+
} from "../index-enc7t99e.js";
|
|
65
65
|
// src/server/swept-map.ts
|
|
66
66
|
function createSweptMap(options) {
|
|
67
67
|
const store = new Map;
|
package/dist/tools/execute.d.ts
CHANGED
|
@@ -15,7 +15,18 @@ export interface ToolCallContext {
|
|
|
15
15
|
}
|
|
16
16
|
export interface ToolCallHooks {
|
|
17
17
|
beforeToolCall?: (toolName: string, args: Record<string, unknown>, context: ToolCallContext, endpoint: MethodDef) => void | Promise<void>;
|
|
18
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Every finished call, success and failure alike — the record of the call.
|
|
20
|
+
*
|
|
21
|
+
* `error` is the value **as thrown**, present only when the call failed by
|
|
22
|
+
* throwing (never for an argument-validation failure, an output-schema
|
|
23
|
+
* mismatch or a `beforeToolCall` rejection — those never had a raw value).
|
|
24
|
+
* It is the same value `onToolError` receives, handed here too so a single
|
|
25
|
+
* hook can build one row that names the cause: the `result` alone cannot,
|
|
26
|
+
* because an unexpected throw is scrubbed to a bare `INTERNAL_SERVER_ERROR`.
|
|
27
|
+
* A six-parameter hook is unaffected.
|
|
28
|
+
*/
|
|
29
|
+
afterToolCall?: (toolName: string, args: Record<string, unknown>, result: ToolResult, durationMs: number, context: ToolCallContext, endpoint: MethodDef, error?: unknown) => void | Promise<void>;
|
|
19
30
|
/**
|
|
20
31
|
* The handler path threw — the value **as thrown**, before it is normalised
|
|
21
32
|
* into a `ToolResult`. The tool-side answer to HTTP's `hooks.onError`, and the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../src/tools/execute.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,MAAM,UAAU,GAClB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAC3B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAElE,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,eAAe,CAAC;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,cAAc,CAAC,EAAE,CACf,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,SAAS,KAChB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,aAAa,CAAC,EAAE,CACd,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,EAAE,UAAU,EAClB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../src/tools/execute.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,MAAM,UAAU,GAClB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAC3B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAElE,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,eAAe,CAAC;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,cAAc,CAAC,EAAE,CACf,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,SAAS,KAChB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B;;;;;;;;;;OAUG;IACH,aAAa,CAAC,EAAE,CACd,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,EAAE,UAAU,EAClB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,SAAS,EACnB,KAAK,CAAC,EAAE,OAAO,KACZ,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,WAAW,CAAC,EAAE,CACZ,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,SAAS,KAChB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;AAEjF;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,aAAa;IAC5B,oDAAoD;IACpD,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,EAAE,QAAQ,EAAE,SAAS,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClF,0DAA0D;IAC1D,WAAW,CAAC,EAAE,CACZ,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,SAAS,KAChB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACjC;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU,EAAE;IAAE,EAAE,EAAE,KAAK,CAAA;CAAE,CAAC,CAQpF;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAC5C,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,OAAO,EAAE,eAAe,EACxB,KAAK,CAAC,EAAE,aAAa,EACrB,SAAS,CAAC,EAAE,aAAa,EACzB,UAAU,UAAQ,EAClB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,GACxC,OAAO,CAAC,UAAU,CAAC,CAwIrB"}
|
package/dist/tools.js
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
inputIsQuery,
|
|
3
3
|
signJwt,
|
|
4
4
|
verifyPkce
|
|
5
|
-
} from "./index-
|
|
5
|
+
} from "./index-nmq59nae.js";
|
|
6
6
|
import {
|
|
7
7
|
DEFAULT_CORS_ALLOW_HEADERS
|
|
8
8
|
} from "./index-czmqks7r.js";
|
|
@@ -21,22 +21,22 @@ import {
|
|
|
21
21
|
readCapped,
|
|
22
22
|
toolResultFromError,
|
|
23
23
|
writeDownload
|
|
24
|
-
} from "./index-
|
|
24
|
+
} from "./index-jr8vf7g8.js";
|
|
25
25
|
import {
|
|
26
26
|
toJsonSchema
|
|
27
27
|
} from "./index-0ed3bx43.js";
|
|
28
28
|
import {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
mergeMeta
|
|
32
|
-
} from "./index-zza375qp.js";
|
|
29
|
+
isWithinDir
|
|
30
|
+
} from "./index-x3fcszf8.js";
|
|
33
31
|
import {
|
|
34
32
|
getTraceId
|
|
35
|
-
} from "./index-
|
|
33
|
+
} from "./index-wya8bkme.js";
|
|
36
34
|
import {
|
|
35
|
+
AppError,
|
|
37
36
|
isRecord,
|
|
37
|
+
mergeMeta,
|
|
38
38
|
typedEntries
|
|
39
|
-
} from "./index-
|
|
39
|
+
} from "./index-enc7t99e.js";
|
|
40
40
|
|
|
41
41
|
// src/tools/agent.ts
|
|
42
42
|
import { tool, zodSchema } from "ai";
|
package/llms-full.txt
CHANGED
|
@@ -2879,10 +2879,22 @@ import {
|
|
|
2879
2879
|
} from 'stitchkit/observability'
|
|
2880
2880
|
|
|
2881
2881
|
createAuthHook({ /* … */ inject: (ctx, user) => user && setRequestUser(user.id) })
|
|
2882
|
-
//
|
|
2882
|
+
// only to override what the framework already recorded — see below:
|
|
2883
2883
|
setRequestError({ code: err.code, message: err.message, details: err.issues })
|
|
2884
2884
|
```
|
|
2885
2885
|
|
|
2886
|
+
**The failure is recorded for you.** Every error travels one path inside the
|
|
2887
|
+
framework, and that path writes `{ code, message, details }` onto the context —
|
|
2888
|
+
so an audited failure names its cause whether or not you wrote an `onError`, and
|
|
2889
|
+
whether or not your `onError` returns its own `Response`. Where the envelope was
|
|
2890
|
+
scrubbed to `INTERNAL_SERVER_ERROR`, the row gets the **real** message rather than
|
|
2891
|
+
the placeholder; the caller still receives the scrubbed one. → ADR 0043
|
|
2892
|
+
|
|
2893
|
+
`setRequestError` is therefore an **override**, not the wiring: call it when you
|
|
2894
|
+
want the row to say something other than what the framework derived (a domain
|
|
2895
|
+
code, a curated message, structured issues). The framework writes only when the
|
|
2896
|
+
context carries nothing yet, so your value always wins.
|
|
2897
|
+
|
|
2886
2898
|
**Endpoint identity is automatic.** The framework writes the matched operation's
|
|
2887
2899
|
`(serviceName, action)` into the context at route-match, *before* validation — so
|
|
2888
2900
|
`event.serviceName` / `event.action` are present on every event, including a
|
|
@@ -3024,9 +3036,10 @@ metric, a custom log line, anything that is not a full audit row.
|
|
|
3024
3036
|
|
|
3025
3037
|
`afterHandle(ctx, result, endpoint)` runs after a handler returns;
|
|
3026
3038
|
`onError(ctx, error, endpoint)` when one throws. `afterToolCall(toolName, args,
|
|
3027
|
-
result, durationMs, context)` runs after every tool call —
|
|
3028
|
-
alike — carrying the tool name, the arguments, the result, the
|
|
3029
|
-
call context
|
|
3039
|
+
result, durationMs, context, endpoint, error)` runs after every tool call —
|
|
3040
|
+
success and error alike — carrying the tool name, the arguments, the result, the
|
|
3041
|
+
duration, the call context, the endpoint identity, and (only when the call failed
|
|
3042
|
+
by throwing) the raw thrown value.
|
|
3030
3043
|
|
|
3031
3044
|
```ts
|
|
3032
3045
|
createMcpHandler({
|
|
@@ -3086,35 +3099,43 @@ observe. (This is also why it lives on `ToolCallHooks` rather than being an
|
|
|
3086
3099
|
`Response`, which a tool call has no use for, and a whole `createServer` hooks
|
|
3087
3100
|
object must stay assignable to `ToolLifecycle`.)
|
|
3088
3101
|
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3102
|
+
**Do not reach for `setRequestError` here.** It writes to the *request* context,
|
|
3103
|
+
which `createAuditHook`'s **tool** row does not read: a tool event takes
|
|
3104
|
+
`errorCode` / `errorMessage` / `errorDetail` from the `ToolResult`, and only
|
|
3105
|
+
identity and `dimensions` from the context. Calling it in `onToolError` would
|
|
3106
|
+
leave the tool row exactly as scrubbed as before — and for MCP over HTTP it would
|
|
3107
|
+
also write the cause into the log line of the enclosing `/mcp` request, turning
|
|
3108
|
+
one incident into two records. It is right for the **HTTP** path, where the
|
|
3109
|
+
request *is* the record.
|
|
3097
3110
|
|
|
3098
|
-
|
|
3099
|
-
row stay the record of the *call*. If you want one row carrying both, correlate
|
|
3100
|
-
the two hooks yourself — key the cause by the call and read it back in
|
|
3101
|
-
`afterToolCall`:
|
|
3111
|
+
### One row that names the cause
|
|
3102
3112
|
|
|
3103
|
-
|
|
3104
|
-
|
|
3113
|
+
You do not need to correlate the two hooks yourself. `afterToolCall` receives the
|
|
3114
|
+
same raw value as a **seventh parameter**, so one hook can build one record:
|
|
3105
3115
|
|
|
3116
|
+
```ts
|
|
3106
3117
|
hooks: {
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
void writeRow({ toolName, result, durationMs, cause: causes.get(context), endpoint })
|
|
3118
|
+
afterToolCall: (toolName, args, result, durationMs, context, endpoint, error) => {
|
|
3119
|
+
void writeRow({ toolName, result, durationMs, cause: error, endpoint })
|
|
3110
3120
|
},
|
|
3111
3121
|
}
|
|
3112
3122
|
```
|
|
3113
3123
|
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3124
|
+
`error` is present only when the call failed by **throwing** — a
|
|
3125
|
+
validation failure or a `beforeToolCall` rejection leaves it `undefined`, because
|
|
3126
|
+
neither ever had a raw value to lose. The parameter is additive: a six-parameter
|
|
3127
|
+
hook written before it keeps compiling and keeps firing.
|
|
3128
|
+
|
|
3129
|
+
`createAuditHook` uses it already. Where the envelope was scrubbed to
|
|
3130
|
+
`INTERNAL_SERVER_ERROR`, the row's `errorMessage` becomes the real message
|
|
3131
|
+
instead of the placeholder; a truthful envelope (a thrown `AppError`, a
|
|
3132
|
+
`ZodError`) is left alone, `errorCode` and `errorDetail` are untouched, and the
|
|
3133
|
+
stack is not written — that is `onToolError`'s job, for a tracker that wants it.
|
|
3134
|
+
The caller still receives the scrubbed envelope in every case: the raw text
|
|
3135
|
+
reaches your server-side record, never the response. → ADR 0042
|
|
3136
|
+
|
|
3137
|
+
So the two hooks divide by **purpose**, not by capability — `onToolError` for a
|
|
3138
|
+
sink of your own (a tracker, a stack, an alert), `afterToolCall` for the record.
|
|
3118
3139
|
|
|
3119
3140
|
### Keying a row on (service, action)
|
|
3120
3141
|
|
|
@@ -3887,7 +3908,7 @@ Server-only. Turns contracts into MCP and AI-agent tools. Needs the
|
|
|
3887
3908
|
| `Toolkit` | _type_ | the context-pinned tool surface from `createToolkit` |
|
|
3888
3909
|
| `ToolExtend` | _type_ | extra-args extension for `mountMcp` / `mountAgent` |
|
|
3889
3910
|
| `ToolLifecycle` | _type_ | `beforeHandle` / `afterHandle` gate for tool calls — [guide](../guide/mcp-and-agents.md#guarding-tools--lifecycle) |
|
|
3890
|
-
| `ToolCallHooks` | _type_ | `beforeToolCall` / `afterToolCall` / `onToolError` observability hooks — the
|
|
3911
|
+
| `ToolCallHooks` | _type_ | `beforeToolCall` / `afterToolCall` / `onToolError` observability hooks — the raw thrown value reaches both of the last two (`afterToolCall`'s 7th parameter), because normalisation scrubs an unexpected error to `INTERNAL_SERVER_ERROR` ([guide](../guide/observability.md#the-cause-behind-a-failed-tool-call)) |
|
|
3891
3912
|
| `ErrorHintFn` | _type_ | `(toolName, errorCode) => string \| null` — a per-tool recovery hint, shared by every mount |
|
|
3892
3913
|
| `ToolResult` | _type_ | the result of one tool call |
|
|
3893
3914
|
| `ToolCallContext` | _type_ | the context every tool hook receives — `{ source }` plus whatever the mount's `context` added |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stitchkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.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",
|
|
@@ -90,7 +90,8 @@
|
|
|
90
90
|
"dev": "bun run build:js -- --watch",
|
|
91
91
|
"prepublishOnly": "cp ../../README.md ./README.md && bun ../../scripts/gen-llms.ts && bun run build",
|
|
92
92
|
"test": "bun test",
|
|
93
|
-
"smoke:node": "node scripts/node-smoke.mjs"
|
|
93
|
+
"smoke:node": "node scripts/node-smoke.mjs",
|
|
94
|
+
"consumer-lane": "bun scripts/consumer-lane/run.mjs"
|
|
94
95
|
},
|
|
95
96
|
"peerDependencies": {
|
|
96
97
|
"@modelcontextprotocol/ext-apps": "^1.7.2",
|
package/dist/index-c7nyw0yt.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
3
|
-
|
|
4
|
-
// src/internal/typed.ts
|
|
5
|
-
function typedEntries(value) {
|
|
6
|
-
return Object.entries(value);
|
|
7
|
-
}
|
|
8
|
-
function isRecord(value) {
|
|
9
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
// src/internal/safe-json.ts
|
|
13
|
-
function isUnsafeKey(key) {
|
|
14
|
-
return key === "__proto__";
|
|
15
|
-
}
|
|
16
|
-
function safeJsonParse(text) {
|
|
17
|
-
return JSON.parse(text, (key, value) => isUnsafeKey(key) ? undefined : value);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export { __require, typedEntries, isRecord, isUnsafeKey, safeJsonParse };
|