stitchkit 0.48.1 → 0.49.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 -2
- package/dist/browser/http.d.ts.map +1 -1
- package/dist/{index-ts21eyz4.js → index-fjfzsq6y.js} +243 -8
- package/dist/index.js +29 -0
- package/dist/node.d.ts +3 -2
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +102 -12
- package/dist/server/bun.d.ts +7 -8
- package/dist/server/bun.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 +119 -6
- package/dist/server/node.d.ts +12 -13
- package/dist/server/node.d.ts.map +1 -1
- package/dist/server/shutdown.d.ts +76 -0
- package/dist/server/shutdown.d.ts.map +1 -0
- package/dist/server/socket-io-config.d.ts +5 -1
- package/dist/server/socket-io-config.d.ts.map +1 -1
- package/dist/server/socket-io-node.d.ts +4 -1
- package/dist/server/socket-io-node.d.ts.map +1 -1
- package/dist/server/socket-io.d.ts +12 -4
- package/dist/server/socket-io.d.ts.map +1 -1
- package/llms-full.txt +163 -27
- package/package.json +2 -1
package/llms-full.txt
CHANGED
|
@@ -712,8 +712,9 @@ The registry is intentionally flat: every key must point to one concrete
|
|
|
712
712
|
|
|
713
713
|
## `createServer`
|
|
714
714
|
|
|
715
|
-
`createServer(config)` builds the router and starts `Bun.serve()`. It returns
|
|
716
|
-
the
|
|
715
|
+
`createServer(config)` builds the router and starts `Bun.serve()`. It returns a
|
|
716
|
+
managed handle with `url`, `port`, the concrete server under `runtime`, live
|
|
717
|
+
`status`, and one idempotent `shutdown()` lifecycle.
|
|
717
718
|
|
|
718
719
|
```ts
|
|
719
720
|
import { createServer } from 'stitchkit/server'
|
|
@@ -746,8 +747,34 @@ server. See [Testing & deployment](./testing-and-deployment.md).
|
|
|
746
747
|
| `logging` | `true` for built-in request logs, or a `LoggingConfig` (see below) |
|
|
747
748
|
| `traceId` | override per-request trace-id resolution — may return `undefined` to fall back |
|
|
748
749
|
| `wrapFetch` | compose wrappers around the finished handler (request context, audit) |
|
|
749
|
-
| `
|
|
750
|
-
| `
|
|
750
|
+
| `socket` | full Stitchkit Socket.IO handle; route, default WebSocket handler and shutdown are mounted once |
|
|
751
|
+
| `websocket` | custom Bun WebSocket handler; with `socket`, this is the explicit composed handler |
|
|
752
|
+
| `development` / `bun` | passthrough to `Bun.serve` |
|
|
753
|
+
|
|
754
|
+
Native Bun `routes` are intentionally not accepted: Bun matches them before
|
|
755
|
+
`fetch`, so they could bypass shutdown admission. Use `rawRoutes`; they retain
|
|
756
|
+
the Fetch `Request → Response` model and participate in lifecycle tracking.
|
|
757
|
+
|
|
758
|
+
### Managed shutdown
|
|
759
|
+
|
|
760
|
+
```ts
|
|
761
|
+
const server = createServer({ services, socket })
|
|
762
|
+
|
|
763
|
+
const result = await server.shutdown({
|
|
764
|
+
gracePeriodMs: 30_000,
|
|
765
|
+
retryAfterSeconds: 5,
|
|
766
|
+
signal: shutdownController.signal,
|
|
767
|
+
})
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
The first call closes HTTP and Socket.IO admission, drains accepted application
|
|
771
|
+
requests, closes realtime transports and stops the runtime within one total
|
|
772
|
+
budget. Repeated calls return the same Promise; the first options win. New
|
|
773
|
+
ordinary HTTP work receives `503`, `Retry-After` and `Connection: close` outside
|
|
774
|
+
`wrapFetch`. `result.outcome` is `clean` or `forced`; a forced result preserves
|
|
775
|
+
the pending snapshot and reason while final pending counters describe the
|
|
776
|
+
post-close transport state. `runtime` is a diagnostics escape hatch, not a
|
|
777
|
+
second canonical stop path.
|
|
751
778
|
|
|
752
779
|
### Trusted HTTPS in development
|
|
753
780
|
|
|
@@ -1561,6 +1588,15 @@ attempt, so the default `2` permits at most three total attempts. The default
|
|
|
1561
1588
|
`statusCodes: []` does not replay HTTP responses; explicit `methods` or
|
|
1562
1589
|
`statusCodes` expand that policy when a project has a proven idempotent case.
|
|
1563
1590
|
|
|
1591
|
+
Inside Next.js 16 server rendering, the first attempt still uses Next's normal
|
|
1592
|
+
request memoization. If Ky authorizes a retry after a network rejection,
|
|
1593
|
+
Stitchkit passes that retry's current `Request.signal` in the second fetch
|
|
1594
|
+
argument and materializes the current Request as URL + init. Next 16.3 otherwise
|
|
1595
|
+
merges `init` into a Request before its dedupe layer and loses the explicit
|
|
1596
|
+
signal opt-out. The retry therefore performs a new network attempt instead of
|
|
1597
|
+
returning the cached rejection. This adapter does not broaden retry policy: POST, unconfigured HTTP
|
|
1598
|
+
statuses, cancellation and exhausted budgets retain the rules above.
|
|
1599
|
+
|
|
1564
1600
|
## `createClient`
|
|
1565
1601
|
|
|
1566
1602
|
```ts
|
|
@@ -3103,13 +3139,12 @@ realtime.onConnection(({ raw, events, to }) => {
|
|
|
3103
3139
|
})
|
|
3104
3140
|
```
|
|
3105
3141
|
|
|
3106
|
-
|
|
3142
|
+
Pass the full handle to `createServer`; it mounts and owns the transport once:
|
|
3107
3143
|
|
|
3108
3144
|
```ts
|
|
3109
3145
|
createServer({
|
|
3110
3146
|
services,
|
|
3111
|
-
|
|
3112
|
-
rawRoutes: [socket.route], // ready-made /socket.io/*socketPath route
|
|
3147
|
+
socket,
|
|
3113
3148
|
})
|
|
3114
3149
|
|
|
3115
3150
|
// elsewhere — validated broadcast:
|
|
@@ -3129,11 +3164,15 @@ export function publishExampleNote(realtime: ExampleRealtimePublisher): void {
|
|
|
3129
3164
|
| Handle field | Purpose |
|
|
3130
3165
|
|--------------|---------|
|
|
3131
3166
|
| `io` | raw Socket.IO server for middleware, handshake auth and transport ownership |
|
|
3132
|
-
| `websocket` | Bun
|
|
3133
|
-
| `route` |
|
|
3134
|
-
|
|
3135
|
-
`
|
|
3136
|
-
|
|
3167
|
+
| `websocket` | Bun handlers used directly only in explicit raw-lane composition |
|
|
3168
|
+
| `route` | `/socket.io/*socketPath`, mounted automatically by `createServer({ socket })` |
|
|
3169
|
+
| `close()` | idempotent standalone close for CLI/tools with no HTTP server |
|
|
3170
|
+
| `beginShutdown()` / `connections()` | lifecycle surface consumed by the managed server |
|
|
3171
|
+
|
|
3172
|
+
`SocketIOServerConfig` also takes `path`, `transports`, `pingTimeout`,
|
|
3173
|
+
`pingInterval` and a runtime-neutral `allowRequest(Request)` handshake policy.
|
|
3174
|
+
The policy is composed with managed-shutdown admission on both Bun and Node.
|
|
3175
|
+
For anything else socket.io's `ServerOptions` exposes, use the
|
|
3137
3176
|
typed **`serverOptions`** passthrough — most often `maxHttpBufferSize` to lift the
|
|
3138
3177
|
1 MB default for large emits:
|
|
3139
3178
|
|
|
@@ -3144,7 +3183,8 @@ await createSocketIOServer({
|
|
|
3144
3183
|
})
|
|
3145
3184
|
```
|
|
3146
3185
|
|
|
3147
|
-
The wrapper-owned fields (`cors` / `path` / `transports` / `ping*`
|
|
3186
|
+
The wrapper-owned fields (`cors` / `path` / `transports` / `ping*` /
|
|
3187
|
+
`allowRequest`) take
|
|
3148
3188
|
precedence over the same keys in `serverOptions`. On Bun the engine-level options
|
|
3149
3189
|
(`maxHttpBufferSize`, the ping heartbeat, `upgradeTimeout`) are forwarded to
|
|
3150
3190
|
`@socket.io/bun-engine` too — so a configured `maxHttpBufferSize` actually applies
|
|
@@ -3441,8 +3481,9 @@ const websocket = composeWebSocketHandlers(
|
|
|
3441
3481
|
|
|
3442
3482
|
createServer({
|
|
3443
3483
|
services,
|
|
3484
|
+
socket,
|
|
3444
3485
|
websocket,
|
|
3445
|
-
rawRoutes: [
|
|
3486
|
+
rawRoutes: [pcmRoute],
|
|
3446
3487
|
})
|
|
3447
3488
|
```
|
|
3448
3489
|
|
|
@@ -4590,8 +4631,34 @@ createServer({
|
|
|
4590
4631
|
})
|
|
4591
4632
|
```
|
|
4592
4633
|
|
|
4593
|
-
|
|
4594
|
-
|
|
4634
|
+
Keep the managed handle and wire process policy explicitly:
|
|
4635
|
+
|
|
4636
|
+
```ts
|
|
4637
|
+
const server = createServer({ services, socket })
|
|
4638
|
+
const force = new AbortController()
|
|
4639
|
+
let closing: Promise<void> | undefined
|
|
4640
|
+
|
|
4641
|
+
function shutdown() {
|
|
4642
|
+
if (closing) {
|
|
4643
|
+
force.abort() // a later signal shortens the same shutdown, not a second chain
|
|
4644
|
+
return closing
|
|
4645
|
+
}
|
|
4646
|
+
closing = server.shutdown({ gracePeriodMs: 30_000, signal: force.signal }).then(async result => {
|
|
4647
|
+
await mcp.close()
|
|
4648
|
+
await prisma.$disconnect()
|
|
4649
|
+
console.log(result)
|
|
4650
|
+
})
|
|
4651
|
+
return closing
|
|
4652
|
+
}
|
|
4653
|
+
|
|
4654
|
+
process.on('SIGTERM', () => void shutdown())
|
|
4655
|
+
process.on('SIGINT', () => void shutdown())
|
|
4656
|
+
```
|
|
4657
|
+
|
|
4658
|
+
The server owns HTTP/Socket.IO transport resources. MCP, databases, queues and
|
|
4659
|
+
domain run-state remain application resources and close explicitly after server
|
|
4660
|
+
drain. Do not call `runtime.stop()` or `socket.io.close()` in parallel with
|
|
4661
|
+
`shutdown()`.
|
|
4595
4662
|
|
|
4596
4663
|
### Deploy on Node
|
|
4597
4664
|
|
|
@@ -4602,10 +4669,13 @@ the listener differs: replace `createServer` with **`serveNode`** (from
|
|
|
4602
4669
|
```ts
|
|
4603
4670
|
import { serveNode } from 'stitchkit/node'
|
|
4604
4671
|
|
|
4605
|
-
serveNode({
|
|
4672
|
+
const server = await serveNode({
|
|
4606
4673
|
services,
|
|
4674
|
+
socket,
|
|
4607
4675
|
port: Number(process.env.PORT ?? 3000),
|
|
4608
4676
|
})
|
|
4677
|
+
|
|
4678
|
+
await server.shutdown({ gracePeriodMs: 30_000 })
|
|
4609
4679
|
```
|
|
4610
4680
|
|
|
4611
4681
|
Notes for a Node host:
|
|
@@ -4614,9 +4684,6 @@ Notes for a Node host:
|
|
|
4614
4684
|
raw routes use `RawRoute<TServer = unknown>`; supply a host server generic
|
|
4615
4685
|
only when an embedding adapter passes one to `createHandler`.
|
|
4616
4686
|
|
|
4617
|
-
- Add **`@types/bun`** as a dev dependency — it is an optional peer that types the
|
|
4618
|
-
shared `stitchkit/server` surface (without it `tsc` reports a missing `Bun`
|
|
4619
|
-
namespace).
|
|
4620
4687
|
- **Socket.IO** attaches to the Node HTTP server via `serveNode({ socket })`, and
|
|
4621
4688
|
on Node the default transport is `['websocket']` — set the client to match
|
|
4622
4689
|
(`transports: ['websocket']`). See [realtime](./realtime.md).
|
|
@@ -5626,9 +5693,68 @@ socket.io
|
|
|
5626
5693
|
socket.attach(nodeHttpServer)
|
|
5627
5694
|
```
|
|
5628
5695
|
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5696
|
+
Node consumers can remove `@types/bun` unless another dependency independently
|
|
5697
|
+
requires it.
|
|
5698
|
+
|
|
5699
|
+
### Managed server shutdown
|
|
5700
|
+
|
|
5701
|
+
`createServer()` and `serveNode()` now return the same structural managed
|
|
5702
|
+
lifecycle. Replace every direct runtime stop and parallel Socket.IO close:
|
|
5703
|
+
|
|
5704
|
+
```ts
|
|
5705
|
+
// before — split ownership
|
|
5706
|
+
const socket = await createSocketIOServer(config)
|
|
5707
|
+
const server = createServer({
|
|
5708
|
+
services,
|
|
5709
|
+
websocket: socket.websocket,
|
|
5710
|
+
rawRoutes: [socket.route],
|
|
5711
|
+
})
|
|
5712
|
+
server.stop()
|
|
5713
|
+
await socket.io.close()
|
|
5714
|
+
|
|
5715
|
+
// after — one owner and one total deadline
|
|
5716
|
+
const socket = await createSocketIOServer(config)
|
|
5717
|
+
const server = createServer({ services, socket })
|
|
5718
|
+
const result = await server.shutdown({ gracePeriodMs: 30_000 })
|
|
5719
|
+
```
|
|
5720
|
+
|
|
5721
|
+
On Node, keep the same `socket` field and replace `handle.close()` with
|
|
5722
|
+
`handle.shutdown()`. Runtime-specific diagnostics move under `handle.runtime`;
|
|
5723
|
+
do not use it as a second shutdown path. Standalone CLI/tools that create a
|
|
5724
|
+
Socket.IO handle without an HTTP server call `await socket.close()`.
|
|
5725
|
+
|
|
5726
|
+
If Bun Socket.IO shares the port with a raw lane, keep the explicit composition
|
|
5727
|
+
but let the server mount the Socket.IO route:
|
|
5728
|
+
|
|
5729
|
+
```ts
|
|
5730
|
+
createServer({
|
|
5731
|
+
services,
|
|
5732
|
+
socket,
|
|
5733
|
+
websocket: composeWebSocketHandlers([
|
|
5734
|
+
webSocketLane({ match: isRaw, handlers: rawHandlers }),
|
|
5735
|
+
socketIoLane(socket.websocket),
|
|
5736
|
+
]),
|
|
5737
|
+
rawRoutes: [rawUpgradeRoute],
|
|
5738
|
+
})
|
|
5739
|
+
```
|
|
5740
|
+
|
|
5741
|
+
Move native Bun `routes` entries to `rawRoutes`. Native routes run before the
|
|
5742
|
+
Fetch handler and therefore cannot participate in admission or drain. Wire
|
|
5743
|
+
`SIGTERM`/`SIGINT` in the application; the first signal starts `shutdown()`, and
|
|
5744
|
+
a later signal may abort the same controller. Close MCP, databases and queues
|
|
5745
|
+
after the server result—those resources remain application-owned.
|
|
5746
|
+
|
|
5747
|
+
Move a handshake policy from the Node-only callback shape inside
|
|
5748
|
+
`serverOptions` to the runtime-neutral top-level policy. It receives a Web
|
|
5749
|
+
`Request`, may be async, and returns whether to admit the handshake:
|
|
5750
|
+
|
|
5751
|
+
```ts
|
|
5752
|
+
// before
|
|
5753
|
+
serverOptions: { allowRequest: (request, done) => done(null, allowed(request)) }
|
|
5754
|
+
|
|
5755
|
+
// after
|
|
5756
|
+
allowRequest: (request) => allowed(request)
|
|
5757
|
+
```
|
|
5632
5758
|
|
|
5633
5759
|
## Your handlers may be returning more than the contract declares
|
|
5634
5760
|
|
|
@@ -5718,7 +5844,7 @@ The browser-and-server entrypoint. Re-exports everything from
|
|
|
5718
5844
|
| `ContractClientConfig` | _type_ | per-tenant / resource-scoped client config — dynamic `pathPrefix` + `stripPrefixKeys` ([guide](../guide/client.md#contractclientconfig--per-tenant--resource-scoped-clients)) |
|
|
5719
5845
|
| `contractEndpointMatchers` | function | compile exact pathname matchers for selected HTTP contract operations and expected-401 policy |
|
|
5720
5846
|
| `PathPrefixArgs` | _type_ | required string-valued keys exposed to a typed dynamic `pathPrefix` callback |
|
|
5721
|
-
| `createHttpClient` | function | the Ky-based HTTP transport — [guide](../guide/client.md#createhttpclient) |
|
|
5847
|
+
| `createHttpClient` | function | the Ky-based HTTP transport; on Next.js SSR its first attempt stays request-memoizable while every retry is a distinct transport attempt — [guide](../guide/client.md#createhttpclient) |
|
|
5722
5848
|
| `ApiError` | class | a non-2xx response, with `code` / `status` / `details` / `hint` and optional readonly `traceId` from `x-request-id` |
|
|
5723
5849
|
| `HttpClient` | _type_ | the transport interface `createClient` builds on |
|
|
5724
5850
|
| `ConfiguredHttpClient` | _type_ | a framework-created `HttpClient` carrying its readonly `baseUrl` for URL builders |
|
|
@@ -5897,6 +6023,12 @@ Also re-exports the error helpers from `stitchkit/contract`.
|
|
|
5897
6023
|
| `parseBody` | function | parse + Zod-validate a JSON body → `data` or `null` (no throw) |
|
|
5898
6024
|
| `HandlerConfig` | _type_ | config for `createHandler`, including optional `maxJsonBodyBytes`; bound to `BunServer` on this entrypoint |
|
|
5899
6025
|
| `BunServerConfig` | _type_ | config for `createServer` (Bun) |
|
|
6026
|
+
| `BunServerHandle` | _type_ | managed Bun handle (`url`, `port`, `runtime`, `status`, `shutdown`) |
|
|
6027
|
+
| `ManagedServerHandle` | _type_ | shared lifecycle shape generic over the runtime escape hatch |
|
|
6028
|
+
| `ShutdownOptionsSchema` / `ShutdownOptions` | schema / _type_ | one grace budget, retry hint and optional external abort signal |
|
|
6029
|
+
| `ShutdownStatusSchema` / `ShutdownStatus` | schema / _type_ | live state and request/WebSocket counters |
|
|
6030
|
+
| `ShutdownResultSchema` / `ShutdownResult` | schema / _type_ | clean/forced result with final counters and at-force snapshots |
|
|
6031
|
+
| `ShutdownStateSchema` / `ShutdownState` | schema / _type_ | managed lifecycle state machine |
|
|
5900
6032
|
| `ServiceDef` | _type_ | the result of `implement` |
|
|
5901
6033
|
| `MethodDef` | _type_ | one resolved endpoint inside a service |
|
|
5902
6034
|
| `OperationIdentity` | _type_ | path-free service/action/scope/method identity shared by contract and native tool operations |
|
|
@@ -5962,8 +6094,10 @@ Also re-exports the error helpers from `stitchkit/contract`.
|
|
|
5962
6094
|
| `RealtimeServer` | _type_ | validated broadcast and connection API inferred from a realtime contract |
|
|
5963
6095
|
| `RealtimeServerConnection` | _type_ | one validated connection with raw socket access for auth and rooms |
|
|
5964
6096
|
| `RealtimeServerHandle` | _type_ | minimal Socket.IO server handle accepted by `bindRealtimeServer` |
|
|
6097
|
+
| `SocketIORequestPolicy` | _type_ | runtime-neutral async-capable Web `Request` handshake admission policy |
|
|
5965
6098
|
| `SocketIOServerConfig` | _type_ | config for `createSocketIOServer` |
|
|
5966
|
-
| `SocketIOServerHandle` | _type_ |
|
|
6099
|
+
| `SocketIOServerHandle` | _type_ | typed Socket.IO server plus Bun mount fields and idempotent lifecycle |
|
|
6100
|
+
| `SocketIOServerLifecycle` | _type_ | non-generic Bun mount/shutdown portion accepted by `createServer` |
|
|
5967
6101
|
| `composeWebSocketHandlers` | function | compose one Bun `websocket` from N lanes — a raw binary lane beside Socket.IO ([guide](../guide/realtime.md#raw-binary-lane-bun)) |
|
|
5968
6102
|
| `webSocketLane` | function | a typed, cast-free lane for `composeWebSocketHandlers` |
|
|
5969
6103
|
| `socketIoLane` | function | the Socket.IO catch-all lane for `composeWebSocketHandlers` |
|
|
@@ -6292,9 +6426,11 @@ runtime-agnostic pieces of `stitchkit/server` and the error helpers.
|
|
|
6292
6426
|
| `createSocketIOServer` | function | the typed Node Socket.IO server (`io` + `attach`; no Bun engine declarations) |
|
|
6293
6427
|
| `implement` / `createImplement` | function | bind a contract to typed handlers (same as `/server`) |
|
|
6294
6428
|
| `NodeServerConfig` | _type_ | config for `serveNode` |
|
|
6295
|
-
| `NodeServerHandle` | _type_ |
|
|
6429
|
+
| `NodeServerHandle` | _type_ | managed Node handle (`url`, `port`, `runtime`, `status`, `shutdown`) |
|
|
6430
|
+
| `NodeRuntimeServer` | _type_ | concrete `srvx/node` runtime escape hatch |
|
|
6431
|
+
| `NodeSocketLifecycle` | _type_ | Bun-free Socket.IO lifecycle accepted by `serveNode` |
|
|
6296
6432
|
| `HandlerConfig` / `ServiceDef` / `RawRoute` / `RawRouteContext` | _type_ | runtime-neutral handler types; raw routes default their host server to `unknown` |
|
|
6297
|
-
| `SocketIOServerConfig` / `SocketIOServerHandle` | _type_ |
|
|
6433
|
+
| `SocketIORequestPolicy` / `SocketIOServerConfig` / `SocketIOServerHandle` | _type_ | runtime-neutral handshake policy, config and Bun-free Node handle with `io`, `attach` and lifecycle |
|
|
6298
6434
|
| `AppError` + `appError` / `badRequest` / `unauthorized` / `forbidden` / `notFound` / `conflict` / `rateLimited` | — | error helpers (same as `/contract`) |
|
|
6299
6435
|
|
|
6300
6436
|
---
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stitchkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.49.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",
|
|
@@ -95,6 +95,7 @@
|
|
|
95
95
|
"prepack": "cp ../../README.md ./README.md && bun ../../scripts/gen-llms.ts && bun run build",
|
|
96
96
|
"test": "bun test",
|
|
97
97
|
"smoke:node": "node scripts/node-smoke.mjs",
|
|
98
|
+
"smoke:next-ssr": "node scripts/next-ssr-retry-smoke.mjs",
|
|
98
99
|
"consumer-lane": "bun scripts/consumer-lane/run.mjs",
|
|
99
100
|
"bench:mcp-preparation": "bun scripts/benchmark-mcp-preparation.ts"
|
|
100
101
|
},
|