stitchkit 0.93.0 → 0.94.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/CHANGELOG.md +134 -0
- package/README.md +5 -2
- package/dist/agent-runtime/children-control.d.ts +6 -0
- package/dist/agent-runtime/children-control.d.ts.map +1 -0
- package/dist/agent-runtime/children-spawn.d.ts +6 -0
- package/dist/agent-runtime/children-spawn.d.ts.map +1 -0
- package/dist/agent-runtime/children-state.d.ts +46 -0
- package/dist/agent-runtime/children-state.d.ts.map +1 -0
- package/dist/agent-runtime/children-usage.d.ts +8 -0
- package/dist/agent-runtime/children-usage.d.ts.map +1 -0
- package/dist/agent-runtime/children.d.ts +11 -14
- package/dist/agent-runtime/children.d.ts.map +1 -1
- package/dist/agent-runtime/coding-tool-contract.d.ts +2 -1
- package/dist/agent-runtime/coding-tool-contract.d.ts.map +1 -1
- package/dist/agent-runtime/deferred-tool-types.d.ts +2 -2
- package/dist/agent-runtime/deferred-tool-types.d.ts.map +1 -1
- package/dist/agent-runtime/events.d.ts +1 -1
- package/dist/agent-runtime/events.d.ts.map +1 -1
- package/dist/agent-runtime/managed-tools.d.ts +1 -1
- package/dist/agent-runtime/managed-tools.d.ts.map +1 -1
- package/dist/agent-runtime/observability.d.ts +1 -1
- package/dist/agent-runtime/observability.d.ts.map +1 -1
- package/dist/{agent-runtime-openrouter.d.ts → agent-runtime/openrouter.d.ts} +3 -3
- package/dist/agent-runtime/openrouter.d.ts.map +1 -0
- package/dist/agent-runtime/projections.d.ts +1 -1
- package/dist/agent-runtime/projections.d.ts.map +1 -1
- package/dist/agent-runtime/run-acquisition.d.ts +17 -0
- package/dist/agent-runtime/run-acquisition.d.ts.map +1 -0
- package/dist/agent-runtime/run-execution-state.d.ts +133 -0
- package/dist/agent-runtime/run-execution-state.d.ts.map +1 -0
- package/dist/agent-runtime/run-execution.d.ts +17 -33
- package/dist/agent-runtime/run-execution.d.ts.map +1 -1
- package/dist/agent-runtime/run-failure.d.ts +18 -0
- package/dist/agent-runtime/run-failure.d.ts.map +1 -0
- package/dist/agent-runtime/run-model-attempts.d.ts +6 -0
- package/dist/agent-runtime/run-model-attempts.d.ts.map +1 -0
- package/dist/agent-runtime/run-prompt-history.d.ts +58 -0
- package/dist/agent-runtime/run-prompt-history.d.ts.map +1 -0
- package/dist/agent-runtime/run-provider-ledger.d.ts +26 -0
- package/dist/agent-runtime/run-provider-ledger.d.ts.map +1 -0
- package/dist/agent-runtime/run-stream-parts.d.ts +17 -0
- package/dist/agent-runtime/run-stream-parts.d.ts.map +1 -0
- package/dist/agent-runtime/run-terminal.d.ts +11 -0
- package/dist/agent-runtime/run-terminal.d.ts.map +1 -0
- package/dist/agent-runtime/run-turn-preparation.d.ts +22 -0
- package/dist/agent-runtime/run-turn-preparation.d.ts.map +1 -0
- package/dist/agent-runtime/runtime-admission-gate.d.ts +29 -0
- package/dist/agent-runtime/runtime-admission-gate.d.ts.map +1 -0
- package/dist/agent-runtime/runtime-control.d.ts +9 -0
- package/dist/agent-runtime/runtime-control.d.ts.map +1 -0
- package/dist/agent-runtime/runtime-recovery.d.ts +10 -0
- package/dist/agent-runtime/runtime-recovery.d.ts.map +1 -0
- package/dist/agent-runtime/runtime-state.d.ts +36 -0
- package/dist/agent-runtime/runtime-state.d.ts.map +1 -0
- package/dist/agent-runtime/runtime-submit.d.ts +5 -0
- package/dist/agent-runtime/runtime-submit.d.ts.map +1 -0
- package/dist/agent-runtime/runtime.d.ts +9 -4
- package/dist/agent-runtime/runtime.d.ts.map +1 -1
- package/dist/{agent-runtime-sqlite-bun.d.ts → agent-runtime/sqlite-bun.d.ts} +5 -4
- package/dist/agent-runtime/sqlite-bun.d.ts.map +1 -0
- package/dist/agent-runtime/sqlite-events.d.ts +5 -0
- package/dist/agent-runtime/sqlite-events.d.ts.map +1 -0
- package/dist/agent-runtime/sqlite-history.d.ts +7 -0
- package/dist/agent-runtime/sqlite-history.d.ts.map +1 -0
- package/dist/agent-runtime/sqlite-ledger.d.ts +7 -0
- package/dist/agent-runtime/sqlite-ledger.d.ts.map +1 -0
- package/dist/{agent-runtime-sqlite-node.d.ts → agent-runtime/sqlite-node.d.ts} +5 -4
- package/dist/agent-runtime/sqlite-node.d.ts.map +1 -0
- package/dist/agent-runtime/sqlite-reads.d.ts +14 -0
- package/dist/agent-runtime/sqlite-reads.d.ts.map +1 -0
- package/dist/agent-runtime/sqlite-rows.d.ts +178 -0
- package/dist/agent-runtime/sqlite-rows.d.ts.map +1 -0
- package/dist/agent-runtime/sqlite-schema.d.ts +7 -0
- package/dist/agent-runtime/sqlite-schema.d.ts.map +1 -0
- package/dist/agent-runtime/sqlite-transactions.d.ts +25 -0
- package/dist/agent-runtime/sqlite-transactions.d.ts.map +1 -0
- package/dist/agent-runtime/sqlite.d.ts +10 -6
- package/dist/agent-runtime/sqlite.d.ts.map +1 -1
- package/dist/agent-runtime/store-archive.d.ts +16 -0
- package/dist/agent-runtime/store-archive.d.ts.map +1 -0
- package/dist/{testing/agent-store-conformance.d.ts → agent-runtime/store-conformance.d.ts} +2 -2
- package/dist/agent-runtime/store-conformance.d.ts.map +1 -0
- package/dist/agent-runtime/store-create.d.ts +11 -0
- package/dist/agent-runtime/store-create.d.ts.map +1 -0
- package/dist/agent-runtime/{store-driver.d.ts → store-driver-contract.d.ts} +7 -7
- package/dist/agent-runtime/store-driver-contract.d.ts.map +1 -0
- package/dist/agent-runtime/store-events.d.ts +7 -89
- package/dist/agent-runtime/store-events.d.ts.map +1 -1
- package/dist/agent-runtime/store-memory.d.ts +4 -0
- package/dist/agent-runtime/store-memory.d.ts.map +1 -0
- package/dist/agent-runtime/store-mutate.d.ts +13 -0
- package/dist/agent-runtime/store-mutate.d.ts.map +1 -0
- package/dist/agent-runtime/store-purge.d.ts +1 -1
- package/dist/agent-runtime/store-purge.d.ts.map +1 -1
- package/dist/agent-runtime/store-reads.d.ts +29 -0
- package/dist/agent-runtime/store-reads.d.ts.map +1 -0
- package/dist/agent-runtime/store-reduce-history.d.ts +10 -0
- package/dist/agent-runtime/store-reduce-history.d.ts.map +1 -0
- package/dist/agent-runtime/store-reduce-run.d.ts +14 -0
- package/dist/agent-runtime/store-reduce-run.d.ts.map +1 -0
- package/dist/agent-runtime/store-reduce-shared.d.ts +76 -0
- package/dist/agent-runtime/store-reduce-shared.d.ts.map +1 -0
- package/dist/agent-runtime/store-reduce-terminal.d.ts +6 -0
- package/dist/agent-runtime/store-reduce-terminal.d.ts.map +1 -0
- package/dist/agent-runtime/store-reducer.d.ts +24 -0
- package/dist/agent-runtime/store-reducer.d.ts.map +1 -0
- package/dist/agent-runtime/store-snapshot.d.ts +19 -0
- package/dist/agent-runtime/store-snapshot.d.ts.map +1 -0
- package/dist/agent-runtime/store.d.ts +1 -1
- package/dist/agent-runtime/store.d.ts.map +1 -1
- package/dist/application/admission.d.ts +1 -1
- package/dist/application/admission.d.ts.map +1 -1
- package/dist/application/events.d.ts +1 -1
- package/dist/application/events.d.ts.map +1 -1
- package/dist/{server → application}/file-state-store.d.ts +1 -1
- package/dist/application/file-state-store.d.ts.map +1 -0
- package/dist/application/health.d.ts +1 -1
- package/dist/application/health.d.ts.map +1 -1
- package/dist/application/kernel-admission.d.ts +8 -0
- package/dist/application/kernel-admission.d.ts.map +1 -0
- package/dist/application/kernel-contract.d.ts +142 -0
- package/dist/application/kernel-contract.d.ts.map +1 -0
- package/dist/application/kernel-resource-context.d.ts +24 -0
- package/dist/application/kernel-resource-context.d.ts.map +1 -0
- package/dist/application/kernel-restart.d.ts +5 -0
- package/dist/application/kernel-restart.d.ts.map +1 -0
- package/dist/application/kernel-rollback.d.ts +4 -0
- package/dist/application/kernel-rollback.d.ts.map +1 -0
- package/dist/application/kernel-shutdown.d.ts +6 -0
- package/dist/application/kernel-shutdown.d.ts.map +1 -0
- package/dist/application/kernel-start.d.ts +20 -0
- package/dist/application/kernel-start.d.ts.map +1 -0
- package/dist/application/kernel-state.d.ts +116 -0
- package/dist/application/kernel-state.d.ts.map +1 -0
- package/dist/application/kernel.d.ts +9 -140
- package/dist/application/kernel.d.ts.map +1 -1
- package/dist/application/latest-sink.d.ts +1 -1
- package/dist/application/latest-sink.d.ts.map +1 -1
- package/dist/application/opentelemetry.d.ts +1 -1
- package/dist/application/opentelemetry.d.ts.map +1 -1
- package/dist/application/watch-hub.d.ts +2 -2
- package/dist/application/watch-hub.d.ts.map +1 -1
- package/dist/browser/client-multipart.d.ts +1 -1
- package/dist/browser/client-multipart.d.ts.map +1 -1
- package/dist/browser/client-url.d.ts +1 -1
- package/dist/browser/client-url.d.ts.map +1 -1
- package/dist/browser/client.d.ts +1 -1
- package/dist/browser/client.d.ts.map +1 -1
- package/dist/browser/contract-stream.d.ts +1 -1
- package/dist/browser/contract-stream.d.ts.map +1 -1
- package/dist/browser/http.d.ts +1 -1
- package/dist/browser/http.d.ts.map +1 -1
- package/dist/browser/resumable.d.ts +1 -28
- package/dist/browser/resumable.d.ts.map +1 -1
- package/dist/browser/retained.d.ts.map +1 -0
- package/dist/browser/socket-io.d.ts +23 -1
- package/dist/browser/socket-io.d.ts.map +1 -1
- package/dist/browser/stream.d.ts +14 -2
- package/dist/browser/stream.d.ts.map +1 -1
- package/dist/contract/define.d.ts +72 -65
- package/dist/contract/define.d.ts.map +1 -1
- package/dist/{internal/errors.d.ts → contract/normalize.d.ts} +3 -39
- package/dist/contract/normalize.d.ts.map +1 -0
- package/dist/contract/tool-view.d.ts +135 -0
- package/dist/contract/tool-view.d.ts.map +1 -0
- package/dist/declaration/declaration.d.ts.map +1 -0
- package/dist/{agent-runtime/durability-contract.d.ts → durability/contract.d.ts} +2 -2
- package/dist/durability/contract.d.ts.map +1 -0
- package/dist/{agent-runtime/durability.d.ts → durability/engine.d.ts} +4 -4
- package/dist/durability/engine.d.ts.map +1 -0
- package/dist/durability/events.d.ts +101 -0
- package/dist/durability/events.d.ts.map +1 -0
- package/dist/{agent-runtime/durability-json.d.ts → durability/json.d.ts} +1 -1
- package/dist/durability/json.d.ts.map +1 -0
- package/dist/{agent-runtime/durability-ledger.d.ts → durability/ledger.d.ts} +2 -2
- package/dist/durability/ledger.d.ts.map +1 -0
- package/dist/{agent-runtime/durability-scheduler.d.ts → durability/scheduler.d.ts} +2 -2
- package/dist/durability/scheduler.d.ts.map +1 -0
- package/dist/entrypoints/agent-runtime/browser.d.ts +5 -0
- package/dist/entrypoints/agent-runtime/browser.d.ts.map +1 -0
- package/dist/{agent-runtime-browser.js → entrypoints/agent-runtime/browser.js} +3 -3
- package/dist/{agent-runtime-coding-tools.d.ts → entrypoints/agent-runtime/coding-tools.d.ts} +2 -2
- package/dist/entrypoints/agent-runtime/coding-tools.d.ts.map +1 -0
- package/dist/entrypoints/agent-runtime/coding-tools.js +22 -0
- package/dist/{agent-runtime-harness.d.ts → entrypoints/agent-runtime/harness.d.ts} +2 -2
- package/dist/entrypoints/agent-runtime/harness.d.ts.map +1 -0
- package/dist/{agent-runtime-harness.js → entrypoints/agent-runtime/harness.js} +24 -25
- package/dist/entrypoints/agent-runtime/openrouter.d.ts +2 -0
- package/dist/entrypoints/agent-runtime/openrouter.d.ts.map +1 -0
- package/dist/{agent-runtime-openrouter.js → entrypoints/agent-runtime/openrouter.js} +3 -3
- package/dist/{agent-runtime-sandbox.d.ts → entrypoints/agent-runtime/sandbox.d.ts} +5 -5
- package/dist/entrypoints/agent-runtime/sandbox.d.ts.map +1 -0
- package/dist/{agent-runtime-sandbox.js → entrypoints/agent-runtime/sandbox.js} +11 -12
- package/dist/entrypoints/agent-runtime/sqlite/bun.d.ts +2 -0
- package/dist/entrypoints/agent-runtime/sqlite/bun.d.ts.map +1 -0
- package/dist/{agent-runtime-sqlite-bun.js → entrypoints/agent-runtime/sqlite/bun.js} +8 -6
- package/dist/entrypoints/agent-runtime/sqlite/node.d.ts +2 -0
- package/dist/entrypoints/agent-runtime/sqlite/node.d.ts.map +1 -0
- package/dist/{agent-runtime-sqlite-node.js → entrypoints/agent-runtime/sqlite/node.js} +8 -6
- package/dist/entrypoints/agent-runtime/testing.d.ts +4 -0
- package/dist/entrypoints/agent-runtime/testing.d.ts.map +1 -0
- package/dist/entrypoints/agent-runtime/testing.js +1084 -0
- package/dist/{agent-runtime.d.ts → entrypoints/agent-runtime.d.ts} +41 -37
- package/dist/entrypoints/agent-runtime.d.ts.map +1 -0
- package/dist/{agent-runtime.js → entrypoints/agent-runtime.js} +408 -366
- package/dist/{application-diagnostic-journal.d.ts → entrypoints/application/diagnostic-journal.d.ts} +2 -2
- package/dist/entrypoints/application/diagnostic-journal.d.ts.map +1 -0
- package/dist/{application-diagnostic-journal.js → entrypoints/application/diagnostic-journal.js} +3 -3
- package/dist/{application-grammy.d.ts → entrypoints/application/grammy.d.ts} +2 -2
- package/dist/entrypoints/application/grammy.d.ts.map +1 -0
- package/dist/{application-grammy.js → entrypoints/application/grammy.js} +2 -2
- package/dist/{application-opentelemetry.d.ts → entrypoints/application/opentelemetry.d.ts} +2 -2
- package/dist/entrypoints/application/opentelemetry.d.ts.map +1 -0
- package/dist/{application-opentelemetry.js → entrypoints/application/opentelemetry.js} +4 -4
- package/dist/{application-schemas.d.ts → entrypoints/application/schemas.d.ts} +2 -2
- package/dist/entrypoints/application/schemas.d.ts.map +1 -0
- package/dist/{application-schemas.js → entrypoints/application/schemas.js} +2 -2
- package/dist/{application.d.ts → entrypoints/application.d.ts} +22 -21
- package/dist/entrypoints/application.d.ts.map +1 -0
- package/dist/{application.js → entrypoints/application.js} +24 -22
- package/dist/entrypoints/bin/upgrade-cli.d.ts.map +1 -0
- package/dist/{upgrade-cli.js → entrypoints/bin/upgrade-cli.js} +3 -3
- package/dist/{cli.d.ts → entrypoints/cli.d.ts} +15 -15
- package/dist/entrypoints/cli.d.ts.map +1 -0
- package/dist/{cli.js → entrypoints/cli.js} +30 -32
- package/dist/entrypoints/contract.d.ts +9 -0
- package/dist/entrypoints/contract.d.ts.map +1 -0
- package/dist/{contract/index.js → entrypoints/contract.js} +14 -6
- package/dist/entrypoints/declaration.d.ts +2 -0
- package/dist/entrypoints/declaration.d.ts.map +1 -0
- package/dist/{declaration.js → entrypoints/declaration.js} +1 -1
- package/dist/{files.d.ts → entrypoints/files.d.ts} +2 -2
- package/dist/entrypoints/files.d.ts.map +1 -0
- package/dist/{files.js → entrypoints/files.js} +3 -3
- package/dist/{geo.d.ts → entrypoints/geo.d.ts} +1 -1
- package/dist/entrypoints/geo.d.ts.map +1 -0
- package/dist/{geo.js → entrypoints/geo.js} +4 -4
- package/dist/entrypoints/google.d.ts +2 -0
- package/dist/entrypoints/google.d.ts.map +1 -0
- package/dist/{google.js → entrypoints/google.js} +1 -1
- package/dist/{index.d.ts → entrypoints/index.d.ts} +14 -13
- package/dist/entrypoints/index.d.ts.map +1 -0
- package/dist/{index.js → entrypoints/index.js} +99 -33
- package/dist/{live.d.ts → entrypoints/live.d.ts} +4 -4
- package/dist/entrypoints/live.d.ts.map +1 -0
- package/dist/{live.js → entrypoints/live.js} +6 -4
- package/dist/{node.d.ts → entrypoints/node.d.ts} +11 -11
- package/dist/entrypoints/node.d.ts.map +1 -0
- package/dist/{node.js → entrypoints/node.js} +20 -16
- package/dist/entrypoints/oauth.d.ts +2 -0
- package/dist/entrypoints/oauth.d.ts.map +1 -0
- package/dist/{oauth.js → entrypoints/oauth.js} +3 -3
- package/dist/{observability/index.d.ts → entrypoints/observability.d.ts} +11 -11
- package/dist/entrypoints/observability.d.ts.map +1 -0
- package/dist/{observability/index.js → entrypoints/observability.js} +9 -11
- package/dist/entrypoints/primitives.d.ts +3 -0
- package/dist/entrypoints/primitives.d.ts.map +1 -0
- package/dist/{primitives.js → entrypoints/primitives.js} +1 -6
- package/dist/{react.d.ts → entrypoints/react.d.ts} +4 -4
- package/dist/entrypoints/react.d.ts.map +1 -0
- package/dist/{react.js → entrypoints/react.js} +3 -3
- package/dist/{release.d.ts → entrypoints/release.d.ts} +4 -4
- package/dist/entrypoints/release.d.ts.map +1 -0
- package/dist/{release.js → entrypoints/release.js} +1 -1
- package/dist/{remote.d.ts → entrypoints/remote.d.ts} +1 -1
- package/dist/entrypoints/remote.d.ts.map +1 -0
- package/dist/entrypoints/remote.js +66 -0
- package/dist/{server/index.d.ts → entrypoints/server.d.ts} +35 -34
- package/dist/entrypoints/server.d.ts.map +1 -0
- package/dist/{server/index.js → entrypoints/server.js} +294 -244
- package/dist/{telegram.d.ts → entrypoints/telegram.d.ts} +2 -2
- package/dist/entrypoints/telegram.d.ts.map +1 -0
- package/dist/{telegram.js → entrypoints/telegram.js} +1 -1
- package/dist/entrypoints/testing.d.ts +9 -0
- package/dist/entrypoints/testing.d.ts.map +1 -0
- package/dist/{testing.js → entrypoints/testing.js} +80 -919
- package/dist/{tools/connections/index.d.ts → entrypoints/tools/connections.d.ts} +6 -7
- package/dist/entrypoints/tools/connections.d.ts.map +1 -0
- package/dist/{tools/connections/index.js → entrypoints/tools/connections.js} +7 -4
- package/dist/{tools-contract.d.ts → entrypoints/tools/contract.d.ts} +5 -5
- package/dist/entrypoints/tools/contract.d.ts.map +1 -0
- package/dist/{tools-contract.js → entrypoints/tools/contract.js} +4 -4
- package/dist/{tool-invoker.d.ts → entrypoints/tools/invoker.d.ts} +2 -2
- package/dist/entrypoints/tools/invoker.d.ts.map +1 -0
- package/dist/entrypoints/tools/invoker.js +20 -0
- package/dist/entrypoints/tools.d.ts +51 -0
- package/dist/entrypoints/tools.d.ts.map +1 -0
- package/dist/{tools.js → entrypoints/tools.js} +1787 -1751
- package/dist/{tracking-server.d.ts → entrypoints/tracking/server.d.ts} +8 -8
- package/dist/entrypoints/tracking/server.d.ts.map +1 -0
- package/dist/{tracking.d.ts → entrypoints/tracking.d.ts} +15 -15
- package/dist/entrypoints/tracking.d.ts.map +1 -0
- package/dist/{tracking.js → entrypoints/tracking.js} +5 -8
- package/dist/google/google.d.ts.map +1 -0
- package/dist/{index-a916km3r.js → index-0b1ess5e.js} +6 -13
- package/dist/{index-zwkgfq1s.js → index-1nd48tm9.js} +113 -118
- package/dist/{index-7zbps32p.js → index-1zbbzgfh.js} +2 -2
- package/dist/{index-mzvgxp0n.js → index-2cr1cqhn.js} +16 -4
- package/dist/{index-f9mb610r.js → index-35mh5bhj.js} +2 -2
- package/dist/index-48ffdxgk.js +6 -0
- package/dist/index-58k4fp34.js +1023 -0
- package/dist/{index-p13mwz16.js → index-83bdg97s.js} +1 -1
- package/dist/index-97ac4gph.js +3618 -0
- package/dist/{index-kw9t5ayn.js → index-cq4qfycf.js} +5 -5
- package/dist/{index-7gnkny4z.js → index-d9pk3td4.js} +1 -1
- package/dist/{index-e42wzzz5.js → index-dcpdvjsc.js} +7 -7
- package/dist/{index-h9etkkyk.js → index-dydjcp8z.js} +102 -25
- package/dist/{index-sr93cwf9.js → index-e2z3ahw9.js} +2 -2
- package/dist/{index-yewwh1w4.js → index-e87kakm4.js} +96 -11
- package/dist/{index-cby4ar3v.js → index-eqqvgdb3.js} +1 -1
- package/dist/index-fdp6hdxn.js +942 -0
- package/dist/{index-0a349c33.js → index-gyr48nzg.js} +125 -198
- package/dist/{index-wnfk50r0.js → index-h6eeys1v.js} +1 -1
- package/dist/{index-77fekveh.js → index-hffjpmq9.js} +1 -15
- package/dist/{index-yn8remb1.js → index-j05z5gn4.js} +50 -14
- package/dist/index-k5hsfc3j.js +31 -0
- package/dist/{index-fq77yfzc.js → index-kj2wbeta.js} +1 -1
- package/dist/index-kvj7f4hq.js +39 -0
- package/dist/{index-4fpa9dy2.js → index-m9zn6cpq.js} +2 -2
- package/dist/{index-2na0qrfn.js → index-mdzh02yc.js} +7 -7
- package/dist/{index-ywd0y6m1.js → index-mkm15s49.js} +1 -1
- package/dist/index-mp5sn43f.js +1084 -0
- package/dist/{index-3bmnp41g.js → index-n6hxg8as.js} +507 -419
- package/dist/index-ngfhvfpd.js +30 -0
- package/dist/{index-e0znv5gn.js → index-p5r7qpdd.js} +2 -93
- package/dist/{index-n0f51dfn.js → index-p81267c2.js} +608 -549
- package/dist/{index-wc4x00bn.js → index-qwhynv6x.js} +1 -1
- package/dist/index-r7nmqpsg.js +154 -0
- package/dist/{index-bx75zwt7.js → index-rewkc2hj.js} +5 -4
- package/dist/{index-9f8ztey6.js → index-sees8yf8.js} +2 -2
- package/dist/{index-vjpvjqpn.js → index-v38qyerb.js} +16 -2
- package/dist/{index-2c2bjw3s.js → index-vcvkq30w.js} +3 -3
- package/dist/index-wqgjfstn.js +18 -0
- package/dist/{index-f477k430.js → index-x4799c19.js} +1 -1
- package/dist/{index-cxqhvq21.js → index-xnj9bgxe.js} +142 -129
- package/dist/index-xsqjkha9.js +281 -0
- package/dist/{index-6kyksz9y.js → index-yw4fzmdz.js} +1 -1
- package/dist/{index-3xwxfj4z.js → index-zeemqhqx.js} +1 -1
- package/dist/{index-tmwcrptx.js → index-zfrhfn82.js} +1 -1
- package/dist/index-znfs20ss.js +98 -0
- package/dist/internal/backoff.d.ts +29 -0
- package/dist/internal/backoff.d.ts.map +1 -0
- package/dist/internal/canonical-json.d.ts +21 -11
- package/dist/internal/canonical-json.d.ts.map +1 -1
- package/dist/internal/channel.d.ts.map +1 -0
- package/dist/internal/logger.d.ts.map +1 -0
- package/dist/internal/request.d.ts.map +1 -0
- package/dist/internal/stable-digest.d.ts +0 -43
- package/dist/internal/stable-digest.d.ts.map +1 -1
- package/dist/internal/typed.d.ts +0 -8
- package/dist/internal/typed.d.ts.map +1 -1
- package/dist/internal/zod-issues.d.ts +38 -0
- package/dist/internal/zod-issues.d.ts.map +1 -0
- package/dist/{tools/json-schema-dialect.d.ts → json-schema/dialect.d.ts} +1 -1
- package/dist/json-schema/dialect.d.ts.map +1 -0
- package/dist/json-schema/json-schema.d.ts.map +1 -0
- package/dist/live/watch-delta.d.ts.map +1 -1
- package/dist/{oauth.d.ts → oauth/authorization-code.d.ts} +1 -1
- package/dist/oauth/authorization-code.d.ts.map +1 -0
- package/dist/observability/audit.d.ts +2 -2
- package/dist/observability/audit.d.ts.map +1 -1
- package/dist/observability/bounded-logger.d.ts +1 -1
- package/dist/observability/bounded-logger.d.ts.map +1 -1
- package/dist/{internal/observability-sink.d.ts → observability/bounded-sink.d.ts} +2 -2
- package/dist/observability/bounded-sink.d.ts.map +1 -0
- package/dist/observability/context.d.ts +1 -1
- package/dist/observability/context.d.ts.map +1 -1
- package/dist/observability/event.d.ts +1 -1
- package/dist/observability/event.d.ts.map +1 -1
- package/dist/observability/work.d.ts +1 -1
- package/dist/observability/work.d.ts.map +1 -1
- package/dist/primitives/audit.d.ts +1 -1
- package/dist/primitives/audit.d.ts.map +1 -1
- package/dist/primitives/export-operation.d.ts +4 -2
- package/dist/primitives/export-operation.d.ts.map +1 -1
- package/dist/realtime/socket.d.ts +1 -1
- package/dist/realtime/socket.d.ts.map +1 -1
- package/dist/server/context-contribution.d.ts +1 -1
- package/dist/server/context-contribution.d.ts.map +1 -1
- package/dist/server/context.d.ts +2 -2
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/contract-method.d.ts +15 -0
- package/dist/server/contract-method.d.ts.map +1 -0
- package/dist/server/contract-stream.d.ts +1 -1
- package/dist/server/contract-stream.d.ts.map +1 -1
- package/dist/server/create.d.ts +8 -0
- package/dist/server/create.d.ts.map +1 -1
- package/dist/server/endpoint-dispatch.d.ts +10 -0
- package/dist/server/endpoint-dispatch.d.ts.map +1 -0
- package/dist/server/endpoint-response.d.ts +18 -0
- package/dist/server/endpoint-response.d.ts.map +1 -0
- package/dist/server/error-dispatch.d.ts +17 -1
- package/dist/server/error-dispatch.d.ts.map +1 -1
- package/dist/server/error-hook.d.ts +2 -2
- package/dist/server/error-hook.d.ts.map +1 -1
- package/dist/server/handler-state.d.ts +44 -0
- package/dist/server/handler-state.d.ts.map +1 -0
- package/dist/server/implement.d.ts +1 -1
- package/dist/server/implement.d.ts.map +1 -1
- package/dist/server/middleware/auth.d.ts +1 -1
- package/dist/server/middleware/auth.d.ts.map +1 -1
- package/dist/server/middleware/cors.d.ts +17 -0
- package/dist/server/middleware/cors.d.ts.map +1 -1
- package/dist/server/middleware/trust-fence.d.ts +1 -1
- package/dist/server/middleware/trust-fence.d.ts.map +1 -1
- package/dist/server/multipart.d.ts +1 -1
- package/dist/server/multipart.d.ts.map +1 -1
- package/dist/{tools/oauth-metadata.d.ts → server/oauth/metadata.d.ts} +2 -2
- package/dist/server/oauth/metadata.d.ts.map +1 -0
- package/dist/{tools/oauth-provider.d.ts → server/oauth/provider.d.ts} +2 -2
- package/dist/server/oauth/provider.d.ts.map +1 -0
- package/dist/server/realtime.d.ts +1 -1
- package/dist/server/realtime.d.ts.map +1 -1
- package/dist/server/request-completion.d.ts +16 -0
- package/dist/server/request-completion.d.ts.map +1 -0
- package/dist/server/response-metadata.d.ts +1 -1
- package/dist/server/response-metadata.d.ts.map +1 -1
- package/dist/server/route-table.d.ts +9 -0
- package/dist/server/route-table.d.ts.map +1 -0
- package/dist/server/stream.d.ts +0 -13
- package/dist/server/stream.d.ts.map +1 -1
- package/dist/server/types.d.ts +7 -4
- package/dist/server/types.d.ts.map +1 -1
- package/dist/testing/handler-clients.d.ts +31 -0
- package/dist/testing/handler-clients.d.ts.map +1 -0
- package/dist/testing/managed-resource-conformance-scenarios.d.ts.map +1 -1
- package/dist/testing/surface-conformance.d.ts +2 -1
- package/dist/testing/surface-conformance.d.ts.map +1 -1
- package/dist/testing/surface-manifest.d.ts +30 -4
- package/dist/testing/surface-manifest.d.ts.map +1 -1
- package/dist/tools/{cli-args.d.ts → cli/args.d.ts} +1 -1
- package/dist/tools/cli/args.d.ts.map +1 -0
- package/dist/tools/{cli-checkpoint.d.ts → cli/checkpoint.d.ts} +1 -1
- package/dist/tools/cli/checkpoint.d.ts.map +1 -0
- package/dist/tools/{cli-command.d.ts → cli/command.d.ts} +4 -4
- package/dist/tools/cli/command.d.ts.map +1 -0
- package/dist/tools/{cli.d.ts → cli/config.d.ts} +5 -9
- package/dist/tools/cli/config.d.ts.map +1 -0
- package/dist/tools/cli/create-cli.d.ts +36 -0
- package/dist/tools/cli/create-cli.d.ts.map +1 -0
- package/dist/tools/cli/download.d.ts +8 -0
- package/dist/tools/cli/download.d.ts.map +1 -0
- package/dist/tools/{cli-format.d.ts → cli/format.d.ts} +2 -2
- package/dist/tools/cli/format.d.ts.map +1 -0
- package/dist/tools/cli/help.d.ts +24 -0
- package/dist/tools/cli/help.d.ts.map +1 -0
- package/dist/tools/{cli-installer.d.ts → cli/installer.d.ts} +2 -2
- package/dist/tools/cli/installer.d.ts.map +1 -0
- package/dist/tools/{cli-invoke.d.ts → cli/invoke.d.ts} +8 -8
- package/dist/tools/cli/invoke.d.ts.map +1 -0
- package/dist/tools/cli/io.d.ts +10 -0
- package/dist/tools/cli/io.d.ts.map +1 -0
- package/dist/tools/cli/managed-surface.d.ts +30 -0
- package/dist/tools/cli/managed-surface.d.ts.map +1 -0
- package/dist/tools/{cli-manifest.d.ts → cli/manifest.d.ts} +1 -1
- package/dist/tools/cli/manifest.d.ts.map +1 -0
- package/dist/tools/{cli-policy.d.ts → cli/policy.d.ts} +2 -2
- package/dist/tools/cli/policy.d.ts.map +1 -0
- package/dist/tools/cli/prepare.d.ts +14 -0
- package/dist/tools/cli/prepare.d.ts.map +1 -0
- package/dist/tools/{cli-profile.d.ts → cli/profile.d.ts} +2 -2
- package/dist/tools/cli/profile.d.ts.map +1 -0
- package/dist/tools/cli/run-managed.d.ts +5 -0
- package/dist/tools/cli/run-managed.d.ts.map +1 -0
- package/dist/tools/cli/run-native.d.ts +9 -0
- package/dist/tools/cli/run-native.d.ts.map +1 -0
- package/dist/tools/cli/session.d.ts +40 -0
- package/dist/tools/cli/session.d.ts.map +1 -0
- package/dist/tools/{cli-signature.d.ts → cli/signature.d.ts} +2 -2
- package/dist/tools/cli/signature.d.ts.map +1 -0
- package/dist/tools/{cli-stream.d.ts → cli/stream.d.ts} +4 -4
- package/dist/tools/cli/stream.d.ts.map +1 -0
- package/dist/tools/{cli-update.d.ts → cli/update.d.ts} +3 -3
- package/dist/tools/cli/update.d.ts.map +1 -0
- package/dist/tools/{cli-view.d.ts → cli/view.d.ts} +2 -2
- package/dist/tools/cli/view.d.ts.map +1 -0
- package/dist/tools/{cli-wait.d.ts → cli/wait.d.ts} +2 -2
- package/dist/tools/cli/wait.d.ts.map +1 -0
- package/dist/tools/connections/runtime.d.ts +1 -1
- package/dist/tools/connections/runtime.d.ts.map +1 -1
- package/dist/tools/connections/types.d.ts +3 -3
- package/dist/tools/connections/types.d.ts.map +1 -1
- package/dist/tools/durability-context.d.ts +1 -1
- package/dist/tools/durability-context.d.ts.map +1 -1
- package/dist/tools/execute.d.ts +30 -3
- package/dist/tools/execute.d.ts.map +1 -1
- package/dist/tools/internal/surface-projector.d.ts +8 -6
- package/dist/tools/internal/surface-projector.d.ts.map +1 -1
- package/dist/tools/internal/tool-view.d.ts +41 -0
- package/dist/tools/internal/tool-view.d.ts.map +1 -0
- package/dist/tools/invoker.d.ts +11 -3
- package/dist/tools/invoker.d.ts.map +1 -1
- package/dist/tools/list-names.d.ts +3 -3
- package/dist/tools/list-names.d.ts.map +1 -1
- package/dist/tools/manifest.d.ts +2 -2
- package/dist/tools/manifest.d.ts.map +1 -1
- package/dist/tools/{mcp-app.d.ts → mcp/app.d.ts} +1 -1
- package/dist/tools/mcp/app.d.ts.map +1 -0
- package/dist/tools/{mcp-catalog.d.ts → mcp/catalog.d.ts} +2 -2
- package/dist/tools/mcp/catalog.d.ts.map +1 -0
- package/dist/tools/{mcp-handler.d.ts → mcp/handler.d.ts} +5 -5
- package/dist/tools/mcp/handler.d.ts.map +1 -0
- package/dist/tools/{mcp.d.ts → mcp/mount.d.ts} +9 -9
- package/dist/tools/mcp/mount.d.ts.map +1 -0
- package/dist/tools/{mcp-prepare.d.ts → mcp/prepare.d.ts} +9 -9
- package/dist/tools/mcp/prepare.d.ts.map +1 -0
- package/dist/tools/{mcp-progress.d.ts → mcp/progress.d.ts} +2 -2
- package/dist/tools/mcp/progress.d.ts.map +1 -0
- package/dist/tools/mcp/register.d.ts +35 -0
- package/dist/tools/mcp/register.d.ts.map +1 -0
- package/dist/tools/{mcp-round-policy.d.ts → mcp/round-policy.d.ts} +2 -2
- package/dist/tools/mcp/round-policy.d.ts.map +1 -0
- package/dist/tools/{mcp-round.d.ts → mcp/round.d.ts} +4 -4
- package/dist/tools/mcp/round.d.ts.map +1 -0
- package/dist/tools/mcp/runtime-tools.d.ts +24 -0
- package/dist/tools/mcp/runtime-tools.d.ts.map +1 -0
- package/dist/tools/{mcp-stdio-signals.d.ts → mcp/stdio-signals.d.ts} +3 -3
- package/dist/tools/mcp/stdio-signals.d.ts.map +1 -0
- package/dist/tools/{mcp-stdio.d.ts → mcp/stdio.d.ts} +4 -4
- package/dist/tools/mcp/stdio.d.ts.map +1 -0
- package/dist/tools/{mcp-trace.d.ts → mcp/trace.d.ts} +1 -1
- package/dist/tools/mcp/trace.d.ts.map +1 -0
- package/dist/tools/mount.d.ts +12 -7
- package/dist/tools/mount.d.ts.map +1 -1
- package/dist/tools/native-definition.d.ts +3 -3
- package/dist/tools/native-definition.d.ts.map +1 -1
- package/dist/tools/operations/async-operation-binding-types.d.ts +127 -0
- package/dist/tools/operations/async-operation-binding-types.d.ts.map +1 -0
- package/dist/tools/operations/async-operation-binding.d.ts +28 -0
- package/dist/tools/operations/async-operation-binding.d.ts.map +1 -0
- package/dist/tools/operations/async-operation-canonical-types.d.ts +110 -0
- package/dist/tools/operations/async-operation-canonical-types.d.ts.map +1 -0
- package/dist/tools/operations/async-operation-canonical.d.ts +7 -0
- package/dist/tools/operations/async-operation-canonical.d.ts.map +1 -0
- package/dist/tools/operations/async-operation-contract.d.ts.map +1 -0
- package/dist/tools/operations/async-operation-id.d.ts +23 -0
- package/dist/tools/operations/async-operation-id.d.ts.map +1 -0
- package/dist/tools/operations/async-operation-runtime-tools.d.ts +40 -0
- package/dist/tools/operations/async-operation-runtime-tools.d.ts.map +1 -0
- package/dist/tools/operations/async-operation-runtime-types.d.ts +68 -0
- package/dist/tools/operations/async-operation-runtime-types.d.ts.map +1 -0
- package/dist/tools/operations/async-operation-runtime.d.ts +5 -0
- package/dist/tools/operations/async-operation-runtime.d.ts.map +1 -0
- package/dist/tools/{define-wait-tool.d.ts → operations/define-wait-tool.d.ts} +2 -2
- package/dist/tools/operations/define-wait-tool.d.ts.map +1 -0
- package/dist/tools/operations/mount-wait.d.ts.map +1 -0
- package/dist/tools/operations/wait-core.d.ts.map +1 -0
- package/dist/tools/remote.d.ts +1 -1
- package/dist/tools/remote.d.ts.map +1 -1
- package/dist/tools/runtime-tool.d.ts +11 -4
- package/dist/tools/runtime-tool.d.ts.map +1 -1
- package/dist/tools/schema/coerce.d.ts.map +1 -0
- package/dist/tools/schema/flatten-join.d.ts.map +1 -0
- package/dist/tools/schema/flatten.d.ts.map +1 -0
- package/dist/tools/schema/portable-formats.d.ts.map +1 -0
- package/dist/tools/{presentation.d.ts → schema/presentation.d.ts} +2 -0
- package/dist/tools/schema/presentation.d.ts.map +1 -0
- package/dist/tools/schema/schema.d.ts.map +1 -0
- package/dist/tools/schema/untyped-properties.d.ts.map +1 -0
- package/dist/tools/surface.d.ts +2 -3
- package/dist/tools/surface.d.ts.map +1 -1
- package/dist/tools/toolkit.d.ts +4 -4
- package/dist/tools/toolkit.d.ts.map +1 -1
- package/dist/tools/{define-download-tool.d.ts → transfer/define-download-tool.d.ts} +4 -4
- package/dist/tools/transfer/define-download-tool.d.ts.map +1 -0
- package/dist/tools/{define-upload-tool.d.ts → transfer/define-upload-tool.d.ts} +3 -3
- package/dist/tools/transfer/define-upload-tool.d.ts.map +1 -0
- package/dist/tools/{define-view-file-tool.d.ts → transfer/define-view-file-tool.d.ts} +2 -2
- package/dist/tools/transfer/define-view-file-tool.d.ts.map +1 -0
- package/dist/tools/{download-core.d.ts → transfer/download-core.d.ts} +2 -2
- package/dist/tools/transfer/download-core.d.ts.map +1 -0
- package/dist/tools/{managed-file-error.d.ts → transfer/managed-file-error.d.ts} +1 -1
- package/dist/tools/transfer/managed-file-error.d.ts.map +1 -0
- package/dist/tools/{mount-download.d.ts → transfer/mount-download.d.ts} +1 -1
- package/dist/tools/transfer/mount-download.d.ts.map +1 -0
- package/dist/tools/{mount-upload.d.ts → transfer/mount-upload.d.ts} +1 -1
- package/dist/tools/transfer/mount-upload.d.ts.map +1 -0
- package/dist/tools/transfer/native-result.d.ts.map +1 -0
- package/dist/tools/{upload-core.d.ts → transfer/upload-core.d.ts} +1 -1
- package/dist/tools/transfer/upload-core.d.ts.map +1 -0
- package/dist/tools/transfer/view-file-contract.d.ts.map +1 -0
- package/dist/tools/{view-file.d.ts → transfer/view-file.d.ts} +1 -1
- package/dist/tools/transfer/view-file.d.ts.map +1 -0
- package/dist/tools/transports.d.ts +1 -1
- package/dist/tools/transports.d.ts.map +1 -1
- package/dist/tracking/contract.d.ts +1 -1
- package/dist/tracking/contract.d.ts.map +1 -1
- package/llms/agent-runtime-browser.txt +33 -0
- package/llms/agent-runtime-coding-tools.txt +58 -0
- package/llms/agent-runtime-harness.txt +45 -0
- package/llms/agent-runtime-openrouter.txt +22 -0
- package/llms/agent-runtime-sandbox.txt +159 -0
- package/llms/agent-runtime-sqlite-bun.txt +29 -0
- package/llms/agent-runtime-sqlite-node.txt +26 -0
- package/llms/agent-runtime-testing.txt +30 -0
- package/llms/agent-runtime.2.txt +550 -0
- package/llms/agent-runtime.3.txt +311 -0
- package/llms/agent-runtime.txt +893 -0
- package/llms/application-diagnostic-journal.txt +30 -0
- package/llms/application-grammy.txt +27 -0
- package/llms/application-opentelemetry.txt +27 -0
- package/llms/application-schemas.txt +42 -0
- package/llms/application.2.txt +223 -0
- package/llms/application.txt +1144 -0
- package/llms/cli.txt +977 -0
- package/llms/contract.txt +606 -0
- package/llms/declaration.txt +286 -0
- package/llms/files.txt +30 -0
- package/llms/geo.txt +114 -0
- package/llms/google.txt +27 -0
- package/llms/live.txt +471 -0
- package/llms/node.txt +43 -0
- package/llms/oauth.txt +136 -0
- package/llms/observability.txt +893 -0
- package/llms/primitives.txt +158 -0
- package/llms/react.txt +184 -0
- package/llms/release.txt +176 -0
- package/llms/remote.txt +25 -0
- package/llms/server.2.txt +361 -0
- package/llms/server.3.txt +1031 -0
- package/llms/server.4.txt +905 -0
- package/llms/server.5.txt +250 -0
- package/llms/server.txt +996 -0
- package/llms/stitchkit.2.txt +836 -0
- package/llms/stitchkit.3.txt +147 -0
- package/llms/stitchkit.txt +260 -0
- package/llms/telegram.txt +30 -0
- package/llms/testing.txt +676 -0
- package/llms/tools-connections.txt +45 -0
- package/llms/tools-contract.txt +9 -0
- package/llms/tools-invoker.txt +9 -0
- package/llms/tools.2.txt +838 -0
- package/llms/tools.txt +1004 -0
- package/llms/tracking-server.txt +32 -0
- package/llms/tracking.txt +263 -0
- package/llms/upgrading-0.37.0-0.50.0.txt +947 -0
- package/llms/upgrading-0.53.0-0.67.0.txt +1174 -0
- package/llms/upgrading-0.68.0-0.94.0.txt +1159 -0
- package/llms/upgrading.txt +215 -0
- package/llms-full.txt +214 -75
- package/llms.txt +54 -5
- package/package.json +84 -83
- package/dist/agent-runtime/durability-contract.d.ts.map +0 -1
- package/dist/agent-runtime/durability-json.d.ts.map +0 -1
- package/dist/agent-runtime/durability-ledger.d.ts.map +0 -1
- package/dist/agent-runtime/durability-scheduler.d.ts.map +0 -1
- package/dist/agent-runtime/durability.d.ts.map +0 -1
- package/dist/agent-runtime/store-driver.d.ts.map +0 -1
- package/dist/agent-runtime-browser.d.ts +0 -5
- package/dist/agent-runtime-browser.d.ts.map +0 -1
- package/dist/agent-runtime-coding-tools.d.ts.map +0 -1
- package/dist/agent-runtime-coding-tools.js +0 -23
- package/dist/agent-runtime-harness.d.ts.map +0 -1
- package/dist/agent-runtime-openrouter.d.ts.map +0 -1
- package/dist/agent-runtime-sandbox.d.ts.map +0 -1
- package/dist/agent-runtime-sqlite-bun.d.ts.map +0 -1
- package/dist/agent-runtime-sqlite-node.d.ts.map +0 -1
- package/dist/agent-runtime-testing.d.ts +0 -3
- package/dist/agent-runtime-testing.d.ts.map +0 -1
- package/dist/agent-runtime-testing.js +0 -156
- package/dist/agent-runtime.d.ts.map +0 -1
- package/dist/application/channel.d.ts.map +0 -1
- package/dist/application-diagnostic-journal.d.ts.map +0 -1
- package/dist/application-grammy.d.ts.map +0 -1
- package/dist/application-opentelemetry.d.ts.map +0 -1
- package/dist/application-schemas.d.ts.map +0 -1
- package/dist/application.d.ts.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/contract/index.d.ts +0 -8
- package/dist/contract/index.d.ts.map +0 -1
- package/dist/declaration.d.ts.map +0 -1
- package/dist/files.d.ts.map +0 -1
- package/dist/geo.d.ts.map +0 -1
- package/dist/google.d.ts.map +0 -1
- package/dist/index-0j3vcp5m.js +0 -28
- package/dist/index-a11g2kry.js +0 -866
- package/dist/index-cnyk6te3.js +0 -77
- package/dist/index-fadnxwna.js +0 -1046
- package/dist/index-hrbajay7.js +0 -1196
- package/dist/index-s8cnx02s.js +0 -90
- package/dist/index-v5573g1k.js +0 -142
- package/dist/index-vk3kq9sb.js +0 -3344
- package/dist/index.d.ts.map +0 -1
- package/dist/internal/errors.d.ts.map +0 -1
- package/dist/internal/observability-sink.d.ts.map +0 -1
- package/dist/live.d.ts.map +0 -1
- package/dist/logger.d.ts.map +0 -1
- package/dist/node.d.ts.map +0 -1
- package/dist/oauth.d.ts.map +0 -1
- package/dist/observability/index.d.ts.map +0 -1
- package/dist/primitives.d.ts +0 -3
- package/dist/primitives.d.ts.map +0 -1
- package/dist/react.d.ts.map +0 -1
- package/dist/release.d.ts.map +0 -1
- package/dist/remote.d.ts.map +0 -1
- package/dist/remote.js +0 -88
- package/dist/retained.d.ts.map +0 -1
- package/dist/server/file-state-store.d.ts.map +0 -1
- package/dist/server/index.d.ts.map +0 -1
- package/dist/server/request.d.ts.map +0 -1
- package/dist/telegram.d.ts.map +0 -1
- package/dist/testing/agent-store-conformance.d.ts.map +0 -1
- package/dist/testing.d.ts +0 -39
- package/dist/testing.d.ts.map +0 -1
- package/dist/tool-invoker.d.ts.map +0 -1
- package/dist/tool-invoker.js +0 -21
- package/dist/tools/async-operation-contract.d.ts.map +0 -1
- package/dist/tools/async-operation.d.ts +0 -332
- package/dist/tools/async-operation.d.ts.map +0 -1
- package/dist/tools/cli-args.d.ts.map +0 -1
- package/dist/tools/cli-checkpoint.d.ts.map +0 -1
- package/dist/tools/cli-command.d.ts.map +0 -1
- package/dist/tools/cli-format.d.ts.map +0 -1
- package/dist/tools/cli-installer.d.ts.map +0 -1
- package/dist/tools/cli-invoke.d.ts.map +0 -1
- package/dist/tools/cli-manifest.d.ts.map +0 -1
- package/dist/tools/cli-policy.d.ts.map +0 -1
- package/dist/tools/cli-profile.d.ts.map +0 -1
- package/dist/tools/cli-signature.d.ts.map +0 -1
- package/dist/tools/cli-stream.d.ts.map +0 -1
- package/dist/tools/cli-update.d.ts.map +0 -1
- package/dist/tools/cli-view.d.ts.map +0 -1
- package/dist/tools/cli-wait.d.ts.map +0 -1
- package/dist/tools/cli.d.ts.map +0 -1
- package/dist/tools/coerce.d.ts.map +0 -1
- package/dist/tools/connections/index.d.ts.map +0 -1
- package/dist/tools/define-download-tool.d.ts.map +0 -1
- package/dist/tools/define-upload-tool.d.ts.map +0 -1
- package/dist/tools/define-view-file-tool.d.ts.map +0 -1
- package/dist/tools/define-wait-tool.d.ts.map +0 -1
- package/dist/tools/download-core.d.ts.map +0 -1
- package/dist/tools/flatten-join.d.ts.map +0 -1
- package/dist/tools/flatten.d.ts.map +0 -1
- package/dist/tools/json-schema-dialect.d.ts.map +0 -1
- package/dist/tools/json-schema.d.ts.map +0 -1
- package/dist/tools/managed-file-error.d.ts.map +0 -1
- package/dist/tools/mcp-app.d.ts.map +0 -1
- package/dist/tools/mcp-catalog.d.ts.map +0 -1
- package/dist/tools/mcp-handler.d.ts.map +0 -1
- package/dist/tools/mcp-prepare.d.ts.map +0 -1
- package/dist/tools/mcp-progress.d.ts.map +0 -1
- package/dist/tools/mcp-round-policy.d.ts.map +0 -1
- package/dist/tools/mcp-round.d.ts.map +0 -1
- package/dist/tools/mcp-stdio-signals.d.ts.map +0 -1
- package/dist/tools/mcp-stdio.d.ts.map +0 -1
- package/dist/tools/mcp-trace.d.ts.map +0 -1
- package/dist/tools/mcp.d.ts.map +0 -1
- package/dist/tools/mount-download.d.ts.map +0 -1
- package/dist/tools/mount-upload.d.ts.map +0 -1
- package/dist/tools/mount-wait.d.ts.map +0 -1
- package/dist/tools/native-mcp.d.ts +0 -20
- package/dist/tools/native-mcp.d.ts.map +0 -1
- package/dist/tools/native-result.d.ts.map +0 -1
- package/dist/tools/oauth-metadata.d.ts.map +0 -1
- package/dist/tools/oauth-provider.d.ts.map +0 -1
- package/dist/tools/portable-formats.d.ts.map +0 -1
- package/dist/tools/presentation.d.ts.map +0 -1
- package/dist/tools/schema.d.ts.map +0 -1
- package/dist/tools/untyped-properties.d.ts.map +0 -1
- package/dist/tools/upload-core.d.ts.map +0 -1
- package/dist/tools/view-file-contract.d.ts.map +0 -1
- package/dist/tools/view-file.d.ts.map +0 -1
- package/dist/tools/wait-core.d.ts.map +0 -1
- package/dist/tools-contract.d.ts.map +0 -1
- package/dist/tools.d.ts +0 -50
- package/dist/tools.d.ts.map +0 -1
- package/dist/tracking-server.d.ts.map +0 -1
- package/dist/tracking.d.ts.map +0 -1
- package/dist/upgrade-cli.d.ts.map +0 -1
- package/dist/{retained.d.ts → browser/retained.d.ts} +0 -0
- package/dist/{declaration.d.ts → declaration/declaration.d.ts} +0 -0
- package/dist/{upgrade-cli.d.ts → entrypoints/bin/upgrade-cli.d.ts} +0 -0
- package/dist/{tracking-server.js → entrypoints/tracking/server.js} +0 -0
- package/dist/{google.d.ts → google/google.d.ts} +0 -0
- package/dist/{index-pq95qkbf.js → index-5jm36zv1.js} +0 -0
- package/dist/{index-actkayzn.js → index-70yj2ebz.js} +3 -3
- /package/dist/{application → internal}/channel.d.ts +0 -0
- /package/dist/{logger.d.ts → internal/logger.d.ts} +0 -0
- /package/dist/{server → internal}/request.d.ts +0 -0
- /package/dist/{tools → json-schema}/json-schema.d.ts +0 -0
- /package/dist/tools/{async-operation-contract.d.ts → operations/async-operation-contract.d.ts} +0 -0
- /package/dist/tools/{mount-wait.d.ts → operations/mount-wait.d.ts} +0 -0
- /package/dist/tools/{wait-core.d.ts → operations/wait-core.d.ts} +0 -0
- /package/dist/tools/{coerce.d.ts → schema/coerce.d.ts} +0 -0
- /package/dist/tools/{flatten-join.d.ts → schema/flatten-join.d.ts} +0 -0
- /package/dist/tools/{flatten.d.ts → schema/flatten.d.ts} +0 -0
- /package/dist/tools/{portable-formats.d.ts → schema/portable-formats.d.ts} +0 -0
- /package/dist/tools/{schema.d.ts → schema/schema.d.ts} +0 -0
- /package/dist/tools/{untyped-properties.d.ts → schema/untyped-properties.d.ts} +0 -0
- /package/dist/tools/{native-result.d.ts → transfer/native-result.d.ts} +0 -0
- /package/dist/tools/{view-file-contract.d.ts → transfer/view-file-contract.d.ts} +0 -0
package/llms/server.txt
ADDED
|
@@ -0,0 +1,996 @@
|
|
|
1
|
+
# stitchkit/server — stitchkit docs slice, part 1 of 5
|
|
2
|
+
|
|
3
|
+
> Generated from the `docs/` tree by `bun run gen:llms`. The index of all slices is
|
|
4
|
+
> `llms.txt` in this package.
|
|
5
|
+
> Continues in `llms/server.2.txt`.
|
|
6
|
+
|
|
7
|
+
Parts of this slice:
|
|
8
|
+
- `llms/server.txt`: Guide: HTTP server (docs/guide/server.md)
|
|
9
|
+
- `llms/server.2.txt`: Guide: HTTP server (docs/guide/server.md)
|
|
10
|
+
- `llms/server.3.txt`: Guide: Realtime (docs/guide/realtime.md)
|
|
11
|
+
- `llms/server.4.txt`: Guide: Auth & errors (docs/guide/auth-and-errors.md) · Guide: Multi-tenant (docs/guide/multi-tenant.md)
|
|
12
|
+
- `llms/server.5.txt`: API reference: `stitchkit/server` (docs/api/reference.md)
|
|
13
|
+
|
|
14
|
+
Also read:
|
|
15
|
+
- see `llms/testing.txt` for the Testing & deployment guide
|
|
16
|
+
|
|
17
|
+
==============================================================================
|
|
18
|
+
# Guide: HTTP server (docs/guide/server.md)
|
|
19
|
+
==============================================================================
|
|
20
|
+
|
|
21
|
+
# HTTP server
|
|
22
|
+
|
|
23
|
+
stitchkit serves contracts on `Bun.serve()` directly — no Hono, no Elysia, no
|
|
24
|
+
Express. You bind a contract to handlers with `implement()`, then mount the
|
|
25
|
+
result on `createServer()`.
|
|
26
|
+
|
|
27
|
+
## `implement`
|
|
28
|
+
|
|
29
|
+
`implement(contract, handlers)` type-checks each handler against its endpoint's
|
|
30
|
+
schemas and returns a `ServiceDef` to mount.
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { implement } from 'stitchkit/server'
|
|
34
|
+
import { users } from '../shared/contracts'
|
|
35
|
+
|
|
36
|
+
const usersService = implement(users, {
|
|
37
|
+
list: () => db.users.findMany(),
|
|
38
|
+
create: (ctx) => db.users.create(ctx.input), // ctx.input: typed
|
|
39
|
+
get: (ctx) => db.users.findById(ctx.params.id), // ctx.params: typed
|
|
40
|
+
delete: (ctx) => db.users.delete(ctx.params.id),
|
|
41
|
+
})
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
A handler may be sync or async. Its return value is checked against the
|
|
45
|
+
endpoint's `output` schema; an endpoint without `output` returns nothing.
|
|
46
|
+
|
|
47
|
+
### The handler context
|
|
48
|
+
|
|
49
|
+
Every handler receives one `ctx` argument:
|
|
50
|
+
|
|
51
|
+
| `ctx` field | Type | Source |
|
|
52
|
+
|-------------|------|--------|
|
|
53
|
+
| `params` | inferred from `params` schema | parsed path params |
|
|
54
|
+
| `input` | inferred from `input` schema | parsed body / query |
|
|
55
|
+
| `files` | inferred `File` map or receiver values | the endpoint's typed multipart fields |
|
|
56
|
+
| `source` | `'http' \| 'mcp' \| 'agent'` | the transport that invoked the handler |
|
|
57
|
+
| `traceId` | `string` | per-request trace id |
|
|
58
|
+
| `ipAddress` | `string` | caller IP |
|
|
59
|
+
| `userAgent` | `string` | caller user-agent |
|
|
60
|
+
|
|
61
|
+
The same handler runs for HTTP, MCP and agent calls — `ctx.source` tells you
|
|
62
|
+
which. Anything an auth hook attaches (e.g. the resolved user) is also on `ctx`.
|
|
63
|
+
|
|
64
|
+
### `createImplement` — a fixed context type
|
|
65
|
+
|
|
66
|
+
To type `ctx` with your app's extras (the user an auth hook injects), fix the
|
|
67
|
+
context type once:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { createImplement } from 'stitchkit/server'
|
|
71
|
+
|
|
72
|
+
interface AppContext extends RuntimeContext { user: User | null }
|
|
73
|
+
|
|
74
|
+
export const implement = createImplement<AppContext>()
|
|
75
|
+
// every implement() call now has ctx.user typed
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Per-scope handler context — `createScopedImplement`
|
|
79
|
+
|
|
80
|
+
`createImplement<Ctx>()` gives every handler the **same** context type. In an app
|
|
81
|
+
where each scope guarantees different injected fields, that single type is a
|
|
82
|
+
superset: a `public` handler is told it has a `userId` the runtime never injects
|
|
83
|
+
there. `createScopedImplement` types each handler by its endpoint's **effective**
|
|
84
|
+
scope instead — the endpoint's own `scope`, else the contract's:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import { createScopedImplement } from 'stitchkit/server'
|
|
88
|
+
|
|
89
|
+
export const implementFor = createScopedImplement<{
|
|
90
|
+
public: object // no extra fields
|
|
91
|
+
user: { userId: string }
|
|
92
|
+
admin: { userId: string; isAdmin: boolean }
|
|
93
|
+
}>()
|
|
94
|
+
|
|
95
|
+
implementFor(postsContract, {
|
|
96
|
+
list: (ctx) => listPosts(ctx.userId), // contract scope 'user'
|
|
97
|
+
purge: (ctx) => (ctx.isAdmin ? purge() : []), // endpoint scope 'admin'
|
|
98
|
+
ping: () => ({ ok: true }), // endpoint scope 'public'
|
|
99
|
+
})
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The map is **type-only** — scope fields are types, so there is no runtime
|
|
103
|
+
argument and no `{} as UserFields` at the call site. `'public'` must be a key:
|
|
104
|
+
a contract without a `scope` is `'public'`. A scope outside the map is a compile
|
|
105
|
+
error on that endpoint, naming the scope.
|
|
106
|
+
|
|
107
|
+
Three boundaries worth knowing:
|
|
108
|
+
|
|
109
|
+
- A field of another scope is not a compile error on access — `RuntimeContext`
|
|
110
|
+
keeps its `[key: string]: unknown` index signature (transports write through
|
|
111
|
+
it). It degrades to `unknown`, so it can no longer pose as a `string`; using it
|
|
112
|
+
in a typed position fails.
|
|
113
|
+
- The map states what **your** `beforeHandle` / `createAuthHook.inject` puts on
|
|
114
|
+
the context. The framework does not verify a hand-written claim (→ ADR 0075) —
|
|
115
|
+
so prefer not to write one: scoped auth rules declare their contribution by
|
|
116
|
+
performing it, and `createScopedImplement<AuthScopes<typeof hook>>()` derives
|
|
117
|
+
the map from the hook
|
|
118
|
+
([details](./auth-and-errors.md#scoped-rules--the-map-createscopedimplement-consumes),
|
|
119
|
+
→ ADR 0078).
|
|
120
|
+
- An endpoint hoisted out of the contract literal widens its `scope` to `string`,
|
|
121
|
+
which is no key of any map, and lands in the error branch. An endpoint whose
|
|
122
|
+
`scope` is added by a conditional spread is optional, so it resolves to **both**
|
|
123
|
+
scopes and is typed against what they guarantee in common. Write endpoints
|
|
124
|
+
inline for the precise type.
|
|
125
|
+
- Scope keys are string literals. A map declared as an index signature
|
|
126
|
+
(`Record<string, …>`) makes every scope valid and disables the check.
|
|
127
|
+
|
|
128
|
+
The same map has a registry form and a streaming form:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
// one contract registry, handlers still typed per endpoint scope
|
|
132
|
+
const implementAll = createScopedImplementRegistry<Scopes>()
|
|
133
|
+
export const services = implementAll(apiContractRegistry, { posts, users })
|
|
134
|
+
|
|
135
|
+
// a service FILE declares its handlers typed but unbound — binding happens
|
|
136
|
+
// once, in the registry. Curried out of necessity: one call cannot both take
|
|
137
|
+
// the contract and contextually infer the handlers from it.
|
|
138
|
+
export const posts = implementFor.declare(postsContract)({
|
|
139
|
+
list: (ctx) => listPosts(ctx.userId), // ctx typed per endpoint scope
|
|
140
|
+
purge: (ctx) => (ctx.isAdmin ? purge() : []),
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
// a streaming multipart handler that reads its scope's fields. The endpoint
|
|
144
|
+
// must declare its own `scope`, and only that literal is accepted: an endpoint
|
|
145
|
+
// inheriting the contract's scope cannot be verified from here, and guessing it
|
|
146
|
+
// would rebuild the superset this factory removes.
|
|
147
|
+
implementFor(mediaContract, {
|
|
148
|
+
upload: implementFor.stream('admin', mediaContract.endpoints.upload, {
|
|
149
|
+
files: { file: receiveFile },
|
|
150
|
+
handler: ({ files, userId }) => store(files.file, userId),
|
|
151
|
+
}),
|
|
152
|
+
})
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Outside a scoped app, `createMultipartStream<Ctx>()` does for streaming handlers
|
|
156
|
+
what `createImplement<Ctx>()` does for ordinary ones. Plain
|
|
157
|
+
`defineMultipartStream` still gives the loose `RuntimeContext`.
|
|
158
|
+
|
|
159
|
+
### `implementRegistry` — one backend registry
|
|
160
|
+
|
|
161
|
+
When contracts already live in one literal registry, bind the backend from that
|
|
162
|
+
same source instead of maintaining a parallel `services` list:
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
import { implementRegistry } from 'stitchkit/server'
|
|
166
|
+
|
|
167
|
+
export const apiServices = implementRegistry(apiContractRegistry, {
|
|
168
|
+
users: usersHandlers,
|
|
169
|
+
posts: postsHandlers,
|
|
170
|
+
})
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Every registry key is required, extra keys fail, and each handler map is checked
|
|
174
|
+
against its own contract. The returned service order follows the contract
|
|
175
|
+
registry order — and the same services ride along under `.byKey`, so a consumer
|
|
176
|
+
whose registry keys are load-bearing (filtering a tool surface per caller) never
|
|
177
|
+
rebuilds a prefix lookup by hand:
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
const services = implementRegistry(apiContractRegistry, handlers)
|
|
181
|
+
createServer({ services }) // the array, as before
|
|
182
|
+
const visible = [services.byKey.users] // the same objects, by key
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
`createImplementRegistry<AppContext>()` fixes the application
|
|
186
|
+
context once, like `createImplement` does for a single contract. Runtime callers
|
|
187
|
+
also fail first on missing/extra keys and on a duplicate prefix **within one
|
|
188
|
+
scope** — the same prefix under two different group scopes is legal (their URLs
|
|
189
|
+
are separated by `scopePrefixes`, and a genuine path clash is the router's own
|
|
190
|
+
construction error).
|
|
191
|
+
|
|
192
|
+
#### A dev stand that survives half a feature — `onMissingHandler`
|
|
193
|
+
|
|
194
|
+
Adding an endpoint is two edits by construction: the contract, then the handler.
|
|
195
|
+
An editor saves one file at a time and a watcher restarts on the first save, so
|
|
196
|
+
between the two the application refuses to start — and on a shared dev stand that
|
|
197
|
+
is everyone's stand, not just yours. A project rule ("make both edits at once")
|
|
198
|
+
cannot outrun the filesystem.
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
implementRegistry(apiContractRegistry, handlers, {
|
|
202
|
+
onMissingHandler: process.env.NODE_ENV === 'production' ? 'throw' : 'stub',
|
|
203
|
+
})
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
With `'stub'` the application starts, the unimplemented endpoint answers `501` and
|
|
207
|
+
names itself in the log at every mount, so it cannot be forgotten. Both save
|
|
208
|
+
orders are covered: a contract ahead of its handler, and a handler ahead of its
|
|
209
|
+
contract. The option is accepted by `implement`, `implementRegistry`,
|
|
210
|
+
`createImplement`, `createScopedImplement`, `createImplementRegistry` and
|
|
211
|
+
`createScopedImplementRegistry`.
|
|
212
|
+
|
|
213
|
+
Three things it deliberately does not do. It stays `'throw'` by default, and
|
|
214
|
+
`'throw'` is the only right answer in production: a release is atomic, so there is
|
|
215
|
+
no window to protect, and a surface with a hole must not ship. It does not relax
|
|
216
|
+
the **compile-time** exactness check — that lives in the generic constraint, your
|
|
217
|
+
editor still shows the missing handler, and a watcher that only transpiles is
|
|
218
|
+
precisely why the runtime refusal was the one that bit. And it does not stub a
|
|
219
|
+
streaming multipart endpoint, which needs `defineMultipartStream()` receivers; the
|
|
220
|
+
stand still stops on one of those.
|
|
221
|
+
|
|
222
|
+
The stub answers `NOT_IMPLEMENTED` with status `501` everywhere — over HTTP and on
|
|
223
|
+
a tool call that crosses a process boundary alike, because the code is registered
|
|
224
|
+
in `STITCH_ERROR_STATUS`. One consequence follows from that and is worth knowing:
|
|
225
|
+
a stubbed endpoint keeps its `expose`, so on a dev stand it is advertised to a
|
|
226
|
+
model as a real tool that always refuses. The refusal is not retryable, so a model
|
|
227
|
+
stops rather than loops; it still costs one turn to find out.
|
|
228
|
+
|
|
229
|
+
The registry is intentionally flat: every key must point to one concrete
|
|
230
|
+
`defineContract()` result. Composed namespace arrays are mounted explicitly with
|
|
231
|
+
`implement()` because they do not have a one-key-to-one-handler-map boundary.
|
|
232
|
+
|
|
233
|
+
## `createServer`
|
|
234
|
+
|
|
235
|
+
`createServer(config)` builds the router and starts `Bun.serve()`. It returns a
|
|
236
|
+
managed handle with `url`, `port`, the concrete server under `runtime`, live
|
|
237
|
+
`status`, and one idempotent `shutdown()` lifecycle.
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
import { createServer } from 'stitchkit/server'
|
|
241
|
+
|
|
242
|
+
createServer({
|
|
243
|
+
services: [usersService, postsService],
|
|
244
|
+
port: 3000,
|
|
245
|
+
cors: { origin: 'https://app.example.com' },
|
|
246
|
+
hooks: { /* … */ },
|
|
247
|
+
logging: true,
|
|
248
|
+
})
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
`createHandler(config)` is the same router as a bare `(req) => Promise<Response>`
|
|
252
|
+
function — no `Bun.serve`. Use it in tests, or to embed stitchkit in another
|
|
253
|
+
server. See [Testing & deployment](./testing-and-deployment.md).
|
|
254
|
+
|
|
255
|
+
### `ServerConfig`
|
|
256
|
+
|
|
257
|
+
| Field | Purpose |
|
|
258
|
+
|-------|---------|
|
|
259
|
+
| `services` | `ServiceDef[]` mounted at the root |
|
|
260
|
+
| `groups` | route groups — a shared path prefix and hooks (see below) |
|
|
261
|
+
| `scopePrefixes` | `scope → path prefix` map — mount `services` by `service.scope` (see below) |
|
|
262
|
+
| `rawRoutes` | non-contract routes (see below) |
|
|
263
|
+
| `maxJsonBodyBytes` | optional JSON body cap (bytes); per-route value overrides; unset preserves existing behaviour |
|
|
264
|
+
| `port` / `hostname` | listen address — port defaults to `3000` |
|
|
265
|
+
| `unix` | listen on a unix domain socket instead of TCP — `'/run/app.sock'` or `{ path, mode }` (see below) |
|
|
266
|
+
| `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. |
|
|
267
|
+
| `hooks` | lifecycle hooks (see below) |
|
|
268
|
+
| `logging` | `true` for built-in request logs, or a `LoggingConfig` (see below) |
|
|
269
|
+
| `traceId` | override per-request trace-id resolution — may return `undefined` to fall back |
|
|
270
|
+
| `wrapFetch` | compose wrappers around the finished handler (request context, audit) |
|
|
271
|
+
| `socket` | full Stitchkit Socket.IO handle; route, default WebSocket handler and shutdown are mounted once |
|
|
272
|
+
| `websocket` | custom Bun WebSocket handler; with `socket`, this is the explicit composed handler |
|
|
273
|
+
| `development` / `bun` | passthrough to `Bun.serve` |
|
|
274
|
+
|
|
275
|
+
Native Bun `routes` are intentionally not accepted: Bun matches them before
|
|
276
|
+
`fetch`, so they could bypass shutdown admission. Use `rawRoutes`; they retain
|
|
277
|
+
the Fetch `Request → Response` model and participate in lifecycle tracking.
|
|
278
|
+
|
|
279
|
+
### Local daemon over a unix socket
|
|
280
|
+
|
|
281
|
+
A local daemon whose door is a socket file needs no TCP port at all:
|
|
282
|
+
|
|
283
|
+
```ts
|
|
284
|
+
const server = createServer({
|
|
285
|
+
services,
|
|
286
|
+
unix: { path: '/run/my-daemon.sock', mode: 0o600 },
|
|
287
|
+
})
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
`unix` is mutually exclusive with `port`/`hostname` (construction error). The
|
|
291
|
+
handle stays honest: `server.url` is `unix:///run/my-daemon.sock` — an
|
|
292
|
+
identifier, **not** a fetchable address (dial the path with
|
|
293
|
+
`createHttpClient({ unix })`, [client guide](client.md#unix-domain-sockets)) —
|
|
294
|
+
and `server.port` is `0`.
|
|
295
|
+
|
|
296
|
+
Socket-file hygiene is built in. A stale file left by a killed process is
|
|
297
|
+
reclaimed automatically: the path is probed, and only a socket that is owned by
|
|
298
|
+
the current user **and** answers no live listener is removed before binding
|
|
299
|
+
(best-effort — two processes racing the same path resolve through the loud
|
|
300
|
+
bind error of the loser). A regular file at the path, another user's socket, or
|
|
301
|
+
a live listener each fail with a specific error instead of being unlinked. A
|
|
302
|
+
clean `server.shutdown()` removes the file.
|
|
303
|
+
|
|
304
|
+
`mode` tightens the socket file permissions after listen. Bun creates the file
|
|
305
|
+
`0755` under a normal umask — since `connect(2)` requires *write* permission
|
|
306
|
+
that is already owner-only in practice — but when access to the socket **is**
|
|
307
|
+
the credential, set `mode: 0o600` explicitly and let the filesystem be the
|
|
308
|
+
policy. The Socket.IO lifecycle (`socket`) cannot mount on a unix listener
|
|
309
|
+
(socket.io clients dial TCP only — construction error); Bun's own WebSocket
|
|
310
|
+
client cannot dial a unix path either. Unix listeners are Bun-only: `srvx`
|
|
311
|
+
resolves a numeric port unconditionally, so `stitchkit/node` does not offer
|
|
312
|
+
`unix` (no half-support).
|
|
313
|
+
|
|
314
|
+
### Managed shutdown
|
|
315
|
+
|
|
316
|
+
```ts
|
|
317
|
+
const server = createServer({ services, socket })
|
|
318
|
+
|
|
319
|
+
const result = await server.shutdown({
|
|
320
|
+
gracePeriodMs: 30_000,
|
|
321
|
+
realtimeCloseTimeoutMs: 1_000,
|
|
322
|
+
forceTimeoutMs: 5_000,
|
|
323
|
+
retryAfterSeconds: 5,
|
|
324
|
+
signal: shutdownController.signal,
|
|
325
|
+
})
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
The first call closes HTTP and Socket.IO admission and cancels owned contract streams and
|
|
329
|
+
`streamingRoute` / `ndjsonRoute` / `sseRoute` sources. Their supplied `signal` is the lifetime:
|
|
330
|
+
waiting producers must honour it, and their iterator cleanup must finish. No separate application
|
|
331
|
+
abort controller or copied shutdown loop is required. Admitted finite HTTP/application work and
|
|
332
|
+
stream cleanup share the full `gracePeriodMs` budget. Once that work drains, `realtimeCloseTimeoutMs` (default `1_000`)
|
|
333
|
+
bounds WebSocket close handshakes inside the same outer deadline. Any upgraded sockets still open
|
|
334
|
+
at that boundary are terminated without shortening HTTP grace, and graceful runtime shutdown
|
|
335
|
+
continues. If the outer grace budget or external signal forces destructive teardown,
|
|
336
|
+
`forceTimeoutMs` bounds physical completion separately. Repeated calls return the same Promise;
|
|
337
|
+
the first options win. New
|
|
338
|
+
ordinary HTTP work receives `503`, `Retry-After` and `Connection: close` outside
|
|
339
|
+
`wrapFetch`. `result.outcome` is `clean` or `forced`; a forced result preserves
|
|
340
|
+
the pending snapshot and reason while final pending counters describe the
|
|
341
|
+
post-close transport state and any still-owned stream cleanup. A streaming request increments
|
|
342
|
+
`completedRequests` only after its source cleanup settles, not when its `Response` is returned.
|
|
343
|
+
`abortedRequests` counts requests pending at outer force, not subscriptions closed cooperatively
|
|
344
|
+
during grace. `forcedWebSockets` counts both sockets terminated at the dedicated
|
|
345
|
+
realtime bound and sockets terminated by outer force; `pendingWebSocketsAtForce` is only the latter
|
|
346
|
+
snapshot, so a clean result can truthfully report a bounded realtime termination. A graceful phase error still runs forced cleanup and
|
|
347
|
+
then rejects with the original error; a forced transport that cannot confirm
|
|
348
|
+
completion before `forceTimeoutMs` rejects instead of reporting a false zero. An uncooperative
|
|
349
|
+
stream stays pending even if its connection is gone; a cleanup error rejects shutdown.
|
|
350
|
+
Standalone `createHandler` and arbitrary raw `Response` bodies have no managed source ownership:
|
|
351
|
+
their embedding host/producer owns cancellation. Request identity must be retained by `wrapFetch`
|
|
352
|
+
when forwarding to owned streaming routes; it is also required by native timeout/upgrade APIs.
|
|
353
|
+
`runtime` is a diagnostics escape hatch, not a second canonical stop path.
|
|
354
|
+
|
|
355
|
+
### Trusted HTTPS in development
|
|
356
|
+
|
|
357
|
+
Stitchkit does not own local certificate generation or a frontend development
|
|
358
|
+
server. When a device or browser feature requires a secure context, generate a
|
|
359
|
+
trusted certificate outside the application (for example with `mkcert`) and pass
|
|
360
|
+
its files through the Bun server boundary:
|
|
361
|
+
|
|
362
|
+
```ts
|
|
363
|
+
import { readFileSync } from 'node:fs'
|
|
364
|
+
import { createServer } from 'stitchkit/server'
|
|
365
|
+
|
|
366
|
+
createServer({
|
|
367
|
+
services,
|
|
368
|
+
hostname: '0.0.0.0',
|
|
369
|
+
bun: {
|
|
370
|
+
tls: {
|
|
371
|
+
cert: readFileSync('./certs/dev.pem'),
|
|
372
|
+
key: readFileSync('./certs/dev-key.pem'),
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
})
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Trust the certificate authority on each test device and configure the frontend's
|
|
379
|
+
HTTPS mode in that frontend project. Certificate renewal, interface discovery
|
|
380
|
+
and device onboarding remain application infrastructure rather than framework
|
|
381
|
+
or starter behavior.
|
|
382
|
+
|
|
383
|
+
### Request logging
|
|
384
|
+
|
|
385
|
+
`logging: true` is shorthand for `logging: {}` — **any object turns logging
|
|
386
|
+
on**, and the fields tune it:
|
|
387
|
+
|
|
388
|
+
```ts
|
|
389
|
+
createServer({
|
|
390
|
+
services,
|
|
391
|
+
logging: {
|
|
392
|
+
// What the built-in formatter writes. Omit it and it follows NODE_ENV.
|
|
393
|
+
format: 'json',
|
|
394
|
+
// Route lines into your stack instead of the built-in formatter.
|
|
395
|
+
logger: myLogger,
|
|
396
|
+
// Silence noise. Runs after the built-in filter (framework assets,
|
|
397
|
+
// favicon, preflights), so it can only quieten more.
|
|
398
|
+
skip: (_req, url) => url.pathname === '/health' || url.pathname.startsWith('/socket.io/'),
|
|
399
|
+
// Extra fields on the completion line.
|
|
400
|
+
enrich: (req, _url, { status }) => ({
|
|
401
|
+
userAgent: req.headers.get('user-agent') ?? undefined,
|
|
402
|
+
cacheable: status === 200,
|
|
403
|
+
}),
|
|
404
|
+
},
|
|
405
|
+
})
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
### Two formats, and who chooses
|
|
409
|
+
|
|
410
|
+
| `format` | What it writes | Carries `enrich` / context identity |
|
|
411
|
+
|---|---|---|
|
|
412
|
+
| `'pretty'` | two coloured lines per request — `→` on arrival, `←` on completion | no — a line sized for a terminal is not a record |
|
|
413
|
+
| `'json'` | one structured line per completed request | yes |
|
|
414
|
+
|
|
415
|
+
Status `499` has one framework-wide meaning: the client closed the request. It
|
|
416
|
+
is logged at `info`, not under the ordinary `4xx → warn` rule. A confirmed
|
|
417
|
+
disconnect is not sent through project `onError`, `normalizeError` or the
|
|
418
|
+
request-error recorder; an `AbortError` while the request signal is still active
|
|
419
|
+
remains an internal failure. A runtime abort reason may be preserved by identity
|
|
420
|
+
through at most eight cycle-safe standard `cause` links; error messages and codes
|
|
421
|
+
are never classifiers. The same rule applies when the disconnect happens while
|
|
422
|
+
Stitchkit is reading a JSON upload body: bounded reads race every pending stream
|
|
423
|
+
read against the request signal and never parse a cancelled partial body. `499`
|
|
424
|
+
is transport telemetry, not a response declared in the contract or generated
|
|
425
|
+
OpenAPI document.
|
|
426
|
+
|
|
427
|
+
Unset, `format` follows `NODE_ENV`: `json` under `production`, `pretty`
|
|
428
|
+
otherwise. That default is read **per request** — not at import, not when this
|
|
429
|
+
package was built — so it reflects the environment your app actually runs in.
|
|
430
|
+
Set `format` and the environment stops being consulted at all.
|
|
431
|
+
|
|
432
|
+
**If your project validates its environment through a single door** — a Zod
|
|
433
|
+
schema, `@t3-oss/env-core`, anything of that shape — set `format` explicitly
|
|
434
|
+
from *your* value rather than leaning on the default. The library reads raw
|
|
435
|
+
`process.env`, which is a second source of truth: let the two disagree on one
|
|
436
|
+
deployment and production writes `pretty` without a word.
|
|
437
|
+
|
|
438
|
+
```ts
|
|
439
|
+
import { env } from '@/config'
|
|
440
|
+
createServer({ services, logging: { format: env.NODE_ENV === 'production' ? 'json' : 'pretty' } })
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
`format` applies to the **built-in** formatter only. With `logger` set, your
|
|
444
|
+
sink always receives the structured object, in every environment — the format
|
|
445
|
+
is not involved.
|
|
446
|
+
|
|
447
|
+
**Want the structured line in development?** Set `format: 'json'`. That is the
|
|
448
|
+
way to see what `enrich` and the request context actually put on the record;
|
|
449
|
+
changing `NODE_ENV` is not needed, and neither is deploying.
|
|
450
|
+
|
|
451
|
+
Three more things about `enrich`:
|
|
452
|
+
|
|
453
|
+
- It runs at close, when the request body is already consumed.
|
|
454
|
+
- The framework owns `traceId`, `method`, `path`, `status`, `durationMs`,
|
|
455
|
+
`errorCode` and `ip` in both structured sinks; the built-in JSON line also
|
|
456
|
+
owns `ts`, `level` and `msg`. Its value wins a collision, and a discarded key
|
|
457
|
+
warns once per handler rather than disappearing silently.
|
|
458
|
+
- `errorCode` has one outcome-aware exception: enrichment may supply it for a
|
|
459
|
+
`4xx`/`5xx` response when the framework derived no code, such as an error
|
|
460
|
+
`Response` returned by a raw route. It cannot add one to a `2xx`/`3xx` or
|
|
461
|
+
replace a framework-derived code.
|
|
462
|
+
- A throw in `skip` or `enrich` is swallowed; neither can fail a request.
|
|
463
|
+
|
|
464
|
+
With an observability context active, the structured line also carries `userId`,
|
|
465
|
+
`serviceName`, `action` and `dimensions` for free. See
|
|
466
|
+
[Observability](./observability.md).
|
|
467
|
+
|
|
468
|
+
## Route groups
|
|
469
|
+
|
|
470
|
+
A group gives a set of services a shared path prefix and its own hooks:
|
|
471
|
+
|
|
472
|
+
```ts
|
|
473
|
+
createServer({
|
|
474
|
+
groups: [
|
|
475
|
+
{ pathPrefix: '/api', services: [usersService, postsService] },
|
|
476
|
+
{ pathPrefix: '/api/admin', services: [adminService], hooks: { authorize: adminAuth } },
|
|
477
|
+
],
|
|
478
|
+
})
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
Each service's own `prefix` is appended to the group prefix — `usersService`
|
|
482
|
+
above is served at `/api/users`.
|
|
483
|
+
|
|
484
|
+
### Param prefixes (resource-scoped paths)
|
|
485
|
+
|
|
486
|
+
A group `pathPrefix` may contain `:param` segments — the spine of a multi-tenant
|
|
487
|
+
or resource-scoped API:
|
|
488
|
+
|
|
489
|
+
```ts
|
|
490
|
+
createServer({
|
|
491
|
+
groups: [
|
|
492
|
+
{ pathPrefix: '/tenants/:tenantId', services: [widgetsService], hooks: { authorize: auth } },
|
|
493
|
+
],
|
|
494
|
+
})
|
|
495
|
+
// widgetsService (prefix 'widgets') → /tenants/:tenantId/widgets/...
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
**Where the prefix param lands.** The router matches the *full* path (group
|
|
499
|
+
prefix + service prefix + endpoint path) and collects every `:param` — from the
|
|
500
|
+
prefix and from the endpoint alike — into one set. Each is spread onto the
|
|
501
|
+
context root, so it is available as **`ctx.tenantId`** (a raw `string`) in the
|
|
502
|
+
`authorize` hook, handler and later lifecycle hooks:
|
|
503
|
+
|
|
504
|
+
```ts
|
|
505
|
+
beforeHandle: (ctx) => {
|
|
506
|
+
const tenantId = ctx.tenantId // string — from the group prefix
|
|
507
|
+
}
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
`ctx.tenantId` is typed `unknown` (it rides the context index signature) — narrow
|
|
511
|
+
it (`String(ctx.tenantId)` / a guard) at the read site.
|
|
512
|
+
|
|
513
|
+
**Relation to the endpoint `params` schema.** `ctx.params` is the endpoint's
|
|
514
|
+
`params` schema **parsed against all collected path params** (prefix + endpoint).
|
|
515
|
+
So to get the prefix param *inside* typed `ctx.params`, add it to that schema:
|
|
516
|
+
|
|
517
|
+
```ts
|
|
518
|
+
// endpoint under /tenants/:tenantId
|
|
519
|
+
{ method: 'GET', path: '/:widgetId', desc: 'Get a widget',
|
|
520
|
+
params: z.object({ tenantId: z.string(), widgetId: z.string() }) }
|
|
521
|
+
// → ctx.params.tenantId and ctx.params.widgetId both typed
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
⚠️ A **`z.strictObject`** params schema that omits the prefix param **rejects the
|
|
525
|
+
request** (the extra `tenantId` key fails the strict parse). Either include every
|
|
526
|
+
prefix param in the schema, use a non-strict `z.object` (extra keys are dropped
|
|
527
|
+
from `ctx.params`, but `ctx.tenantId` still works), or read the param off the
|
|
528
|
+
context root.
|
|
529
|
+
|
|
530
|
+
**Trailing wildcard.** A contract path may end in a named wildcard.
|
|
531
|
+
`/app/:slug/*filePath` matches
|
|
532
|
+
both `/app/foo` and nested paths such as `/app/foo/a/b`; the collected params are
|
|
533
|
+
`{ slug: 'foo', filePath: '' }` and `{ slug: 'foo', filePath: 'a/b' }`
|
|
534
|
+
respectively. Put `filePath` in the endpoint's `params` schema to keep it in
|
|
535
|
+
typed `ctx.params`. Each captured segment is URL-decoded before the remainder is
|
|
536
|
+
joined, so encoded spaces and reserved characters reach the handler as their
|
|
537
|
+
semantic values while `/` remains the segment boundary. Static and named-param
|
|
538
|
+
routes are matched before a catch-all, so a
|
|
539
|
+
more specific endpoint wins regardless of declaration order. The same matcher
|
|
540
|
+
drives `405 Allow` resolution.
|
|
541
|
+
|
|
542
|
+
### Scope-driven mounting (`scopePrefixes`)
|
|
543
|
+
|
|
544
|
+
With several scopes, hand-partitioning services into `groups` duplicates the
|
|
545
|
+
scope↔prefix mapping. Instead, map `scope → prefix` once and pass the flat
|
|
546
|
+
`services` list — each entry mounts under `scopePrefixes[service.scope]`:
|
|
547
|
+
|
|
548
|
+
```ts
|
|
549
|
+
createServer({
|
|
550
|
+
services, // mixed scopes, listed once
|
|
551
|
+
scopePrefixes: { tenant: 'tenants/:tenantId', project: 'projects/:projectId' },
|
|
552
|
+
})
|
|
553
|
+
// scope 'tenant' → /tenants/:tenantId/<prefix>/...
|
|
554
|
+
// scope 'project' → /projects/:projectId/<prefix>/...
|
|
555
|
+
// unmapped scope → mounted flat
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
A prefix may carry `:param` segments (they land on the context exactly as above).
|
|
559
|
+
Services listed under explicit `groups` are unaffected — the group prefix wins.
|
|
560
|
+
Scope stays a free string (unless the contract is authored through
|
|
561
|
+
`createContractFactory<Scope>()`); the core attaches no meaning beyond this lookup
|
|
562
|
+
(→ ADR 0024). When each scope needs a different handler-context shape, use
|
|
563
|
+
[`createScopedImplement`](#per-scope-handler-context--createscopedimplement)
|
|
564
|
+
rather than a single superset context.
|
|
565
|
+
|
|
566
|
+
## Lifecycle hooks
|
|
567
|
+
|
|
568
|
+
Five hooks wrap every contract request. Route matching and path-param
|
|
569
|
+
validation happen before authorization; body/query parsing happens only after
|
|
570
|
+
authorization succeeds:
|
|
571
|
+
|
|
572
|
+
```ts
|
|
573
|
+
createServer({
|
|
574
|
+
services,
|
|
575
|
+
hooks: {
|
|
576
|
+
onRequest(req) { /* logging, global rate limit — may return a Response to short-circuit */ },
|
|
577
|
+
authorize(ctx, endpoint) { /* identity + scope, before body reads — throw to reject */ },
|
|
578
|
+
beforeHandle(ctx, endpoint) { /* validated-input preconditions */ },
|
|
579
|
+
afterHandle(ctx, result, ep) { /* transform the result data */ },
|
|
580
|
+
onError(ctx, error, ep) { /* custom error response — return a Response */ },
|
|
581
|
+
},
|
|
582
|
+
})
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
- **`onRequest`** — runs first, with the raw `Request`. Return a `Response` to
|
|
586
|
+
short-circuit (a rate-limit 429, a redirect); return nothing to continue.
|
|
587
|
+
- **`authorize`** — runs after route matching and validated path params, but
|
|
588
|
+
before query, JSON or multipart parsing. It receives request metadata and
|
|
589
|
+
params, with `input: undefined` and no files. This is the HTTP home of
|
|
590
|
+
[`createAuthHook`](./auth-and-errors.md#createauthhook).
|
|
591
|
+
- **`beforeHandle`** — runs after the complete context has been parsed and
|
|
592
|
+
validated, immediately before the handler. Put input-dependent application
|
|
593
|
+
preconditions here, not authentication.
|
|
594
|
+
- **`afterHandle`** — receives the handler result; return a replacement to
|
|
595
|
+
transform it.
|
|
596
|
+
- **`onError`** — for a matched contract route, the group's hook runs first,
|
|
597
|
+
then the global hook, then the standard error envelope. The first returned
|
|
598
|
+
`Response` wins (including its status and headers); an absent hook, `undefined`
|
|
599
|
+
return or thrown/rejected hook continues to the next level with the **original
|
|
600
|
+
error** and the same context and endpoint. A failing hook is diagnosed through
|
|
601
|
+
`logging.logger.error` (or the internal console fallback), never substituted
|
|
602
|
+
into the client response. This covers path/body validation, authorization,
|
|
603
|
+
before/after hooks and handler errors, including raw-response contract handlers.
|
|
604
|
+
Global `onRequest` failures, `rawRoutes` and unmatched 404/405 have no matched
|
|
605
|
+
contract group and use only the global hook. A confirmed client disconnect
|
|
606
|
+
deliberately bypasses both hooks; errors after streaming headers have been
|
|
607
|
+
sent keep their existing stream-error path.
|
|
608
|
+
|
|
609
|
+
Hooks see `RuntimeContext` (loose types); handlers see `HandlerContext` (typed).
|
|
610
|
+
That split is deliberate — see [ADR 0003](../decisions/0003-two-context-types.md).
|
|
611
|
+
|
|
612
|
+
## Signed JSON webhooks
|
|
613
|
+
|
|
614
|
+
A provider signs the original JSON text, not `JSON.stringify(ctx.input)`.
|
|
615
|
+
Declare `rawBody: true` to retain the same decoded text the router reads while
|
|
616
|
+
keeping normal Zod validation:
|
|
617
|
+
|
|
618
|
+
```ts
|
|
619
|
+
const webhooks = defineContract(
|
|
620
|
+
{ prefix: 'webhooks' },
|
|
621
|
+
{
|
|
622
|
+
receive: {
|
|
623
|
+
method: 'POST', path: '/provider', desc: 'Receive a signed event',
|
|
624
|
+
rawBody: true,
|
|
625
|
+
maxJsonBodyBytes: 256 * 1024,
|
|
626
|
+
input: ProviderEventSchema,
|
|
627
|
+
output: z.object({ accepted: z.boolean() }),
|
|
628
|
+
},
|
|
629
|
+
},
|
|
630
|
+
)
|
|
631
|
+
|
|
632
|
+
receive: async (ctx) => {
|
|
633
|
+
const signature = ctx.req.headers.get('x-signature')
|
|
634
|
+
await verifyWebhookHmac(ctx.rawBody, signature) // guaranteed string
|
|
635
|
+
return { accepted: true }
|
|
636
|
+
}
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
This endpoint is HTTP-only and cannot be multipart or exposed as a tool. The
|
|
640
|
+
router sets `ctx.rawBody` before JSON/Zod validation, so `onError` can inspect it
|
|
641
|
+
after malformed JSON or a schema failure. Endpoints without `rawBody: true` do
|
|
642
|
+
not retain the text. `maxJsonBodyBytes` may also be set once on `createServer` /
|
|
643
|
+
`createHandler`; a route value wins. Both limits are opt-in and abort an
|
|
644
|
+
oversized stream before it is fully buffered. → ADR 0051
|
|
645
|
+
|
|
646
|
+
## Safelisted request bodies (beacons)
|
|
647
|
+
|
|
648
|
+
A body the router reads is `application/json`, and a non-empty body with any
|
|
649
|
+
other `Content-Type` is a `400`. That is deliberate: a `text/plain` body is a
|
|
650
|
+
CORS *simple* request, sent with cookies from any site before the browser asks
|
|
651
|
+
whether the page may read the reply — so requiring JSON forces a preflight, and
|
|
652
|
+
the preflight is where a foreign form is stopped.
|
|
653
|
+
|
|
654
|
+
A page that is being unloaded does not get a preflight. `navigator.sendBeacon`
|
|
655
|
+
with an `application/json` blob and `fetch({ keepalive: true })` with a JSON
|
|
656
|
+
`Content-Type` both report success and both die before the server sees them
|
|
657
|
+
when the API is on another origin. A beacon with a **string** body is
|
|
658
|
+
`text/plain` and arrives. Declare the endpoint that receives it:
|
|
659
|
+
|
|
660
|
+
```ts
|
|
661
|
+
const tracking = defineContract(
|
|
662
|
+
{ prefix: 'tracking', scope: 'public' },
|
|
663
|
+
{
|
|
664
|
+
track: {
|
|
665
|
+
method: 'POST', path: '/events', desc: 'Batch track browser events',
|
|
666
|
+
safelistedBody: true,
|
|
667
|
+
maxJsonBodyBytes: 256 * 1024,
|
|
668
|
+
input: TrackEventsRequestSchema,
|
|
669
|
+
output: TrackEventsResponseSchema,
|
|
670
|
+
},
|
|
671
|
+
},
|
|
672
|
+
)
|
|
673
|
+
|
|
674
|
+
// browser, on `pagehide`
|
|
675
|
+
navigator.sendBeacon(urls.tracking.track(), JSON.stringify(batch)) // text/plain;charset=UTF-8
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
The body is parsed by the same JSON parser, validated against the same `input`
|
|
679
|
+
and bounded by the same `maxJsonBodyBytes`; `application/json` keeps working.
|
|
680
|
+
What changes is one rule, and it is the rule that keeps the door from being a
|
|
681
|
+
hole: a `text/plain` body is accepted **only from an `Origin` on the server's
|
|
682
|
+
explicit `cors.origin` allow-list**. Everything else is a `403` before the text
|
|
683
|
+
is read:
|
|
684
|
+
|
|
685
|
+
| Request | Outcome |
|
|
686
|
+
|---------|---------|
|
|
687
|
+
| `Origin` on the allow-list (string or list, case-insensitive) | parsed |
|
|
688
|
+
| `Content-Type: text/plain; charset=application/json` from a foreign origin | `403` — the media type is compared whole, never as a substring |
|
|
689
|
+
| `Origin` of a site the server never named | `403` |
|
|
690
|
+
| `Origin: null` — sandboxed iframe, cross-origin redirect, `file://` | `403` |
|
|
691
|
+
| no `Origin` header | `403` |
|
|
692
|
+
| server `cors.origin: '*'` | `403` — a wildcard is not an allow-list |
|
|
693
|
+
| server without `cors` | `403` — there is no allow-list |
|
|
694
|
+
| `application/json` from any origin | unchanged — never subject to the check |
|
|
695
|
+
|
|
696
|
+
The check is a browser invariant, not authentication: a `curl` with a stolen
|
|
697
|
+
cookie and a forged `Origin` passes, and that is an authenticated call, not a
|
|
698
|
+
cross-site one. Two things follow for the application:
|
|
699
|
+
|
|
700
|
+
- **`sendBeacon` sends no headers.** A bearer-authenticated client has to put
|
|
701
|
+
its token in the body — and the body is read *after* `authorize`, so that
|
|
702
|
+
token is available in `beforeHandle` or the handler, never in an auth hook.
|
|
703
|
+
Cookie-authenticated apps are unaffected.
|
|
704
|
+
- **`POST` only.** `defineContract` refuses the flag on `GET`, `HEAD`, `PUT`,
|
|
705
|
+
`PATCH`, `DELETE`, multipart, streaming and input-less endpoints: none of them
|
|
706
|
+
is a simple request with a JSON body, so the flag would only widen the surface.
|
|
707
|
+
|
|
708
|
+
Unlike `rawBody`, the flag does not force `expose: ['HTTP']` — a beacon endpoint
|
|
709
|
+
may still be a tool; only HTTP body parsing changes. OpenAPI lists both media
|
|
710
|
+
types with one schema. → ADR 0165
|
|
711
|
+
|
|
712
|
+
A [raw route](#raw-routes) has none of this: `parseBody` reads any
|
|
713
|
+
`Content-Type`, so a raw beacon route is reachable from any site with the
|
|
714
|
+
user's cookies unless the route checks `Origin` itself. Prefer the flag.
|
|
715
|
+
|
|
716
|
+
### Choosing an HTTP boundary
|
|
717
|
+
|
|
718
|
+
| Need | Contract declaration | What remains framework-owned |
|
|
719
|
+
|------|----------------------|------------------------------|
|
|
720
|
+
| Typed JSON request/response | ordinary `input` / `output` | routing, auth, schemas, hooks, client, OpenAPI |
|
|
721
|
+
| HMAC-signed JSON | `rawBody: true` + `input` / `output` | the same pipeline plus the exact decoded request text |
|
|
722
|
+
| Page-unload beacon from another origin | `safelistedBody: true` + `input` / `output` | the same pipeline; a `text/plain` body is admitted only from an allow-listed `Origin` |
|
|
723
|
+
| File upload | typed `multipart` descriptor | file cardinality/limits, text input validation and client form encoding |
|
|
724
|
+
| File, stream or redirect response behind contract auth | `rawResponse: true` | request parsing, route identity, auth and typed URL/client surface |
|
|
725
|
+
| Transport that cannot be expressed as the contract pipeline | `RawRoute` | only raw routing, CORS, request hook and error normalisation |
|
|
726
|
+
|
|
727
|
+
`rawBody` is for JSON signatures: verify `ctx.rawBody` against the signature
|
|
728
|
+
header, then use the already validated `ctx.input`. A provider-specific binary
|
|
729
|
+
signature protocol, OAuth callback or WebSocket upgrade can be a real raw route,
|
|
730
|
+
but it must own its validation and authorization explicitly.
|
|
731
|
+
|
|
732
|
+
## Typed JSON response metadata
|
|
733
|
+
|
|
734
|
+
A JSON endpoint that must attach dynamic HTTP headers while preserving typed
|
|
735
|
+
output declares `responseMeta`. The handler still returns ordinary data:
|
|
736
|
+
|
|
737
|
+
```ts
|
|
738
|
+
const auth = defineContract({ prefix: 'auth' }, {
|
|
739
|
+
complete: {
|
|
740
|
+
method: 'POST', path: '/complete', desc: 'Complete authentication',
|
|
741
|
+
input: CompleteAuthSchema,
|
|
742
|
+
output: AuthUserSchema,
|
|
743
|
+
responseMeta: { status: 200 },
|
|
744
|
+
},
|
|
745
|
+
})
|
|
746
|
+
|
|
747
|
+
complete: async ({ input, response }) => {
|
|
748
|
+
const result = await authenticate(input.token)
|
|
749
|
+
response.headers.append('Set-Cookie', session.set(result.sessionId))
|
|
750
|
+
response.headers.append('Set-Cookie', preferences.set(result.preferencesId))
|
|
751
|
+
return result.user
|
|
752
|
+
}
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
`ctx.response.headers` is a fresh Web Fetch `Headers` bag per request. `append`
|
|
756
|
+
preserves repeated `Set-Cookie` values on Bun and Node. The endpoint is
|
|
757
|
+
HTTP-only, but its typed client method still resolves to `AuthUser` — not
|
|
758
|
+
`Response` — and the final data still passes group/global `afterHandle` and the
|
|
759
|
+
declared `output` schema exactly once.
|
|
760
|
+
|
|
761
|
+
`responseMeta.status` is static contract metadata and OpenAPI publishes the same
|
|
762
|
+
2xx code. Without it, an endpoint with `output` uses `200`; an endpoint without
|
|
763
|
+
`output` uses `204`. A nullable output returns JSON `null` with status `200` —
|
|
764
|
+
the runtime value never turns a declared response body into no content.
|
|
765
|
+
`undefined` violates a declared output, while returning non-null data without an
|
|
766
|
+
output schema is also a server fault. Bodyless `204`/`205` cannot be combined
|
|
767
|
+
with `output`. Redirects, streams, files and handler-owned status/body logic
|
|
768
|
+
remain [`rawResponse: true`](#raw-response-endpoints).
|
|
769
|
+
|
|
770
|
+
Collected headers are merged only after the complete success pipeline. A
|
|
771
|
+
handler, hook or output-validation failure discards them. `Content-Type`,
|
|
772
|
+
`Content-Length`, `x-request-id` and every `Access-Control-*` header remain
|
|
773
|
+
framework-owned; trying to set one fails loudly with the endpoint identity.
|
|
774
|
+
→ [ADR 0052](../decisions/0052-typed-json-response-metadata.md)
|
|
775
|
+
|
|
776
|
+
## Raw-response endpoints
|
|
777
|
+
|
|
778
|
+
An endpoint that answers with **bytes rather than data** — a PDF download, a
|
|
779
|
+
file, an SSE stream — declares `rawResponse: true` and returns the `Response` itself:
|
|
780
|
+
|
|
781
|
+
```ts
|
|
782
|
+
// contract
|
|
783
|
+
export const documents = defineContract(
|
|
784
|
+
{ prefix: 'documents', scope: 'admin' },
|
|
785
|
+
{
|
|
786
|
+
download: {
|
|
787
|
+
method: 'GET', path: '/:id/pdf', desc: 'Download a document as a PDF',
|
|
788
|
+
params: z.object({ id: z.uuid() }),
|
|
789
|
+
rawResponse: true, contentType: 'application/pdf',
|
|
790
|
+
},
|
|
791
|
+
},
|
|
792
|
+
)
|
|
793
|
+
|
|
794
|
+
// handler — no guard on the first line; `beforeHandle` already ran
|
|
795
|
+
download: (ctx) => serveFile(ctx.req, { path: pathFor(ctx.params.id),
|
|
796
|
+
filename: 'offer.pdf' }),
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
The request half is untouched: `params`, `input` and `multipart` parse and
|
|
800
|
+
validate exactly as elsewhere, and the endpoint goes through `beforeHandle` — so
|
|
801
|
+
the **auth gate applies without a guard in the handler**. Only the response is
|
|
802
|
+
handed over, so there is no `output` schema, `afterHandle` is skipped (it
|
|
803
|
+
transforms data; there is none) and the endpoint is HTTP-only: never an MCP
|
|
804
|
+
tool, an agent tool or a CLI command. Declaring `output`, `tool` or a non-HTTP
|
|
805
|
+
`expose` alongside `rawResponse` is a type error, and throws
|
|
806
|
+
at definition time for a contract assembled at runtime.
|
|
807
|
+
|
|
808
|
+
On the typed client the method resolves to the untouched `Response` — the
|
|
809
|
+
filename lives in `Content-Disposition`, so a `Blob` alone would lose it:
|
|
810
|
+
|
|
811
|
+
```ts
|
|
812
|
+
const client = createClient(documents, http)
|
|
813
|
+
const res = await client.download({ id }) // Response
|
|
814
|
+
const name = res.headers.get('Content-Disposition')
|
|
815
|
+
const blob = await res.blob()
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
When the browser should navigate or assign the endpoint directly to `src`, use
|
|
819
|
+
[`createUrlBuilder`](./client.md#contract-url-builders). Raw-response GET methods
|
|
820
|
+
are included, while mutation and multipart methods are intentionally absent.
|
|
821
|
+
|
|
822
|
+
Cross-origin, remember that those headers are readable only because CORS exposes
|
|
823
|
+
them — see [`cors.exposeHeaders`](#serving-files--range-requests).
|
|
824
|
+
|
|
825
|
+
**Raw response or [raw route](#raw-routes)?** Both hand the `Response` to your
|
|
826
|
+
code. A raw-response *endpoint* stays in the contract: only its response is
|
|
827
|
+
raw — it is still routed, gated, typed and documented like every other endpoint.
|
|
828
|
+
A raw *route* is outside the contract entirely — no schemas, no auth gate, no
|
|
829
|
+
client — which is what you want for an OAuth redirect or a non-JSON webhook. A
|
|
830
|
+
signed JSON webhook can stay validated through
|
|
831
|
+
[`rawBody: true`](#signed-json-webhooks).
|
|
832
|
+
|
|
833
|
+
If an endpoint returns typed JSON and only needs an additional status/header,
|
|
834
|
+
use [`responseMeta`](#typed-json-response-metadata), not `rawResponse`: the raw
|
|
835
|
+
variant deliberately transfers response ownership and changes the client result
|
|
836
|
+
to `Response`.
|
|
837
|
+
|
|
838
|
+
⚠️ **Delete the old raw route when you move an endpoint into the contract.** Raw
|
|
839
|
+
routes are matched **first**, so a leftover one keeps serving the bytes and the
|
|
840
|
+
contract endpoint — with its auth gate — never runs. stitchkit warns at startup
|
|
841
|
+
when a raw route shadows a contract route, naming both and the scope being
|
|
842
|
+
bypassed; treat that warning as a bug.
|
|
843
|
+
|
|
844
|
+
⚠️ **A path built from user input needs a containment check.** `staticRoute`
|
|
845
|
+
enforces it; `serveFile` deliberately leaves it to the caller, so an endpoint
|
|
846
|
+
serving `/:filename` must not pass it through:
|
|
847
|
+
|
|
848
|
+
```ts
|
|
849
|
+
import { isWithinDir, serveFile } from 'stitchkit/server'
|
|
850
|
+
import { resolve } from 'node:path'
|
|
851
|
+
|
|
852
|
+
const ROOT = resolve('./uploads')
|
|
853
|
+
|
|
854
|
+
file: (ctx) => {
|
|
855
|
+
const target = resolve(ROOT, ctx.params.filename)
|
|
856
|
+
// `../../etc/passwd` resolves outside ROOT — reject before touching disk.
|
|
857
|
+
if (!isWithinDir(ROOT, target)) throw notFound('File not found')
|
|
858
|
+
return serveFile(ctx.req, { path: target })
|
|
859
|
+
},
|
|
860
|
+
```
|
|
861
|
+
|
|
862
|
+
`implementRemote` proxies a raw-response endpoint like any other — the remote
|
|
863
|
+
`Response` is forwarded verbatim. Request headers are not relayed, so a `Range`
|
|
864
|
+
sent to the proxy does not reach the origin and the full body comes back.
|
|
865
|
+
→ ADR 0038.
|
|
866
|
+
|
|
867
|
+
## Raw routes
|
|
868
|
+
|
|
869
|
+
Some routes cannot be a clean JSON contract — an OAuth redirect, a webhook with
|
|
870
|
+
signature verification, static files, the Socket.IO endpoint. `rawRoutes` are
|
|
871
|
+
plain `Request → Response` handlers, matched by the same router (shared CORS and
|
|
872
|
+
`onRequest`) but with no schema parsing and no `beforeHandle` gate — a raw route
|
|
873
|
+
authorises itself.
|
|
874
|
+
|
|
875
|
+
```ts
|
|
876
|
+
createServer({
|
|
877
|
+
services,
|
|
878
|
+
rawRoutes: [
|
|
879
|
+
{
|
|
880
|
+
method: 'GET',
|
|
881
|
+
path: '/health',
|
|
882
|
+
handler: () => Response.json({ status: 'ok' }),
|
|
883
|
+
},
|
|
884
|
+
{
|
|
885
|
+
method: 'POST',
|
|
886
|
+
path: '/webhooks/:provider',
|
|
887
|
+
serviceName: 'webhooks',
|
|
888
|
+
action: 'receive',
|
|
889
|
+
handler: (req, ctx) => handleWebhook(ctx.params.provider, req),
|
|
890
|
+
},
|
|
891
|
+
],
|
|
892
|
+
})
|
|
893
|
+
```
|
|
894
|
+
|
|
895
|
+
`serviceName` and `action` are optional observability identity. Declare
|
|
896
|
+
`serviceName` when audit, logging, sampling or another cross-cutting policy
|
|
897
|
+
groups this raw route with a service; add `action` when the policy needs an
|
|
898
|
+
operation within that service. Stitchkit never derives either from `path`.
|
|
899
|
+
`action` without `serviceName`, and empty identity strings, fail at startup.
|
|
900
|
+
|
|
901
|
+
A path may be exact, carry `:param` segments, or end in `/*filePath` for a prefix
|
|
902
|
+
wildcard — and the two combine: `/app/:slug/*filePath` matches `/app/x/a/b` with
|
|
903
|
+
`ctx.params.slug === 'x'` and the remainder in `ctx.params.filePath` (a SPA
|
|
904
|
+
deep-link fallback). List more specific routes before the wildcard — the first
|
|
905
|
+
match wins. `staticRoute()` builds a raw route that serves a directory.
|
|
906
|
+
|
|
907
|
+
At startup Stitchkit rejects exact duplicates, equivalent parameter shapes
|
|
908
|
+
(`/users/:id` vs `/users/:userId`) and any later raw route completely hidden by
|
|
909
|
+
an earlier raw route. Partial overlap stays legal, including the recommended
|
|
910
|
+
specific-before-wildcard order. `GET` and `HEAD` remain independent because that
|
|
911
|
+
is how the actual raw matcher dispatches them. Raw-vs-contract overlap remains a
|
|
912
|
+
startup diagnostic naming the bypassed contract identity and scope.
|
|
913
|
+
|
|
914
|
+
### Raw-route helpers
|
|
915
|
+
|
|
916
|
+
A raw route gives up the contract pipeline, so three things get re-implemented in
|
|
917
|
+
every one. `stitchkit/server` ships them — conveniences, not a second pipeline
|
|
918
|
+
(no auth, no schema gate beyond `parseBody`):
|
|
919
|
+
|
|
920
|
+
```ts
|
|
921
|
+
import { respondJson, errorResponse, parseBody, badRequest } from 'stitchkit/server'
|
|
922
|
+
|
|
923
|
+
handler: async (req) => {
|
|
924
|
+
try {
|
|
925
|
+
const body = await parseBody(req, MySchema) // typed value, or null (no throw)
|
|
926
|
+
if (!body) throw badRequest('invalid body') // helpers THROW an AppError
|
|
927
|
+
return respondJson(await myService(body)) // JSON; 204 when null/undefined
|
|
928
|
+
} catch (err) {
|
|
929
|
+
return errorResponse(err) // same envelope as a contract route
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
```
|
|
933
|
+
|
|
934
|
+
The error helpers (`badRequest`, `notFound`, …) **throw** an `AppError`, so raise
|
|
935
|
+
them inside the `try` and let `errorResponse(err)` render it — that runs any
|
|
936
|
+
thrown value through the framework's `normalizeError`, so a raw route returns the
|
|
937
|
+
**identical** `{ error: { code, message, … } }` shape (and `x-request-id`) a
|
|
938
|
+
contract route does.
|
|
939
|
+
|
|
940
|
+
### Serving files & Range requests
|
|
941
|
+
|
|
942
|
+
`staticRoute` is basic on purpose — it reads the whole file into memory and has
|
|
943
|
+
no `Range` or conditional support; put a CDN in front, or use it for small web
|
|
944
|
+
assets. To serve **media** (video / audio / large downloads) that a browser must
|
|
945
|
+
seek and cache, use **`serveFile`** (Bun) — it streams the requested byte range
|
|
946
|
+
and speaks the conditional-request half of RFC 7233 / 9110:
|
|
947
|
+
|
|
948
|
+
```ts
|
|
949
|
+
import { defineContract } from 'stitchkit/contract'
|
|
950
|
+
import { implement, serveFile } from 'stitchkit/server'
|
|
951
|
+
import { z } from 'zod'
|
|
952
|
+
|
|
953
|
+
const MediaParams = z.object({ id: z.string() })
|
|
954
|
+
const media = defineContract({ prefix: 'media' }, {
|
|
955
|
+
download: {
|
|
956
|
+
method: 'GET', path: '/:id', desc: 'Download media',
|
|
957
|
+
params: MediaParams, rawResponse: true, contentType: 'video/mp4',
|
|
958
|
+
},
|
|
959
|
+
inspect: {
|
|
960
|
+
method: 'HEAD', path: '/:id', desc: 'Inspect media',
|
|
961
|
+
params: MediaParams, rawResponse: true, contentType: 'video/mp4',
|
|
962
|
+
},
|
|
963
|
+
})
|
|
964
|
+
|
|
965
|
+
const mediaService = implement(media, {
|
|
966
|
+
download: ({ req, params }) =>
|
|
967
|
+
serveFile(req, { path: pathForId(params.id), filename: 'clip.mp4' }),
|
|
968
|
+
inspect: ({ req, params }) =>
|
|
969
|
+
serveFile(req, { path: pathForId(params.id), filename: 'clip.mp4' }),
|
|
970
|
+
})
|
|
971
|
+
```
|
|
972
|
+
|
|
973
|
+
GET and HEAD are separate operations deliberately: declaring GET never creates
|
|
974
|
+
a hidden HEAD alias. Both travel through the normal contract router, params,
|
|
975
|
+
lifecycle/RBAC and request logging. A HEAD handler may inspect the raw query via
|
|
976
|
+
`ctx.req.url`, but cannot declare a request input schema or body.
|
|
977
|
+
|
|
978
|
+
`serveFile` returns `206` (range, with `Content-Range` + `Content-Length`), `200`
|
|
979
|
+
(full), `416` (unsatisfiable, `Content-Range: bytes */size`), `304`
|
|
980
|
+
(`If-None-Match` / `If-Modified-Since`), `404` (missing) or `405` (non GET/HEAD).
|
|
981
|
+
It always sets `Accept-Ranges: bytes`, a weak `ETag` and `Last-Modified` (so
|
|
982
|
+
`If-Range` keeps a changing file from being stitched from stale + fresh bytes),
|
|
983
|
+
and `nosniff`. `Content-Type` is auto-detected from the path — override it, or
|
|
984
|
+
pass `disposition` / `cacheControl` / `etag: false`, via the options.
|
|
985
|
+
|
|
986
|
+
Cross-origin, the browser lets JavaScript read only the CORS-safelisted response
|
|
987
|
+
headers. stitchkit therefore exposes the download-relevant ones by default
|
|
988
|
+
(`Content-Disposition`, `Content-Range`, `ETag`, …) — without that a `fetch`-based
|
|
989
|
+
download cannot recover the file's name. Override with `cors.exposeHeaders`
|
|
990
|
+
(extend `DEFAULT_CORS_EXPOSE_HEADERS` rather than replacing it), or pass `[]` to
|
|
991
|
+
emit none.
|
|
992
|
+
|
|
993
|
+
`serveFile` takes an explicit `path` and trusts it — **the caller owns
|
|
994
|
+
containment**. For a URL-derived path use `staticRoute` (which enforces it) or
|
|
995
|
+
`isWithinDir` first. The byte-range parser is exported on its own as
|
|
996
|
+
`parseByteRange(header, size)` for direct use and testing. → ADR 0023.
|