@abide/abide 0.49.0 → 0.50.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.
Files changed (325) hide show
  1. package/AGENTS.md +70 -52
  2. package/CHANGELOG.md +177 -0
  3. package/README.md +12 -7
  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
@@ -1,9 +1,12 @@
1
1
  import { carriesBodyArgs } from '../../shared/carriesBodyArgs.ts'
2
2
  import { contentTypeOf } from '../../shared/contentTypeOf.ts'
3
- import { decodeRefJson } from '../../shared/decodeRefJson.ts'
3
+ import { decodeWireBody } from '../../shared/decodeWireBody.ts'
4
4
  import { HttpError } from '../../shared/HttpError.ts'
5
5
  import { REF_JSON_HEADER } from '../../shared/REF_JSON_HEADER.ts'
6
+ import { reviveWireField } from '../../shared/reviveWireField.ts'
6
7
  import type { HttpMethod } from '../../shared/types/HttpMethod.ts'
8
+ import type { InputCoercion } from '../../shared/types/InputCoercion.ts'
9
+ import type { WireKind } from '../../shared/types/WireKind.ts'
7
10
  import { error } from '../error.ts'
8
11
  import { requestContext } from '../runtime/requestContext.ts'
9
12
  import { readBodyWithinLimit } from './readBodyWithinLimit.ts'
@@ -62,6 +65,7 @@ export async function parseArgs(
62
65
  method: HttpMethod,
63
66
  request: Request,
64
67
  maxBodySize?: number,
68
+ coerce?: InputCoercion,
65
69
  ): Promise<unknown> {
66
70
  /*
67
71
  Skip the URL parse entirely when the raw request URL has no query —
@@ -99,9 +103,7 @@ export async function parseArgs(
99
103
  OpenAPI SDK) omits it and sends ordinary JSON — read with plain
100
104
  JSON.parse, since the ref-json envelope is ambiguous with a 2-element
101
105
  array body. */
102
- body = bounded.headers.has(REF_JSON_HEADER)
103
- ? decodeRefJson(text)
104
- : JSON.parse(text)
106
+ body = decodeWireBody(text, bounded.headers.has(REF_JSON_HEADER))
105
107
  }
106
108
  } else if (
107
109
  contentType.includes('application/x-www-form-urlencoded') ||
@@ -122,19 +124,22 @@ export async function parseArgs(
122
124
  }
123
125
 
124
126
  if (!url) {
125
- /* `body` is undefined or a plain object here return it as-is. */
127
+ /* `body` is undefined or a plain object here. A form-encoded body arrives stringly, so
128
+ coerce its numeric/boolean fields (a JSON body's values are already typed — non-strings
129
+ the coercion leaves untouched). */
130
+ if (body !== undefined && coerce !== undefined) {
131
+ applyCoercion(body as Record<string, unknown>, coerce)
132
+ }
126
133
  return body
127
134
  }
128
135
 
129
136
  /*
130
- TODO(query-coercion): query params arrive as strings, so a numeric/boolean
131
- field reaches schema validation as `'2'`/`'true'`. Deferred deliberately:
132
- parseArgs has no access to the rpc's inputSchema (it lives in defineRpc),
133
- and Standard Schema exposes no type structure to drive type-aware coercion.
134
- Blind value-shape coercion is unsafe it would corrupt legitimately
135
- string-typed fields whose value looks numeric/boolean (ids, zip codes,
136
- version strings like '1.0'), silently breaking GET validation. A correct fix
137
- needs the schema threaded in here (or a coercing schema adapter at the rpc).
137
+ Query params (and form-encoded body fields) arrive as strings, so a numeric/boolean field
138
+ would reach schema validation as `'2'`/`'true'`. `coerce` (ADR-0028) is the build-time plan
139
+ of exactly which fields are numeric/boolean in the endpoint's `Args` type resolved through
140
+ the warm server program, so a string-typed field that merely looks numeric (an id, a zip code,
141
+ `'1.0'`) is never listed and stays a string. Applied to the merged bag below; a JSON body's
142
+ already-typed values are non-strings the coercion skips.
138
143
  */
139
144
  const bodyObject = (body ?? {}) as Record<string, unknown>
140
145
  /* Collect the query into an object, arraying repeated keys (`?tag=a&tag=b` → `['a','b']`)
@@ -155,5 +160,85 @@ export async function parseArgs(
155
160
  if (Object.keys(merged).length === 0) {
156
161
  return undefined
157
162
  }
163
+ if (coerce !== undefined) {
164
+ applyCoercion(merged, coerce)
165
+ }
158
166
  return merged
159
167
  }
168
+
169
+ /*
170
+ Revives the plain-JSON wire values in `args` into the runtime types the plan declares, in place
171
+ (ADR-0028 scalars, ADR-0029 structured). The scalar kinds (number/boolean/date/bigint) touch
172
+ strings only — a value the merge layered in from a typed JSON body is already the right type and
173
+ left alone; a repeated key (`?tag=1&tag=2`) is an array whose string members revive per element.
174
+ The container kinds (set/map) build from a JSON array (or object) but pass an already-typed value
175
+ (from the abide client's ref-json body) through untouched. Every branch is fail-open: an
176
+ unrevivable value stays its JSON form so the schema surfaces an honest issue rather than a throw.
177
+ */
178
+ function applyCoercion(args: Record<string, unknown>, coerce: InputCoercion): void {
179
+ for (const key in coerce) {
180
+ const kind = coerce[key]
181
+ if (kind === undefined || !(key in args)) {
182
+ continue
183
+ }
184
+ args[key] = reviveValue(args[key], kind)
185
+ }
186
+ }
187
+
188
+ /* One field's wire value → its runtime type per `kind`, fail-open. The container kinds wrap a JSON
189
+ array/object; the scalar kinds revive a string (or each string in a repeated-key array). An
190
+ already-typed value (Set/Map/Date/bigint from the abide ref-json body) passes through. */
191
+ function reviveValue(value: unknown, kind: WireKind): unknown {
192
+ /* Container kinds (set/map) decode through the shared wire codec; the input side additionally
193
+ accepts a plain object as Map entries — a query/form body can't send a JSON entries array
194
+ (ADR-0029 defers descending into array MEMBERS). */
195
+ if (kind === 'set' || kind === 'map') {
196
+ if (
197
+ kind === 'map' &&
198
+ !(value instanceof Map) &&
199
+ !Array.isArray(value) &&
200
+ value !== null &&
201
+ typeof value === 'object'
202
+ ) {
203
+ return new Map(Object.entries(value))
204
+ }
205
+ return reviveWireField(value, kind)
206
+ }
207
+ /* Scalar kinds (number/boolean/date/bigint): query/form values arrive as strings — a lone
208
+ value, or each string in a repeated-key array. */
209
+ if (typeof value === 'string') {
210
+ return reviveScalar(value, kind)
211
+ }
212
+ if (Array.isArray(value)) {
213
+ return value.map((element) =>
214
+ typeof element === 'string' ? reviveScalar(element, kind) : element,
215
+ )
216
+ }
217
+ return value
218
+ }
219
+
220
+ /* One string → its number/boolean/date/bigint value, or the original string when it doesn't parse
221
+ cleanly (empty/whitespace, `NaN`, a non-`true`/`false` boolean, an unparseable date, a
222
+ non-integer bigint), so a bad value fails validation with the field intact instead of coercing
223
+ to `0`/`NaN`/`Invalid Date`. */
224
+ function reviveScalar(value: string, kind: WireKind): unknown {
225
+ if (kind === 'number') {
226
+ if (value.trim() === '') {
227
+ return value
228
+ }
229
+ const parsed = Number(value)
230
+ return Number.isNaN(parsed) ? value : parsed
231
+ }
232
+ if (kind === 'boolean') {
233
+ if (value === 'true') {
234
+ return true
235
+ }
236
+ if (value === 'false') {
237
+ return false
238
+ }
239
+ return value
240
+ }
241
+ /* date/bigint from a query/form string decode identically to the shared wire codec (ISO
242
+ string → Date, digit string → bigint), fail-open. */
243
+ return reviveWireField(value, kind)
244
+ }
@@ -0,0 +1,28 @@
1
+ import { jsonSchemaForSchema } from '../../shared/jsonSchemaForSchema.ts'
2
+ import type { RpcRegistryEntry } from './types/RpcRegistryEntry.ts'
3
+
4
+ /*
5
+ The ONE resolution every surface (MCP tools, OpenAPI, the inspector catalog, and the
6
+ standalone CLI manifest) uses for an rpc's advertised INPUT shape: a declared
7
+ `inputSchema` VALIDATOR overrides the build-projected `inputJsonSchema` (ADR-0030 input
8
+ side); with neither, `undefined`. Callers that need a non-empty schema apply the opaque
9
+ `?? jsonSchemaForSchema(undefined)` fallback themselves (MCP/OpenAPI/CLI want it; the
10
+ inspector leaves it undefined). One origin so the surfaces can't drift — the CLI manifest
11
+ emitter previously read only `inputSchema` and silently dropped every plainly-typed
12
+ handler's projected shape (no typed flags, no `--help`, array flags overwriting).
13
+ */
14
+ export function resolveInputJsonSchema(
15
+ entry: RpcRegistryEntry,
16
+ ): Record<string, unknown> | undefined {
17
+ return entry.inputSchema ? jsonSchemaForSchema(entry.inputSchema) : entry.inputJsonSchema
18
+ }
19
+
20
+ /*
21
+ The output analog: an `outputSchema` VALIDATOR overrides the projected `outputJsonSchema`;
22
+ with neither, `undefined` (no output shape advertised — every surface leaves it off).
23
+ */
24
+ export function resolveOutputJsonSchema(
25
+ entry: RpcRegistryEntry,
26
+ ): Record<string, unknown> | undefined {
27
+ return entry.outputSchema ? jsonSchemaForSchema(entry.outputSchema) : entry.outputJsonSchema
28
+ }
@@ -1,7 +1,9 @@
1
+ import type { CachePolicy } from '../../../shared/types/CachePolicy.ts'
1
2
  import type { ClientFlags } from '../../../shared/types/ClientFlags.ts'
2
3
  import type { ErrorSpec } from '../../../shared/types/ErrorSpec.ts'
3
4
  import type { RemoteFunction } from '../../../shared/types/RemoteFunction.ts'
4
5
  import type { StandardSchemaV1 } from '../../../shared/types/StandardSchemaV1.ts'
6
+ import type { StreamPolicy } from '../../../shared/types/StreamPolicy.ts'
5
7
  import type { TypedError } from './TypedError.ts'
6
8
  import type { TypedResponse } from './TypedResponse.ts'
7
9
 
@@ -49,25 +51,39 @@ type RpcArgs<F extends RpcFn> = Parameters<F> extends [infer Args, ...unknown[]]
49
51
  The RemoteFunction a bare overload produces from handler `F`. `SuccessBody`/`InferredErrors`
50
52
  run over `Awaited<ReturnType<F>>` INLINE (not via an intermediate `extends RpcFn`-constrained
51
53
  alias) — such an alias resolves `ReturnType` against the constraint bound and degrades the
52
- body to `any`. `Durable` threads the outbox bit through for the durable overloads.
54
+ body to `any`.
53
55
  */
54
- type RpcOf<F extends RpcFn, Durable extends boolean = false> = RemoteFunction<
56
+ type RpcOf<F extends RpcFn> = RemoteFunction<
55
57
  RpcArgs<F>,
56
58
  SuccessBody<Awaited<ReturnType<F>>>,
57
- InferredErrors<Awaited<ReturnType<F>>>,
58
- Durable
59
+ InferredErrors<Awaited<ReturnType<F>>>
59
60
  >
60
61
 
61
62
  /*
62
- Options every rpc overload accepts: the OpenAPI 200 `outputSchema`, the
63
- `clients` surface flags, the same-origin CSRF exemption (`crossOrigin`), the
64
- pre-parse body-byte ceiling (`maxBodySize`), and the per-surface handler
65
- `timeout` (ms). The schema-bearing overloads intersect this with their own
66
- `inputSchema`/`filesSchema` members. Mutating helpers widen it with `outbox`
67
- (see MutatingRpcOpts) — a read RPC never accepts it.
63
+ The schema namespace (ADR-0020): `schemas: { input?, output?, files? }` replaces the
64
+ flat `inputSchema`/`outputSchema`/`filesSchema`. `input` drives the handler's arg type,
65
+ `output` is the success-body schema (OpenAPI 200 / MCP outputSchema — never drives arg
66
+ inference), `files` validates multipart File parts. The schema-bearing helper overloads
67
+ tighten specific members to required.
68
68
  */
69
- type RpcBaseOpts = {
70
- outputSchema?: StandardSchemaV1
69
+ type RpcSchemas = {
70
+ input?: StandardSchemaV1
71
+ output?: StandardSchemaV1
72
+ files?: StandardSchemaV1
73
+ }
74
+
75
+ /*
76
+ Options every rpc overload accepts: the `schemas` namespace, the `clients` surface
77
+ flags (browser/mcp/cli), the same-origin
78
+ CSRF exemption (`crossOrigin`), the pre-parse body-byte ceiling
79
+ (`maxBodySize`), and the per-surface handler `timeout` (ms). Read helpers add the
80
+ endpoint `cache`/`stream` policy (ADR-0020); mutating helpers add neither — a write has
81
+ no replayable value to retain or stream. The kind-scoping is what makes `POST(fn, { cache })`
82
+ a compile error rather than a silent no-op. The single canonical source both `defineRpc`
83
+ and `RpcRegistryEntry` project from.
84
+ */
85
+ type RpcSharedOpts = {
86
+ schemas?: RpcSchemas
71
87
  clients?: Partial<ClientFlags>
72
88
  crossOrigin?: boolean
73
89
  maxBodySize?: number
@@ -75,63 +91,38 @@ type RpcBaseOpts = {
75
91
  }
76
92
 
77
93
  /*
78
- Mutating-helper options: the shared base plus durable delivery. `outbox` lives
79
- here, not on RpcBaseOpts, because a read RPC has nothing to durably deliver —
80
- so `GET(fn, { outbox: true })` is a compile error, not the runtime throw it used
81
- to be. Keeps the type surface honest with the defineRpc guard.
94
+ Read-helper (GET/HEAD) options: the shared base plus the endpoint `cache` policy
95
+ (`ttl`/`tags`/`throttle`/`debounce`/`shared`) and `stream` policy (`n`, replay depth).
96
+ Generic over `Args` so `cache.tags`'s `(args) => string[]` form is typed against the
97
+ rpc's own argument shape.
82
98
  */
83
- type MutatingRpcOpts = RpcBaseOpts & {
84
- /* Durable delivery: on an unreachable server the call still throws, and the request is
85
- parked for replay. Drains on `rpc.outbox.retry()` — no auto-drain. The call shape is
86
- unchanged — `rpc.outbox` exposes the queue. */
87
- outbox?: boolean
99
+ type RpcReadOpts<Args> = RpcSharedOpts & {
100
+ cache?: CachePolicy<Args>
101
+ stream?: StreamPolicy
88
102
  }
89
103
 
90
104
  /*
91
- Shared signature for every rpc helper (GET / POST / …). The handler's return
92
- type is inferred whole (`Awaited<ReturnType<F>>`), then split: `SuccessBody`
93
- becomes the caller's `Return`, `InferredErrors` becomes the rpc's `Errors`
94
- (driving `isError`). Typed errors are raised by returning an
95
- `error.typed(name, status, schema?)` constructor there is no `errors:` opt.
96
- Four overloads by argument source:
105
+ The read helpers (GET/HEAD). The handler's return type is inferred whole
106
+ (`Awaited<ReturnType<F>>`), then split: `SuccessBody` becomes the caller's `Return`,
107
+ `InferredErrors` becomes the rpc's `Errors` (driving `isError`). Typed errors are raised
108
+ by returning an `error.typed(name, status, schema?)` constructor there is no `errors:`
109
+ opt. Four overloads by argument source (most-specific first):
97
110
 
98
- - `Rpc(fn, { inputSchema, outputSchema?, clients? })` — `Args` infers
99
- from `InferInput<InputSchema>`, the handler receives
100
- `InferOutput<InputSchema>`. `outputSchema` is an optional Standard Schema
101
- for the success body it feeds the OpenAPI 200 response and the MCP tool
102
- `outputSchema`. JSON Schema is projected from each schema's own
103
- `toJSONSchema()` (wrap with withJsonSchema if the library lacks one).
104
- `clients` controls which surfaces (browser / mcp / cli) expose this rpc.
105
- `crossOrigin: true` exempts a mutating rpc from the router's same-origin
106
- CSRF gateby default a browser request whose Origin doesn't match the
107
- app's own host is refused with 403 on every non-GET/HEAD rpc.
108
- `maxBodySize` caps the body's actual received bytes (413 past it),
109
- enforced before parsing; omitted, the only ceiling is Bun.serve's
110
- server-wide maxRequestBodySize. `timeout` (ms) bounds the handler's
111
- execution on every surface (SSR / MCP / CLI / network) — a 504 once
112
- exceeded; on the network path it also aborts request().signal so a
113
- handler's `fetch(ext, { signal: request().signal })` is cancelled, not
114
- just abandoned.
115
- - `Rpc(fn, { clients })` — schemaless but with explicit client
116
- targeting (e.g. server-internal RPC with `clients: { browser: false }`).
117
- - `Rpc(fn)` — bare handler, a single `F extends RpcFn` generic. Everything
118
- is read off the handler: `Args` from its parameter (annotate it —
119
- `POST((a: { id: string }) => …)` — or leave it nullary for `undefined`),
120
- `Return` from the `TypedResponse<T>` brand on
121
- `json`/`error`/`redirect`/`jsonl`/`sse`, `Errors` from any
122
- `error.typed(...)` branches. You never pass `<Args, Return>` generics; a
123
- stray one is a loud constraint error, not a silent `unknown` body.
111
+ - `GET(fn, { schemas: { input, files, output? }, cache?, … })` — multipart upload.
112
+ The handler receives the text fields (`InferOutput<input>`) intersected with the
113
+ validated File parts (`InferOutput<files>`); the call site sends a FormData. `files`
114
+ stays off the JSON-Schema projection (a File has no honest conversion) only `input`
115
+ feeds MCP/CLI/OpenAPI. `Args` (the RemoteFunction's call type) is `InferInput<input>`.
116
+ - `GET(fn, { schemas: { input, output? }, cache?, … })` — `Args` = `InferInput<input>`,
117
+ the handler receives `InferOutput<input>`. `output` feeds the OpenAPI 200 / MCP tool
118
+ outputSchema and never drives arg inference.
119
+ - `GET(fn, opts)` schemaless-with-opts (includes `schemas: { output }`-only): args
120
+ read off the handler `F`. `cache`/`clients`/`timeout`/… live here.
121
+ - `GET(fn)` bare handler; everything reads off `F`: `Args` from its parameter, `Return`
122
+ from the `TypedResponse<T>` brand, `Errors` from any `error.typed(...)` branches. You
123
+ never pass `<Args, Return>` generics; a stray one is a loud constraint error.
124
124
  */
125
- type RpcHelperOf<Opts> = {
126
- /*
127
- `Rpc(fn, { inputSchema, filesSchema, … })` — multipart upload. The
128
- handler receives the text fields (`InferOutput<InputSchema>`) intersected
129
- with the validated File parts (`InferOutput<FilesSchema>`); both are merged
130
- into one args bag. The call site sends a FormData (RemoteFunction's call
131
- accepts it), since a File can't ride a JSON body. filesSchema stays off the
132
- JSON-Schema projection — a File has no honest conversion (see
133
- jsonSchemaForSchema) — so only inputSchema feeds MCP/CLI/OpenAPI.
134
- */
125
+ export type RpcHelper = {
135
126
  <
136
127
  R extends Response,
137
128
  InputSchema extends StandardSchemaV1 = StandardSchemaV1,
@@ -141,30 +132,28 @@ type RpcHelperOf<Opts> = {
141
132
  args: StandardSchemaV1.InferOutput<InputSchema> &
142
133
  StandardSchemaV1.InferOutput<FilesSchema>,
143
134
  ) => R | Promise<R>,
144
- opts: Opts & {
145
- inputSchema: InputSchema
146
- filesSchema: FilesSchema
135
+ opts: RpcReadOpts<StandardSchemaV1.InferInput<InputSchema>> & {
136
+ schemas: { input: InputSchema; files: FilesSchema; output?: StandardSchemaV1 }
147
137
  },
148
138
  ): RemoteFunction<StandardSchemaV1.InferInput<InputSchema>, SuccessBody<R>, InferredErrors<R>>
149
139
  <R extends Response, InputSchema extends StandardSchemaV1 = StandardSchemaV1>(
150
140
  fn: (args: StandardSchemaV1.InferOutput<InputSchema>) => R | Promise<R>,
151
- opts: Opts & { inputSchema: InputSchema },
141
+ opts: RpcReadOpts<StandardSchemaV1.InferInput<InputSchema>> & {
142
+ schemas: { input: InputSchema; output?: StandardSchemaV1 }
143
+ },
152
144
  ): RemoteFunction<StandardSchemaV1.InferInput<InputSchema>, SuccessBody<R>, InferredErrors<R>>
153
- <F extends RpcFn>(fn: F, opts: Opts): RpcOf<F>
145
+ <F extends RpcFn>(fn: F, opts: RpcReadOpts<RpcArgs<F>>): RpcOf<F>
154
146
  <F extends RpcFn>(fn: F): RpcOf<F>
155
147
  }
156
148
 
157
- /* The read helpers (GET/HEAD): no `outbox` — a read has nothing to durably deliver. */
158
- export type RpcHelper = RpcHelperOf<RpcBaseOpts>
159
-
160
149
  /*
161
- Durable-call overloads: an `outbox: true` opt returns a RemoteFunction whose `Durable` bit
162
- is set, so `rpc.outbox` is the queue face rather than optionally-undefined. Mirrors the
163
- opts-bearing base overloads (multipart-upload, schema'd, schemaless); the bare `Rpc(fn)`
164
- form has no opts to carry `outbox`, so it stays non-durable. Intersected AHEAD of the base
165
- set in MutatingRpcHelper so an `outbox: true` literal resolves here first.
150
+ The mutating helpers (POST/PUT/PATCH/DELETE). The base opts is the shared `RpcSharedOpts`
151
+ (no `cache`/`stream`) — coalesce-only is the method default and a write has no replayable
152
+ value to retain or stream which is what makes `cache`/`stream` a compile error here while
153
+ they stay legal on the read helpers (`RpcReadOpts`) the kind-scoping. Overloads mirror the read helpers by argument source
154
+ (multipart-upload, schema'd, schemaless-with-opts, bare).
166
155
  */
167
- type DurableMutatingRpcHelper = {
156
+ export type MutatingRpcHelper = {
168
157
  <
169
158
  R extends Response,
170
159
  InputSchema extends StandardSchemaV1 = StandardSchemaV1,
@@ -174,35 +163,16 @@ type DurableMutatingRpcHelper = {
174
163
  args: StandardSchemaV1.InferOutput<InputSchema> &
175
164
  StandardSchemaV1.InferOutput<FilesSchema>,
176
165
  ) => R | Promise<R>,
177
- opts: MutatingRpcOpts & {
178
- inputSchema: InputSchema
179
- filesSchema: FilesSchema
180
- outbox: true
166
+ opts: RpcSharedOpts & {
167
+ schemas: { input: InputSchema; files: FilesSchema; output?: StandardSchemaV1 }
181
168
  },
182
- ): RemoteFunction<
183
- StandardSchemaV1.InferInput<InputSchema>,
184
- SuccessBody<R>,
185
- InferredErrors<R>,
186
- true
187
- >
169
+ ): RemoteFunction<StandardSchemaV1.InferInput<InputSchema>, SuccessBody<R>, InferredErrors<R>>
188
170
  <R extends Response, InputSchema extends StandardSchemaV1 = StandardSchemaV1>(
189
171
  fn: (args: StandardSchemaV1.InferOutput<InputSchema>) => R | Promise<R>,
190
- opts: MutatingRpcOpts & { inputSchema: InputSchema; outbox: true },
191
- ): RemoteFunction<
192
- StandardSchemaV1.InferInput<InputSchema>,
193
- SuccessBody<R>,
194
- InferredErrors<R>,
195
- true
196
- >
197
- <F extends RpcFn>(fn: F, opts: MutatingRpcOpts & { outbox: true }): RpcOf<F, true>
172
+ opts: RpcSharedOpts & {
173
+ schemas: { input: InputSchema; output?: StandardSchemaV1 }
174
+ },
175
+ ): RemoteFunction<StandardSchemaV1.InferInput<InputSchema>, SuccessBody<R>, InferredErrors<R>>
176
+ <F extends RpcFn>(fn: F, opts: RpcSharedOpts): RpcOf<F>
177
+ <F extends RpcFn>(fn: F): RpcOf<F>
198
178
  }
199
-
200
- /*
201
- The mutating helpers (POST/PUT/PATCH/DELETE). A durable (`outbox`) call is a normal
202
- RemoteFunction — it throws exactly like a non-durable one and only parks the request as a
203
- side-effect on an unreachable server — so there is no separate return shape; `outbox` rides
204
- MutatingRpcOpts and `rpc.outbox` exposes the queue. The distinct opts base is what makes
205
- `outbox` legal here and a compile error on the read helpers; the durable overloads then set
206
- the return type's `Durable` bit so `rpc.outbox` is present without an optional chain.
207
- */
208
- export type MutatingRpcHelper = DurableMutatingRpcHelper & RpcHelperOf<MutatingRpcOpts>
@@ -1,13 +1,14 @@
1
- import type { ClientFlags } from '../../../shared/types/ClientFlags.ts'
1
+ import type { ErrorJsonSchemas } from '../../../shared/types/ErrorJsonSchemas.ts'
2
2
  import type { RemoteFunction } from '../../../shared/types/RemoteFunction.ts'
3
3
  import type { StandardSchemaV1 } from '../../../shared/types/StandardSchemaV1.ts'
4
4
 
5
5
  /*
6
6
  Per-rpc registry record on the server side. MCP and CLI enumerate this
7
- to discover which RPCs are advertised (clients flags) and what shapes
8
- they expect/return. The schemas and resolved clients stay off the public
9
- RemoteFunction shape so the browser-side proxy doesn't need to carry
10
- server-only state.
7
+ to discover what shapes an rpc expects/returns. The schemas stay off the
8
+ public RemoteFunction shape so the browser-side proxy doesn't need to carry
9
+ server-only state; the resolved `clients` and `crossOrigin` ride on
10
+ `entry.remote` (ADR-0020 sweep finding #4 — no duplicate fields), so readers
11
+ reach them via `entry.remote.clients` / `entry.remote.crossOrigin`.
11
12
 
12
13
  `inputSchema` validates the argument bag and feeds the MCP tool
13
14
  `inputSchema` / OpenAPI parameters; `outputSchema` describes the success
@@ -23,13 +24,27 @@ multipart body advertises the file parts generically as binary.
23
24
  export type RpcRegistryEntry = {
24
25
  remote: RemoteFunction<unknown, unknown>
25
26
  inputSchema: StandardSchemaV1 | undefined
27
+ /* The handler's input args projected to JSON Schema at build time (ADR-0030 input side) — the
28
+ input surface's fallback when no `inputSchema` VALIDATOR is declared, mirroring how
29
+ `outputJsonSchema` backs the output surface. Already JSON Schema (not a validator), so surfaces
30
+ use it verbatim; `inputSchema`, when present, overrides it. It also makes the endpoint
31
+ advertisable (MCP/CLI) — the same role a declared `inputSchema` plays — but purely as a SHAPE
32
+ description: it is never run as runtime validation, so it can't produce a 422. */
33
+ inputJsonSchema: Record<string, unknown> | undefined
26
34
  outputSchema: StandardSchemaV1 | undefined
35
+ /* The handler's return type projected to JSON Schema at build time (ADR-0030 D2) — the output
36
+ surface's fallback when no `outputSchema` VALIDATOR is declared. Already JSON Schema (not a
37
+ validator), so surfaces use it verbatim; `outputSchema`, when present, overrides it. */
38
+ outputJsonSchema: Record<string, unknown> | undefined
39
+ /* The handler's typed-error branches projected to a status-keyed JSON-Schema map at build time
40
+ (ADR-0030) — each `error.typed(...)` return branch's status + `data` payload. Feeds the OpenAPI
41
+ `responses[status]` entries alongside the 200; undefined when the handler declares no typed
42
+ errors or the build couldn't resolve them, so the surface omits the error responses. */
43
+ errorJsonSchemas: ErrorJsonSchemas | undefined
27
44
  filesSchema: StandardSchemaV1 | undefined
28
- clients: ClientFlags
29
45
  /* The rpc's declared opts, recorded so introspection (inspector) can report
30
- the deadline/body-cap/CSRF-exemption a handler runs under. Undefined = the
31
- framework default (no deadline, Bun's server-wide body ceiling, gated). */
46
+ the deadline/body-cap a handler runs under. Undefined = the framework default
47
+ (no deadline, Bun's server-wide body ceiling). */
32
48
  timeout: number | undefined
33
49
  maxBodySize: number | undefined
34
- crossOrigin: boolean | undefined
35
50
  }
@@ -1,7 +1,7 @@
1
+ import { fieldErrorsFromIssues } from '../../shared/fieldErrorsFromIssues.ts'
1
2
  import type { StandardSchemaV1 } from '../../shared/types/StandardSchemaV1.ts'
2
3
  import type { ValidationErrorData } from '../../shared/types/ValidationErrorData.ts'
3
4
  import { typedErrorResponse } from '../runtime/typedErrorResponse.ts'
4
- import { fieldErrorsFromIssues } from './fieldErrorsFromIssues.ts'
5
5
 
6
6
  /*
7
7
  The framework-reserved `validation` typed error a 422 carries: the raw Standard
@@ -21,11 +21,9 @@ slept still reloads it. The initial connect runs even when the page loads
21
21
  hidden (the baseline must be the serving worker's stamp, captured before a swap
22
22
  can replace it) and releases itself once that first event lands.
23
23
  */
24
- import { DEV_HOT_PREFIX } from '../../shared/DEV_HOT_PREFIX.ts'
25
24
  import { DEV_RELOAD_PATH } from '../../shared/DEV_RELOAD_PATH.ts'
26
25
 
27
26
  export const DEV_RELOAD_CLIENT_SCRIPT = `<script>
28
- window.__abideDev = true;
29
27
  ;(() => {
30
28
  let stamp
31
29
  let source
@@ -48,18 +46,6 @@ window.__abideDev = true;
48
46
  }
49
47
  }
50
48
  }
51
- function swapComponents(next, prev) {
52
- const components = next.components || {}
53
- const before = prev.components || {}
54
- for (const id in components) {
55
- if (components[id] !== before[id]) {
56
- // The hot module sources its runtime from window.__abide and self-invokes
57
- // hotReplace; a load/compile failure or a component with no live instance
58
- // falls back to a reload.
59
- import('${DEV_HOT_PREFIX}' + id + '?v=' + components[id]).catch(() => location.reload())
60
- }
61
- }
62
- }
63
49
  function connect() {
64
50
  if (source) {
65
51
  return
@@ -84,7 +70,6 @@ window.__abideDev = true;
84
70
  if (next.cssHref && next.cssHref !== stamp.cssHref) {
85
71
  swapCss(next.cssHref)
86
72
  }
87
- swapComponents(next, stamp)
88
73
  stamp = next
89
74
  }
90
75
  source.onerror = () => {
@@ -18,20 +18,20 @@ function preview(value: unknown): string | undefined {
18
18
  }
19
19
  }
20
20
 
21
- /* The entry's armed swr policy as a label, if it declared one. A bare `swr: true`
22
- has no window, so it labels as plain `swr`. */
21
+ /* The entry's armed refetch (stale-while-revalidate) policy as a label, if it declared
22
+ one. A windowless policy refetches immediately, so it labels as plain `refetch`. */
23
23
  function policyLabel(entry: CacheEntry): string | undefined {
24
24
  const policy = entry.invalidation
25
25
  if (!policy) {
26
26
  return undefined
27
27
  }
28
28
  if (policy.debounce !== undefined) {
29
- return `swr debounce ${policy.debounce}ms`
29
+ return `refetch debounce ${policy.debounce}ms`
30
30
  }
31
31
  if (policy.throttle !== undefined) {
32
- return `swr throttle ${policy.throttle}ms`
32
+ return `refetch throttle ${policy.throttle}ms`
33
33
  }
34
- return 'swr'
34
+ return 'refetch'
35
35
  }
36
36
 
37
37
  function projectEntry(entry: CacheEntry, now: number): InspectorCacheEntry {
@@ -48,10 +48,11 @@ function projectEntry(entry: CacheEntry, now: number): InspectorCacheEntry {
48
48
  }
49
49
 
50
50
  /*
51
- Snapshots the process-level cache store (the persistent one `cache(fn, { shared:
52
- true, ttl: Infinity })` writes to) for the inspector. Read at call time, so it
53
- reflects the store as it stands; request-scoped stores are deliberately
54
- excluded — they're ephemeral and already visible as per-request cache tallies.
51
+ Snapshots the process-level cache store (the persistent one an endpoint declaring
52
+ `cache: { shared: true, ttl }` or a producer `cache(fn, args, { shared: true, ttl })`
53
+ writes to) for the inspector. Read at call time, so it reflects the store as it
54
+ stands; request-scoped stores are deliberately excluded — they're ephemeral and
55
+ already visible as per-request cache tallies.
55
56
  */
56
57
  export function buildCacheSnapshot(): InspectorCacheSnapshot {
57
58
  const now = Date.now()
@@ -1,5 +1,5 @@
1
- import { jsonSchemaForSchema } from '../../shared/jsonSchemaForSchema.ts'
2
1
  import { promptRegistry } from '../prompts/promptRegistry.ts'
2
+ import { resolveInputJsonSchema, resolveOutputJsonSchema } from '../rpc/resolveRpcJsonSchema.ts'
3
3
  import { rpcRegistry } from '../rpc/rpcRegistry.ts'
4
4
  import { socketOperations } from '../sockets/socketOperations.ts'
5
5
  import { socketRegistry } from '../sockets/socketRegistry.ts'
@@ -18,12 +18,14 @@ export function buildInspectorSurface(): InspectorSurface {
18
18
  url: entry.remote.url,
19
19
  method: entry.remote.method,
20
20
  clients: { ...entry.remote.clients },
21
- inputSchema: entry.inputSchema ? jsonSchemaForSchema(entry.inputSchema) : undefined,
22
- outputSchema: entry.outputSchema ? jsonSchemaForSchema(entry.outputSchema) : undefined,
21
+ // The declared inputSchema VALIDATOR wins; else the build-projected input-type schema
22
+ // (ADR-0030 input side); with neither the rpc advertises no input shape, as before.
23
+ inputSchema: resolveInputJsonSchema(entry),
24
+ outputSchema: resolveOutputJsonSchema(entry),
23
25
  files: entry.filesSchema !== undefined,
24
26
  timeout: entry.timeout,
25
27
  maxBodySize: entry.maxBodySize,
26
- crossOrigin: entry.crossOrigin,
28
+ crossOrigin: entry.remote.crossOrigin,
27
29
  }))
28
30
  const sockets = Array.from(socketRegistry.values()).map((entry) => ({
29
31
  name: entry.socket.name,