@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,337 @@
1
+ import type { ServerWebSocket } from 'bun'
2
+ import { abideLog } from '../../shared/abideLog.ts'
3
+ import { memoizeByKey } from '../../shared/memoizeByKey.ts'
4
+ import { error } from '../error.ts'
5
+ import { json } from '../json.ts'
6
+ import { sse } from '../sse.ts'
7
+ import { lookupSocket } from './lookupSocket.ts'
8
+ import type { SocketClientFrame } from './types/SocketClientFrame.ts'
9
+ import type { SocketRegistryEntry } from './types/SocketRegistryEntry.ts'
10
+ import type { SocketRoutes } from './types/SocketRoutes.ts'
11
+ import type { SocketServerFrame } from './types/SocketServerFrame.ts'
12
+
13
+ // Reused across every inbound binary frame rather than allocated per message.
14
+ const textDecoder = new TextDecoder()
15
+
16
+ /* Socket REST-face spans (CLI/MCP tail + publish), opt-in via DEBUG=abide:sockets. */
17
+ const socketsLog = abideLog.channel('abide:sockets')
18
+
19
+ type SocketDispatcher = {
20
+ open(ws: ServerWebSocket<unknown>): void
21
+ message(ws: ServerWebSocket<unknown>, data: string | Buffer): void
22
+ close(ws: ServerWebSocket<unknown>): void
23
+ rest(req: Request, name: string): Promise<Response>
24
+ }
25
+
26
+ /*
27
+ Per-connection state: which sockets this ws is currently subscribed to
28
+ (at the Bun-topic level), and which `sub` ids map to which socket. One
29
+ ws can hold multiple subs against the same socket (e.g. one seeded
30
+ from the retained tail, one live-only); the Bun-topic subscription is reference-counted
31
+ so we only `ws.unsubscribe` when the last local sub drops.
32
+ */
33
+ type ConnectionState = {
34
+ subToSocket: Map<string, string>
35
+ socketSubs: Map<string, Set<string>>
36
+ }
37
+
38
+ /*
39
+ Bridges the framework's socket registry to a single ws per client. All
40
+ sockets multiplex over `/__abide/sockets`. Steady-state fan-out rides
41
+ Bun's native `server.publish('socket:<name>', frame)` so the dispatcher
42
+ is only on the path for sub/unsub bookkeeping and client-initiated pub
43
+ validation; the published `msg` frames go from publisher to subscribers
44
+ without touching JS per frame.
45
+
46
+ `sub` opens a subscription: the requested slice of the retained tail
47
+ is replayed directly to this ws, then the ws is added to the Bun
48
+ topic. `unsub` drops the local sub and unsubscribes the ws from
49
+ the Bun topic if no other local subs remain. `pub` validates the
50
+ socket's `allowClientPublish` policy and calls `socket.publish` —
51
+ which fans out to in-process iterators and republishes through Bun
52
+ to other connected clients.
53
+
54
+ Module-level lookups are cached per socket name: loading a socket
55
+ module triggers its `defineSocket` call, which inserts into the
56
+ registry. After that the dispatcher just reads the registry.
57
+ */
58
+ export function createSocketDispatcher(sockets: SocketRoutes): SocketDispatcher {
59
+ const connections = new WeakMap<ServerWebSocket<unknown>, ConnectionState>()
60
+
61
+ const ensureLoaded = memoizeByKey((name): Promise<void> | undefined => {
62
+ const loader = sockets[name]
63
+ return loader ? loader().then(() => undefined) : undefined
64
+ })
65
+
66
+ /*
67
+ Loads a socket module on first use and reads its registry entry — the
68
+ one statement of the load/lookup failure semantics (a throwing module is
69
+ logged here and stays cached as failed by ensureLoaded). Each face maps
70
+ the failure kind to its own rendering: sub emits err+end frames, pub
71
+ drops silently, rest answers with HTTP errors.
72
+ */
73
+ async function resolveEntry(
74
+ name: string,
75
+ ): Promise<
76
+ | { entry: SocketRegistryEntry }
77
+ | { failure: 'unregistered' | 'load-failed' | 'no-export'; message: string }
78
+ > {
79
+ const loader = ensureLoaded(name)
80
+ if (!loader) {
81
+ return { failure: 'unregistered', message: `[abide] no socket registered at ${name}` }
82
+ }
83
+ try {
84
+ await loader
85
+ } catch (loadError) {
86
+ abideLog.error(loadError)
87
+ return {
88
+ failure: 'load-failed',
89
+ message: loadError instanceof Error ? loadError.message : String(loadError),
90
+ }
91
+ }
92
+ const entry = lookupSocket(name)
93
+ if (!entry) {
94
+ return {
95
+ failure: 'no-export',
96
+ message: `[abide] socket module at ${name} did not register a Socket export`,
97
+ }
98
+ }
99
+ return { entry }
100
+ }
101
+
102
+ function send(ws: ServerWebSocket<unknown>, frame: SocketServerFrame): void {
103
+ if (ws.readyState !== WebSocket.OPEN) {
104
+ return
105
+ }
106
+ ws.send(JSON.stringify(frame))
107
+ }
108
+
109
+ function addSub(state: ConnectionState, name: string, sub: string): boolean {
110
+ state.subToSocket.set(sub, name)
111
+ let subs = state.socketSubs.get(name)
112
+ if (!subs) {
113
+ subs = new Set()
114
+ state.socketSubs.set(name, subs)
115
+ }
116
+ const wasEmpty = subs.size === 0
117
+ subs.add(sub)
118
+ return wasEmpty
119
+ }
120
+
121
+ function removeSub(state: ConnectionState, sub: string): string | undefined {
122
+ const name = state.subToSocket.get(sub)
123
+ if (!name) {
124
+ return undefined
125
+ }
126
+ state.subToSocket.delete(sub)
127
+ const subs = state.socketSubs.get(name)
128
+ if (!subs) {
129
+ return undefined
130
+ }
131
+ subs.delete(sub)
132
+ if (subs.size === 0) {
133
+ state.socketSubs.delete(name)
134
+ return name
135
+ }
136
+ return undefined
137
+ }
138
+
139
+ async function handleSub(
140
+ ws: ServerWebSocket<unknown>,
141
+ state: ConnectionState,
142
+ frame: Extract<SocketClientFrame, { type: 'sub' }>,
143
+ ): Promise<void> {
144
+ // Reject this sub: emit the error then the terminal end frame for its id.
145
+ function fail(message: string): void {
146
+ send(ws, { type: 'err', sub: frame.sub, message })
147
+ send(ws, { type: 'end', sub: frame.sub })
148
+ }
149
+ const resolution = await resolveEntry(frame.socket)
150
+ if ('failure' in resolution) {
151
+ return fail(resolution.message)
152
+ }
153
+ const { entry } = resolution
154
+ const isFirstLocalSub = addSub(state, frame.socket, frame.sub)
155
+ if (isFirstLocalSub) {
156
+ ws.subscribe(`socket:${frame.socket}`)
157
+ }
158
+ /*
159
+ Replay rides one per-sub `replay` frame (sent even when empty), not
160
+ socket-keyed msg frames: the batch demarcates replay from live for
161
+ the reader, and the per-sub address keeps this sub's replay out of
162
+ sibling subs on the same socket. Live messages flow through the Bun
163
+ topic the ws just joined; a frame published between the join and
164
+ this snapshot can appear in both, so user payloads should carry an
165
+ id/timestamp when ordering matters.
166
+
167
+ `replay === undefined` means the whole retained tail (`.tail()`
168
+ no-arg); a number is clamped to the buffer length so the client
169
+ can ask for "as many as available, up to N" — `0` (bare
170
+ `for await`) replays nothing.
171
+ */
172
+ send(ws, { type: 'replay', sub: frame.sub, messages: entry.snapshotTail(frame.replay) })
173
+ }
174
+
175
+ function handleUnsub(
176
+ ws: ServerWebSocket<unknown>,
177
+ state: ConnectionState,
178
+ frame: Extract<SocketClientFrame, { type: 'unsub' }>,
179
+ ): void {
180
+ const emptied = removeSub(state, frame.sub)
181
+ if (emptied) {
182
+ ws.unsubscribe(`socket:${emptied}`)
183
+ }
184
+ send(ws, { type: 'end', sub: frame.sub })
185
+ }
186
+
187
+ async function handlePub(
188
+ ws: ServerWebSocket<unknown>,
189
+ frame: Extract<SocketClientFrame, { type: 'pub' }>,
190
+ ): Promise<void> {
191
+ const resolution = await resolveEntry(frame.socket)
192
+ if ('failure' in resolution) {
193
+ return
194
+ }
195
+ const { entry } = resolution
196
+ if (!entry.allowClientPublish) {
197
+ /*
198
+ Silent drop: the publish is rejected because the topic
199
+ wasn't declared `{ clientPublish: true }`. Surfacing this as
200
+ an error per-publish would tempt apps to attempt-then-handle
201
+ instead of routing through an HTTP route for auth. Log it
202
+ once per process at debug level (out of scope here) if
203
+ visibility is needed.
204
+ */
205
+ return
206
+ }
207
+ /*
208
+ publish() runs the topic's optional Standard Schema synchronously
209
+ and throws on failure (see defineSocket.validateSync). The
210
+ dispatcher invokes us via `void handlePub(...)`, so an unhandled
211
+ throw would surface as an unhandled promise rejection on every
212
+ malformed client frame. Catch + log so a buggy client can't take
213
+ the process down.
214
+ */
215
+ try {
216
+ entry.socket.publish(frame.message)
217
+ } catch (error) {
218
+ abideLog.error(error)
219
+ }
220
+ /*
221
+ ws parameter retained for future per-ws auth context (cookies on
222
+ upgrade) the canPublish hook would consult.
223
+ */
224
+ void ws
225
+ }
226
+
227
+ /*
228
+ HTTP face of the sockets hub at `/__abide/sockets/<name>`, for the CLI
229
+ and MCP (which can't speak the ws multiplex protocol):
230
+
231
+ GET text/event-stream → live SSE stream; `?tail=N` seeds with the
232
+ last N retained frames before tailing live (default 0 = live only).
233
+ GET otherwise → JSON array of the retained tail
234
+ (`?tail=N` caps it; default all).
235
+ POST → publish the JSON body, gated by the socket's
236
+ clientPublish policy and validated against its schema.
237
+
238
+ Loads the socket module on first hit (same cache the ws path uses) so
239
+ its defineSocket call populates the registry.
240
+ */
241
+ async function rest(req: Request, name: string): Promise<Response> {
242
+ return socketsLog.trace(`socket-rest ${name}`, () => restImpl(req, name))
243
+ }
244
+
245
+ async function restImpl(req: Request, name: string): Promise<Response> {
246
+ const resolution = await resolveEntry(name)
247
+ if ('failure' in resolution) {
248
+ return resolution.failure === 'load-failed'
249
+ ? error(500, 'socket failed to load')
250
+ : error(404)
251
+ }
252
+ const { entry } = resolution
253
+ const tailParam = new URL(req.url).searchParams.get('tail')
254
+ const parsedTail = tailParam !== null ? Number(tailParam) : undefined
255
+ // A non-numeric ?tail= yields NaN; treat it as absent rather than letting
256
+ // snapshotTail(NaN)/slice(NaN) leak the whole retained buffer.
257
+ const count =
258
+ parsedTail !== undefined && Number.isFinite(parsedTail) ? parsedTail : undefined
259
+ if (req.method === 'GET' || req.method === 'HEAD') {
260
+ if ((req.headers.get('accept') ?? '').includes('text/event-stream')) {
261
+ return sse(entry.socket.tail(count ?? 0))
262
+ }
263
+ return json(entry.snapshotTail(count))
264
+ }
265
+ if (req.method === 'POST') {
266
+ if (!entry.allowClientPublish) {
267
+ return error(403, 'publishing not allowed')
268
+ }
269
+ let message: unknown
270
+ try {
271
+ message = await req.json()
272
+ } catch {
273
+ return error(400, 'body must be JSON')
274
+ }
275
+ try {
276
+ // publish() validates against the socket schema and throws on a bad payload.
277
+ entry.socket.publish(message)
278
+ } catch (publishError) {
279
+ return error(
280
+ 422,
281
+ publishError instanceof Error ? publishError.message : String(publishError),
282
+ )
283
+ }
284
+ return json({ ok: true })
285
+ }
286
+ return error(405, undefined, { headers: { Allow: 'GET, POST' } })
287
+ }
288
+
289
+ return {
290
+ rest,
291
+
292
+ open(ws) {
293
+ connections.set(ws, { subToSocket: new Map(), socketSubs: new Map() })
294
+ },
295
+
296
+ message(ws, data) {
297
+ const state = connections.get(ws)
298
+ if (!state) {
299
+ return
300
+ }
301
+ const text = typeof data === 'string' ? data : textDecoder.decode(data)
302
+ let frame: SocketClientFrame
303
+ try {
304
+ frame = JSON.parse(text) as SocketClientFrame
305
+ } catch {
306
+ return
307
+ }
308
+ // JSON.parse succeeds on `null`/`42`/`true`; ignore anything that isn't a frame object.
309
+ if (typeof frame !== 'object' || frame === null) {
310
+ return
311
+ }
312
+ if (frame.type === 'sub') {
313
+ void handleSub(ws, state, frame)
314
+ return
315
+ }
316
+ if (frame.type === 'unsub') {
317
+ handleUnsub(ws, state, frame)
318
+ return
319
+ }
320
+ if (frame.type === 'pub') {
321
+ void handlePub(ws, frame)
322
+ return
323
+ }
324
+ },
325
+
326
+ close(ws) {
327
+ const state = connections.get(ws)
328
+ if (!state) {
329
+ return
330
+ }
331
+ connections.delete(ws)
332
+ for (const name of state.socketSubs.keys()) {
333
+ ws.unsubscribe(`socket:${name}`)
334
+ }
335
+ },
336
+ }
337
+ }
@@ -0,0 +1,179 @@
1
+ import { createPushIterator } from '../../shared/createPushIterator.ts'
2
+ import { resolveClientFlags } from '../../shared/resolveClientFlags.ts'
3
+ import { socketTapSlot } from '../../shared/socketTapSlot.ts'
4
+ import type { TailHooks } from '../../shared/types/TailHooks.ts'
5
+ import { getActiveServer } from '../runtime/getActiveServer.ts'
6
+ import { registerSocket } from './registerSocket.ts'
7
+ import type { Socket } from './types/Socket.ts'
8
+ import type { SocketOptions } from './types/SocketOptions.ts'
9
+
10
+ /*
11
+ Server-side construction of a Socket. The bundler rewrites every
12
+ `export const NAME = socket(opts)` inside `src/server/sockets/<file>.ts` into
13
+ `__abideDefineSocket__("<name>", opts)` so the file path becomes the
14
+ socket's identity. Each subscriber gets its own queue + notifier, the
15
+ optional retained tail is shared, and outbound fan-out rides Bun's
16
+ native `server.publish` so connected ws clients are notified by the
17
+ runtime in C rather than per-client iteration in JS.
18
+
19
+ The Socket itself is the AsyncIterable: `for await (const m of chat)`
20
+ is the live stream — no replay. `chat.tail(count)` opens a subscription
21
+ seeded with the last `count` retained frames (no-arg = the whole
22
+ retained tail, clamped to the declared `tail` size). When `ttl` is set,
23
+ retained frames older than `ttl` ms are evicted lazily on every
24
+ read/append — no timer runs in the background. `chat.publish(m)` is isomorphic —
25
+ called server-side it both notifies in-process iterators and broadcasts
26
+ to remote subscribers; called client-side (via socketProxy) it sends a
27
+ `pub` frame the dispatcher validates and forwards.
28
+ */
29
+ // @readme plumbing
30
+ export function defineSocket<T>(name: string, opts: SocketOptions = {}): Socket<T> {
31
+ const retention = opts.tail ?? 0
32
+ const ttl = opts.ttl
33
+ const schema = opts.schema
34
+ /*
35
+ A schema makes the socket's payload safe to advertise to non-browser
36
+ surfaces, so it flips mcp/cli on by default — exposing the `tail` read
37
+ tool (and `publish` when clientPublish is set). Explicit `clients` wins.
38
+ */
39
+ const hasSchema = schema !== undefined
40
+ const clients = resolveClientFlags(opts.clients, { mcp: hasSchema, cli: hasSchema })
41
+ type BufferEntry = { value: T; expiresAt: number | undefined }
42
+ const buffer: BufferEntry[] = []
43
+ const subscribers = new Set<(message: T) => void>()
44
+ const topic = `socket:${name}`
45
+
46
+ /*
47
+ Retained frames are stored with an expiry timestamp. When `ttl` is set,
48
+ every read/append starts by dropping leading entries whose expiry has
49
+ passed — entries are appended in order so the expired prefix is
50
+ contiguous. No timer/setInterval is needed: expiry is lazy.
51
+ */
52
+ function pruneExpired(now: number): void {
53
+ if (ttl === undefined) {
54
+ return
55
+ }
56
+ let drop = 0
57
+ for (const entry of buffer) {
58
+ if (entry.expiresAt !== undefined && entry.expiresAt <= now) {
59
+ drop++
60
+ } else {
61
+ break
62
+ }
63
+ }
64
+ if (drop > 0) {
65
+ buffer.splice(0, drop)
66
+ }
67
+ }
68
+
69
+ /*
70
+ Active server is set once per process during createServer's boot,
71
+ immediately after Bun.serve resolves, and never reassigned. Resolve
72
+ it lazily on the first publish then keep the reference so subsequent
73
+ publishes skip the per-call getter.
74
+ */
75
+ let cachedServer: ReturnType<typeof getActiveServer>
76
+ /*
77
+ When a schema is attached, publish() validates synchronously and
78
+ throws on bad payloads. Standard Schema's validate() is generally
79
+ async — but for the synchronous server-side publish path we treat
80
+ a Promise return as a programming error (publish must be sync to
81
+ preserve in-process notify ordering). Schemas that need async
82
+ refinement should pre-validate at the call site instead.
83
+ */
84
+ function validateSync(message: T): T {
85
+ if (!schema) {
86
+ return message
87
+ }
88
+ const result = schema['~standard'].validate(message)
89
+ if (result instanceof Promise) {
90
+ throw new Error(
91
+ `[abide] socket "${name}" schema returned a Promise — sockets require sync validation`,
92
+ )
93
+ }
94
+ if (result.issues) {
95
+ throw new Error(
96
+ `[abide] socket "${name}" publish payload failed validation: ${JSON.stringify(result.issues)}`,
97
+ )
98
+ }
99
+ return result.value as T
100
+ }
101
+ function publish(message: T): void {
102
+ const validated = validateSync(message)
103
+ if (retention > 0) {
104
+ const now = Date.now()
105
+ pruneExpired(now)
106
+ buffer.push({ value: validated, expiresAt: ttl === undefined ? undefined : now + ttl })
107
+ if (buffer.length > retention) {
108
+ buffer.shift()
109
+ }
110
+ }
111
+ for (const notify of subscribers) {
112
+ notify(validated)
113
+ }
114
+ // Observe the fanned-out frame (inspector); no-op when unobserved.
115
+ socketTapSlot.tap?.({ socket: name, message: validated })
116
+ if (cachedServer === undefined) {
117
+ cachedServer = getActiveServer()
118
+ }
119
+ const server = cachedServer
120
+ if (server) {
121
+ server.publish(topic, JSON.stringify({ type: 'msg', socket: name, message: validated }))
122
+ }
123
+ }
124
+
125
+ /*
126
+ replay === 'all' replays the whole retained tail (`.tail()` no-arg);
127
+ a number replays the last min(count, buffer.length) items — `0` is
128
+ live-only, the bare `for await` behavior. `hooks.replayed` is queued
129
+ in-band after the replayed frames so a window reader commits its seed
130
+ atomically, strictly before any live frame.
131
+ */
132
+ function iterate(replay: number | 'all', hooks?: TailHooks): AsyncIterable<T> {
133
+ return {
134
+ [Symbol.asyncIterator](): AsyncIterator<T, void, undefined> {
135
+ let subscriber: ((message: T) => void) | undefined
136
+ const iter = createPushIterator<T>(() => {
137
+ if (subscriber) {
138
+ subscribers.delete(subscriber)
139
+ }
140
+ })
141
+ pruneExpired(Date.now())
142
+ const replayCount =
143
+ replay === 'all' ? buffer.length : Math.min(replay, buffer.length)
144
+ if (replayCount > 0) {
145
+ const start = buffer.length - replayCount
146
+ for (let index = start; index < buffer.length; index++) {
147
+ iter.push((buffer[index] as BufferEntry).value)
148
+ }
149
+ }
150
+ if (hooks?.replayed) {
151
+ iter.control(hooks.replayed)
152
+ }
153
+ subscriber = (message: T) => iter.push(message)
154
+ subscribers.add(subscriber)
155
+ return iter
156
+ },
157
+ }
158
+ }
159
+
160
+ const self: Socket<T> = {
161
+ name,
162
+ clients,
163
+ publish,
164
+ tail: (count?: number, hooks?: TailHooks) => iterate(count ?? 'all', hooks),
165
+ [Symbol.asyncIterator]: () => iterate(0)[Symbol.asyncIterator](),
166
+ }
167
+ registerSocket({
168
+ socket: self as Socket<unknown>,
169
+ allowClientPublish: opts.clientPublish ?? false,
170
+ schema,
171
+ clients,
172
+ snapshotTail: (count?: number) => {
173
+ pruneExpired(Date.now())
174
+ const start = count === undefined ? 0 : Math.max(0, buffer.length - count)
175
+ return buffer.slice(start).map((entry) => entry.value)
176
+ },
177
+ })
178
+ return self
179
+ }
@@ -0,0 +1,6 @@
1
+ import { socketRegistry } from './socketRegistry.ts'
2
+ import type { SocketRegistryEntry } from './types/SocketRegistryEntry.ts'
3
+
4
+ export function lookupSocket(name: string): SocketRegistryEntry | undefined {
5
+ return socketRegistry.get(name)
6
+ }
@@ -0,0 +1,6 @@
1
+ import { socketRegistry } from './socketRegistry.ts'
2
+ import type { SocketRegistryEntry } from './types/SocketRegistryEntry.ts'
3
+
4
+ export function registerSocket(entry: SocketRegistryEntry): void {
5
+ socketRegistry.set(entry.socket.name, entry)
6
+ }
@@ -0,0 +1,36 @@
1
+ import { commandNameForUrl } from '../../shared/commandNameForUrl.ts'
2
+ import { SOCKETS_PATH } from '../../shared/SOCKETS_PATH.ts'
3
+ import type { SocketOperation } from './types/SocketOperation.ts'
4
+ import type { SocketRegistryEntry } from './types/SocketRegistryEntry.ts'
5
+
6
+ /*
7
+ Projects a socket registry entry into the operations it exposes to the
8
+ CLI and MCP. Single source for the naming convention (`<base>-tail` /
9
+ `<base>-publish`), the existence rule (tail always; publish only when the
10
+ socket allows client publishing), and each operation's HTTP face — so the
11
+ CLI manifest builder, the MCP tool list, and the MCP tool dispatcher can't
12
+ disagree about which operations a socket has or what they're called.
13
+ */
14
+ export function socketOperations(entry: SocketRegistryEntry): SocketOperation[] {
15
+ const base = commandNameForUrl(entry.socket.name)
16
+ const restUrl = `${SOCKETS_PATH}/${entry.socket.name}`
17
+ const operations: SocketOperation[] = [
18
+ {
19
+ kind: 'tail',
20
+ name: `${base}-tail`,
21
+ socketName: entry.socket.name,
22
+ restUrl,
23
+ method: 'GET',
24
+ },
25
+ ]
26
+ if (entry.allowClientPublish) {
27
+ operations.push({
28
+ kind: 'publish',
29
+ name: `${base}-publish`,
30
+ socketName: entry.socket.name,
31
+ restUrl,
32
+ method: 'POST',
33
+ })
34
+ }
35
+ return operations
36
+ }
@@ -0,0 +1,9 @@
1
+ import type { SocketRegistryEntry } from './types/SocketRegistryEntry.ts'
2
+
3
+ /*
4
+ Process-wide registry of every Socket declared in the app. defineSocket
5
+ inserts on first construction; the dispatcher reads on every `sub` /
6
+ `pub` frame so it can find the right Socket by name and check the
7
+ opted-in `allowClientPublish` policy.
8
+ */
9
+ export const socketRegistry = new Map<string, SocketRegistryEntry>()
@@ -0,0 +1,23 @@
1
+ import type { ClientFlags } from '../../../shared/types/ClientFlags.ts'
2
+ import type { TailHooks } from '../../../shared/types/TailHooks.ts'
3
+
4
+ /*
5
+ Bidirectional named broadcast primitive. Declared once with `socket<T>()`
6
+ inside a file under `src/server/sockets/`; the same import resolves to a server-side
7
+ fan-out and a client-side ws proxy by build target. Iterating the socket
8
+ is the live stream — no replay. `.tail(count)` opens a subscription seeded
9
+ with the last `count` frames of the retained tail (declared via
10
+ `{ tail: n }`; no-arg = the whole retained tail) before going live — the
11
+ optional Subscribable retention capability the reactive `tail()` consumer
12
+ seeds from. `publish` is isomorphic: server code publishes in-process
13
+ and fans out to remote subscribers; client code sends a `pub` frame the
14
+ dispatcher validates against the topic's `clientPublish` flag. `clients`
15
+ exposes which adapter surfaces (browser / mcp / cli) advertise this
16
+ socket.
17
+ */
18
+ export interface Socket<T> extends AsyncIterable<T> {
19
+ readonly name: string
20
+ readonly clients: ClientFlags
21
+ publish(message: T): void
22
+ tail(count?: number, hooks?: TailHooks): AsyncIterable<T>
23
+ }
@@ -0,0 +1,19 @@
1
+ /*
2
+ Wire frame the browser sends over the multiplexed socket connection.
3
+ `sub` opens a subscription against `socket`. The optional `replay`
4
+ controls seeding from the retained tail: omitted = the whole retained
5
+ tail (`.tail()` no-arg); a number = at most that many trailing frames,
6
+ clamped server-side to what the topic retains — `0` (bare `for await`)
7
+ is live-only. `unsub` closes one. `pub` publishes a message —
8
+ the dispatcher checks the topic's `clientPublish` flag before fanning
9
+ out.
10
+
11
+ `sub` is the per-subscription id minted client-side; the server treats
12
+ it as opaque and routes inbound `msg|err|end` frames back to the same
13
+ id so one ws can multiplex many subscriptions to the same or different
14
+ sockets.
15
+ */
16
+ export type SocketClientFrame =
17
+ | { type: 'sub'; sub: string; socket: string; replay?: number }
18
+ | { type: 'unsub'; sub: string }
19
+ | { type: 'pub'; socket: string; message: unknown }
@@ -0,0 +1,22 @@
1
+ import type { HttpVerb } from '../../../shared/types/HttpVerb.ts'
2
+
3
+ /*
4
+ One operation a socket exposes to the non-browser surfaces. A socket
5
+ always offers a `tail` (read recent / stream live) and, when
6
+ `clientPublish` is set, a `publish` (send a message). This is the shared
7
+ skeleton — name, kind, HTTP face — that the CLI manifest, the MCP tool
8
+ list, and the MCP dispatcher all read instead of re-deriving the naming
9
+ convention and existence rule independently. Each surface dresses it with
10
+ its own presentation (descriptions, input schema, annotations).
11
+ */
12
+ export type SocketOperation = {
13
+ kind: 'tail' | 'publish'
14
+ // Command/tool name: the socket's command-name base plus `-tail` / `-publish`.
15
+ name: string
16
+ // Raw socket name, for the HTTP path and human-facing descriptions.
17
+ socketName: string
18
+ // HTTP face of the operation: `/__abide/sockets/<name>`.
19
+ restUrl: string
20
+ // GET for tail, POST for publish.
21
+ method: HttpVerb
22
+ }
@@ -0,0 +1,26 @@
1
+ import type { ClientFlags } from '../../../shared/types/ClientFlags.ts'
2
+ import type { StandardSchemaV1 } from '../../../shared/types/StandardSchemaV1.ts'
3
+
4
+ /*
5
+ Server-side options passed when declaring a socket via `socket<T>(opts)`.
6
+ `tail` opts the topic into retention: the socket keeps its last `tail`
7
+ frames so readers that weren't there — late joiners, reconnects, the
8
+ CLI/MCP/SSE read surfaces — can seed from `.tail(count)`. Omitted, the
9
+ socket is a pure live pipe and storage is the consumer's concern (a
10
+ `tail(chat, { last: n })` window fills from live frames only). Per-frame
11
+ TTL (retained frames older than `ttl` ms are evicted before replay), and
12
+ the client-publish gate (off by default — server-only topics ignore pub
13
+ frames coming over the wire). Optional Standard Schema validates payloads on
14
+ publish and gives MCP / CLI a typed payload to describe (projected via the
15
+ schema's own `toJSONSchema()` — wrap with withJsonSchema if its library lacks
16
+ one). `clients` controls which non-browser surfaces (mcp / cli) expose this
17
+ socket; browser is the historical default. All server-only state the bundler
18
+ strips out of the client stub.
19
+ */
20
+ export type SocketOptions<Schema extends StandardSchemaV1 = StandardSchemaV1> = {
21
+ tail?: number
22
+ ttl?: number
23
+ clientPublish?: boolean
24
+ schema?: Schema
25
+ clients?: Partial<ClientFlags>
26
+ }