@abide/abide 0.49.0 → 0.50.1

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.
Files changed (325) hide show
  1. package/AGENTS.md +564 -437
  2. package/CHANGELOG.md +183 -0
  3. package/README.md +165 -202
  4. package/package.json +14 -5
  5. package/src/abideLsp.ts +5 -6
  6. package/src/abideResolverPlugin.ts +197 -169
  7. package/src/build.ts +79 -56
  8. package/src/buildDisconnected.ts +0 -2
  9. package/src/checkAbide.ts +12 -2
  10. package/src/devEntry.ts +113 -28
  11. package/src/discoveryEntry.ts +3 -2
  12. package/src/lib/bundle/disconnected.abide +69 -73
  13. package/src/lib/bundle/infoPlist.ts +13 -7
  14. package/src/lib/bundle/spawnEmbeddedServer.ts +13 -4
  15. package/src/lib/bundle/waitForServer.ts +6 -1
  16. package/src/lib/mcp/mcpTools.ts +15 -6
  17. package/src/lib/server/error.ts +6 -6
  18. package/src/lib/server/json.ts +17 -3
  19. package/src/lib/server/rpc/defineRpc.ts +58 -24
  20. package/src/lib/server/rpc/parseArgs.ts +98 -13
  21. package/src/lib/server/rpc/resolveRpcJsonSchema.ts +28 -0
  22. package/src/lib/server/rpc/types/RpcHelper.ts +75 -105
  23. package/src/lib/server/rpc/types/RpcRegistryEntry.ts +24 -9
  24. package/src/lib/server/rpc/validationError.ts +1 -1
  25. package/src/lib/server/runtime/DEV_RELOAD_CLIENT_SCRIPT.ts +0 -15
  26. package/src/lib/server/runtime/buildCacheSnapshot.ts +10 -9
  27. package/src/lib/server/runtime/buildInspectorSurface.ts +6 -4
  28. package/src/lib/server/runtime/buildOpenApiSpec.ts +55 -11
  29. package/src/lib/server/runtime/buildPreloadManifest.ts +12 -10
  30. package/src/lib/server/runtime/createAppAssetServer.ts +18 -15
  31. package/src/lib/server/runtime/createAppRouteResolver.ts +145 -0
  32. package/src/lib/server/runtime/createPlumbingRouter.ts +212 -0
  33. package/src/lib/server/runtime/createRouteDispatcher.ts +4 -10
  34. package/src/lib/server/runtime/createServer.ts +120 -324
  35. package/src/lib/server/runtime/createUiPageRenderer.ts +137 -26
  36. package/src/lib/server/runtime/devClientFingerprint.ts +19 -34
  37. package/src/lib/server/runtime/installAmbientScopeStore.ts +30 -9
  38. package/src/lib/server/runtime/logExposedSurfaces.ts +8 -7
  39. package/src/lib/server/runtime/pathStore.ts +15 -0
  40. package/src/lib/server/runtime/registryManifests.ts +1 -1
  41. package/src/lib/server/runtime/renderCellBarrierStore.ts +15 -0
  42. package/src/lib/server/runtime/runWithRequestScope.ts +3 -0
  43. package/src/lib/server/runtime/serializeCacheSnapshot.ts +6 -4
  44. package/src/lib/server/runtime/snapshotEntryFromCache.ts +14 -14
  45. package/src/lib/server/runtime/streamCacheResolutions.ts +14 -7
  46. package/src/lib/server/runtime/streamFromIterator.ts +30 -2
  47. package/src/lib/server/runtime/textResponse.ts +23 -0
  48. package/src/lib/server/runtime/types/DevReloadStamp.ts +6 -10
  49. package/src/lib/server/runtime/types/InspectorCacheEntry.ts +1 -1
  50. package/src/lib/server/runtime/types/RequestStore.ts +27 -0
  51. package/src/lib/server/runtime/warnUnguardedMcp.ts +15 -5
  52. package/src/lib/server/sockets/createSocketDispatcher.ts +9 -11
  53. package/src/lib/server/sockets/defineSocket.ts +5 -4
  54. package/src/lib/server/sse.ts +5 -1
  55. package/src/lib/shared/ASYNC_CELL.ts +5 -0
  56. package/src/lib/shared/DEV_REBUILD_PATH.ts +6 -0
  57. package/src/lib/shared/HTTP_METHODS.ts +6 -0
  58. package/src/lib/shared/HttpError.ts +1 -5
  59. package/src/lib/shared/MCP_PATH.ts +6 -0
  60. package/src/lib/shared/PROXIED_SERVER_SUBDIRS.ts +15 -0
  61. package/src/lib/shared/REF_JSON_TAGS.ts +13 -1
  62. package/src/lib/shared/RPC_SHIM_GLOBALS.ts +9 -0
  63. package/src/lib/shared/activeCacheStore.ts +1 -0
  64. package/src/lib/shared/activePage.ts +1 -0
  65. package/src/lib/shared/buildArtifact.ts +4 -1
  66. package/src/lib/shared/buildSocketOverChannel.ts +4 -3
  67. package/src/lib/shared/bundleGraphFromMetafile.ts +68 -0
  68. package/src/lib/shared/cache.ts +156 -131
  69. package/src/lib/shared/changeAffectsClient.ts +12 -7
  70. package/src/lib/shared/createCacheStore.ts +31 -8
  71. package/src/lib/shared/createLifecycleChannel.ts +7 -1
  72. package/src/lib/shared/createReachable.ts +47 -78
  73. package/src/lib/shared/createRemoteFunction.ts +52 -30
  74. package/src/lib/shared/createRpcServerProgram.ts +820 -0
  75. package/src/lib/shared/decodeRefJson.ts +4 -4
  76. package/src/lib/shared/decodeResponse.ts +2 -2
  77. package/src/lib/shared/decodeWireBody.ts +35 -0
  78. package/src/lib/shared/detectRpcMethod.ts +8 -1
  79. package/src/lib/shared/done.ts +8 -2
  80. package/src/lib/shared/encodeRefJson.ts +4 -4
  81. package/src/lib/shared/encodeWireBody.ts +18 -0
  82. package/src/lib/{server/rpc → shared}/fieldErrorsFromIssues.ts +5 -1
  83. package/src/lib/shared/generateDeclarations.ts +72 -0
  84. package/src/lib/shared/hasSeedableRequest.ts +25 -0
  85. package/src/lib/shared/hydrationWindow.ts +53 -0
  86. package/src/lib/shared/isAsyncCell.ts +11 -0
  87. package/src/lib/shared/isAsyncIterable.ts +8 -0
  88. package/src/lib/shared/isSubscribable.ts +3 -3
  89. package/src/lib/shared/isThenable.ts +9 -0
  90. package/src/lib/shared/jsonSchemaForType.ts +258 -0
  91. package/src/lib/shared/lenientDecode.ts +15 -0
  92. package/src/lib/shared/loadProjectTsConfig.ts +28 -0
  93. package/src/lib/shared/markFrameworkSourcesIgnored.ts +1 -1
  94. package/src/lib/shared/matchRoute.ts +4 -14
  95. package/src/lib/shared/peek.ts +14 -0
  96. package/src/lib/shared/pending.ts +9 -6
  97. package/src/lib/shared/pendingAsyncCellsSlot.ts +13 -0
  98. package/src/lib/shared/prepareRpcModule.ts +91 -93
  99. package/src/lib/shared/prepareSocketModule.ts +3 -2
  100. package/src/lib/shared/probeRegistries.ts +5 -18
  101. package/src/lib/shared/reachable.ts +7 -10
  102. package/src/lib/shared/refresh.ts +12 -2
  103. package/src/lib/shared/refreshing.ts +8 -2
  104. package/src/lib/shared/resolvedCellsSlot.ts +13 -0
  105. package/src/lib/shared/reviveWireField.ts +49 -0
  106. package/src/lib/shared/reviveWireOutput.ts +36 -0
  107. package/src/lib/shared/rpcServerForRoot.ts +25 -0
  108. package/src/lib/shared/scanPages.ts +25 -0
  109. package/src/lib/shared/snapshotShippable.ts +8 -7
  110. package/src/lib/shared/streamedCellsSlot.ts +14 -0
  111. package/src/lib/shared/subscribableFromResponse.ts +4 -4
  112. package/src/lib/shared/subscribableProbes.ts +1 -1
  113. package/src/lib/shared/tailProbeSlot.ts +1 -1
  114. package/src/lib/shared/types/AsyncComputed.ts +20 -0
  115. package/src/lib/shared/types/AsyncState.ts +13 -0
  116. package/src/lib/shared/types/CacheEntry.ts +7 -7
  117. package/src/lib/shared/types/CacheOptions.ts +23 -37
  118. package/src/lib/shared/types/CachePolicy.ts +25 -0
  119. package/src/lib/shared/types/CacheSnapshotEntry.ts +6 -5
  120. package/src/lib/shared/types/ErrorJsonSchemas.ts +8 -0
  121. package/src/lib/shared/types/HttpMethod.ts +3 -1
  122. package/src/lib/shared/types/InputCoercion.ts +17 -0
  123. package/src/lib/shared/types/{Subscribable.ts → NamedAsyncIterable.ts} +1 -1
  124. package/src/lib/shared/types/OutputWirePlan.ts +17 -0
  125. package/src/lib/shared/types/PagesScan.ts +7 -0
  126. package/src/lib/shared/types/PendingAsyncCells.ts +10 -0
  127. package/src/lib/shared/types/RawRemoteFunction.ts +6 -0
  128. package/src/lib/shared/types/RemoteCallable.ts +8 -7
  129. package/src/lib/shared/types/RemoteFunction.ts +16 -15
  130. package/src/lib/shared/types/ResolvedCells.ts +11 -0
  131. package/src/lib/shared/types/ReturnBody.ts +15 -0
  132. package/src/lib/shared/types/RpcBuildStamps.ts +23 -0
  133. package/src/lib/shared/types/RpcErrorGuard.ts +3 -8
  134. package/src/lib/shared/types/Socket.ts +4 -4
  135. package/src/lib/shared/types/SsrPayload.ts +5 -1
  136. package/src/lib/shared/types/StreamPolicy.ts +11 -0
  137. package/src/lib/shared/types/StreamedCells.ts +19 -0
  138. package/src/lib/shared/types/StreamedResolution.ts +24 -6
  139. package/src/lib/shared/types/TailHooks.ts +1 -1
  140. package/src/lib/shared/types/WireKind.ts +11 -0
  141. package/src/lib/shared/validationHttpError.ts +33 -0
  142. package/src/lib/shared/warmSeedKey.ts +16 -0
  143. package/src/lib/shared/wireJsonReplacer.ts +30 -0
  144. package/src/lib/shared/writeRpcDts.ts +11 -1
  145. package/src/lib/shared/writeTestSocketsDts.ts +1 -1
  146. package/src/lib/test/createTestApp.ts +19 -1
  147. package/src/lib/ui/README.md +1 -1
  148. package/src/lib/ui/activePendingCells.ts +14 -0
  149. package/src/lib/ui/compile/BLOCK_KEYWORDS.ts +21 -0
  150. package/src/lib/ui/compile/SSR_ESCAPE.ts +4 -1
  151. package/src/lib/ui/compile/UI_RUNTIME_IMPORTS.ts +35 -8
  152. package/src/lib/ui/compile/abideUiPlugin.ts +19 -2
  153. package/src/lib/ui/compile/analyzeComponent.ts +89 -6
  154. package/src/lib/ui/compile/assignmentTargetNames.ts +54 -0
  155. package/src/lib/ui/compile/asyncInterpolationFields.ts +152 -0
  156. package/src/lib/ui/compile/asyncValuePositionError.ts +21 -0
  157. package/src/lib/ui/compile/asyncValuePositionInterpolations.ts +64 -0
  158. package/src/lib/ui/compile/attrLiftPosition.ts +18 -0
  159. package/src/lib/ui/compile/cachedSourceFile.ts +40 -0
  160. package/src/lib/ui/compile/catchBinding.ts +7 -5
  161. package/src/lib/ui/compile/classifyInterpolationType.ts +46 -0
  162. package/src/lib/ui/compile/collectAbideDiagnostics.ts +96 -0
  163. package/src/lib/ui/compile/compileComponent.ts +19 -3
  164. package/src/lib/ui/compile/compileModule.ts +27 -58
  165. package/src/lib/ui/compile/compileSSR.ts +51 -12
  166. package/src/lib/ui/compile/compileShadow.ts +145 -18
  167. package/src/lib/ui/compile/composeProps.ts +12 -2
  168. package/src/lib/ui/compile/createShadowLanguageService.ts +116 -68
  169. package/src/lib/ui/compile/createShadowProgram.ts +35 -6
  170. package/src/lib/ui/compile/declaredNames.ts +36 -0
  171. package/src/lib/ui/compile/desugarSignals.ts +418 -36
  172. package/src/lib/ui/compile/expressionIsPrefixEvaluable.ts +19 -0
  173. package/src/lib/ui/compile/generateBuild.ts +52 -9
  174. package/src/lib/ui/compile/generateSSR.ts +242 -43
  175. package/src/lib/ui/compile/hoistableAwaits.ts +193 -0
  176. package/src/lib/ui/compile/hoistableChildRenders.ts +112 -0
  177. package/src/lib/ui/compile/interpolatedTemplateLiteral.ts +3 -1
  178. package/src/lib/ui/compile/interpolationClassifierForRoot.ts +37 -0
  179. package/src/lib/ui/compile/isSpuriousAsyncReadDiagnostic.ts +174 -0
  180. package/src/lib/ui/compile/liftAsyncSubExpressions.ts +166 -0
  181. package/src/lib/ui/compile/lowerAsyncInterpolations.ts +92 -0
  182. package/src/lib/ui/compile/lowerContext.ts +10 -0
  183. package/src/lib/ui/compile/lowerDocAccess.ts +26 -23
  184. package/src/lib/ui/compile/lowerScript.ts +40 -4
  185. package/src/lib/ui/compile/nodeAtShadowOffset.ts +28 -0
  186. package/src/lib/ui/compile/parseTemplate.ts +18 -1092
  187. package/src/lib/ui/compile/parseTemplateRecovering.ts +1385 -0
  188. package/src/lib/ui/compile/referencedIdentifiers.ts +35 -0
  189. package/src/lib/ui/compile/renameSignalRefs.ts +21 -22
  190. package/src/lib/ui/compile/seedTypeClassifierForRoot.ts +65 -0
  191. package/src/lib/ui/compile/shadowInterpolationClassifier.ts +47 -0
  192. package/src/lib/ui/compile/sourceFileOptionsSignature.ts +20 -0
  193. package/src/lib/ui/compile/structuralHeadTokens.ts +107 -0
  194. package/src/lib/ui/compile/templateSemanticTokens.ts +21 -0
  195. package/src/lib/ui/compile/templateStartOffset.ts +15 -0
  196. package/src/lib/ui/compile/tryPlan.ts +4 -3
  197. package/src/lib/ui/compile/types/AnalyzedComponent.ts +4 -0
  198. package/src/lib/ui/compile/types/AsyncInterpolationField.ts +19 -0
  199. package/src/lib/ui/compile/types/InterpolationClassifier.ts +12 -0
  200. package/src/lib/ui/compile/types/InterpolationKind.ts +7 -0
  201. package/src/lib/ui/compile/types/ParseDiagnostic.ts +8 -0
  202. package/src/lib/ui/compile/types/SeedTypeClassifier.ts +15 -0
  203. package/src/lib/ui/compile/types/TemplateNode.ts +36 -4
  204. package/src/lib/ui/compile/types/ValuePositionInterpolation.ts +13 -0
  205. package/src/lib/ui/compile/writtenTemplateNames.ts +84 -0
  206. package/src/lib/ui/computed.ts +28 -1
  207. package/src/lib/ui/createScope.ts +35 -68
  208. package/src/lib/ui/dom/anchoredBranch.ts +142 -0
  209. package/src/lib/ui/dom/appendText.ts +8 -3
  210. package/src/lib/ui/dom/awaitBlock.ts +43 -94
  211. package/src/lib/ui/dom/bindProp.ts +22 -0
  212. package/src/lib/ui/dom/bindableProp.ts +47 -0
  213. package/src/lib/ui/dom/cellPending.ts +24 -0
  214. package/src/lib/ui/dom/disposeRange.ts +2 -1
  215. package/src/lib/ui/dom/each.ts +26 -4
  216. package/src/lib/ui/dom/eachAsync.ts +39 -3
  217. package/src/lib/ui/dom/fillBoundary.ts +2 -3
  218. package/src/lib/ui/dom/fillRange.ts +4 -4
  219. package/src/lib/ui/dom/hydrate.ts +6 -12
  220. package/src/lib/ui/dom/isComment.ts +1 -1
  221. package/src/lib/ui/dom/matchingRangeClose.ts +29 -0
  222. package/src/lib/ui/dom/mount.ts +1 -2
  223. package/src/lib/ui/dom/mountChild.ts +9 -40
  224. package/src/lib/ui/dom/mountRange.ts +2 -3
  225. package/src/lib/ui/dom/mountStreamedChild.ts +84 -0
  226. package/src/lib/ui/dom/mountSwappableRange.ts +46 -4
  227. package/src/lib/ui/dom/mutateDocContainer.ts +65 -0
  228. package/src/lib/ui/dom/on.ts +2 -2
  229. package/src/lib/ui/dom/readCell.ts +40 -0
  230. package/src/lib/ui/dom/switchBlock.ts +30 -7
  231. package/src/lib/ui/dom/tryBlock.ts +230 -66
  232. package/src/lib/ui/dom/types/SwitchCase.ts +5 -1
  233. package/src/lib/ui/dom/when.ts +12 -2
  234. package/src/lib/ui/dom/withScope.ts +2 -2
  235. package/src/lib/ui/finalizeStreamedChildren.ts +89 -0
  236. package/src/lib/ui/flight.ts +56 -0
  237. package/src/lib/ui/html.ts +12 -1
  238. package/src/lib/ui/isolateCellBarrier.ts +17 -0
  239. package/src/lib/ui/linked.ts +48 -2
  240. package/src/lib/ui/remoteProxy.ts +84 -159
  241. package/src/lib/ui/renderChain.ts +49 -13
  242. package/src/lib/ui/renderToStream.ts +22 -18
  243. package/src/lib/ui/resumeSeedScript.ts +1 -1
  244. package/src/lib/ui/router.ts +63 -36
  245. package/src/lib/ui/runtime/AsyncCellError.ts +20 -0
  246. package/src/lib/ui/runtime/CELL_SEED.ts +14 -0
  247. package/src/lib/ui/runtime/CURRENT_BOUNDARY.ts +11 -0
  248. package/src/lib/ui/runtime/CURRENT_PATH.ts +29 -0
  249. package/src/lib/ui/runtime/RENDER.ts +10 -7
  250. package/src/lib/ui/runtime/RESUME.ts +4 -4
  251. package/src/lib/ui/runtime/STREAMED_CELLS.ts +57 -0
  252. package/src/lib/ui/runtime/ambientPathBacking.ts +32 -0
  253. package/src/lib/ui/runtime/applyPatchToTree.ts +1 -1
  254. package/src/lib/ui/runtime/blockId.ts +31 -0
  255. package/src/lib/ui/runtime/boundaryFor.ts +11 -0
  256. package/src/lib/ui/runtime/cellBarrierBacking.ts +30 -0
  257. package/src/lib/ui/runtime/createAsyncCell.ts +300 -0
  258. package/src/lib/ui/runtime/createDoc.ts +3 -42
  259. package/src/lib/ui/runtime/createEffectNode.ts +9 -0
  260. package/src/lib/ui/runtime/enterRenderPass.ts +5 -4
  261. package/src/lib/ui/runtime/flushEffects.ts +16 -6
  262. package/src/lib/ui/runtime/isAsyncFunction.ts +14 -0
  263. package/src/lib/ui/runtime/nextBlockId.ts +8 -7
  264. package/src/lib/ui/runtime/renderPath.ts +16 -0
  265. package/src/lib/ui/runtime/types/Boundary.ts +9 -0
  266. package/src/lib/ui/runtime/types/RenderContext.ts +9 -7
  267. package/src/lib/ui/runtime/types/SsrRender.ts +9 -2
  268. package/src/lib/ui/runtime/types/UiComponent.ts +1 -5
  269. package/src/lib/ui/runtime/withOptionalPath.ts +8 -0
  270. package/src/lib/ui/runtime/withPath.ts +16 -0
  271. package/src/lib/ui/runtime/withPathFrom.ts +20 -0
  272. package/src/lib/ui/seedStreamedResolution.ts +31 -6
  273. package/src/lib/ui/settleAsyncCells.ts +24 -0
  274. package/src/lib/ui/socketProxy.ts +1 -1
  275. package/src/lib/ui/startClient.ts +16 -31
  276. package/src/lib/ui/trackedComputed.ts +68 -0
  277. package/src/lib/ui/types/Scope.ts +8 -24
  278. package/src/lib/ui/watch.ts +3 -3
  279. package/src/serverEntry.ts +14 -0
  280. package/template/src/server/rpc/getHello.ts +15 -13
  281. package/template/test/app.test.ts +1 -1
  282. package/src/lib/server/runtime/devHotModuleResponse.ts +0 -41
  283. package/src/lib/shared/DEV_HOT_PREFIX.ts +0 -7
  284. package/src/lib/shared/UNREACHABLE_STATUSES.ts +0 -13
  285. package/src/lib/shared/hasReplayableRequest.ts +0 -17
  286. package/src/lib/shared/hydratingSlot.ts +0 -12
  287. package/src/lib/shared/outboxProbeSlot.ts +0 -20
  288. package/src/lib/shared/types/Outbox.ts +0 -9
  289. package/src/lib/shared/types/OutboxEntry.ts +0 -27
  290. package/src/lib/shared/types/SmartReadOptions.ts +0 -36
  291. package/src/lib/shared/wakeHydrationPeeks.ts +0 -20
  292. package/src/lib/ui/COMPONENT_WRAPPER_PREFIX.ts +0 -5
  293. package/src/lib/ui/compile/REACTIVE_CALLEES.ts +0 -11
  294. package/src/lib/ui/compile/assertRuntimeHelpersBound.ts +0 -80
  295. package/src/lib/ui/compile/markupTokens.ts +0 -313
  296. package/src/lib/ui/compile/structuralBlockTokens.ts +0 -100
  297. package/src/lib/ui/dom/applyResolved.ts +0 -87
  298. package/src/lib/ui/dom/mutateDocArray.ts +0 -38
  299. package/src/lib/ui/dom/scopeLabel.ts +0 -21
  300. package/src/lib/ui/dom/text.ts +0 -20
  301. package/src/lib/ui/history.ts +0 -108
  302. package/src/lib/ui/installHotBridge.ts +0 -95
  303. package/src/lib/ui/installInspectorBridge.ts +0 -140
  304. package/src/lib/ui/outbox.ts +0 -35
  305. package/src/lib/ui/persist.ts +0 -115
  306. package/src/lib/ui/rpcOutbox/createOutboxQueue.ts +0 -281
  307. package/src/lib/ui/rpcOutbox/outboxRegistry.ts +0 -69
  308. package/src/lib/ui/runtime/PATCH_BUS.ts +0 -28
  309. package/src/lib/ui/runtime/captureModelDoc.ts +0 -43
  310. package/src/lib/ui/runtime/hotInstances.ts +0 -10
  311. package/src/lib/ui/runtime/hotReloadEnabled.ts +0 -8
  312. package/src/lib/ui/runtime/hotReplace.ts +0 -38
  313. package/src/lib/ui/runtime/liveScopes.ts +0 -15
  314. package/src/lib/ui/runtime/localStoragePersistence.ts +0 -47
  315. package/src/lib/ui/runtime/registerHotInstance.ts +0 -23
  316. package/src/lib/ui/runtime/seedModelDoc.ts +0 -26
  317. package/src/lib/ui/runtime/types/HotInstance.ts +0 -22
  318. package/src/lib/ui/runtime/types/PatchEvent.ts +0 -16
  319. package/src/lib/ui/seedResolved.ts +0 -28
  320. package/src/lib/ui/sync.ts +0 -48
  321. package/src/lib/ui/types/History.ts +0 -14
  322. package/src/lib/ui/types/PersistHandle.ts +0 -11
  323. package/src/lib/ui/types/PersistenceStore.ts +0 -12
  324. package/src/lib/ui/types/ResolvedFrame.ts +0 -15
  325. package/src/lib/ui/types/SyncTransport.ts +0 -13
@@ -149,72 +149,69 @@ async function saveConfig() {
149
149
  }
150
150
  </script>
151
151
 
152
- <template if={phase === 'splash'}>
152
+ {#if phase === 'splash'}
153
153
  <div class="flex min-h-screen items-center justify-center bg-gray-50 dark:bg-gray-950">
154
- <template if={logo}>
154
+ {#if logo}
155
155
  <img src={logo} alt="" class="h-16 w-16 rounded-xl object-contain opacity-90" />
156
- </template>
156
+ {/if}
157
157
  </div>
158
- <template else>
159
- <main
160
- class="flex min-h-screen items-center justify-center bg-gray-50 p-6 text-gray-900 dark:bg-gray-950 dark:text-gray-100">
161
- <div
162
- class="w-full max-w-sm rounded-2xl bg-white p-8 shadow-sm ring-1 ring-gray-200 dark:bg-gray-900 dark:ring-gray-800">
163
- <template if={logo}>
164
- <img
165
- src={logo}
166
- alt=""
167
- class="mx-auto mb-5 h-16 w-16 rounded-xl object-contain" />
168
- </template>
169
- <h1 class="mb-6 text-center text-xl font-semibold tracking-tight">{heading}</h1>
170
-
171
- <form class="flex flex-col gap-3" onsubmit={(event) => { event.preventDefault(); void connect() }}>
172
- <input
173
- type="url"
174
- bind:value={url}
175
- placeholder={placeholder}
176
- autocomplete="url"
177
- class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none focus:border-gray-900 focus:ring-1 focus:ring-gray-900 dark:border-gray-700 dark:bg-gray-800 dark:focus:border-gray-100 dark:focus:ring-gray-100" />
178
- <button
179
- type="submit"
180
- class="w-full rounded-lg bg-gray-900 px-3 py-2 text-sm font-medium text-white hover:bg-gray-700 dark:bg-gray-100 dark:text-gray-900 dark:hover:bg-gray-300">
181
- Connect
182
- </button>
183
- </form>
184
-
185
- <div class="my-5 flex items-center gap-3 text-xs text-gray-400 dark:text-gray-500">
186
- <span class="h-px flex-1 bg-gray-200 dark:bg-gray-800"></span>
187
- or
188
- <span class="h-px flex-1 bg-gray-200 dark:bg-gray-800"></span>
189
- </div>
158
+ {:else}
159
+ <main
160
+ class="flex min-h-screen items-center justify-center bg-gray-50 p-6 text-gray-900 dark:bg-gray-950 dark:text-gray-100">
161
+ <div
162
+ class="w-full max-w-sm rounded-2xl bg-white p-8 shadow-sm ring-1 ring-gray-200 dark:bg-gray-900 dark:ring-gray-800">
163
+ {#if logo}
164
+ <img
165
+ src={logo}
166
+ alt=""
167
+ class="mx-auto mb-5 h-16 w-16 rounded-xl object-contain" />
168
+ {/if}
169
+ <h1 class="mb-6 text-center text-xl font-semibold tracking-tight">{heading}</h1>
190
170
 
171
+ <form class="flex flex-col gap-3" onsubmit={(event) => { event.preventDefault(); void connect() }}>
172
+ <input
173
+ type="url"
174
+ bind:value={url}
175
+ placeholder={placeholder}
176
+ autocomplete="url"
177
+ class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none focus:border-gray-900 focus:ring-1 focus:ring-gray-900 dark:border-gray-700 dark:bg-gray-800 dark:focus:border-gray-100 dark:focus:ring-gray-100" />
191
178
  <button
192
- type="button"
193
- onclick={() => void start()}
194
- class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm font-medium hover:bg-gray-50 dark:border-gray-700 dark:hover:bg-gray-800">
195
- Start server
179
+ type="submit"
180
+ class="w-full rounded-lg bg-gray-900 px-3 py-2 text-sm font-medium text-white hover:bg-gray-700 dark:bg-gray-100 dark:text-gray-900 dark:hover:bg-gray-300">
181
+ Connect
196
182
  </button>
183
+ </form>
197
184
 
198
- <template if={error}>
199
- <p class="mt-4 text-center text-sm text-red-600 dark:text-red-400">
200
- {error}
201
- </p>
202
- </template>
203
-
204
- <p class="mt-8 text-center text-xs text-gray-400 dark:text-gray-500">
205
- made with
206
- <a
207
- href="https://github.com/briancray/abide"
208
- class="underline hover:text-gray-600 dark:hover:text-gray-300"
209
- >abide</a
210
- >
211
- </p>
185
+ <div class="my-5 flex items-center gap-3 text-xs text-gray-400 dark:text-gray-500">
186
+ <span class="h-px flex-1 bg-gray-200 dark:bg-gray-800"></span>
187
+ or
188
+ <span class="h-px flex-1 bg-gray-200 dark:bg-gray-800"></span>
212
189
  </div>
213
- </main>
214
- </template>
215
- </template>
216
190
 
217
- <template if={showConfig}>
191
+ <button
192
+ type="button"
193
+ onclick={() => void start()}
194
+ class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm font-medium hover:bg-gray-50 dark:border-gray-700 dark:hover:bg-gray-800">
195
+ Start server
196
+ </button>
197
+
198
+ {#if error}
199
+ <p class="mt-4 text-center text-sm text-red-600 dark:text-red-400">{error}</p>
200
+ {/if}
201
+
202
+ <p class="mt-8 text-center text-xs text-gray-400 dark:text-gray-500">
203
+ made with
204
+ <a
205
+ href="https://github.com/briancray/abide"
206
+ class="underline hover:text-gray-600 dark:hover:text-gray-300"
207
+ >abide</a
208
+ >
209
+ </p>
210
+ </div>
211
+ </main>
212
+ {/if}
213
+
214
+ {#if showConfig}
218
215
  <div
219
216
  class="fixed inset-0 z-10 flex items-center justify-center bg-black/40 p-6 text-gray-900 dark:text-gray-100">
220
217
  <div
@@ -222,29 +219,28 @@ async function saveConfig() {
222
219
  <h2 class="mb-5 text-lg font-semibold tracking-tight">Set up {heading}</h2>
223
220
 
224
221
  <form class="flex flex-col gap-4" onsubmit={(event) => { event.preventDefault(); void saveConfig() }}>
225
- <template each={configFields} as="field" key="field.key">
222
+ {#for field of configFields by field.key}
226
223
  <label class="flex flex-col gap-1 text-sm">
227
224
  <span class="font-medium">{field.label}</span>
228
- <template if={field.inputType === 'checkbox'}>
225
+ {#if field.inputType === 'checkbox'}
229
226
  <input
230
227
  type="checkbox"
231
228
  onchange={(event) => { configValues[field.key] = event.currentTarget.checked ? 'true' : 'false' }}
232
229
  class="mt-1 size-4 self-start rounded border-gray-300 dark:border-gray-700" />
233
- <template else>
234
- <input
235
- type={field.inputType}
236
- value={configValues[field.key]}
237
- oninput={(event) => { configValues[field.key] = event.currentTarget.value }}
238
- class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none focus:border-gray-900 focus:ring-1 focus:ring-gray-900 dark:border-gray-700 dark:bg-gray-800 dark:focus:border-gray-100 dark:focus:ring-gray-100" />
239
- </template>
240
- </template>
241
- <template if={field.description}>
230
+ {:else}
231
+ <input
232
+ type={field.inputType}
233
+ value={configValues[field.key]}
234
+ oninput={(event) => { configValues[field.key] = event.currentTarget.value }}
235
+ class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none focus:border-gray-900 focus:ring-1 focus:ring-gray-900 dark:border-gray-700 dark:bg-gray-800 dark:focus:border-gray-100 dark:focus:ring-gray-100" />
236
+ {/if}
237
+ {#if field.description}
242
238
  <span class="text-xs text-gray-400 dark:text-gray-500"
243
239
  >{field.description}</span
244
240
  >
245
- </template>
241
+ {/if}
246
242
  </label>
247
- </template>
243
+ {/for}
248
244
 
249
245
  <div class="mt-1 flex gap-3">
250
246
  <button
@@ -261,9 +257,9 @@ async function saveConfig() {
261
257
  </div>
262
258
  </form>
263
259
 
264
- <template if={error}>
260
+ {#if error}
265
261
  <p class="mt-4 text-center text-sm text-red-600 dark:text-red-400">{error}</p>
266
- </template>
262
+ {/if}
267
263
  </div>
268
264
  </div>
269
- </template>
265
+ {/if}
@@ -15,9 +15,15 @@ export function infoPlist({
15
15
  version: string
16
16
  icon?: string
17
17
  }): string {
18
+ /* Interpolated values ride inside plist `<string>` elements, so any `&`/`<`/`>` in a
19
+ project name would otherwise emit malformed XML that codesign/Gatekeeper reject. */
20
+ const escapeXml = (value: string): string =>
21
+ value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;')
22
+ const safeName = escapeXml(name)
23
+ const safeVersion = escapeXml(version)
18
24
  const iconEntry = icon
19
25
  ? ` <key>CFBundleIconFile</key>
20
- <string>${icon}</string>
26
+ <string>${escapeXml(icon)}</string>
21
27
  `
22
28
  : ''
23
29
  return `<?xml version="1.0" encoding="UTF-8"?>
@@ -25,17 +31,17 @@ export function infoPlist({
25
31
  <plist version="1.0">
26
32
  <dict>
27
33
  <key>CFBundleName</key>
28
- <string>${name}</string>
34
+ <string>${safeName}</string>
29
35
  <key>CFBundleDisplayName</key>
30
- <string>${name}</string>
36
+ <string>${safeName}</string>
31
37
  <key>CFBundleExecutable</key>
32
- <string>${name}</string>
38
+ <string>${safeName}</string>
33
39
  <key>CFBundleIdentifier</key>
34
- <string>com.abide.${name}</string>
40
+ <string>com.abide.${safeName}</string>
35
41
  <key>CFBundleVersion</key>
36
- <string>${version}</string>
42
+ <string>${safeVersion}</string>
37
43
  <key>CFBundleShortVersionString</key>
38
- <string>${version}</string>
44
+ <string>${safeVersion}</string>
39
45
  ${iconEntry} <key>CFBundlePackageType</key>
40
46
  <string>APPL</string>
41
47
  <key>NSHighResolutionCapable</key>
@@ -53,10 +53,19 @@ export async function spawnEmbeddedServer({
53
53
  env: { ...process.env, PORT: String(port), ABIDE_PARENT_PID: String(process.pid) },
54
54
  stdio: ['inherit', 'inherit', 'inherit'],
55
55
  })
56
- const outcome = await Promise.race([
57
- waitForServer(url, timeoutMs ? { timeoutMs } : undefined).then(() => undefined),
58
- child.exited,
59
- ])
56
+ let outcome: number | undefined
57
+ try {
58
+ outcome = await Promise.race([
59
+ waitForServer(url, timeoutMs ? { timeoutMs } : undefined).then(() => undefined),
60
+ child.exited,
61
+ ])
62
+ } catch (error) {
63
+ /* Readiness timed out (waitForServer threw) — reap the child we spawned so it doesn't
64
+ orphan and hold its fixed PORT, which would then fail the next spawn's bind. The caller
65
+ only owns the child once we successfully return it. */
66
+ child.kill()
67
+ throw error
68
+ }
60
69
  if (outcome !== undefined) {
61
70
  throw new Error(`[abide] embedded server exited (code ${outcome}) before binding`)
62
71
  }
@@ -13,7 +13,12 @@ export async function waitForServer(
13
13
  const deadline = Bun.nanoseconds() + timeoutMs * 1e6
14
14
  while (Bun.nanoseconds() < deadline) {
15
15
  try {
16
- await fetch(url)
16
+ /* Bound each probe by the time left to the deadline: a connection that is accepted but
17
+ stalls its HTTP response leaves a bare `fetch(url)` pending forever (it never rejects),
18
+ which would hang the loop past its own timeout. The abort caps a single hung probe at
19
+ the remaining budget so the deadline is always honored. */
20
+ const remainingMs = Math.max(0, (deadline - Bun.nanoseconds()) / 1e6)
21
+ await fetch(url, { signal: AbortSignal.timeout(remainingMs) })
17
22
  return
18
23
  } catch {
19
24
  await Bun.sleep(intervalMs)
@@ -1,4 +1,8 @@
1
1
  import { dispatchRpcInProcess } from '../server/rpc/dispatchRpcInProcess.ts'
2
+ import {
3
+ resolveInputJsonSchema,
4
+ resolveOutputJsonSchema,
5
+ } from '../server/rpc/resolveRpcJsonSchema.ts'
2
6
  import { rpcRegistry } from '../server/rpc/rpcRegistry.ts'
3
7
  import type { RpcRegistryEntry } from '../server/rpc/types/RpcRegistryEntry.ts'
4
8
  import { socketOperations } from '../server/sockets/socketOperations.ts'
@@ -37,7 +41,7 @@ RPCs enumerate first, so on a name collision the rpc tool wins.
37
41
  function mcpToolRefs(): McpToolRef[] {
38
42
  const refs: McpToolRef[] = []
39
43
  for (const entry of rpcRegistry.values()) {
40
- if (!entry.clients.mcp) {
44
+ if (!entry.remote.clients.mcp) {
41
45
  continue
42
46
  }
43
47
  refs.push({ kind: 'rpc', name: commandNameForUrl(entry.remote.url), entry })
@@ -63,7 +67,9 @@ from a write.
63
67
  */
64
68
  function describeTool(ref: McpToolRef): ToolDescriptor {
65
69
  if (ref.kind === 'rpc') {
66
- const inputSchema = jsonSchemaForSchema(ref.entry.inputSchema)
70
+ // The declared inputSchema VALIDATOR overrides the build-projected input-type schema
71
+ // (ADR-0030 input side); with neither the tool advertises the opaque fallback, as before.
72
+ const inputSchema = resolveInputJsonSchema(ref.entry) ?? jsonSchemaForSchema(undefined)
67
73
  const tool: ToolDescriptor = {
68
74
  name: ref.name,
69
75
  description:
@@ -72,8 +78,11 @@ function describeTool(ref: McpToolRef): ToolDescriptor {
72
78
  inputSchema,
73
79
  annotations: annotationsForMethod(ref.entry.remote.method),
74
80
  }
75
- if (ref.entry.outputSchema) {
76
- tool.outputSchema = jsonSchemaForSchema(ref.entry.outputSchema)
81
+ // The declared outputSchema VALIDATOR overrides the build-projected return-type schema
82
+ // (ADR-0030 D2); with neither the tool advertises no outputSchema, exactly as before.
83
+ const outputSchema = resolveOutputJsonSchema(ref.entry)
84
+ if (outputSchema) {
85
+ tool.outputSchema = outputSchema
77
86
  }
78
87
  return tool
79
88
  }
@@ -149,8 +158,8 @@ function callSocketTool(
149
158
  }
150
159
  }
151
160
  try {
152
- // broadcast() validates the payload against the socket schema and throws on failure.
153
- entry.socket.broadcast(args)
161
+ // publish() validates the payload against the socket schema and throws on failure.
162
+ entry.socket.publish(args)
154
163
  } catch (error) {
155
164
  return textResult(messageFromError(error), true)
156
165
  }
@@ -93,15 +93,15 @@ expose the error on `rpc.isError(e, 'name')` (`.kind` and typed `.data`). No
93
93
  (a const can't read its own variable name); `schema` types `.data` and is never
94
94
  validated at runtime here.
95
95
  */
96
- function typed<Name extends string, Schema extends StandardSchemaV1>(
96
+ function typed<Name extends string, Status extends number, Schema extends StandardSchemaV1>(
97
97
  name: Name,
98
- status: number,
98
+ status: Status,
99
99
  schema: Schema,
100
- ): (data: StandardSchemaV1.InferInput<Schema>) => TypedError<Name, { status: number; data: Schema }>
101
- function typed<Name extends string>(
100
+ ): (data: StandardSchemaV1.InferInput<Schema>) => TypedError<Name, { status: Status; data: Schema }>
101
+ function typed<Name extends string, Status extends number>(
102
102
  name: Name,
103
- status: number,
104
- ): () => TypedError<Name, { status: number; data?: undefined }>
103
+ status: Status,
104
+ ): () => TypedError<Name, { status: Status; data?: undefined }>
105
105
  function typed(name: string, status: number, _schema?: StandardSchemaV1) {
106
106
  return (data?: unknown) => typedErrorResponse(name, status, data)
107
107
  }
@@ -1,7 +1,13 @@
1
1
  import { NO_STORE } from '../shared/CACHE_CONTROL_VALUES.ts'
2
+ import { encodeWireBody } from '../shared/encodeWireBody.ts'
2
3
  import type { TypedResponse } from './rpc/types/TypedResponse.ts'
3
4
  import { withResponseDefaults } from './runtime/withResponseDefaults.ts'
4
5
 
6
+ /* The Content-Type `Response.json` sets — replicated here because the wire-encode step serializes
7
+ with a replacer, so the body is built by hand rather than through `Response.json`. Seeds the
8
+ default header set so an explicit caller `content-type` still wins. */
9
+ const JSON_CONTENT_TYPE = 'application/json;charset=utf-8'
10
+
5
11
  /*
6
12
  JSON Response with rpc-friendly defaults — same shape as
7
13
  `Response.json(data, init)`, except `Cache-Control: no-store` is set
@@ -36,8 +42,16 @@ export function json<T>(data: T, init?: ResponseInit): TypedResponse<T> {
36
42
  withResponseDefaults(init, { 'Cache-Control': NO_STORE }, 204),
37
43
  ) as TypedResponse<T>
38
44
  }
39
- return Response.json(
40
- data,
41
- withResponseDefaults(init, { 'Cache-Control': NO_STORE }),
45
+ /* Wire-encode the body (ADR-0029 output path) through the shared honest-JSON encoder: a
46
+ value-directed replacer rewrites a `Set` → array, a `Map` → `[K,V]` entries, and a `bigint`
47
+ → digit string, so a structured return crosses as honest JSON rather than a lost `{}` or a
48
+ 500 (plain `Response.json` throws on a bigint). A `Date` already rides as an ISO string via
49
+ its native `toJSON`. */
50
+ return new Response(
51
+ encodeWireBody(data),
52
+ withResponseDefaults(init, {
53
+ 'Cache-Control': NO_STORE,
54
+ 'Content-Type': JSON_CONTENT_TYPE,
55
+ }),
42
56
  ) as TypedResponse<T>
43
57
  }
@@ -4,10 +4,14 @@ import { createRemoteFunction } from '../../shared/createRemoteFunction.ts'
4
4
  import { forwardHeaders } from '../../shared/forwardHeaders.ts'
5
5
  import { isReadOnlyMethod } from '../../shared/isReadOnlyMethod.ts'
6
6
  import { resolveClientFlags } from '../../shared/resolveClientFlags.ts'
7
+ import type { CachePolicy } from '../../shared/types/CachePolicy.ts'
7
8
  import type { ClientFlags } from '../../shared/types/ClientFlags.ts'
9
+ import type { ErrorJsonSchemas } from '../../shared/types/ErrorJsonSchemas.ts'
8
10
  import type { HttpMethod } from '../../shared/types/HttpMethod.ts'
11
+ import type { InputCoercion } from '../../shared/types/InputCoercion.ts'
9
12
  import type { RemoteFunction } from '../../shared/types/RemoteFunction.ts'
10
13
  import type { StandardSchemaV1 } from '../../shared/types/StandardSchemaV1.ts'
14
+ import type { StreamPolicy } from '../../shared/types/StreamPolicy.ts'
11
15
  import { requestContext } from '../runtime/requestContext.ts'
12
16
  import { parseArgs } from './parseArgs.ts'
13
17
  import { registerRpc } from './registerRpc.ts'
@@ -50,38 +54,57 @@ export function defineRpc<Args, Return>(
50
54
  url: string,
51
55
  handler: RemoteHandler<Args, Return>,
52
56
  opts?: {
53
- inputSchema?: StandardSchemaV1
54
- outputSchema?: StandardSchemaV1
55
- filesSchema?: StandardSchemaV1
57
+ /* Namespaced schemas (ADR-0020): `input` types + validates args, `output` is the
58
+ success-body schema (OpenAPI/MCP), `files` validates multipart File parts. */
59
+ schemas?: {
60
+ input?: StandardSchemaV1
61
+ output?: StandardSchemaV1
62
+ files?: StandardSchemaV1
63
+ }
64
+ /* Surface-exposure flags (browser/mcp/cli) — which surfaces advertise this rpc. */
56
65
  clients?: Partial<ClientFlags>
57
66
  crossOrigin?: boolean
58
67
  /* Per-rpc cap on actual received body bytes (413 past it); omitted = Bun's server-wide maxRequestBodySize. */
59
68
  maxBodySize?: number
60
69
  /* Per-rpc handler deadline (ms): a 504 once exceeded, on every surface (SSR/MCP/CLI/network). */
61
70
  timeout?: number
62
- /* Durable delivery: on an unreachable server (transport failure / 502/503/504/52x)
63
- the client proxy parks the request for replay instead of just throwing. The
64
- parked write drains on `rpc.outbox.retry()` (no auto-drain). Mutating methods
65
- only. */
66
- outbox?: boolean
67
- /* Handler returns jsonl()/sse(): the bare call returns the Subscribable directly (for
71
+ /* Handler returns jsonl()/sse(): the bare call returns the NamedAsyncIterable directly (for
68
72
  isomorphic `for await (… of fn(args))` in server code). Bundler-stamped; the router's
69
73
  wire path (.fetch) is unaffected. */
70
74
  streaming?: boolean
75
+ /* Numeric/boolean input fields → parseArgs coerces a string query/form value into the
76
+ typed value the input schema expects (ADR-0028). Bundler-stamped from the endpoint's
77
+ `Args` type; absent on the fail-open path (a GET field then stays a string as before). */
78
+ coerce?: InputCoercion
79
+ /* The handler's input args projected to JSON Schema (ADR-0030 input side). Bundler-stamped
80
+ from the endpoint's input-parameter type; feeds the OpenAPI parameters/body / MCP inputSchema
81
+ / inspector input surface when no `schemas.input` validator is declared (which still
82
+ overrides it), and makes the endpoint advertisable to MCP/CLI. A SHAPE description only —
83
+ never runtime-validated, so it can't produce a 422. Absent on the fail-open path — the
84
+ surface then omits the input schema exactly as before. */
85
+ inputJsonSchema?: Record<string, unknown>
86
+ /* The handler's return type projected to JSON Schema (ADR-0030 D2). Bundler-stamped from the
87
+ endpoint's success-body type; feeds the OpenAPI 200 / MCP outputSchema / inspector surface
88
+ when no `schemas.output` validator is declared (which still overrides it). Absent on the
89
+ fail-open path — the surface then omits the response schema exactly as before. */
90
+ outputJsonSchema?: Record<string, unknown>
91
+ /* The handler's typed-error branches as a status-keyed JSON-Schema map (ADR-0030). Bundler-
92
+ stamped from the endpoint's `error.typed(...)` return branches; feeds the OpenAPI
93
+ `responses[status]` surface so each typed error's status + data payload is documented
94
+ alongside the 200. Absent on the fail-open path — the surface then omits the error
95
+ responses exactly as before. */
96
+ errorJsonSchemas?: ErrorJsonSchemas
97
+ /* Endpoint cache policy (ADR-0020) — read helpers only; readThrough's bottom layer. */
98
+ cache?: CachePolicy<Args>
99
+ /* Endpoint stream policy (ADR-0020) — streaming read helpers only; replay depth `n`. */
100
+ stream?: StreamPolicy
71
101
  },
72
102
  ): RemoteFunction<Args, Return> {
73
- /* `outbox: true` is a mutation contract — a read RPC has nothing to durably deliver,
74
- so reject it at declaration rather than silently ignore. The server handler itself
75
- is unaffected; durability is a client-proxy concern. */
76
- if (opts?.outbox === true && isReadOnlyMethod(method)) {
77
- throw new Error(
78
- `[abide] outbox: true is only valid on mutating RPCs (POST/PUT/PATCH/DELETE), not ${method}`,
79
- )
80
- }
81
103
  const timeout = opts?.timeout
82
- const inputSchema = opts?.inputSchema
83
- const outputSchema = opts?.outputSchema
84
- const filesSchema = opts?.filesSchema
104
+ const inputSchema = opts?.schemas?.input
105
+ const inputJsonSchema = opts?.inputJsonSchema
106
+ const outputSchema = opts?.schemas?.output
107
+ const filesSchema = opts?.schemas?.files
85
108
  /*
86
109
  An input schema makes the handler safe to advertise to non-browser
87
110
  surfaces. CLI flips on for any rpc with one (a human/script invokes it
@@ -89,8 +112,16 @@ export function defineRpc<Args, Return>(
89
112
  model shouldn't be able to mutate/delete just because the handler
90
113
  carries a schema, so mutating rpcs require an explicit clients.mcp.
91
114
  Explicit `clients` always wins.
115
+
116
+ A build-projected `inputJsonSchema` (ADR-0030 input side — the handler's
117
+ input parameter type projected to JSON Schema, present only when the type
118
+ resolved cleanly) counts as a machine-advertisable contract too, so a
119
+ plainly-typed handler auto-exposes without a hand-written `schemas.input`.
120
+ This is SHAPE-only: the projection describes the input, it is NEVER run as
121
+ runtime validation (no 422) — declaring `schemas.input` is still what adds
122
+ the runtime narrowing.
92
123
  */
93
- const hasSchema = inputSchema !== undefined
124
+ const hasSchema = inputSchema !== undefined || inputJsonSchema !== undefined
94
125
  const clients = resolveClientFlags(opts?.clients, {
95
126
  mcp: hasSchema && isReadOnlyMethod(method),
96
127
  cli: hasSchema,
@@ -189,10 +220,12 @@ export function defineRpc<Args, Return>(
189
220
  clients,
190
221
  streaming: opts?.streaming ?? false,
191
222
  crossOrigin: opts?.crossOrigin,
223
+ cache: opts?.cache,
224
+ stream: opts?.stream,
192
225
  buildRequest,
193
226
  invoke,
194
227
  parseArgsForFetch: async (request) => {
195
- const args = await parseArgs(method, request, opts?.maxBodySize)
228
+ const args = await parseArgs(method, request, opts?.maxBodySize, opts?.coerce)
196
229
  /*
197
230
  Compose this rpc's deadline into request().signal so a handler's
198
231
  fetch(ext, { signal: request().signal }) is cancelled when the
@@ -225,12 +258,13 @@ export function defineRpc<Args, Return>(
225
258
  registerRpc({
226
259
  remote: remote as RemoteFunction<unknown, unknown>,
227
260
  inputSchema,
261
+ inputJsonSchema,
228
262
  outputSchema,
263
+ outputJsonSchema: opts?.outputJsonSchema,
264
+ errorJsonSchemas: opts?.errorJsonSchemas,
229
265
  filesSchema,
230
- clients,
231
266
  timeout,
232
267
  maxBodySize: opts?.maxBodySize,
233
- crossOrigin: opts?.crossOrigin,
234
268
  })
235
269
  return remote
236
270
  }