stitchkit 0.36.0 → 0.37.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 +4 -2
- package/dist/browser/http.d.ts +2 -2
- package/dist/browser/http.d.ts.map +1 -1
- package/dist/cli.js +3 -3
- package/dist/contract/define.d.ts +28 -2
- package/dist/contract/define.d.ts.map +1 -1
- package/dist/contract/errors.d.ts.map +1 -1
- package/dist/contract/index.js +1 -1
- package/dist/{index-dvrn81q4.js → index-4pvtq6h2.js} +83 -34
- package/dist/{index-0ed3bx43.js → index-frfyw9fa.js} +2 -2
- package/dist/{index-pmftwk2a.js → index-g3jrbd0z.js} +26 -0
- package/dist/{index-kgga1n8d.js → index-s6yhmg1k.js} +563 -342
- package/dist/{index-bx49hskg.js → index-xq45akyd.js} +1 -1
- package/dist/index.js +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +7 -3
- package/dist/observability/index.js +3 -3
- package/dist/server/bun.d.ts +32 -0
- package/dist/server/bun.d.ts.map +1 -0
- package/dist/server/context.d.ts +1 -1
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/create.d.ts +2 -3
- package/dist/server/create.d.ts.map +1 -1
- package/dist/server/implement.d.ts.map +1 -1
- package/dist/server/index.d.ts +3 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +25 -5
- package/dist/server/logger.d.ts +5 -5
- package/dist/server/logger.d.ts.map +1 -1
- package/dist/server/logging.d.ts +4 -1
- package/dist/server/logging.d.ts.map +1 -1
- package/dist/server/middleware/auth.d.ts +2 -2
- package/dist/server/middleware/auth.d.ts.map +1 -1
- package/dist/server/request-body.d.ts +4 -0
- package/dist/server/request-body.d.ts.map +1 -0
- package/dist/server/router.d.ts +3 -3
- package/dist/server/router.d.ts.map +1 -1
- package/dist/server/socket-io-config.d.ts +20 -0
- package/dist/server/socket-io-config.d.ts.map +1 -0
- package/dist/server/socket-io-node.d.ts +14 -0
- package/dist/server/socket-io-node.d.ts.map +1 -0
- package/dist/server/socket-io.d.ts +5 -32
- package/dist/server/socket-io.d.ts.map +1 -1
- package/dist/server/types.d.ts +54 -49
- package/dist/server/types.d.ts.map +1 -1
- package/dist/tools/agent.d.ts.map +1 -1
- package/dist/tools/execute.d.ts +60 -15
- package/dist/tools/execute.d.ts.map +1 -1
- package/dist/tools/flatten-join.d.ts +8 -0
- package/dist/tools/flatten-join.d.ts.map +1 -0
- package/dist/tools/flatten.d.ts +7 -44
- package/dist/tools/flatten.d.ts.map +1 -1
- package/dist/tools/json-schema.d.ts +1 -1
- package/dist/tools/json-schema.d.ts.map +1 -1
- package/dist/tools/manifest.d.ts.map +1 -1
- package/dist/tools/mcp-handler.d.ts +7 -13
- package/dist/tools/mcp-handler.d.ts.map +1 -1
- package/dist/tools/mcp.d.ts +61 -24
- package/dist/tools/mcp.d.ts.map +1 -1
- package/dist/tools/mount.d.ts +9 -6
- package/dist/tools/mount.d.ts.map +1 -1
- package/dist/tools/names.d.ts +1 -1
- package/dist/tools/native-mcp.d.ts +57 -0
- package/dist/tools/native-mcp.d.ts.map +1 -0
- package/dist/tools/portable-formats.d.ts +15 -0
- package/dist/tools/portable-formats.d.ts.map +1 -0
- package/dist/tools/presentation.d.ts +15 -0
- package/dist/tools/presentation.d.ts.map +1 -0
- package/dist/tools/remote.d.ts.map +1 -1
- package/dist/tools/schema.d.ts +3 -8
- package/dist/tools/schema.d.ts.map +1 -1
- package/dist/tools/tool-logger.d.ts.map +1 -1
- package/dist/tools/view-file.d.ts +5 -3
- package/dist/tools/view-file.d.ts.map +1 -1
- package/dist/tools.d.ts +7 -4
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +357 -95
- package/llms-full.txt +374 -77
- package/package.json +14 -11
- /package/dist/{index-17rdjw68.js → index-e497hxcy.js} +0 -0
package/README.md
CHANGED
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
- **One contract, five surfaces.** Define your API once — get HTTP routes, MCP tools (for Claude/Cursor), AI SDK tools (for agents), a [CLI](./docs/guide/cli.md) (for scripts & Skills), and a typed client.
|
|
27
27
|
- **Zero HTTP framework deps.** Built on `Bun.serve()` (Bun) or `srvx` (Node). No Hono, no Elysia, no Express.
|
|
28
28
|
- **Fullstack type safety.** Server handlers, client calls, MCP tools — all typed from the same contract.
|
|
29
|
-
- **
|
|
29
|
+
- **Inspectable.** A focused core with explicit adapters. No generated
|
|
30
|
+
application code or framework build step in your app.
|
|
30
31
|
- **Thin over what you already use.** WebSocket = Socket.IO (`createSocketIOClient` / `createSocketIOServer`). React data layer = `react-query-kit` (`createCursorQuery`). stitchkit owns the contract and the transport — not its own competing WebSocket or hook engine.
|
|
31
32
|
|
|
32
33
|
### The problem it solves
|
|
@@ -344,7 +345,8 @@ app code share a single instance. Bundled copies would double `zod`, split the
|
|
|
344
345
|
`react` hook runtime and break `instanceof`. Optional peers mean an app that
|
|
345
346
|
never touches MCP never installs the MCP SDK. → [ADR 0011](./docs/decisions/0011-bun-only-one-package.md)
|
|
346
347
|
|
|
347
|
-
The framework
|
|
348
|
+
The framework stays focused and inspectable: explicit adapters, no generated
|
|
349
|
+
application code and no framework build step in your app.
|
|
348
350
|
|
|
349
351
|
## Example
|
|
350
352
|
|
package/dist/browser/http.d.ts
CHANGED
|
@@ -10,9 +10,9 @@ export type ApiEventListener = (event: ApiEvent) => void;
|
|
|
10
10
|
export declare class ApiError extends Error {
|
|
11
11
|
readonly code: string;
|
|
12
12
|
readonly status: number;
|
|
13
|
-
readonly details?: unknown
|
|
13
|
+
readonly details?: unknown;
|
|
14
14
|
readonly hint?: string | undefined;
|
|
15
|
-
constructor(code: string, status?: number, details?: unknown
|
|
15
|
+
constructor(code: string, status?: number, details?: unknown, message?: string, hint?: string | undefined);
|
|
16
16
|
static is(error: unknown): error is ApiError;
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/browser/http.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD,MAAM,MAAM,QAAQ,GAChB;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,GACzB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEvB,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;AAEzD,qBAAa,QAAS,SAAQ,KAAK;aAEf,IAAI,EAAE,MAAM;aACZ,MAAM,EAAE,MAAM;aACd,OAAO,CAAC,EAAE,OAAO;aAEjB,IAAI,CAAC,EAAE,MAAM;
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/browser/http.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
emitResult,
|
|
5
5
|
parseCliArgs,
|
|
6
6
|
pollUntilDone
|
|
7
|
-
} from "./index-
|
|
8
|
-
import"./index-
|
|
7
|
+
} from "./index-s6yhmg1k.js";
|
|
8
|
+
import"./index-frfyw9fa.js";
|
|
9
9
|
import"./index-x3fcszf8.js";
|
|
10
|
-
import"./index-
|
|
10
|
+
import"./index-e497hxcy.js";
|
|
11
11
|
export {
|
|
12
12
|
pollUntilDone,
|
|
13
13
|
parseCliArgs,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ZodType, z } from 'zod';
|
|
2
2
|
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
3
|
-
export declare const ALL_TRANSPORTS: readonly [
|
|
3
|
+
export declare const ALL_TRANSPORTS: readonly ['HTTP', 'MCP', 'AGENT', 'CLI'];
|
|
4
4
|
export type Transport = (typeof ALL_TRANSPORTS)[number];
|
|
5
5
|
/**
|
|
6
6
|
* The transport tag on `ctx.source`. The four built-ins keep autocomplete, but
|
|
@@ -26,6 +26,12 @@ interface EndpointDefBase {
|
|
|
26
26
|
* own caps. Ignored on non-multipart endpoints.
|
|
27
27
|
*/
|
|
28
28
|
maxUploadBytes?: number;
|
|
29
|
+
/**
|
|
30
|
+
* Per-route ceiling in bytes for a JSON request body. Overrides the server's
|
|
31
|
+
* `maxJsonBodyBytes`; without either, JSON body size is unchanged/unbounded.
|
|
32
|
+
* Enforced while streaming, before the complete body is buffered.
|
|
33
|
+
*/
|
|
34
|
+
maxJsonBodyBytes?: number;
|
|
29
35
|
/**
|
|
30
36
|
* HTTP client timeout in ms for this endpoint. Use it for slow synchronous
|
|
31
37
|
* endpoints (AI generation) that need more than the client default. A
|
|
@@ -94,6 +100,7 @@ interface HttpOnlyEndpointDef extends EndpointDefBase {
|
|
|
94
100
|
expose: readonly ['HTTP'];
|
|
95
101
|
toolName?: never;
|
|
96
102
|
rawResponse?: never;
|
|
103
|
+
rawBody?: never;
|
|
97
104
|
}
|
|
98
105
|
interface ToolEndpointDef extends EndpointDefBase {
|
|
99
106
|
toolName?: string;
|
|
@@ -103,6 +110,19 @@ interface ToolEndpointDef extends EndpointDefBase {
|
|
|
103
110
|
/** MCP behavioural hints (read-only / destructive / title) for hosts. */
|
|
104
111
|
annotations?: EndpointToolAnnotations;
|
|
105
112
|
rawResponse?: never;
|
|
113
|
+
rawBody?: never;
|
|
114
|
+
}
|
|
115
|
+
/** A validated JSON endpoint that also retains the original decoded body text. */
|
|
116
|
+
interface RawBodyEndpointDef extends EndpointDefBase {
|
|
117
|
+
method: 'POST' | 'PUT' | 'PATCH';
|
|
118
|
+
input: ZodType<unknown>;
|
|
119
|
+
rawBody: true;
|
|
120
|
+
multipart?: never;
|
|
121
|
+
rawResponse?: never;
|
|
122
|
+
toolName?: never;
|
|
123
|
+
ui?: never;
|
|
124
|
+
annotations?: never;
|
|
125
|
+
expose?: readonly ['HTTP'];
|
|
106
126
|
}
|
|
107
127
|
/**
|
|
108
128
|
* An endpoint whose handler returns the **`Response` itself** instead of data —
|
|
@@ -132,6 +152,8 @@ interface RawResponseEndpointDef extends EndpointDefBase {
|
|
|
132
152
|
* "normal".
|
|
133
153
|
*/
|
|
134
154
|
rawResponse: true;
|
|
155
|
+
/** Retain the validated JSON request's original decoded body text. */
|
|
156
|
+
rawBody?: true;
|
|
135
157
|
/**
|
|
136
158
|
* The `Content-Type` this endpoint answers with, for documentation only —
|
|
137
159
|
* the handler still sets the real header (`serveFile` detects it from the
|
|
@@ -150,7 +172,7 @@ interface RawResponseEndpointDef extends EndpointDefBase {
|
|
|
150
172
|
/** Redundant but allowed, so `expose: ['HTTP']` survives a migration. */
|
|
151
173
|
expose?: readonly ['HTTP'];
|
|
152
174
|
}
|
|
153
|
-
export type EndpointDef = HttpOnlyEndpointDef | ToolEndpointDef | RawResponseEndpointDef;
|
|
175
|
+
export type EndpointDef = HttpOnlyEndpointDef | ToolEndpointDef | RawBodyEndpointDef | RawResponseEndpointDef;
|
|
154
176
|
export interface ContractMeta<TScope extends string = string> {
|
|
155
177
|
prefix: string;
|
|
156
178
|
scope?: TScope;
|
|
@@ -198,6 +220,8 @@ export interface RuntimeContext {
|
|
|
198
220
|
params: unknown;
|
|
199
221
|
input: unknown;
|
|
200
222
|
file?: File;
|
|
223
|
+
/** Original decoded JSON request text when the endpoint declares `rawBody: true`. */
|
|
224
|
+
rawBody?: string;
|
|
201
225
|
source: TransportSource;
|
|
202
226
|
/**
|
|
203
227
|
* The raw Web `Request`, its parsed `URL` and `Headers` — set on the HTTP
|
|
@@ -220,6 +244,8 @@ export interface HandlerContext<TParams = undefined, TInput = undefined> {
|
|
|
220
244
|
params: TParams;
|
|
221
245
|
input: TInput;
|
|
222
246
|
file?: File;
|
|
247
|
+
/** Original decoded JSON request text when the endpoint declares `rawBody: true`. */
|
|
248
|
+
rawBody?: string;
|
|
223
249
|
source: TransportSource;
|
|
224
250
|
/** Raw Web `Request` / `URL` / `Headers` — set on the HTTP transport, absent
|
|
225
251
|
* on the tool transports (see {@link RuntimeContext}). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../../src/contract/define.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAEtC,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;AAErE,eAAO,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../../src/contract/define.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/contract/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE;QACL,2CAA2C;QAC3C,IAAI,EAAE,MAAM,CAAC;QACb,8BAA8B;QAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,gCAAgC;QAChC,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,0BAA0B;QAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAcD;;;;GAIG;AACH,qBAAa,QAAS,SAAQ,KAAK;aAEf,IAAI,EAAE,MAAM;aAEZ,MAAM,EAAE,MAAM;aACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;aACjC,IAAI,CAAC,EAAE,MAAM;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/contract/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE;QACL,2CAA2C;QAC3C,IAAI,EAAE,MAAM,CAAC;QACb,8BAA8B;QAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,gCAAgC;QAChC,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,0BAA0B;QAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAcD;;;;GAIG;AACH,qBAAa,QAAS,SAAQ,KAAK;aAEf,IAAI,EAAE,MAAM;aAEZ,MAAM,EAAE,MAAM;aACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;aACjC,IAAI,CAAC,EAAE,MAAM;IAL/B,YACkB,IAAI,EAAE,MAAM,EAC5B,OAAO,CAAC,EAAE,MAAM,EACA,MAAM,GAAE,MAAY,EACpB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,YAAA,EACjC,IAAI,CAAC,EAAE,MAAM,YAAA,EAQ9B;IAED,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,QAAQ,CAEvC;IAED,MAAM,IAAI,aAAa,CAStB;CACF;AAED,qCAAqC;AACrC,wBAAgB,QAAQ,CAAC,OAAO,SAAc,GAAG,KAAK,CAErD;AAED,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,KAAK,CAEpF;AAED,wCAAwC;AACxC,wBAAgB,YAAY,CAAC,OAAO,SAAiB,GAAG,KAAK,CAE5D;AAED,qCAAqC;AACrC,wBAAgB,SAAS,CAAC,OAAO,SAAc,GAAG,KAAK,CAEtD;AAED,0EAA0E;AAC1E,wBAAgB,QAAQ,CAAC,OAAO,SAAa,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,KAAK,CAEvF;AAED,wCAAwC;AACxC,wBAAgB,WAAW,CAAC,OAAO,SAAsB,GAAG,KAAK,CAEhE;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;CAUE,CAAC;AAEnC,mFAAmF;AACnF,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,mBAAmB,CAAC;AAE/D,iEAAiE;AACjE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,eAAe,CAEvE;AAED,+FAA+F;AAC/F,wBAAgB,QAAQ,CACtB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,KAAK,CAOP"}
|
package/dist/contract/index.js
CHANGED
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
setRequestError,
|
|
28
28
|
typedEntries,
|
|
29
29
|
validateHandlerOutput
|
|
30
|
-
} from "./index-
|
|
30
|
+
} from "./index-e497hxcy.js";
|
|
31
31
|
|
|
32
32
|
// src/server/multipart.ts
|
|
33
33
|
var DEFAULT_MAX_UPLOAD_BYTES = 25 * 1024 * 1024;
|
|
@@ -299,6 +299,45 @@ function staticRoute(prefix, dir) {
|
|
|
299
299
|
};
|
|
300
300
|
}
|
|
301
301
|
|
|
302
|
+
// src/server/request-body.ts
|
|
303
|
+
function assertJsonBodyLimit(maxBytes, owner) {
|
|
304
|
+
if (maxBytes !== undefined && (!Number.isSafeInteger(maxBytes) || maxBytes <= 0)) {
|
|
305
|
+
throw new Error(`${owner} must be a positive safe integer, received ${maxBytes}`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
async function readRequestText(req, maxBytes) {
|
|
309
|
+
if (maxBytes === undefined)
|
|
310
|
+
return req.text();
|
|
311
|
+
assertJsonBodyLimit(maxBytes, "maxJsonBodyBytes");
|
|
312
|
+
const reader = req.body?.getReader();
|
|
313
|
+
if (!reader)
|
|
314
|
+
return "";
|
|
315
|
+
const chunks = [];
|
|
316
|
+
let total = 0;
|
|
317
|
+
try {
|
|
318
|
+
while (true) {
|
|
319
|
+
const { done, value } = await reader.read();
|
|
320
|
+
if (done)
|
|
321
|
+
break;
|
|
322
|
+
total += value.byteLength;
|
|
323
|
+
if (total > maxBytes) {
|
|
324
|
+
await reader.cancel();
|
|
325
|
+
badRequest(`JSON body exceeds the ${maxBytes}-byte limit`);
|
|
326
|
+
}
|
|
327
|
+
chunks.push(value);
|
|
328
|
+
}
|
|
329
|
+
} finally {
|
|
330
|
+
reader.releaseLock();
|
|
331
|
+
}
|
|
332
|
+
const bytes = new Uint8Array(total);
|
|
333
|
+
let offset = 0;
|
|
334
|
+
for (const chunk of chunks) {
|
|
335
|
+
bytes.set(chunk, offset);
|
|
336
|
+
offset += chunk.byteLength;
|
|
337
|
+
}
|
|
338
|
+
return new TextDecoder().decode(bytes);
|
|
339
|
+
}
|
|
340
|
+
|
|
302
341
|
// src/server/context.ts
|
|
303
342
|
var RESERVED_KEYS = new Set([
|
|
304
343
|
"params",
|
|
@@ -307,13 +346,13 @@ var RESERVED_KEYS = new Set([
|
|
|
307
346
|
"req",
|
|
308
347
|
"url",
|
|
309
348
|
"headers",
|
|
349
|
+
"rawBody",
|
|
310
350
|
"traceId",
|
|
311
351
|
"spanId",
|
|
312
352
|
"ipAddress",
|
|
313
353
|
"userAgent"
|
|
314
354
|
]);
|
|
315
|
-
|
|
316
|
-
const text = await req.text();
|
|
355
|
+
function parseJsonBody(req, text) {
|
|
317
356
|
if (text.trim() === "")
|
|
318
357
|
return {};
|
|
319
358
|
if (!req.headers.get("content-type")?.includes("application/json")) {
|
|
@@ -343,7 +382,7 @@ function buildBaseContext(req, url, pathParams, traceId, clientIp) {
|
|
|
343
382
|
...getClientInfo(req, clientIp)
|
|
344
383
|
};
|
|
345
384
|
}
|
|
346
|
-
async function parseRequestInto(ctx, req, url, method, maxUploadBytes) {
|
|
385
|
+
async function parseRequestInto(ctx, req, url, method, maxUploadBytes, maxJsonBodyBytes) {
|
|
347
386
|
if (method.paramsSchema) {
|
|
348
387
|
ctx.params = method.paramsSchema.parse(ctx.params);
|
|
349
388
|
}
|
|
@@ -358,9 +397,19 @@ async function parseRequestInto(ctx, req, url, method, maxUploadBytes) {
|
|
|
358
397
|
ctx.input = method.inputSchema.parse(parseQueryParams(url));
|
|
359
398
|
} else if (req.method === "DELETE") {
|
|
360
399
|
const ct = req.headers.get("content-type");
|
|
361
|
-
|
|
400
|
+
if (ct?.includes("application/json")) {
|
|
401
|
+
const text = await readRequestText(req, method.maxJsonBodyBytes ?? maxJsonBodyBytes);
|
|
402
|
+
if (method.rawBody)
|
|
403
|
+
ctx.rawBody = text;
|
|
404
|
+
ctx.input = method.inputSchema.parse(parseJsonBody(req, text));
|
|
405
|
+
} else {
|
|
406
|
+
ctx.input = method.inputSchema.parse(parseQueryParams(url));
|
|
407
|
+
}
|
|
362
408
|
} else {
|
|
363
|
-
|
|
409
|
+
const text = await readRequestText(req, method.maxJsonBodyBytes ?? maxJsonBodyBytes);
|
|
410
|
+
if (method.rawBody)
|
|
411
|
+
ctx.rawBody = text;
|
|
412
|
+
ctx.input = method.inputSchema.parse(parseJsonBody(req, text));
|
|
364
413
|
}
|
|
365
414
|
}
|
|
366
415
|
}
|
|
@@ -433,7 +482,10 @@ function levelForStatus(status) {
|
|
|
433
482
|
return "info";
|
|
434
483
|
}
|
|
435
484
|
function buildLogFields(method, path, status, durationMs, traceId, errorCode2) {
|
|
436
|
-
|
|
485
|
+
const fields = { traceId, method, path, status, durationMs };
|
|
486
|
+
if (errorCode2 === undefined && status >= 400)
|
|
487
|
+
return fields;
|
|
488
|
+
return { ...fields, errorCode: errorCode2 };
|
|
437
489
|
}
|
|
438
490
|
function formatMs(ms) {
|
|
439
491
|
if (ms >= 1000)
|
|
@@ -532,6 +584,7 @@ function shouldSkipLog(config, req, url) {
|
|
|
532
584
|
}
|
|
533
585
|
function collectExtraLogFields(config, req, url, outcome) {
|
|
534
586
|
const fields = {};
|
|
587
|
+
const enrichKeys = [];
|
|
535
588
|
const ctx = getRequestContext();
|
|
536
589
|
if (ctx) {
|
|
537
590
|
if (ctx.userId)
|
|
@@ -546,11 +599,13 @@ function collectExtraLogFields(config, req, url, outcome) {
|
|
|
546
599
|
if (config.enrich) {
|
|
547
600
|
try {
|
|
548
601
|
const extra = config.enrich(req, url, outcome);
|
|
549
|
-
if (extra)
|
|
602
|
+
if (extra) {
|
|
603
|
+
enrichKeys.push(...Object.keys(extra));
|
|
550
604
|
Object.assign(fields, extra);
|
|
605
|
+
}
|
|
551
606
|
} catch {}
|
|
552
607
|
}
|
|
553
|
-
return fields;
|
|
608
|
+
return { fields, enrichKeys };
|
|
554
609
|
}
|
|
555
610
|
|
|
556
611
|
// src/server/create.ts
|
|
@@ -558,6 +613,7 @@ function createHandler(config) {
|
|
|
558
613
|
const { cors, hooks, logging = false, trustProxy = false } = config;
|
|
559
614
|
if (cors)
|
|
560
615
|
assertCorsConfig(cors);
|
|
616
|
+
assertJsonBodyLimit(config.maxJsonBodyBytes, "HandlerConfig.maxJsonBodyBytes");
|
|
561
617
|
const logConfig = resolveLoggingConfig(logging);
|
|
562
618
|
const customLogger = logConfig?.logger ?? null;
|
|
563
619
|
const useDefaultLog = logConfig !== null && !logConfig.logger;
|
|
@@ -571,6 +627,7 @@ function createHandler(config) {
|
|
|
571
627
|
} catch {}
|
|
572
628
|
};
|
|
573
629
|
let traceResolverBroken = false;
|
|
630
|
+
const warnedEnrichKeys = new Set;
|
|
574
631
|
const resolveId = (req) => {
|
|
575
632
|
if (!customTraceId)
|
|
576
633
|
return resolveTraceId(req);
|
|
@@ -617,11 +674,21 @@ function createHandler(config) {
|
|
|
617
674
|
logged = true;
|
|
618
675
|
try {
|
|
619
676
|
const durationMs = Math.round(elapsedMs(reqLog.startTime));
|
|
620
|
-
const
|
|
677
|
+
const collected = collectExtraLogFields(logConfig, req, url, {
|
|
621
678
|
status,
|
|
622
679
|
durationMs,
|
|
623
680
|
errorCode: errorCode2
|
|
624
681
|
});
|
|
682
|
+
const frameworkFields = buildLogFields(req.method, url.pathname, status, durationMs, reqLog.traceId, errorCode2);
|
|
683
|
+
const ownedFields = { ...frameworkFields, ip: ipAddress };
|
|
684
|
+
for (const key of collected.enrichKeys) {
|
|
685
|
+
const ownedByActiveSink = Object.hasOwn(ownedFields, key) || useDefaultLog && (key === "ts" || key === "level" || key === "msg");
|
|
686
|
+
if (ownedByActiveSink && !warnedEnrichKeys.has(key)) {
|
|
687
|
+
warnedEnrichKeys.add(key);
|
|
688
|
+
warn(`[stitchkit] logging.enrich field "${key}" was discarded because the framework owns it`);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
const extra = collected.fields;
|
|
625
692
|
if (useDefaultLog) {
|
|
626
693
|
logOutgoing({
|
|
627
694
|
req,
|
|
@@ -639,7 +706,7 @@ function createHandler(config) {
|
|
|
639
706
|
const level = levelForStatus(status);
|
|
640
707
|
customLogger[level](`${req.method} ${url.pathname} ${status}${errorCode2 ? ` ${errorCode2}` : ""} ${durationMs}ms`, {
|
|
641
708
|
...extra,
|
|
642
|
-
...
|
|
709
|
+
...frameworkFields,
|
|
643
710
|
ip: ipAddress
|
|
644
711
|
});
|
|
645
712
|
}
|
|
@@ -719,7 +786,7 @@ function createHandler(config) {
|
|
|
719
786
|
const ctx = buildBaseContext(req, url, pathParams, traceId, clientIp);
|
|
720
787
|
setRequestEndpoint(method.serviceName, method.key);
|
|
721
788
|
try {
|
|
722
|
-
await parseRequestInto(ctx, req, url, method, config.maxUploadBytes);
|
|
789
|
+
await parseRequestInto(ctx, req, url, method, config.maxUploadBytes, config.maxJsonBodyBytes);
|
|
723
790
|
if (hooks?.beforeHandle) {
|
|
724
791
|
await hooks.beforeHandle(ctx, method);
|
|
725
792
|
}
|
|
@@ -783,26 +850,6 @@ function createHandler(config) {
|
|
|
783
850
|
return response;
|
|
784
851
|
};
|
|
785
852
|
}
|
|
786
|
-
function createServer(config) {
|
|
787
|
-
const { routes, websocket, development, bun: bunExtra, port = 3000, hostname } = config;
|
|
788
|
-
const handler = createHandler(config);
|
|
789
|
-
const fetch = config.wrapFetch ? config.wrapFetch(handler) : handler;
|
|
790
|
-
return websocket ? Bun.serve({
|
|
791
|
-
...bunExtra,
|
|
792
|
-
...routes && { routes },
|
|
793
|
-
...development && { development },
|
|
794
|
-
port,
|
|
795
|
-
hostname,
|
|
796
|
-
websocket,
|
|
797
|
-
fetch
|
|
798
|
-
}) : Bun.serve({
|
|
799
|
-
...bunExtra,
|
|
800
|
-
...development && { development },
|
|
801
|
-
port,
|
|
802
|
-
hostname,
|
|
803
|
-
fetch
|
|
804
|
-
});
|
|
805
|
-
}
|
|
806
853
|
function normalizeGroups(config) {
|
|
807
854
|
const result = [];
|
|
808
855
|
if (config.services) {
|
|
@@ -876,18 +923,20 @@ function implement(contract, handlers) {
|
|
|
876
923
|
serviceName: contract.meta.prefix,
|
|
877
924
|
key: String(key),
|
|
878
925
|
toolName: "toolName" in endpoint ? endpoint.toolName : undefined,
|
|
879
|
-
expose: endpoint.rawResponse ? HTTP_ONLY : endpoint.expose,
|
|
926
|
+
expose: endpoint.rawResponse || endpoint.rawBody ? HTTP_ONLY : endpoint.expose,
|
|
880
927
|
scope: endpoint.scope ?? groupScope,
|
|
881
928
|
paramsSchema: endpoint.params,
|
|
882
929
|
inputSchema: endpoint.input,
|
|
883
930
|
outputSchema: endpoint.output,
|
|
884
931
|
multipart: endpoint.multipart,
|
|
885
932
|
maxUploadBytes: endpoint.maxUploadBytes,
|
|
933
|
+
maxJsonBodyBytes: endpoint.maxJsonBodyBytes,
|
|
886
934
|
idempotent: endpoint.idempotent,
|
|
887
935
|
ui: "ui" in endpoint ? endpoint.ui : undefined,
|
|
888
936
|
annotations: "annotations" in endpoint ? endpoint.annotations : undefined,
|
|
889
937
|
meta: mergeMeta(contract.meta.meta, endpoint.meta),
|
|
890
938
|
rawResponse: endpoint.rawResponse,
|
|
939
|
+
rawBody: endpoint.rawBody,
|
|
891
940
|
contentType: "contentType" in endpoint ? endpoint.contentType : undefined,
|
|
892
941
|
handler: (ctx) => typedHandler(ctx)
|
|
893
942
|
};
|
|
@@ -1049,4 +1098,4 @@ function socketIoLane(websocket) {
|
|
|
1049
1098
|
});
|
|
1050
1099
|
}
|
|
1051
1100
|
|
|
1052
|
-
export { parseMultipart, mimeForPath, staticRoute, createHandler,
|
|
1101
|
+
export { parseMultipart, mimeForPath, staticRoute, createHandler, implement, createImplement, webSocketLane, composeWebSocketHandlers, createSocketIOServer, socketIoLane };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// src/tools/json-schema.ts
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
function toJsonSchema(schema, io, unrepresentable = "throw") {
|
|
3
|
+
function toJsonSchema(schema, io, unrepresentable = "throw", target = "draft-2020-12") {
|
|
4
4
|
return z.toJSONSchema(schema, {
|
|
5
5
|
io,
|
|
6
|
-
target
|
|
6
|
+
target,
|
|
7
7
|
unrepresentable,
|
|
8
8
|
cycles: "ref"
|
|
9
9
|
});
|
|
@@ -10,8 +10,13 @@ function defineContract(meta, endpoints) {
|
|
|
10
10
|
if (ep.desc.trim() === "") {
|
|
11
11
|
throw new Error(`Contract "${meta.prefix}": endpoint "${key}" has an empty desc`);
|
|
12
12
|
}
|
|
13
|
+
if (ep.maxJsonBodyBytes !== undefined && (!Number.isSafeInteger(ep.maxJsonBodyBytes) || ep.maxJsonBodyBytes <= 0)) {
|
|
14
|
+
throw new Error(`Contract "${meta.prefix}": endpoint "${key}" maxJsonBodyBytes must be a positive safe integer, received ${ep.maxJsonBodyBytes}`);
|
|
15
|
+
}
|
|
13
16
|
if (ep.rawResponse)
|
|
14
17
|
assertRawEndpoint(meta.prefix, key, ep);
|
|
18
|
+
if (ep.rawBody)
|
|
19
|
+
assertRawBodyEndpoint(meta.prefix, key, ep);
|
|
15
20
|
if (!("toolName" in ep) || !ep.toolName)
|
|
16
21
|
continue;
|
|
17
22
|
const transports = new Set(ep.expose ? ep.expose.filter((t) => t !== "HTTP") : ["MCP", "AGENT"]);
|
|
@@ -48,6 +53,27 @@ function assertRawEndpoint(prefix, key, ep) {
|
|
|
48
53
|
throw new Error(`${where} is HTTP-only — remove ${nonHttp.join(", ")} from expose`);
|
|
49
54
|
}
|
|
50
55
|
}
|
|
56
|
+
function assertRawBodyEndpoint(prefix, key, ep) {
|
|
57
|
+
const where = `Contract "${prefix}": rawBody endpoint "${key}"`;
|
|
58
|
+
if (!ep.input)
|
|
59
|
+
throw new Error(`${where} must declare an input schema`);
|
|
60
|
+
if (ep.multipart)
|
|
61
|
+
throw new Error(`${where} cannot be multipart`);
|
|
62
|
+
if (ep.method !== "POST" && ep.method !== "PUT" && ep.method !== "PATCH") {
|
|
63
|
+
throw new Error(`${where} must use POST, PUT or PATCH`);
|
|
64
|
+
}
|
|
65
|
+
if ("toolName" in ep && ep.toolName)
|
|
66
|
+
throw new Error(`${where} cannot set a toolName`);
|
|
67
|
+
if ("ui" in ep && ep.ui)
|
|
68
|
+
throw new Error(`${where} cannot set MCP ui metadata`);
|
|
69
|
+
if ("annotations" in ep && ep.annotations) {
|
|
70
|
+
throw new Error(`${where} cannot set MCP annotations`);
|
|
71
|
+
}
|
|
72
|
+
const nonHttp = (ep.expose ?? []).filter((transport) => transport !== "HTTP");
|
|
73
|
+
if (nonHttp.length > 0) {
|
|
74
|
+
throw new Error(`${where} is HTTP-only — remove ${nonHttp.join(", ")} from expose`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
51
77
|
// src/contract/errors.ts
|
|
52
78
|
var APP_ERROR_BRAND = Symbol.for("stitchkit.AppError");
|
|
53
79
|
|