@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,426 @@
1
+ import { mkdir } from 'node:fs/promises'
2
+ import { bindConnectedFlag } from './lib/bundle/bindConnectedFlag.ts'
3
+ import { bindRequestNavigate } from './lib/bundle/bindRequestNavigate.ts'
4
+ import { listenLocalControlServer } from './lib/bundle/listenLocalControlServer.ts'
5
+ import { probeAbideServer } from './lib/bundle/probeAbideServer.ts'
6
+ import { resolveWebviewLib } from './lib/bundle/resolveWebviewLib.ts'
7
+ import { spawnEmbeddedServer } from './lib/bundle/spawnEmbeddedServer.ts'
8
+ import { stableLocalPort } from './lib/bundle/stableLocalPort.ts'
9
+ import { abideLog } from './lib/shared/abideLog.ts'
10
+ import { appDataDir } from './lib/shared/appDataDir.ts'
11
+ import { binaryDirEnvPath } from './lib/shared/binaryDirEnvPath.ts'
12
+ import { clearLastConnection } from './lib/shared/clearLastConnection.ts'
13
+ import { createLivenessWatch } from './lib/shared/createLivenessWatch.ts'
14
+ import { dataDirEnvPath } from './lib/shared/dataDirEnvPath.ts'
15
+ import { readEnvFile } from './lib/shared/readEnvFile.ts'
16
+ import { readLastConnection } from './lib/shared/readLastConnection.ts'
17
+ import { serializeEnv } from './lib/shared/serializeEnv.ts'
18
+ import { writeLastConnection } from './lib/shared/writeLastConnection.ts'
19
+
20
+ /*
21
+ The bundle's control server, run in a Worker so it owns its own thread.
22
+
23
+ `webview_run` enters a native UI run loop that blocks the launcher's main thread
24
+ indefinitely (the window owns it until close), which freezes the main thread's
25
+ JS event loop. An in-process `Bun.serve` there can never answer a request, so the
26
+ webview pointed at it would only ever see a hung navigation — a blank window.
27
+
28
+ Running the control server on this Worker thread keeps it answering the whole time
29
+ the window is open. It owns the pieces that must live beside it: the embedded
30
+ server child it spawns, and its own FFI handle to the native menu flag (set here
31
+ because the main thread can't process a postMessage while blocked in webview_run,
32
+ yet the flag is a process-global the main-thread menu still reads).
33
+
34
+ Bun does not apply the launcher build's plugins to a worker entry, so this module
35
+ can't import abide's virtual modules (the connect-screen HTML, app title). The
36
+ launcher — which can — passes them in the `init` message; on `shutdown` it has us
37
+ reap the embedded child before the launcher exits.
38
+
39
+ Once connected it also watches the chosen server's liveness — polling its identity
40
+ endpoint — and, when it stops answering, corrects the menu flag and bounces the
41
+ window back to the connect screen, since a dead server (local crash or remote
42
+ outage) otherwise leaves a frozen page and a menu that still claims connected.
43
+
44
+ GET / → the connect screen (title injected at serve time)
45
+ GET /__abide/config → { schema, values } for the first-run config form
46
+ POST /__abide/config → persist the form's answers to the data-dir .env
47
+ POST /connect {url} → record connected, reply { redirect: url }
48
+ POST /start → spawn the server binary, reply { redirect: localUrl }
49
+ GET /__abide/disconnect → reap the child, clear connected
50
+ */
51
+
52
+ /*
53
+ Init payload from the launcher, plus the per-run state the handlers close over.
54
+ `configSchema` is the JSON Schema derived from the app's BundleWindow.config
55
+ (undefined when none declared), driving the connect screen's first-run form.
56
+ */
57
+ type Init = {
58
+ disconnectedHtml: string
59
+ title: string
60
+ programName: string
61
+ configSchema?: Record<string, unknown>
62
+ }
63
+ let disconnectedHtml = ''
64
+ let title = ''
65
+ let programName = ''
66
+ let configSchema: Record<string, unknown> | undefined
67
+ let flag: ReturnType<typeof bindConnectedFlag> | undefined
68
+ let server: ReturnType<typeof listenLocalControlServer> | undefined
69
+
70
+ // The control-server origin (where the connect screen lives) and the webview
71
+ // handle, forwarded by the launcher — together they let the watch bounce a dead
72
+ // window back to the connect screen.
73
+ let controlOrigin = ''
74
+ let navigate: ReturnType<typeof bindRequestNavigate> | undefined
75
+ let webviewHandle: number | undefined
76
+
77
+ /*
78
+ Liveness watch over the currently-connected server: the shared state machine
79
+ (createLivenessWatch) driven by the identity-endpoint probe — a couple of
80
+ consecutive misses, tolerating a transient blip or a quick restart, count as
81
+ a death. Stopped whenever we're not connected.
82
+ */
83
+ const liveness = createLivenessWatch({
84
+ probe: async (url) => (await probeAbideServer(url)) !== undefined,
85
+ onLost: handleConnectionLost,
86
+ })
87
+
88
+ // Embedded-server child, spawned on demand by Start server; undefined when none.
89
+ let serverChild: ReturnType<typeof Bun.spawn> | undefined
90
+
91
+ // Reaps the embedded server child if one is running.
92
+ function killServerChild(): void {
93
+ if (serverChild) {
94
+ serverChild.kill()
95
+ serverChild = undefined
96
+ }
97
+ }
98
+
99
+ /*
100
+ Optional local-assistant bridge, tied to the connected app. Started only when the
101
+ app bundled @abide/claude-code (its UI uses browser/assistant, so the dynamic
102
+ import resolves — an app that doesn't is tolerated by --compile and no-ops) AND
103
+ `claude` is on PATH. The page receives the loopback bridge's port+token through the
104
+ webview URL fragment (withAssistant); no `claude` yields an `unavailable` fragment
105
+ so the page shows an install hint instead of a run-this-command prompt. The bridge
106
+ is loopback-only and torn down with the connection (stopLivenessWatch).
107
+ */
108
+ let bridge: ReturnType<typeof import('@abide/claude-code/serve')['serve']> | undefined
109
+ let assistantHandshake: string | undefined
110
+
111
+ async function startBridge(url: string): Promise<void> {
112
+ const claude = await import('@abide/claude-code/serve').catch(() => undefined)
113
+ if (!claude) {
114
+ return
115
+ }
116
+ if (!Bun.which('claude')) {
117
+ assistantHandshake = 'unavailable'
118
+ return
119
+ }
120
+ const token = crypto.randomUUID()
121
+ const server = claude.serve({ url, allowOrigins: [url], token })
122
+ bridge = server
123
+ assistantHandshake = `${server.port}.${token}`
124
+ }
125
+
126
+ function stopBridge(): void {
127
+ bridge?.stop(true)
128
+ bridge = undefined
129
+ assistantHandshake = undefined
130
+ }
131
+
132
+ // Append the bridge handshake to a webview URL so the page's assistant() finds its port+token.
133
+ function withAssistant(url: string): string {
134
+ return assistantHandshake ? `${url}#abide-assistant=${assistantHandshake}` : url
135
+ }
136
+
137
+ // Begin (or restart) watching `url` for liveness once the window points at it.
138
+ function startLivenessWatch(url: string): void {
139
+ liveness.watch(url)
140
+ }
141
+
142
+ // Stop watching — on explicit disconnect, on detected death, or at shutdown.
143
+ function stopLivenessWatch(): void {
144
+ liveness.stop()
145
+ // The assistant bridge follows the connection — drop it whenever we disconnect.
146
+ stopBridge()
147
+ }
148
+
149
+ /*
150
+ The connected-state transition pair. Connected is three coordinated pieces —
151
+ the native menu flag, the liveness watch, and the assistant bridge — that must
152
+ move together; disconnected additionally reaps any embedded child. Every
153
+ handler routes through these so no path can desync the menu or leave a bridge
154
+ running against a dead server.
155
+ */
156
+ async function becomeConnected(url: string): Promise<void> {
157
+ flag?.setConnected(true)
158
+ startLivenessWatch(url)
159
+ await startBridge(url)
160
+ }
161
+
162
+ function becomeDisconnected(): void {
163
+ stopLivenessWatch()
164
+ killServerChild()
165
+ flag?.setConnected(false)
166
+ }
167
+
168
+ /*
169
+ The connected server stopped answering. Tear the connection down and bounce the
170
+ window back to the connect screen with a `lost` notice. The flag flip alone keeps
171
+ the menu honest even when the navigate is a no-op (off macOS, or no handle yet).
172
+ */
173
+ function handleConnectionLost(url: string): void {
174
+ abideLog.warn(`connected server stopped responding: ${url}`)
175
+ becomeDisconnected()
176
+ if (webviewHandle !== undefined) {
177
+ navigate?.requestNavigate(webviewHandle, `${controlOrigin}/?action=lost`)
178
+ }
179
+ }
180
+
181
+ /*
182
+ Boots the embedded server via the shared spawn helper and keeps the child so
183
+ killServerChild can reap it. Any previous child is reaped first so only one
184
+ embedded server runs at a time; returns the URL to point the window at.
185
+ */
186
+ async function startEmbeddedServer(timeoutMs?: number): Promise<string> {
187
+ killServerChild()
188
+ const { url, child } = await spawnEmbeddedServer({ programName, timeoutMs })
189
+ serverChild = child
190
+ return url
191
+ }
192
+
193
+ /*
194
+ Where the window should point on launch, resolved before it ever opens so the
195
+ connect screen never flashes. Repeats the last connection from the launcher-owned
196
+ record (which survives relaunch where the embedded server's fresh port can't):
197
+
198
+ - embedded, config complete → boot it and point at the live server
199
+ - embedded, config missing → the connect screen, so the user can configure
200
+ - remote url, still alive → point straight at it
201
+ - remote url, now dead → the connect screen with a `lost` notice
202
+ - nothing recorded → the connect screen
203
+
204
+ Boot is bounded by a short ceiling: a failed or slow boot falls back to the
205
+ connect screen rather than leaving the launcher window-less, and reaps the child
206
+ so a half-started server doesn't hold its port.
207
+ */
208
+ const AUTO_START_CEILING_MS = 3000
209
+ async function resolveLaunchTarget(): Promise<string> {
210
+ const last = await readLastConnection(programName)
211
+ if (!last) {
212
+ return controlOrigin
213
+ }
214
+ if (last.kind === 'embedded') {
215
+ if (await autoStartBlockedByConfig()) {
216
+ return controlOrigin
217
+ }
218
+ try {
219
+ const url = await startEmbeddedServer(AUTO_START_CEILING_MS)
220
+ await becomeConnected(url)
221
+ abideLog.info(`resumed embedded server at ${url}`)
222
+ return url
223
+ } catch (error) {
224
+ killServerChild()
225
+ abideLog.warn(`embedded server did not resume: ${String(error)}`)
226
+ return controlOrigin
227
+ }
228
+ }
229
+ const identity = await probeAbideServer(last.url)
230
+ if (identity) {
231
+ await becomeConnected(last.url)
232
+ abideLog.info(`reconnected to ${identity.name} at ${last.url}`)
233
+ return last.url
234
+ }
235
+ abideLog.warn(`saved server did not respond: ${last.url}`)
236
+ return `${controlOrigin}/?action=lost`
237
+ }
238
+
239
+ // True when the app declares required config that nothing yet supplies, so an
240
+ // embedded auto-start would only crash for the lack of it — land on the connect
241
+ // screen (and its setup modal) instead.
242
+ async function autoStartBlockedByConfig(): Promise<boolean> {
243
+ const required = (configSchema?.required as string[] | undefined) ?? []
244
+ if (required.length === 0) {
245
+ return false
246
+ }
247
+ const values = await resolveConfigValues()
248
+ return required.some((key) => !values[key])
249
+ }
250
+
251
+ /*
252
+ Injects the app title into the connect-screen HTML just before serving — the build
253
+ left a `<!--abide:connect-config-->` marker in <head>.
254
+ */
255
+ function renderConnectScreen(): Response {
256
+ const script = `<script>window.__ABIDE_TITLE__=${JSON.stringify(title)}</script>`
257
+ const html = disconnectedHtml.replace('<!--abide:connect-config-->', script)
258
+ return new Response(html, { headers: { 'content-type': 'text/html; charset=utf-8' } })
259
+ }
260
+
261
+ /*
262
+ Resolves the value to pre-fill each config field with, following the same
263
+ precedence the server applies below the shell: the user's saved data-dir `.env`,
264
+ then the bundle's shipped binary-dir `.env`, then the schema's own `default`.
265
+ Empty string when nothing supplies it — which is how the form spots an unmet
266
+ required field.
267
+ */
268
+ async function resolveConfigValues(): Promise<Record<string, string>> {
269
+ const properties = (configSchema?.properties ?? {}) as Record<string, { default?: unknown }>
270
+ // Independent reads — fetch together; precedence is applied in the merge below.
271
+ const [dataDirEnv, binaryDirEnv] = await Promise.all([
272
+ readEnvFile(dataDirEnvPath(programName)),
273
+ readEnvFile(binaryDirEnvPath()),
274
+ ])
275
+ return Object.fromEntries(
276
+ Object.keys(properties).map((key) => {
277
+ const fallback = properties[key]?.default
278
+ const value =
279
+ dataDirEnv[key] ??
280
+ binaryDirEnv[key] ??
281
+ (fallback === undefined ? '' : String(fallback))
282
+ return [key, value]
283
+ }),
284
+ )
285
+ }
286
+
287
+ /*
288
+ Persists the form's answers to the data-dir `.env`, merged over any existing
289
+ file so keys the form didn't touch survive. Creates the data dir on first run
290
+ (appDataDir only computes the path).
291
+ */
292
+ async function writeConfig(values: Record<string, string>): Promise<void> {
293
+ const path = dataDirEnvPath(programName)
294
+ const merged = { ...(await readEnvFile(path)), ...values }
295
+ await mkdir(appDataDir(programName), { recursive: true })
296
+ await Bun.write(path, serializeEnv(merged))
297
+ }
298
+
299
+ // GET /__abide/config — the form's schema + current values, or null schema to skip the gate.
300
+ async function handleConfigGet(): Promise<Response> {
301
+ if (!configSchema) {
302
+ return Response.json({ schema: null, values: {} })
303
+ }
304
+ return Response.json({ schema: configSchema, values: await resolveConfigValues() })
305
+ }
306
+
307
+ // POST /__abide/config — persist the form's answers to the data-dir `.env`.
308
+ async function handleConfigPost(request: Request): Promise<Response> {
309
+ const { values } = (await request.json()) as { values: Record<string, string> }
310
+ await writeConfig(values)
311
+ return new Response(undefined, { status: 204 })
312
+ }
313
+
314
+ // POST /connect — point the window at a remote abide server after probing it.
315
+ async function handleConnect(request: Request): Promise<Response> {
316
+ const { url: target } = (await request.json()) as { url: string }
317
+ // Verify it's actually a abide server before pointing the window at it.
318
+ const identity = await probeAbideServer(target)
319
+ if (!identity) {
320
+ abideLog.warn(`no abide server responded at ${target}`)
321
+ return Response.json({ error: `No abide server responded at ${target}` }, { status: 502 })
322
+ }
323
+ await becomeConnected(target)
324
+ // Record the choice so the next launch reconnects here before opening.
325
+ await writeLastConnection(programName, { kind: 'url', url: target })
326
+ abideLog.info(`connecting to ${identity.name} at ${target}`)
327
+ return Response.json({ redirect: withAssistant(target) })
328
+ }
329
+
330
+ // POST /start — boot the embedded server and point the window at it.
331
+ async function handleStart(): Promise<Response> {
332
+ try {
333
+ const localUrl = await startEmbeddedServer()
334
+ await becomeConnected(localUrl)
335
+ // Record the choice so the next launch boots the embedded server first.
336
+ await writeLastConnection(programName, { kind: 'embedded' })
337
+ abideLog.info(`started embedded server at ${localUrl}`)
338
+ return Response.json({ redirect: withAssistant(localUrl) })
339
+ } catch (error) {
340
+ killServerChild()
341
+ return Response.json({ error: String(error) }, { status: 500 })
342
+ }
343
+ }
344
+
345
+ // GET /__abide/disconnect — tear down the embedded server and forget the auto-resume choice.
346
+ async function handleDisconnect(): Promise<Response> {
347
+ becomeDisconnected()
348
+ await clearLastConnection(programName)
349
+ return new Response(undefined, { status: 204 })
350
+ }
351
+
352
+ /*
353
+ The control server's routes, keyed by `${method} ${pathname}` (exact match). The
354
+ connect screen owns localStorage + navigation; this worker owns the embedded-
355
+ server process and the native flag.
356
+ */
357
+ const controlRoutes: Record<string, (request: Request) => Promise<Response> | Response> = {
358
+ 'GET /': () => renderConnectScreen(),
359
+ 'GET /__abide/config': handleConfigGet,
360
+ 'POST /__abide/config': handleConfigPost,
361
+ 'POST /connect': handleConnect,
362
+ 'POST /start': handleStart,
363
+ 'GET /__abide/disconnect': handleDisconnect,
364
+ }
365
+
366
+ function handleControlRequest(request: Request): Promise<Response> | Response {
367
+ const { pathname } = new URL(request.url)
368
+ const route = controlRoutes[`${request.method} ${pathname}`]
369
+ return route ? route(request) : new Response('not found', { status: 404 })
370
+ }
371
+
372
+ /*
373
+ Bind the control server to 127.0.0.1 literally (not `localhost`) so the webview
374
+ reaches it without any IPv4/IPv6 name-resolution ambiguity, open the native flag
375
+ handle, then resolve where the window should open before handing back. Resolving
376
+ the launch target here — booting/probing the last connection before `ready` — is
377
+ what lets the launcher open the window straight at the live server, so the
378
+ connect screen never flashes; only an unconfigured, failed, or absent resume
379
+ falls back to it. The launcher gets both `origin` (for the File-menu actions) and
380
+ `target` (where to point the window now).
381
+ */
382
+ async function start(init: Init): Promise<void> {
383
+ disconnectedHtml = init.disconnectedHtml
384
+ title = init.title
385
+ programName = init.programName
386
+ configSchema = init.configSchema
387
+ const libPath = await resolveWebviewLib()
388
+ flag = bindConnectedFlag(libPath)
389
+ navigate = bindRequestNavigate(libPath)
390
+ server = listenLocalControlServer(stableLocalPort(init.programName), handleControlRequest)
391
+ controlOrigin = `http://127.0.0.1:${server.port}`
392
+ abideLog.info(`${title} control server listening at ${controlOrigin}`)
393
+ const target = await resolveLaunchTarget()
394
+ // Fold in the assistant handshake (when a bridge started) so the page is configured on first load.
395
+ self.postMessage({ type: 'ready', origin: controlOrigin, target: withAssistant(target) })
396
+ }
397
+
398
+ // Reap the child + release the server and FFI handles, then confirm so the
399
+ // launcher can exit cleanly.
400
+ function shutdown(): void {
401
+ stopLivenessWatch()
402
+ killServerChild()
403
+ server?.stop(true)
404
+ flag?.close()
405
+ navigate?.close()
406
+ self.postMessage({ type: 'shutdownDone' })
407
+ }
408
+
409
+ /*
410
+ The launcher drives the lifecycle: `init` (with the data this worker can't import)
411
+ starts the server, `window` forwards the webview handle the liveness watch needs to
412
+ navigate, and `shutdown` tears it all down once the window closes.
413
+ */
414
+ self.addEventListener('message', (event: MessageEvent) => {
415
+ const data = event.data as
416
+ | { type: 'init'; init: Init }
417
+ | { type: 'window'; handle: number }
418
+ | { type: 'shutdown' }
419
+ if (data.type === 'init') {
420
+ void start(data.init)
421
+ } else if (data.type === 'window') {
422
+ webviewHandle = data.handle
423
+ } else if (data.type === 'shutdown') {
424
+ shutdown()
425
+ }
426
+ })
@@ -0,0 +1,250 @@
1
+ import { watch } from 'node:fs'
2
+ import type { Subprocess } from 'bun'
3
+ import { build } from './build.ts'
4
+ import { DEFAULT_PORT } from './lib/server/runtime/DEFAULT_PORT.ts'
5
+ import { DEV_READY_MESSAGE } from './lib/server/runtime/DEV_READY_MESSAGE.ts'
6
+ import { DEV_REBUILD_MESSAGE } from './lib/server/runtime/DEV_REBUILD_MESSAGE.ts'
7
+ import { findOpenPort } from './lib/server/runtime/findOpenPort.ts'
8
+ import { abideLog } from './lib/shared/abideLog.ts'
9
+
10
+ /*
11
+ Dev orchestrator. Replaces `bun --watch` (which only watches the import graph,
12
+ so new files / CSS / public assets never triggered a restart) with an explicit
13
+ loop we own end to end:
14
+
15
+ 1. Build the client once — uncompressed, unminified (compressing on every
16
+ rebuild dwarfs the bundle; the server serves the plain bytes when no .gz exists).
17
+ 2. Spawn the server as a child against a fixed dev port and ABIDE_DEV=1, which
18
+ makes it mount the /__abide/dev live-reload channel.
19
+ 3. Watch src/ recursively. On any change, rebuild then swap the worker: the
20
+ replacement boots alongside the incumbent (both bind the port via
21
+ reusePort) and the incumbent is retired only once the replacement reports
22
+ ready over IPC — the port keeps answering across the whole restart. SSR
23
+ renders pages through Bun's module cache, so a fresh module graph (a new
24
+ process) is the reliable way to reflect a source edit — Bun has no stable
25
+ in-process invalidation. Killing the incumbent drops the browser's
26
+ live-reload channel; it reconnects straight onto the already-listening
27
+ replacement and reloads itself.
28
+
29
+ Restarts are serialized (a build mid-flight queues the next) and the port is
30
+ fixed so the browser tab stays valid across restarts. A failed build — or a
31
+ replacement that dies or hangs while booting — keeps the last-good server
32
+ running rather than tearing the loop down. A worker that crashes while active
33
+ is respawned, bounded so a boot-time crash loop gives up and waits for a save.
34
+ */
35
+ const cwd = process.cwd()
36
+ const PRELOAD = new URL('./preload.ts', import.meta.url).pathname
37
+ const SERVER_ENTRY = new URL('./serverEntry.ts', import.meta.url).pathname
38
+ const SOURCE_DIR = `${cwd}/src`
39
+ // Coalesce editor save bursts (and multi-file saves) into one rebuild.
40
+ const REBUILD_DEBOUNCE_MS = 60
41
+ // How long a booting replacement gets to report ready before it is discarded.
42
+ const READY_TIMEOUT_MS = 30000
43
+ // Consecutive worker exits with no ready signal in between before the
44
+ // crash-respawn loop gives up and waits for the next save.
45
+ const MAX_EXITS_WITHOUT_READY = 3
46
+ /*
47
+ Generated dir the build itself writes into src/ (route type declarations). It
48
+ must be ignored or each rebuild's write retriggers the watcher — an endless
49
+ rebuild loop.
50
+ */
51
+ const GENERATED_DIR = '.abide'
52
+
53
+ // True for paths under src/.abide (the build's own generated output).
54
+ function isGenerated(filename: string): boolean {
55
+ return filename.split(/[\\/]/).includes(GENERATED_DIR)
56
+ }
57
+
58
+ // clean:false leaves the live dist in place — each build swaps _app in atomically,
59
+ // so the running server never serves a half-built or emptied bundle.
60
+ const buildOptions = {
61
+ cwd,
62
+ minify: false,
63
+ compress: false,
64
+ clean: false,
65
+ exitOnFailure: false,
66
+ } as const
67
+
68
+ // The worker currently meant to be serving; undefined while crashed or replaced.
69
+ let server: Subprocess | undefined
70
+ let shuttingDown = false
71
+ // Worker exits since the last ready signal — bounds the crash-respawn loop.
72
+ let exitsWithoutReady = 0
73
+
74
+ /*
75
+ Spawn a server worker against the fixed dev port. `ready` resolves when the
76
+ worker reports its listener is up and init() has run (DEV_READY_MESSAGE) — the
77
+ cue that a replacement may retire its predecessor.
78
+ */
79
+ function spawnWorker(port: number): { proc: Subprocess; ready: Promise<void> } {
80
+ const readiness = Promise.withResolvers<void>()
81
+ const proc = Bun.spawn({
82
+ cmd: ['bun', '--preload', PRELOAD, SERVER_ENTRY],
83
+ cwd,
84
+ env: { ...process.env, PORT: String(port), ABIDE_DEV: '1' },
85
+ stdio: ['inherit', 'inherit', 'inherit'],
86
+ // The child's POST /__abide/reload route signals a rebuild over IPC, so the
87
+ // trigger rides the app's own port instead of a side channel.
88
+ ipc(message) {
89
+ if (message === DEV_REBUILD_MESSAGE) {
90
+ void rebuild(port)
91
+ }
92
+ if (message === DEV_READY_MESSAGE) {
93
+ exitsWithoutReady = 0
94
+ readiness.resolve()
95
+ }
96
+ },
97
+ })
98
+ respawnOnUnexpectedExit(proc, port)
99
+ return { proc, ready: readiness.promise }
100
+ }
101
+
102
+ /*
103
+ A worker that dies while it is the active server — an app crash, not a swap or
104
+ shutdown — used to leave the port dead until a manual restart. Respawn it,
105
+ giving up after MAX_EXITS_WITHOUT_READY exits in a row so a crash-on-boot
106
+ doesn't spin; the next save retries through rebuild.
107
+ */
108
+ function respawnOnUnexpectedExit(proc: Subprocess, port: number): void {
109
+ void proc.exited.then((exitCode) => {
110
+ if (shuttingDown || server !== proc) {
111
+ return
112
+ }
113
+ server = undefined
114
+ exitsWithoutReady += 1
115
+ if (exitsWithoutReady >= MAX_EXITS_WITHOUT_READY) {
116
+ abideLog.warn(
117
+ `server keeps exiting (code ${exitCode}) — fix the error and save to retry`,
118
+ )
119
+ return
120
+ }
121
+ abideLog.warn(`server exited unexpectedly (code ${exitCode}) — restarting`)
122
+ server = spawnWorker(port).proc
123
+ })
124
+ }
125
+
126
+ /* Terminate a worker and wait for it to exit (SIGKILL watchdog for a wedged exit). */
127
+ async function stopWorker(proc: Subprocess): Promise<void> {
128
+ proc.kill()
129
+ const watchdog = setTimeout(() => proc.kill('SIGKILL'), 3000)
130
+ await proc.exited
131
+ clearTimeout(watchdog)
132
+ }
133
+
134
+ /*
135
+ Zero-downtime swap. The replacement overlaps the incumbent — both bind the dev
136
+ port via reusePort (see createServer) and the kernel keeps delivering
137
+ connections to the incumbent until it stops — so the port answers throughout
138
+ the new module graph's boot. Only a replacement that reports ready retires the
139
+ incumbent; one that dies or hangs booting is discarded and the last-good server
140
+ keeps serving, mirroring how a failed build is handled.
141
+ */
142
+ async function replaceServer(port: number): Promise<void> {
143
+ const previous = server
144
+ const next = spawnWorker(port)
145
+ const outcome = await Promise.race([
146
+ next.ready.then(() => 'ready' as const),
147
+ next.proc.exited.then(() => 'exited' as const),
148
+ Bun.sleep(READY_TIMEOUT_MS).then(() => 'timeout' as const),
149
+ ])
150
+ if (outcome !== 'ready') {
151
+ if (outcome === 'timeout') {
152
+ await stopWorker(next.proc)
153
+ }
154
+ abideLog.warn('new server failed to boot — the previous one (if any) keeps serving')
155
+ return
156
+ }
157
+ server = next.proc
158
+ if (previous) {
159
+ await stopWorker(previous)
160
+ }
161
+ }
162
+
163
+ let building = false
164
+ let queued = false
165
+
166
+ /*
167
+ Rebuild the client, then (on success) swap in a fresh server child. Serialized:
168
+ a change arriving mid-build sets `queued` so exactly one more rebuild runs
169
+ after, collapsing any further changes in between. A failed build leaves the
170
+ current child untouched — the error is logged and the last-good server keeps
171
+ serving.
172
+ */
173
+ async function rebuild(port: number): Promise<void> {
174
+ if (building) {
175
+ queued = true
176
+ return
177
+ }
178
+ building = true
179
+ try {
180
+ const succeeded = await build(buildOptions)
181
+ if (succeeded) {
182
+ await replaceServer(port)
183
+ }
184
+ } finally {
185
+ building = false
186
+ if (queued) {
187
+ queued = false
188
+ void rebuild(port)
189
+ }
190
+ }
191
+ }
192
+
193
+ /*
194
+ Pick a free port once and reuse it for every restart, so the browser tab keeps
195
+ pointing at the same address. Scans upward from the shared default so dev lands
196
+ on the same predictable 3000+ address as `bun start`; reusing the number across
197
+ restarts (not re-scanning) is what keeps the tab valid.
198
+ */
199
+ const port = findOpenPort(DEFAULT_PORT)
200
+ const firstBuild = await build(buildOptions)
201
+ if (!firstBuild) {
202
+ abideLog.warn('initial build failed — fix the error and save to retry')
203
+ }
204
+ server = spawnWorker(port).proc
205
+
206
+ /*
207
+ ABIDE_DEV_NO_WATCH=1 skips the fs watcher: rebuild only on demand via POST
208
+ /__abide/reload (always mounted under dev), so a long-lived in-process job — e.g.
209
+ an agent editing the app's own source — isn't yanked mid-run by a save.
210
+ */
211
+ const manualRebuild = Bun.env.ABIDE_DEV_NO_WATCH === '1'
212
+
213
+ let debounce: ReturnType<typeof setTimeout> | undefined
214
+ const watcher = manualRebuild
215
+ ? undefined
216
+ : watch(SOURCE_DIR, { recursive: true }, (_event, filename) => {
217
+ if (!filename || isGenerated(filename)) {
218
+ return
219
+ }
220
+ clearTimeout(debounce)
221
+ debounce = setTimeout(() => void rebuild(port), REBUILD_DEBOUNCE_MS)
222
+ })
223
+ if (manualRebuild) {
224
+ abideLog.info(
225
+ `manual rebuild mode — POST http://localhost:${port}/__abide/reload to apply changes`,
226
+ )
227
+ }
228
+
229
+ /* Tear down the watcher and the child on shutdown so neither outlives the orchestrator. */
230
+ const shutdown = async () => {
231
+ shuttingDown = true
232
+ watcher?.close()
233
+ if (server) {
234
+ await stopWorker(server)
235
+ }
236
+ process.exit(0)
237
+ }
238
+ process.on('SIGINT', shutdown)
239
+ process.on('SIGTERM', shutdown)
240
+ process.on('SIGHUP', shutdown)
241
+
242
+ /*
243
+ Last-resort sync cleanup: Bun.spawn'd children aren't reaped when the parent
244
+ dies, so a crash (uncaught error, terminal close) would otherwise leave the
245
+ server holding the dev port. 'exit' fires for every exit path; kill is
246
+ synchronous, which is enough to signal the child before we go.
247
+ */
248
+ process.on('exit', () => {
249
+ server?.kill()
250
+ })