stitchkit 0.44.1 → 0.46.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 +1 -1
- package/dist/browser/http.d.ts.map +1 -1
- package/dist/browser/socket-io.d.ts +16 -0
- package/dist/browser/socket-io.d.ts.map +1 -1
- package/dist/cli.js +3 -4
- package/dist/contract/errors.d.ts +1 -0
- package/dist/contract/errors.d.ts.map +1 -1
- package/dist/contract/index.js +2 -2
- package/dist/{index-yh0p5ddn.js → index-5s8b7z6q.js} +31 -11
- package/dist/{index-mvjyq9h9.js → index-62pqb23z.js} +309 -144
- package/dist/index-c40tkxcd.js +181 -0
- package/dist/{index-r6czv7yt.js → index-pwyedf7b.js} +4 -3
- package/dist/{index-6jypn22c.js → index-r1qp4rve.js} +9 -1
- package/dist/{index-s4qsmgwe.js → index-tss6bk5c.js} +4 -1
- package/dist/{index-36f1xkdb.js → index-w1s873ng.js} +2 -2
- package/dist/index-yxpe3phd.js +63 -0
- package/dist/{index-4fyn9f09.js → index-zwqty9zf.js} +396 -246
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +266 -17
- package/dist/internal/errors.d.ts.map +1 -1
- package/dist/internal/secure-fetch.d.ts +23 -3
- package/dist/internal/secure-fetch.d.ts.map +1 -1
- package/dist/internal/typed.d.ts +7 -0
- package/dist/internal/typed.d.ts.map +1 -1
- package/dist/internal/within-dir.d.ts +6 -0
- package/dist/internal/within-dir.d.ts.map +1 -1
- package/dist/logger.d.ts +8 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/node.d.ts +1 -0
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +5 -4
- package/dist/observability/audit.d.ts.map +1 -1
- package/dist/observability/event.d.ts +7 -0
- package/dist/observability/event.d.ts.map +1 -1
- package/dist/observability/index.js +54 -125
- package/dist/observability/sanitize.d.ts +8 -1
- package/dist/observability/sanitize.d.ts.map +1 -1
- package/dist/observability/trace.d.ts +3 -1
- package/dist/observability/trace.d.ts.map +1 -1
- package/dist/react/cache-bridge.d.ts +4 -0
- package/dist/react/cache-bridge.d.ts.map +1 -1
- package/dist/react.js +46 -5
- package/dist/realtime/contract.d.ts +39 -0
- package/dist/realtime/contract.d.ts.map +1 -0
- package/dist/realtime/index.d.ts +3 -0
- package/dist/realtime/index.d.ts.map +1 -0
- package/dist/realtime/rejection.d.ts +10 -0
- package/dist/realtime/rejection.d.ts.map +1 -0
- package/dist/realtime/socket.d.ts +30 -0
- package/dist/realtime/socket.d.ts.map +1 -0
- package/dist/server/cache.d.ts +5 -1
- package/dist/server/cache.d.ts.map +1 -1
- package/dist/server/error-hook.d.ts +1 -0
- package/dist/server/error-hook.d.ts.map +1 -1
- package/dist/server/file.d.ts +6 -0
- package/dist/server/file.d.ts.map +1 -1
- package/dist/server/implement.d.ts.map +1 -1
- package/dist/server/index.d.ts +3 -3
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +110 -11
- package/dist/server/middleware/cors.d.ts.map +1 -1
- package/dist/server/openapi.d.ts.map +1 -1
- package/dist/server/realtime.d.ts +24 -0
- package/dist/server/realtime.d.ts.map +1 -0
- package/dist/server/router.d.ts +0 -8
- package/dist/server/router.d.ts.map +1 -1
- package/dist/server/stream.d.ts.map +1 -1
- package/dist/server/types.d.ts +2 -7
- package/dist/server/types.d.ts.map +1 -1
- package/dist/tools/cli-args.d.ts +11 -3
- package/dist/tools/cli-args.d.ts.map +1 -1
- package/dist/tools/cli.d.ts +5 -0
- package/dist/tools/cli.d.ts.map +1 -1
- package/dist/tools/coerce.d.ts.map +1 -1
- package/dist/tools/json-schema.d.ts.map +1 -1
- package/dist/tools/mcp-handler.d.ts +4 -3
- package/dist/tools/mcp-handler.d.ts.map +1 -1
- package/dist/tools/mcp-prepare.d.ts +95 -0
- package/dist/tools/mcp-prepare.d.ts.map +1 -0
- package/dist/tools/mcp-round.d.ts +4 -0
- package/dist/tools/mcp-round.d.ts.map +1 -1
- package/dist/tools/mcp-stdio.d.ts +2 -1
- package/dist/tools/mcp-stdio.d.ts.map +1 -1
- package/dist/tools/mcp.d.ts +2 -78
- package/dist/tools/mcp.d.ts.map +1 -1
- package/dist/tools/mount-download.d.ts +5 -0
- package/dist/tools/mount-download.d.ts.map +1 -1
- package/dist/tools/native-mcp.d.ts +1 -1
- package/dist/tools/native-mcp.d.ts.map +1 -1
- package/dist/tools/oauth-provider.d.ts +20 -1
- package/dist/tools/oauth-provider.d.ts.map +1 -1
- package/dist/tools/tool-logger.d.ts +1 -1
- package/dist/tools/tool-logger.d.ts.map +1 -1
- package/dist/tools/view-file.d.ts +9 -0
- package/dist/tools/view-file.d.ts.map +1 -1
- package/dist/tools.d.ts +2 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +342 -214
- package/llms-full.txt +175 -44
- package/llms.txt +1 -1
- package/package.json +2 -2
- package/dist/index-frfyw9fa.js +0 -33
- package/dist/index-x3fcszf8.js +0 -8
package/llms-full.txt
CHANGED
|
@@ -677,7 +677,7 @@ server. See [Testing & deployment](./testing-and-deployment.md).
|
|
|
677
677
|
| `maxUploadBytes` | default multipart upload cap (bytes); per-route `EndpointDef.maxUploadBytes` overrides |
|
|
678
678
|
| `maxJsonBodyBytes` | optional JSON body cap (bytes); per-route value overrides; unset preserves existing behaviour |
|
|
679
679
|
| `port` / `hostname` | listen address — port defaults to `3000` |
|
|
680
|
-
| `cors` | CORS policy — `{ origin, credentials, methods, headers, exposeHeaders }` |
|
|
680
|
+
| `cors` | CORS policy — `{ origin, credentials, methods, headers, exposeHeaders }`. `origin` is **required** when `cors` is present: pass an explicit origin (or list), or `'*'` to deliberately allow every origin — an origin-less config is a construction error, never a silent wildcard. Omit `cors` entirely to emit no CORS headers. |
|
|
681
681
|
| `hooks` | lifecycle hooks (see below) |
|
|
682
682
|
| `logging` | `true` for built-in request logs, or a `LoggingConfig` (see below) |
|
|
683
683
|
| `traceId` | override per-request trace-id resolution — may return `undefined` to fall back |
|
|
@@ -685,6 +685,34 @@ server. See [Testing & deployment](./testing-and-deployment.md).
|
|
|
685
685
|
| `websocket` | Bun WebSocket handlers — e.g. from `createSocketIOServer` |
|
|
686
686
|
| `routes` / `development` / `bun` | passthrough to `Bun.serve` |
|
|
687
687
|
|
|
688
|
+
### Trusted HTTPS in development
|
|
689
|
+
|
|
690
|
+
Stitchkit does not own local certificate generation or a frontend development
|
|
691
|
+
server. When a device or browser feature requires a secure context, generate a
|
|
692
|
+
trusted certificate outside the application (for example with `mkcert`) and pass
|
|
693
|
+
its files through the Bun server boundary:
|
|
694
|
+
|
|
695
|
+
```ts
|
|
696
|
+
import { readFileSync } from 'node:fs'
|
|
697
|
+
import { createServer } from 'stitchkit/server'
|
|
698
|
+
|
|
699
|
+
createServer({
|
|
700
|
+
services,
|
|
701
|
+
hostname: '0.0.0.0',
|
|
702
|
+
bun: {
|
|
703
|
+
tls: {
|
|
704
|
+
cert: readFileSync('./certs/dev.pem'),
|
|
705
|
+
key: readFileSync('./certs/dev-key.pem'),
|
|
706
|
+
},
|
|
707
|
+
},
|
|
708
|
+
})
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
Trust the certificate authority on each test device and configure the frontend's
|
|
712
|
+
HTTPS mode in that frontend project. Certificate renewal, interface discovery
|
|
713
|
+
and device onboarding remain application infrastructure rather than framework
|
|
714
|
+
or starter behavior.
|
|
715
|
+
|
|
688
716
|
### Request logging
|
|
689
717
|
|
|
690
718
|
`logging: true` is shorthand for `logging: {}` — **any object turns logging
|
|
@@ -1207,7 +1235,7 @@ The client side is [`parseSSE`](./client.md#sse).
|
|
|
1207
1235
|
```ts
|
|
1208
1236
|
import { parseMultipart } from 'stitchkit/server'
|
|
1209
1237
|
|
|
1210
|
-
const { file, fields } = await parseMultipart(req,
|
|
1238
|
+
const { file, fields } = await parseMultipart(req, 'file', undefined, 10_000_000)
|
|
1211
1239
|
```
|
|
1212
1240
|
|
|
1213
1241
|
When an endpoint declares `multipart`, the framework parses the upload for you
|
|
@@ -1232,8 +1260,11 @@ createServer({ services, maxUploadBytes: 50 * 1024 * 1024 })
|
|
|
1232
1260
|
```ts
|
|
1233
1261
|
import { createRateLimiter } from 'stitchkit/server'
|
|
1234
1262
|
|
|
1235
|
-
const limiter = createRateLimiter(
|
|
1236
|
-
// in onRequest:
|
|
1263
|
+
const limiter = createRateLimiter()
|
|
1264
|
+
// in onRequest:
|
|
1265
|
+
if (!limiter.check(ip, { window: 60_000, max: 60 })) {
|
|
1266
|
+
return new Response('Too many', { status: 429 })
|
|
1267
|
+
}
|
|
1237
1268
|
```
|
|
1238
1269
|
|
|
1239
1270
|
### Event bus
|
|
@@ -2259,6 +2290,15 @@ timeouts, redirect count and body size are bounded. Its bounded cache respects
|
|
|
2259
2290
|
`Last-Modified`, coalesces concurrent misses and reports sanitized cache events;
|
|
2260
2291
|
invalid or unavailable identity receives only a short fail-closed cache entry.
|
|
2261
2292
|
|
|
2293
|
+
Resolution itself is rate-limited on two levels (`CimdCachePolicy`):
|
|
2294
|
+
`maxResolutionsPerClient` (default 10) bounds ONE `client_id` per
|
|
2295
|
+
`resolutionWindowMs` window (default 60 s) — a client whose document disables
|
|
2296
|
+
caching burns its own budget, not the server's — while `maxResolutions`
|
|
2297
|
+
(default 120) caps the server-wide outbound rate as the flood backstop.
|
|
2298
|
+
Positive and negative cache entries keep separate pools of `maxEntries` each,
|
|
2299
|
+
so a flood of unresolvable ids cannot evict warmed clients and a burst of
|
|
2300
|
+
successes cannot reset a failing client's backoff.
|
|
2301
|
+
|
|
2262
2302
|
### Authorization hardening (MCP 2026-07-28)
|
|
2263
2303
|
|
|
2264
2304
|
- **`iss` on every authorization response (RFC 9207, SEP-2468).** Success *and*
|
|
@@ -2586,6 +2626,11 @@ mountMcp(server, services, { hooks: createToolLogger() })
|
|
|
2586
2626
|
// [tool] warn get_widget (widgets.get) NOT_FOUND 4ms
|
|
2587
2627
|
```
|
|
2588
2628
|
|
|
2629
|
+
By default the line goes to `console.error` — **stderr, never stdout**. That is
|
|
2630
|
+
deliberate: in a stdio-MCP server stdout is the JSON-RPC protocol channel, and a
|
|
2631
|
+
log line written there corrupts the stream. If you pass your own `log`, keep it
|
|
2632
|
+
off stdout for any stdio deployment.
|
|
2633
|
+
|
|
2589
2634
|
Pass `log` to redirect the line, or `onRecord` to feed a metrics sink the
|
|
2590
2635
|
structured `ToolCallRecord`. That record carries `traceId` whenever an
|
|
2591
2636
|
observability context is active, so a tool call made inside an HTTP request
|
|
@@ -2798,36 +2843,64 @@ fallback, heartbeats, acks, a mature client. stitchkit does not ship its own
|
|
|
2798
2843
|
WebSocket engine; it ships thin, typed wrappers over Socket.IO and a bridge that
|
|
2799
2844
|
syncs socket events into the TanStack Query cache. See
|
|
2800
2845
|
[ADR 0008](../decisions/0008-thin-wrappers.md).
|
|
2846
|
+
The separate contract shape and rejection ownership are recorded in
|
|
2847
|
+
[ADR 0069](../decisions/0069-realtime-contracts-validate-without-owning-delivery.md).
|
|
2801
2848
|
|
|
2802
|
-
##
|
|
2849
|
+
## Zod-first event contract
|
|
2803
2850
|
|
|
2804
|
-
Declare
|
|
2851
|
+
Declare wire events once in the shared module. Each `args` schema is a tuple, so
|
|
2852
|
+
Socket.IO's no-payload and variadic forms stay intact; `ack` describes the value
|
|
2853
|
+
returned through an acknowledgement callback.
|
|
2805
2854
|
|
|
2806
2855
|
```ts
|
|
2807
2856
|
// shared/contracts.ts
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
}
|
|
2857
|
+
import { defineRealtimeContract } from 'stitchkit'
|
|
2858
|
+
import { z } from 'zod'
|
|
2859
|
+
|
|
2860
|
+
export const realtimeContract = defineRealtimeContract({
|
|
2861
|
+
serverToClient: {
|
|
2862
|
+
'note:created': { args: z.tuple([NoteSchema]) },
|
|
2863
|
+
'note:deleted': { args: z.tuple([z.string()]) },
|
|
2864
|
+
},
|
|
2865
|
+
clientToServer: {
|
|
2866
|
+
'room:join': {
|
|
2867
|
+
args: z.tuple([z.string()]),
|
|
2868
|
+
ack: z.object({ joined: z.boolean() }),
|
|
2869
|
+
},
|
|
2870
|
+
ready: { args: z.tuple([]) },
|
|
2871
|
+
},
|
|
2872
|
+
})
|
|
2815
2873
|
```
|
|
2816
2874
|
|
|
2817
|
-
|
|
2818
|
-
|
|
2875
|
+
The contract derives every event handler type and validates both directions at
|
|
2876
|
+
runtime. A malformed inbound tuple never reaches the application handler;
|
|
2877
|
+
invalid outbound data throws before Socket.IO publishes it. Rejections call the
|
|
2878
|
+
optional `onRejected` hook with event, direction, phase and the Zod error.
|
|
2819
2879
|
|
|
2820
2880
|
## Server — `createSocketIOServer`
|
|
2821
2881
|
|
|
2822
2882
|
```ts
|
|
2823
|
-
import {
|
|
2883
|
+
import {
|
|
2884
|
+
bindRealtimeServer,
|
|
2885
|
+
createServer,
|
|
2886
|
+
createSocketIOServer,
|
|
2887
|
+
} from 'stitchkit/server'
|
|
2888
|
+
import { realtimeContract } from '@app/shared'
|
|
2824
2889
|
|
|
2825
|
-
const socket = await createSocketIOServer
|
|
2890
|
+
const socket = await createSocketIOServer({
|
|
2826
2891
|
cors: { origin: 'https://app.example.com' },
|
|
2827
2892
|
})
|
|
2828
2893
|
|
|
2829
|
-
|
|
2830
|
-
|
|
2894
|
+
const realtime = bindRealtimeServer(realtimeContract, socket, {
|
|
2895
|
+
onRejected: (event) => audit.realtimeRejected(event),
|
|
2896
|
+
})
|
|
2897
|
+
|
|
2898
|
+
realtime.onConnection(({ raw, events, to }) => {
|
|
2899
|
+
events.on('room:join', (room, acknowledge) => {
|
|
2900
|
+
raw.join(room) // authorization and room membership remain application policy
|
|
2901
|
+
acknowledge({ joined: true })
|
|
2902
|
+
to(room).emit('note:created', note)
|
|
2903
|
+
})
|
|
2831
2904
|
})
|
|
2832
2905
|
```
|
|
2833
2906
|
|
|
@@ -2840,13 +2913,23 @@ createServer({
|
|
|
2840
2913
|
rawRoutes: [socket.route], // ready-made /socket.io/*socketPath route
|
|
2841
2914
|
})
|
|
2842
2915
|
|
|
2843
|
-
// elsewhere — broadcast:
|
|
2844
|
-
|
|
2916
|
+
// elsewhere — validated broadcast:
|
|
2917
|
+
realtime.emit('note:created', note)
|
|
2918
|
+
```
|
|
2919
|
+
|
|
2920
|
+
The canonical room-broadcast example below is executed by the test suite. Its
|
|
2921
|
+
body is kept byte-identical to `packages/core/examples/realtime-room.ts`.
|
|
2922
|
+
|
|
2923
|
+
```ts canonical-realtime-room
|
|
2924
|
+
export function publishExampleNote(realtime: ExampleRealtimePublisher): void {
|
|
2925
|
+
const note = { id: 'note-1', text: 'Ready' };
|
|
2926
|
+
realtime.to('general').emit('note:created', note);
|
|
2927
|
+
}
|
|
2845
2928
|
```
|
|
2846
2929
|
|
|
2847
2930
|
| Handle field | Purpose |
|
|
2848
2931
|
|--------------|---------|
|
|
2849
|
-
| `io` |
|
|
2932
|
+
| `io` | raw Socket.IO server for middleware, handshake auth and transport ownership |
|
|
2850
2933
|
| `websocket` | Bun WebSocket handlers — pass to `createServer({ websocket })` |
|
|
2851
2934
|
| `route` | the `/socket.io/*socketPath` raw route — pass to `createServer({ rawRoutes })` |
|
|
2852
2935
|
|
|
@@ -2868,20 +2951,29 @@ precedence over the same keys in `serverOptions`. On Bun the engine-level option
|
|
|
2868
2951
|
`@socket.io/bun-engine` too — so a configured `maxHttpBufferSize` actually applies
|
|
2869
2952
|
instead of silently truncating at 1 MB.
|
|
2870
2953
|
|
|
2871
|
-
## Client — `
|
|
2954
|
+
## Client — `createRealtimeClient`
|
|
2872
2955
|
|
|
2873
2956
|
```ts
|
|
2874
|
-
import {
|
|
2957
|
+
import { createRealtimeClient } from 'stitchkit'
|
|
2958
|
+
import { realtimeContract } from '@app/shared'
|
|
2875
2959
|
|
|
2876
|
-
const socket =
|
|
2960
|
+
const socket = createRealtimeClient(realtimeContract, {
|
|
2877
2961
|
url: 'https://api.example.com',
|
|
2962
|
+
retain: ['note:created'],
|
|
2963
|
+
onRejected: (event) => reportClientError(event),
|
|
2878
2964
|
})
|
|
2879
2965
|
|
|
2880
2966
|
socket.connect()
|
|
2881
2967
|
socket.on('note:created', (note) => { /* typed note */ })
|
|
2882
|
-
socket.emit('room:join', 'general')
|
|
2968
|
+
socket.emit('room:join', 'general', ({ joined }) => { /* typed + validated */ })
|
|
2883
2969
|
```
|
|
2884
2970
|
|
|
2971
|
+
## Low-level transport
|
|
2972
|
+
|
|
2973
|
+
`createSocketIOClient` remains the low-level Socket.IO transport wrapper for
|
|
2974
|
+
schema-agnostic infrastructure. Application wire events should use
|
|
2975
|
+
`createRealtimeClient`; it adds the shared contract without replacing Socket.IO.
|
|
2976
|
+
|
|
2885
2977
|
### Durable subscriptions
|
|
2886
2978
|
|
|
2887
2979
|
`socket.on(...)` returns an unsubscribe and is **durable** — the handler is
|
|
@@ -2901,7 +2993,7 @@ subscribes (and on the next subscribe after a re-render). It is the pub/sub
|
|
|
2901
2993
|
analogue of an MQTT *retained* message or an RxJS `BehaviorSubject`.
|
|
2902
2994
|
|
|
2903
2995
|
```ts
|
|
2904
|
-
const client =
|
|
2996
|
+
const client = createRealtimeClient(realtimeContract, {
|
|
2905
2997
|
url,
|
|
2906
2998
|
retain: ['presence:changed', 'job:state'], // events to keep the last value of
|
|
2907
2999
|
})
|
|
@@ -2938,7 +3030,7 @@ handshake with **`auth`** instead — the token reaches the server as
|
|
|
2938
3030
|
// client — a function is re-read on every (re)connect, so a rotated token is
|
|
2939
3031
|
// picked up automatically; no need to recreate the client (or lose durable
|
|
2940
3032
|
// subscriptions). It may be async.
|
|
2941
|
-
const socket =
|
|
3033
|
+
const socket = createRealtimeClient(realtimeContract, {
|
|
2942
3034
|
url: 'https://api.example.com',
|
|
2943
3035
|
auth: () => ({ token: getAccessToken() }),
|
|
2944
3036
|
})
|
|
@@ -3092,11 +3184,9 @@ policies; this helper only applies declared CRUD semantics.
|
|
|
3092
3184
|
|
|
3093
3185
|
## Raw binary lane (Bun)
|
|
3094
3186
|
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
port. On Bun that is awkward: `Bun.serve` has a single `websocket` handler, and
|
|
3099
|
-
`createSocketIOServer().websocket` claims it.
|
|
3187
|
+
For a high-throughput raw binary channel beside Socket.IO, use the orthogonal
|
|
3188
|
+
composition boundary from
|
|
3189
|
+
[ADR 0020](../decisions/0020-raw-websocket-lane.md).
|
|
3100
3190
|
|
|
3101
3191
|
`composeWebSocketHandlers` composes that one handler from several lanes. A raw
|
|
3102
3192
|
lane stamps its own marker onto `ws.data` at upgrade and is matched positively;
|
|
@@ -3379,9 +3469,11 @@ pick the scheme), and checks `exp` and `nbf`.
|
|
|
3379
3469
|
|
|
3380
3470
|
```ts
|
|
3381
3471
|
import { verifyJwt, extractToken } from 'stitchkit/server'
|
|
3472
|
+
import { env } from './env'
|
|
3382
3473
|
|
|
3383
3474
|
const token = extractToken(req) // from Authorization, or a cookie name
|
|
3384
|
-
|
|
3475
|
+
if (!token) throw unauthorized('Bearer token is required')
|
|
3476
|
+
const payload = await verifyJwt(token, env.JWT_SECRET)
|
|
3385
3477
|
```
|
|
3386
3478
|
|
|
3387
3479
|
`extractToken(req, cookieName?)` reads a bearer token from the `Authorization`
|
|
@@ -3579,6 +3671,7 @@ const onError = createErrorHook({
|
|
|
3579
3671
|
NOT_FOUND: 'not_found', METHOD_NOT_ALLOWED: 'not_found',
|
|
3580
3672
|
CONFLICT: 'conflict', RATE_LIMITED: 'rate_limited',
|
|
3581
3673
|
INTERNAL_SERVER_ERROR: 'internal',
|
|
3674
|
+
REALTIME_CONTRACT_VIOLATION: 'internal',
|
|
3582
3675
|
} satisfies Record<StitchErrorCode, string>,
|
|
3583
3676
|
// `ctx` is the request's RuntimeContext — read `ctx.traceId` for a
|
|
3584
3677
|
// correlation id in the envelope. Declaring it is optional.
|
|
@@ -3932,8 +4025,10 @@ each request.
|
|
|
3932
4025
|
|
|
3933
4026
|
A payload goes into an audit row only after `sanitizePayload`:
|
|
3934
4027
|
|
|
3935
|
-
- **secret-named keys are masked** —
|
|
3936
|
-
`
|
|
4028
|
+
- **secret-named keys are masked** — a key whose words include a secret term is
|
|
4029
|
+
redacted (`password`, `sessionToken`, `X-Api-Key`, `authorization`, `cookie`,
|
|
4030
|
+
…), while identifiers that merely contain one (`authorId`, `sessionCount`,
|
|
4031
|
+
`tokenizer`) survive;
|
|
3937
4032
|
- **binary blobs** (`Uint8Array`, `Blob`, `FormData`) collapse to metadata —
|
|
3938
4033
|
never the bytes;
|
|
3939
4034
|
- the result is **capped** — anything over the byte limit becomes a preview.
|
|
@@ -4529,7 +4624,7 @@ current one *up to* your target, and apply each snippet.
|
|
|
4529
4624
|
(`STITCH_ERROR_STATUS`, `serveFile`, `scopePrefixes`, `afterToolCall`'s
|
|
4530
4625
|
`MethodDef`, `maxUploadBytes`) are available to adopt, not required.
|
|
4531
4626
|
|
|
4532
|
-
##
|
|
4627
|
+
## Released migration: 0.44.0
|
|
4533
4628
|
|
|
4534
4629
|
### MCP TypeScript SDK v2 and protocol `2026-07-28`
|
|
4535
4630
|
|
|
@@ -4724,14 +4819,29 @@ implemented or advertised.
|
|
|
4724
4819
|
3. Delete all session mode, event-store and session-id code.
|
|
4725
4820
|
4. Move OAuth client policy under `clientRegistration`; publish CIMD or enable
|
|
4726
4821
|
DCR explicitly.
|
|
4727
|
-
5.
|
|
4822
|
+
5. Make `authorizeUser` return the exact consented scope subset. The framework
|
|
4823
|
+
validates that it is a subset of the request before saving the authorization
|
|
4824
|
+
code:
|
|
4825
|
+
|
|
4826
|
+
```ts
|
|
4827
|
+
// before
|
|
4828
|
+
authorizeUser: async () => ({ userId })
|
|
4829
|
+
|
|
4830
|
+
// after
|
|
4831
|
+
authorizeUser: async (_req, request) => ({
|
|
4832
|
+
userId,
|
|
4833
|
+
approvedScopes: request.scope?.split(' ') ?? [],
|
|
4834
|
+
})
|
|
4835
|
+
```
|
|
4836
|
+
|
|
4837
|
+
6. Snapshot `listToolNames`, run one contract tool, one runtime tool, any raw
|
|
4728
4838
|
multimodal tool and every MCP App resource you use.
|
|
4729
|
-
|
|
4839
|
+
7. Exercise modern HTTP and stdio with protocol `2026-07-28`; exercise legacy
|
|
4730
4840
|
only if `legacy: 'serve'` is part of your support policy.
|
|
4731
|
-
|
|
4841
|
+
8. Run the consumer's typecheck and runtime gates. A browser/HTTP-only consumer
|
|
4732
4842
|
must continue to work without either MCP package.
|
|
4733
4843
|
|
|
4734
|
-
## Historical breaking migrations through 0.
|
|
4844
|
+
## Historical breaking migrations through 0.44.0
|
|
4735
4845
|
|
|
4736
4846
|
HTTP observability now completes inside the framework handler instead of a
|
|
4737
4847
|
nested fetch wrapper. Configure request and tool sinks explicitly:
|
|
@@ -5207,12 +5317,27 @@ The browser-and-server entrypoint. Re-exports everything from
|
|
|
5207
5317
|
|
|
5208
5318
|
| Export | Kind | Summary |
|
|
5209
5319
|
|--------|------|---------|
|
|
5210
|
-
| `createSocketIOClient` | function |
|
|
5320
|
+
| `createSocketIOClient` | function | low-level typed Socket.IO transport primitive — [guide](../guide/realtime.md#low-level-transport) |
|
|
5321
|
+
| `defineRealtimeContract` | function | Zod-first shared Socket.IO event contract — [guide](../guide/realtime.md#zod-first-event-contract) |
|
|
5322
|
+
| `createRealtimeClient` | function | inferred, runtime-validated Socket.IO client — [guide](../guide/realtime.md#client--createrealtimeclient) |
|
|
5211
5323
|
| `createRetainedTopics` | function | retained last-value store for sticky events — [guide](../guide/realtime.md#sticky-events) |
|
|
5212
5324
|
| `parseSSE` | function | parse an SSE `Response` into an async generator — [guide](../guide/client.md#sse) |
|
|
5213
5325
|
| `SocketIOClient` | _type_ | the client handle |
|
|
5214
5326
|
| `SocketIOClientConfig` | _type_ | config for `createSocketIOClient` (incl. `retain`) |
|
|
5215
5327
|
| `SocketEventMap` | _type_ | the shape of an event map |
|
|
5328
|
+
| `RealtimeClient` | _type_ | validated client inferred from a realtime contract |
|
|
5329
|
+
| `RealtimeClientOptions` | _type_ | transport options and the rejected-event hook for `createRealtimeClient` |
|
|
5330
|
+
| `RealtimeContract` | _type_ | shared server-to-client and client-to-server event registries |
|
|
5331
|
+
| `RealtimeEventRegistry` | _type_ | string-keyed registry of event definitions |
|
|
5332
|
+
| `RealtimeEventDefinition` | _type_ | one tuple-shaped event and optional acknowledgement schema |
|
|
5333
|
+
| `RealtimeEventArguments` | _type_ | tuple inferred from an event definition |
|
|
5334
|
+
| `RealtimeEmitArguments` | _type_ | emit arguments including an inferred acknowledgement callback |
|
|
5335
|
+
| `RealtimeEventHandler` | _type_ | handler inferred from an event definition |
|
|
5336
|
+
| `InferRealtimeEventMap` | _type_ | inferred Socket.IO-compatible event map |
|
|
5337
|
+
| `RealtimeRejectDirection` | _type_ | server/client inbound/outbound rejection direction |
|
|
5338
|
+
| `RealtimeRejectedEvent` | _type_ | structured rejected event with event, direction, phase, reason and fault |
|
|
5339
|
+
| `RealtimeRejectedEventHook` | _type_ | sync/async observer for structured realtime rejections |
|
|
5340
|
+
| `ValidatedRealtimeSocket` | _type_ | runtime-validating `on`/`emit` surface inferred from registries |
|
|
5216
5341
|
| `RetainedTopics` | _type_ | the `createRetainedTopics` handle |
|
|
5217
5342
|
| `ParseSSEOptions` | _type_ | options for `parseSSE` |
|
|
5218
5343
|
|
|
@@ -5407,6 +5532,10 @@ Also re-exports the error helpers from `stitchkit/contract`.
|
|
|
5407
5532
|
| Export | Kind | Summary |
|
|
5408
5533
|
|--------|------|---------|
|
|
5409
5534
|
| `createSocketIOServer` | function | the typed Socket.IO server — [guide](../guide/realtime.md#server--createsocketioserver) |
|
|
5535
|
+
| `bindRealtimeServer` | function | inferred, runtime-validated connection and broadcast boundary |
|
|
5536
|
+
| `RealtimeServer` | _type_ | validated broadcast and connection API inferred from a realtime contract |
|
|
5537
|
+
| `RealtimeServerConnection` | _type_ | one validated connection with raw socket access for auth and rooms |
|
|
5538
|
+
| `RealtimeServerHandle` | _type_ | minimal Socket.IO server handle accepted by `bindRealtimeServer` |
|
|
5410
5539
|
| `SocketIOServerConfig` | _type_ | config for `createSocketIOServer` |
|
|
5411
5540
|
| `SocketIOServerHandle` | _type_ | the `{ io, websocket, route }` handle |
|
|
5412
5541
|
| `composeWebSocketHandlers` | function | compose one Bun `websocket` from N lanes — a raw binary lane beside Socket.IO ([guide](../guide/realtime.md#raw-binary-lane-bun)) |
|
|
@@ -5426,6 +5555,7 @@ Also re-exports the error helpers from `stitchkit/contract`.
|
|
|
5426
5555
|
| `parseMultipart` | function | parse a `multipart/form-data` request — [guide](../guide/server.md#multipart) |
|
|
5427
5556
|
| `createRateLimiter` | function | token-bucket rate limiting — [guide](../guide/server.md#rate-limiting) |
|
|
5428
5557
|
| `createCache` | function | an in-memory TTL cache |
|
|
5558
|
+
| `CacheOptions` | _type_ | bounded-cache options, including the maximum retained entry count |
|
|
5429
5559
|
| `cacheHeaders` | function | build a `Cache-Control` header |
|
|
5430
5560
|
| `EventBusOptions` | _type_ | options for `createEventBus` |
|
|
5431
5561
|
| `EventHandler` | _type_ | one event-bus subscriber |
|
|
@@ -5553,6 +5683,7 @@ payload.
|
|
|
5553
5683
|
| `StdioMcpServerConfig` | _type_ | config for `createStdioMcpServer` |
|
|
5554
5684
|
| `McpServerBuildConfig` | _type_ | shared config for `buildMcpServer` |
|
|
5555
5685
|
| `McpServerSharedConfig` | _type_ | transport-neutral options shared by direct and finite surface configs |
|
|
5686
|
+
| `McpServer` | _type_ | official split-SDK server instance accepted by raw extension points |
|
|
5556
5687
|
| `DirectMcpSurfaceConfig` | _type_ | static or identity-dynamic `services` / `runtimeTools` source |
|
|
5557
5688
|
| `FiniteMcpSurfaceConfig` | _type_ | bounded `surfaces` registry plus typed selector |
|
|
5558
5689
|
| `McpSurfaceDefinition` | _type_ | one immutable `{ services, runtimeTools }` MCP surface |
|
|
@@ -5583,7 +5714,7 @@ payload.
|
|
|
5583
5714
|
| `CimdClientMetadataFetcher` | _type_ | injectable secure network boundary for metadata loading |
|
|
5584
5715
|
| `CimdFetchResponse` | _type_ | bounded metadata fetch result passed across the injectable network boundary |
|
|
5585
5716
|
| `CimdFetchPolicy` | _type_ | CIMD timeout, redirect and size limits |
|
|
5586
|
-
| `CimdCachePolicy` | _type_ | bounded HTTP-aware positive/negative
|
|
5717
|
+
| `CimdCachePolicy` | _type_ | bounded HTTP-aware cache policy: separate positive/negative pools (`maxEntries`), per-client and server-wide resolution rate limits (`maxResolutionsPerClient` / `maxResolutions` per `resolutionWindowMs`) |
|
|
5587
5718
|
| `CimdCacheEvent` | _type_ | observable CIMD cache hit, miss, revalidation and eviction event |
|
|
5588
5719
|
| `createSecureClientMetadataFetcher` | function | production HTTPS, DNS/IP-pinned CIMD fetcher |
|
|
5589
5720
|
| `RuntimeAgentModelOutput` | _type_ | AI SDK model-facing text/JSON/content output returned by `present.agent` |
|
|
@@ -5696,7 +5827,7 @@ Advanced building blocks — the shared machinery the mounts are built on.
|
|
|
5696
5827
|
|
|
5697
5828
|
Server-only, for Node ≥ 22 (Bun uses `stitchkit/server`). The runtime-agnostic
|
|
5698
5829
|
core plus a Node HTTP adapter — [ADR 0013](../decisions/0013-runtime-agnostic-core.md),
|
|
5699
|
-
[deployment guide](../guide/testing-and-deployment.md#node). Re-exports the
|
|
5830
|
+
[deployment guide](../guide/testing-and-deployment.md#deploy-on-node). Re-exports the
|
|
5700
5831
|
runtime-agnostic pieces of `stitchkit/server` and the error helpers.
|
|
5701
5832
|
|
|
5702
5833
|
| Export | Kind | Summary |
|
package/llms.txt
CHANGED
|
@@ -13,7 +13,7 @@ Build with stitchkit: define a contract once, then `implement` it and serve it (
|
|
|
13
13
|
- [CLI](https://github.com/max-listov/stitchkit/blob/master/docs/guide/cli.md): contracts as a command-line program
|
|
14
14
|
- [Realtime](https://github.com/max-listov/stitchkit/blob/master/docs/guide/realtime.md): Socket.IO server/client wrappers, handshake auth, the cache bridge, a raw WebSocket lane
|
|
15
15
|
- [Auth & errors](https://github.com/max-listov/stitchkit/blob/master/docs/guide/auth-and-errors.md): scopes, createAuthHook, JWT/cookies, the AppError model, the stitch error-code registry
|
|
16
|
-
- [Observability](https://github.com/max-listov/stitchkit/blob/master/docs/guide/observability.md): request and tool-call
|
|
16
|
+
- [Observability](https://github.com/max-listov/stitchkit/blob/master/docs/guide/observability.md): request and tool-call observability, W3C trace context, createObservability
|
|
17
17
|
- [Testing & deployment](https://github.com/max-listov/stitchkit/blob/master/docs/guide/testing-and-deployment.md): in-process testing; deploying on Bun and on Node (serveNode)
|
|
18
18
|
- [Multi-tenant](https://github.com/max-listov/stitchkit/blob/master/docs/guide/multi-tenant.md): a /tenants/:id/… scenario end-to-end — scopePrefixes, scoped client, extend
|
|
19
19
|
- [Frontend integrations](https://github.com/max-listov/stitchkit/blob/master/docs/guide/frontend-integrations.md): React Router resource routes and a separate Vite development proxy
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stitchkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.46.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",
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
"build:types": "bun x tsc -p tsconfig.build.json --emitDeclarationOnly",
|
|
89
89
|
"build": "rm -rf dist && bun run build:js && bun run build:types && bun scripts/check-browser-clean.mjs && bun scripts/check-env-live.mjs && bun scripts/check-public-types.mjs",
|
|
90
90
|
"dev": "bun run build:js -- --watch",
|
|
91
|
-
"
|
|
91
|
+
"prepack": "cp ../../README.md ./README.md && bun ../../scripts/gen-llms.ts && bun run build",
|
|
92
92
|
"test": "bun test",
|
|
93
93
|
"smoke:node": "node scripts/node-smoke.mjs",
|
|
94
94
|
"consumer-lane": "bun scripts/consumer-lane/run.mjs",
|
package/dist/index-frfyw9fa.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
// src/tools/json-schema.ts
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
function toJsonSchema(schema, io, unrepresentable = "throw", target = "draft-2020-12") {
|
|
4
|
-
return z.toJSONSchema(schema, {
|
|
5
|
-
io,
|
|
6
|
-
target,
|
|
7
|
-
unrepresentable,
|
|
8
|
-
cycles: "ref"
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
function isObjectNode(value) {
|
|
12
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13
|
-
}
|
|
14
|
-
function jsonSchemaFields(jsonSchema) {
|
|
15
|
-
const properties = jsonSchema.properties;
|
|
16
|
-
if (!isObjectNode(properties))
|
|
17
|
-
return [];
|
|
18
|
-
const required = new Set(Array.isArray(jsonSchema.required) ? jsonSchema.required.filter((k) => typeof k === "string") : []);
|
|
19
|
-
const fields = [];
|
|
20
|
-
for (const [name, raw] of Object.entries(properties)) {
|
|
21
|
-
const schema = isObjectNode(raw) ? raw : {};
|
|
22
|
-
const description = typeof schema.description === "string" ? schema.description : undefined;
|
|
23
|
-
fields.push({
|
|
24
|
-
name,
|
|
25
|
-
schema,
|
|
26
|
-
required: required.has(name),
|
|
27
|
-
...description && { description }
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
return fields;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export { toJsonSchema, jsonSchemaFields };
|
package/dist/index-x3fcszf8.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
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 };
|