@abide/abide 0.41.1 → 0.43.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/AGENTS.md +234 -294
- package/CHANGELOG.md +82 -0
- package/README.md +51 -95
- package/package.json +3 -2
- package/src/abideLsp.ts +46 -8
- package/src/abideResolverPlugin.ts +107 -19
- package/src/buildCli.ts +1 -1
- package/src/discoveryEntry.ts +3 -3
- package/src/lib/bundle/BundleMenuItem.ts +1 -1
- package/src/lib/cli/createClient.ts +12 -12
- package/src/lib/cli/dispatchCommand.ts +1 -1
- package/src/lib/cli/printSessionStatus.ts +1 -1
- package/src/lib/cli/resolveCliTarget.ts +1 -1
- package/src/lib/cli/runCli.ts +1 -1
- package/src/lib/cli/types/CliManifest.ts +1 -1
- package/src/lib/cli/types/CliManifestEntry.ts +2 -2
- package/src/lib/mcp/annotationsForMethod.ts +5 -5
- package/src/lib/mcp/createMcpServer.ts +3 -3
- package/src/lib/mcp/mcpSurface.ts +14 -14
- package/src/lib/mcp/types/McpServerOptions.ts +1 -1
- package/src/lib/server/DELETE.ts +4 -4
- package/src/lib/server/GET.ts +4 -4
- package/src/lib/server/HEAD.ts +4 -4
- package/src/lib/server/PATCH.ts +4 -4
- package/src/lib/server/POST.ts +4 -4
- package/src/lib/server/PUT.ts +4 -4
- package/src/lib/server/agent.ts +4 -4
- package/src/lib/server/env.ts +1 -1
- package/src/lib/server/error.ts +49 -7
- package/src/lib/server/json.ts +1 -1
- package/src/lib/server/rpc/buildErrorConstructors.ts +19 -0
- package/src/lib/server/rpc/{defineVerb.ts → defineRpc.ts} +52 -34
- package/src/lib/server/rpc/{dispatchVerbInProcess.ts → dispatchRpcInProcess.ts} +3 -3
- package/src/lib/server/rpc/fieldErrorsFromIssues.ts +23 -0
- package/src/lib/server/rpc/{findVerbByCommandName.ts → findRpcByCommandName.ts} +5 -5
- package/src/lib/server/rpc/parseArgs.ts +6 -6
- package/src/lib/server/rpc/readBodyWithinLimit.ts +2 -2
- package/src/lib/server/rpc/registerRpc.ts +6 -0
- package/src/lib/server/rpc/{verbRegistry.ts → rpcRegistry.ts} +4 -4
- package/src/lib/server/rpc/{runWithVerbTimeout.ts → runWithRpcTimeout.ts} +4 -4
- package/src/lib/server/rpc/types/RemoteHandler.ts +9 -3
- package/src/lib/server/rpc/types/{VerbHelper.ts → RpcHelper.ts} +57 -23
- package/src/lib/server/rpc/types/{VerbRegistryEntry.ts → RpcRegistryEntry.ts} +3 -3
- package/src/lib/server/rpc/types/TypedResponse.ts +2 -2
- package/src/lib/server/rpc/unprocessed.ts +6 -7
- package/src/lib/server/rpc/validationError.ts +17 -0
- package/src/lib/server/runtime/buildInspectorSurface.ts +7 -7
- package/src/lib/server/runtime/buildOpenApiSpec.ts +6 -6
- package/src/lib/server/runtime/createRouteDispatcher.ts +7 -7
- package/src/lib/server/runtime/createServer.ts +4 -4
- package/src/lib/server/runtime/crossOriginForbidden.ts +1 -1
- package/src/lib/server/runtime/crossOriginGate.ts +4 -4
- package/src/lib/server/runtime/devClientFingerprint.ts +2 -1
- package/src/lib/server/runtime/logExposedSurfaces.ts +4 -4
- package/src/lib/server/runtime/registryManifests.ts +2 -2
- package/src/lib/server/runtime/types/InspectorCacheEntry.ts +1 -1
- package/src/lib/server/runtime/types/InspectorRpc.ts +27 -0
- package/src/lib/server/runtime/types/InspectorSurface.ts +4 -4
- package/src/lib/server/runtime/types/RequestStore.ts +1 -1
- package/src/lib/server/runtime/warnUnguardedMcp.ts +2 -2
- package/src/lib/server/sockets/createSocketDispatcher.ts +15 -14
- package/src/lib/server/sockets/types/SocketOperation.ts +2 -2
- package/src/lib/shared/HttpError.ts +15 -1
- package/src/lib/shared/UNREACHABLE_STATUSES.ts +13 -0
- package/src/lib/shared/buildRpcRequest.ts +5 -5
- package/src/lib/shared/cache.ts +50 -168
- package/src/lib/shared/carriesBodyArgs.ts +4 -4
- package/src/lib/shared/createCacheStore.ts +12 -0
- package/src/lib/shared/createRemoteFunction.ts +11 -6
- package/src/lib/shared/decodeResponse.ts +2 -2
- package/src/lib/shared/detectRpcMethod.ts +17 -0
- package/src/lib/shared/emitLogRecord.ts +3 -3
- package/src/lib/shared/escapeRegex.ts +9 -0
- package/src/lib/shared/extraForwardHeaders.ts +1 -1
- package/src/lib/shared/fileName.ts +9 -0
- package/src/lib/shared/fileStem.ts +3 -1
- package/src/lib/shared/findExportCallSite.ts +30 -273
- package/src/lib/shared/forwardHeaders.ts +2 -2
- package/src/lib/shared/httpErrorFor.ts +26 -0
- package/src/lib/shared/isAsciiWhitespace.ts +5 -0
- package/src/lib/shared/isIdentPart.ts +9 -0
- package/src/lib/shared/isIdentStart.ts +8 -0
- package/src/lib/shared/isReadOnlyMethod.ts +4 -4
- package/src/lib/shared/keyForRemoteCall.ts +4 -4
- package/src/lib/shared/log.ts +1 -1
- package/src/lib/shared/outboxProbeSlot.ts +20 -0
- package/src/lib/shared/pending.ts +4 -1
- package/src/lib/shared/prepareRpcModule.ts +96 -14
- package/src/lib/shared/probeRegistries.ts +17 -4
- package/src/lib/shared/programNameForPackage.ts +3 -1
- package/src/lib/shared/queryStringFromArgs.ts +1 -1
- package/src/lib/shared/remoteMetaStore.ts +2 -2
- package/src/lib/shared/resolveClientFlags.ts +1 -1
- package/src/lib/shared/skipNonCode.ts +230 -0
- package/src/lib/shared/streamResponse.ts +9 -6
- package/src/lib/shared/stripImport.ts +6 -4
- package/src/lib/shared/types/CacheEntry.ts +2 -4
- package/src/lib/shared/types/CacheOnContext.ts +1 -11
- package/src/lib/shared/types/CacheStore.ts +13 -7
- package/src/lib/shared/types/ClientFlags.ts +1 -1
- package/src/lib/shared/types/ErrorConstructors.ts +17 -0
- package/src/lib/shared/types/ErrorDescriptor.ts +10 -0
- package/src/lib/shared/types/ErrorSpec.ts +11 -0
- package/src/lib/shared/types/HttpMethod.ts +1 -0
- package/src/lib/shared/types/Outbox.ts +9 -0
- package/src/lib/shared/types/OutboxEntry.ts +27 -0
- package/src/lib/shared/types/RawRemoteFunction.ts +2 -2
- package/src/lib/shared/types/RemoteCallable.ts +2 -2
- package/src/lib/shared/types/RemoteFunction.ts +21 -6
- package/src/lib/shared/types/RequestScopeInfo.ts +1 -1
- package/src/lib/shared/types/RpcErrorGuard.ts +40 -0
- package/src/lib/shared/types/RpcInvoker.ts +1 -1
- package/src/lib/shared/types/StandardSchemaV1.ts +1 -1
- package/src/lib/shared/types/ValidationErrorData.ts +20 -0
- package/src/lib/shared/url.ts +3 -3
- package/src/lib/shared/writeRpcDts.ts +7 -7
- package/src/lib/shared/writeTestRpcDts.ts +3 -3
- package/src/lib/test/createTestApp.ts +10 -10
- package/src/lib/ui/compile/ABIDE_SEMANTIC_TOKENS_LEGEND.ts +45 -0
- package/src/lib/ui/compile/abideUiPlugin.ts +2 -1
- package/src/lib/ui/compile/awaitPlan.ts +48 -0
- package/src/lib/ui/compile/compileShadow.ts +117 -31
- package/src/lib/ui/compile/createShadowLanguageService.ts +48 -0
- package/src/lib/ui/compile/desugarSignals.ts +42 -6
- package/src/lib/ui/compile/encodeSemanticTokens.ts +37 -0
- package/src/lib/ui/compile/generateBuild.ts +45 -62
- package/src/lib/ui/compile/generateSSR.ts +90 -60
- package/src/lib/ui/compile/ifPlan.ts +30 -0
- package/src/lib/ui/compile/makeVarNamer.ts +10 -0
- package/src/lib/ui/compile/offsetToPosition.ts +9 -0
- package/src/lib/ui/compile/parseTemplate.ts +565 -115
- package/src/lib/ui/compile/stripEffects.ts +17 -9
- package/src/lib/ui/compile/structuralBlockTokens.ts +101 -0
- package/src/lib/ui/compile/switchPlan.ts +22 -0
- package/src/lib/ui/compile/tryPlan.ts +29 -0
- package/src/lib/ui/compile/types/SemanticToken.ts +11 -0
- package/src/lib/ui/compile/types/TemplateAttr.ts +8 -2
- package/src/lib/ui/compile/types/TemplateNode.ts +15 -2
- package/src/lib/ui/createScope.ts +20 -2
- package/src/lib/ui/dom/appendText.ts +1 -5
- package/src/lib/ui/dom/applyResolved.ts +1 -5
- package/src/lib/ui/dom/disposeRange.ts +6 -10
- package/src/lib/ui/dom/hydrate.ts +2 -5
- package/src/lib/ui/dom/isComment.ts +6 -0
- package/src/lib/ui/dom/mount.ts +1 -2
- package/src/lib/ui/dom/withScope.ts +6 -9
- package/src/lib/ui/effect.ts +4 -2
- package/src/lib/ui/navigate.ts +40 -13
- package/src/lib/ui/outbox.ts +30 -110
- package/src/lib/ui/remoteProxy.ts +160 -10
- package/src/lib/ui/router.ts +3 -3
- package/src/lib/ui/rpcOutbox/createOutboxQueue.ts +281 -0
- package/src/lib/ui/rpcOutbox/outboxRegistry.ts +69 -0
- package/src/lib/ui/runtime/createDoc.ts +3 -3
- package/src/lib/ui/runtime/pathSegments.ts +10 -0
- package/src/lib/ui/socketChannel.ts +7 -1
- package/src/lib/ui/types/Scope.ts +20 -5
- package/template/.zed/settings.json +4 -0
- package/template/CLAUDE.md +1 -1
- package/template/src/server/rpc/getHello.ts +3 -3
- package/template/src/ui/pages/page.abide +4 -4
- package/template/test/app.test.ts +2 -2
- package/src/lib/server/rpc/registerVerb.ts +0 -6
- package/src/lib/server/runtime/types/InspectorVerb.ts +0 -27
- package/src/lib/shared/detectVerbMethod.ts +0 -17
- package/src/lib/shared/types/HttpVerb.ts +0 -1
- package/src/lib/ui/types/Outbox.ts +0 -14
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import type { ClientFlags } from '../../../shared/types/ClientFlags.ts'
|
|
2
|
+
import type { ErrorSpec } from '../../../shared/types/ErrorSpec.ts'
|
|
2
3
|
import type { RemoteFunction } from '../../../shared/types/RemoteFunction.ts'
|
|
3
4
|
import type { StandardSchemaV1 } from '../../../shared/types/StandardSchemaV1.ts'
|
|
4
5
|
import type { RemoteHandler } from './RemoteHandler.ts'
|
|
5
6
|
|
|
6
7
|
/*
|
|
7
|
-
Options every
|
|
8
|
+
Options every rpc overload accepts: the OpenAPI 200 `outputSchema`, the
|
|
8
9
|
`clients` surface flags, the same-origin CSRF exemption (`crossOrigin`), the
|
|
9
10
|
pre-parse body-byte ceiling (`maxBodySize`), and the per-surface handler
|
|
10
11
|
`timeout` (ms). The schema-bearing overloads intersect this with their own
|
|
11
|
-
`inputSchema`/`filesSchema` members.
|
|
12
|
+
`inputSchema`/`filesSchema` members. Mutating helpers widen it with `outbox`
|
|
13
|
+
(see MutatingRpcOpts) — a read RPC never accepts it.
|
|
12
14
|
*/
|
|
13
|
-
type
|
|
15
|
+
type RpcBaseOpts = {
|
|
14
16
|
outputSchema?: StandardSchemaV1
|
|
15
17
|
clients?: Partial<ClientFlags>
|
|
16
18
|
crossOrigin?: boolean
|
|
@@ -19,9 +21,22 @@ type VerbBaseOpts = {
|
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
/*
|
|
22
|
-
|
|
24
|
+
Mutating-helper options: the shared base plus durable delivery. `outbox` lives
|
|
25
|
+
here, not on RpcBaseOpts, because a read RPC has nothing to durably deliver —
|
|
26
|
+
so `GET(fn, { outbox: true })` is a compile error, not the runtime throw it used
|
|
27
|
+
to be. Keeps the type surface honest with the defineRpc guard.
|
|
28
|
+
*/
|
|
29
|
+
type MutatingRpcOpts = RpcBaseOpts & {
|
|
30
|
+
/* Durable delivery: on an unreachable server the call still throws, and the request is
|
|
31
|
+
parked for replay. Drains on `rpc.outbox.retry()` — no auto-drain. The call shape is
|
|
32
|
+
unchanged — `rpc.outbox` exposes the queue. */
|
|
33
|
+
outbox?: boolean
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/*
|
|
37
|
+
Shared signature for every rpc helper (GET / POST / …). Three overloads:
|
|
23
38
|
|
|
24
|
-
- `
|
|
39
|
+
- `Rpc(fn, { inputSchema, outputSchema?, clients? })` — `Args` infers
|
|
25
40
|
from `InferInput<InputSchema>`, the handler receives
|
|
26
41
|
`InferOutput<InputSchema>`. Generic order is `<Return, InputSchema>` so
|
|
27
42
|
users can override `Return` while letting `InputSchema` infer from
|
|
@@ -29,10 +44,10 @@ Shared signature for every verb helper (GET / POST / …). Three overloads:
|
|
|
29
44
|
the success body — it feeds the OpenAPI 200 response and the MCP tool
|
|
30
45
|
`outputSchema`. JSON Schema is projected from each schema's own
|
|
31
46
|
`toJSONSchema()` (wrap with withJsonSchema if the library lacks one).
|
|
32
|
-
`clients` controls which surfaces (browser / mcp / cli) expose this
|
|
33
|
-
`crossOrigin: true` exempts a mutating
|
|
47
|
+
`clients` controls which surfaces (browser / mcp / cli) expose this rpc.
|
|
48
|
+
`crossOrigin: true` exempts a mutating rpc from the router's same-origin
|
|
34
49
|
CSRF gate — by default a browser request whose Origin doesn't match the
|
|
35
|
-
app's own host is refused with 403 on every non-GET/HEAD
|
|
50
|
+
app's own host is refused with 403 on every non-GET/HEAD rpc.
|
|
36
51
|
`maxBodySize` caps the body's actual received bytes (413 past it),
|
|
37
52
|
enforced before parsing; omitted, the only ceiling is Bun.serve's
|
|
38
53
|
server-wide maxRequestBodySize. `timeout` (ms) bounds the handler's
|
|
@@ -40,15 +55,15 @@ Shared signature for every verb helper (GET / POST / …). Three overloads:
|
|
|
40
55
|
exceeded; on the network path it also aborts request().signal so a
|
|
41
56
|
handler's `fetch(ext, { signal: request().signal })` is cancelled, not
|
|
42
57
|
just abandoned.
|
|
43
|
-
- `
|
|
58
|
+
- `Rpc(fn, { clients })` — schemaless but with explicit client
|
|
44
59
|
targeting (e.g. server-internal RPC with `clients: { browser: false }`).
|
|
45
|
-
- `
|
|
60
|
+
- `Rpc(fn)` — bare handler. `Args` and `Return` come from the handler
|
|
46
61
|
type; `Return` is usually inferred via the `TypedResponse<T>` brand on
|
|
47
62
|
`json`/`error`/`redirect`/`jsonl`/`sse`.
|
|
48
63
|
*/
|
|
49
|
-
|
|
64
|
+
type RpcHelperOf<Opts> = {
|
|
50
65
|
/*
|
|
51
|
-
`
|
|
66
|
+
`Rpc(fn, { inputSchema, filesSchema, … })` — multipart upload. The
|
|
52
67
|
handler receives the text fields (`InferOutput<InputSchema>`) intersected
|
|
53
68
|
with the validated File parts (`InferOutput<FilesSchema>`); both are merged
|
|
54
69
|
into one args bag. The call site sends a FormData (RemoteFunction's call
|
|
@@ -60,25 +75,44 @@ export type VerbHelper = {
|
|
|
60
75
|
Return = unknown,
|
|
61
76
|
InputSchema extends StandardSchemaV1 = StandardSchemaV1,
|
|
62
77
|
FilesSchema extends StandardSchemaV1 = StandardSchemaV1,
|
|
78
|
+
Errors extends ErrorSpec = Record<string, never>,
|
|
63
79
|
>(
|
|
64
80
|
fn: RemoteHandler<
|
|
65
81
|
StandardSchemaV1.InferOutput<InputSchema> & StandardSchemaV1.InferOutput<FilesSchema>,
|
|
66
|
-
Return
|
|
82
|
+
Return,
|
|
83
|
+
Errors
|
|
67
84
|
>,
|
|
68
|
-
opts:
|
|
85
|
+
opts: Opts & {
|
|
69
86
|
inputSchema: InputSchema
|
|
70
87
|
filesSchema: FilesSchema
|
|
88
|
+
errors?: Errors
|
|
71
89
|
},
|
|
72
|
-
): RemoteFunction<StandardSchemaV1.InferInput<InputSchema>, Return>
|
|
73
|
-
<
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
fn: RemoteHandler<
|
|
79
|
-
opts:
|
|
80
|
-
): RemoteFunction<
|
|
90
|
+
): RemoteFunction<StandardSchemaV1.InferInput<InputSchema>, Return, Errors>
|
|
91
|
+
<
|
|
92
|
+
Return = unknown,
|
|
93
|
+
InputSchema extends StandardSchemaV1 = StandardSchemaV1,
|
|
94
|
+
Errors extends ErrorSpec = Record<string, never>,
|
|
95
|
+
>(
|
|
96
|
+
fn: RemoteHandler<StandardSchemaV1.InferOutput<InputSchema>, Return, Errors>,
|
|
97
|
+
opts: Opts & { inputSchema: InputSchema; errors?: Errors },
|
|
98
|
+
): RemoteFunction<StandardSchemaV1.InferInput<InputSchema>, Return, Errors>
|
|
99
|
+
<Args = undefined, Return = unknown, Errors extends ErrorSpec = Record<never, never>>(
|
|
100
|
+
fn: RemoteHandler<Args, Return, Errors>,
|
|
101
|
+
opts: Opts & { errors?: Errors },
|
|
102
|
+
): RemoteFunction<Args, Return, Errors>
|
|
81
103
|
<Args = undefined, Return = unknown>(
|
|
82
104
|
fn: RemoteHandler<Args, Return>,
|
|
83
105
|
): RemoteFunction<Args, Return>
|
|
84
106
|
}
|
|
107
|
+
|
|
108
|
+
/* The read helpers (GET/HEAD): no `outbox` — a read has nothing to durably deliver. */
|
|
109
|
+
export type RpcHelper = RpcHelperOf<RpcBaseOpts>
|
|
110
|
+
|
|
111
|
+
/*
|
|
112
|
+
The mutating helpers (POST/PUT/PATCH/DELETE). A durable (`outbox`) call is a normal
|
|
113
|
+
RemoteFunction — it throws exactly like a non-durable one and only parks the request as a
|
|
114
|
+
side-effect on an unreachable server — so there is no separate return shape; `outbox` rides
|
|
115
|
+
MutatingRpcOpts and `rpc.outbox` exposes the queue. The distinct opts base is what makes
|
|
116
|
+
`outbox` legal here and a compile error on the read helpers.
|
|
117
|
+
*/
|
|
118
|
+
export type MutatingRpcHelper = RpcHelperOf<MutatingRpcOpts>
|
|
@@ -3,7 +3,7 @@ import type { RemoteFunction } from '../../../shared/types/RemoteFunction.ts'
|
|
|
3
3
|
import type { StandardSchemaV1 } from '../../../shared/types/StandardSchemaV1.ts'
|
|
4
4
|
|
|
5
5
|
/*
|
|
6
|
-
Per-
|
|
6
|
+
Per-rpc registry record on the server side. MCP and CLI enumerate this
|
|
7
7
|
to discover which RPCs are advertised (clients flags) and what shapes
|
|
8
8
|
they expect/return. The schemas and resolved clients stay off the public
|
|
9
9
|
RemoteFunction shape so the browser-side proxy doesn't need to carry
|
|
@@ -20,13 +20,13 @@ from `inputSchema` because a File has no honest JSON-Schema conversion — it
|
|
|
20
20
|
stays out of the MCP/CLI projection that `inputSchema` feeds, and the OpenAPI
|
|
21
21
|
multipart body advertises the file parts generically as binary.
|
|
22
22
|
*/
|
|
23
|
-
export type
|
|
23
|
+
export type RpcRegistryEntry = {
|
|
24
24
|
remote: RemoteFunction<unknown, unknown>
|
|
25
25
|
inputSchema: StandardSchemaV1 | undefined
|
|
26
26
|
outputSchema: StandardSchemaV1 | undefined
|
|
27
27
|
filesSchema: StandardSchemaV1 | undefined
|
|
28
28
|
clients: ClientFlags
|
|
29
|
-
/* The
|
|
29
|
+
/* The rpc's declared opts, recorded so introspection (inspector) can report
|
|
30
30
|
the deadline/body-cap/CSRF-exemption a handler runs under. Undefined = the
|
|
31
31
|
framework default (no deadline, Bun's server-wide body ceiling, gated). */
|
|
32
32
|
timeout: number | undefined
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/*
|
|
2
2
|
A `Response` tagged with the body type the framework will hand back to
|
|
3
3
|
callers after Content-Type-driven decoding. The tag is phantom — it
|
|
4
|
-
adds no runtime field, only a type-level slot so the
|
|
4
|
+
adds no runtime field, only a type-level slot so the rpc helpers can
|
|
5
5
|
infer `Return` from the handler's return type instead of forcing every
|
|
6
6
|
route to annotate it via `GET<Args, Return>`.
|
|
7
7
|
|
|
8
8
|
The respond helpers (`json<T>`, `error`, `redirect`, `jsonl<F>`,
|
|
9
9
|
`sse<F>`) all return a `TypedResponse<T>`, so a handler ending in
|
|
10
10
|
`return json({ user })` exposes `{ user: ... }` as its body type; the
|
|
11
|
-
|
|
11
|
+
rpc overload picks it up via `RemoteHandler<Args, Return>`.
|
|
12
12
|
|
|
13
13
|
`T` is optional on the brand so a plain `new Response(...)` (untagged)
|
|
14
14
|
remains assignable to `TypedResponse<unknown>`; in that case `Return`
|
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import type { RemoteFunction } from '../../shared/types/RemoteFunction.ts'
|
|
2
|
-
|
|
3
1
|
/*
|
|
4
|
-
|
|
2
|
+
Rpc helpers (GET / POST / …) are placeholders — the bundler rewrites every
|
|
5
3
|
`export const x = GET(fn)` inside `src/server/rpc/<file>.ts` into a call to
|
|
6
|
-
|
|
4
|
+
defineRpc (server target) or remoteProxy (client target). If a call slips
|
|
7
5
|
through, the bundler plugin didn't process the file; throwing here surfaces
|
|
8
|
-
that cleanly instead of silently returning undefined.
|
|
6
|
+
that cleanly instead of silently returning undefined. Returns `never` (it always
|
|
7
|
+
throws), so a helper's arrow satisfies every overload of RpcHelper/MutatingRpcHelper.
|
|
9
8
|
*/
|
|
10
|
-
export function unprocessed
|
|
9
|
+
export function unprocessed(method: string): never {
|
|
11
10
|
throw new Error(
|
|
12
|
-
`[abide] \`${
|
|
11
|
+
`[abide] \`${method}\` was called outside an $rpc module — rpc helpers are only valid as the value of \`export const <filename> = ...\` inside a file under src/server/rpc/`,
|
|
13
12
|
)
|
|
14
13
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from '../../shared/types/StandardSchemaV1.ts'
|
|
2
|
+
import type { ValidationErrorData } from '../../shared/types/ValidationErrorData.ts'
|
|
3
|
+
import { error } from '../error.ts'
|
|
4
|
+
import { fieldErrorsFromIssues } from './fieldErrorsFromIssues.ts'
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
The framework-reserved `validation` typed error a 422 carries: the raw Standard
|
|
8
|
+
Schema `issues` plus the form-friendly field → first-message map. Routed through
|
|
9
|
+
the single `error()` funnel (descriptor form) so it serializes as the same
|
|
10
|
+
`{ $abideError, data }` body every typed error uses, and so the 422's reason
|
|
11
|
+
phrase reaches `HttpError.statusText`. The client parses it back onto
|
|
12
|
+
`HttpError.kind = 'validation'` / `.data: ValidationErrorData`.
|
|
13
|
+
*/
|
|
14
|
+
export function validationError(issues: readonly StandardSchemaV1.Issue[]): Response {
|
|
15
|
+
const data: ValidationErrorData = { issues, fields: fieldErrorsFromIssues(issues) }
|
|
16
|
+
return error({ $abideError: 'validation', status: 422, data })
|
|
17
|
+
}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { jsonSchemaForSchema } from '../../shared/jsonSchemaForSchema.ts'
|
|
2
2
|
import { promptRegistry } from '../prompts/promptRegistry.ts'
|
|
3
|
-
import {
|
|
3
|
+
import { rpcRegistry } from '../rpc/rpcRegistry.ts'
|
|
4
4
|
import { socketOperations } from '../sockets/socketOperations.ts'
|
|
5
5
|
import { socketRegistry } from '../sockets/socketRegistry.ts'
|
|
6
6
|
import type { InspectorSurface } from './types/InspectorSurface.ts'
|
|
7
7
|
|
|
8
8
|
/*
|
|
9
|
-
Projects the live
|
|
10
|
-
at call time (not cached) so
|
|
9
|
+
Projects the live rpc + socket registries into the inspector's catalog. Read
|
|
10
|
+
at call time (not cached) so rpcs constructed after boot — lazily on first
|
|
11
11
|
hit, or eagerly once ensureRegistriesLoaded walked the manifest — show up.
|
|
12
12
|
Schemas go through jsonSchemaForSchema, the same projection MCP and OpenAPI
|
|
13
|
-
use, so the three can't disagree on a
|
|
14
|
-
undefined to mark a
|
|
13
|
+
use, so the three can't disagree on a rpc's shape; a missing schema stays
|
|
14
|
+
undefined to mark a rpc that carries no machine-advertisable contract.
|
|
15
15
|
*/
|
|
16
16
|
export function buildInspectorSurface(): InspectorSurface {
|
|
17
|
-
const
|
|
17
|
+
const rpcs = Array.from(rpcRegistry.values()).map((entry) => ({
|
|
18
18
|
url: entry.remote.url,
|
|
19
19
|
method: entry.remote.method,
|
|
20
20
|
clients: { ...entry.remote.clients },
|
|
@@ -41,5 +41,5 @@ export function buildInspectorSurface(): InspectorSurface {
|
|
|
41
41
|
description: entry.prompt.description,
|
|
42
42
|
inputSchema: entry.jsonSchema,
|
|
43
43
|
}))
|
|
44
|
-
return {
|
|
44
|
+
return { rpcs, sockets, prompts }
|
|
45
45
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { carriesBodyArgs } from '../../shared/carriesBodyArgs.ts'
|
|
2
2
|
import { commandNameForUrl } from '../../shared/commandNameForUrl.ts'
|
|
3
3
|
import { jsonSchemaForSchema } from '../../shared/jsonSchemaForSchema.ts'
|
|
4
|
-
import {
|
|
4
|
+
import { rpcRegistry } from '../rpc/rpcRegistry.ts'
|
|
5
5
|
|
|
6
6
|
/*
|
|
7
|
-
Turns a
|
|
7
|
+
Turns a rpc's resolved JSON Schema into OpenAPI query parameters — one
|
|
8
8
|
per top-level property, marked required when the schema lists it. Used
|
|
9
9
|
for GET/DELETE/HEAD operations, which carry their args on the query
|
|
10
10
|
string (mirroring buildRpcRequest).
|
|
@@ -24,7 +24,7 @@ function queryParameters(jsonSchema: Record<string, unknown>): Array<Record<stri
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
/*
|
|
27
|
-
Request body schema for a multipart upload
|
|
27
|
+
Request body schema for a multipart upload rpc: the text fields from
|
|
28
28
|
inputSchema, plus the binary parts. A File has no honest
|
|
29
29
|
Standard-Schema→JSON-Schema conversion, so the file parts are advertised
|
|
30
30
|
generically as additional binary properties rather than named per field.
|
|
@@ -44,7 +44,7 @@ function multipartBodySchema(textSchema: Record<string, unknown>): Record<string
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
/*
|
|
47
|
-
Builds an OpenAPI 3.1 document from the
|
|
47
|
+
Builds an OpenAPI 3.1 document from the rpc registry — the HTTP surface
|
|
48
48
|
every rpc exposes regardless of which non-browser clients it advertises.
|
|
49
49
|
GET/DELETE/HEAD args become query parameters; POST/PUT/PATCH args become
|
|
50
50
|
a JSON request body. operationId is the folder-prefixed command name so
|
|
@@ -55,13 +55,13 @@ export function buildOpenApiSpec(info: {
|
|
|
55
55
|
version: string
|
|
56
56
|
}): Record<string, unknown> {
|
|
57
57
|
const paths: Record<string, Record<string, unknown>> = {}
|
|
58
|
-
for (const entry of
|
|
58
|
+
for (const entry of rpcRegistry.values()) {
|
|
59
59
|
const url = entry.remote.url
|
|
60
60
|
const method = entry.remote.method
|
|
61
61
|
const jsonSchema = jsonSchemaForSchema(entry.inputSchema)
|
|
62
62
|
const description = jsonSchema.description as string | undefined
|
|
63
63
|
/*
|
|
64
|
-
When the
|
|
64
|
+
When the rpc declares an `outputSchema`, describe the 200 body
|
|
65
65
|
with it so external tooling sees the real return shape; otherwise
|
|
66
66
|
fall back to a bare OK.
|
|
67
67
|
*/
|
|
@@ -3,7 +3,7 @@ import { memoizeByKey } from '../../shared/memoizeByKey.ts'
|
|
|
3
3
|
import { NAV_HEADER } from '../../shared/NAV_HEADER.ts'
|
|
4
4
|
import { REMOTE_FUNCTION } from '../../shared/REMOTE_FUNCTION.ts'
|
|
5
5
|
import { TEXT_PLAIN } from '../../shared/TEXT_PLAIN.ts'
|
|
6
|
-
import type {
|
|
6
|
+
import type { HttpMethod } from '../../shared/types/HttpMethod.ts'
|
|
7
7
|
import type { RemoteFunction } from '../../shared/types/RemoteFunction.ts'
|
|
8
8
|
import type { Pages } from '../../ui/types/Pages.ts'
|
|
9
9
|
import type { RemoteRoutes } from '../rpc/types/RemoteRoutes.ts'
|
|
@@ -26,7 +26,7 @@ type RenderPage = (
|
|
|
26
26
|
store: RequestStore,
|
|
27
27
|
) => Promise<Response>
|
|
28
28
|
|
|
29
|
-
/* The framework's 405 — `Allow` names the permitted
|
|
29
|
+
/* The framework's 405 — `Allow` names the permitted rpc(s), body and NO_STORE shared so the rpc and page branches can't drift. */
|
|
30
30
|
function methodNotAllowed(allow: string): Response {
|
|
31
31
|
return new Response('Method Not Allowed', {
|
|
32
32
|
status: 405,
|
|
@@ -36,9 +36,9 @@ function methodNotAllowed(allow: string): Response {
|
|
|
36
36
|
|
|
37
37
|
/*
|
|
38
38
|
Owns route dispatch: deciding, per registered URL, whether a request hits an
|
|
39
|
-
rpc
|
|
39
|
+
rpc rpc, a page render, or nothing — and the method-matching that picks the
|
|
40
40
|
status. Page URLs (under src/ui/pages/) serve GET/HEAD by rendering; rpc
|
|
41
|
-
URLs (under src/server/rpc/, `/rpc/...`) dispatch to the single declared
|
|
41
|
+
URLs (under src/server/rpc/, `/rpc/...`) dispatch to the single declared rpc,
|
|
42
42
|
405 on method mismatch; an unregistered URL is 404. Page and rpc URLs are
|
|
43
43
|
disjoint by construction, so each route lands in exactly one branch.
|
|
44
44
|
|
|
@@ -66,7 +66,7 @@ export function createRouteDispatcher({
|
|
|
66
66
|
Each $rpc module has exactly one named export, validated at build
|
|
67
67
|
time. Pick the first REMOTE_FUNCTION-branded export — exact, so an
|
|
68
68
|
incidental re-export carrying method/url props can't be mistaken
|
|
69
|
-
for the
|
|
69
|
+
for the rpc.
|
|
70
70
|
*/
|
|
71
71
|
return loader().then((mod) => {
|
|
72
72
|
for (const value of Object.values(mod)) {
|
|
@@ -82,14 +82,14 @@ export function createRouteDispatcher({
|
|
|
82
82
|
const hasPage = pages[routeUrl] !== undefined
|
|
83
83
|
const hasRpc = rpc[routeUrl] !== undefined
|
|
84
84
|
return async function routeHandler(req, pathParams, store) {
|
|
85
|
-
const method = req.method as
|
|
85
|
+
const method = req.method as HttpMethod
|
|
86
86
|
if (hasRpc) {
|
|
87
87
|
const fn = await loadRpc(routeUrl)
|
|
88
88
|
if (fn && fn.method === method) {
|
|
89
89
|
const forbidden = crossOriginGate(req, store.url, {
|
|
90
90
|
allowReadOnly: true,
|
|
91
91
|
optOut: fn.crossOrigin === true,
|
|
92
|
-
hint: 'Declare `crossOrigin: true` on the
|
|
92
|
+
hint: 'Declare `crossOrigin: true` on the rpc to accept cross-site calls.',
|
|
93
93
|
})
|
|
94
94
|
if (forbidden) {
|
|
95
95
|
return forbidden
|
|
@@ -71,7 +71,7 @@ const DEV_REBUILD_PATH = '/__abide/reload'
|
|
|
71
71
|
/*
|
|
72
72
|
Unlike the framework's own plumbing routes above (the socket multiplex, MCP
|
|
73
73
|
endpoint, CLI download), the OpenAPI document describes the app's public HTTP
|
|
74
|
-
surface — the /rpc/*
|
|
74
|
+
surface — the /rpc/* rpcs — rather than abide internals, so it sits at the
|
|
75
75
|
conventional root path where external tooling and scanners expect to find it
|
|
76
76
|
(/openapi.json, alongside /swagger.json, /.well-known/*) rather than under the
|
|
77
77
|
/__abide/ namespace.
|
|
@@ -81,7 +81,7 @@ const OPENAPI_PATH = '/openapi.json'
|
|
|
81
81
|
/*
|
|
82
82
|
Starts a Bun HTTP server that ties together the framework conventions:
|
|
83
83
|
page.abide under src/ui/pages/ for views (layout.abide wraps the pages beneath
|
|
84
|
-
it), one named export per file under src/server/rpc/ for
|
|
84
|
+
it), one named export per file under src/server/rpc/ for rpc-bound remote
|
|
85
85
|
functions, one named export
|
|
86
86
|
per file under src/server/sockets/ for broadcast sockets, and an optional
|
|
87
87
|
app.ts for boot-time setup, request middleware, and error fallback. Page
|
|
@@ -120,7 +120,7 @@ export async function createServer({
|
|
|
120
120
|
/*
|
|
121
121
|
Bun's server-wide request body ceiling, enforced natively by Bun.serve
|
|
122
122
|
(its own default is ~128MB). Surfaced as an option + env so deployments
|
|
123
|
-
can raise/lower it; per-
|
|
123
|
+
can raise/lower it; per-rpc tightening is the rpcs' maxBodySize.
|
|
124
124
|
*/
|
|
125
125
|
maxRequestBodySize = parseBoundedEnvInt(
|
|
126
126
|
process.env.ABIDE_MAX_REQUEST_BODY_SIZE,
|
|
@@ -242,7 +242,7 @@ export async function createServer({
|
|
|
242
242
|
isn't installed. Resolved at boot so the fetch route below can branch on it.
|
|
243
243
|
*/
|
|
244
244
|
const inspectorHandler = await maybeMountInspector({ name: appName, version: appVersion })
|
|
245
|
-
/* Built on first request, then reused — the
|
|
245
|
+
/* Built on first request, then reused — the rpc registry is frozen after load. */
|
|
246
246
|
let openApiSpec: ReturnType<typeof buildOpenApiSpec> | undefined
|
|
247
247
|
const cliCwd = process.cwd()
|
|
248
248
|
|
|
@@ -4,7 +4,7 @@ import { TEXT_PLAIN } from '../../shared/TEXT_PLAIN.ts'
|
|
|
4
4
|
/*
|
|
5
5
|
The framework's CSRF refusal, shared by every same-origin gate — the socket
|
|
6
6
|
upgrade, the socket REST publish face, the MCP endpoint, and mutating rpc
|
|
7
|
-
|
|
7
|
+
rpcs — so the 403 body can't drift between surfaces. `hint` appends a
|
|
8
8
|
surface-specific remedy; rpc names its `crossOrigin: true` opt-out so the
|
|
9
9
|
first developer this 403s can self-serve.
|
|
10
10
|
*/
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { isReadOnlyMethod } from '../../shared/isReadOnlyMethod.ts'
|
|
2
|
-
import type {
|
|
2
|
+
import type { HttpMethod } from '../../shared/types/HttpMethod.ts'
|
|
3
3
|
import { crossOriginForbidden } from './crossOriginForbidden.ts'
|
|
4
4
|
import { isCrossOriginRequest } from './isCrossOriginRequest.ts'
|
|
5
5
|
|
|
6
6
|
/*
|
|
7
7
|
The framework's CSRF/CSWSH posture in one place: a cross-origin browser
|
|
8
8
|
request to a mutating framework endpoint is refused. Every endpoint that
|
|
9
|
-
parses bodies ignoring Content-Type (rpc
|
|
9
|
+
parses bodies ignoring Content-Type (rpc rpcs, socket publish, MCP JSON-RPC)
|
|
10
10
|
must gate here — a hostile page's text/plain form trick could otherwise
|
|
11
11
|
smuggle a payload in with the visitor's ambient cookies; non-browser clients
|
|
12
12
|
send no Origin and pass. Returns the 403 to send, or undefined to proceed.
|
|
13
13
|
Mount sites declare their variation: `allowReadOnly` lets GET/HEAD reads
|
|
14
14
|
through (rpc reads and socket tails stay open cross-origin), `optOut` honours
|
|
15
|
-
a
|
|
15
|
+
a rpc's explicit `crossOrigin: true`, `hint` names the remedy in the 403.
|
|
16
16
|
*/
|
|
17
17
|
export function crossOriginGate(
|
|
18
18
|
req: Request,
|
|
@@ -22,7 +22,7 @@ export function crossOriginGate(
|
|
|
22
22
|
if (options.optOut) {
|
|
23
23
|
return undefined
|
|
24
24
|
}
|
|
25
|
-
if (options.allowReadOnly && isReadOnlyMethod(req.method as
|
|
25
|
+
if (options.allowReadOnly && isReadOnlyMethod(req.method as HttpMethod)) {
|
|
26
26
|
return undefined
|
|
27
27
|
}
|
|
28
28
|
return isCrossOriginRequest(req, url) ? crossOriginForbidden(options.hint) : undefined
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { relative } from 'node:path'
|
|
2
|
+
import { fileName } from '../../shared/fileName.ts'
|
|
2
3
|
import { analyzeComponent } from '../../ui/compile/analyzeComponent.ts'
|
|
3
4
|
import { compileComponent } from '../../ui/compile/compileComponent.ts'
|
|
4
5
|
import { nearestProjectRoot } from '../../ui/compile/nearestProjectRoot.ts'
|
|
@@ -19,7 +20,7 @@ const GENERATED = /(^|\/)\.abide\//
|
|
|
19
20
|
// page.abide / layout.abide are router-mounted, not `mountChild`-tracked, so they
|
|
20
21
|
// can't hot-swap — they fold into `structure` (a reload) instead.
|
|
21
22
|
function isPageOrLayout(moduleId: string): boolean {
|
|
22
|
-
const file = moduleId
|
|
23
|
+
const file = fileName(moduleId)
|
|
23
24
|
return file === 'page.abide' || file === 'layout.abide'
|
|
24
25
|
}
|
|
25
26
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { abideLog } from '../../shared/abideLog.ts'
|
|
2
2
|
import type { Pages } from '../../ui/types/Pages.ts'
|
|
3
|
-
import {
|
|
3
|
+
import { rpcRegistry } from '../rpc/rpcRegistry.ts'
|
|
4
4
|
import { socketRegistry } from '../sockets/socketRegistry.ts'
|
|
5
5
|
import { ensureRegistriesLoaded } from './registryManifests.ts'
|
|
6
6
|
|
|
@@ -15,7 +15,7 @@ const redden = (text: string): string =>
|
|
|
15
15
|
hasColor ? `${Bun.color('red', 'ansi-256')}${text}\x1b[39m` : text
|
|
16
16
|
|
|
17
17
|
/*
|
|
18
|
-
A declared inputSchema is what makes mcp/cli safe to advertise (see
|
|
18
|
+
A declared inputSchema is what makes mcp/cli safe to advertise (see defineRpc /
|
|
19
19
|
defineSocket), so a missing schema gets a red `·` to flag the declaration whose
|
|
20
20
|
machine surfaces are gated behind it.
|
|
21
21
|
*/
|
|
@@ -127,12 +127,12 @@ export async function logExposedSurfaces(routing: { pages: Pages }): Promise<voi
|
|
|
127
127
|
*/
|
|
128
128
|
const methodWidth = Math.max(
|
|
129
129
|
'http'.length,
|
|
130
|
-
...Array.from(
|
|
130
|
+
...Array.from(rpcRegistry.values(), (entry) => entry.remote.method.length),
|
|
131
131
|
)
|
|
132
132
|
const withMethod = (method: string, identifier: string): string =>
|
|
133
133
|
method.padEnd(methodWidth + COLUMN_GAP) + identifier
|
|
134
134
|
|
|
135
|
-
const rpcRows = Array.from(
|
|
135
|
+
const rpcRows = Array.from(rpcRegistry.values(), (entry) => [
|
|
136
136
|
withMethod(entry.remote.method, entry.remote.url),
|
|
137
137
|
schemaCell(Boolean(entry.inputSchema)),
|
|
138
138
|
flag(entry.clients.browser),
|
|
@@ -7,7 +7,7 @@ Process-wide slot for the rpc + sockets + prompts manifests. createServer
|
|
|
7
7
|
assigns once at boot (right after the route table is built); the MCP
|
|
8
8
|
server, the OpenAPI emitter, and prompt enumeration read it on first
|
|
9
9
|
request so they can lazy-import every module and walk the
|
|
10
|
-
|
|
10
|
+
rpc/socket/prompt registries.
|
|
11
11
|
|
|
12
12
|
The slot pattern (mirrors getActiveServer) lets the framework-generated
|
|
13
13
|
McpServer bind to the manifests at module scope while the loaders stay
|
|
@@ -29,7 +29,7 @@ export function setRegistryManifests(value: RegistryManifests): void {
|
|
|
29
29
|
|
|
30
30
|
/*
|
|
31
31
|
On first call, eagerly imports every rpc + socket + prompt module so
|
|
32
|
-
|
|
32
|
+
defineRpc / defineSocket / definePrompt fire and populate the
|
|
33
33
|
registries. Idempotent — repeat calls reuse the same in-flight promise,
|
|
34
34
|
so concurrent first requests (e.g. /openapi.json + an MCP tools/list)
|
|
35
35
|
trigger exactly one load instead of racing to fire the full import set
|
|
@@ -9,7 +9,7 @@ export type InspectorCacheEntry = {
|
|
|
9
9
|
key: string
|
|
10
10
|
/* Lifecycle: 'settled' | 'in-flight' | 'refreshing'. */
|
|
11
11
|
status: string
|
|
12
|
-
/* True when the entry stores a wire Response (a remote
|
|
12
|
+
/* True when the entry stores a wire Response (a remote rpc), false for a plain producer value. */
|
|
13
13
|
remote: boolean
|
|
14
14
|
/* Retention ttl in ms; undefined = forever, 0 = dedupe-only. */
|
|
15
15
|
ttl: number | undefined
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/*
|
|
2
|
+
One RPC rpc projected for the inspector: the registry entry reduced to the
|
|
3
|
+
serializable facts the UI renders — where it mounts, its method, which
|
|
4
|
+
non-browser surfaces advertise it, and its argument/result shapes as JSON
|
|
5
|
+
Schema. Schemas project through jsonSchemaForSchema (same as MCP/OpenAPI), so
|
|
6
|
+
a rpc whose library can't render a schema still lists with an opaque shape.
|
|
7
|
+
*/
|
|
8
|
+
export type InspectorRpc = {
|
|
9
|
+
/* Registry key — the rpc URL the rpc mounts at (e.g. /rpc/users/create). */
|
|
10
|
+
url: string
|
|
11
|
+
/* HTTP method bound to the rpc. */
|
|
12
|
+
method: string
|
|
13
|
+
/* Which surfaces advertise it (browser/mcp/cli), from the rpc's ClientFlags. */
|
|
14
|
+
clients: Record<string, boolean>
|
|
15
|
+
/* Argument-bag shape as JSON Schema; undefined when the rpc declares none. */
|
|
16
|
+
inputSchema: Record<string, unknown> | undefined
|
|
17
|
+
/* Success-body shape as JSON Schema; undefined when the rpc declares none. */
|
|
18
|
+
outputSchema: Record<string, unknown> | undefined
|
|
19
|
+
/* True when the rpc declares a filesSchema — it accepts multipart File parts. */
|
|
20
|
+
files: boolean
|
|
21
|
+
/* Per-rpc handler deadline in ms; undefined = no deadline. */
|
|
22
|
+
timeout: number | undefined
|
|
23
|
+
/* Per-rpc received-body cap in bytes; undefined = Bun's server-wide ceiling. */
|
|
24
|
+
maxBodySize: number | undefined
|
|
25
|
+
/* True when the rpc opts out of the same-origin CSRF gate. */
|
|
26
|
+
crossOrigin: boolean | undefined
|
|
27
|
+
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import type { InspectorPrompt } from './InspectorPrompt.ts'
|
|
2
|
+
import type { InspectorRpc } from './InspectorRpc.ts'
|
|
2
3
|
import type { InspectorSocket } from './InspectorSocket.ts'
|
|
3
|
-
import type { InspectorVerb } from './InspectorVerb.ts'
|
|
4
4
|
|
|
5
5
|
/*
|
|
6
6
|
The app's machine surface projected for the inspector — the static catalog the
|
|
7
|
-
UI renders. Built by reading the
|
|
7
|
+
UI renders. Built by reading the rpc, socket, and prompt registries after
|
|
8
8
|
they're eager-loaded, so a freshly-booted server lists its whole surface, not
|
|
9
|
-
just the
|
|
9
|
+
just the rpcs hit so far. Pages stay out: they're the human surface, already
|
|
10
10
|
navigable.
|
|
11
11
|
*/
|
|
12
12
|
export type InspectorSurface = {
|
|
13
|
-
|
|
13
|
+
rpcs: InspectorRpc[]
|
|
14
14
|
sockets: InspectorSocket[]
|
|
15
15
|
prompts: InspectorPrompt[]
|
|
16
16
|
}
|
|
@@ -3,7 +3,7 @@ import type { TraceContext } from '../../../shared/types/TraceContext.ts'
|
|
|
3
3
|
|
|
4
4
|
/*
|
|
5
5
|
Per-request state propagated through AsyncLocalStorage. Every field is
|
|
6
|
-
populated once at the server's fetch boundary; helpers and
|
|
6
|
+
populated once at the server's fetch boundary; helpers and rpc-defined
|
|
7
7
|
remote functions read from it without threading arguments through user code.
|
|
8
8
|
The inbound request's AbortSignal is reached via `req.signal` rather than a
|
|
9
9
|
separate field.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { abideLog } from '../../shared/abideLog.ts'
|
|
2
|
-
import {
|
|
2
|
+
import { rpcRegistry } from '../rpc/rpcRegistry.ts'
|
|
3
3
|
import { socketRegistry } from '../sockets/socketRegistry.ts'
|
|
4
4
|
import { ensureRegistriesLoaded } from './registryManifests.ts'
|
|
5
5
|
|
|
@@ -19,7 +19,7 @@ export async function warnUnguardedMcp(): Promise<void> {
|
|
|
19
19
|
return
|
|
20
20
|
}
|
|
21
21
|
const isMcpExposed = (entry: { clients: { mcp: boolean } }): boolean => entry.clients.mcp
|
|
22
|
-
const exposed = [...
|
|
22
|
+
const exposed = [...rpcRegistry.values(), ...socketRegistry.values()].filter(
|
|
23
23
|
isMcpExposed,
|
|
24
24
|
).length
|
|
25
25
|
if (exposed === 0) {
|