stitchkit 0.55.0 → 0.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/socket-io.d.ts +14 -6
- package/dist/browser/socket-io.d.ts.map +1 -1
- package/dist/cli.js +4 -4
- package/dist/contract/errors.d.ts +7 -0
- package/dist/contract/errors.d.ts.map +1 -1
- package/dist/contract/index.js +1 -1
- package/dist/files/boundary.d.ts +8 -5
- package/dist/files/boundary.d.ts.map +1 -1
- package/dist/files.js +1 -1
- package/dist/{index-rgd18hx3.js → index-28cqssm3.js} +7 -0
- package/dist/index-557by2db.js +34 -0
- package/dist/index-5r13htq1.js +283 -0
- package/dist/index-6y759j86.js +1031 -0
- package/dist/{index-1k16zv57.js → index-bfcpjw20.js} +125 -31
- package/dist/{index-0gv7k0ra.js → index-escqg10p.js} +73 -666
- package/dist/{index-v4bbq3p4.js → index-j3dem06f.js} +1 -1
- package/dist/{index-qx84bqzk.js → index-jcc611vh.js} +1 -1
- package/dist/{index-npye5kt7.js → index-n4nfa7gh.js} +3 -3
- package/dist/{index-pzc32v75.js → index-sm2tjx06.js} +41 -4
- package/dist/{index-js7yexng.js → index-xy8fmh6w.js} +83 -4
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -6
- package/dist/node.js +5 -4
- package/dist/observability/index.js +3 -3
- package/dist/remote.js +1 -1
- package/dist/server/context-contribution.d.ts +8 -0
- package/dist/server/context-contribution.d.ts.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +8 -5
- package/dist/server/middleware/auth.d.ts +29 -2
- package/dist/server/middleware/auth.d.ts.map +1 -1
- package/dist/testing/surface-conformance.d.ts +143 -10
- package/dist/testing/surface-conformance.d.ts.map +1 -1
- package/dist/testing/surface-manifest.d.ts +132 -33
- package/dist/testing/surface-manifest.d.ts.map +1 -1
- package/dist/testing.d.ts +2 -2
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +470 -122
- package/dist/tools/async-operation.d.ts +210 -18
- package/dist/tools/async-operation.d.ts.map +1 -1
- package/dist/tools/define-download-tool.d.ts.map +1 -1
- package/dist/tools/define-upload-tool.d.ts.map +1 -1
- package/dist/tools/internal/surface-projector.d.ts +101 -0
- package/dist/tools/internal/surface-projector.d.ts.map +1 -0
- package/dist/tools/managed-file-error.d.ts +6 -0
- package/dist/tools/managed-file-error.d.ts.map +1 -0
- package/dist/tools/mcp-prepare.d.ts +3 -21
- package/dist/tools/mcp-prepare.d.ts.map +1 -1
- package/dist/tools/mcp-round-policy.d.ts +9 -0
- package/dist/tools/mcp-round-policy.d.ts.map +1 -0
- package/dist/tools/mcp-round.d.ts +0 -4
- package/dist/tools/mcp-round.d.ts.map +1 -1
- package/dist/tools/mount-download.d.ts.map +1 -1
- package/dist/tools/mount-upload.d.ts.map +1 -1
- package/dist/tools/mount.d.ts +5 -9
- package/dist/tools/mount.d.ts.map +1 -1
- package/dist/tools/runtime-tool.d.ts.map +1 -1
- package/dist/tools/surface.d.ts +7 -0
- package/dist/tools/surface.d.ts.map +1 -1
- package/dist/tools/view-file.d.ts.map +1 -1
- package/dist/tools.d.ts +1 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +348 -383
- package/llms-full.txt +361 -58
- package/package.json +1 -1
- package/dist/index-h60df7rj.js +0 -110
- package/dist/index-k4dftwf7.js +0 -66
package/llms-full.txt
CHANGED
|
@@ -3024,7 +3024,13 @@ import {
|
|
|
3024
3024
|
import { createManagedFileBoundary } from 'stitchkit/files'
|
|
3025
3025
|
import { z } from 'zod'
|
|
3026
3026
|
|
|
3027
|
-
const files = await createManagedFileBoundary({
|
|
3027
|
+
const files = await createManagedFileBoundary({
|
|
3028
|
+
root: '/srv/job-media',
|
|
3029
|
+
createRoot: true, // creates only this final directory; /srv must already be trusted
|
|
3030
|
+
inspectionTimeoutMs: 15_000,
|
|
3031
|
+
inspect: ({ prefix, name, declaredMediaType, signal }) =>
|
|
3032
|
+
inspectFile(prefix, { name, declaredMediaType, signal }),
|
|
3033
|
+
})
|
|
3028
3034
|
|
|
3029
3035
|
const waitForJob = defineWaitTool({
|
|
3030
3036
|
name: 'wait_for_job',
|
|
@@ -3131,16 +3137,14 @@ const runtimeTools = exportOperation.runtimeTools
|
|
|
3131
3137
|
```
|
|
3132
3138
|
|
|
3133
3139
|
Every follow-up repeats `authorize`; an opaque id is never authority. Aborting
|
|
3134
|
-
`wait` only stops waiting and never calls optional domain `cancel`.
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
id and snapshot Zod instances; declare each once and reuse it:
|
|
3140
|
+
`wait` only stops waiting and never calls optional domain `cancel`.
|
|
3141
|
+
|
|
3142
|
+
For a new HTTP surface, derive the canonical capability contract once. The
|
|
3143
|
+
factory defines schemas and routes only; the application still supplies normal
|
|
3144
|
+
handlers to `implement` and owns the job:
|
|
3140
3145
|
|
|
3141
3146
|
```ts
|
|
3142
|
-
import {
|
|
3143
|
-
import { bindContractAsyncOperation } from 'stitchkit/tools'
|
|
3147
|
+
import { defineAsyncOperationContract } from 'stitchkit/tools'
|
|
3144
3148
|
import { z } from 'zod'
|
|
3145
3149
|
|
|
3146
3150
|
const operationId = z.object({ id: z.string() })
|
|
@@ -3148,38 +3152,82 @@ const operationSnapshot = z.object({
|
|
|
3148
3152
|
phase: z.enum(['pending', 'running', 'succeeded', 'failed', 'cancelled']),
|
|
3149
3153
|
})
|
|
3150
3154
|
|
|
3151
|
-
const operations =
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3155
|
+
const operations = defineAsyncOperationContract({
|
|
3156
|
+
prefix: 'exports',
|
|
3157
|
+
scope: 'user',
|
|
3158
|
+
description: 'Export project data',
|
|
3159
|
+
startInput: z.object({ projectId: z.string() }),
|
|
3160
|
+
id: operationId,
|
|
3161
|
+
snapshot: operationSnapshot,
|
|
3162
|
+
cancel: true,
|
|
3163
|
+
result: z.object({ downloadUrl: z.url() }),
|
|
3164
|
+
})
|
|
3165
|
+
|
|
3166
|
+
// Pass operations.contract and ordinary handlers to implement(...).
|
|
3167
|
+
// operations.capabilities/schemas/adapters are the canonical protocol metadata.
|
|
3168
|
+
```
|
|
3169
|
+
|
|
3170
|
+
If an existing dedicated contract has a different wire shape — for example
|
|
3171
|
+
`start` returns a snapshot while follow-ups accept separate envelopes — bind it
|
|
3172
|
+
without casts or duplicate endpoints. Inline callbacks are contextually typed;
|
|
3173
|
+
every returned id/input is parsed by the destination schema:
|
|
3174
|
+
|
|
3175
|
+
```ts
|
|
3176
|
+
import { defineContract } from 'stitchkit/contract'
|
|
3177
|
+
import { bindContractAsyncOperation } from 'stitchkit/tools'
|
|
3178
|
+
|
|
3179
|
+
const startOutput = z.object({ operation: operationId, acceptedAt: z.string() })
|
|
3180
|
+
const statusInput = z.object({ operationId: z.string() })
|
|
3181
|
+
const waitInput = z.object({ lookup: operationId })
|
|
3182
|
+
|
|
3183
|
+
const existing = defineContract({ prefix: 'exports' }, {
|
|
3184
|
+
start: {
|
|
3185
|
+
method: 'POST', path: '/', desc: 'Start export',
|
|
3186
|
+
input: z.object({ projectId: z.string() }), output: startOutput,
|
|
3166
3187
|
},
|
|
3167
|
-
|
|
3188
|
+
status: {
|
|
3189
|
+
method: 'POST', path: '/status', desc: 'Read export status',
|
|
3190
|
+
input: statusInput, output: operationSnapshot,
|
|
3191
|
+
},
|
|
3192
|
+
wait: {
|
|
3193
|
+
method: 'POST', path: '/wait', desc: 'Wait for export',
|
|
3194
|
+
input: waitInput, output: operationSnapshot,
|
|
3195
|
+
},
|
|
3196
|
+
})
|
|
3168
3197
|
|
|
3169
3198
|
const operationHandlers = {
|
|
3170
|
-
start: () => ({ id: 'example' }),
|
|
3199
|
+
start: () => ({ operation: { id: 'example' }, acceptedAt: new Date().toISOString() }),
|
|
3171
3200
|
status: (): z.output<typeof operationSnapshot> => ({ phase: 'pending' }),
|
|
3172
3201
|
wait: (): z.output<typeof operationSnapshot> => ({ phase: 'succeeded' }),
|
|
3173
3202
|
}
|
|
3174
3203
|
|
|
3175
3204
|
const bound = bindContractAsyncOperation({
|
|
3176
3205
|
mode: 'contract-backed',
|
|
3177
|
-
|
|
3206
|
+
binding: 'adapted',
|
|
3207
|
+
contract: existing,
|
|
3208
|
+
id: operationId,
|
|
3178
3209
|
capabilities: { start: 'start', status: 'status', wait: 'wait' },
|
|
3210
|
+
adapters: {
|
|
3211
|
+
idFromStart: (output) => output.operation,
|
|
3212
|
+
inputFor: {
|
|
3213
|
+
status: (id) => ({ operationId: id.id }),
|
|
3214
|
+
wait: (id) => ({ lookup: id }),
|
|
3215
|
+
},
|
|
3216
|
+
},
|
|
3179
3217
|
handlers: operationHandlers,
|
|
3180
3218
|
})
|
|
3181
3219
|
```
|
|
3182
3220
|
|
|
3221
|
+
Direct binding remains the short form when start already returns the id and
|
|
3222
|
+
every follow-up reuses that exact schema instance. Its ID schema must be
|
|
3223
|
+
wire-stable: input and output types match and parsing has no transform,
|
|
3224
|
+
coercion, default or overwrite. This also excludes same-type transforms because
|
|
3225
|
+
the direct adapters would parse an already parsed ID again. Use adapted binding
|
|
3226
|
+
and explicitly project the parsed ID back into each follow-up input. In both
|
|
3227
|
+
modes the binder creates no router;
|
|
3228
|
+
it returns the existing contract, handlers and protocol metadata for
|
|
3229
|
+
application composition.
|
|
3230
|
+
|
|
3183
3231
|
### Explicit raw SDK registration
|
|
3184
3232
|
|
|
3185
3233
|
`rawTools` is deliberately named as an escape hatch. A tool registered there
|
|
@@ -3758,6 +3806,24 @@ socket.on('note:created', (note) => { /* typed note */ })
|
|
|
3758
3806
|
socket.emit('room:join', 'general', ({ joined }) => { /* typed + validated */ })
|
|
3759
3807
|
```
|
|
3760
3808
|
|
|
3809
|
+
When an application already owns the low-level Stitchkit transport, bind the
|
|
3810
|
+
contract without opening a second connection:
|
|
3811
|
+
|
|
3812
|
+
```ts
|
|
3813
|
+
import { bindRealtimeClient, createSocketIOClient } from 'stitchkit'
|
|
3814
|
+
|
|
3815
|
+
const transport = createSocketIOClient({ url: 'https://api.example.com' })
|
|
3816
|
+
const events = bindRealtimeClient(realtimeContract, transport, { onRejected })
|
|
3817
|
+
|
|
3818
|
+
transport.connect() // lifecycle stays with the transport owner
|
|
3819
|
+
events.on('note:created', handleNote)
|
|
3820
|
+
await events.request('room:join', 'general', { timeoutMs: 5_000 })
|
|
3821
|
+
```
|
|
3822
|
+
|
|
3823
|
+
The bound handle intentionally has no `connect()` or `disconnect()`. Its
|
|
3824
|
+
`on`/`emit`/`request`, rejection and timeout semantics are exactly the path used
|
|
3825
|
+
by `createRealtimeClient`; only transport construction/lifecycle differs.
|
|
3826
|
+
|
|
3761
3827
|
### Request-response over realtime
|
|
3762
3828
|
|
|
3763
3829
|
For an event with an `ack` schema, `request()` is the Promise form of the same
|
|
@@ -3792,6 +3858,7 @@ transport.
|
|
|
3792
3858
|
`createSocketIOClient` remains the low-level Socket.IO transport wrapper for
|
|
3793
3859
|
schema-agnostic infrastructure. Application wire events should use
|
|
3794
3860
|
`createRealtimeClient`; it adds the shared contract without replacing Socket.IO.
|
|
3861
|
+
Use `bindRealtimeClient` when that low-level transport already exists.
|
|
3795
3862
|
|
|
3796
3863
|
### Durable subscriptions
|
|
3797
3864
|
|
|
@@ -4344,10 +4411,10 @@ fields; `true | object` correctly makes them optional.
|
|
|
4344
4411
|
Annotate `rules` with `satisfies Record<MyScope, AuthRule<User>>` so the compiler
|
|
4345
4412
|
catches a scope you forgot to cover. With the scoped rule objects below, widen
|
|
4346
4413
|
the annotation to
|
|
4347
|
-
`satisfies Record<MyScope, AuthRule<User> | ScopedAuthRule<User,
|
|
4348
|
-
keeps the coverage check and does not disturb the derivation. (When every
|
|
4349
|
-
comes from the derived map, the check is already implicit: a scope missing
|
|
4350
|
-
`rules` is no key of the map, and a contract using it fails to compile.)
|
|
4414
|
+
`satisfies Record<MyScope, AuthRule<User> | ScopedAuthRule<User, AuthRuleContribution>>`
|
|
4415
|
+
— it keeps the coverage check and does not disturb the derivation. (When every
|
|
4416
|
+
scope comes from the derived map, the check is already implicit: a scope missing
|
|
4417
|
+
from `rules` is no key of the map, and a contract using it fails to compile.)
|
|
4351
4418
|
|
|
4352
4419
|
### Scoped rules — the map `createScopedImplement` consumes
|
|
4353
4420
|
|
|
@@ -4393,11 +4460,58 @@ widens its inject and degrades that scope's fields to `object`.
|
|
|
4393
4460
|
|
|
4394
4461
|
Derivation needs the identity generic to be **inferred** (write no explicit
|
|
4395
4462
|
`createAuthHook<User>` — TypeScript has no partial inference); with an explicit
|
|
4396
|
-
generic, or fields injected outside the hook, keep the hand-written map.
|
|
4397
|
-
hooks guarding different scopes compose by intersection:
|
|
4398
|
-
`createScopedImplement<AuthScopes<typeof userHook> & AuthScopes<typeof botHook>>()`.
|
|
4463
|
+
generic, or fields injected outside the hook, keep the hand-written map.
|
|
4399
4464
|
→ ADR 0078
|
|
4400
4465
|
|
|
4466
|
+
### Multiple auth domains — `composeAuthHooks`
|
|
4467
|
+
|
|
4468
|
+
Independent identity domains should keep separate resolvers and rules. Compose
|
|
4469
|
+
their canonical hooks and derive one handler context without a manual dispatcher
|
|
4470
|
+
or scope-map intersection:
|
|
4471
|
+
|
|
4472
|
+
```ts
|
|
4473
|
+
const userAuth = createAuthHook({
|
|
4474
|
+
resolve: resolveUser,
|
|
4475
|
+
resolveFromContext: resolveToolUser,
|
|
4476
|
+
rules: {
|
|
4477
|
+
user: (user) => ({ userId: user.id }),
|
|
4478
|
+
workspace: (user) => ({ userId: user.id }),
|
|
4479
|
+
},
|
|
4480
|
+
})
|
|
4481
|
+
|
|
4482
|
+
const workspaceAuth = createAuthHook({
|
|
4483
|
+
resolve: resolveMembership,
|
|
4484
|
+
resolveFromContext: resolveToolMembership,
|
|
4485
|
+
rules: {
|
|
4486
|
+
workspace: (membership) => ({ workspaceId: membership.workspaceId }),
|
|
4487
|
+
},
|
|
4488
|
+
})
|
|
4489
|
+
|
|
4490
|
+
export const auth = composeAuthHooks({
|
|
4491
|
+
hooks: [userAuth, workspaceAuth],
|
|
4492
|
+
defaultScope: 'user',
|
|
4493
|
+
})
|
|
4494
|
+
export const implementFor = createScopedImplement<AuthScopes<typeof auth>>()
|
|
4495
|
+
```
|
|
4496
|
+
|
|
4497
|
+
Only hooks that declare the selected scope run. An unknown scope fails closed;
|
|
4498
|
+
when several hooks own one scope, every owner must pass in declaration order.
|
|
4499
|
+
Each owner evaluates on an isolated shadow context. Stitchkit validates all
|
|
4500
|
+
changed fields, rejects reserved/unsafe fields and cross-owner key collisions,
|
|
4501
|
+
then commits the combined contribution once. A rejected or cancelled composite
|
|
4502
|
+
therefore exposes no partial fields to the handler.
|
|
4503
|
+
|
|
4504
|
+
Isolation is per-key, not deep: a contribution is the set of context keys whose
|
|
4505
|
+
descriptor changed. Mutating an existing value **in place**
|
|
4506
|
+
(`ctx.user.role = 'admin'`) edits the object every owner already shares, so it
|
|
4507
|
+
is neither reported as a contribution nor checked for collisions — contribute
|
|
4508
|
+
new fields instead of editing another owner's object. External side effects
|
|
4509
|
+
likewise remain consumer-owned and are not rolled back.
|
|
4510
|
+
|
|
4511
|
+
The composite's `defaultScope` is the only implicit scope. A child hook's own
|
|
4512
|
+
default must be absent or equal to it, so reordering hooks cannot change which
|
|
4513
|
+
scope protects an endpoint.
|
|
4514
|
+
|
|
4401
4515
|
### Auth on the tool surface — `resolveFromContext`
|
|
4402
4516
|
|
|
4403
4517
|
The same hook guards every transport, but the lifecycle slot and identity
|
|
@@ -5332,35 +5446,98 @@ exercise the same framework pipeline.
|
|
|
5332
5446
|
|
|
5333
5447
|
### Transport conformance
|
|
5334
5448
|
|
|
5335
|
-
`buildSurfaceManifest` snapshots
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5449
|
+
`buildSurfaceManifest` v2 snapshots canonical operations separately from actual
|
|
5450
|
+
HTTP topology and mounted MCP/Agent/CLI projections. Named MCP surfaces keep
|
|
5451
|
+
their own advertised input digests (including `extend`, flattening and schema
|
|
5452
|
+
policy), and named realtime contracts keep directional argument/ack input and
|
|
5453
|
+
output digests. Compare only discovery you actually observed, then run explicit
|
|
5454
|
+
drivers for the transports you provide:
|
|
5339
5455
|
|
|
5340
5456
|
```ts
|
|
5457
|
+
import { bindRealtimeClient } from 'stitchkit'
|
|
5341
5458
|
import {
|
|
5342
5459
|
assertSurfaceDiscovery,
|
|
5343
5460
|
buildSurfaceManifest,
|
|
5461
|
+
createRealtimeProbeDriver,
|
|
5462
|
+
defineRealtimeProbe,
|
|
5344
5463
|
runSurfaceProbes,
|
|
5345
5464
|
} from 'stitchkit/testing'
|
|
5346
5465
|
|
|
5347
|
-
const manifest = buildSurfaceManifest({
|
|
5466
|
+
const manifest = buildSurfaceManifest({
|
|
5467
|
+
groups: [{ pathPrefix: '/api', services: httpServices }],
|
|
5468
|
+
mcpPreparation: { extend, schemaValidation, multiRound },
|
|
5469
|
+
mcpSurfaces: {
|
|
5470
|
+
member: { services: memberServices, runtimeTools },
|
|
5471
|
+
admin: { services: adminServices, runtimeTools },
|
|
5472
|
+
},
|
|
5473
|
+
toolSurfaces: {
|
|
5474
|
+
AGENT: { services: agentServices, runtimeTools },
|
|
5475
|
+
CLI: { services: cliServices, runtimeTools: cliRuntimeTools },
|
|
5476
|
+
},
|
|
5477
|
+
realtime: { primary: realtimeContract },
|
|
5478
|
+
cliCommands,
|
|
5479
|
+
})
|
|
5348
5480
|
assertSurfaceDiscovery(manifest, {
|
|
5349
5481
|
openApi,
|
|
5350
|
-
|
|
5482
|
+
toolSurfaces: [{
|
|
5483
|
+
transport: 'MCP',
|
|
5484
|
+
surface: 'member',
|
|
5485
|
+
names: (await mcpClient.listTools()).tools.map((tool) => tool.name),
|
|
5486
|
+
}],
|
|
5351
5487
|
AGENT: Object.keys(agentTools),
|
|
5352
5488
|
CLI: cliHelpNames,
|
|
5489
|
+
realtime: {
|
|
5490
|
+
primary: { serverToClient: observedServerEvents, clientToServer: observedClientEvents },
|
|
5491
|
+
},
|
|
5492
|
+
})
|
|
5493
|
+
|
|
5494
|
+
const invalidInbound = defineRealtimeProbe({
|
|
5495
|
+
name: 'invalid inbound payload',
|
|
5496
|
+
scenario: 'invalid_arguments',
|
|
5497
|
+
fixture: invalidPayloadFixture,
|
|
5498
|
+
expected: {
|
|
5499
|
+
outcome: 'realtime_rejected',
|
|
5500
|
+
code: 'REALTIME_CONTRACT_VIOLATION',
|
|
5501
|
+
rejection: {
|
|
5502
|
+
direction: 'server-inbound', phase: 'arguments',
|
|
5503
|
+
reason: 'invalid-arguments', fault: 'peer',
|
|
5504
|
+
},
|
|
5505
|
+
handlerCalls: 0,
|
|
5506
|
+
},
|
|
5507
|
+
})
|
|
5508
|
+
|
|
5509
|
+
const realtimeDriver = createRealtimeProbeDriver({
|
|
5510
|
+
bind: (onRejected, fixture) => {
|
|
5511
|
+
const client = bindRealtimeClient(realtimeContract, existingTransport, { onRejected })
|
|
5512
|
+
const scenario = bindApplicationRealtimeScenario(client, fixture)
|
|
5513
|
+
return {
|
|
5514
|
+
connected: () => client.connected,
|
|
5515
|
+
invoke: scenario.invoke,
|
|
5516
|
+
dispose: scenario.dispose, // subscriptions only; never disconnect the transport
|
|
5517
|
+
}
|
|
5518
|
+
},
|
|
5519
|
+
handlerCalls: () => applicationRealtimeHandlerCalls,
|
|
5353
5520
|
})
|
|
5354
5521
|
|
|
5355
5522
|
await runSurfaceProbes({
|
|
5356
|
-
probes,
|
|
5357
|
-
drivers: {
|
|
5523
|
+
probes: [invalidInbound],
|
|
5524
|
+
drivers: { REALTIME: realtimeDriver },
|
|
5358
5525
|
})
|
|
5359
5526
|
```
|
|
5360
5527
|
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5528
|
+
MCP preparation is global because the real MCP mount has one preparation
|
|
5529
|
+
policy; named surfaces select tools, while `extend.filter` decides which
|
|
5530
|
+
selected operations receive extra fields. CLI selection is deliberately plain,
|
|
5531
|
+
and Agent owns its own reachable presentation shaping.
|
|
5532
|
+
|
|
5533
|
+
The kit never starts a server, discovers Socket.IO topology remotely, invents
|
|
5534
|
+
credentials or synthesises invalid Zod values. Fixtures and observations come
|
|
5535
|
+
from the application. The realtime driver creates a rejection channel per
|
|
5536
|
+
scenario, observes connection state before invocation, and disposes only
|
|
5537
|
+
probe-owned subscriptions. Each scenario has one absolute deadline shared by
|
|
5538
|
+
signalled setup, invocation and teardown. An outer timeout stops waiting; it
|
|
5539
|
+
does not disconnect a foreign transport or retract an already emitted packet.
|
|
5540
|
+
A missing driver is unsupported, not silently marked conformant.
|
|
5364
5541
|
|
|
5365
5542
|
### Test handlers with raw Requests
|
|
5366
5543
|
|
|
@@ -5921,7 +6098,95 @@ current one *up to* your target, and apply each snippet.
|
|
|
5921
6098
|
runtime): bootstrap the server, one HTTP request, and any feature you rely on
|
|
5922
6099
|
(Socket.IO connect, an MCP tool call, a multipart upload, …).
|
|
5923
6100
|
|
|
5924
|
-
##
|
|
6101
|
+
## Released migration: 0.56.0
|
|
6102
|
+
|
|
6103
|
+
### Surface manifests are version 2
|
|
6104
|
+
|
|
6105
|
+
`operation.tools` could not describe a role-selected MCP surface, a different
|
|
6106
|
+
Agent or CLI selection, or an advertised `extend` schema, so projections moved
|
|
6107
|
+
out of the canonical operation:
|
|
6108
|
+
|
|
6109
|
+
```ts
|
|
6110
|
+
// before
|
|
6111
|
+
manifest.operations[0].tools.MCP
|
|
6112
|
+
// after
|
|
6113
|
+
manifest.toolSurfaces.find((s) => s.transport === 'MCP' && s.surface === null)?.tools
|
|
6114
|
+
|
|
6115
|
+
// before
|
|
6116
|
+
buildSurfaceManifest({ mcpSurfaces: { admin: { services, extend } } })
|
|
6117
|
+
// after
|
|
6118
|
+
buildSurfaceManifest({ mcpSurfaces: { admin: { services } }, mcpPreparation: { extend } })
|
|
6119
|
+
```
|
|
6120
|
+
|
|
6121
|
+
A committed snapshot is regenerated once, deliberately: `manifestVersion` is
|
|
6122
|
+
`2`, and `ConformanceTransport` gained `REALTIME`, so any exhaustive
|
|
6123
|
+
`Record<ConformanceTransport, …>` must handle it.
|
|
6124
|
+
|
|
6125
|
+
### `FILE_*` codes joined the error registry
|
|
6126
|
+
|
|
6127
|
+
`StitchErrorCode` gained `FILE_INVALID_PATH`, `FILE_OUTSIDE_ROOT`,
|
|
6128
|
+
`FILE_NOT_FOUND`, `FILE_NOT_REGULAR`, `FILE_INSPECTION_REJECTED`,
|
|
6129
|
+
`FILE_TOO_LARGE` and `FILE_EXISTS`. Only exhaustive maps break:
|
|
6130
|
+
|
|
6131
|
+
```ts
|
|
6132
|
+
// before — compiled while the registry had no file codes
|
|
6133
|
+
const copy: Record<StitchErrorCode, string> = { …, RATE_LIMITED: '…' }
|
|
6134
|
+
// after — add the seven managed-file codes (or use Partial<Record<…>>)
|
|
6135
|
+
```
|
|
6136
|
+
|
|
6137
|
+
Unexpected IO stays scrubbed as `INTERNAL_SERVER_ERROR`: the new codes are the
|
|
6138
|
+
caller-safe ones only.
|
|
6139
|
+
|
|
6140
|
+
### `ScopedAuthHook` is nominal
|
|
6141
|
+
|
|
6142
|
+
A hand-written function shaped like an auth hook is no longer assignable —
|
|
6143
|
+
identity now comes from the factory, so scope ownership and the inferred
|
|
6144
|
+
context cannot drift apart:
|
|
6145
|
+
|
|
6146
|
+
```ts
|
|
6147
|
+
// before — a structural stand-in
|
|
6148
|
+
const auth: ScopedAuthHook<Scopes> = async (ctx, endpoint) => { … }
|
|
6149
|
+
// after — create it, then compose domains
|
|
6150
|
+
const auth = createAuthHook({ resolve, rules })
|
|
6151
|
+
const composed = composeAuthHooks({ hooks: [auth, billingAuth], defaultScope: 'public' })
|
|
6152
|
+
```
|
|
6153
|
+
|
|
6154
|
+
### Managed-file inspectors also run on reads
|
|
6155
|
+
|
|
6156
|
+
An inspector is no longer write-only, and it has a finite default deadline
|
|
6157
|
+
(15 s). Make it read-aware and idempotent — a read carries no
|
|
6158
|
+
`declaredMediaType`:
|
|
6159
|
+
|
|
6160
|
+
```ts
|
|
6161
|
+
// before
|
|
6162
|
+
inspect: ({ declaredMediaType }) => inspectDeclaredType(declaredMediaType!)
|
|
6163
|
+
// after
|
|
6164
|
+
inspect: ({ prefix, declaredMediaType, signal }) =>
|
|
6165
|
+
inspectBytes(prefix, { declaredMediaType, signal })
|
|
6166
|
+
```
|
|
6167
|
+
|
|
6168
|
+
Set `inspectionTimeoutMs` explicitly when 15 seconds is the wrong budget.
|
|
6169
|
+
|
|
6170
|
+
### Direct async-operation binding needs a wire-stable ID
|
|
6171
|
+
|
|
6172
|
+
A direct binding reuses the start output as the follow-up wire input, so the ID
|
|
6173
|
+
schema must parse to itself (`z.input` equals `z.output`, no transform,
|
|
6174
|
+
coercion, default or overwrite). Anything else is parsed twice:
|
|
6175
|
+
|
|
6176
|
+
```ts
|
|
6177
|
+
// before — a transform silently ran on start and again on every follow-up
|
|
6178
|
+
defineAsyncOperationContract({ binding: 'direct', id: z.string().transform(Number) })
|
|
6179
|
+
// after — keep the wire shape, adapt explicitly
|
|
6180
|
+
defineAsyncOperationContract({
|
|
6181
|
+
binding: 'adapted',
|
|
6182
|
+
id,
|
|
6183
|
+
adapters: { idFromStart, inputFor },
|
|
6184
|
+
})
|
|
6185
|
+
```
|
|
6186
|
+
|
|
6187
|
+
## Released migration: 0.55.0
|
|
6188
|
+
|
|
6189
|
+
### Peer-free `implementRemote`
|
|
5925
6190
|
|
|
5926
6191
|
`implementRemote` now has one canonical, optional-peer-free owner. This keeps
|
|
5927
6192
|
MCP SDK and AI SDK modules out of CLI bundles that only proxy HTTP calls:
|
|
@@ -5933,7 +6198,7 @@ import { implementRemote } from 'stitchkit/tools'
|
|
|
5933
6198
|
import { implementRemote } from 'stitchkit/remote'
|
|
5934
6199
|
```
|
|
5935
6200
|
|
|
5936
|
-
|
|
6201
|
+
### Managed file boundary and strict auth returns
|
|
5937
6202
|
|
|
5938
6203
|
Create one boundary during application bootstrap and pass the capability, never
|
|
5939
6204
|
a per-call directory or host path:
|
|
@@ -6927,6 +7192,7 @@ The browser-and-server entrypoint. Re-exports everything from
|
|
|
6927
7192
|
| `createSocketIOClient` | function | low-level typed Socket.IO transport primitive — [guide](../guide/realtime.md#low-level-transport) |
|
|
6928
7193
|
| `defineRealtimeContract` | function | Zod-first shared Socket.IO event contract — [guide](../guide/realtime.md#zod-first-event-contract) |
|
|
6929
7194
|
| `createRealtimeClient` | function | inferred, runtime-validated Socket.IO client — [guide](../guide/realtime.md#client--createrealtimeclient) |
|
|
7195
|
+
| `bindRealtimeClient` | function | bind contract validation and typed acknowledgements to an existing Stitchkit client transport without owning its lifecycle |
|
|
6930
7196
|
| `createRetainedTopics` | function | retained last-value store for sticky events — [guide](../guide/realtime.md#sticky-events) |
|
|
6931
7197
|
| `parseSSE` | function | parse an SSE `Response` into an async generator — [guide](../guide/client.md#sse) |
|
|
6932
7198
|
| `SocketIOClient` | _type_ | low-level client handle; `emit` reports disconnected drops and `emitWithAck` exposes the native Promise primitive used by validated `request()` |
|
|
@@ -6934,6 +7200,9 @@ The browser-and-server entrypoint. Re-exports everything from
|
|
|
6934
7200
|
| `SocketEventMap` | _type_ | the shape of an event map |
|
|
6935
7201
|
| `RealtimeClient` | _type_ | validated client inferred from a realtime contract |
|
|
6936
7202
|
| `RealtimeClientOptions` | _type_ | transport options and the rejected-event hook for `createRealtimeClient` |
|
|
7203
|
+
| `BoundRealtimeClient` | _type_ | validated non-owning `on`/`emit`/`request` client with no `connect`/`disconnect` |
|
|
7204
|
+
| `RealtimeClientTransport` | _type_ | minimal existing transport capability accepted by `bindRealtimeClient` |
|
|
7205
|
+
| `BindRealtimeClientOptions` | _type_ | rejection/logger options for a bound existing transport |
|
|
6937
7206
|
| `RealtimeAcknowledgedEvent` | _type_ | event-name union restricted to definitions with an `ack` schema |
|
|
6938
7207
|
| `RealtimeAcknowledgement` | _type_ | validated acknowledgement output inferred from an event definition |
|
|
6939
7208
|
| `RealtimeRequestArguments` | _type_ | request arguments inferred from an acknowledged event tuple |
|
|
@@ -7140,6 +7409,7 @@ Also re-exports the error helpers from `stitchkit/contract`.
|
|
|
7140
7409
|
| Export | Kind | Summary |
|
|
7141
7410
|
|--------|------|---------|
|
|
7142
7411
|
| `createAuthHook` | function | one scope gate for HTTP `authorize` and tool `beforeHandle` — [guide](../guide/auth-and-errors.md#createauthhook) |
|
|
7412
|
+
| `composeAuthHooks` | function | route multiple canonical auth domains by owned scope and atomically commit their typed contributions |
|
|
7143
7413
|
| `createErrorHook` | function | an async-capable, endpoint-aware `onError` hook from a code map + envelope renderer — [guide](../guide/auth-and-errors.md#createerrorhook) |
|
|
7144
7414
|
| `ErrorHookConfig` | _type_ | async observer/renderer config for `createErrorHook` |
|
|
7145
7415
|
| `ResolvedError` | _type_ | the normalised error handed to `createErrorHook`'s `render` |
|
|
@@ -7159,6 +7429,8 @@ Also re-exports the error helpers from `stitchkit/contract`.
|
|
|
7159
7429
|
| `RuleScopes` | _type_ | scope→context map derived from a `rules` object; `'public'` fields become optional |
|
|
7160
7430
|
| `ScopedAuthHook` | _type_ | an auth hook carrying its derived scope map at the type level |
|
|
7161
7431
|
| `AuthScopes` | _type_ | recover the derived map — `createScopedImplement<AuthScopes<typeof hook>>()` |
|
|
7432
|
+
| `ComposeAuthHooksConfig` | _type_ | ordered canonical hooks plus the optional explicit composite default scope |
|
|
7433
|
+
| `ComposedAuthScopes` | _type_ | scope→context map derived from every owner in a composed auth hook |
|
|
7162
7434
|
| `BearerResolverConfig` | _type_ | config for `createBearerResolver` |
|
|
7163
7435
|
| `JwtPayload` | _type_ | a decoded JWT payload |
|
|
7164
7436
|
| `SignJwtOptions` | _type_ | options for `signJwt` (expiry, claims) |
|
|
@@ -7477,6 +7749,7 @@ runtime-tool runner, plus deliberate raw MCP adapters over the same mechanics.
|
|
|
7477
7749
|
| `ManagedWaitRender` | _type_ | optional managed wait terminal text and failure classification |
|
|
7478
7750
|
| `UploadToolInputSchema` | constant | fixed `{ path: string }` input schema for `defineUploadTool` |
|
|
7479
7751
|
| `defineAsyncOperation` | function | runtime-only start/status/wait plus configured cancel/result/artifacts definitions |
|
|
7752
|
+
| `defineAsyncOperationContract` | function | define one canonical Zod-first HTTP contract for start/status/wait plus optional capabilities |
|
|
7480
7753
|
| `bindContractAsyncOperation` | function | bind literal methods from an existing contract without creating another HTTP surface |
|
|
7481
7754
|
| `createAsyncOperationSnapshotSchema` | function | canonical pending/running/succeeded/failed/cancelled Zod snapshot |
|
|
7482
7755
|
| `AsyncOperationCancelResultSchema` | constant | validated accepted/already_terminal/rejected cancellation result |
|
|
@@ -7488,9 +7761,18 @@ runtime-tool runner, plus deliberate raw MCP adapters over the same mechanics.
|
|
|
7488
7761
|
| `AsyncOperationOutputCapability` | _type_ | optional result/artifact schema plus handler |
|
|
7489
7762
|
| `RuntimeAsyncOperationConfig` | _type_ | runtime-only descriptor configuration |
|
|
7490
7763
|
| `RuntimeAsyncOperation` | _type_ | inferred generated definitions and schemas |
|
|
7764
|
+
| `AsyncOperationContractConfig` | _type_ | canonical contract config where start returns the operation id |
|
|
7765
|
+
| `AsyncOperationContractWithStartOutputConfig` | _type_ | canonical contract config with an application start envelope and typed id extractor |
|
|
7766
|
+
| `DefinedAsyncOperationContract` | _type_ | generated contract, capability keys, schemas and parsed adapters |
|
|
7491
7767
|
| `ContractAsyncOperationConfig` | _type_ | literal contract method binding and handlers |
|
|
7768
|
+
| `AdaptedContractAsyncOperationConfig` | _type_ | existing-contract binding with explicit id and per-capability input adapters |
|
|
7769
|
+
| `BoundAdaptedContractAsyncOperation` | _type_ | inferred adapted binding with parsed id/input projectors |
|
|
7770
|
+
| `AdaptedContractAsyncOperationStartKey` | _type_ | existing-contract keys with a declared start output |
|
|
7771
|
+
| `AdaptedContractAsyncOperationFollowKey` | _type_ | existing-contract keys with both input and output schemas |
|
|
7772
|
+
| `AdaptedContractAsyncOperationWaitKey` | _type_ | adapted follow-up keys whose output matches the selected status snapshot |
|
|
7773
|
+
| `ContractAsyncOperationInputAdapters` | _type_ | guaranteed direct-binding status/wait input adapters plus configured optional capabilities |
|
|
7492
7774
|
| `ContractAsyncOperationKeys` | _type_ | literal method-key union of a bound contract |
|
|
7493
|
-
| `ContractAsyncOperationStartKey` | _type_ |
|
|
7775
|
+
| `ContractAsyncOperationStartKey` | _type_ | direct-binding start keys whose declared ID schema has stable input/output |
|
|
7494
7776
|
| `ContractAsyncOperationFollowKey` | _type_ | contract keys whose input schema type matches the selected start output |
|
|
7495
7777
|
| `ContractAsyncOperationWaitKey` | _type_ | follow-up keys whose output schema type also matches the selected status output |
|
|
7496
7778
|
| `composeToolLifecycle` | function | ordered composition of tool before/after phases |
|
|
@@ -7573,21 +7855,42 @@ handler pipeline without opening a TCP port.
|
|
|
7573
7855
|
| `HandlerTestClientConfig` | _type_ | handler, contract, path prefix, scoped config and client request defaults |
|
|
7574
7856
|
| `HandlerTestClientsConfig` | _type_ | batch helper configuration |
|
|
7575
7857
|
| `HandlerTestTransportConfig` | _type_ | shared in-process handler, origin, prefix, client defaults and optional server handle |
|
|
7576
|
-
| `buildSurfaceManifest` | function | deterministic
|
|
7858
|
+
| `buildSurfaceManifest` | function | deterministic manifest v2 with HTTP topology, transport-specific tool projections, named MCP surfaces and realtime schema digests |
|
|
7577
7859
|
| `assertSurfaceManifestSnapshot` | function | bounded deterministic manifest drift assertion |
|
|
7578
|
-
| `assertSurfaceDiscovery` | function | compare real OpenAPI/
|
|
7579
|
-
| `runSurfaceProbes` | function | execute explicit transport drivers
|
|
7580
|
-
| `
|
|
7860
|
+
| `assertSurfaceDiscovery` | function | compare real OpenAPI/tool/realtime caller-observed discovery to the exact manifest projection |
|
|
7861
|
+
| `runSurfaceProbes` | function | execute explicit transport drivers under one per-scenario setup/invoke/teardown deadline |
|
|
7862
|
+
| `defineRealtimeProbe` | function | declare one canonical realtime scenario for a caller-supplied real transport driver |
|
|
7863
|
+
| `createRealtimeProbeDriver` | function | bind each scenario to a caller-owned transport and normalize actual event/ack/rejection/disconnect outcomes without lifecycle ownership |
|
|
7864
|
+
| `TransportObservationSchema` | constant | normalized HTTP/tool/realtime outcome including rejection fields and handler-call evidence |
|
|
7865
|
+
| `RealtimeRejectionObservationSchema` | constant | direction/phase/reason/fault observation for a rejected realtime event |
|
|
7866
|
+
| `RealtimeDisconnectObservationSchema` | constant | observed before-invoke versus in-flight disconnect phase |
|
|
7581
7867
|
| `ConformanceTransport` | _type_ | explicit probe transport union |
|
|
7582
7868
|
| `RunSurfaceProbesConfig` | _type_ | probes, drivers and diagnostic byte cap |
|
|
7583
7869
|
| `SurfaceDiscoveryObservation` | _type_ | real OpenAPI/tool/CLI/extension discovery values |
|
|
7870
|
+
| `SurfaceToolDiscoveryObservation` | _type_ | transport, optional named surface and caller-observed tool names |
|
|
7871
|
+
| `SurfaceRealtimeDiscoveryObservation` | _type_ | caller-observed server→client and client→server event names |
|
|
7584
7872
|
| `SurfaceProbe` / `SurfaceProbeDriver` | _type_ | one bounded scenario and its consumer-supplied runner |
|
|
7873
|
+
| `DefineRealtimeProbeConfig` | _type_ | name, canonical scenario, explicit fixture and expected realtime outcome |
|
|
7874
|
+
| `CreateRealtimeProbeDriverConfig` | _type_ | per-scenario foreign-transport binder and optional handler-call counter |
|
|
7875
|
+
| `RealtimeProbeAdapter` | _type_ | connected-state observation, scenario invocation and subscription-only cleanup |
|
|
7876
|
+
| `RealtimeProbeFixture` / `RealtimeProbeScenario` | _type_ | driver input and supported event/ack/invalid/disconnect/timeout scenario vocabulary |
|
|
7877
|
+
| `RealtimeRejectionObservation` | _type_ | parsed structured realtime rejection observation |
|
|
7878
|
+
| `RealtimeDisconnectObservation` | _type_ | normalized physical timing of a realtime disconnect |
|
|
7585
7879
|
| `TransportObservation` | _type_ | validated normalized driver result |
|
|
7586
7880
|
| `SurfaceManifest` / `SurfaceManifestConfig` | _type_ | deterministic surface snapshot and its inputs |
|
|
7587
7881
|
| `SurfaceManifestOperation` / `SurfaceManifestOperationSchema` | _type_ / schema | one contract or runtime operation row |
|
|
7882
|
+
| `SurfaceManifestTool` / `SurfaceManifestToolSchema` | _type_ / schema | one mounted tool row with advertised input digest |
|
|
7883
|
+
| `SurfaceManifestToolSurface` / `SurfaceManifestToolSurfaceSchema` | _type_ / schema | one static transport projection, optionally keyed for a finite MCP surface |
|
|
7884
|
+
| `SurfaceManifestRealtimeEvent` / `SurfaceManifestRealtimeEventSchema` | _type_ / schema | one named directional realtime event with input/output args and ack digests |
|
|
7588
7885
|
| `SurfaceManifestExtension` / `SurfaceManifestExtensionSchema` | _type_ / schema | declared transport extension row |
|
|
7589
7886
|
| `SurfaceManifestSchema` | schema | complete deterministic manifest schema |
|
|
7590
|
-
| `
|
|
7887
|
+
| `SurfaceToolDefinition` | _type_ | plain service/runtime selection used by CLI and finite named MCP surfaces |
|
|
7888
|
+
| `SurfaceAgentProjection` | _type_ | Agent selection plus its reachable extend/flatten presentation policy |
|
|
7889
|
+
| `SurfaceMcpPreparation` | _type_ | one global MCP extend/flatten/schema-validation/multi-round preparation policy |
|
|
7890
|
+
| `SurfaceRuntimeToolDefinition` | _type_ | peer-free non-executable runtime-operation descriptor for manifest projection |
|
|
7891
|
+
| `SurfaceToolExtension` | _type_ | canonical structural tool extension including schema, resolver and optional filter |
|
|
7892
|
+
| `McpSchemaValidationConfig` / `IncompatibleSchemaPolicy` | _type_ | canonical MCP schema-preparation policy used by real mounts and manifests |
|
|
7893
|
+
| `SurfaceRealtimeSchemaPairSchema` | schema | input/output digest pair for args or acknowledgements |
|
|
7591
7894
|
| `SurfaceSchemaDigestsSchema` | schema | params/input/output/multipart digest object |
|
|
7592
7895
|
| `serializeSurfaceValue` | function | canonical versioned serialization used for deterministic digests |
|
|
7593
7896
|
|
|
@@ -7600,16 +7903,16 @@ available from `stitchkit/contract`.
|
|
|
7600
7903
|
|
|
7601
7904
|
| Export | Kind | Summary |
|
|
7602
7905
|
|--------|------|---------|
|
|
7603
|
-
| `createManagedFileBoundary` | function | bind
|
|
7906
|
+
| `createManagedFileBoundary` | function | bind an application-owned root, optionally creating one final directory under a trusted existing parent |
|
|
7604
7907
|
| `ManagedFileBoundary` | _type_ | non-reopenable `read`/`write` capability over canonical relative paths |
|
|
7605
|
-
| `ManagedFileBoundaryConfig` | _type_ | bound root, finite limits, inspector and cleanup observer |
|
|
7908
|
+
| `ManagedFileBoundaryConfig` | _type_ | bound root, optional `createRoot`, finite read/write/inspection limits, inspector and cleanup observer |
|
|
7606
7909
|
| `ManagedFileRefSchema` / `ManagedFileRef` | schema / _type_ | transport-safe relative path, measured size and optional media metadata |
|
|
7607
7910
|
| `ManagedFilePathSchema` / `ManagedFilePath` | schema / _type_ | canonical POSIX relative managed-file path |
|
|
7608
7911
|
| `ManagedFileSource` | _type_ | bounded immutable bytes plus validated ref passed to upload callbacks |
|
|
7609
7912
|
| `ManagedFileReadOptions` / `ManagedFileWriteOptions` | _type_ | per-operation byte cap, signal and atomic write policy |
|
|
7610
|
-
| `ManagedFileError` / `ManagedFileErrorCode` | class / _type_ | stable caller-safe
|
|
7611
|
-
| `ManagedFileInspector` | _type_ | bounded-prefix
|
|
7612
|
-
| `ManagedFileInspectionInput` / `ManagedFileInspection` | _type_ | inspector input and
|
|
7913
|
+
| `ManagedFileError` / `ManagedFileErrorCode` | class / _type_ | stable boundary failures; registered `FILE_*` mistakes are caller-safe while `FILE_IO_ERROR` remains internal |
|
|
7914
|
+
| `ManagedFileInspector` | _type_ | bounded-prefix read/write inspection callback with a finite cancellation signal that cannot own path or size |
|
|
7915
|
+
| `ManagedFileInspectionInput` / `ManagedFileInspection` | _type_ | inspector prefix/name/declared media/signal input and validated metadata-only result |
|
|
7613
7916
|
|
|
7614
7917
|
---
|
|
7615
7918
|
|
package/package.json
CHANGED