stitchkit 0.66.1 → 0.68.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-runtime.js +5 -6
- package/dist/application/admission.d.ts +131 -0
- package/dist/application/admission.d.ts.map +1 -0
- package/dist/application/channel.d.ts +106 -0
- package/dist/application/channel.d.ts.map +1 -0
- package/dist/application/graph.d.ts +1 -1
- package/dist/application/graph.d.ts.map +1 -1
- package/dist/application/kernel.d.ts.map +1 -1
- package/dist/application/latest-sink.d.ts.map +1 -1
- package/dist/application/resource.d.ts +72 -1
- package/dist/application/resource.d.ts.map +1 -1
- package/dist/application/server-resource.d.ts +26 -5
- package/dist/application/server-resource.d.ts.map +1 -1
- package/dist/application-grammy.js +2 -2
- package/dist/application-opentelemetry.js +1 -1
- package/dist/application.d.ts +4 -2
- package/dist/application.d.ts.map +1 -1
- package/dist/application.js +408 -24
- package/dist/browser/client.d.ts +2 -3
- package/dist/browser/client.d.ts.map +1 -1
- package/dist/browser/contract-stream.d.ts +5 -0
- package/dist/browser/contract-stream.d.ts.map +1 -0
- package/dist/browser/http.d.ts +3 -0
- package/dist/browser/http.d.ts.map +1 -1
- package/dist/browser/stream.d.ts +4 -1
- package/dist/browser/stream.d.ts.map +1 -1
- package/dist/browser/transport.d.ts +3 -0
- package/dist/browser/transport.d.ts.map +1 -0
- package/dist/cli.js +6 -7
- package/dist/contract/define.d.ts +34 -2
- package/dist/contract/define.d.ts.map +1 -1
- package/dist/contract/errors.d.ts +4 -0
- package/dist/contract/errors.d.ts.map +1 -1
- package/dist/contract/index.d.ts +2 -1
- package/dist/contract/index.d.ts.map +1 -1
- package/dist/contract/index.js +5 -1
- package/dist/contract/stream.d.ts +19 -0
- package/dist/contract/stream.d.ts.map +1 -0
- package/dist/{index-xxye8j3k.js → index-0w9abg87.js} +4 -0
- package/dist/index-2k4yrqkc.js +9 -0
- package/dist/index-69m2278y.js +115 -0
- package/dist/{index-eabpd4tb.js → index-6wzr93cg.js} +21 -2
- package/dist/{index-1pgeyyee.js → index-7pn02bgv.js} +4 -6
- package/dist/index-9ky9hhg3.js +484 -0
- package/dist/{index-wrhf06ak.js → index-avjd67h6.js} +8 -8
- package/dist/{index-413xk7ga.js → index-cq9q73nf.js} +50 -1
- package/dist/{index-6taryy00.js → index-dafax5md.js} +1 -1
- package/dist/{index-s1tywej8.js → index-nymn7wbg.js} +1103 -20
- package/dist/{index-y91zd0ch.js → index-pr0qsmjy.js} +3 -2
- package/dist/index-v5bayx1z.js +138 -0
- package/dist/{index-mfw1pec7.js → index-wqyrvhzz.js} +3 -3
- package/dist/{index-svqhcrrj.js → index-zd4v31v2.js} +156 -10
- package/dist/{index-x8v8b7rc.js → index-zpyj7hsv.js} +330 -58
- package/dist/index.js +270 -105
- package/dist/internal/bounded-lines.d.ts +4 -0
- package/dist/internal/bounded-lines.d.ts.map +1 -0
- package/dist/node.d.ts +1 -0
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +11 -8
- package/dist/observability/index.js +6 -7
- package/dist/remote.js +5 -6
- package/dist/server/contract-stream.d.ts +5 -0
- package/dist/server/contract-stream.d.ts.map +1 -0
- package/dist/server/create.d.ts.map +1 -1
- package/dist/server/implement.d.ts.map +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +41 -283
- package/dist/server/openapi.d.ts.map +1 -1
- package/dist/server/process-signals.d.ts.map +1 -1
- package/dist/server/stream.d.ts +3 -1
- package/dist/server/stream.d.ts.map +1 -1
- package/dist/server/streaming-route.d.ts +2 -0
- package/dist/server/streaming-route.d.ts.map +1 -1
- package/dist/server/types.d.ts +13 -3
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/unix-client-body.d.ts +4 -0
- package/dist/server/unix-client-body.d.ts.map +1 -0
- package/dist/server/unix-client-bun.d.ts +13 -0
- package/dist/server/unix-client-bun.d.ts.map +1 -0
- package/dist/server/unix-client-error.d.ts +9 -0
- package/dist/server/unix-client-error.d.ts.map +1 -0
- package/dist/server/unix-client.d.ts +25 -0
- package/dist/server/unix-client.d.ts.map +1 -0
- package/dist/testing.js +7 -5
- package/dist/tools.js +14 -16
- package/llms-full.txt +594 -29
- package/package.json +1 -1
- package/dist/index-48ffdxgk.js +0 -6
- package/dist/index-pzyt11ch.js +0 -127
- package/dist/index-tvwcrx2d.js +0 -224
- package/dist/index-wd8g8z6e.js +0 -206
- package/dist/index-yr276yz0.js +0 -6
package/llms-full.txt
CHANGED
|
@@ -58,9 +58,9 @@ own, recorded as an ADR.
|
|
|
58
58
|
| `stitchkit/testing` | tests on Bun or Node | stable | in-process generated clients over a real Fetch handler, plus the store and managed-resource conformance kits |
|
|
59
59
|
| `stitchkit/declaration` | build and deployment tooling (Bun or Node) | evolving | `ProjectDeclarationSchema` — the one machine-readable statement a repository makes about itself |
|
|
60
60
|
| `stitchkit/react` | browser | stable | `createCursorQuery`, `createCacheBridge` |
|
|
61
|
-
| `stitchkit/agent-runtime` | server | evolving<br>_redefined in 9 of the
|
|
61
|
+
| `stitchkit/agent-runtime` | server | evolving<br>_redefined in 9 of the 13 minors since 0.56.2, most recently 0.66.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
|
|
62
62
|
| `stitchkit/agent-runtime/openrouter` | server | evolving | isolated OpenRouter language-model adapter |
|
|
63
|
-
| `stitchkit/application` | server | evolving | managed resource graph, readiness, admission, schedules and bounded shutdown |
|
|
63
|
+
| `stitchkit/application` | server | evolving<br>_redefined in 3 of the 13 minors since 0.56.2, most recently 0.67.0_ | managed resource graph, readiness, admission, schedules and bounded shutdown |
|
|
64
64
|
| `stitchkit/application/grammy` | server | evolving | isolated grammY polling and webhook lifecycle adapters |
|
|
65
65
|
| `stitchkit/application/opentelemetry` | server | evolving | maps application snapshots onto an injected OpenTelemetry `Meter` |
|
|
66
66
|
|
|
@@ -1521,6 +1521,57 @@ focused helper — not a sub-framework.
|
|
|
1521
1521
|
| `createCache()` + `cacheHeaders()` | in-memory TTL cache; `Cache-Control` builder |
|
|
1522
1522
|
| `createEventBus<EventMap>()` | typed in-process pub/sub |
|
|
1523
1523
|
|
|
1524
|
+
### Contract-first streams
|
|
1525
|
+
|
|
1526
|
+
Use an endpoint `stream` descriptor when the item schema and completion belong
|
|
1527
|
+
to the contract, rather than handing an application-owned `Response` to
|
|
1528
|
+
`rawResponse`:
|
|
1529
|
+
|
|
1530
|
+
```ts
|
|
1531
|
+
const Item = z.discriminatedUnion('kind', [
|
|
1532
|
+
z.object({ kind: z.literal('line'), text: z.string() }),
|
|
1533
|
+
z.object({ kind: z.literal('complete'), count: z.number().int() }),
|
|
1534
|
+
])
|
|
1535
|
+
|
|
1536
|
+
const reports = defineContract({ prefix: 'reports' }, {
|
|
1537
|
+
watch: {
|
|
1538
|
+
method: 'GET', path: '/:id/watch', desc: 'Watch one report',
|
|
1539
|
+
params: z.object({ id: z.string() }),
|
|
1540
|
+
stream: {
|
|
1541
|
+
item: Item,
|
|
1542
|
+
format: 'ndjson',
|
|
1543
|
+
maxFrameBytes: 64 * 1024,
|
|
1544
|
+
terminal: z.object({ kind: z.literal('complete') }).loose(),
|
|
1545
|
+
},
|
|
1546
|
+
},
|
|
1547
|
+
})
|
|
1548
|
+
|
|
1549
|
+
const service = implement(reports, {
|
|
1550
|
+
watch: async function* ({ params, signal }) {
|
|
1551
|
+
yield { kind: 'line', text: `starting ${params.id}` }
|
|
1552
|
+
if (signal.aborted) return
|
|
1553
|
+
yield { kind: 'complete', count: 1 }
|
|
1554
|
+
},
|
|
1555
|
+
})
|
|
1556
|
+
```
|
|
1557
|
+
|
|
1558
|
+
The handler return is inferred as `AsyncIterable<z.output<typeof Item>>`; an
|
|
1559
|
+
invalid JavaScript producer is rejected before its value reaches the wire. Data,
|
|
1560
|
+
safe errors and normal end use a framework envelope. The default encoded frame
|
|
1561
|
+
limit is 256 KiB. `format` defaults to `ndjson`; `heartbeatMs` defaults to five
|
|
1562
|
+
seconds; `idleTimeoutSeconds` defaults to disabled; `lifetimeMs` is optional.
|
|
1563
|
+
|
|
1564
|
+
After headers, HTTP status can no longer report failure, so the stream sends a
|
|
1565
|
+
normalized code and never the raw internal exception. Normal producer completion
|
|
1566
|
+
sends an explicit `end`; when `terminal` is declared, at least one item must
|
|
1567
|
+
match it. Request abort and client iterator return abort `signal`, including a
|
|
1568
|
+
source waiting in `next()` or suspended at a yielded item.
|
|
1569
|
+
|
|
1570
|
+
Contract streams are HTTP-only and cannot declare `output`, `rawResponse`,
|
|
1571
|
+
multipart or tool exposure. They do not provide replay, cursors or durable
|
|
1572
|
+
subscriptions. Keep using `streamingRoute` for an application-owned protocol and
|
|
1573
|
+
`rawResponse` for arbitrary response bodies. → ADR 0117.
|
|
1574
|
+
|
|
1524
1575
|
### SSE streaming
|
|
1525
1576
|
|
|
1526
1577
|
`streamSSE` returns a `Response`, so its endpoint declares
|
|
@@ -1834,23 +1885,46 @@ your own `(pathname) => boolean`.
|
|
|
1834
1885
|
|
|
1835
1886
|
### Unix domain sockets
|
|
1836
1887
|
|
|
1837
|
-
The same typed client dials a local daemon's socket file
|
|
1838
|
-
([server side](server.md#local-daemon-over-a-unix-socket)):
|
|
1888
|
+
The same typed client dials a local daemon's socket file through an explicit,
|
|
1889
|
+
owned Bun/Node transport ([server side](server.md#local-daemon-over-a-unix-socket)):
|
|
1839
1890
|
|
|
1840
1891
|
```ts
|
|
1892
|
+
import { createHttpClient } from 'stitchkit'
|
|
1893
|
+
import { createUnixClientTransport } from 'stitchkit/server' // or stitchkit/node
|
|
1894
|
+
|
|
1895
|
+
const transport = createUnixClientTransport({
|
|
1896
|
+
socketPath: '/run/my-daemon.sock',
|
|
1897
|
+
maxRequestBytes: 4 * 1024 * 1024,
|
|
1898
|
+
maxResponseBytes: 16 * 1024 * 1024,
|
|
1899
|
+
})
|
|
1841
1900
|
const http = createHttpClient({
|
|
1842
|
-
baseUrl: 'http://
|
|
1843
|
-
|
|
1901
|
+
baseUrl: 'http://my-daemon', // URL/Host source; never dialled as TCP
|
|
1902
|
+
fetch: transport.fetch,
|
|
1903
|
+
retry: { limit: 0 },
|
|
1844
1904
|
})
|
|
1845
1905
|
const daemon = createClient(daemonContract, http)
|
|
1906
|
+
|
|
1907
|
+
// At application shutdown:
|
|
1908
|
+
await transport.close()
|
|
1846
1909
|
```
|
|
1847
1910
|
|
|
1848
|
-
`baseUrl` stays required
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1911
|
+
`baseUrl` stays required because it supplies the URL and `Host` header. The
|
|
1912
|
+
adapter structurally owns dispatch: relative and absolute redirects use the same
|
|
1913
|
+
socket, and a missing socket cannot fall through to that host over TCP. Defaults
|
|
1914
|
+
are 16 MiB request/response bodies, 64 KiB headers, 30 s to response headers,
|
|
1915
|
+
eight connections and five redirects.
|
|
1916
|
+
|
|
1917
|
+
`UnixClientTransportError` carries a stable `code` and `delivery`:
|
|
1918
|
+
`not-dispatched`, `possibly-dispatched` or `response-received`. Only the first
|
|
1919
|
+
proves that the remote operation did not begin; Stitchkit never silently retries
|
|
1920
|
+
an ambiguous write. Response consumption/cancellation belongs to the operation,
|
|
1921
|
+
and `close()` interrupts active work and destroys owned connections.
|
|
1922
|
+
|
|
1923
|
+
The legacy `createHttpClient({ unix: '/absolute/path' })` spelling remains a
|
|
1924
|
+
Bun-only convenience. On a non-Bun runtime it now refuses before dispatch
|
|
1925
|
+
instead of ignoring the selection and dialing TCP. `unix` and an injected
|
|
1926
|
+
`fetch` are mutually exclusive; use `createUnixClientTransport` for portable,
|
|
1927
|
+
explicit lifecycle ownership. → ADR 0116.
|
|
1854
1928
|
|
|
1855
1929
|
`trace: true` mints a fresh root trace per request. The stitchkit server
|
|
1856
1930
|
[continues an inbound `traceparent`](./observability.md#trace-context), so the
|
|
@@ -2218,6 +2292,41 @@ sends one. The result keeps the full `react-query-kit` surface (`.getKey()`,
|
|
|
2218
2292
|
`useSuspenseInfiniteQuery`, every option). The endpoint must return the
|
|
2219
2293
|
`{ items, nextCursor }` envelope — see [Contracts → pagination](./contracts.md#pagination).
|
|
2220
2294
|
|
|
2295
|
+
## Contract-first streams
|
|
2296
|
+
|
|
2297
|
+
When an endpoint declares `stream`, `createClient` returns a schema-derived
|
|
2298
|
+
owned iterator rather than an untyped `Response`:
|
|
2299
|
+
|
|
2300
|
+
```ts
|
|
2301
|
+
const Progress = z.discriminatedUnion('kind', [
|
|
2302
|
+
z.object({ kind: z.literal('line'), text: z.string() }),
|
|
2303
|
+
z.object({ kind: z.literal('complete'), count: z.number().int() }),
|
|
2304
|
+
])
|
|
2305
|
+
|
|
2306
|
+
const contract = defineContract({ prefix: 'reports' }, {
|
|
2307
|
+
watch: {
|
|
2308
|
+
method: 'GET', path: '/:id/watch', desc: 'Watch one report',
|
|
2309
|
+
params: z.object({ id: z.string() }),
|
|
2310
|
+
stream: {
|
|
2311
|
+
item: Progress,
|
|
2312
|
+
format: 'ndjson', // default; `sse` is also supported
|
|
2313
|
+
maxFrameBytes: 64 * 1024, // default 256 KiB
|
|
2314
|
+
terminal: z.object({ kind: z.literal('complete') }).loose(),
|
|
2315
|
+
},
|
|
2316
|
+
},
|
|
2317
|
+
})
|
|
2318
|
+
|
|
2319
|
+
const stream = await createClient(contract, http).watch({ id: 'r-1' })
|
|
2320
|
+
for await (const item of stream) console.log(item) // inferred from Progress
|
|
2321
|
+
```
|
|
2322
|
+
|
|
2323
|
+
The iterator validates every frame and item. Normal completion requires the
|
|
2324
|
+
wire `end` frame and, when declared, at least one matching terminal item; EOF is
|
|
2325
|
+
`STREAM_TRUNCATED`, and a missing terminal is `STREAM_TERMINAL_MISSING`.
|
|
2326
|
+
`return()`/`break`, caller abort, producer failure and optional `lifetimeMs`
|
|
2327
|
+
converge on the request operation. See the
|
|
2328
|
+
[server half](./server.md#contract-first-streams). → ADR 0117.
|
|
2329
|
+
|
|
2221
2330
|
## SSE
|
|
2222
2331
|
|
|
2223
2332
|
For a streaming endpoint, consume the response with `parseSSE`:
|
|
@@ -2233,7 +2342,9 @@ for await (const event of parseSSE(res)) {
|
|
|
2233
2342
|
|
|
2234
2343
|
The server side is [`streamSSE`](./server.md#sse-streaming), or
|
|
2235
2344
|
[`sseRoute`](./server.md#long-lived-subscriptions) for a subscription that stays
|
|
2236
|
-
open.
|
|
2345
|
+
open. One line is bounded by `maxLineBytes` (default 1 MiB), UTF-8 is decoded
|
|
2346
|
+
strictly and malformed input throws. Supply `onParseError` only when
|
|
2347
|
+
skip-and-report is an explicit application policy.
|
|
2237
2348
|
|
|
2238
2349
|
## NDJSON
|
|
2239
2350
|
|
|
@@ -2265,8 +2376,9 @@ convenience: a long-lived stream must send something while it is idle or
|
|
|
2265
2376
|
intermediaries drop it, and an empty line is the natural pulse for this framing.
|
|
2266
2377
|
Writing the rule down on both sides is what stops it being a verbal agreement —
|
|
2267
2378
|
the server's keep-alive and the reader's skip are one decision with two
|
|
2268
|
-
implementations.
|
|
2269
|
-
|
|
2379
|
+
implementations. One line is bounded by `maxLineBytes` (default 1 MiB), UTF-8 is
|
|
2380
|
+
decoded strictly and malformed input throws. Passing `onParseError` explicitly
|
|
2381
|
+
selects tolerant skip-and-report behaviour.
|
|
2270
2382
|
|
|
2271
2383
|
|
|
2272
2384
|
==============================================================================
|
|
@@ -4243,17 +4355,24 @@ const cleanup = createManagedSchedule({
|
|
|
4243
4355
|
run: ({ signal }) => removeExpiredRecords(signal),
|
|
4244
4356
|
})
|
|
4245
4357
|
|
|
4358
|
+
const http = managedServerResource({
|
|
4359
|
+
id: 'http',
|
|
4360
|
+
dependsOn: [database],
|
|
4361
|
+
// Called during `start`, after `database` is ready — which is how "bind the
|
|
4362
|
+
// port once the database is up" is expressed. Pass an already-running server
|
|
4363
|
+
// instead when the application created it itself.
|
|
4364
|
+
server: () => createServer({ port: env.PORT, services }),
|
|
4365
|
+
})
|
|
4366
|
+
|
|
4246
4367
|
const app = createApplication({
|
|
4247
4368
|
id: 'service',
|
|
4248
|
-
resources: [
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
],
|
|
4369
|
+
resources: [database, http, cleanup],
|
|
4370
|
+
// One number for how long this application may take to stop. `shutdown()`
|
|
4371
|
+
// with no options and the signal path both spend it.
|
|
4372
|
+
shutdown: { gracePeriodMs: 30_000, forceTimeoutMs: 5_000 },
|
|
4253
4373
|
})
|
|
4254
4374
|
|
|
4255
4375
|
const signals = bindProcessSignals(app, {
|
|
4256
|
-
shutdown: { gracePeriodMs: 30_000, forceTimeoutMs: 5_000 },
|
|
4257
4376
|
onComplete: (result) => {
|
|
4258
4377
|
process.exitCode = result.outcome === 'clean' ? 0 : 1
|
|
4259
4378
|
},
|
|
@@ -4263,9 +4382,10 @@ await app.start()
|
|
|
4263
4382
|
```
|
|
4264
4383
|
|
|
4265
4384
|
The exact resource callbacks are typed by their public configuration. The
|
|
4266
|
-
important ownership rule is stable: the application
|
|
4267
|
-
database/server/provider objects
|
|
4268
|
-
lifecycle
|
|
4385
|
+
important ownership rule is stable: the application decides what the
|
|
4386
|
+
database/server/provider objects are and how they are configured; Stitchkit
|
|
4387
|
+
orders their process-local lifecycle and decides *when* they come into
|
|
4388
|
+
existence.
|
|
4269
4389
|
|
|
4270
4390
|
## Resource authoring
|
|
4271
4391
|
|
|
@@ -4304,6 +4424,40 @@ Invoking `start` makes the descriptor rollback-eligible immediately. Its
|
|
|
4304
4424
|
before returning a runtime handle. The kernel calls cleanup once and continues
|
|
4305
4425
|
cleaning other attempted resources even if one close fails.
|
|
4306
4426
|
|
|
4427
|
+
### Handing a resource to the resources that depend on it
|
|
4428
|
+
|
|
4429
|
+
`dependsOn` says *when*. To say *what*, return a `value` from `start` and read
|
|
4430
|
+
it with `context.use(...)`:
|
|
4431
|
+
|
|
4432
|
+
```ts
|
|
4433
|
+
const database = defineManagedResource({
|
|
4434
|
+
id: 'database',
|
|
4435
|
+
start: async ({ signal }) => ({ value: await connect(env.DATABASE_URL, signal) }),
|
|
4436
|
+
})
|
|
4437
|
+
|
|
4438
|
+
const worker = defineManagedResource({
|
|
4439
|
+
id: 'worker',
|
|
4440
|
+
dependsOn: [database],
|
|
4441
|
+
start: (context) => {
|
|
4442
|
+
const db = context.use(database) // Connection — not Connection | null
|
|
4443
|
+
return { completion: consume(db, context.signal) }
|
|
4444
|
+
},
|
|
4445
|
+
})
|
|
4446
|
+
```
|
|
4447
|
+
|
|
4448
|
+
Declare the dependency with the **resource**, not its id, whenever you intend to
|
|
4449
|
+
read from it: that is the form `use` can type, and it keeps the declaration and
|
|
4450
|
+
the read from drifting apart. A string still works when all you need is order.
|
|
4451
|
+
|
|
4452
|
+
The value is published when `start` resolves and stays readable for the rest of
|
|
4453
|
+
the application's life — from `activate` and from the shutdown phases too, where
|
|
4454
|
+
a dependant may still need the handle it was given.
|
|
4455
|
+
|
|
4456
|
+
`use` refuses two things, both loudly: a resource that was never declared in
|
|
4457
|
+
`dependsOn` (it happens to work whenever declaration order is lucky), and a
|
|
4458
|
+
resource that published nothing. The second is refused by the compiler as well —
|
|
4459
|
+
reading a value off a resource with no `value` in its `start` does not type.
|
|
4460
|
+
|
|
4307
4461
|
## Readiness and health
|
|
4308
4462
|
|
|
4309
4463
|
`await app.start()` resolves only after every required resource is ready and
|
|
@@ -4488,6 +4642,69 @@ try {
|
|
|
4488
4642
|
`release()` is idempotent. Admission and counter increment are atomic, so work
|
|
4489
4643
|
cannot slip between the shutdown check and drain accounting.
|
|
4490
4644
|
|
|
4645
|
+
### Bounded operation admission
|
|
4646
|
+
|
|
4647
|
+
Compose `createBoundedAdmission` when accepted work also competes for a finite
|
|
4648
|
+
process-local resource:
|
|
4649
|
+
|
|
4650
|
+
```ts
|
|
4651
|
+
const generations = createBoundedAdmission({
|
|
4652
|
+
upstream: app.admission,
|
|
4653
|
+
policy: {
|
|
4654
|
+
global: { maxConcurrent: 8, rate: { limit: 120, intervalMs: 60_000 } },
|
|
4655
|
+
perKey: { maxConcurrent: 1, maxKeys: 2_000 },
|
|
4656
|
+
},
|
|
4657
|
+
})
|
|
4658
|
+
|
|
4659
|
+
await generations.run(accountId, ({ signal }) => generate({ signal }), {
|
|
4660
|
+
signal: request.signal,
|
|
4661
|
+
timeoutMs: 30_000,
|
|
4662
|
+
})
|
|
4663
|
+
```
|
|
4664
|
+
|
|
4665
|
+
Acquisition is no-queue and atomic across every configured budget. Refusal names
|
|
4666
|
+
the exact bound; only a rate refusal carries `retryAfterMs`. `maxKeys` keeps the
|
|
4667
|
+
per-key registry finite, and expired idle entries are retired.
|
|
4668
|
+
|
|
4669
|
+
The caller timeout is a wait budget, not proof that the resource stopped. It
|
|
4670
|
+
aborts the signal and settles the caller, but the lease remains active until the
|
|
4671
|
+
underlying Promise actually settles. `drain()` therefore reports real work;
|
|
4672
|
+
`force()` closes admission and reports remaining work without claiming to have
|
|
4673
|
+
terminated it. → ADR 0118.
|
|
4674
|
+
|
|
4675
|
+
### Bounded delivery and byte credit
|
|
4676
|
+
|
|
4677
|
+
`createBoundedChannel` is for one asynchronous reader when an event bus is not a
|
|
4678
|
+
queue:
|
|
4679
|
+
|
|
4680
|
+
```ts
|
|
4681
|
+
const output = createBoundedChannel<string>({
|
|
4682
|
+
policy: 'ordered',
|
|
4683
|
+
maxItems: 64,
|
|
4684
|
+
maxBytes: 256 * 1024,
|
|
4685
|
+
sizeOf: (value) => new TextEncoder().encode(value).byteLength,
|
|
4686
|
+
})
|
|
4687
|
+
|
|
4688
|
+
const progress = createBoundedChannel<{ revision: number }>({
|
|
4689
|
+
policy: 'latest',
|
|
4690
|
+
maxItems: 1,
|
|
4691
|
+
maxBytes: 128,
|
|
4692
|
+
sizeOf: () => 128,
|
|
4693
|
+
})
|
|
4694
|
+
```
|
|
4695
|
+
|
|
4696
|
+
`ordered` never overwrites accepted values; overflow is a reasoned refusal.
|
|
4697
|
+
`latest` retains exactly one pending replaceable value and reports
|
|
4698
|
+
`coalesced`. Offers never create a hidden writer queue, and only one `next()` may
|
|
4699
|
+
wait. Close chooses `drain` (default) or `discard`; abort discards; failure
|
|
4700
|
+
rejects the parked and all later reads.
|
|
4701
|
+
|
|
4702
|
+
`createCreditWindow({ capacityBytes })` is the smaller primitive for a protocol
|
|
4703
|
+
that already owns its queue but needs exact byte permission. Each credit lease
|
|
4704
|
+
replenishes once; it is flow-control credit, not a durable acknowledgement. The
|
|
4705
|
+
application snapshot sink now shares the same latest-value mechanics without
|
|
4706
|
+
changing its revision or status contract. → ADR 0119.
|
|
4707
|
+
|
|
4491
4708
|
Shutdown performs one phase barrier at a time: stop admission everywhere,
|
|
4492
4709
|
cancel future schedules, drain admitted work, then close in reverse stable
|
|
4493
4710
|
topological order. Every hook shares the same grace deadline. Forced cleanup
|
|
@@ -4498,6 +4715,15 @@ Do not add a second `process.on('SIGTERM')` handler around the application.
|
|
|
4498
4715
|
`bindProcessSignals(app)` is the force/escalation owner. Exit code and hard-exit
|
|
4499
4716
|
policy remain application/supervisor choices.
|
|
4500
4717
|
|
|
4718
|
+
**Where the budget comes from on the signal path.** `bindProcessSignals(app)`
|
|
4719
|
+
with no `shutdown` forwards nothing, so the application spends the budget it
|
|
4720
|
+
declared in `createApplication({ shutdown })`. Pass `shutdown` to the binding
|
|
4721
|
+
only to override that budget for signals specifically; passing one key overrides
|
|
4722
|
+
that key alone and leaves the other at the declaration. Declare the budget once,
|
|
4723
|
+
on the application — repeating it in both places is how the two numbers start to
|
|
4724
|
+
disagree, and the operator's supervisor timeout is calculated from the one they
|
|
4725
|
+
can read.
|
|
4726
|
+
|
|
4501
4727
|
## Managed schedules
|
|
4502
4728
|
|
|
4503
4729
|
Schedules activate only after top-level readiness:
|
|
@@ -4753,6 +4979,115 @@ asynchronous subscriber callback racing cleanup: publish `getSnapshot()` before
|
|
|
4753
4979
|
closing the outer sink, so any older or duplicate late delivery is rejected as
|
|
4754
4980
|
stale instead of dropping the final state.
|
|
4755
4981
|
|
|
4982
|
+
## Bound a handler and a local worker with one lease policy
|
|
4983
|
+
|
|
4984
|
+
Use a bounded admission when two entry paths consume the same finite local
|
|
4985
|
+
capacity. Composing it with `application.admission` keeps readiness and shutdown
|
|
4986
|
+
as the upstream gate:
|
|
4987
|
+
|
|
4988
|
+
```ts
|
|
4989
|
+
const work = createBoundedAdmission({
|
|
4990
|
+
upstream: app.admission,
|
|
4991
|
+
policy: {
|
|
4992
|
+
global: { maxConcurrent: 4 },
|
|
4993
|
+
perKey: { maxConcurrent: 1, maxKeys: 1_000 },
|
|
4994
|
+
},
|
|
4995
|
+
})
|
|
4996
|
+
|
|
4997
|
+
const fromHttp = (key: string, signal: AbortSignal) =>
|
|
4998
|
+
work.run(key, (context) => render(context.signal), { signal, timeoutMs: 20_000 })
|
|
4999
|
+
|
|
5000
|
+
const fromWorker = (key: string) =>
|
|
5001
|
+
work.run(key, (context) => reconcile(context.signal))
|
|
5002
|
+
```
|
|
5003
|
+
|
|
5004
|
+
A timeout in `fromHttp` does not free a permit while `render` remains active.
|
|
5005
|
+
At shutdown call `work.stopAdmission()` with the other admission owners and
|
|
5006
|
+
await `work.drain(...)`; the result reports the actual remainder.
|
|
5007
|
+
|
|
5008
|
+
## Replace ad-hoc output and progress queues
|
|
5009
|
+
|
|
5010
|
+
Ordered output and replaceable progress are separate declarations:
|
|
5011
|
+
|
|
5012
|
+
```ts
|
|
5013
|
+
const lines = createBoundedChannel<string>({
|
|
5014
|
+
policy: 'ordered', maxItems: 100, maxBytes: 1_000_000,
|
|
5015
|
+
sizeOf: (line) => new TextEncoder().encode(line).byteLength,
|
|
5016
|
+
})
|
|
5017
|
+
|
|
5018
|
+
const state = createBoundedChannel<{ revision: number; percent: number }>({
|
|
5019
|
+
policy: 'latest', maxItems: 1, maxBytes: 64,
|
|
5020
|
+
sizeOf: () => 64,
|
|
5021
|
+
})
|
|
5022
|
+
|
|
5023
|
+
const lineResult = lines.offer('one durable-in-process ordering unit')
|
|
5024
|
+
const stateResult = state.offer({ revision: 2, percent: 50 })
|
|
5025
|
+
```
|
|
5026
|
+
|
|
5027
|
+
Handle `refused` from `lines` at the protocol boundary; do not turn it into
|
|
5028
|
+
implicit loss. A `coalesced` state result is expected latest-value behaviour,
|
|
5029
|
+
not evidence that an ordered event was delivered. Neither channel is durable;
|
|
5030
|
+
persist first when restart replay is required.
|
|
5031
|
+
|
|
5032
|
+
## Handing a handle to the resources that depend on it
|
|
5033
|
+
|
|
5034
|
+
`dependsOn` carries ordering. To carry the object as well, return a `value` from
|
|
5035
|
+
`start` and read it with `context.use(...)`. This replaces the module-local
|
|
5036
|
+
`let handle: T | null` with the guard the graph makes unreachable.
|
|
5037
|
+
|
|
5038
|
+
```ts
|
|
5039
|
+
const database = defineManagedResource({
|
|
5040
|
+
id: 'database',
|
|
5041
|
+
start: async () => ({ value: await connect(env.DATABASE_URL) }),
|
|
5042
|
+
})
|
|
5043
|
+
|
|
5044
|
+
const worker = defineManagedResource({
|
|
5045
|
+
id: 'worker',
|
|
5046
|
+
dependsOn: [database],
|
|
5047
|
+
start(context) {
|
|
5048
|
+
const db = context.use(database) // Connection — not Connection | null
|
|
5049
|
+
},
|
|
5050
|
+
})
|
|
5051
|
+
```
|
|
5052
|
+
|
|
5053
|
+
Declare the dependency with the **resource** whenever you intend to read from
|
|
5054
|
+
it: that is the form `use` can type, and it stops the declaration and the read
|
|
5055
|
+
from drifting. A string still expresses order on its own.
|
|
5056
|
+
|
|
5057
|
+
The value is published when `start` resolves and stays readable from `activate`
|
|
5058
|
+
and from the shutdown phases. `use` refuses a resource that was not declared in
|
|
5059
|
+
`dependsOn` — that only ever worked by luck of declaration order — and refuses a
|
|
5060
|
+
resource that published nothing, which the compiler refuses too. The executable
|
|
5061
|
+
recipe proves both the read and the refusal.
|
|
5062
|
+
|
|
5063
|
+
## Managed HTTP server
|
|
5064
|
+
|
|
5065
|
+
The main resource of a web backend, and the one whose ownership rule is
|
|
5066
|
+
counter-intuitive: give `managedServerResource` a **thunk** and it creates the
|
|
5067
|
+
server during `start`, after its dependencies are ready. Give it an
|
|
5068
|
+
already-listening server and it adopts that one instead.
|
|
5069
|
+
|
|
5070
|
+
```ts
|
|
5071
|
+
const http = managedServerResource({
|
|
5072
|
+
id: 'http',
|
|
5073
|
+
dependsOn: [database],
|
|
5074
|
+
server: () => createServer({ port: env.PORT, services }),
|
|
5075
|
+
})
|
|
5076
|
+
|
|
5077
|
+
const app = createApplication({ id: 'service', resources: [database, http] })
|
|
5078
|
+
await app.start() // the port is bound; the snapshot means it
|
|
5079
|
+
```
|
|
5080
|
+
|
|
5081
|
+
The server resource publishes its `ManagedServerHandle`, so anything that needs
|
|
5082
|
+
the running server — a Socket.IO attachment, a URL to log, a probe — reads it
|
|
5083
|
+
with `context.use(http)` rather than through a module-local.
|
|
5084
|
+
|
|
5085
|
+
Do not spread this resource over your own `start` to control creation order:
|
|
5086
|
+
that shape exists only as a workaround for the version whose `start` was empty,
|
|
5087
|
+
and `dependsOn` now expresses the order directly. The executable recipe binds a
|
|
5088
|
+
real port, proves a request reaches it after `start()` resolves, and proves the
|
|
5089
|
+
port is closed after `shutdown()`.
|
|
5090
|
+
|
|
4756
5091
|
## Deletion checklist
|
|
4757
5092
|
|
|
4758
5093
|
After the cutover, remove the old generic lifecycle path completely:
|
|
@@ -7928,6 +8263,186 @@ implement `AgentRuntimeStoreDriver` and compose the aggregate with
|
|
|
7928
8263
|
runtime): bootstrap the server, one HTTP request, and any feature you rely on
|
|
7929
8264
|
(Socket.IO connect, an MCP tool call, a multipart upload, …).
|
|
7930
8265
|
|
|
8266
|
+
## Released migration: 0.68.0
|
|
8267
|
+
|
|
8268
|
+
### Make Unix transport selection explicit outside Bun
|
|
8269
|
+
|
|
8270
|
+
The legacy `unix` option remains a Bun convenience, but on Node or another
|
|
8271
|
+
runtime it now fails before dispatch. Replace it with the owned adapter and close
|
|
8272
|
+
that adapter with the application:
|
|
8273
|
+
|
|
8274
|
+
```ts
|
|
8275
|
+
// before — unsafe outside Bun: an unsupported fetch could dial baseUrl over TCP
|
|
8276
|
+
const http = createHttpClient({ baseUrl, unix: '/run/service.sock' })
|
|
8277
|
+
|
|
8278
|
+
// after — Bun and Node; every dispatch and redirect stays on the socket
|
|
8279
|
+
import { createUnixClientTransport } from 'stitchkit/server' // or stitchkit/node
|
|
8280
|
+
const transport = createUnixClientTransport({ socketPath: '/run/service.sock' })
|
|
8281
|
+
const http = createHttpClient({ baseUrl, fetch: transport.fetch })
|
|
8282
|
+
// during shutdown
|
|
8283
|
+
await transport.close()
|
|
8284
|
+
```
|
|
8285
|
+
|
|
8286
|
+
Do not automatically replay `possibly-dispatched`: a timeout or connection loss
|
|
8287
|
+
after bytes left the process does not prove that a write did not happen.
|
|
8288
|
+
|
|
8289
|
+
### Choose tolerant stream parsing explicitly
|
|
8290
|
+
|
|
8291
|
+
`parseNDJSON` and `parseSSE` now throw on malformed JSON, invalid UTF-8 and an
|
|
8292
|
+
over-limit line. The default line ceiling is 1 MiB. If a feed deliberately skips
|
|
8293
|
+
bad records, retain that policy explicitly:
|
|
8294
|
+
|
|
8295
|
+
```ts
|
|
8296
|
+
// before — malformed input disappeared implicitly
|
|
8297
|
+
parseNDJSON(response)
|
|
8298
|
+
|
|
8299
|
+
// after — ordinary fail-closed path
|
|
8300
|
+
parseNDJSON(response, { maxLineBytes: 256 * 1024 })
|
|
8301
|
+
|
|
8302
|
+
// after — deliberately tolerant path
|
|
8303
|
+
parseNDJSON(response, { onParseError: (raw, error) => report(raw, error) })
|
|
8304
|
+
```
|
|
8305
|
+
|
|
8306
|
+
### Extend exhaustive framework-error handling
|
|
8307
|
+
|
|
8308
|
+
If a switch makes `StitchErrorCode` exhaustive, add
|
|
8309
|
+
`STREAM_ITEM_INVALID`, `STREAM_FRAME_TOO_LARGE`,
|
|
8310
|
+
`STREAM_TERMINAL_MISSING` and `STREAM_LIFETIME_EXCEEDED`. A partial application
|
|
8311
|
+
status map needs no change (ADR 0105).
|
|
8312
|
+
|
|
8313
|
+
The admission/channel APIs and endpoint `stream` descriptor are additive; raw
|
|
8314
|
+
responses and raw `streamingRoute` remain supported.
|
|
8315
|
+
|
|
8316
|
+
## Released migration: 0.67.0
|
|
8317
|
+
|
|
8318
|
+
Three application-kernel changes. Two of them fix silent failures, so the most
|
|
8319
|
+
important part of this migration is not what the compiler points at — it is the
|
|
8320
|
+
two things that change with no compile error at all: **when your server is
|
|
8321
|
+
created**, and **which shutdown budget a signal spends**.
|
|
8322
|
+
|
|
8323
|
+
### If you gave `managedServerResource` a thunk
|
|
8324
|
+
|
|
8325
|
+
It is now called during `start`, after the resource's dependencies are ready —
|
|
8326
|
+
the reading the type always suggested. Before, it was called on the way *down*,
|
|
8327
|
+
so `app.start()` resolved, the snapshot said `healthy` and `ready`, and nothing
|
|
8328
|
+
was bound to the port.
|
|
8329
|
+
|
|
8330
|
+
If your application looked like this, it was never listening:
|
|
8331
|
+
|
|
8332
|
+
```ts
|
|
8333
|
+
// before — resolves clean, listens on nothing
|
|
8334
|
+
managedServerResource({ id: 'http', dependsOn: ['database'], server: () => createServer(config) })
|
|
8335
|
+
```
|
|
8336
|
+
|
|
8337
|
+
Nothing to change: the same code now binds the port. Check your startup logs for
|
|
8338
|
+
a healthy report you never actually verified with a request.
|
|
8339
|
+
|
|
8340
|
+
If you used the spread workaround — this resource's phases over your own
|
|
8341
|
+
`start` — it keeps working, and you can now delete it:
|
|
8342
|
+
|
|
8343
|
+
```ts
|
|
8344
|
+
// before
|
|
8345
|
+
let handle: ManagedServerHandle<T> | null = null
|
|
8346
|
+
const shutdown = managedServerResource({ id: 'http', server: () => handle! })
|
|
8347
|
+
const http = defineManagedResource({
|
|
8348
|
+
...shutdown,
|
|
8349
|
+
dependsOn: ['database', 'socket-io'],
|
|
8350
|
+
start: () => { handle = createServer(config) },
|
|
8351
|
+
})
|
|
8352
|
+
|
|
8353
|
+
// after
|
|
8354
|
+
const http = managedServerResource({
|
|
8355
|
+
id: 'http',
|
|
8356
|
+
dependsOn: [database, socketIo],
|
|
8357
|
+
server: () => createServer(config),
|
|
8358
|
+
})
|
|
8359
|
+
```
|
|
8360
|
+
|
|
8361
|
+
An already-created handle is adopted exactly as before.
|
|
8362
|
+
|
|
8363
|
+
### If you passed `shutdown` to `bindProcessSignals`
|
|
8364
|
+
|
|
8365
|
+
`bindProcessSignals` used to fill in the schema's defaults for every budget you
|
|
8366
|
+
omitted, which made `createApplication({ shutdown })` unreachable on the signal
|
|
8367
|
+
path — the one path production stops through. An application declaring five
|
|
8368
|
+
seconds took thirty-five.
|
|
8369
|
+
|
|
8370
|
+
Declare the budget once, on the application, and delete the repetition:
|
|
8371
|
+
|
|
8372
|
+
```ts
|
|
8373
|
+
// before — the same two numbers in two places, and only one of them applied
|
|
8374
|
+
const app = createApplication({ id: 'svc', resources, shutdown: SHUTDOWN_BUDGET })
|
|
8375
|
+
bindProcessSignals(app, { shutdown: SHUTDOWN_BUDGET })
|
|
8376
|
+
|
|
8377
|
+
// after
|
|
8378
|
+
const app = createApplication({ id: 'svc', resources, shutdown: SHUTDOWN_BUDGET })
|
|
8379
|
+
bindProcessSignals(app)
|
|
8380
|
+
```
|
|
8381
|
+
|
|
8382
|
+
**Check this even if you change nothing.** If you declared a budget on the
|
|
8383
|
+
application and did not repeat it on the binding, your process has been stopping
|
|
8384
|
+
on 30 s / 5 s and will now stop on what you declared. Compare it against the
|
|
8385
|
+
supervisor timeout that watches it — `kill_timeout`, `TimeoutStopSec` — because
|
|
8386
|
+
that number was probably calculated from the declaration.
|
|
8387
|
+
|
|
8388
|
+
Passing `shutdown` to the binding still works and still wins, key by key: one
|
|
8389
|
+
key overrides that key alone and leaves the other at the declaration.
|
|
8390
|
+
|
|
8391
|
+
### If you read `resource.dependsOn`
|
|
8392
|
+
|
|
8393
|
+
Its type widened from `readonly string[]` to `readonly ManagedResourceDependency[]`
|
|
8394
|
+
— `string | ManagedResource` — so a dependency can be declared as the resource
|
|
8395
|
+
itself. Declaring stays compatible; reading needs one call:
|
|
8396
|
+
|
|
8397
|
+
```ts
|
|
8398
|
+
// before
|
|
8399
|
+
const ids: readonly string[] = resource.dependsOn ?? []
|
|
8400
|
+
|
|
8401
|
+
// after
|
|
8402
|
+
import { managedResourceDependencyId } from 'stitchkit/application'
|
|
8403
|
+
const ids = (resource.dependsOn ?? []).map(managedResourceDependencyId)
|
|
8404
|
+
```
|
|
8405
|
+
|
|
8406
|
+
### If you thread a handle through a module-local
|
|
8407
|
+
|
|
8408
|
+
This is the pattern the change exists to remove, and it is opt-in — nothing
|
|
8409
|
+
breaks if you keep yours:
|
|
8410
|
+
|
|
8411
|
+
```ts
|
|
8412
|
+
// before
|
|
8413
|
+
let socket: SocketHandle | null = null
|
|
8414
|
+
const socketIo = defineManagedResource({
|
|
8415
|
+
id: 'socket-io',
|
|
8416
|
+
start: async () => { socket = await createSocketServer(config) },
|
|
8417
|
+
})
|
|
8418
|
+
const http = defineManagedResource({
|
|
8419
|
+
id: 'http',
|
|
8420
|
+
dependsOn: ['socket-io'],
|
|
8421
|
+
start: () => {
|
|
8422
|
+
if (!socket) throw new Error('socket is not initialized') // unreachable
|
|
8423
|
+
server = createServer({ socket })
|
|
8424
|
+
},
|
|
8425
|
+
})
|
|
8426
|
+
|
|
8427
|
+
// after
|
|
8428
|
+
const socketIo = defineManagedResource({
|
|
8429
|
+
id: 'socket-io',
|
|
8430
|
+
start: async () => ({ value: await createSocketServer(config) }),
|
|
8431
|
+
})
|
|
8432
|
+
const http = defineManagedResource({
|
|
8433
|
+
id: 'http',
|
|
8434
|
+
dependsOn: [socketIo],
|
|
8435
|
+
start: (context) => {
|
|
8436
|
+
server = createServer({ socket: context.use(socketIo) }) // SocketHandle
|
|
8437
|
+
},
|
|
8438
|
+
})
|
|
8439
|
+
```
|
|
8440
|
+
|
|
8441
|
+
Declare the dependency with the **resource** when you intend to read from it —
|
|
8442
|
+
that is the form `use` can type. `use` refuses a resource missing from
|
|
8443
|
+
`dependsOn`, and refuses one that published no value; the second refusal is a
|
|
8444
|
+
compile error too.
|
|
8445
|
+
|
|
7931
8446
|
## Released migration: 0.66.0
|
|
7932
8447
|
|
|
7933
8448
|
Three changes, and only one of them is a feature. The other two are shapes that
|
|
@@ -10191,7 +10706,7 @@ The browser-and-server entrypoint. Re-exports everything from
|
|
|
10191
10706
|
| `createUrlBuilders` | function | build one exact URL builder per contract in a registry |
|
|
10192
10707
|
| `UrlBuilderConfig` | _type_ | explicit `{ baseUrl }` source for a URL builder |
|
|
10193
10708
|
| `ClientConfig` | _type_ | config for `createClient`'s bare-fetch mode (2nd arg, no `HttpClient`) |
|
|
10194
|
-
| `ClientFetch` | _type_ | injectable Fetch-compatible transport used by framework
|
|
10709
|
+
| `ClientFetch` | _type_ | injectable Fetch-compatible transport used by framework and application-owned adapters |
|
|
10195
10710
|
| `ClientRequestOptions` | _type_ | per-call `{ signal?: AbortSignal }` passed through an endpoint callable's `.withOptions(...)`; caller abort is distinct from timeout — [guide](../guide/client.md#per-call-cancellation) |
|
|
10196
10711
|
| `ContractClientConfig` | _type_ | per-tenant / resource-scoped client config — dynamic `pathPrefix` + `stripPrefixKeys` ([guide](../guide/client.md#contractclientconfig--per-tenant--resource-scoped-clients)) |
|
|
10197
10712
|
| `contractEndpointMatchers` | function | compile exact pathname matchers for selected HTTP contract operations and expected-401 policy |
|
|
@@ -10200,7 +10715,7 @@ The browser-and-server entrypoint. Re-exports everything from
|
|
|
10200
10715
|
| `ApiError` | class | a non-2xx response, with `code` / `status` / `details` / `hint` and optional readonly `traceId` from `x-request-id` |
|
|
10201
10716
|
| `HttpClient` | _type_ | the transport interface `createClient` builds on |
|
|
10202
10717
|
| `ConfiguredHttpClient` | _type_ | a framework-created `HttpClient` carrying its readonly `baseUrl` for URL builders |
|
|
10203
|
-
| `HttpClientConfig` | _type_ | config for `createHttpClient`; retry `limit` counts retries after the initial attempt (default 2 = at most 3 GET attempts), with `statusCodes: []` by default; `
|
|
10718
|
+
| `HttpClientConfig` | _type_ | config for `createHttpClient`; retry `limit` counts retries after the initial attempt (default 2 = at most 3 GET attempts), with `statusCodes: []` by default; `fetch` installs an explicit transport and is mutually exclusive with the legacy Bun-only `unix` option — [details](../guide/client.md#createhttpclient) |
|
|
10204
10719
|
| `UnauthorizedMatcher` | _type_ | exact `(pathname) => boolean` policy accepted by `suppressUnauthorizedFor` |
|
|
10205
10720
|
| `RequestOptions` | _type_ | per-call options — params, timeout, response type |
|
|
10206
10721
|
| `HeaderProvider` | _type_ | static or per-request headers |
|
|
@@ -10218,6 +10733,8 @@ The browser-and-server entrypoint. Re-exports everything from
|
|
|
10218
10733
|
| `createRetainedTopics` | function | retained last-value store for sticky events — [guide](../guide/realtime.md#sticky-events) |
|
|
10219
10734
|
| `parseSSE` | function | parse an SSE `Response` into an async generator — [guide](../guide/client.md#sse) |
|
|
10220
10735
|
| `parseNDJSON` | function | parse an NDJSON `Response`; blank keep-alive lines are skipped — [guide](../guide/client.md#ndjson) |
|
|
10736
|
+
| `ContractStreamFrameSchema` / `ContractStreamFrame` | schema / _type_ | internal-on-the-wire `data` / safe `error` / `end` envelope of a contract-first stream |
|
|
10737
|
+
| `DEFAULT_CONTRACT_STREAM_FRAME_BYTES` | const | default maximum encoded contract-stream frame: 256 KiB |
|
|
10221
10738
|
| `SocketIOClient` | _type_ | low-level client handle; `emit` reports disconnected drops and `emitWithAck` exposes the native Promise primitive used by validated `request()` |
|
|
10222
10739
|
| `SocketIOClientPeerLoaders` | _type_ | inject `socket.io-client` so a bundler can put it in a self-contained artifact |
|
|
10223
10740
|
| `SocketIOClientConfig` | _type_ | config for `createSocketIOClient` (incl. `retain`, `onConnectError`, `onDroppedEmit`) |
|
|
@@ -10293,6 +10810,7 @@ from the root `stitchkit`.
|
|
|
10293
10810
|
| `ContractDef` | _type_ | a defined contract |
|
|
10294
10811
|
| `ContractMeta` | _type_ | a contract's `prefix` + optional `scope` and `meta` (a default every endpoint shallow-merges over) |
|
|
10295
10812
|
| `EndpointDef` | _type_ | a single endpoint definition; `output` declares JSON response presence (`null` is data, `undefined` is invalid) |
|
|
10813
|
+
| `EndpointStreamDescriptor` | _type_ | HTTP-only schema-derived stream declaration: item schema, NDJSON/SSE framing, frame/lifetime/heartbeat/idle bounds and optional terminal predicate — [guide](../guide/server.md#contract-first-streams) |
|
|
10296
10814
|
| `HeadEndpointDef` | _type_ | explicit HTTP-only, bodyless `HEAD` endpoint definition |
|
|
10297
10815
|
| `EndpointResponseMeta` | _type_ | static success metadata declared by an HTTP-only typed-data endpoint |
|
|
10298
10816
|
| `ResponseMetadata` | _type_ | per-request outbound collector exposed as `ctx.response` only for a `responseMeta` endpoint |
|
|
@@ -10551,6 +11069,12 @@ Also re-exports the error helpers from `stitchkit/contract`.
|
|
|
10551
11069
|
| `StreamingRouteOptions` | _type_ | options for `streamingRoute` / `ndjsonRoute` / `sseRoute` |
|
|
10552
11070
|
| `StreamingSourceContext` | _type_ | what a streaming source is given, including the cancellation `signal` |
|
|
10553
11071
|
| `StreamingFormat` | _type_ | `'ndjson' \| 'sse'` |
|
|
11072
|
+
| `createUnixClientTransport` | function | owned Fetch-compatible Unix-socket transport on Bun and Node; every redirect stays on the socket — [guide](../guide/client.md#unix-domain-sockets) |
|
|
11073
|
+
| `UnixClientTransportConfig` | _type_ | absolute socket path plus request/response/header/connection/redirect bounds |
|
|
11074
|
+
| `UnixClientTransport` | _type_ | `{ fetch, closed, close() }`; `close()` settles owned active work |
|
|
11075
|
+
| `UnixClientTransportError` | class | stable transport failure with `code` and dispatch certainty in `delivery` |
|
|
11076
|
+
| `UnixClientTransportErrorCode` | _type_ | finite Unix transport failure-code union |
|
|
11077
|
+
| `UnixClientDeliveryState` | _type_ | `not-dispatched \| possibly-dispatched \| response-received`; input to application retry policy, never an implicit retry |
|
|
10554
11078
|
|
|
10555
11079
|
### OpenAPI
|
|
10556
11080
|
|
|
@@ -10584,17 +11108,55 @@ cutovers are covered by the executable
|
|
|
10584
11108
|
| `ApplicationShutdownBudgetSchema` / `ApplicationShutdownBudget` | schema / _type_ | the same two budgets without a signal — `ApplicationConfig.shutdown`, the default for `shutdown()` and the only budget a failed startup's rollback can read |
|
|
10585
11109
|
| `ActivityTokenBrand` | const | the brand symbol `ActivityToken` carries, exported so `ActivityProjection` is implementable |
|
|
10586
11110
|
| `defineManagedResource` | function | retain the exact typed resource declaration; every invoked start is rollback-eligible |
|
|
10587
|
-
| `managedServerResource` | function |
|
|
11111
|
+
| `managedServerResource` | function | own a managed server's lifecycle without copying its HTTP/WebSocket shutdown machine — a thunk is called during `start`, and the handle is published to dependants |
|
|
11112
|
+
| `managedResourceDependencyId` | function | the id of a dependency declared either as a string or as the resource itself |
|
|
10588
11113
|
| `createApplicationHealthHandler` | function | build a Fetch-clean liveness or readiness response from the sanitized application snapshot |
|
|
10589
11114
|
| `createApplicationOperationalHandlers` | function | compose always-readable status plus the canonical readiness/liveness handlers |
|
|
10590
11115
|
| `ApplicationAdmissionError` | class | stable `APPLICATION_NOT_ACCEPTING` rejection from `admission.run(...)` |
|
|
10591
11116
|
| `ApplicationConfig` / `ApplicationHandle` | _type_ | application declaration and its start/snapshot/subscription/admission/shutdown handle |
|
|
10592
11117
|
| `ApplicationAdmission` / `ApplicationOperationLease` | _type_ | atomic process-local admission and idempotent release primitive |
|
|
10593
|
-
| `ManagedResource` / `ManagedResourceContext` / `ManagedResourceStartResult` | _type_ | resource lifecycle callbacks, shared deadlines, health reporting
|
|
10594
|
-
| `
|
|
11118
|
+
| `ManagedResource` / `ManagedResourceContext` / `ManagedResourceStartResult` | _type_ | resource lifecycle callbacks, shared deadlines, health reporting, separate readiness/completion promises and the value a resource publishes to its dependants |
|
|
11119
|
+
| `ManagedResourceDependency` | _type_ | a dependency named by id or given as the resource itself — the second form is what `context.use(...)` can type |
|
|
11120
|
+
| `ManagedResourcePublished` | _type_ | the value type `context.use(resource)` returns, recovered from that resource's own `start` |
|
|
11121
|
+
| `ManagedResourcePublishesNoValue` | _type_ | what `context.use(...)` returns for a resource that publishes nothing — a branded refusal rather than `never`, so reading it does not silently compile |
|
|
11122
|
+
| `ManagedServerResourceConfig` | _type_ | the server or a thunk that makes one, stable ID, dependencies and policy for `managedServerResource` |
|
|
11123
|
+
| `ManagedServerResource` | _type_ | the resource `managedServerResource` returns, whose `start` publishes the `ManagedServerHandle` |
|
|
10595
11124
|
| `ApplicationHealthHandlerOptions` / `ApplicationHealthHandlerOptionsSchema` | _type_ / schema | liveness/readiness selection and sanitized `Retry-After` policy |
|
|
10596
11125
|
| `ApplicationOperationalHandlers` / `ApplicationOperationalHandlersOptions` / `ApplicationOperationalHandlersOptionsSchema` | _type_ / schema | conventional status/readiness/liveness route surface and shared retry policy |
|
|
10597
11126
|
|
|
11127
|
+
### Bounded admission
|
|
11128
|
+
|
|
11129
|
+
| Export | Kind | Summary |
|
|
11130
|
+
|--------|------|---------|
|
|
11131
|
+
| `createBoundedAdmission` | function | process-local no-queue global/per-key concurrency and rate leases, optionally composed with application admission |
|
|
11132
|
+
| `BoundedAdmissionPolicySchema` / `BoundedAdmissionPolicy` | schema / _type_ | finite global budget and optional finite per-key budget with `maxKeys` |
|
|
11133
|
+
| `BoundedRateBudgetSchema` / `BoundedRateBudget` | schema / _type_ | `{ limit, intervalMs }` monotonic sliding-window budget |
|
|
11134
|
+
| `BoundedAdmissionStateSchema` / `BoundedAdmissionState` | schema / _type_ | `accepting \| draining \| closed` |
|
|
11135
|
+
| `BoundedAdmissionRefusalReasonSchema` / `BoundedAdmissionRefusalReason` | schema / _type_ | exact local/upstream refusal vocabulary |
|
|
11136
|
+
| `BoundedAdmissionSnapshotSchema` / `BoundedAdmissionSnapshot` | schema / _type_ | absolute active/lifetime/refusal/rate-accounting counters |
|
|
11137
|
+
| `BoundedAdmission` / `BoundedAdmissionConfig` / `BoundedAdmissionClock` | _type_ | handle, policy/upstream/clock configuration and monotonic clock seam |
|
|
11138
|
+
| `BoundedAdmissionResult` / `BoundedAdmissionLeaseResult` / `BoundedAdmissionRefusedResult` | _type_ | explicit leased-or-refused acquisition result; retry time exists only for rate bounds |
|
|
11139
|
+
| `BoundedOperationLease` | _type_ | idempotent release lease, optionally carrying its key |
|
|
11140
|
+
| `BoundedOperationRunContext` / `BoundedOperationRunOptions` | _type_ | underlying work signal and caller abort/timeout wait budget |
|
|
11141
|
+
| `BoundedAdmissionDrainOptions` / `BoundedAdmissionDrainResult` / `BoundedAdmissionForceResult` | _type_ | bounded drain inputs and honest remaining-work results |
|
|
11142
|
+
| `BoundedAdmissionRefusalError` | class | `run()` refusal with reason and optional `retryAfterMs` |
|
|
11143
|
+
| `BoundedOperationWaitError` | class | caller wait ended as `cancelled` or `timed-out`; underlying capacity remains leased until work settles |
|
|
11144
|
+
|
|
11145
|
+
### Bounded delivery
|
|
11146
|
+
|
|
11147
|
+
| Export | Kind | Summary |
|
|
11148
|
+
|--------|------|---------|
|
|
11149
|
+
| `createBoundedChannel` | function | finite single-reader async channel with explicit ordered or latest-value policy |
|
|
11150
|
+
| `BoundedChannelPolicySchema` / `BoundedChannelPolicy` | schema / _type_ | `ordered \| latest` retention policy |
|
|
11151
|
+
| `BoundedChannelStateSchema` / `BoundedChannelState` | schema / _type_ | `open \| draining \| closed \| failed` |
|
|
11152
|
+
| `BoundedChannelSnapshotSchema` / `BoundedChannelSnapshot` | schema / _type_ | exact retained count/bytes, waiter and outcome counters |
|
|
11153
|
+
| `BoundedChannel` / `BoundedChannelConfig` / `BoundedChannelCloseOptions` | _type_ | iterator/offer handle, explicit count/byte/size policy and drain/discard close mode |
|
|
11154
|
+
| `BoundedChannelOfferResult` | _type_ | `delivered`, `queued`, `coalesced`, or reasoned `refused` outcome |
|
|
11155
|
+
| `BoundedChannelReaderError` | class | refusal of a second concurrent pending `next()` |
|
|
11156
|
+
| `createCreditWindow` | function | finite byte-credit lease window with exact once-only replenishment |
|
|
11157
|
+
| `CreditWindow` / `CreditWindowSnapshot` / `CreditWindowSnapshotSchema` | _type_ / schema | byte-credit handle and absolute accounting record |
|
|
11158
|
+
| `CreditAcquireResult` / `CreditLease` | _type_ | reasoned refusal or idempotently releasable byte-credit lease |
|
|
11159
|
+
|
|
10598
11160
|
### Managed schedules
|
|
10599
11161
|
|
|
10600
11162
|
| Export | Kind | Summary |
|
|
@@ -11311,6 +11873,7 @@ runtime-agnostic pieces of `stitchkit/server` and the error helpers.
|
|
|
11311
11873
|
| `serveNode` | function | build the router and start a Node HTTP server (via `srvx`) |
|
|
11312
11874
|
| `createHandler` | function | the router as a bare `(req) => Response` (same as `/server`) |
|
|
11313
11875
|
| `createSocketIOServer` | function | the typed Node Socket.IO server (`io` + `attach`; no Bun engine declarations) |
|
|
11876
|
+
| `createUnixClientTransport` | function | the same fail-closed Bun/Node Unix client adapter exported by `stitchkit/server` |
|
|
11314
11877
|
| `implement` / `createImplement` / `createScopedImplement` / `createScopedImplementRegistry` / `createMultipartStream` | function | bind a contract to typed handlers, optionally typed per endpoint scope (same as `/server`) |
|
|
11315
11878
|
| `NodeServerConfig` | _type_ | config for `serveNode` |
|
|
11316
11879
|
| `NodeServerHandle` | _type_ | managed Node handle (`url`, `port`, `runtime`, `status`, `shutdown`) |
|
|
@@ -11318,6 +11881,8 @@ runtime-agnostic pieces of `stitchkit/server` and the error helpers.
|
|
|
11318
11881
|
| `NodeSocketLifecycle` | _type_ | Bun-free Socket.IO lifecycle accepted by `serveNode` |
|
|
11319
11882
|
| `HandlerConfig` / `ServiceDef` / `RawRoute` / `RawRouteContext` | _type_ | runtime-neutral handler types; raw routes default their host server to `unknown` |
|
|
11320
11883
|
| `SocketIORequestPolicy` / `SocketIOServerConfig` / `SocketIOPeerLoaders` / `SocketIOServerHandle` | _type_ | runtime-neutral handshake policy, config, optional-peer loaders and the Bun-free Node handle with `io`, `attach` and lifecycle |
|
|
11884
|
+
| `UnixClientTransportConfig` / `UnixClientTransport` | _type_ | Unix socket bounds and owned Fetch-compatible handle |
|
|
11885
|
+
| `UnixClientTransportError` / `UnixClientTransportErrorCode` / `UnixClientDeliveryState` | class / _type_ | stable failure plus dispatch certainty; no cross-transport fallback |
|
|
11321
11886
|
| `AppError` + `appError` / `badRequest` / `unauthorized` / `forbidden` / `notFound` / `conflict` / `rateLimited` | — | error helpers (same as `/contract`) |
|
|
11322
11887
|
|
|
11323
11888
|
---
|