@abide/abide 0.48.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 (360) hide show
  1. package/AGENTS.md +85 -59
  2. package/CHANGELOG.md +216 -0
  3. package/README.md +21 -10
  4. package/package.json +15 -4
  5. package/src/abideLsp.ts +5 -6
  6. package/src/abideResolverPlugin.ts +197 -169
  7. package/src/build.ts +79 -56
  8. package/src/buildCli.ts +3 -5
  9. package/src/buildDisconnected.ts +2 -5
  10. package/src/bundleApp.ts +2 -3
  11. package/src/checkAbide.ts +12 -2
  12. package/src/compile.ts +2 -4
  13. package/src/devEntry.ts +113 -28
  14. package/src/discoveryEntry.ts +3 -2
  15. package/src/lib/bundle/disconnected.abide +69 -73
  16. package/src/lib/bundle/infoPlist.ts +13 -7
  17. package/src/lib/bundle/installDownloads.ts +13 -3
  18. package/src/lib/bundle/installMacMenu.ts +13 -3
  19. package/src/lib/bundle/spawnEmbeddedServer.ts +13 -4
  20. package/src/lib/bundle/waitForServer.ts +6 -1
  21. package/src/lib/cli/parseArgvForRpc.ts +36 -9
  22. package/src/lib/cli/tokenizeArgvFlags.ts +4 -2
  23. package/src/lib/mcp/createMcpResourceServer.ts +13 -2
  24. package/src/lib/mcp/mcpTools.ts +15 -6
  25. package/src/lib/mcp/toolResultFromResponse.ts +40 -12
  26. package/src/lib/server/error.ts +6 -6
  27. package/src/lib/server/json.ts +17 -3
  28. package/src/lib/server/rpc/defineRpc.ts +58 -24
  29. package/src/lib/server/rpc/parseArgs.ts +113 -14
  30. package/src/lib/server/rpc/resolveRpcJsonSchema.ts +28 -0
  31. package/src/lib/server/rpc/runWithRpcTimeout.ts +12 -1
  32. package/src/lib/server/rpc/types/RpcHelper.ts +75 -105
  33. package/src/lib/server/rpc/types/RpcRegistryEntry.ts +24 -9
  34. package/src/lib/server/rpc/validationError.ts +1 -1
  35. package/src/lib/server/runtime/DEV_RELOAD_CLIENT_SCRIPT.ts +0 -15
  36. package/src/lib/server/runtime/buildCacheSnapshot.ts +10 -9
  37. package/src/lib/server/runtime/buildInspectorSurface.ts +6 -4
  38. package/src/lib/server/runtime/buildOpenApiSpec.ts +55 -11
  39. package/src/lib/server/runtime/buildPreloadManifest.ts +12 -10
  40. package/src/lib/server/runtime/createAppAssetServer.ts +18 -15
  41. package/src/lib/server/runtime/createAppRouteResolver.ts +145 -0
  42. package/src/lib/server/runtime/createPlumbingRouter.ts +212 -0
  43. package/src/lib/server/runtime/createRouteDispatcher.ts +4 -10
  44. package/src/lib/server/runtime/createServer.ts +120 -324
  45. package/src/lib/server/runtime/createUiPageRenderer.ts +137 -26
  46. package/src/lib/server/runtime/devClientFingerprint.ts +19 -34
  47. package/src/lib/server/runtime/finalizeResponse.ts +11 -1
  48. package/src/lib/server/runtime/installAmbientScopeStore.ts +30 -9
  49. package/src/lib/server/runtime/logExposedSurfaces.ts +8 -7
  50. package/src/lib/server/runtime/pathStore.ts +15 -0
  51. package/src/lib/server/runtime/registryManifests.ts +1 -1
  52. package/src/lib/server/runtime/renderCellBarrierStore.ts +15 -0
  53. package/src/lib/server/runtime/runWithRequestScope.ts +3 -0
  54. package/src/lib/server/runtime/serializeCacheSnapshot.ts +6 -4
  55. package/src/lib/server/runtime/snapshotEntryFromCache.ts +24 -16
  56. package/src/lib/server/runtime/streamCacheResolutions.ts +14 -7
  57. package/src/lib/server/runtime/streamFromIterator.ts +30 -2
  58. package/src/lib/server/runtime/textResponse.ts +23 -0
  59. package/src/lib/server/runtime/types/DevReloadStamp.ts +6 -10
  60. package/src/lib/server/runtime/types/InspectorCacheEntry.ts +1 -1
  61. package/src/lib/server/runtime/types/RequestStore.ts +27 -0
  62. package/src/lib/server/runtime/warnUnguardedMcp.ts +15 -5
  63. package/src/lib/server/sockets/createSocketDispatcher.ts +9 -11
  64. package/src/lib/server/sockets/defineSocket.ts +5 -4
  65. package/src/lib/server/sse.ts +5 -1
  66. package/src/lib/shared/ASYNC_CELL.ts +5 -0
  67. package/src/lib/shared/DEV_REBUILD_PATH.ts +6 -0
  68. package/src/lib/shared/HTTP_METHODS.ts +6 -0
  69. package/src/lib/shared/HttpError.ts +1 -5
  70. package/src/lib/shared/MCP_PATH.ts +6 -0
  71. package/src/lib/shared/PROXIED_SERVER_SUBDIRS.ts +15 -0
  72. package/src/lib/shared/REF_JSON_TAGS.ts +13 -1
  73. package/src/lib/shared/RPC_SHIM_GLOBALS.ts +9 -0
  74. package/src/lib/shared/activeCacheStore.ts +1 -0
  75. package/src/lib/shared/activePage.ts +1 -0
  76. package/src/lib/shared/buildArtifact.ts +20 -0
  77. package/src/lib/shared/buildRpcRequest.ts +6 -2
  78. package/src/lib/shared/buildSocketOverChannel.ts +4 -3
  79. package/src/lib/shared/bundleGraphFromMetafile.ts +68 -0
  80. package/src/lib/shared/cache.ts +156 -131
  81. package/src/lib/shared/canonicalJson.ts +24 -1
  82. package/src/lib/shared/changeAffectsClient.ts +12 -7
  83. package/src/lib/shared/createCacheStore.ts +31 -8
  84. package/src/lib/shared/createChannelLog.ts +24 -0
  85. package/src/lib/shared/createLifecycleChannel.ts +7 -1
  86. package/src/lib/shared/createReachable.ts +47 -78
  87. package/src/lib/shared/createRemoteFunction.ts +52 -30
  88. package/src/lib/shared/createRpcServerProgram.ts +820 -0
  89. package/src/lib/shared/decodeRefJson.ts +4 -4
  90. package/src/lib/shared/decodeResponse.ts +2 -2
  91. package/src/lib/shared/decodeWireBody.ts +35 -0
  92. package/src/lib/shared/detectRpcMethod.ts +8 -1
  93. package/src/lib/shared/done.ts +8 -2
  94. package/src/lib/shared/encodeRefJson.ts +4 -4
  95. package/src/lib/shared/encodeWireBody.ts +18 -0
  96. package/src/lib/shared/exitOnBuildFailure.ts +4 -3
  97. package/src/lib/{server/rpc → shared}/fieldErrorsFromIssues.ts +5 -1
  98. package/src/lib/shared/generateDeclarations.ts +72 -0
  99. package/src/lib/shared/hasSeedableRequest.ts +25 -0
  100. package/src/lib/shared/hydrationWindow.ts +53 -0
  101. package/src/lib/shared/isAsyncCell.ts +11 -0
  102. package/src/lib/shared/isAsyncIterable.ts +8 -0
  103. package/src/lib/shared/isSubscribable.ts +3 -3
  104. package/src/lib/shared/isThenable.ts +9 -0
  105. package/src/lib/shared/jsonSchemaForType.ts +258 -0
  106. package/src/lib/shared/lenientDecode.ts +15 -0
  107. package/src/lib/shared/loadProjectTsConfig.ts +28 -0
  108. package/src/lib/shared/markFrameworkSourcesIgnored.ts +1 -1
  109. package/src/lib/shared/matchRoute.ts +4 -14
  110. package/src/lib/shared/parseEnv.ts +17 -6
  111. package/src/lib/shared/peek.ts +14 -0
  112. package/src/lib/shared/pending.ts +9 -6
  113. package/src/lib/shared/pendingAsyncCellsSlot.ts +13 -0
  114. package/src/lib/shared/prepareRpcModule.ts +91 -93
  115. package/src/lib/shared/prepareSocketModule.ts +3 -2
  116. package/src/lib/shared/probeRegistries.ts +5 -18
  117. package/src/lib/shared/reachable.ts +7 -10
  118. package/src/lib/shared/refresh.ts +12 -2
  119. package/src/lib/shared/refreshing.ts +8 -2
  120. package/src/lib/shared/resolvedCellsSlot.ts +13 -0
  121. package/src/lib/shared/reviveWireField.ts +49 -0
  122. package/src/lib/shared/reviveWireOutput.ts +36 -0
  123. package/src/lib/shared/rpcServerForRoot.ts +25 -0
  124. package/src/lib/shared/scanPages.ts +25 -0
  125. package/src/lib/shared/serializeEnv.ts +18 -6
  126. package/src/lib/shared/snapshotShippable.ts +8 -7
  127. package/src/lib/shared/snippet.ts +11 -6
  128. package/src/lib/shared/streamedCellsSlot.ts +14 -0
  129. package/src/lib/shared/subscribableFromResponse.ts +4 -4
  130. package/src/lib/shared/subscribableProbes.ts +1 -1
  131. package/src/lib/shared/tailProbeSlot.ts +1 -1
  132. package/src/lib/shared/types/AsyncComputed.ts +20 -0
  133. package/src/lib/shared/types/AsyncState.ts +13 -0
  134. package/src/lib/shared/types/CacheEntry.ts +7 -7
  135. package/src/lib/shared/types/CacheOptions.ts +23 -37
  136. package/src/lib/shared/types/CachePolicy.ts +25 -0
  137. package/src/lib/shared/types/CacheSnapshotEntry.ts +6 -5
  138. package/src/lib/shared/types/ErrorJsonSchemas.ts +8 -0
  139. package/src/lib/shared/types/HttpMethod.ts +3 -1
  140. package/src/lib/shared/types/InputCoercion.ts +17 -0
  141. package/src/lib/shared/types/{Subscribable.ts → NamedAsyncIterable.ts} +1 -1
  142. package/src/lib/shared/types/OutputWirePlan.ts +17 -0
  143. package/src/lib/shared/types/PagesScan.ts +7 -0
  144. package/src/lib/shared/types/PendingAsyncCells.ts +10 -0
  145. package/src/lib/shared/types/RawRemoteFunction.ts +6 -0
  146. package/src/lib/shared/types/RemoteCallable.ts +8 -7
  147. package/src/lib/shared/types/RemoteFunction.ts +16 -15
  148. package/src/lib/shared/types/ResolvedCells.ts +11 -0
  149. package/src/lib/shared/types/ReturnBody.ts +15 -0
  150. package/src/lib/shared/types/RpcBuildStamps.ts +23 -0
  151. package/src/lib/shared/types/RpcErrorGuard.ts +3 -8
  152. package/src/lib/shared/types/Socket.ts +4 -4
  153. package/src/lib/shared/types/SsrPayload.ts +5 -1
  154. package/src/lib/shared/types/StreamPolicy.ts +11 -0
  155. package/src/lib/shared/types/StreamedCells.ts +19 -0
  156. package/src/lib/shared/types/StreamedResolution.ts +24 -6
  157. package/src/lib/shared/types/TailHooks.ts +1 -1
  158. package/src/lib/shared/types/WireKind.ts +11 -0
  159. package/src/lib/shared/url.ts +5 -0
  160. package/src/lib/shared/validationHttpError.ts +33 -0
  161. package/src/lib/shared/warmSeedKey.ts +16 -0
  162. package/src/lib/shared/wireJsonReplacer.ts +30 -0
  163. package/src/lib/shared/writeRpcDts.ts +11 -1
  164. package/src/lib/shared/writeTestSocketsDts.ts +1 -1
  165. package/src/lib/test/createTestApp.ts +19 -1
  166. package/src/lib/ui/README.md +1 -1
  167. package/src/lib/ui/activePendingCells.ts +14 -0
  168. package/src/lib/ui/compile/BLOCK_KEYWORDS.ts +21 -0
  169. package/src/lib/ui/compile/SSR_ESCAPE.ts +4 -1
  170. package/src/lib/ui/compile/UI_RUNTIME_IMPORTS.ts +35 -7
  171. package/src/lib/ui/compile/abideUiPlugin.ts +19 -2
  172. package/src/lib/ui/compile/analyzeComponent.ts +89 -6
  173. package/src/lib/ui/compile/assignmentTargetNames.ts +54 -0
  174. package/src/lib/ui/compile/asyncInterpolationFields.ts +152 -0
  175. package/src/lib/ui/compile/asyncValuePositionError.ts +21 -0
  176. package/src/lib/ui/compile/asyncValuePositionInterpolations.ts +64 -0
  177. package/src/lib/ui/compile/attrLiftPosition.ts +18 -0
  178. package/src/lib/ui/compile/bindListenEvent.ts +5 -6
  179. package/src/lib/ui/compile/cachedSourceFile.ts +40 -0
  180. package/src/lib/ui/compile/catchBinding.ts +7 -5
  181. package/src/lib/ui/compile/classifyInterpolationType.ts +46 -0
  182. package/src/lib/ui/compile/collectAbideDiagnostics.ts +96 -0
  183. package/src/lib/ui/compile/compileComponent.ts +19 -3
  184. package/src/lib/ui/compile/compileModule.ts +27 -58
  185. package/src/lib/ui/compile/compileSSR.ts +52 -13
  186. package/src/lib/ui/compile/compileShadow.ts +265 -42
  187. package/src/lib/ui/compile/composeProps.ts +15 -4
  188. package/src/lib/ui/compile/createShadowLanguageService.ts +116 -68
  189. package/src/lib/ui/compile/createShadowProgram.ts +35 -6
  190. package/src/lib/ui/compile/declaredNames.ts +36 -0
  191. package/src/lib/ui/compile/desugarSignals.ts +419 -37
  192. package/src/lib/ui/compile/expressionIsPrefixEvaluable.ts +19 -0
  193. package/src/lib/ui/compile/generateBuild.ts +74 -17
  194. package/src/lib/ui/compile/generateSSR.ts +286 -75
  195. package/src/lib/ui/compile/hoistableAwaits.ts +193 -0
  196. package/src/lib/ui/compile/hoistableChildRenders.ts +112 -0
  197. package/src/lib/ui/compile/interpolatedTemplateLiteral.ts +3 -1
  198. package/src/lib/ui/compile/interpolationClassifierForRoot.ts +37 -0
  199. package/src/lib/ui/compile/isSpuriousAsyncReadDiagnostic.ts +174 -0
  200. package/src/lib/ui/compile/isWhitespaceText.ts +10 -2
  201. package/src/lib/ui/compile/liftAsyncSubExpressions.ts +166 -0
  202. package/src/lib/ui/compile/lowerAsyncInterpolations.ts +92 -0
  203. package/src/lib/ui/compile/lowerContext.ts +10 -0
  204. package/src/lib/ui/compile/lowerDocAccess.ts +51 -10
  205. package/src/lib/ui/compile/lowerScript.ts +40 -4
  206. package/src/lib/ui/compile/nodeAtShadowOffset.ts +28 -0
  207. package/src/lib/ui/compile/parseTemplate.ts +18 -1090
  208. package/src/lib/ui/compile/parseTemplateRecovering.ts +1385 -0
  209. package/src/lib/ui/compile/referencedIdentifiers.ts +35 -0
  210. package/src/lib/ui/compile/renameSignalRefs.ts +26 -40
  211. package/src/lib/ui/compile/resolveReactiveExport.ts +4 -7
  212. package/src/lib/ui/compile/scopeCss.ts +27 -4
  213. package/src/lib/ui/compile/seedTypeClassifierForRoot.ts +65 -0
  214. package/src/lib/ui/compile/shadowInterpolationClassifier.ts +47 -0
  215. package/src/lib/ui/compile/sourceFileOptionsSignature.ts +20 -0
  216. package/src/lib/ui/compile/structuralHeadTokens.ts +107 -0
  217. package/src/lib/ui/compile/templateSemanticTokens.ts +21 -0
  218. package/src/lib/ui/compile/templateStartOffset.ts +15 -0
  219. package/src/lib/ui/compile/tryPlan.ts +4 -3
  220. package/src/lib/ui/compile/types/AnalyzedComponent.ts +4 -0
  221. package/src/lib/ui/compile/types/AsyncInterpolationField.ts +19 -0
  222. package/src/lib/ui/compile/types/InterpolationClassifier.ts +12 -0
  223. package/src/lib/ui/compile/types/InterpolationKind.ts +7 -0
  224. package/src/lib/ui/compile/types/ParseDiagnostic.ts +8 -0
  225. package/src/lib/ui/compile/types/SeedTypeClassifier.ts +15 -0
  226. package/src/lib/ui/compile/types/TemplateNode.ts +36 -4
  227. package/src/lib/ui/compile/types/ValuePositionInterpolation.ts +13 -0
  228. package/src/lib/ui/compile/writtenTemplateNames.ts +84 -0
  229. package/src/lib/ui/computed.ts +28 -1
  230. package/src/lib/ui/createScope.ts +35 -68
  231. package/src/lib/ui/dom/anchoredBranch.ts +142 -0
  232. package/src/lib/ui/dom/appendText.ts +8 -3
  233. package/src/lib/ui/dom/awaitBlock.ts +62 -75
  234. package/src/lib/ui/dom/bindProp.ts +22 -0
  235. package/src/lib/ui/dom/bindableProp.ts +47 -0
  236. package/src/lib/ui/dom/cellPending.ts +24 -0
  237. package/src/lib/ui/dom/discardBoundary.ts +24 -6
  238. package/src/lib/ui/dom/disposeRange.ts +2 -1
  239. package/src/lib/ui/dom/each.ts +41 -12
  240. package/src/lib/ui/dom/eachAsync.ts +39 -3
  241. package/src/lib/ui/dom/fillBefore.ts +6 -7
  242. package/src/lib/ui/dom/fillBoundary.ts +2 -3
  243. package/src/lib/ui/dom/fillRange.ts +4 -4
  244. package/src/lib/ui/dom/hydrate.ts +6 -12
  245. package/src/lib/ui/dom/isComment.ts +1 -1
  246. package/src/lib/ui/dom/matchingRangeClose.ts +29 -0
  247. package/src/lib/ui/dom/mergeProps.ts +1 -1
  248. package/src/lib/ui/dom/mount.ts +1 -2
  249. package/src/lib/ui/dom/mountChild.ts +9 -40
  250. package/src/lib/ui/dom/mountRange.ts +2 -3
  251. package/src/lib/ui/dom/mountSlot.ts +1 -1
  252. package/src/lib/ui/dom/mountStreamedChild.ts +84 -0
  253. package/src/lib/ui/dom/mountSwappableRange.ts +46 -4
  254. package/src/lib/ui/dom/mutateDocContainer.ts +65 -0
  255. package/src/lib/ui/dom/on.ts +2 -2
  256. package/src/lib/ui/dom/readCell.ts +40 -0
  257. package/src/lib/ui/dom/restProps.ts +2 -2
  258. package/src/lib/ui/dom/spreadProps.ts +3 -4
  259. package/src/lib/ui/dom/switchBlock.ts +30 -7
  260. package/src/lib/ui/dom/tryBlock.ts +233 -70
  261. package/src/lib/ui/dom/types/SwitchCase.ts +5 -1
  262. package/src/lib/ui/dom/when.ts +12 -2
  263. package/src/lib/ui/dom/withScope.ts +9 -2
  264. package/src/lib/ui/finalizeStreamedChildren.ts +89 -0
  265. package/src/lib/ui/flight.ts +56 -0
  266. package/src/lib/ui/html.ts +12 -1
  267. package/src/lib/ui/isolateCellBarrier.ts +17 -0
  268. package/src/lib/ui/linked.ts +48 -2
  269. package/src/lib/ui/props.ts +17 -0
  270. package/src/lib/ui/remoteProxy.ts +84 -159
  271. package/src/lib/ui/renderChain.ts +53 -13
  272. package/src/lib/ui/renderToStream.ts +22 -18
  273. package/src/lib/ui/resumeSeedScript.ts +1 -1
  274. package/src/lib/ui/router.ts +86 -53
  275. package/src/lib/ui/runtime/AsyncCellError.ts +20 -0
  276. package/src/lib/ui/runtime/CELL_SEED.ts +14 -0
  277. package/src/lib/ui/runtime/CHILD_PRESENT.ts +2 -2
  278. package/src/lib/ui/runtime/CURRENT_BOUNDARY.ts +11 -0
  279. package/src/lib/ui/runtime/CURRENT_PATH.ts +29 -0
  280. package/src/lib/ui/runtime/RENDER.ts +10 -7
  281. package/src/lib/ui/runtime/RESUME.ts +4 -4
  282. package/src/lib/ui/runtime/STREAMED_CELLS.ts +57 -0
  283. package/src/lib/ui/runtime/ambientPathBacking.ts +32 -0
  284. package/src/lib/ui/runtime/applyPatchToTree.ts +17 -4
  285. package/src/lib/ui/runtime/blockId.ts +31 -0
  286. package/src/lib/ui/runtime/boundaryFor.ts +11 -0
  287. package/src/lib/ui/runtime/cellBarrierBacking.ts +30 -0
  288. package/src/lib/ui/runtime/createAsyncCell.ts +300 -0
  289. package/src/lib/ui/runtime/createDoc.ts +31 -54
  290. package/src/lib/ui/runtime/createEffectNode.ts +9 -0
  291. package/src/lib/ui/runtime/enterRenderPass.ts +5 -4
  292. package/src/lib/ui/runtime/flushEffects.ts +33 -1
  293. package/src/lib/ui/runtime/isAsyncFunction.ts +14 -0
  294. package/src/lib/ui/runtime/nextBlockId.ts +8 -7
  295. package/src/lib/ui/runtime/renderPath.ts +16 -0
  296. package/src/lib/ui/runtime/scope.ts +11 -0
  297. package/src/lib/ui/runtime/toTeardown.ts +10 -3
  298. package/src/lib/ui/runtime/types/Boundary.ts +9 -0
  299. package/src/lib/ui/runtime/types/RenderContext.ts +9 -7
  300. package/src/lib/ui/runtime/types/SsrRender.ts +9 -2
  301. package/src/lib/ui/runtime/types/UiComponent.ts +1 -5
  302. package/src/lib/ui/runtime/types/UiProps.ts +6 -5
  303. package/src/lib/ui/runtime/withOptionalPath.ts +8 -0
  304. package/src/lib/ui/runtime/withPath.ts +16 -0
  305. package/src/lib/ui/runtime/withPathFrom.ts +20 -0
  306. package/src/lib/ui/runtime/withoutHydration.ts +22 -0
  307. package/src/lib/ui/seedStreamedResolution.ts +31 -6
  308. package/src/lib/ui/settleAsyncCells.ts +24 -0
  309. package/src/lib/ui/socketProxy.ts +1 -1
  310. package/src/lib/ui/startClient.ts +16 -31
  311. package/src/lib/ui/state.ts +9 -3
  312. package/src/lib/ui/trackedComputed.ts +68 -0
  313. package/src/lib/ui/types/Scope.ts +8 -24
  314. package/src/lib/ui/watch.ts +3 -3
  315. package/src/serverEntry.ts +14 -0
  316. package/template/src/server/rpc/getHello.ts +15 -13
  317. package/template/test/app.test.ts +1 -1
  318. package/src/lib/server/runtime/devHotModuleResponse.ts +0 -41
  319. package/src/lib/shared/DEV_HOT_PREFIX.ts +0 -7
  320. package/src/lib/shared/UNREACHABLE_STATUSES.ts +0 -13
  321. package/src/lib/shared/hasReplayableRequest.ts +0 -17
  322. package/src/lib/shared/hydratingSlot.ts +0 -12
  323. package/src/lib/shared/outboxProbeSlot.ts +0 -20
  324. package/src/lib/shared/types/Outbox.ts +0 -9
  325. package/src/lib/shared/types/OutboxEntry.ts +0 -27
  326. package/src/lib/shared/types/SmartReadOptions.ts +0 -36
  327. package/src/lib/shared/wakeHydrationPeeks.ts +0 -20
  328. package/src/lib/ui/COMPONENT_WRAPPER_PREFIX.ts +0 -5
  329. package/src/lib/ui/compile/REACTIVE_CALLEES.ts +0 -11
  330. package/src/lib/ui/compile/assertRuntimeHelpersBound.ts +0 -80
  331. package/src/lib/ui/compile/markupTokens.ts +0 -313
  332. package/src/lib/ui/compile/structuralBlockTokens.ts +0 -100
  333. package/src/lib/ui/dom/applyResolved.ts +0 -87
  334. package/src/lib/ui/dom/scopeLabel.ts +0 -21
  335. package/src/lib/ui/dom/text.ts +0 -20
  336. package/src/lib/ui/history.ts +0 -101
  337. package/src/lib/ui/installHotBridge.ts +0 -93
  338. package/src/lib/ui/installInspectorBridge.ts +0 -140
  339. package/src/lib/ui/outbox.ts +0 -35
  340. package/src/lib/ui/persist.ts +0 -115
  341. package/src/lib/ui/rpcOutbox/createOutboxQueue.ts +0 -281
  342. package/src/lib/ui/rpcOutbox/outboxRegistry.ts +0 -69
  343. package/src/lib/ui/runtime/PATCH_BUS.ts +0 -28
  344. package/src/lib/ui/runtime/captureModelDoc.ts +0 -28
  345. package/src/lib/ui/runtime/hotInstances.ts +0 -10
  346. package/src/lib/ui/runtime/hotReloadEnabled.ts +0 -8
  347. package/src/lib/ui/runtime/hotReplace.ts +0 -38
  348. package/src/lib/ui/runtime/liveScopes.ts +0 -15
  349. package/src/lib/ui/runtime/localStoragePersistence.ts +0 -47
  350. package/src/lib/ui/runtime/registerHotInstance.ts +0 -23
  351. package/src/lib/ui/runtime/seedModelDoc.ts +0 -26
  352. package/src/lib/ui/runtime/types/HotInstance.ts +0 -22
  353. package/src/lib/ui/runtime/types/PatchEvent.ts +0 -16
  354. package/src/lib/ui/seedResolved.ts +0 -28
  355. package/src/lib/ui/sync.ts +0 -48
  356. package/src/lib/ui/types/History.ts +0 -14
  357. package/src/lib/ui/types/PersistHandle.ts +0 -11
  358. package/src/lib/ui/types/PersistenceStore.ts +0 -12
  359. package/src/lib/ui/types/ResolvedFrame.ts +0 -15
  360. package/src/lib/ui/types/SyncTransport.ts +0 -13
@@ -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
  }
@@ -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,24 +124,121 @@ 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
- const merged = { ...Object.fromEntries(url.searchParams), ...bodyObject }
145
+ /* Collect the query into an object, arraying repeated keys (`?tag=a&tag=b` → `['a','b']`)
146
+ rather than letting `Object.fromEntries` silently keep only the last — mirrors
147
+ splitFormData, so a repeated field reaches the schema as an array, not a dropped value. */
148
+ const queryObject: Record<string, unknown> = {}
149
+ for (const [key, value] of url.searchParams) {
150
+ const existing = queryObject[key]
151
+ if (!(key in queryObject)) {
152
+ queryObject[key] = value
153
+ } else if (Array.isArray(existing)) {
154
+ existing.push(value)
155
+ } else {
156
+ queryObject[key] = [existing, value]
157
+ }
158
+ }
159
+ const merged = { ...queryObject, ...bodyObject }
141
160
  if (Object.keys(merged).length === 0) {
142
161
  return undefined
143
162
  }
163
+ if (coerce !== undefined) {
164
+ applyCoercion(merged, coerce)
165
+ }
144
166
  return merged
145
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,3 +1,4 @@
1
+ import { abideLog } from '../../shared/abideLog.ts'
1
2
  import { error } from '../error.ts'
2
3
 
3
4
  /*
@@ -39,7 +40,17 @@ export function runWithRpcTimeout(
39
40
  (late) => {
40
41
  void late.body?.cancel()
41
42
  },
42
- () => {},
43
+ (rejection) => {
44
+ /* The handler outlived its timeout and then rejected. A cooperative
45
+ abort (it honoured the composed signal) is expected and stays quiet;
46
+ any other error is a genuine handler bug and would otherwise leave
47
+ zero server-side trace behind the client's 504 — log it. */
48
+ const aborted =
49
+ (rejection as { name?: string } | undefined)?.name === 'AbortError'
50
+ if (!aborted) {
51
+ abideLog.error(rejection)
52
+ }
53
+ },
43
54
  )
44
55
  }
45
56
  }
@@ -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>