stitchkit 0.45.0 → 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.
Files changed (100) hide show
  1. package/README.md +1 -1
  2. package/dist/browser/http.d.ts.map +1 -1
  3. package/dist/browser/socket-io.d.ts +3 -1
  4. package/dist/browser/socket-io.d.ts.map +1 -1
  5. package/dist/cli.js +3 -4
  6. package/dist/contract/errors.d.ts +1 -0
  7. package/dist/contract/errors.d.ts.map +1 -1
  8. package/dist/contract/index.js +2 -2
  9. package/dist/{index-yh0p5ddn.js → index-5s8b7z6q.js} +31 -11
  10. package/dist/{index-mvjyq9h9.js → index-62pqb23z.js} +309 -144
  11. package/dist/index-c40tkxcd.js +181 -0
  12. package/dist/{index-r6czv7yt.js → index-pwyedf7b.js} +4 -3
  13. package/dist/{index-6jypn22c.js → index-r1qp4rve.js} +9 -1
  14. package/dist/{index-s4qsmgwe.js → index-tss6bk5c.js} +4 -1
  15. package/dist/{index-36f1xkdb.js → index-w1s873ng.js} +2 -2
  16. package/dist/index-yxpe3phd.js +63 -0
  17. package/dist/{index-w0mec79h.js → index-zwqty9zf.js} +261 -281
  18. package/dist/index.d.ts +1 -0
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +129 -51
  21. package/dist/internal/errors.d.ts.map +1 -1
  22. package/dist/internal/secure-fetch.d.ts +23 -3
  23. package/dist/internal/secure-fetch.d.ts.map +1 -1
  24. package/dist/internal/typed.d.ts +7 -0
  25. package/dist/internal/typed.d.ts.map +1 -1
  26. package/dist/internal/within-dir.d.ts +6 -0
  27. package/dist/internal/within-dir.d.ts.map +1 -1
  28. package/dist/logger.d.ts +8 -0
  29. package/dist/logger.d.ts.map +1 -0
  30. package/dist/node.js +3 -4
  31. package/dist/observability/audit.d.ts.map +1 -1
  32. package/dist/observability/event.d.ts +7 -0
  33. package/dist/observability/event.d.ts.map +1 -1
  34. package/dist/observability/index.js +54 -125
  35. package/dist/observability/sanitize.d.ts +8 -1
  36. package/dist/observability/sanitize.d.ts.map +1 -1
  37. package/dist/observability/trace.d.ts +3 -1
  38. package/dist/observability/trace.d.ts.map +1 -1
  39. package/dist/react/cache-bridge.d.ts +4 -0
  40. package/dist/react/cache-bridge.d.ts.map +1 -1
  41. package/dist/react.js +46 -5
  42. package/dist/realtime/contract.d.ts +4 -1
  43. package/dist/realtime/contract.d.ts.map +1 -1
  44. package/dist/realtime/rejection.d.ts +10 -0
  45. package/dist/realtime/rejection.d.ts.map +1 -0
  46. package/dist/realtime/socket.d.ts +14 -1
  47. package/dist/realtime/socket.d.ts.map +1 -1
  48. package/dist/server/cache.d.ts +5 -1
  49. package/dist/server/cache.d.ts.map +1 -1
  50. package/dist/server/error-hook.d.ts +1 -0
  51. package/dist/server/error-hook.d.ts.map +1 -1
  52. package/dist/server/file.d.ts +6 -0
  53. package/dist/server/file.d.ts.map +1 -1
  54. package/dist/server/implement.d.ts.map +1 -1
  55. package/dist/server/index.d.ts +2 -3
  56. package/dist/server/index.d.ts.map +1 -1
  57. package/dist/server/index.js +108 -11
  58. package/dist/server/middleware/cors.d.ts.map +1 -1
  59. package/dist/server/openapi.d.ts.map +1 -1
  60. package/dist/server/realtime.d.ts +2 -0
  61. package/dist/server/realtime.d.ts.map +1 -1
  62. package/dist/server/router.d.ts +0 -8
  63. package/dist/server/router.d.ts.map +1 -1
  64. package/dist/server/stream.d.ts.map +1 -1
  65. package/dist/server/types.d.ts +2 -7
  66. package/dist/server/types.d.ts.map +1 -1
  67. package/dist/tools/cli-args.d.ts +11 -3
  68. package/dist/tools/cli-args.d.ts.map +1 -1
  69. package/dist/tools/cli.d.ts +5 -0
  70. package/dist/tools/cli.d.ts.map +1 -1
  71. package/dist/tools/coerce.d.ts.map +1 -1
  72. package/dist/tools/json-schema.d.ts.map +1 -1
  73. package/dist/tools/mcp-handler.d.ts +4 -3
  74. package/dist/tools/mcp-handler.d.ts.map +1 -1
  75. package/dist/tools/mcp-prepare.d.ts +95 -0
  76. package/dist/tools/mcp-prepare.d.ts.map +1 -0
  77. package/dist/tools/mcp-round.d.ts +4 -0
  78. package/dist/tools/mcp-round.d.ts.map +1 -1
  79. package/dist/tools/mcp-stdio.d.ts +2 -1
  80. package/dist/tools/mcp-stdio.d.ts.map +1 -1
  81. package/dist/tools/mcp.d.ts +2 -78
  82. package/dist/tools/mcp.d.ts.map +1 -1
  83. package/dist/tools/mount-download.d.ts +5 -0
  84. package/dist/tools/mount-download.d.ts.map +1 -1
  85. package/dist/tools/native-mcp.d.ts +1 -1
  86. package/dist/tools/native-mcp.d.ts.map +1 -1
  87. package/dist/tools/oauth-provider.d.ts +20 -1
  88. package/dist/tools/oauth-provider.d.ts.map +1 -1
  89. package/dist/tools/tool-logger.d.ts +1 -1
  90. package/dist/tools/tool-logger.d.ts.map +1 -1
  91. package/dist/tools/view-file.d.ts +9 -0
  92. package/dist/tools/view-file.d.ts.map +1 -1
  93. package/dist/tools.d.ts +2 -1
  94. package/dist/tools.d.ts.map +1 -1
  95. package/dist/tools.js +342 -214
  96. package/llms-full.txt +107 -19
  97. package/llms.txt +1 -1
  98. package/package.json +2 -2
  99. package/dist/index-frfyw9fa.js +0 -33
  100. 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, { maxBytes: 10_000_000 })
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({ capacity: 60, refillPerSecond: 1 })
1236
- // in onRequest: if (!limiter.take(ip)) return new Response('Too many', { status: 429 })
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,6 +2843,8 @@ 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
 
@@ -2870,6 +2917,16 @@ createServer({
2870
2917
  realtime.emit('note:created', note)
2871
2918
  ```
2872
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
+ }
2928
+ ```
2929
+
2873
2930
  | Handle field | Purpose |
2874
2931
  |--------------|---------|
2875
2932
  | `io` | raw Socket.IO server for middleware, handshake auth and transport ownership |
@@ -3127,11 +3184,9 @@ policies; this helper only applies declared CRUD semantics.
3127
3184
 
3128
3185
  ## Raw binary lane (Bun)
3129
3186
 
3130
- Socket.IO carries binary fine for most streams a binary event (`pcm(frame)`)
3131
- is enough. But a *truly* high-throughput binary channel (video, large
3132
- transfers) may want a raw WebSocket with no Socket.IO framing, on the **same**
3133
- port. On Bun that is awkward: `Bun.serve` has a single `websocket` handler, and
3134
- `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).
3135
3190
 
3136
3191
  `composeWebSocketHandlers` composes that one handler from several lanes. A raw
3137
3192
  lane stamps its own marker onto `ws.data` at upgrade and is matched positively;
@@ -3414,9 +3469,11 @@ pick the scheme), and checks `exp` and `nbf`.
3414
3469
 
3415
3470
  ```ts
3416
3471
  import { verifyJwt, extractToken } from 'stitchkit/server'
3472
+ import { env } from './env'
3417
3473
 
3418
3474
  const token = extractToken(req) // from Authorization, or a cookie name
3419
- const payload = await verifyJwt(token, process.env.JWT_SECRET!)
3475
+ if (!token) throw unauthorized('Bearer token is required')
3476
+ const payload = await verifyJwt(token, env.JWT_SECRET)
3420
3477
  ```
3421
3478
 
3422
3479
  `extractToken(req, cookieName?)` reads a bearer token from the `Authorization`
@@ -3614,6 +3671,7 @@ const onError = createErrorHook({
3614
3671
  NOT_FOUND: 'not_found', METHOD_NOT_ALLOWED: 'not_found',
3615
3672
  CONFLICT: 'conflict', RATE_LIMITED: 'rate_limited',
3616
3673
  INTERNAL_SERVER_ERROR: 'internal',
3674
+ REALTIME_CONTRACT_VIOLATION: 'internal',
3617
3675
  } satisfies Record<StitchErrorCode, string>,
3618
3676
  // `ctx` is the request's RuntimeContext — read `ctx.traceId` for a
3619
3677
  // correlation id in the envelope. Declaring it is optional.
@@ -3967,8 +4025,10 @@ each request.
3967
4025
 
3968
4026
  A payload goes into an audit row only after `sanitizePayload`:
3969
4027
 
3970
- - **secret-named keys are masked** — `password`, `token`, `apiKey`, `secret`,
3971
- `authorization`, `cookie`, (value `[redacted]`);
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;
3972
4032
  - **binary blobs** (`Uint8Array`, `Blob`, `FormData`) collapse to metadata —
3973
4033
  never the bytes;
3974
4034
  - the result is **capped** — anything over the byte limit becomes a preview.
@@ -4564,7 +4624,7 @@ current one *up to* your target, and apply each snippet.
4564
4624
  (`STITCH_ERROR_STATUS`, `serveFile`, `scopePrefixes`, `afterToolCall`'s
4565
4625
  `MethodDef`, `maxUploadBytes`) are available to adopt, not required.
4566
4626
 
4567
- ## Unreleased breaking migrations
4627
+ ## Released migration: 0.44.0
4568
4628
 
4569
4629
  ### MCP TypeScript SDK v2 and protocol `2026-07-28`
4570
4630
 
@@ -4759,14 +4819,29 @@ implemented or advertised.
4759
4819
  3. Delete all session mode, event-store and session-id code.
4760
4820
  4. Move OAuth client policy under `clientRegistration`; publish CIMD or enable
4761
4821
  DCR explicitly.
4762
- 5. Snapshot `listToolNames`, run one contract tool, one runtime tool, any raw
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
4763
4838
  multimodal tool and every MCP App resource you use.
4764
- 6. Exercise modern HTTP and stdio with protocol `2026-07-28`; exercise legacy
4839
+ 7. Exercise modern HTTP and stdio with protocol `2026-07-28`; exercise legacy
4765
4840
  only if `legacy: 'serve'` is part of your support policy.
4766
- 7. Run the consumer's typecheck and runtime gates. A browser/HTTP-only consumer
4841
+ 8. Run the consumer's typecheck and runtime gates. A browser/HTTP-only consumer
4767
4842
  must continue to work without either MCP package.
4768
4843
 
4769
- ## Historical breaking migrations through 0.43.1
4844
+ ## Historical breaking migrations through 0.44.0
4770
4845
 
4771
4846
  HTTP observability now completes inside the framework handler instead of a
4772
4847
  nested fetch wrapper. Configure request and tool sinks explicitly:
@@ -5252,6 +5327,17 @@ The browser-and-server entrypoint. Re-exports everything from
5252
5327
  | `SocketEventMap` | _type_ | the shape of an event map |
5253
5328
  | `RealtimeClient` | _type_ | validated client inferred from a realtime contract |
5254
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 |
5255
5341
  | `RetainedTopics` | _type_ | the `createRetainedTopics` handle |
5256
5342
  | `ParseSSEOptions` | _type_ | options for `parseSSE` |
5257
5343
 
@@ -5469,6 +5555,7 @@ Also re-exports the error helpers from `stitchkit/contract`.
5469
5555
  | `parseMultipart` | function | parse a `multipart/form-data` request — [guide](../guide/server.md#multipart) |
5470
5556
  | `createRateLimiter` | function | token-bucket rate limiting — [guide](../guide/server.md#rate-limiting) |
5471
5557
  | `createCache` | function | an in-memory TTL cache |
5558
+ | `CacheOptions` | _type_ | bounded-cache options, including the maximum retained entry count |
5472
5559
  | `cacheHeaders` | function | build a `Cache-Control` header |
5473
5560
  | `EventBusOptions` | _type_ | options for `createEventBus` |
5474
5561
  | `EventHandler` | _type_ | one event-bus subscriber |
@@ -5596,6 +5683,7 @@ payload.
5596
5683
  | `StdioMcpServerConfig` | _type_ | config for `createStdioMcpServer` |
5597
5684
  | `McpServerBuildConfig` | _type_ | shared config for `buildMcpServer` |
5598
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 |
5599
5687
  | `DirectMcpSurfaceConfig` | _type_ | static or identity-dynamic `services` / `runtimeTools` source |
5600
5688
  | `FiniteMcpSurfaceConfig` | _type_ | bounded `surfaces` registry plus typed selector |
5601
5689
  | `McpSurfaceDefinition` | _type_ | one immutable `{ services, runtimeTools }` MCP surface |
@@ -5626,7 +5714,7 @@ payload.
5626
5714
  | `CimdClientMetadataFetcher` | _type_ | injectable secure network boundary for metadata loading |
5627
5715
  | `CimdFetchResponse` | _type_ | bounded metadata fetch result passed across the injectable network boundary |
5628
5716
  | `CimdFetchPolicy` | _type_ | CIMD timeout, redirect and size limits |
5629
- | `CimdCachePolicy` | _type_ | bounded HTTP-aware positive/negative cache policy |
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`) |
5630
5718
  | `CimdCacheEvent` | _type_ | observable CIMD cache hit, miss, revalidation and eviction event |
5631
5719
  | `createSecureClientMetadataFetcher` | function | production HTTPS, DNS/IP-pinned CIMD fetcher |
5632
5720
  | `RuntimeAgentModelOutput` | _type_ | AI SDK model-facing text/JSON/content output returned by `present.agent` |
@@ -5739,7 +5827,7 @@ Advanced building blocks — the shared machinery the mounts are built on.
5739
5827
 
5740
5828
  Server-only, for Node ≥ 22 (Bun uses `stitchkit/server`). The runtime-agnostic
5741
5829
  core plus a Node HTTP adapter — [ADR 0013](../decisions/0013-runtime-agnostic-core.md),
5742
- [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
5743
5831
  runtime-agnostic pieces of `stitchkit/server` and the error helpers.
5744
5832
 
5745
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 logging via hooks, W3C trace context, createAuditHook
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.45.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
- "prepublishOnly": "cp ../../README.md ./README.md && bun ../../scripts/gen-llms.ts && bun run build",
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",
@@ -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 };
@@ -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 };