@abide/abide 0.28.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 (562) hide show
  1. package/CHANGELOG.md +607 -0
  2. package/LICENSE +21 -0
  3. package/README.md +154 -0
  4. package/bin/abide.ts +212 -0
  5. package/package.json +155 -0
  6. package/src/abideLsp.ts +211 -0
  7. package/src/abideModules.d.ts +8 -0
  8. package/src/abideResolverPlugin.ts +923 -0
  9. package/src/appEntry.ts +151 -0
  10. package/src/assets/app.html +12 -0
  11. package/src/build.ts +143 -0
  12. package/src/buildCli.ts +127 -0
  13. package/src/buildDisconnected.ts +118 -0
  14. package/src/bundleApp.ts +147 -0
  15. package/src/bundleDisconnectedEntry.ts +14 -0
  16. package/src/checkAbide.ts +77 -0
  17. package/src/cliEntry.ts +25 -0
  18. package/src/clientBuildPlugins.ts +33 -0
  19. package/src/clientEntry.ts +17 -0
  20. package/src/compile.ts +63 -0
  21. package/src/controlServerWorker.ts +426 -0
  22. package/src/devEntry.ts +250 -0
  23. package/src/discoveryEntry.ts +81 -0
  24. package/src/lib/bundle/BundleMenu.ts +12 -0
  25. package/src/lib/bundle/BundleMenuItem.ts +25 -0
  26. package/src/lib/bundle/BundleWindow.ts +37 -0
  27. package/src/lib/bundle/WEBVIEW_BUILD_REVISION.ts +9 -0
  28. package/src/lib/bundle/WEBVIEW_VERSION.ts +7 -0
  29. package/src/lib/bundle/bindConnectedFlag.ts +29 -0
  30. package/src/lib/bundle/bindRequestNavigate.ts +34 -0
  31. package/src/lib/bundle/buildWebviewLib.ts +111 -0
  32. package/src/lib/bundle/bundled.ts +35 -0
  33. package/src/lib/bundle/disconnected.abide +236 -0
  34. package/src/lib/bundle/disconnected.css +9 -0
  35. package/src/lib/bundle/ensureWebviewLib.ts +20 -0
  36. package/src/lib/bundle/exitWithParent.ts +28 -0
  37. package/src/lib/bundle/infoPlist.ts +46 -0
  38. package/src/lib/bundle/installDownloads.ts +24 -0
  39. package/src/lib/bundle/installMacMenu.ts +39 -0
  40. package/src/lib/bundle/listenLocalControlServer.ts +19 -0
  41. package/src/lib/bundle/native/abideMenu.mm +422 -0
  42. package/src/lib/bundle/native/webview.h +4557 -0
  43. package/src/lib/bundle/onMenu.ts +42 -0
  44. package/src/lib/bundle/openWebview.ts +104 -0
  45. package/src/lib/bundle/pngToIcns.ts +47 -0
  46. package/src/lib/bundle/probeAbideServer.ts +57 -0
  47. package/src/lib/bundle/resolveServerBinary.ts +12 -0
  48. package/src/lib/bundle/resolveWebviewLib.ts +53 -0
  49. package/src/lib/bundle/serverBinaryFilename.ts +8 -0
  50. package/src/lib/bundle/signMacApp.ts +37 -0
  51. package/src/lib/bundle/spawnEmbeddedServer.ts +64 -0
  52. package/src/lib/bundle/stableLocalPort.ts +19 -0
  53. package/src/lib/bundle/waitForServer.ts +23 -0
  54. package/src/lib/bundle/webviewCachePath.ts +23 -0
  55. package/src/lib/bundle/webviewLibName.ts +11 -0
  56. package/src/lib/cli/connectToServer.ts +23 -0
  57. package/src/lib/cli/createClient.ts +108 -0
  58. package/src/lib/cli/dispatchCommand.ts +71 -0
  59. package/src/lib/cli/loadEnvFromBinaryDir.ts +15 -0
  60. package/src/lib/cli/parseArgvForRpc.ts +100 -0
  61. package/src/lib/cli/printHelp.ts +119 -0
  62. package/src/lib/cli/printSessionHelp.ts +27 -0
  63. package/src/lib/cli/printSessionStatus.ts +21 -0
  64. package/src/lib/cli/printTrimmed.ts +8 -0
  65. package/src/lib/cli/printValue.ts +10 -0
  66. package/src/lib/cli/resolveCliTarget.ts +48 -0
  67. package/src/lib/cli/runCli.ts +176 -0
  68. package/src/lib/cli/runSession.ts +108 -0
  69. package/src/lib/cli/startLocalInstance.ts +14 -0
  70. package/src/lib/cli/tokenizeLine.ts +51 -0
  71. package/src/lib/cli/types/CliManifest.ts +9 -0
  72. package/src/lib/cli/types/CliManifestEntry.ts +17 -0
  73. package/src/lib/cli/types/CliTarget.ts +13 -0
  74. package/src/lib/mcp/annotationsForMethod.ts +29 -0
  75. package/src/lib/mcp/createMcpResourceServer.ts +102 -0
  76. package/src/lib/mcp/createMcpServer.ts +48 -0
  77. package/src/lib/mcp/dispatchMcpRequest.ts +138 -0
  78. package/src/lib/mcp/mcpResourceServerSlot.ts +18 -0
  79. package/src/lib/mcp/mcpSurface.ts +295 -0
  80. package/src/lib/mcp/toolResultFromResponse.ts +66 -0
  81. package/src/lib/mcp/types/JsonRpcRequest.ts +12 -0
  82. package/src/lib/mcp/types/JsonRpcResponse.ts +20 -0
  83. package/src/lib/mcp/types/McpResourceContents.ts +10 -0
  84. package/src/lib/mcp/types/McpResourceDescriptor.ts +6 -0
  85. package/src/lib/mcp/types/McpResourceServer.ts +12 -0
  86. package/src/lib/mcp/types/McpServer.ts +9 -0
  87. package/src/lib/mcp/types/McpServerOptions.ts +16 -0
  88. package/src/lib/server/AppModule.ts +47 -0
  89. package/src/lib/server/DELETE.ts +10 -0
  90. package/src/lib/server/GET.ts +10 -0
  91. package/src/lib/server/HEAD.ts +10 -0
  92. package/src/lib/server/PATCH.ts +10 -0
  93. package/src/lib/server/POST.ts +10 -0
  94. package/src/lib/server/PUT.ts +10 -0
  95. package/src/lib/server/agent.ts +86 -0
  96. package/src/lib/server/appDataDir.ts +16 -0
  97. package/src/lib/server/cli/buildEnvContent.ts +19 -0
  98. package/src/lib/server/cli/createTarGz.ts +77 -0
  99. package/src/lib/server/cli/handleCliDownload.ts +150 -0
  100. package/src/lib/server/cli/handleCliInstall.ts +37 -0
  101. package/src/lib/server/cli/installScript.ts +31 -0
  102. package/src/lib/server/cli/maxSourceMtime.ts +26 -0
  103. package/src/lib/server/cookies.ts +30 -0
  104. package/src/lib/server/env.ts +51 -0
  105. package/src/lib/server/error.ts +73 -0
  106. package/src/lib/server/json.ts +42 -0
  107. package/src/lib/server/jsonl.ts +47 -0
  108. package/src/lib/server/prompts/definePrompt.ts +21 -0
  109. package/src/lib/server/prompts/promptRegistry.ts +9 -0
  110. package/src/lib/server/prompts/registerPrompt.ts +6 -0
  111. package/src/lib/server/prompts/renderPromptTemplate.ts +17 -0
  112. package/src/lib/server/prompts/types/Prompt.ts +13 -0
  113. package/src/lib/server/prompts/types/PromptOptions.ts +12 -0
  114. package/src/lib/server/prompts/types/PromptRegistryEntry.ts +13 -0
  115. package/src/lib/server/prompts/types/PromptRoutes.ts +10 -0
  116. package/src/lib/server/reachable.ts +45 -0
  117. package/src/lib/server/redirect.ts +43 -0
  118. package/src/lib/server/request.ts +19 -0
  119. package/src/lib/server/rpc/defineVerb.ts +210 -0
  120. package/src/lib/server/rpc/dispatchVerbInProcess.ts +46 -0
  121. package/src/lib/server/rpc/findVerbByCommandName.ts +18 -0
  122. package/src/lib/server/rpc/parseArgs.ts +127 -0
  123. package/src/lib/server/rpc/readBodyWithinLimit.ts +44 -0
  124. package/src/lib/server/rpc/registerVerb.ts +6 -0
  125. package/src/lib/server/rpc/runWithVerbTimeout.ts +49 -0
  126. package/src/lib/server/rpc/types/RemoteHandler.ts +27 -0
  127. package/src/lib/server/rpc/types/RemoteRoutes.ts +13 -0
  128. package/src/lib/server/rpc/types/TypedResponse.ts +18 -0
  129. package/src/lib/server/rpc/types/VerbHelper.ts +87 -0
  130. package/src/lib/server/rpc/types/VerbRegistryEntry.ts +35 -0
  131. package/src/lib/server/rpc/unprocessed.ts +14 -0
  132. package/src/lib/server/rpc/verbRegistry.ts +11 -0
  133. package/src/lib/server/runtime/DEFAULT_PORT.ts +6 -0
  134. package/src/lib/server/runtime/DEV_READY_MESSAGE.ts +6 -0
  135. package/src/lib/server/runtime/DEV_REBUILD_MESSAGE.ts +4 -0
  136. package/src/lib/server/runtime/DEV_RELOAD_CLIENT_SCRIPT.ts +107 -0
  137. package/src/lib/server/runtime/SSR_SWAP_SCRIPT.ts +16 -0
  138. package/src/lib/server/runtime/acceptsGzip.ts +24 -0
  139. package/src/lib/server/runtime/buildCacheSnapshot.ts +61 -0
  140. package/src/lib/server/runtime/buildHealthPayload.ts +34 -0
  141. package/src/lib/server/runtime/buildInspectorSurface.ts +37 -0
  142. package/src/lib/server/runtime/buildOpenApiSpec.ts +106 -0
  143. package/src/lib/server/runtime/cacheControlForAsset.ts +22 -0
  144. package/src/lib/server/runtime/containsTraversal.ts +37 -0
  145. package/src/lib/server/runtime/createAppAssetServer.ts +76 -0
  146. package/src/lib/server/runtime/createAssetHeaderCache.ts +31 -0
  147. package/src/lib/server/runtime/createPublicAssetServer.ts +67 -0
  148. package/src/lib/server/runtime/createReachable.ts +109 -0
  149. package/src/lib/server/runtime/createRouteDispatcher.ts +127 -0
  150. package/src/lib/server/runtime/createServer.ts +674 -0
  151. package/src/lib/server/runtime/createUiPageRenderer.ts +181 -0
  152. package/src/lib/server/runtime/crossOriginForbidden.ts +17 -0
  153. package/src/lib/server/runtime/crossOriginGate.ts +29 -0
  154. package/src/lib/server/runtime/devClientFingerprint.ts +117 -0
  155. package/src/lib/server/runtime/devHotModuleResponse.ts +40 -0
  156. package/src/lib/server/runtime/devReloadResponse.ts +41 -0
  157. package/src/lib/server/runtime/disableIdleTimeoutForStream.ts +27 -0
  158. package/src/lib/server/runtime/envSchemaStore.ts +15 -0
  159. package/src/lib/server/runtime/findOpenPort.ts +21 -0
  160. package/src/lib/server/runtime/getActiveServer.ts +6 -0
  161. package/src/lib/server/runtime/globToPathSet.ts +29 -0
  162. package/src/lib/server/runtime/gzipResponse.ts +46 -0
  163. package/src/lib/server/runtime/inProcessServer.ts +20 -0
  164. package/src/lib/server/runtime/internalErrorResponse.ts +25 -0
  165. package/src/lib/server/runtime/isCrossOriginRequest.ts +23 -0
  166. package/src/lib/server/runtime/listenOnOpenPort.ts +36 -0
  167. package/src/lib/server/runtime/logExposedSurfaces.ts +156 -0
  168. package/src/lib/server/runtime/maybeMountInspector.ts +97 -0
  169. package/src/lib/server/runtime/mimeForExtension.ts +14 -0
  170. package/src/lib/server/runtime/pageUrlFromStore.ts +15 -0
  171. package/src/lib/server/runtime/parseIdleTimeout.ts +10 -0
  172. package/src/lib/server/runtime/parsePort.ts +11 -0
  173. package/src/lib/server/runtime/registryManifests.ts +66 -0
  174. package/src/lib/server/runtime/requestContext.ts +5 -0
  175. package/src/lib/server/runtime/resolvePageSnapshot.ts +25 -0
  176. package/src/lib/server/runtime/respondWithEmbeddedAsset.ts +18 -0
  177. package/src/lib/server/runtime/runWithRequestScope.ts +150 -0
  178. package/src/lib/server/runtime/safeJsonForScript.ts +17 -0
  179. package/src/lib/server/runtime/serializeCacheSnapshot.ts +45 -0
  180. package/src/lib/server/runtime/serverSlot.ts +13 -0
  181. package/src/lib/server/runtime/setActiveServer.ts +6 -0
  182. package/src/lib/server/runtime/snapshotEntryFromCache.ts +83 -0
  183. package/src/lib/server/runtime/streamCacheResolutions.ts +37 -0
  184. package/src/lib/server/runtime/streamFromIterator.ts +86 -0
  185. package/src/lib/server/runtime/types/Assets.ts +6 -0
  186. package/src/lib/server/runtime/types/DevReloadStamp.ts +18 -0
  187. package/src/lib/server/runtime/types/InspectorCacheEntry.ts +24 -0
  188. package/src/lib/server/runtime/types/InspectorCacheSnapshot.ts +11 -0
  189. package/src/lib/server/runtime/types/InspectorContext.ts +30 -0
  190. package/src/lib/server/runtime/types/InspectorSocket.ts +17 -0
  191. package/src/lib/server/runtime/types/InspectorSurface.ts +13 -0
  192. package/src/lib/server/runtime/types/InspectorVerb.ts +27 -0
  193. package/src/lib/server/runtime/types/RequestStore.ts +55 -0
  194. package/src/lib/server/runtime/warnUnguardedMcp.ts +32 -0
  195. package/src/lib/server/runtime/withResponseDefaults.ts +24 -0
  196. package/src/lib/server/server.ts +33 -0
  197. package/src/lib/server/socket.ts +32 -0
  198. package/src/lib/server/sockets/createSocketDispatcher.ts +337 -0
  199. package/src/lib/server/sockets/defineSocket.ts +179 -0
  200. package/src/lib/server/sockets/lookupSocket.ts +6 -0
  201. package/src/lib/server/sockets/registerSocket.ts +6 -0
  202. package/src/lib/server/sockets/socketOperations.ts +36 -0
  203. package/src/lib/server/sockets/socketRegistry.ts +9 -0
  204. package/src/lib/server/sockets/types/Socket.ts +23 -0
  205. package/src/lib/server/sockets/types/SocketClientFrame.ts +19 -0
  206. package/src/lib/server/sockets/types/SocketOperation.ts +22 -0
  207. package/src/lib/server/sockets/types/SocketOptions.ts +26 -0
  208. package/src/lib/server/sockets/types/SocketRegistryEntry.ts +19 -0
  209. package/src/lib/server/sockets/types/SocketRoutes.ts +10 -0
  210. package/src/lib/server/sockets/types/SocketServerFrame.ts +24 -0
  211. package/src/lib/server/sse.ts +54 -0
  212. package/src/lib/shared/ABIDE_PACKAGE_NAME.ts +7 -0
  213. package/src/lib/shared/ABIDE_VERSION.ts +9 -0
  214. package/src/lib/shared/CACHE_CONTROL_VALUES.ts +16 -0
  215. package/src/lib/shared/CACHE_WRAPPED.ts +8 -0
  216. package/src/lib/shared/CLI_PATH.ts +7 -0
  217. package/src/lib/shared/DEV_HOT_PREFIX.ts +7 -0
  218. package/src/lib/shared/DEV_RELOAD_PATH.ts +6 -0
  219. package/src/lib/shared/HEALTH_PATH.ts +7 -0
  220. package/src/lib/shared/HttpError.ts +20 -0
  221. package/src/lib/shared/IDENTITY_PATH.ts +6 -0
  222. package/src/lib/shared/INSPECTOR_PATH.ts +7 -0
  223. package/src/lib/shared/NAV_HEADER.ts +8 -0
  224. package/src/lib/shared/OFFLINE_HEADER.ts +8 -0
  225. package/src/lib/shared/REMOTE_FUNCTION.ts +8 -0
  226. package/src/lib/shared/REPLAYABLE_METHODS.ts +12 -0
  227. package/src/lib/shared/SOCKETS_PATH.ts +7 -0
  228. package/src/lib/shared/STREAMING_CONTENT_TYPES.ts +11 -0
  229. package/src/lib/shared/SocketDisconnectedError.ts +13 -0
  230. package/src/lib/shared/TEXT_PLAIN.ts +7 -0
  231. package/src/lib/shared/abideImportName.ts +44 -0
  232. package/src/lib/shared/abideLog.ts +38 -0
  233. package/src/lib/shared/activeCacheStore.ts +20 -0
  234. package/src/lib/shared/activePage.ts +25 -0
  235. package/src/lib/shared/appDataDir.ts +34 -0
  236. package/src/lib/shared/appNameSlot.ts +10 -0
  237. package/src/lib/shared/basePath.ts +10 -0
  238. package/src/lib/shared/basePathFromAppUrl.ts +20 -0
  239. package/src/lib/shared/baseSlot.ts +14 -0
  240. package/src/lib/shared/binaryDirEnvPath.ts +12 -0
  241. package/src/lib/shared/browserClientFlags.ts +10 -0
  242. package/src/lib/shared/buildRpcProxy.ts +39 -0
  243. package/src/lib/shared/buildRpcRequest.ts +70 -0
  244. package/src/lib/shared/buildSocketOverChannel.ts +58 -0
  245. package/src/lib/shared/bundleLayout.ts +36 -0
  246. package/src/lib/shared/cache.ts +951 -0
  247. package/src/lib/shared/cacheEntryFromSnapshot.ts +59 -0
  248. package/src/lib/shared/cacheStoreSlot.ts +16 -0
  249. package/src/lib/shared/cacheStores.ts +10 -0
  250. package/src/lib/shared/canonicalJson.ts +63 -0
  251. package/src/lib/shared/carriesBodyArgs.ts +13 -0
  252. package/src/lib/shared/clearLastConnection.ts +7 -0
  253. package/src/lib/shared/commandNameForUrl.ts +17 -0
  254. package/src/lib/shared/createCacheStore.ts +104 -0
  255. package/src/lib/shared/createChannelLog.ts +122 -0
  256. package/src/lib/shared/createLifecycleChannel.ts +56 -0
  257. package/src/lib/shared/createLivenessWatch.ts +118 -0
  258. package/src/lib/shared/createPushIterator.ts +127 -0
  259. package/src/lib/shared/createRemoteFunction.ts +122 -0
  260. package/src/lib/shared/createSubscriber.ts +55 -0
  261. package/src/lib/shared/createTraceContext.ts +21 -0
  262. package/src/lib/shared/dataDirEnvPath.ts +12 -0
  263. package/src/lib/shared/decodeResponse.ts +47 -0
  264. package/src/lib/shared/detectTarget.ts +27 -0
  265. package/src/lib/shared/detectVerbMethod.ts +17 -0
  266. package/src/lib/shared/emitLogRecord.ts +190 -0
  267. package/src/lib/shared/exeSuffix.ts +9 -0
  268. package/src/lib/shared/exitOnBuildFailure.ts +17 -0
  269. package/src/lib/shared/extraForwardHeaders.ts +16 -0
  270. package/src/lib/shared/fileStem.ts +9 -0
  271. package/src/lib/shared/findExportCallSite.ts +476 -0
  272. package/src/lib/shared/formatTraceparent.ts +6 -0
  273. package/src/lib/shared/forwardHeaders.ts +44 -0
  274. package/src/lib/shared/getRemoteMeta.ts +5 -0
  275. package/src/lib/shared/globalCacheStore.ts +15 -0
  276. package/src/lib/shared/globalCacheStoreSlot.ts +14 -0
  277. package/src/lib/shared/health.ts +179 -0
  278. package/src/lib/shared/healthReadSlot.ts +11 -0
  279. package/src/lib/shared/healthSeedSlot.ts +12 -0
  280. package/src/lib/shared/html.ts +38 -0
  281. package/src/lib/shared/importNamesToStrip.ts +13 -0
  282. package/src/lib/shared/invalidateEvent.ts +11 -0
  283. package/src/lib/shared/invalidateTripwire.ts +40 -0
  284. package/src/lib/shared/isAbideHealthPayload.ts +11 -0
  285. package/src/lib/shared/isCompileTarget.ts +15 -0
  286. package/src/lib/shared/isDebugEnabled.ts +26 -0
  287. package/src/lib/shared/isDebugNegated.ts +19 -0
  288. package/src/lib/shared/isModuleNotFound.ts +16 -0
  289. package/src/lib/shared/isReadOnlyMethod.ts +14 -0
  290. package/src/lib/shared/isReplayableMethod.ts +7 -0
  291. package/src/lib/shared/isStreamingResponse.ts +11 -0
  292. package/src/lib/shared/isSubscribable.ts +15 -0
  293. package/src/lib/shared/jsonSchemaForPromptArguments.ts +29 -0
  294. package/src/lib/shared/jsonSchemaForSchema.ts +39 -0
  295. package/src/lib/shared/jsonlErrorFrame.ts +24 -0
  296. package/src/lib/shared/keyForRemoteCall.ts +29 -0
  297. package/src/lib/shared/keyMatchesPrefix.ts +9 -0
  298. package/src/lib/shared/lastConnectionPath.ts +7 -0
  299. package/src/lib/shared/layoutChainForRoute.ts +22 -0
  300. package/src/lib/shared/loadEnvFile.ts +17 -0
  301. package/src/lib/shared/loadEnvFromDataDir.ts +14 -0
  302. package/src/lib/shared/log.ts +24 -0
  303. package/src/lib/shared/logClosingRecord.ts +28 -0
  304. package/src/lib/shared/logTapSlot.ts +13 -0
  305. package/src/lib/shared/manifestModule.ts +39 -0
  306. package/src/lib/shared/matchesDebugPattern.ts +16 -0
  307. package/src/lib/shared/memoizeByKey.ts +32 -0
  308. package/src/lib/shared/normalizeTarget.ts +10 -0
  309. package/src/lib/shared/online.ts +51 -0
  310. package/src/lib/shared/page.ts +30 -0
  311. package/src/lib/shared/pageSlot.ts +17 -0
  312. package/src/lib/shared/pageUrlForFile.ts +14 -0
  313. package/src/lib/shared/parseBoundedEnvInt.ts +20 -0
  314. package/src/lib/shared/parseDebugPatterns.ts +21 -0
  315. package/src/lib/shared/parseEnv.ts +30 -0
  316. package/src/lib/shared/parsePromptMarkdown.ts +35 -0
  317. package/src/lib/shared/parseRouteSegments.ts +22 -0
  318. package/src/lib/shared/parseTraceparent.ts +26 -0
  319. package/src/lib/shared/pending.ts +30 -0
  320. package/src/lib/shared/prepareRpcModule.ts +59 -0
  321. package/src/lib/shared/prepareSocketModule.ts +49 -0
  322. package/src/lib/shared/probeRegistries.ts +68 -0
  323. package/src/lib/shared/producerKey.ts +32 -0
  324. package/src/lib/shared/programNameForPackage.ts +14 -0
  325. package/src/lib/shared/promptNameForFile.ts +10 -0
  326. package/src/lib/shared/queryStringFromArgs.ts +27 -0
  327. package/src/lib/shared/randomHexId.ts +14 -0
  328. package/src/lib/shared/readEnvFile.ts +15 -0
  329. package/src/lib/shared/readLastConnection.ts +18 -0
  330. package/src/lib/shared/readPackageJson.ts +9 -0
  331. package/src/lib/shared/recordRemoteMeta.ts +5 -0
  332. package/src/lib/shared/refreshing.ts +31 -0
  333. package/src/lib/shared/remoteMetaStore.ts +16 -0
  334. package/src/lib/shared/requestScopeSlot.ts +15 -0
  335. package/src/lib/shared/resolveClientFlags.ts +20 -0
  336. package/src/lib/shared/responseErrorText.ts +9 -0
  337. package/src/lib/shared/rpcTimeoutSlot.ts +9 -0
  338. package/src/lib/shared/rpcUrlForFile.ts +19 -0
  339. package/src/lib/shared/runningAsStandaloneBinary.ts +13 -0
  340. package/src/lib/shared/selectorMatcher.ts +68 -0
  341. package/src/lib/shared/selectorPrefix.ts +39 -0
  342. package/src/lib/shared/serializeEnv.ts +18 -0
  343. package/src/lib/shared/setAppName.ts +5 -0
  344. package/src/lib/shared/setBaseResolver.ts +6 -0
  345. package/src/lib/shared/setCacheStoreResolver.ts +6 -0
  346. package/src/lib/shared/setGlobalCacheStoreResolver.ts +6 -0
  347. package/src/lib/shared/setPageResolver.ts +7 -0
  348. package/src/lib/shared/setRequestScopeResolver.ts +6 -0
  349. package/src/lib/shared/snippet.ts +25 -0
  350. package/src/lib/shared/socketNameForFile.ts +11 -0
  351. package/src/lib/shared/socketTapSlot.ts +12 -0
  352. package/src/lib/shared/sseErrorFrame.ts +29 -0
  353. package/src/lib/shared/streamResponse.ts +169 -0
  354. package/src/lib/shared/stripImport.ts +27 -0
  355. package/src/lib/shared/subscribableFromResponse.ts +51 -0
  356. package/src/lib/shared/tailProbeSlot.ts +16 -0
  357. package/src/lib/shared/toBunRoutePattern.ts +28 -0
  358. package/src/lib/shared/toScopeSet.ts +4 -0
  359. package/src/lib/shared/trace.ts +16 -0
  360. package/src/lib/shared/types/CacheEntry.ts +84 -0
  361. package/src/lib/shared/types/CacheInvalidation.ts +9 -0
  362. package/src/lib/shared/types/CacheOnContext.ts +25 -0
  363. package/src/lib/shared/types/CacheOptions.ts +39 -0
  364. package/src/lib/shared/types/CacheSelector.ts +17 -0
  365. package/src/lib/shared/types/CacheSnapshot.ts +16 -0
  366. package/src/lib/shared/types/CacheSnapshotEntry.ts +17 -0
  367. package/src/lib/shared/types/CacheStats.ts +13 -0
  368. package/src/lib/shared/types/CacheStore.ts +39 -0
  369. package/src/lib/shared/types/ChannelLog.ts +13 -0
  370. package/src/lib/shared/types/ClientFlags.ts +11 -0
  371. package/src/lib/shared/types/CompileTarget.ts +6 -0
  372. package/src/lib/shared/types/FrameworkLog.ts +13 -0
  373. package/src/lib/shared/types/HttpVerb.ts +1 -0
  374. package/src/lib/shared/types/LastConnection.ts +9 -0
  375. package/src/lib/shared/types/Log.ts +13 -0
  376. package/src/lib/shared/types/LogRecord.ts +42 -0
  377. package/src/lib/shared/types/LogVoice.ts +7 -0
  378. package/src/lib/shared/types/PageSnapshot.ts +14 -0
  379. package/src/lib/shared/types/PromptArgument.ts +12 -0
  380. package/src/lib/shared/types/RawRemoteFunction.ts +14 -0
  381. package/src/lib/shared/types/RemoteCallable.ts +12 -0
  382. package/src/lib/shared/types/RemoteFunction.ts +47 -0
  383. package/src/lib/shared/types/ReplayableMethod.ts +7 -0
  384. package/src/lib/shared/types/RequestScopeInfo.ts +16 -0
  385. package/src/lib/shared/types/RpcInvoker.ts +6 -0
  386. package/src/lib/shared/types/SocketChannel.ts +17 -0
  387. package/src/lib/shared/types/SocketSubCallbacks.ts +13 -0
  388. package/src/lib/shared/types/StandardSchemaV1.ts +56 -0
  389. package/src/lib/shared/types/StreamedResolution.ts +10 -0
  390. package/src/lib/shared/types/Subscribable.ts +26 -0
  391. package/src/lib/shared/types/TailHooks.ts +12 -0
  392. package/src/lib/shared/types/TailOptions.ts +10 -0
  393. package/src/lib/shared/types/TraceContext.ts +17 -0
  394. package/src/lib/shared/url.ts +118 -0
  395. package/src/lib/shared/withBase.ts +11 -0
  396. package/src/lib/shared/withBaseUrl.ts +17 -0
  397. package/src/lib/shared/withJsonSchema.ts +21 -0
  398. package/src/lib/shared/writeDts.ts +12 -0
  399. package/src/lib/shared/writeHealthDts.ts +36 -0
  400. package/src/lib/shared/writeLastConnection.ts +13 -0
  401. package/src/lib/shared/writePublicAssetsDts.ts +31 -0
  402. package/src/lib/shared/writeRoutesDts.ts +73 -0
  403. package/src/lib/shared/writeRpcDts.ts +49 -0
  404. package/src/lib/shared/writeTestRpcDts.ts +45 -0
  405. package/src/lib/shared/writeTestSocketsDts.ts +34 -0
  406. package/src/lib/test/assertAgentFrameConformance.ts +73 -0
  407. package/src/lib/test/createScriptedSurface.ts +45 -0
  408. package/src/lib/test/createTestApp.ts +203 -0
  409. package/src/lib/test/createTestSocketChannel.ts +142 -0
  410. package/src/lib/ui/README.md +86 -0
  411. package/src/lib/ui/compile/SSR_ESCAPE.ts +25 -0
  412. package/src/lib/ui/compile/UI_RUNTIME_IMPORTS.ts +36 -0
  413. package/src/lib/ui/compile/VOID_TAGS.ts +21 -0
  414. package/src/lib/ui/compile/abideUiPlugin.ts +65 -0
  415. package/src/lib/ui/compile/analyzeComponent.ts +117 -0
  416. package/src/lib/ui/compile/assetModulesFile.ts +32 -0
  417. package/src/lib/ui/compile/branchElements.ts +50 -0
  418. package/src/lib/ui/compile/collectAbideDiagnostics.ts +59 -0
  419. package/src/lib/ui/compile/compileComponent.ts +20 -0
  420. package/src/lib/ui/compile/compileModule.ts +116 -0
  421. package/src/lib/ui/compile/compileSSR.ts +36 -0
  422. package/src/lib/ui/compile/compileShadow.ts +352 -0
  423. package/src/lib/ui/compile/createShadowLanguageService.ts +197 -0
  424. package/src/lib/ui/compile/createShadowProgram.ts +96 -0
  425. package/src/lib/ui/compile/decodeHtmlEntities.ts +49 -0
  426. package/src/lib/ui/compile/desugarSignals.ts +133 -0
  427. package/src/lib/ui/compile/escapeHtml.ts +15 -0
  428. package/src/lib/ui/compile/generateBuild.ts +638 -0
  429. package/src/lib/ui/compile/generateSSR.ts +380 -0
  430. package/src/lib/ui/compile/groupBindParts.ts +28 -0
  431. package/src/lib/ui/compile/hoistCells.ts +120 -0
  432. package/src/lib/ui/compile/loadShadowTsConfig.ts +31 -0
  433. package/src/lib/ui/compile/lowerDocAccess.ts +202 -0
  434. package/src/lib/ui/compile/nearestProjectRoot.ts +16 -0
  435. package/src/lib/ui/compile/parseTemplate.ts +396 -0
  436. package/src/lib/ui/compile/partitionSlots.ts +36 -0
  437. package/src/lib/ui/compile/prepareNestedScript.ts +42 -0
  438. package/src/lib/ui/compile/remapShadowDiagnostic.ts +30 -0
  439. package/src/lib/ui/compile/renameSignalRefs.ts +85 -0
  440. package/src/lib/ui/compile/resolveAbideImports.ts +29 -0
  441. package/src/lib/ui/compile/scopeCss.ts +115 -0
  442. package/src/lib/ui/compile/shadowNaming.ts +11 -0
  443. package/src/lib/ui/compile/sourceToShadowOffset.ts +24 -0
  444. package/src/lib/ui/compile/staticAttrValue.ts +13 -0
  445. package/src/lib/ui/compile/stripEffects.ts +32 -0
  446. package/src/lib/ui/compile/types/AbideDiagnostic.ts +14 -0
  447. package/src/lib/ui/compile/types/AnalyzedComponent.ts +25 -0
  448. package/src/lib/ui/compile/types/CompiledShadow.ts +15 -0
  449. package/src/lib/ui/compile/types/TemplateAttr.ts +16 -0
  450. package/src/lib/ui/compile/types/TemplateNode.ts +78 -0
  451. package/src/lib/ui/compile/types/TextPart.ts +8 -0
  452. package/src/lib/ui/derived.ts +28 -0
  453. package/src/lib/ui/doc.ts +15 -0
  454. package/src/lib/ui/dom/appendSnippet.ts +34 -0
  455. package/src/lib/ui/dom/appendStatic.ts +27 -0
  456. package/src/lib/ui/dom/appendText.ts +114 -0
  457. package/src/lib/ui/dom/applyResolved.ts +72 -0
  458. package/src/lib/ui/dom/attach.ts +20 -0
  459. package/src/lib/ui/dom/attr.ts +19 -0
  460. package/src/lib/ui/dom/awaitBlock.ts +224 -0
  461. package/src/lib/ui/dom/cloneStatic.ts +52 -0
  462. package/src/lib/ui/dom/each.ts +115 -0
  463. package/src/lib/ui/dom/eachAsync.ts +153 -0
  464. package/src/lib/ui/dom/hydrate.ts +35 -0
  465. package/src/lib/ui/dom/mount.ts +29 -0
  466. package/src/lib/ui/dom/mountChild.ts +33 -0
  467. package/src/lib/ui/dom/on.ts +15 -0
  468. package/src/lib/ui/dom/openChild.ts +22 -0
  469. package/src/lib/ui/dom/openRoot.ts +20 -0
  470. package/src/lib/ui/dom/switchBlock.ts +75 -0
  471. package/src/lib/ui/dom/text.ts +20 -0
  472. package/src/lib/ui/dom/tryBlock.ts +112 -0
  473. package/src/lib/ui/dom/types/EachRow.ts +3 -0
  474. package/src/lib/ui/dom/types/SwitchCase.ts +6 -0
  475. package/src/lib/ui/dom/when.ts +73 -0
  476. package/src/lib/ui/effect.ts +16 -0
  477. package/src/lib/ui/installHotBridge.ts +73 -0
  478. package/src/lib/ui/matchRoute.ts +89 -0
  479. package/src/lib/ui/navigate.ts +17 -0
  480. package/src/lib/ui/probeNavigation.ts +33 -0
  481. package/src/lib/ui/remoteProxy.ts +97 -0
  482. package/src/lib/ui/renderChain.ts +50 -0
  483. package/src/lib/ui/renderToStream.ts +104 -0
  484. package/src/lib/ui/router.ts +286 -0
  485. package/src/lib/ui/runtime/OUTLET_TAG.ts +8 -0
  486. package/src/lib/ui/runtime/OWNER.ts +8 -0
  487. package/src/lib/ui/runtime/REACTIVE_CONTEXT.ts +14 -0
  488. package/src/lib/ui/runtime/RENDER.ts +23 -0
  489. package/src/lib/ui/runtime/RESUME.ts +16 -0
  490. package/src/lib/ui/runtime/applyPatchToTree.ts +41 -0
  491. package/src/lib/ui/runtime/claimChild.ts +10 -0
  492. package/src/lib/ui/runtime/clientPage.ts +16 -0
  493. package/src/lib/ui/runtime/createComputedNode.ts +16 -0
  494. package/src/lib/ui/runtime/createDoc.ts +177 -0
  495. package/src/lib/ui/runtime/createEffectNode.ts +58 -0
  496. package/src/lib/ui/runtime/createSignalNode.ts +16 -0
  497. package/src/lib/ui/runtime/detachLink.ts +21 -0
  498. package/src/lib/ui/runtime/endTracking.ts +24 -0
  499. package/src/lib/ui/runtime/enterRenderPass.ts +12 -0
  500. package/src/lib/ui/runtime/exitRenderPass.ts +7 -0
  501. package/src/lib/ui/runtime/firstOutlet.ts +22 -0
  502. package/src/lib/ui/runtime/flushEffects.ts +17 -0
  503. package/src/lib/ui/runtime/hotInstances.ts +10 -0
  504. package/src/lib/ui/runtime/hotReloadEnabled.ts +8 -0
  505. package/src/lib/ui/runtime/hotReplace.ts +25 -0
  506. package/src/lib/ui/runtime/nextBlockId.ts +11 -0
  507. package/src/lib/ui/runtime/pathExists.ts +23 -0
  508. package/src/lib/ui/runtime/readNode.ts +17 -0
  509. package/src/lib/ui/runtime/registerHotInstance.ts +23 -0
  510. package/src/lib/ui/runtime/runNode.ts +28 -0
  511. package/src/lib/ui/runtime/runtimePath.ts +9 -0
  512. package/src/lib/ui/runtime/scope.ts +24 -0
  513. package/src/lib/ui/runtime/toTeardown.ts +26 -0
  514. package/src/lib/ui/runtime/track.ts +58 -0
  515. package/src/lib/ui/runtime/trigger.ts +44 -0
  516. package/src/lib/ui/runtime/types/Cell.ts +5 -0
  517. package/src/lib/ui/runtime/types/Derived.ts +3 -0
  518. package/src/lib/ui/runtime/types/Doc.ts +19 -0
  519. package/src/lib/ui/runtime/types/EffectResult.ts +10 -0
  520. package/src/lib/ui/runtime/types/HotInstance.ts +14 -0
  521. package/src/lib/ui/runtime/types/NavVerdict.ts +9 -0
  522. package/src/lib/ui/runtime/types/Patch.ts +11 -0
  523. package/src/lib/ui/runtime/types/ReactiveLink.ts +21 -0
  524. package/src/lib/ui/runtime/types/ReactiveNode.ts +25 -0
  525. package/src/lib/ui/runtime/types/Route.ts +8 -0
  526. package/src/lib/ui/runtime/types/RouteLoader.ts +7 -0
  527. package/src/lib/ui/runtime/types/SsrRender.ts +22 -0
  528. package/src/lib/ui/runtime/types/State.ts +3 -0
  529. package/src/lib/ui/runtime/types/Teardown.ts +5 -0
  530. package/src/lib/ui/runtime/types/UiComponent.ts +16 -0
  531. package/src/lib/ui/runtime/types/UiProps.ts +15 -0
  532. package/src/lib/ui/runtime/unlinkDeps.ts +20 -0
  533. package/src/lib/ui/runtime/untrack.ts +20 -0
  534. package/src/lib/ui/runtime/valueAtPath.ts +18 -0
  535. package/src/lib/ui/runtime/writeNode.ts +16 -0
  536. package/src/lib/ui/socketChannel.ts +227 -0
  537. package/src/lib/ui/socketProxy.ts +25 -0
  538. package/src/lib/ui/startClient.ts +58 -0
  539. package/src/lib/ui/state.ts +25 -0
  540. package/src/lib/ui/tail.ts +324 -0
  541. package/src/lib/ui/types/Layouts.ts +9 -0
  542. package/src/lib/ui/types/Pages.ts +8 -0
  543. package/src/preload.ts +19 -0
  544. package/src/scaffold.ts +153 -0
  545. package/src/serverBuildPlugins.ts +19 -0
  546. package/src/serverEntry.ts +95 -0
  547. package/template/bunfig.toml +4 -0
  548. package/template/package.json +18 -0
  549. package/template/src/app.ts +28 -0
  550. package/template/src/bundle/icon.png +0 -0
  551. package/template/src/cli/banner.txt +3 -0
  552. package/template/src/cli/footer.txt +1 -0
  553. package/template/src/server/config.ts +17 -0
  554. package/template/src/server/rpc/getHello.ts +36 -0
  555. package/template/src/ui/Layout.abide +19 -0
  556. package/template/src/ui/app.css +21 -0
  557. package/template/src/ui/app.html +24 -0
  558. package/template/src/ui/pages/about/page.abide +9 -0
  559. package/template/src/ui/pages/page.abide +22 -0
  560. package/template/test/app.test.ts +30 -0
  561. package/template/tsconfig.json +18 -0
  562. package/tsconfig.app.json +17 -0
@@ -0,0 +1,13 @@
1
+ /*
2
+ An MCP prompt declared by a markdown file under `src/mcp/prompts/`. The
3
+ resolver plugin parses the file's frontmatter + body and generates a call
4
+ to definePrompt, stamping in the `name` from the file path; `render(args)`
5
+ interpolates the body's `{{name}}` placeholders into the single user
6
+ message returned by `prompts/get`. Prompts are MCP-only — there is no
7
+ client-side counterpart, so the shape carries no ClientFlags.
8
+ */
9
+ export type Prompt = {
10
+ readonly name: string
11
+ readonly description: string | undefined
12
+ render(args: Record<string, string>): string
13
+ }
@@ -0,0 +1,12 @@
1
+ /*
2
+ Options definePrompt receives for one markdown prompt. The resolver plugin
3
+ generates this object from the file: `description` + `jsonSchema` come from
4
+ the frontmatter (the schema built from the `arguments` list), and `render`
5
+ closes over the parsed body. All of this is server-only — prompts are never
6
+ imported by client code.
7
+ */
8
+ export type PromptOptions = {
9
+ description?: string
10
+ jsonSchema?: Record<string, unknown>
11
+ render: (args: Record<string, string>) => string
12
+ }
@@ -0,0 +1,13 @@
1
+ import type { Prompt } from './Prompt.ts'
2
+
3
+ /*
4
+ Per-prompt registry record. The MCP dispatcher enumerates this to build
5
+ `prompts/list` (description + arguments from the JSON Schema) and to
6
+ dispatch `prompts/get` (render the body with the caller's arguments).
7
+ jsonSchema stays off the public Prompt shape so the render closure isn't
8
+ burdened with metadata it never reads.
9
+ */
10
+ export type PromptRegistryEntry = {
11
+ prompt: Prompt
12
+ jsonSchema: Record<string, unknown> | undefined
13
+ }
@@ -0,0 +1,10 @@
1
+ import type { Prompt } from './Prompt.ts'
2
+
3
+ /*
4
+ Manifest of prompt-name → module loader. Produced by the resolver plugin
5
+ from each `.md` under src/mcp/prompts/. Each markdown file is transformed
6
+ into a module that registers one Prompt (its `.name` stamped in by the
7
+ generated definePrompt call) on import. The registry loader imports every
8
+ module once so the MCP dispatcher can enumerate the full prompt surface.
9
+ */
10
+ export type PromptRoutes = Record<string, () => Promise<Record<string, Prompt>>>
@@ -0,0 +1,45 @@
1
+ import { parseBoundedEnvInt } from '../shared/parseBoundedEnvInt.ts'
2
+ import { createReachable } from './runtime/createReachable.ts'
3
+
4
+ /*
5
+ Server-only outbound reachability for an external host. `await reachable(host)`
6
+ HEADs the host's origin: the first call awaits a real probe (faithful — a down
7
+ host costs the full timeout, an up host one handshake) and starts a background
8
+ poll that re-probes every TTL, so every later call resolves instantly off the
9
+ warm value, fresh within one TTL. A down host going down is caught within
10
+ ~failureLimit polls; recovery flips it back automatically.
11
+
12
+ if (!(await reachable('api.example.com'))) return error(503)
13
+
14
+ A bare host defaults to https; pass an explicit http://… for a non-TLS host.
15
+ Answers "can I connect to this host," NOT "is my endpoint healthy": any
16
+ completed HTTP response (even 4xx/5xx, even a 405 to HEAD) counts as reachable;
17
+ only a connection failure or timeout reads as unreachable. There is no ambient
18
+ server-side connectivity signal, so this is the honest way to fail a doomed
19
+ outbound call fast — see online() for the inbound/client-reported counterpart.
20
+
21
+ ABIDE_REACHABLE_TTL (poll cadence / freshness, ms) and ABIDE_REACHABLE_TIMEOUT
22
+ (per-HEAD bound, ms) tune the defaults; the timeout is deliberately generous so
23
+ a healthy-but-distant host over a slow link is not mis-read as down.
24
+ */
25
+ const TTL_MS = parseBoundedEnvInt(process.env.ABIDE_REACHABLE_TTL, 1_000, 600_000) ?? 30_000
26
+ const TIMEOUT_MS = parseBoundedEnvInt(process.env.ABIDE_REACHABLE_TIMEOUT, 100, 60_000) ?? 3_000
27
+ /* Stop polling a host nobody has read in a few TTLs; the next read restarts it cold. */
28
+ const IDLE_MS = TTL_MS * 3
29
+
30
+ /* Status-agnostic HEAD: a completed response proves connectivity; reject/timeout does not. */
31
+ async function probeOrigin(origin: string): Promise<boolean> {
32
+ try {
33
+ await fetch(origin, { method: 'HEAD', signal: AbortSignal.timeout(TIMEOUT_MS) })
34
+ return true
35
+ } catch {
36
+ return false
37
+ }
38
+ }
39
+
40
+ // @readme observability
41
+ export const reachable = createReachable({
42
+ probe: probeOrigin,
43
+ intervalMs: TTL_MS,
44
+ idleMs: IDLE_MS,
45
+ }).reachable
@@ -0,0 +1,43 @@
1
+ /*
2
+ Redirect Response with abide-friendly ergonomics — accepts relative
3
+ URLs (the platform's `Response.redirect` throws on them), defaults to
4
+ 302, and matches the helper-style call site of `json`/`error` for
5
+ visual consistency inside a handler.
6
+
7
+ return redirect('/login') // 302 to /login
8
+ return redirect('/articles/1', 301) // permanent
9
+ return redirect(externalUrl, 307) // preserve method (POST stays POST)
10
+
11
+ Status guidance:
12
+ - 301 — moved permanently (cacheable; browsers may swap method to GET)
13
+ - 302 — found / temporary (default; browsers may swap method to GET)
14
+ - 303 — "after a POST, GET this" (forces GET on the follow-up)
15
+ - 307 — temporary, preserve method
16
+ - 308 — permanent, preserve method
17
+
18
+ A final `ResponseInit` adds headers (e.g. a `Set-Cookie` on the redirect);
19
+ the positional `status` always wins over any `init.status`.
20
+ */
21
+ import { NO_STORE } from '../shared/CACHE_CONTROL_VALUES.ts'
22
+ import type { TypedResponse } from './rpc/types/TypedResponse.ts'
23
+ import { withResponseDefaults } from './runtime/withResponseDefaults.ts'
24
+
25
+ type RedirectStatus = 301 | 302 | 303 | 307 | 308
26
+
27
+ /*
28
+ Return type is `TypedResponse<never>` for the same reason `error()` is —
29
+ the wire response is a 3xx with no body the caller resolves to, so it
30
+ must not pollute the inferred `Return` of a route that conditionally
31
+ redirects vs returns json.
32
+ */
33
+ // @readme response
34
+ export function redirect(
35
+ url: string,
36
+ status: RedirectStatus = 302,
37
+ init?: ResponseInit,
38
+ ): TypedResponse<never> {
39
+ return new Response(
40
+ null,
41
+ withResponseDefaults(init, { Location: url, 'Cache-Control': NO_STORE }, status),
42
+ ) as TypedResponse<never>
43
+ }
@@ -0,0 +1,19 @@
1
+ import { requestContext } from './runtime/requestContext.ts'
2
+
3
+ /*
4
+ Returns the inbound Request for the current SSR/RPC pass. Implemented as an
5
+ AsyncLocalStorage lookup over the per-request store the server installs at
6
+ the fetch boundary. Throws if called outside a request scope (e.g. from
7
+ top-level module code or from app.ts init) — silent undefined would mask
8
+ the misuse.
9
+ */
10
+ // @readme request-scope
11
+ export function request(): Request {
12
+ const store = requestContext.getStore()
13
+ if (!store) {
14
+ throw new Error(
15
+ '[abide] request() called outside a request scope — it only resolves while an SSR render or rpc handler is in flight',
16
+ )
17
+ }
18
+ return store.req
19
+ }
@@ -0,0 +1,210 @@
1
+ import { abideLog } from '../../shared/abideLog.ts'
2
+ import { buildRpcRequest } from '../../shared/buildRpcRequest.ts'
3
+ import { createRemoteFunction } from '../../shared/createRemoteFunction.ts'
4
+ import { forwardHeaders } from '../../shared/forwardHeaders.ts'
5
+ import { isReadOnlyMethod } from '../../shared/isReadOnlyMethod.ts'
6
+ import { resolveClientFlags } from '../../shared/resolveClientFlags.ts'
7
+ import type { ClientFlags } from '../../shared/types/ClientFlags.ts'
8
+ import type { HttpVerb } from '../../shared/types/HttpVerb.ts'
9
+ import type { RemoteFunction } from '../../shared/types/RemoteFunction.ts'
10
+ import type { StandardSchemaV1 } from '../../shared/types/StandardSchemaV1.ts'
11
+ import { json } from '../json.ts'
12
+ import { requestContext } from '../runtime/requestContext.ts'
13
+ import { parseArgs } from './parseArgs.ts'
14
+ import { registerVerb } from './registerVerb.ts'
15
+ import { runWithVerbTimeout } from './runWithVerbTimeout.ts'
16
+ import type { RemoteHandler } from './types/RemoteHandler.ts'
17
+
18
+ /*
19
+ Stash for the per-request AbortController a timed verb composes into the
20
+ inbound signal — read back in invoke's deadline callback to fire it. Lives on
21
+ the scope's Request (one verb per .fetch — network or in-process dispatch) so
22
+ an SSR pass's many in-process cache reads, which call invoke() directly and
23
+ never reach parseArgsForFetch, can't cross-cancel.
24
+ */
25
+ const VERB_TIMEOUT_ABORT = Symbol('abideVerbTimeoutAbort')
26
+
27
+ /* Verb dispatch + validation spans, opt-in via DEBUG=abide:rpc. Reveals an
28
+ in-process RPC→RPC call (same request scope, same trace) as a nested span. */
29
+ const rpcLog = abideLog.channel('abide:rpc')
30
+
31
+ /*
32
+ Builds a RemoteFunction from an HTTP verb + RPC URL + handler. The bundler
33
+ rewrites every `export const VERB = handler(fn)` inside an `$rpc/**` module
34
+ so the verb (from the export name) and the URL (from the file path under
35
+ `src/server/rpc/`, with `/rpc/` prefix) are threaded into defineVerb.
36
+
37
+ The plain call (`fn(args)`) resolves to the Content-Type-decoded body;
38
+ non-2xx responses throw HttpError. `.raw(args)` returns the underlying
39
+ Response for callers that need status/headers/body streaming.
40
+ `.fetch(req)` is the dispatch hook the framework's router uses to
41
+ invoke the handler from an incoming HTTP request (with args parsed off
42
+ the Request via parseArgs).
43
+
44
+ Every raw invocation records the synthesized Request against the returned
45
+ promise so cache() can stash it on the entry without re-building.
46
+ */
47
+ // @readme plumbing
48
+ export function defineVerb<Args, Return>(
49
+ method: HttpVerb,
50
+ url: string,
51
+ handler: RemoteHandler<Args, Return>,
52
+ opts?: {
53
+ inputSchema?: StandardSchemaV1
54
+ outputSchema?: StandardSchemaV1
55
+ filesSchema?: StandardSchemaV1
56
+ clients?: Partial<ClientFlags>
57
+ crossOrigin?: boolean
58
+ /* Per-verb cap on actual received body bytes (413 past it); omitted = Bun's server-wide maxRequestBodySize. */
59
+ maxBodySize?: number
60
+ /* Per-verb handler deadline (ms): a 504 once exceeded, on every surface (SSR/MCP/CLI/network). */
61
+ timeout?: number
62
+ },
63
+ ): RemoteFunction<Args, Return> {
64
+ const timeout = opts?.timeout
65
+ const inputSchema = opts?.inputSchema
66
+ const outputSchema = opts?.outputSchema
67
+ const filesSchema = opts?.filesSchema
68
+ /*
69
+ An input schema makes the handler safe to advertise to non-browser
70
+ surfaces. CLI flips on for any verb with one (a human/script invokes it
71
+ deliberately). MCP only auto-exposes read-only verbs (GET/HEAD) — a
72
+ model shouldn't be able to mutate/delete just because the handler
73
+ carries a schema, so mutating verbs require an explicit clients.mcp.
74
+ Explicit `clients` always wins.
75
+ */
76
+ const hasSchema = inputSchema !== undefined
77
+ const clients = resolveClientFlags(opts?.clients, {
78
+ mcp: hasSchema && isReadOnlyMethod(method),
79
+ cli: hasSchema,
80
+ })
81
+
82
+ function buildRequest(args: Args | undefined): Request {
83
+ const store = requestContext.getStore()
84
+ const baseUrl = store ? store.url.href : 'http://localhost/'
85
+ const headers = store ? forwardHeaders(store.req.headers) : new Headers()
86
+ return buildRpcRequest({ method, url, args, baseUrl, headers })
87
+ }
88
+
89
+ /*
90
+ Handler bodies may throw synchronously (e.g. an `assert(...)` at the
91
+ top of the function). The `async function` wrapper coerces both sync
92
+ throws and returned non-promises into the Promise<Response> shape
93
+ callers expect, so an SSR caller's `await` always sees the rejection
94
+ through the cache layer's snapshot boundary instead of the error
95
+ escaping the request scope.
96
+ */
97
+ async function runHandler(args: Args | undefined): Promise<Response> {
98
+ return rpcLog.trace(
99
+ `rpc ${method} ${url}`,
100
+ () => handler(args as Args) as unknown as Response,
101
+ )
102
+ }
103
+
104
+ /*
105
+ Validates the parsed args against inputSchema (text fields), then — when the
106
+ verb declares filesSchema — validates the File parts parseArgs split onto
107
+ the request store and merges them into the args bag the handler receives.
108
+ Either schema's issues become a 422. Files stay out of inputSchema so its
109
+ JSON-Schema projection (OpenAPI/MCP/CLI) never has to model a binary.
110
+ */
111
+ async function validateThenHandle(args: Args | undefined): Promise<Response> {
112
+ let value: unknown = args
113
+ if (inputSchema) {
114
+ const result = await rpcLog.trace(`validate ${url}`, () =>
115
+ inputSchema['~standard'].validate(value),
116
+ )
117
+ if (result.issues) {
118
+ return json({ issues: result.issues }, { status: 422 })
119
+ }
120
+ value = result.value
121
+ }
122
+ if (filesSchema) {
123
+ const files = requestContext.getStore()?.files ?? {}
124
+ const result = await filesSchema['~standard'].validate(files)
125
+ if (result.issues) {
126
+ return json({ issues: result.issues }, { status: 422 })
127
+ }
128
+ value = { ...(value as object), ...(result.value as object) }
129
+ }
130
+ return runHandler(value as Args)
131
+ }
132
+
133
+ /*
134
+ `getRequest` is unused on the server path — handlers receive parsed
135
+ `args` directly and reach the inbound Request via `request()`.
136
+ createRemoteFunction passes a thunk so the client side can lazily
137
+ synthesize its Request without forcing the server to allocate one per
138
+ SSR call.
139
+ */
140
+ /* Abort the controller parseArgsForFetch stashed on store.req; a no-op when none was stashed (SSR cache reads). */
141
+ function abortVerbTimeout(): void {
142
+ const req = requestContext.getStore()?.req as
143
+ | (Request & { [VERB_TIMEOUT_ABORT]?: AbortController })
144
+ | undefined
145
+ req?.[VERB_TIMEOUT_ABORT]?.abort(new DOMException('handler timeout', 'TimeoutError'))
146
+ }
147
+
148
+ function invoke(args: Args | undefined): Promise<Response> {
149
+ const work = inputSchema || filesSchema ? validateThenHandle(args) : runHandler(args)
150
+ if (timeout === undefined) {
151
+ return work
152
+ }
153
+ /*
154
+ On the deadline, fire the controller parseArgsForFetch composed into
155
+ request().signal (absent on the SSR cache-read path, so a sibling
156
+ verb's outbound fetch is never cancelled) — then 504.
157
+ */
158
+ return runWithVerbTimeout(work, timeout, abortVerbTimeout)
159
+ }
160
+
161
+ const remote = createRemoteFunction<Args, Return>({
162
+ method,
163
+ url,
164
+ clients,
165
+ crossOrigin: opts?.crossOrigin,
166
+ buildRequest,
167
+ invoke,
168
+ parseArgsForFetch: async (request) => {
169
+ const args = await parseArgs(method, request, opts?.maxBodySize)
170
+ /*
171
+ Compose this verb's deadline into request().signal so a handler's
172
+ fetch(ext, { signal: request().signal }) is cancelled when the
173
+ timeout fires — not just abandoned. Applied after parseArgs onto the
174
+ scope's *final* request: a maxBodySize verb swaps store.req for a
175
+ buffered copy (readBodyWithinLimit) and an app.handle hook may
176
+ rewrite it, so composing onto the inbound `request` would leave
177
+ request() — and abortVerbTimeout, which reads store.req — pointed at
178
+ an un-cancellable signal. Only the signal is shadowed; the body
179
+ stays readable. The store always exists here (network + in-process
180
+ dispatch both run inside runWithRequestScope); SSR cache reads call
181
+ invoke() directly, never this path, so a sibling verb is never
182
+ cross-cancelled.
183
+ */
184
+ if (timeout !== undefined) {
185
+ const req = requestContext.getStore()?.req
186
+ if (req) {
187
+ const controller = new AbortController()
188
+ const composed = AbortSignal.any([req.signal, controller.signal])
189
+ Object.defineProperty(req, 'signal', { value: composed, configurable: true })
190
+ Object.defineProperty(req, VERB_TIMEOUT_ABORT, {
191
+ value: controller,
192
+ configurable: true,
193
+ })
194
+ }
195
+ }
196
+ return args as Args | undefined
197
+ },
198
+ })
199
+ registerVerb({
200
+ remote: remote as RemoteFunction<unknown, unknown>,
201
+ inputSchema,
202
+ outputSchema,
203
+ filesSchema,
204
+ clients,
205
+ timeout,
206
+ maxBodySize: opts?.maxBodySize,
207
+ crossOrigin: opts?.crossOrigin,
208
+ })
209
+ return remote
210
+ }
@@ -0,0 +1,46 @@
1
+ import { buildRpcRequest } from '../../shared/buildRpcRequest.ts'
2
+ import type { RemoteFunction } from '../../shared/types/RemoteFunction.ts'
3
+ import type { AppModule } from '../AppModule.ts'
4
+ import { runWithRequestScope } from '../runtime/runWithRequestScope.ts'
5
+
6
+ /*
7
+ Runs a verb in-process: synthesizes the rpc Request from the remote's own
8
+ method + url and pipes it through remote.fetch — the same handler/validation/
9
+ error path the HTTP router uses, no network hop. The single in-process
10
+ dispatch every consumer surface (the CLI client, the MCP tool dispatcher, and
11
+ the test client) routes through, so they can't drift on how a verb is invoked.
12
+ Takes the RemoteFunction directly — invocation never reads the registry
13
+ entry's schemas/clients (validation is closed over inside the remote), so the
14
+ entry is not a dependency here. `baseUrl` gives the synthetic Request its
15
+ origin (handlers reading request.url see the caller's host); `headers` carries
16
+ forwarded auth/identity context.
17
+
18
+ Runs inside the runWithRequestScope seam createServer crosses for real
19
+ requests, so a handler sees an identical scope to a live HTTP request: a fresh
20
+ per-request cache, the cookie jar with Set-Cookie flush, request()/server()
21
+ resolution, and the app's handleError (or the 500 fallback) on a throw. The
22
+ synthesized Request is shared between the scope store and the handler fetch so
23
+ request() returns the same Request parseArgs read from.
24
+ */
25
+ export function dispatchVerbInProcess({
26
+ remote,
27
+ args,
28
+ baseUrl,
29
+ headers,
30
+ app,
31
+ }: {
32
+ remote: RemoteFunction<unknown, unknown>
33
+ args: unknown
34
+ baseUrl: string
35
+ headers?: Headers
36
+ app?: AppModule
37
+ }): Promise<Response> {
38
+ const request = buildRpcRequest({
39
+ method: remote.method,
40
+ url: remote.url,
41
+ args,
42
+ baseUrl,
43
+ headers,
44
+ })
45
+ return runWithRequestScope(request, { app, logRequests: false }, () => remote.fetch(request))
46
+ }
@@ -0,0 +1,18 @@
1
+ import { commandNameForUrl } from '../../shared/commandNameForUrl.ts'
2
+ import type { VerbRegistryEntry } from './types/VerbRegistryEntry.ts'
3
+ import { verbRegistry } from './verbRegistry.ts'
4
+
5
+ /*
6
+ Finds the registered verb whose URL maps to a given command name (folder
7
+ segments joined with `-`, per commandNameForUrl). The CLI client proxy and
8
+ the MCP tool dispatcher both key off this name, so the scan lives here once
9
+ rather than being re-implemented — and reused — at each call site.
10
+ */
11
+ export function findVerbByCommandName(name: string): VerbRegistryEntry | undefined {
12
+ for (const entry of verbRegistry.values()) {
13
+ if (commandNameForUrl(entry.remote.url) === name) {
14
+ return entry
15
+ }
16
+ }
17
+ return undefined
18
+ }
@@ -0,0 +1,127 @@
1
+ import { carriesBodyArgs } from '../../shared/carriesBodyArgs.ts'
2
+ import { HttpError } from '../../shared/HttpError.ts'
3
+ import type { HttpVerb } from '../../shared/types/HttpVerb.ts'
4
+ import { error } from '../error.ts'
5
+ import { requestContext } from '../runtime/requestContext.ts'
6
+ import { readBodyWithinLimit } from './readBodyWithinLimit.ts'
7
+
8
+ /*
9
+ Splits a parsed FormData into the text fields that become args and the File
10
+ parts that don't. Repeated text keys collapse into an array (an HTML form posts
11
+ multiple same-named inputs); File parts group by field name and stash on the
12
+ request store for files() to read — they never enter args, so the input schema
13
+ keeps validating a plain object with no binary in it.
14
+ */
15
+ function splitFormData(form: FormData): Record<string, unknown> {
16
+ const fileMap: Record<string, File[]> = {}
17
+ const fields: Record<string, unknown> = {}
18
+ for (const [key, value] of form) {
19
+ if (value instanceof File) {
20
+ fileMap[key] ??= []
21
+ fileMap[key].push(value)
22
+ continue
23
+ }
24
+ const existing = fields[key]
25
+ if (!(key in fields)) {
26
+ fields[key] = value
27
+ } else if (Array.isArray(existing)) {
28
+ existing.push(value)
29
+ } else {
30
+ fields[key] = [existing, value]
31
+ }
32
+ }
33
+ const store = requestContext.getStore()
34
+ if (store && Object.keys(fileMap).length > 0) {
35
+ store.files = fileMap
36
+ }
37
+ return fields
38
+ }
39
+
40
+ /*
41
+ Parses + merges every source of args available for a verb-defined handler:
42
+ - body (json or form-encoded, ignored for GET/DELETE/HEAD)
43
+ - url query string
44
+
45
+ When both are present and the body is a plain object, the merge layers the
46
+ body on top of the query so the typed body wins on collision — the query
47
+ supplies defaults a body field can override, and a URL param can't silently
48
+ shadow a validated body value. A non-object body (array, primitive, null)
49
+ skips the merge entirely and is returned as-is — there's no key on the body
50
+ to layer the query into, and the framework's args type is a single bag rather
51
+ than a `{body, query}` envelope. Returns undefined when no source contributes
52
+ any key.
53
+
54
+ `maxBodySize` (per-verb, opt-in) bounds the body's actual received bytes
55
+ before any parse — see readBodyWithinLimit. Omitted = no abide-level check;
56
+ Bun.serve's server-wide maxRequestBodySize is the ceiling.
57
+ */
58
+ export async function parseArgs(
59
+ method: HttpVerb,
60
+ request: Request,
61
+ maxBodySize?: number,
62
+ ): Promise<unknown> {
63
+ /*
64
+ Skip the URL parse entirely when the raw request URL has no query —
65
+ typical POST/PUT/PATCH calls land here with a flat rpc URL and no
66
+ `?…`, so the `new URL(...)` constructor cost (which dwarfs the
67
+ indexOf check) is wasted work.
68
+ */
69
+ const queryStart = request.url.indexOf('?')
70
+ const hasQuery = queryStart !== -1
71
+ const url = hasQuery ? new URL(request.url) : undefined
72
+
73
+ let body: unknown
74
+ if (carriesBodyArgs(method)) {
75
+ let bounded = request
76
+ if (maxBodySize !== undefined) {
77
+ bounded = await readBodyWithinLimit(request, maxBodySize)
78
+ /*
79
+ The size check drained the original body, so point the scope's
80
+ request at the readable copy — a handler with a content-type this
81
+ parse skips (raw uploads) reads the body via request() itself, and
82
+ it must see the bytes, not 'Body already used'.
83
+ */
84
+ const store = requestContext.getStore()
85
+ if (store) {
86
+ store.req = bounded
87
+ }
88
+ }
89
+ const contentType = (bounded.headers.get('content-type') ?? '').toLowerCase()
90
+ try {
91
+ if (contentType.includes('application/json')) {
92
+ const text = await bounded.text()
93
+ if (text !== '') {
94
+ body = JSON.parse(text)
95
+ }
96
+ } else if (
97
+ contentType.includes('application/x-www-form-urlencoded') ||
98
+ contentType.includes('multipart/form-data')
99
+ ) {
100
+ body = splitFormData(await bounded.formData())
101
+ }
102
+ } catch {
103
+ /* A malformed body is the client's fault — surface a 400, not the 500 a
104
+ raw JSON.parse/formData throw would bubble to. createRemoteFunction.fetch
105
+ unwraps the HttpError's response onto the wire (as it does for the 413). */
106
+ throw new HttpError(error(400, 'Malformed request body'))
107
+ }
108
+ }
109
+
110
+ if (body !== undefined && (typeof body !== 'object' || body === null || Array.isArray(body))) {
111
+ return body
112
+ }
113
+
114
+ if (!url) {
115
+ if (body === undefined) {
116
+ return undefined
117
+ }
118
+ return body
119
+ }
120
+
121
+ const bodyObject = (body ?? {}) as Record<string, unknown>
122
+ const merged = { ...Object.fromEntries(url.searchParams), ...bodyObject }
123
+ if (Object.keys(merged).length === 0) {
124
+ return undefined
125
+ }
126
+ return merged
127
+ }
@@ -0,0 +1,44 @@
1
+ import { HttpError } from '../../shared/HttpError.ts'
2
+ import { error } from '../error.ts'
3
+
4
+ /*
5
+ Enforces a verb's maxBodySize on the actual received bytes, not the
6
+ Content-Length header (absent on chunked bodies, trivially spoofed). A
7
+ declared Content-Length over the limit rejects before reading anything;
8
+ otherwise the body streams into a buffer that throws 413 the moment the
9
+ limit is crossed — aborting the read, so the remaining bytes are never
10
+ consumed. Returns a reassembled Request carrying the buffered body and the
11
+ original headers, so the parse path's .text()/.formData() work unchanged.
12
+ Only called when the verb declares maxBodySize; the default ceiling is
13
+ Bun.serve's server-wide maxRequestBodySize, which Bun enforces natively.
14
+ */
15
+ export async function readBodyWithinLimit(request: Request, maxBytes: number): Promise<Request> {
16
+ const tooLarge = () =>
17
+ new HttpError(error(413, `request body exceeds maxBodySize (${maxBytes} bytes)`))
18
+ const declared = Number(request.headers.get('content-length') ?? Number.NaN)
19
+ if (Number.isFinite(declared) && declared > maxBytes) {
20
+ throw tooLarge()
21
+ }
22
+ if (!request.body) {
23
+ return request
24
+ }
25
+ const chunks: Uint8Array[] = []
26
+ let total = 0
27
+ for await (const chunk of request.body) {
28
+ total += chunk.byteLength
29
+ if (total > maxBytes) {
30
+ throw tooLarge()
31
+ }
32
+ chunks.push(chunk)
33
+ }
34
+ const buffered = new Uint8Array(total)
35
+ chunks.reduce((offset, chunk) => {
36
+ buffered.set(chunk, offset)
37
+ return offset + chunk.byteLength
38
+ }, 0)
39
+ return new Request(request.url, {
40
+ method: request.method,
41
+ headers: request.headers,
42
+ body: buffered,
43
+ })
44
+ }
@@ -0,0 +1,6 @@
1
+ import type { VerbRegistryEntry } from './types/VerbRegistryEntry.ts'
2
+ import { verbRegistry } from './verbRegistry.ts'
3
+
4
+ export function registerVerb(entry: VerbRegistryEntry): void {
5
+ verbRegistry.set(entry.remote.url, entry)
6
+ }
@@ -0,0 +1,49 @@
1
+ import { error } from '../error.ts'
2
+
3
+ /*
4
+ Bounds a verb handler's execution: races the in-flight `work` against `ms`.
5
+ On the deadline it resolves a 504 — so the caller (an SSR cache read, an
6
+ MCP/CLI invocation, or the network response) is unblocked in time with an
7
+ honest status — and calls `onTimeout` to cancel any
8
+ cooperating outbound work (the network path composes the verb's deadline into
9
+ request().signal; see defineVerb).
10
+
11
+ `work` keeps running after the deadline — JS can't cancel a running async
12
+ function, only stop awaiting it — so its eventual settlement is swallowed to
13
+ avoid an unhandled rejection, and a late-resolved streaming Response has its
14
+ body cancelled to release the underlying source. A handler that wants its own
15
+ outbound I/O torn down should pass request().signal to it.
16
+ */
17
+ export function runWithVerbTimeout(
18
+ work: Promise<Response>,
19
+ ms: number,
20
+ onTimeout: () => void,
21
+ ): Promise<Response> {
22
+ let timer: ReturnType<typeof setTimeout> | undefined
23
+ let timedOut = false
24
+ const deadline = new Promise<Response>((resolve) => {
25
+ timer = setTimeout(() => {
26
+ timedOut = true
27
+ onTimeout()
28
+ resolve(error(504, 'handler timeout'))
29
+ }, ms)
30
+ })
31
+ return (async () => {
32
+ try {
33
+ return await Promise.race([work, deadline])
34
+ } finally {
35
+ clearTimeout(timer)
36
+ if (timedOut) {
37
+ // The race already returned a 504. Swallow a late rejection, and
38
+ // cancel a late-resolved streaming Response's body so its source
39
+ // (DB cursor, file handle) is released rather than leaked.
40
+ void work.then(
41
+ (late) => {
42
+ void late.body?.cancel()
43
+ },
44
+ () => {},
45
+ )
46
+ }
47
+ }
48
+ })()
49
+ }