@abide/abide 0.49.0 → 0.50.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (325) hide show
  1. package/AGENTS.md +564 -437
  2. package/CHANGELOG.md +183 -0
  3. package/README.md +165 -202
  4. package/package.json +14 -5
  5. package/src/abideLsp.ts +5 -6
  6. package/src/abideResolverPlugin.ts +197 -169
  7. package/src/build.ts +79 -56
  8. package/src/buildDisconnected.ts +0 -2
  9. package/src/checkAbide.ts +12 -2
  10. package/src/devEntry.ts +113 -28
  11. package/src/discoveryEntry.ts +3 -2
  12. package/src/lib/bundle/disconnected.abide +69 -73
  13. package/src/lib/bundle/infoPlist.ts +13 -7
  14. package/src/lib/bundle/spawnEmbeddedServer.ts +13 -4
  15. package/src/lib/bundle/waitForServer.ts +6 -1
  16. package/src/lib/mcp/mcpTools.ts +15 -6
  17. package/src/lib/server/error.ts +6 -6
  18. package/src/lib/server/json.ts +17 -3
  19. package/src/lib/server/rpc/defineRpc.ts +58 -24
  20. package/src/lib/server/rpc/parseArgs.ts +98 -13
  21. package/src/lib/server/rpc/resolveRpcJsonSchema.ts +28 -0
  22. package/src/lib/server/rpc/types/RpcHelper.ts +75 -105
  23. package/src/lib/server/rpc/types/RpcRegistryEntry.ts +24 -9
  24. package/src/lib/server/rpc/validationError.ts +1 -1
  25. package/src/lib/server/runtime/DEV_RELOAD_CLIENT_SCRIPT.ts +0 -15
  26. package/src/lib/server/runtime/buildCacheSnapshot.ts +10 -9
  27. package/src/lib/server/runtime/buildInspectorSurface.ts +6 -4
  28. package/src/lib/server/runtime/buildOpenApiSpec.ts +55 -11
  29. package/src/lib/server/runtime/buildPreloadManifest.ts +12 -10
  30. package/src/lib/server/runtime/createAppAssetServer.ts +18 -15
  31. package/src/lib/server/runtime/createAppRouteResolver.ts +145 -0
  32. package/src/lib/server/runtime/createPlumbingRouter.ts +212 -0
  33. package/src/lib/server/runtime/createRouteDispatcher.ts +4 -10
  34. package/src/lib/server/runtime/createServer.ts +120 -324
  35. package/src/lib/server/runtime/createUiPageRenderer.ts +137 -26
  36. package/src/lib/server/runtime/devClientFingerprint.ts +19 -34
  37. package/src/lib/server/runtime/installAmbientScopeStore.ts +30 -9
  38. package/src/lib/server/runtime/logExposedSurfaces.ts +8 -7
  39. package/src/lib/server/runtime/pathStore.ts +15 -0
  40. package/src/lib/server/runtime/registryManifests.ts +1 -1
  41. package/src/lib/server/runtime/renderCellBarrierStore.ts +15 -0
  42. package/src/lib/server/runtime/runWithRequestScope.ts +3 -0
  43. package/src/lib/server/runtime/serializeCacheSnapshot.ts +6 -4
  44. package/src/lib/server/runtime/snapshotEntryFromCache.ts +14 -14
  45. package/src/lib/server/runtime/streamCacheResolutions.ts +14 -7
  46. package/src/lib/server/runtime/streamFromIterator.ts +30 -2
  47. package/src/lib/server/runtime/textResponse.ts +23 -0
  48. package/src/lib/server/runtime/types/DevReloadStamp.ts +6 -10
  49. package/src/lib/server/runtime/types/InspectorCacheEntry.ts +1 -1
  50. package/src/lib/server/runtime/types/RequestStore.ts +27 -0
  51. package/src/lib/server/runtime/warnUnguardedMcp.ts +15 -5
  52. package/src/lib/server/sockets/createSocketDispatcher.ts +9 -11
  53. package/src/lib/server/sockets/defineSocket.ts +5 -4
  54. package/src/lib/server/sse.ts +5 -1
  55. package/src/lib/shared/ASYNC_CELL.ts +5 -0
  56. package/src/lib/shared/DEV_REBUILD_PATH.ts +6 -0
  57. package/src/lib/shared/HTTP_METHODS.ts +6 -0
  58. package/src/lib/shared/HttpError.ts +1 -5
  59. package/src/lib/shared/MCP_PATH.ts +6 -0
  60. package/src/lib/shared/PROXIED_SERVER_SUBDIRS.ts +15 -0
  61. package/src/lib/shared/REF_JSON_TAGS.ts +13 -1
  62. package/src/lib/shared/RPC_SHIM_GLOBALS.ts +9 -0
  63. package/src/lib/shared/activeCacheStore.ts +1 -0
  64. package/src/lib/shared/activePage.ts +1 -0
  65. package/src/lib/shared/buildArtifact.ts +4 -1
  66. package/src/lib/shared/buildSocketOverChannel.ts +4 -3
  67. package/src/lib/shared/bundleGraphFromMetafile.ts +68 -0
  68. package/src/lib/shared/cache.ts +156 -131
  69. package/src/lib/shared/changeAffectsClient.ts +12 -7
  70. package/src/lib/shared/createCacheStore.ts +31 -8
  71. package/src/lib/shared/createLifecycleChannel.ts +7 -1
  72. package/src/lib/shared/createReachable.ts +47 -78
  73. package/src/lib/shared/createRemoteFunction.ts +52 -30
  74. package/src/lib/shared/createRpcServerProgram.ts +820 -0
  75. package/src/lib/shared/decodeRefJson.ts +4 -4
  76. package/src/lib/shared/decodeResponse.ts +2 -2
  77. package/src/lib/shared/decodeWireBody.ts +35 -0
  78. package/src/lib/shared/detectRpcMethod.ts +8 -1
  79. package/src/lib/shared/done.ts +8 -2
  80. package/src/lib/shared/encodeRefJson.ts +4 -4
  81. package/src/lib/shared/encodeWireBody.ts +18 -0
  82. package/src/lib/{server/rpc → shared}/fieldErrorsFromIssues.ts +5 -1
  83. package/src/lib/shared/generateDeclarations.ts +72 -0
  84. package/src/lib/shared/hasSeedableRequest.ts +25 -0
  85. package/src/lib/shared/hydrationWindow.ts +53 -0
  86. package/src/lib/shared/isAsyncCell.ts +11 -0
  87. package/src/lib/shared/isAsyncIterable.ts +8 -0
  88. package/src/lib/shared/isSubscribable.ts +3 -3
  89. package/src/lib/shared/isThenable.ts +9 -0
  90. package/src/lib/shared/jsonSchemaForType.ts +258 -0
  91. package/src/lib/shared/lenientDecode.ts +15 -0
  92. package/src/lib/shared/loadProjectTsConfig.ts +28 -0
  93. package/src/lib/shared/markFrameworkSourcesIgnored.ts +1 -1
  94. package/src/lib/shared/matchRoute.ts +4 -14
  95. package/src/lib/shared/peek.ts +14 -0
  96. package/src/lib/shared/pending.ts +9 -6
  97. package/src/lib/shared/pendingAsyncCellsSlot.ts +13 -0
  98. package/src/lib/shared/prepareRpcModule.ts +91 -93
  99. package/src/lib/shared/prepareSocketModule.ts +3 -2
  100. package/src/lib/shared/probeRegistries.ts +5 -18
  101. package/src/lib/shared/reachable.ts +7 -10
  102. package/src/lib/shared/refresh.ts +12 -2
  103. package/src/lib/shared/refreshing.ts +8 -2
  104. package/src/lib/shared/resolvedCellsSlot.ts +13 -0
  105. package/src/lib/shared/reviveWireField.ts +49 -0
  106. package/src/lib/shared/reviveWireOutput.ts +36 -0
  107. package/src/lib/shared/rpcServerForRoot.ts +25 -0
  108. package/src/lib/shared/scanPages.ts +25 -0
  109. package/src/lib/shared/snapshotShippable.ts +8 -7
  110. package/src/lib/shared/streamedCellsSlot.ts +14 -0
  111. package/src/lib/shared/subscribableFromResponse.ts +4 -4
  112. package/src/lib/shared/subscribableProbes.ts +1 -1
  113. package/src/lib/shared/tailProbeSlot.ts +1 -1
  114. package/src/lib/shared/types/AsyncComputed.ts +20 -0
  115. package/src/lib/shared/types/AsyncState.ts +13 -0
  116. package/src/lib/shared/types/CacheEntry.ts +7 -7
  117. package/src/lib/shared/types/CacheOptions.ts +23 -37
  118. package/src/lib/shared/types/CachePolicy.ts +25 -0
  119. package/src/lib/shared/types/CacheSnapshotEntry.ts +6 -5
  120. package/src/lib/shared/types/ErrorJsonSchemas.ts +8 -0
  121. package/src/lib/shared/types/HttpMethod.ts +3 -1
  122. package/src/lib/shared/types/InputCoercion.ts +17 -0
  123. package/src/lib/shared/types/{Subscribable.ts → NamedAsyncIterable.ts} +1 -1
  124. package/src/lib/shared/types/OutputWirePlan.ts +17 -0
  125. package/src/lib/shared/types/PagesScan.ts +7 -0
  126. package/src/lib/shared/types/PendingAsyncCells.ts +10 -0
  127. package/src/lib/shared/types/RawRemoteFunction.ts +6 -0
  128. package/src/lib/shared/types/RemoteCallable.ts +8 -7
  129. package/src/lib/shared/types/RemoteFunction.ts +16 -15
  130. package/src/lib/shared/types/ResolvedCells.ts +11 -0
  131. package/src/lib/shared/types/ReturnBody.ts +15 -0
  132. package/src/lib/shared/types/RpcBuildStamps.ts +23 -0
  133. package/src/lib/shared/types/RpcErrorGuard.ts +3 -8
  134. package/src/lib/shared/types/Socket.ts +4 -4
  135. package/src/lib/shared/types/SsrPayload.ts +5 -1
  136. package/src/lib/shared/types/StreamPolicy.ts +11 -0
  137. package/src/lib/shared/types/StreamedCells.ts +19 -0
  138. package/src/lib/shared/types/StreamedResolution.ts +24 -6
  139. package/src/lib/shared/types/TailHooks.ts +1 -1
  140. package/src/lib/shared/types/WireKind.ts +11 -0
  141. package/src/lib/shared/validationHttpError.ts +33 -0
  142. package/src/lib/shared/warmSeedKey.ts +16 -0
  143. package/src/lib/shared/wireJsonReplacer.ts +30 -0
  144. package/src/lib/shared/writeRpcDts.ts +11 -1
  145. package/src/lib/shared/writeTestSocketsDts.ts +1 -1
  146. package/src/lib/test/createTestApp.ts +19 -1
  147. package/src/lib/ui/README.md +1 -1
  148. package/src/lib/ui/activePendingCells.ts +14 -0
  149. package/src/lib/ui/compile/BLOCK_KEYWORDS.ts +21 -0
  150. package/src/lib/ui/compile/SSR_ESCAPE.ts +4 -1
  151. package/src/lib/ui/compile/UI_RUNTIME_IMPORTS.ts +35 -8
  152. package/src/lib/ui/compile/abideUiPlugin.ts +19 -2
  153. package/src/lib/ui/compile/analyzeComponent.ts +89 -6
  154. package/src/lib/ui/compile/assignmentTargetNames.ts +54 -0
  155. package/src/lib/ui/compile/asyncInterpolationFields.ts +152 -0
  156. package/src/lib/ui/compile/asyncValuePositionError.ts +21 -0
  157. package/src/lib/ui/compile/asyncValuePositionInterpolations.ts +64 -0
  158. package/src/lib/ui/compile/attrLiftPosition.ts +18 -0
  159. package/src/lib/ui/compile/cachedSourceFile.ts +40 -0
  160. package/src/lib/ui/compile/catchBinding.ts +7 -5
  161. package/src/lib/ui/compile/classifyInterpolationType.ts +46 -0
  162. package/src/lib/ui/compile/collectAbideDiagnostics.ts +96 -0
  163. package/src/lib/ui/compile/compileComponent.ts +19 -3
  164. package/src/lib/ui/compile/compileModule.ts +27 -58
  165. package/src/lib/ui/compile/compileSSR.ts +51 -12
  166. package/src/lib/ui/compile/compileShadow.ts +145 -18
  167. package/src/lib/ui/compile/composeProps.ts +12 -2
  168. package/src/lib/ui/compile/createShadowLanguageService.ts +116 -68
  169. package/src/lib/ui/compile/createShadowProgram.ts +35 -6
  170. package/src/lib/ui/compile/declaredNames.ts +36 -0
  171. package/src/lib/ui/compile/desugarSignals.ts +418 -36
  172. package/src/lib/ui/compile/expressionIsPrefixEvaluable.ts +19 -0
  173. package/src/lib/ui/compile/generateBuild.ts +52 -9
  174. package/src/lib/ui/compile/generateSSR.ts +242 -43
  175. package/src/lib/ui/compile/hoistableAwaits.ts +193 -0
  176. package/src/lib/ui/compile/hoistableChildRenders.ts +112 -0
  177. package/src/lib/ui/compile/interpolatedTemplateLiteral.ts +3 -1
  178. package/src/lib/ui/compile/interpolationClassifierForRoot.ts +37 -0
  179. package/src/lib/ui/compile/isSpuriousAsyncReadDiagnostic.ts +174 -0
  180. package/src/lib/ui/compile/liftAsyncSubExpressions.ts +166 -0
  181. package/src/lib/ui/compile/lowerAsyncInterpolations.ts +92 -0
  182. package/src/lib/ui/compile/lowerContext.ts +10 -0
  183. package/src/lib/ui/compile/lowerDocAccess.ts +26 -23
  184. package/src/lib/ui/compile/lowerScript.ts +40 -4
  185. package/src/lib/ui/compile/nodeAtShadowOffset.ts +28 -0
  186. package/src/lib/ui/compile/parseTemplate.ts +18 -1092
  187. package/src/lib/ui/compile/parseTemplateRecovering.ts +1385 -0
  188. package/src/lib/ui/compile/referencedIdentifiers.ts +35 -0
  189. package/src/lib/ui/compile/renameSignalRefs.ts +21 -22
  190. package/src/lib/ui/compile/seedTypeClassifierForRoot.ts +65 -0
  191. package/src/lib/ui/compile/shadowInterpolationClassifier.ts +47 -0
  192. package/src/lib/ui/compile/sourceFileOptionsSignature.ts +20 -0
  193. package/src/lib/ui/compile/structuralHeadTokens.ts +107 -0
  194. package/src/lib/ui/compile/templateSemanticTokens.ts +21 -0
  195. package/src/lib/ui/compile/templateStartOffset.ts +15 -0
  196. package/src/lib/ui/compile/tryPlan.ts +4 -3
  197. package/src/lib/ui/compile/types/AnalyzedComponent.ts +4 -0
  198. package/src/lib/ui/compile/types/AsyncInterpolationField.ts +19 -0
  199. package/src/lib/ui/compile/types/InterpolationClassifier.ts +12 -0
  200. package/src/lib/ui/compile/types/InterpolationKind.ts +7 -0
  201. package/src/lib/ui/compile/types/ParseDiagnostic.ts +8 -0
  202. package/src/lib/ui/compile/types/SeedTypeClassifier.ts +15 -0
  203. package/src/lib/ui/compile/types/TemplateNode.ts +36 -4
  204. package/src/lib/ui/compile/types/ValuePositionInterpolation.ts +13 -0
  205. package/src/lib/ui/compile/writtenTemplateNames.ts +84 -0
  206. package/src/lib/ui/computed.ts +28 -1
  207. package/src/lib/ui/createScope.ts +35 -68
  208. package/src/lib/ui/dom/anchoredBranch.ts +142 -0
  209. package/src/lib/ui/dom/appendText.ts +8 -3
  210. package/src/lib/ui/dom/awaitBlock.ts +43 -94
  211. package/src/lib/ui/dom/bindProp.ts +22 -0
  212. package/src/lib/ui/dom/bindableProp.ts +47 -0
  213. package/src/lib/ui/dom/cellPending.ts +24 -0
  214. package/src/lib/ui/dom/disposeRange.ts +2 -1
  215. package/src/lib/ui/dom/each.ts +26 -4
  216. package/src/lib/ui/dom/eachAsync.ts +39 -3
  217. package/src/lib/ui/dom/fillBoundary.ts +2 -3
  218. package/src/lib/ui/dom/fillRange.ts +4 -4
  219. package/src/lib/ui/dom/hydrate.ts +6 -12
  220. package/src/lib/ui/dom/isComment.ts +1 -1
  221. package/src/lib/ui/dom/matchingRangeClose.ts +29 -0
  222. package/src/lib/ui/dom/mount.ts +1 -2
  223. package/src/lib/ui/dom/mountChild.ts +9 -40
  224. package/src/lib/ui/dom/mountRange.ts +2 -3
  225. package/src/lib/ui/dom/mountStreamedChild.ts +84 -0
  226. package/src/lib/ui/dom/mountSwappableRange.ts +46 -4
  227. package/src/lib/ui/dom/mutateDocContainer.ts +65 -0
  228. package/src/lib/ui/dom/on.ts +2 -2
  229. package/src/lib/ui/dom/readCell.ts +40 -0
  230. package/src/lib/ui/dom/switchBlock.ts +30 -7
  231. package/src/lib/ui/dom/tryBlock.ts +230 -66
  232. package/src/lib/ui/dom/types/SwitchCase.ts +5 -1
  233. package/src/lib/ui/dom/when.ts +12 -2
  234. package/src/lib/ui/dom/withScope.ts +2 -2
  235. package/src/lib/ui/finalizeStreamedChildren.ts +89 -0
  236. package/src/lib/ui/flight.ts +56 -0
  237. package/src/lib/ui/html.ts +12 -1
  238. package/src/lib/ui/isolateCellBarrier.ts +17 -0
  239. package/src/lib/ui/linked.ts +48 -2
  240. package/src/lib/ui/remoteProxy.ts +84 -159
  241. package/src/lib/ui/renderChain.ts +49 -13
  242. package/src/lib/ui/renderToStream.ts +22 -18
  243. package/src/lib/ui/resumeSeedScript.ts +1 -1
  244. package/src/lib/ui/router.ts +63 -36
  245. package/src/lib/ui/runtime/AsyncCellError.ts +20 -0
  246. package/src/lib/ui/runtime/CELL_SEED.ts +14 -0
  247. package/src/lib/ui/runtime/CURRENT_BOUNDARY.ts +11 -0
  248. package/src/lib/ui/runtime/CURRENT_PATH.ts +29 -0
  249. package/src/lib/ui/runtime/RENDER.ts +10 -7
  250. package/src/lib/ui/runtime/RESUME.ts +4 -4
  251. package/src/lib/ui/runtime/STREAMED_CELLS.ts +57 -0
  252. package/src/lib/ui/runtime/ambientPathBacking.ts +32 -0
  253. package/src/lib/ui/runtime/applyPatchToTree.ts +1 -1
  254. package/src/lib/ui/runtime/blockId.ts +31 -0
  255. package/src/lib/ui/runtime/boundaryFor.ts +11 -0
  256. package/src/lib/ui/runtime/cellBarrierBacking.ts +30 -0
  257. package/src/lib/ui/runtime/createAsyncCell.ts +300 -0
  258. package/src/lib/ui/runtime/createDoc.ts +3 -42
  259. package/src/lib/ui/runtime/createEffectNode.ts +9 -0
  260. package/src/lib/ui/runtime/enterRenderPass.ts +5 -4
  261. package/src/lib/ui/runtime/flushEffects.ts +16 -6
  262. package/src/lib/ui/runtime/isAsyncFunction.ts +14 -0
  263. package/src/lib/ui/runtime/nextBlockId.ts +8 -7
  264. package/src/lib/ui/runtime/renderPath.ts +16 -0
  265. package/src/lib/ui/runtime/types/Boundary.ts +9 -0
  266. package/src/lib/ui/runtime/types/RenderContext.ts +9 -7
  267. package/src/lib/ui/runtime/types/SsrRender.ts +9 -2
  268. package/src/lib/ui/runtime/types/UiComponent.ts +1 -5
  269. package/src/lib/ui/runtime/withOptionalPath.ts +8 -0
  270. package/src/lib/ui/runtime/withPath.ts +16 -0
  271. package/src/lib/ui/runtime/withPathFrom.ts +20 -0
  272. package/src/lib/ui/seedStreamedResolution.ts +31 -6
  273. package/src/lib/ui/settleAsyncCells.ts +24 -0
  274. package/src/lib/ui/socketProxy.ts +1 -1
  275. package/src/lib/ui/startClient.ts +16 -31
  276. package/src/lib/ui/trackedComputed.ts +68 -0
  277. package/src/lib/ui/types/Scope.ts +8 -24
  278. package/src/lib/ui/watch.ts +3 -3
  279. package/src/serverEntry.ts +14 -0
  280. package/template/src/server/rpc/getHello.ts +15 -13
  281. package/template/test/app.test.ts +1 -1
  282. package/src/lib/server/runtime/devHotModuleResponse.ts +0 -41
  283. package/src/lib/shared/DEV_HOT_PREFIX.ts +0 -7
  284. package/src/lib/shared/UNREACHABLE_STATUSES.ts +0 -13
  285. package/src/lib/shared/hasReplayableRequest.ts +0 -17
  286. package/src/lib/shared/hydratingSlot.ts +0 -12
  287. package/src/lib/shared/outboxProbeSlot.ts +0 -20
  288. package/src/lib/shared/types/Outbox.ts +0 -9
  289. package/src/lib/shared/types/OutboxEntry.ts +0 -27
  290. package/src/lib/shared/types/SmartReadOptions.ts +0 -36
  291. package/src/lib/shared/wakeHydrationPeeks.ts +0 -20
  292. package/src/lib/ui/COMPONENT_WRAPPER_PREFIX.ts +0 -5
  293. package/src/lib/ui/compile/REACTIVE_CALLEES.ts +0 -11
  294. package/src/lib/ui/compile/assertRuntimeHelpersBound.ts +0 -80
  295. package/src/lib/ui/compile/markupTokens.ts +0 -313
  296. package/src/lib/ui/compile/structuralBlockTokens.ts +0 -100
  297. package/src/lib/ui/dom/applyResolved.ts +0 -87
  298. package/src/lib/ui/dom/mutateDocArray.ts +0 -38
  299. package/src/lib/ui/dom/scopeLabel.ts +0 -21
  300. package/src/lib/ui/dom/text.ts +0 -20
  301. package/src/lib/ui/history.ts +0 -108
  302. package/src/lib/ui/installHotBridge.ts +0 -95
  303. package/src/lib/ui/installInspectorBridge.ts +0 -140
  304. package/src/lib/ui/outbox.ts +0 -35
  305. package/src/lib/ui/persist.ts +0 -115
  306. package/src/lib/ui/rpcOutbox/createOutboxQueue.ts +0 -281
  307. package/src/lib/ui/rpcOutbox/outboxRegistry.ts +0 -69
  308. package/src/lib/ui/runtime/PATCH_BUS.ts +0 -28
  309. package/src/lib/ui/runtime/captureModelDoc.ts +0 -43
  310. package/src/lib/ui/runtime/hotInstances.ts +0 -10
  311. package/src/lib/ui/runtime/hotReloadEnabled.ts +0 -8
  312. package/src/lib/ui/runtime/hotReplace.ts +0 -38
  313. package/src/lib/ui/runtime/liveScopes.ts +0 -15
  314. package/src/lib/ui/runtime/localStoragePersistence.ts +0 -47
  315. package/src/lib/ui/runtime/registerHotInstance.ts +0 -23
  316. package/src/lib/ui/runtime/seedModelDoc.ts +0 -26
  317. package/src/lib/ui/runtime/types/HotInstance.ts +0 -22
  318. package/src/lib/ui/runtime/types/PatchEvent.ts +0 -16
  319. package/src/lib/ui/seedResolved.ts +0 -28
  320. package/src/lib/ui/sync.ts +0 -48
  321. package/src/lib/ui/types/History.ts +0 -14
  322. package/src/lib/ui/types/PersistHandle.ts +0 -11
  323. package/src/lib/ui/types/PersistenceStore.ts +0 -12
  324. package/src/lib/ui/types/ResolvedFrame.ts +0 -15
  325. package/src/lib/ui/types/SyncTransport.ts +0 -13
package/AGENTS.md CHANGED
@@ -1,478 +1,605 @@
1
1
  # AGENTS.md — abide complete surface map
2
2
 
3
- > This file is the exhaustive public-surface map of `@abide/abide`: every
4
- > `exports` key, grouped by namespace, with its import specifier and a one-line
5
- > spec. The README is the curated three-primitive intro; `CONTEXT.md` is the
6
- > domain glossary; `docs/adr/` holds the rationale behind decisions. Ground
7
- > rules: there are **no barrels** — every public name has its own module path,
8
- > and the namespace marks the side it runs on (`abide/server/*` server-only,
9
- > `abide/ui/*` client-only, `abide/shared/*` isomorphic same callable, same
10
- > behavior on both sides). Package `@abide/abide`, runtime Bun 1.3, one
11
- > direct dependency (TypeScript). Import specifiers below are `exports`-map
12
- > keys (`@abide/abide/server/GET`), not source file paths.
3
+ > This file is the exhaustive map of abide's public surface: every `exports` key
4
+ > grouped by namespace, with its import specifier and a one-line spec, so an
5
+ > agent can grasp the whole API in one read. For the curated three-primitive
6
+ > intro read `README.md`; for the domain glossary read `CONTEXT.md`; for the
7
+ > rationale behind a decision read `docs/adr/`.
8
+ >
9
+ > Ground rule **no barrels**. Every public name is its own module path; there
10
+ > is no umbrella `index.ts`, so importing one name never drags side-effecting
11
+ > siblings into the bundle. The namespace marks the side a name runs on:
12
+ > `abide/server/*` server-side, `abide/ui/*` client-side, `abide/shared/*`
13
+ > isomorphic (same callable, same behaviour on both sides). The package is
14
+ > `@abide/abide` (Bun ≥ 1.3.0, one direct dependency — TypeScript). Every import
15
+ > specifier below is `@abide/abide<exports-key>`; the file path after it is the
16
+ > source, not an import target.
13
17
 
14
18
  ## The premise
15
19
 
16
- One typed declaration fans out to every surface:
20
+ One declared RPC fans out to five surfaces:
17
21
 
18
22
  ```text
19
- src/server/rpc/getMessages.ts
20
-
21
- ├─ SSR / server await getMessages({ room }) in-process, no HTTP
22
- ├─ browser await getMessages({ room }) typed fetch proxy
23
- ├─ HTTP GET /rpc/getMessages?room=…
24
- ├─ CLI my-app get-messages --room …
25
- ├─ MCP tool: get-messages
26
- └─ OpenAPI operation in /openapi.json
23
+ export const getMessages = GET(fn, { schemas })
24
+
25
+ ┌───────────┬──────────────┼──────────────┬─────────────┐
26
+ ▼ ▼ ▼ ▼ ▼
27
+ SSR call browser fetch MCP tool CLI subcmd OpenAPI op
28
+ (bare, (same call, (read-only abide-cli /openapi.json
29
+ in-proc) swap to fetch) from schema) getMessages
27
30
  ```
28
31
 
29
- An `inputSchema` (any Standard Schema library zod, valibot, arktype,
30
- unadapted) is the gate: it unlocks the CLI, and for read-only methods
31
- (GET/HEAD) the MCP tool. A mutating method (POST/PUT/PATCH/DELETE) never
32
- auto-exposes to MCP — it requires explicit `clients: { mcp: true }`.
32
+ A `schemas.input` (any Standard Schema) unlocks the **CLI** on any RPC and
33
+ **MCP** for read-only methods (`GET`/`HEAD`). A mutating method
34
+ (`POST`/`PUT`/`PATCH`/`DELETE`) never auto-exposes to MCP it needs an explicit
35
+ `clients: { mcp: true }`. Explicit `clients` values always win; `browser`
36
+ defaults on. A socket with a `schema` auto-exposes to MCP and CLI regardless of
37
+ direction.
33
38
 
34
39
  ## File-based conventions
35
40
 
41
+ The bundler and route resolver read these paths by convention (dir aliases
42
+ `$server`, `$ui`, `$shared`, `$mcp`, `$cli` point at the matching `src/` dirs):
43
+
36
44
  | Path | Meaning |
37
- | --- | --- |
38
- | `src/server/rpc/<name>.ts` | One RPC per file; the export name must match the file stem; the file path becomes the URL `/rpc/<name>` (subdirectories nest into the path) |
39
- | `src/server/sockets/<name>.ts` | One broadcast socket per file; export name = file stem = topic name |
40
- | `src/mcp/prompts/<name>.md` | An MCP prompt template; `{{arg}}` placeholders become the prompt's arguments |
41
- | `src/mcp/resources/**` | Files served as MCP resources (gzip-embedded into builds) |
42
- | `src/server/config.ts` | Boot-time `env()` validation; eager-imported so a bad environment fails the boot |
43
- | `src/app.ts` | Optional `AppModule` hooks: `init`, `handle`, `handleError`, `health`, `forwardHeaders` |
44
- | `src/bundle/window.ts` | Optional `BundleWindow` default export configuring the desktop bundle's window and menus |
45
- | `src/ui/pages/**/page.abide` | A routed page; the directory path is the route a `[id]` folder is a path param, `[[id]]` an optional param, `[...rest]` a catch-all |
46
- | `src/ui/pages/**/layout.abide` | A layout wrapping the pages below it; its `{children()}` is the router outlet |
47
- | `src/ui/public/` | Static assets served as-is |
48
- | `src/.abide/*.d.ts` | Generated typing (rpc args for `url()`, page routes, health fields, test rpc/socket clients, public asset paths) |
49
- | `dist/` | Build output — `dist/_app` client bundle, `dist/cli-thin/<platform>/` CLI tarballs |
50
-
51
- Project import aliases resolve to the five top-level source dirs: `$server`,
52
- `$ui`, `$shared`, `$mcp`, `$cli` (e.g. `$server/rpc/getMessages`,
53
- `$ui/pages/...`). `$server/rpc/*` and `$server/sockets/*` are proxied into
54
- client bundles; any other `$server/*` import from client code is a
55
- side-crossing error.
45
+ |---|---|
46
+ | `src/server/rpc/<name>.ts` | One RPC per file; filename = export name = URL under `/rpc/`. The method helper picks the verb. Rewritten to `defineRpc` (server) / `remoteProxy` (client). |
47
+ | `src/server/sockets/<name>.ts` | One socket per file (`export const <name> = socket(...)`); path socket name. Rewritten to `defineSocket` (server) / `socketProxy` (client). |
48
+ | `src/mcp/prompts/<name>.md` | Markdown MCP prompt: frontmatter (description + arguments) + `{{arg}}` template body, compiled to `definePrompt`. |
49
+ | `src/mcp/resources/*` | MCP resource files served by the generated MCP server. |
50
+ | `src/server/config.ts` | Optional typed-env module: `export const config = env(schema)` validates `Bun.env` at boot (or the floor `export const config = Bun.env`). Eager-imported; deletable. |
51
+ | `src/app.ts` | Optional app hooks (`AppModule` shape): `init` / `handle` / `handleError` / `health` / `forwardHeaders`. Deletable. |
52
+ | `src/ui/pages/**/page.abide` | Folder-based route: a folder's `page.abide` mounts at that folder's URL. `[name]` / `[[name]]` (optional) / `[...rest]` (catch-all) are dynamic segments → `page.params`. |
53
+ | `src/ui/pages/**/layout.abide` | Wraps every page at/below its folder; renders the page where it calls `{children()}`; kept mounted across navigation. |
54
+ | `src/ui/app.html`, `src/ui/app.css` | Custom document shell and root stylesheet. |
55
+ | `src/ui/public/` | Static assets, served at the site root (`/<file>`). |
56
+ | `src/bundle/window.ts` | Optional default-exported `BundleWindow` for the desktop bundle (plus optional `src/bundle/disconnected.abide` connect-screen override). |
57
+ | `src/cli/banner.txt`, `src/cli/footer.txt` | CLI help chrome. |
58
+ | `src/.abide/*.d.ts` | Generated ambient types (`rpc.d.ts`, `routes.d.ts`, `health.d.ts`, `publicAssets.d.ts`, `testRpc.d.ts`, `testSockets.d.ts`). Do not hand-edit. |
59
+ | `dist/` | Build output: `dist/_app/` (prod client) or `dist/_app.gen-<id>/` (dev), `dist/app` (compiled binary), `dist/cli-*`. |
56
60
 
57
61
  ## CLI
58
62
 
63
+ `abide <command>` (the `abide` bin):
64
+
59
65
  | Command | Does |
60
- | --- | --- |
61
- | `bunx abide scaffold <name>` | Scaffolds the bundled template, installs it, and (interactive TTY only) starts the dev server; `--no-install` / `--no-dev` opt out |
62
- | `abide dev` | Dev orchestrator: builds the client, runs the server as a child, watches `src/`, rebuilds + restarts on change, live-reloads the browser |
63
- | `abide build` | One-shot client build into `dist/_app` (CI / static deploys) |
64
- | `abide start` | Runs the production server against an already-built `dist/` |
65
- | `abide run <file> [args...]` | Runs any script under the abide preload same runtime as the server (`.abide` compilation, `abide/*` + `$` alias resolution) |
66
- | `abide compile [--target=…] [--out=…]` | Compiles a standalone server executable (client assets embedded) |
67
- | `abide cli [--target=…] [--out=…] [--platforms=a,b,c]` | Builds the thin CLI binary (rpc manifest baked in) that talks to a remote server or starts a local one; `--platforms` cross-compiles into `dist/cli-thin/<platform>/` |
68
- | `abide bundle` | Assembles a movable, self-contained desktop app bundle (server binary + launcher + webview) for the host platform; unsigned |
69
- | `abide check` | Type-checks every `.abide` component's template + props through its shadow; non-zero exit on errors |
70
- | `abide lsp` | Runs the `.abide` language server over stdio (JSON-RPC) for editor diagnostics |
71
- | `abide init-agent` | Writes/refreshes the CLAUDE.md pointer to this surface map for non-scaffolded projects |
66
+ |---|---|
67
+ | `abide scaffold <name> [--no-install] [--no-dev]` | Scaffold a project from the bundled template, install it, and (TTY only) start dev. |
68
+ | `abide dev` | Dev orchestrator: build client, spawn the server child, watch `src/`, rebuild + restart on change, browser live-reload. |
69
+ | `abide build` | Single client build into `dist/_app/`, no server (CI / static deploys). |
70
+ | `abide start` | Run the production server against an already-built `dist/`. |
71
+ | `abide run <file> [args...]` | Run an arbitrary script under the abide preload (same runtime as the server); argv after the file is forwarded verbatim. |
72
+ | `abide compile [--target=<bun-…>] [--out=<path>]` | Build a standalone server executable. |
73
+ | `abide cli [--target=…] [--out=…] [--platforms=<a,b,c>]` | Build the thin CLI binary (manifest baked in, ships the compiled server beside it); `--platforms` cross-compiles into `dist/cli-thin/<platform>/`. |
74
+ | `abide bundle` | Assemble a self-contained desktop app bundle for the host platform (`.app` on macOS), unsigned. |
75
+ | `abide check` | Type-check every `.abide` component's template + props through its shadow program; non-zero on error. |
76
+ | `abide lsp` | Run the `.abide` language server over stdio (JSON-RPC) for editor diagnostics. |
77
+ | `abide init-agent` | Write/refresh the abide agent-guide pointer in the project root `CLAUDE.md`. |
72
78
 
73
79
  For tests, add `preload = ["@abide/abide/preload"]` under `[test]` in
74
- `bunfig.toml` and use `bun test`.
80
+ `bunfig.toml` and run `bun test`.
75
81
 
76
82
  ## Authoring contracts
77
83
 
78
- **RPC** the handler receives the schema-validated args
79
- (`InferOutput<inputSchema>`); typed generics on the helper are a compile error
80
- type the parameter, let the body infer. Inside it, `request()` / `cookies()`
81
- / `server()` read the request scope. Return `json(data)` (or `jsonl` / `sse`
82
- for streams, `error` / `redirect`, or a raw `Response`). Options:
83
- `inputSchema`, `outputSchema`, `filesSchema` (validates uploaded `File` parts,
84
- kept out of the JSON-Schema projection), `clients: { browser, mcp, cli }`,
85
- `crossOrigin` (exempts a mutating rpc from the same-origin CSRF gate),
86
- `timeout` (handler deadline in ms 504 on every surface, composed into
87
- `request().signal`), `maxBodySize` (per-rpc 413 cap), `outbox` (durable
88
- delivery, mutating methods only). Query args on GET/HEAD/DELETE travel as
89
- strings coerce in the schema (`z.coerce.number()`). A body rpc also accepts
90
- a `FormData` in place of typed args (the upload escape hatch): text fields
91
- validate as args, `File` parts validate against `filesSchema`.
92
-
93
- **Consuming an rpc** the bare call `fn(args, opts?)` IS the smart read:
94
- cached, coalesced, reactive, stale-while-revalidate for replayable (GET/HEAD)
95
- reads; the second arg takes `{ ttl, tags, throttle, debounce, shared, n }`
96
- (retention and the refetch clock not transport). `ttl` defaults to `0` on
97
- the server (coalesce-only — the request is the atomic unit, nothing is
98
- retained past it) and `Infinity` on the client (retain until invalidate/
99
- refresh). `shared: true` selects the process-level store instead of the
100
- request-scoped default (server) — it does not by itself retain, pair it with
101
- an explicit `ttl` (e.g. `{ shared: true, ttl: Infinity }`) to memoise across
102
- requests, for an external endpoint, never per-user data; on the client it is
103
- a no-op (one tab store). A read with no request in flight (e.g. a background
104
- job) also resolves against the process-level store. During SSR the same call
105
- resolves
106
- in-process and its value is baked into the HTML so hydration starts warm —
107
- there is no `cache()` wrapper; the bare call carries the caching. Around it:
108
- `fn.raw(args, init?)` returns the raw `Response` (per-call transport options —
109
- `signal`, `headers`, `keepalive`, … — live here); `fn.refresh(args?)`
110
- refetches keeping the stale value visible; `fn.patch(args?, updater)` mutates
111
- the retained value locally (absent on streaming rpcs); `fn.peek(args?)` reads
112
- it synchronously; `fn.pending(args?)` / `fn.refreshing(args?)` are reactive
113
- probes; `fn.error(args?)` is the rpc's last typed error; `fn.watch(args?,
114
- handler)` pipes each resolved value to a handler (client-only; SSR-inert);
115
- `fn.isError(e, kind?)` type-guards a caught error against the rpc's declared
116
- error kinds; `fn.outbox()` exposes a durable rpc's parked-write queue. A
117
- handler that returns `jsonl()`/`sse()` makes the bare call return a
118
- `Subscribable` (`for await` it) detected at build, nothing to declare;
119
- awaiting a streaming call is a compile error.
120
-
121
- **Typed errors** declare a constructor with
122
- `error.typed(name, status, schema?)` and `return` it from the handler; the
123
- client's `HttpError` then carries `kind` (the name) and `data` (the schema's
124
- payload), narrowed via `rpc.isError`. The framework reserves
125
- `kind: 'validation'` (422, `data: ValidationErrorData`) and `kind: 'queued'`
126
- (a durable call parked while unreachable; `data` is the parked `OutboxEntry`).
127
-
128
- **Durable outbox** an `outbox: true` (mutating) rpc still fetches and throws
129
- normally, but an unreachable server (transport failure or 502/503/504/52x)
130
- parks the request for replay as a side-effect and throws `kind: 'queued'`;
131
- once a backlog exists new calls park straight to the tail so writes stay
132
- ordered. Nothing auto-drains: replay via `rpc.outbox.retry()` or the global
133
- `outbox.retry()`; cancel via an entry's `controller.abort()`.
134
-
135
- **Socket** `socket<T>(opts)` or `socket({ schema, … })` (with a schema, `T`
136
- infers and publishes validate). Options: `tail` (retained frames, default 1 —
137
- `tail: 0` opts out), `ttl` (retained frames expire lazily after N ms),
138
- `clientPublish` (accept browser/HTTP publishes, off by default), `clients`
139
- (mcp/cli exposure; a schema flips both on by default). The socket IS the
140
- `AsyncIterable` iterating is the live stream, no replay. Members:
141
- `broadcast(msg)` (isomorphic publish server fans out in-process + to remote
142
- subscribers, client sends a validated `pub` frame), `tail(count?)` (a
143
- subscription seeded with retained frames), `peek()` (latest retained frame),
144
- `refresh()` (drop local frames and re-pull the server tail; server-side
145
- no-op), `watch(handler)` `watch(socket, handler)` (client-only, SSR-inert),
146
- `pending()` / `refreshing()` / `done()` / `error()` (reactive stream probes),
147
- plus `name` and `clients`.
148
-
149
- **Pages and layouts** `src/ui/pages/blog/[id]/page.abide` serves
150
- `/blog/<id>`; the param arrives as a prop (`const { id } = props()`) and on the
151
- reactive `page.params`. A `[[name]]` folder is an optional segment (the route
152
- matches with or without it; the param is absent when unmatched), `[...rest]`
153
- a catch-all capturing the remaining path (last segment only). One matcher
154
- resolves routes on both sides at the first position where two matching
155
- patterns differ, literal beats `[name]` beats `[[name]]` beats `[...rest]`. A `layout.abide` wraps every page below its directory
156
- and renders the page at its `{children()}` outlet. Links are plain `<a href>`
157
- (the router intercepts in-app hrefs); build paths with `url()` and navigate
158
- programmatically with `navigate()`.
159
-
160
- **app.ts / config.ts** — `src/app.ts` optionally exports the `AppModule`
161
- hooks: `init({ server })` (boot; may return a cleanup run on SIGINT/SIGTERM),
162
- `handle(request, next)` (single middleware), `handleError(error, request)`,
163
- `health(request)` (fields merged into the `/__abide/health` payload — public
164
- and unauthenticated, keep it cheap), and `forwardHeaders` (extra inbound
165
- header names forwarded onto in-process rpc requests beyond the built-in
166
- auth/identity set). `src/server/config.ts` holds the `env(schema)` call so a
167
- bad environment fails at boot.
168
-
169
- ## `.abide` template grammar
170
-
171
- A component file is: an optional leading `<script>` (imports + author scope),
172
- markup, optional `<style>` blocks. The compiler emits a client build and an
173
- SSR render from the same parse; `abide check` / the LSP type-check the
174
- template through a generated shadow. HTML comments are dropped; a bare
175
- `<template>` is an inert element.
176
-
177
- Reactive state is reached through **imported primitives**, resolved by import
178
- binding (alias-safe) and lowered by the compiler inside a component you read
179
- and write the declared names as plain variables (`{count}`,
180
- `onclick={() => (count += 1)}`); there is no `.value` in `.abide` authoring
181
- and no `$state` sigils. In plain `.ts` modules the same imports are runtime
182
- cells read/written through `.value`.
183
-
184
- | Primitive | Import | Spec |
185
- | --- | --- | --- |
186
- | `state(initial, transform?)` | `@abide/abide/ui/state` | Writable cell. Plain `state(v)` lowers to a serializable doc slot (SSR-resumable); with `transform` the gate runs on every write (`(next, previous) => stored`) |
187
- | `state.computed(fn)` | member of `state` | Read-only derived value, lazy, never serialized |
188
- | `state.linked(fn, transform?)` | member of `state` | Writable cell re-seeded whenever the thunk's dependencies change |
189
- | `state.share(key, value)` / `state.shared(key)` | members of `state` | Put a named value on the ambient scope / read the closest ancestor's |
190
- | `watch(source, handler)` | `@abide/abide/ui/watch` | The single reaction primitive (client-only, stripped from SSR). Sources: a bare thunk `watch(() => …)` (auto-tracked effect), a state cell, a cell array, a socket/stream (`handler(frame)` per frame with reconnect replay), an rpc (`watch(fn, args?, handler)` — runs the smart read, `handler(value)` on each change). Returns a scope-tied disposer |
191
- | `html(str)` / `` html`…` `` | `@abide/abide/ui/html` | Brands trusted raw HTML so `{expr}` inserts nodes instead of escaped text; plain `{value}` always escapes |
192
- | `props()` | `@abide/abide/ui/props` | The prop reader, resolved by import binding (alias-safe) like `state`: `const { name = fallback, ...rest } = props()`; a page/layout's declared props are additive with its route-param shape. `children` is an ordinary declared prop (`const { children } = props<{ children: Snippet }>()`), not ambient |
193
-
194
- Bindings and directives (the attribute kinds `readAttributes` parses):
195
-
196
- | Form | Spec |
197
- | --- | --- |
198
- | `{expr}` | Text interpolation, escaped; a snippet or `html`-branded value mounts as nodes |
199
- | `name={expr}` | Attribute/prop bound to an expression |
200
- | `name="a {expr} b"` | Interpolated attribute a literal `{` in a quoted value always interpolates (write `&lbrace;` for a literal brace) |
201
- | `{...expr}` | Spread props onto a component, attributes onto a native element (rejected on `<template>`) |
202
- | `on<event>={fn}` | Event listener (`onclick`, `onsubmit`, …); on a component it is a checked callback prop |
203
- | `bind:value={cell}` | Two-way input/select/textarea binding. `<input type="number"/"range">` writes back a number; `<select>` re-applies against late-mounting options and `<select multiple>` binds an array of selected values |
204
- | `bind:value={{ get, set }}` | Writable-computed binding: read via `get()`, write via `set(next)` |
205
- | `bind:checked={cell}` / `bind:group={cell}` | Checkbox boolean / radio-group value (SSR emits boolean attributes bare — `checked`, `open`, `selected` on the matching option) |
206
- | `class:name={cond}` | Toggles a class; merges with a reactive `class` base in one effect |
207
- | `style:property={value}` | Sets one style property; merges with a reactive `style` base |
208
- | `attach={fn}` | Runs `fn(element)` at build time; an optional returned teardown runs on dispose |
209
-
210
- Control flow is mustache blocks (`{#…}` open, `{:…}` branch, `{/…}` close —
211
- the close must name its block, and a branch outside its block is a parse
212
- error):
213
-
214
- | Block | Spec |
215
- | --- | --- |
216
- | `{#if cond}…{:else if cond}…{:else}…{/if}` | Conditional chain (the branch keyword is `{:else if}`, with a space) |
217
- | `{#for item, i of list by key}…{:catch e}…{/for}` | Keyed list; `, i` index and `by` key optional; `{#for await item of asyncIterable}` renders rows as they arrive (its `{:catch}` shows the stream error) |
218
- | `{#await p}…{:then v}…{:catch e}…{:finally}…{/await}` | Async block. The branch form streams: SSR flushes the shell and streams the fragment out of order. The head form `{#await p then v}` is blocking — rendered inline (depth-first, serial) during the SSR pass |
219
- | `{#switch subject}{:case match}…{:default}…{/switch}` | Multi-branch on a subject; only branches render stray content is a compile error |
220
- | `{#try}…{:catch e}…{:finally}…{/try}` | Synchronous error boundary around a build/reactive throw |
221
- | `{#snippet name(args)}…{/snippet}` | Declares a reusable builder, called as an interpolation: `{name(args)}`; a snippet value passes through props like any other value |
222
-
223
- Components are capitalised tags (`<Panel prop={x}>…</Panel>`); nested content
224
- becomes the component's `children` prop an ordinary declared prop of type
225
- `Snippet`, read with `const { children } = props<{ children: Snippet }>()`
226
- and called as `{children()}` — the single fill point (`{#if children}
227
- {children()}{:else}…{/if}` for a fallback; there are no named slots and no
228
- `<slot>` element). Slotted content (`<Panel>…</Panel>`) and an explicit
229
- `children={aSnippet}` attribute set the same prop — slotted content rides in
230
- as the trailing prop layer, so it wins over an explicit `children` attribute
231
- on the same tag (`mergeProps`, last layer wins per key). A layout's
232
- `{children()}` is the route outlet.
233
-
234
- `<script>` and `<style>` are **not component-root-only**: either may sit
235
- inside a control-flow branch, scoped to that branch. A nested `<script>`
236
- declares branch-local `state` / `state.computed` / `state.linked` the same
237
- imported way (re-seeded per mount; static `import` statements are illegal
238
- there — imports live in the leading `<script>`). A **root** `<style>` is
239
- component-scoped; a nested `<style>` scopes to its sibling subtree only.
240
-
241
- Removed forms throw migration errors at parse time: the `<slot>` element (use
242
- a declared `children: Snippet` prop, called `{children()}`), `<template
243
- name>` snippets (use `{#snippet}`), and all `<template
244
- if/each/await/switch/…>` control flow (use `{#…}` blocks).
245
-
246
- ## Server surface `abide/server/*`
247
-
248
- ### RPC`@documentation rpc`
249
-
250
- - `@abide/abide/server/GET` GET rpc helper: `export const x = GET(handler, opts?)` inside `src/server/rpc/`; the bundler rewrites it to the server dispatcher or the browser proxy — calling it outside an rpc module throws.
251
- - `@abide/abide/server/POST` — POST rpc helper (mutating: accepts `outbox`, JSON/FormData body).
252
- - `@abide/abide/server/PUT`PUT rpc helper (mutating).
253
- - `@abide/abide/server/PATCH` — PATCH rpc helper (mutating).
254
- - `@abide/abide/server/DELETE` — DELETE rpc helper (mutating; args travel in the query string).
255
- - `@abide/abide/server/HEAD` HEAD rpc helper (read-only).
256
-
257
- ### Responses — `@documentation response`
258
-
259
- - `@abide/abide/server/json` — `json(data, init?)`: JSON response with `Cache-Control: no-store` default; `json(undefined)` emits 204 and round-trips back to `undefined`; carries the value type so the rpc's `Return` infers.
260
- - `@abide/abide/server/jsonl` — `jsonl(asyncIterable, init?)`: JSON Lines streaming response; consumer cancel flows into the generator's `return`; a generator throw becomes a final `{"$error": message}` line.
261
- - `@abide/abide/server/sse` — `sse(asyncIterable, init?)`: Server-Sent Events response with a 15s keepalive comment; errors emit an `event: error` frame carrying only the message.
262
- - `@abide/abide/server/error` — `error(status, message?, init?)`: plain-text error response (message defaults to the reason phrase); the caller's await throws `HttpError`. Member `error.typed(name, status, schema?)` declares a reusable typed-error constructor the handler returns (see Authoring contracts).
263
- - `@abide/abide/server/redirect` `redirect(url, status = 302, init?)`: redirect response accepting relative URLs; 301/302/303/307/308.
264
-
265
- ### Request scope`@documentation request-scope`
266
-
267
- - `@abide/abide/server/request` — `request()`: the inbound `Request` for the in-flight SSR/rpc pass (AsyncLocalStorage); throws outside a request scope.
268
- - `@abide/abide/server/cookies` — `cookies()`: the request's cookie jar (Bun `CookieMap`) — reads parse the inbound header; `set`/`delete` flush as `Set-Cookie` when the handler returns.
269
- - `@abide/abide/server/server` — `server()`: the active `Bun.serve` instance; a no-op stand-in during in-process dispatch (CLI/MCP/tests); throws before boot.
270
-
271
- ### Configuration`@documentation configuration`
272
-
273
- - `@abide/abide/server/env` — `env(schema)`: validates `Bun.env` against a Standard Schema at module top level (synchronous; every issue reported at once) and returns the typed config; the schema also projects the bundle's first-run setup form.
274
-
275
- ### Sockets — `@documentation sockets`
276
-
277
- - `@abide/abide/server/socket` — `socket<T>(opts?)` / `socket({ schema, tail, ttl, clientPublish, clients })`: declares the broadcast topic inside `src/server/sockets/<name>.ts`; see Authoring contracts for the full `Socket<T>` member surface.
278
-
279
- ### Agent `@documentation agent`
280
-
281
- - `@abide/abide/server/agent``agent(engine, messages)`: runs a provider engine (an `@abide/<provider>` package) against the app's own MCP surface inside an rpc's request scope and returns its `AgentFrame` stream; the handler picks the transport (`jsonl(agent(…))` / `sse(agent(…))`). The module also exports the neutral contract types: `NeutralMessage` (user/assistant/tool turns), `AgentFrame` (`text` deltas, `tool_use`, `tool_result`, `done` with a stop reason), `AgentSurface` (the gated tool/prompt/resource surface), and `AgentEngine` (surface + messages + origin in, frames out).
282
-
283
- ### Server plumbing`@documentation plumbing`
284
-
285
- - `@abide/abide/server/AppModule` — the type of `src/app.ts`'s optional hooks (`init`, `handle`, `handleError`, `health`, `forwardHeaders`).
286
- - `@abide/abide/server/InspectorContext` — the capability object core injects into `@abide/inspector` (`loadSurface`, `cacheSnapshot`, `inFlightSnapshot`, `onRecord`, app identity); keeps the inspector a pure consumer.
287
- - `@abide/abide/server/rpc/defineRpc` — `defineRpc(method, url, handler, opts?)`: the server-side construction the bundler rewrites rpc helper calls into — validation, timeout composition, client-flag resolution, registry entry.
288
- - `@abide/abide/server/sockets/defineSocket` — `defineSocket(name, opts?)`: server-side socket construction (retained-tail buffer with lazy TTL eviction, per-subscriber queues, `server.publish` fan-out).
289
- - `@abide/abide/server/prompts/definePrompt``definePrompt(name, opts)`: registers an MCP prompt; the resolver plugin generates one call per `src/mcp/prompts/<name>.md`.
290
- - `@abide/abide/server/prompts/renderPromptTemplate` — `renderPromptTemplate(template, args)`: substitutes `{{name}}` placeholders in a prompt body (missing args collapse to empty).
291
-
292
- ## Isomorphic surface `abide/shared/*`
293
-
294
- ### Cache mutators `@documentation cache`
295
-
296
- - `@abide/abide/shared/refresh` — `refresh(selector?, args?)`: refetch every cached read matching the selector, keeping the stale value visible until the fresh one swaps in. Selector grammar: `(fn, args)` exact call, `(fn)` every args-variant, `({ tags })` a tagged group, `()` everything. `fn.refresh(args?)` is the pre-bound sugar.
297
- - `@abide/abide/shared/patch` — `patch(fn, args?, updater)` / `patch({ tags }, updater)`: mutate the retained value(s) in place — reactive, no network; the optimistic-update / socket-frame primitive. `fn.patch(…)` is the sugar.
298
-
299
- ### Probes — `@documentation probes`
300
-
301
- Probes report, never act reading one opens no fetch and no stream.
302
-
303
- - `@abide/abide/shared/pending` — `pending(selector?, args?)`: reactive "no value yet" probe over calls and streams (global, per-rpc, per-call, tagged, per-subscribable; a durable rpc's parked writes count too).
304
- - `@abide/abide/shared/refreshing` `refreshing(selector?, args?)`: "holding a value while a fresher one is in flight" — the SWR reload / stream-reconnect badge.
305
- - `@abide/abide/shared/peek` `peek(fn, args?)` / `peek(socket)`: the retained value (or latest frame), synchronously, `T | undefined`; reactive inside a tracking scope.
306
- - `@abide/abide/shared/done` — `done(subscribable)`: true once a stream closed (stream-only; a cache read's "done" is `!pending && !refreshing`).
307
- - `@abide/abide/shared/online``online()`: reactive connectivity probe — browser `online`/`offline` events; server-side it reflects the *calling client's* reported connectivity (always true during SSR and outside a scope).
308
-
309
- ### Errors`@documentation response`
310
-
311
- - `@abide/abide/shared/HttpError` — thrown by rpc calls on non-2xx; carries `status`, `statusText`, the raw `response`, and — for typed/validation/queued errors — `kind` + `data`.
312
- - `@abide/abide/shared/ValidationErrorData` — the `data` shape of a `kind: 'validation'` failure: the raw Standard Schema `issues` plus a `fields` (field → first message) map.
313
-
314
- ### Schema projection — `@documentation rpc`
315
-
316
- - `@abide/abide/shared/withJsonSchema` `withJsonSchema(schema, toJsonSchema)`: attaches the `toJSONSchema()` projection to a Standard Schema whose library lacks one, feeding OpenAPI, MCP, CLI help, and the bundle setup form.
317
-
318
- ### Observability `@documentation observability`
319
-
320
- - `@abide/abide/shared/health` — `health()`: reactive backend health `{ reachable, abide, name, version, …app health-hook fields }`, polled from `/__abide/health` only while a tracking scope reads it; SSR-seeded so hydration starts warm; constant `{ reachable: true }` on the server. The `AppHealth`/`AppHealthMap` types augment from the generated `health.d.ts`.
321
- - `@abide/abide/shared/reachable` — `await reachable(host?)`: outbound reachability, same callable both sides. The first call probes (HEAD) and starts a TTL background poll; later calls answer instantly off the warm value. Any completed HTTP response counts as reachable. No host asks about the app's own backend: constant true on the server and on a loopback origin (dev, desktop bundle works offline); a deployed origin probes like any host. The browser probes no-cors and composes `navigator.onLine` in at read time (loopback exempt). Tuned by `ABIDE_REACHABLE_TTL` / `ABIDE_REACHABLE_TIMEOUT` (server env; the browser runs the defaults).
322
- - `@abide/abide/shared/log` — the unified logger: `log(...)` / `.warn` / `.error` / `.trace` on the app's always-on channel, every record carrying request-scope context (short trace id, +elapsed, method+path); member `log.channel(name)` returns the same shape on a DEBUG-gated diagnostic channel. Renders tsv (default) or JSON per `ABIDE_LOG_FORMAT`.
323
- - `@abide/abide/shared/trace` — `trace()`: the current request's W3C `traceparent` (client-side: the trace of the request that rendered the page), or undefined outside any scope.
324
-
325
- ### Page — `@documentation page`
326
-
327
- - `@abide/abide/shared/page` — the reactive page proxy: `page.route`, `page.params`, `page.url` (browser-space on both sides, mount base included), `page.navigating`; isomorphic, re-runs readers across navigations.
328
-
329
- ### URL `@documentation url`
330
-
331
- - `@abide/abide/shared/url` — `url(path, params?/args?)`: resolves any in-app URL to its base-correct form — a page route literal interpolates its `[name]` / `[[name]]` / `[...rest]` params (typed via `PathParams`; an absent optional drops its segment), a GET rpc path serializes typed args to the query, anything else is base-prefixed. Also exports the augmentable `RpcRoutes` / `PageRoutes` / `PublicAssets` maps and the `PathParams<P>` type.
332
-
333
- ### Templating — `@documentation templating`
334
-
335
- - `@abide/abide/shared/snippet` — `snippet(payload)`: brands a snippet payload so a `{expr}` interpolation mounts it (client: a DOM builder; server: the rendered string); the compiler wraps `{#snippet}` bodies in this. Also exports the `Snippet<Args>` type — a callable `(...args: Args) => SnippetValue`, generic over its call arguments (`children` is `Snippet`, invoked `children()`; a row snippet is `Snippet<[Item]>`, invoked `row(item)`) — plus `SnippetValue` (the internal payload brand) and `snippetPayload(value)` (a branded value's payload, or undefined for plain values).
336
-
337
- ### Shared plumbing — `@documentation plumbing`
338
-
339
- - `@abide/abide/shared/createSubscriber` — `createSubscriber(start)`: open-on-first-tracked-read / close-on-last-reader resource lifecycle grounded in the signal core; the substrate under `health()`, `online()`, and the tail probes.
340
-
341
- ## UI surface `abide/ui/*` (client-only)
342
-
343
- ### Reactive state `@documentation reactive-state`
344
-
345
- - `@abide/abide/ui/state` — the `state` primitive: `state(initial, transform?)` writable cell, `state.computed(fn)` read-only derived, `state.linked(fn, transform?)` writable-reseeded, `state.share(key, value)` / `state.shared(key)` ambient context. In `.abide` files the compiler lowers reads/writes to plain variable syntax; in `.ts` the cell is read/written through `.value`.
346
- - `@abide/abide/ui/watch` — `watch(source, handler)`: the single reaction primitive over a thunk, cell, cell array, socket/stream, or rpc (see the grammar table). Client-only; the compiler strips author calls from SSR, and the `socket.watch` / `fn.watch` instance sugar is SSR-inert.
347
- - `@abide/abide/ui/props` `props<T>()`: the prop reader, resolved by import binding (alias-safe) like `state`; a required import — there is no ambient `props()`. Destructure declared props off it (`const { name, ...rest } = props<T>()`); a page/layout's declared `T` is additive with its auto-typed route-param shape. `children` is an ordinary declared prop, not ambient: `const { children } = props<{ children: Snippet }>()` (`Snippet` from `@abide/abide/shared/snippet`).
348
-
349
- ### Templating`@documentation templating`
350
-
351
- - `@abide/abide/ui/html` — `html(string)` / `` html`…` ``: brands trusted raw HTML for unescaped interpolation; the tag does not escape its interpolations — only feed it values you trust.
352
-
353
- ### Navigate — `@documentation navigate`
354
-
355
- - `@abide/abide/ui/navigate` `navigate(path, params?, options?)`: typed programmatic navigation off the route map; params interpolate through `url()` (base-correct). Options `{ replace, keepScroll }`. The module also exports `navigatePath(path, options?)` (already-resolved paths — the router's own entry, no re-basing) and the `NavigateOptions` type.
356
-
357
- ### Outbox — `@documentation ui`
358
-
359
- - `@abide/abide/ui/outbox` — the global reactive outbox: `outbox()` lists every durable rpc's undelivered entries (each tagged with its `rpc`), member `outbox.retry()` drains every queue. Empty list server-side. Types `GlobalOutbox`, `GlobalOutboxEntry`.
360
-
361
- ### UI plumbing — `@documentation plumbing`
362
-
363
- Compiler/runtime machinery published so generated code, the type shadow, and
364
- tests can import it, not for app code.
365
-
366
- - `@abide/abide/ui/effect` — `effect(fn)`: the raw auto-tracked effect the compiler emits for bindings; authors use `watch`. Returns a disposer; SSR strips author calls.
367
- - `@abide/abide/ui/currentScope` `scope()`: the ambient lexical scope the internal lowering host for `state`/`effect` (`derive`/`linked`/`effect`/`share` land here).
368
- - `@abide/abide/ui/enterRenderScope` — `enterScope()`: opens an isolated scope for an SSR render; returns the previous scope to restore.
369
- - `@abide/abide/ui/exitRenderScope` — `exitScope(previous)`: restores the scope `enterScope` saved.
370
- - `@abide/abide/ui/router` `router(...)`: the client router — fills layout/page chains into comment-marker outlet boundaries, intercepts in-app links, buckets/restores scroll per history entry.
371
- - `@abide/abide/ui/startClient` — `startClient(...)`: the client entry — reads every `__SSR__` field into its shared slot (cache seed, health seed, client timeout, resume manifest), hydrates the chain, starts the router.
372
- - `@abide/abide/ui/renderToStream``renderToStream(render)`: out-of-order SSR streaming — shell first, then one `<abide-resolve>` fragment per streaming await block in completion order; blocking (`then`-head) awaits render inline.
373
- - `@abide/abide/ui/remoteProxy` — `remoteProxy(method, url, opts?)`: the browser-side rpc stub the bundler emits (fetch, decode, HttpError, outbox parking, streaming); the `DurableOptions` type rides along.
374
- - `@abide/abide/ui/socketProxy` — `socketProxy(name)`: the browser-side socket stub — the identical `Socket<T>` shape over the page's lazily-opened multiplexed ws channel.
375
- - `@abide/abide/ui/runtime/escapeKey` JSON-Pointer-escapes one reactive-doc path key (`~`→`~0`, `/`→`~1`).
376
- - `@abide/abide/ui/runtime/nextBlockId` the next await/try block id in the current render pass (document order, shared across inlined children).
377
- - `@abide/abide/ui/runtime/enterRenderPass` — marks entry into a render/mount; the outermost resets the block-id counter.
378
- - `@abide/abide/ui/runtime/exitRenderPass`unwinds `enterRenderPass`'s depth.
379
- - `@abide/abide/ui/dom/mount` — mounts a top-level page/layout into a host under an ownership scope; returns the unmount.
380
- - `@abide/abide/ui/dom/mountChild` — mounts a nested child component as a comment-marker range (dev builds also register it with the hot bridge).
381
- - `@abide/abide/ui/dom/mountSlot` mounts a component's passed-children content as a marker-bounded range.
382
- - `@abide/abide/ui/dom/outlet` — a layout's outlet: an empty `<!--abide:outlet-->…<!--/abide:outlet-->` boundary the router fills.
383
- - `@abide/abide/ui/dom/hydrate` — adopts server-rendered DOM instead of rebuilding: runs the build with a claim cursor over the existing nodes.
384
- - `@abide/abide/ui/dom/skeleton`the parsed-once static-structure clone path every bound element builds through; element holes by path, blocks by anchor comments.
385
- - `@abide/abide/ui/dom/anchorCursor` — positions a skeleton-anchored block/slot at its `<!--a-->` anchor, in clone and hydrate modes alike.
386
- - `@abide/abide/ui/dom/cloneStatic` — appends a fully-static subtree (no bindings, control flow, or listeners) by cloning.
387
- - `@abide/abide/ui/dom/appendStatic` a static text node: created (create mode) or claimed from server-rendered text (hydrate mode).
388
- - `@abide/abide/ui/dom/appendText` — a reactive `{expr}` text node under a parent.
389
- - `@abide/abide/ui/dom/appendTextAt` a reactive text node mounted at a skeleton anchor (text interleaved with element siblings).
390
- - `@abide/abide/ui/dom/appendSnippet` — mounts a `{snippet(args)}` interpolation's builder into a marker-bounded range.
391
- - `@abide/abide/ui/dom/text` a text node whose content tracks a reactive read.
392
- - `@abide/abide/ui/dom/attr` — binds an element attribute to a read (boolean true bare attribute, false/nullish → removed).
393
- - `@abide/abide/ui/dom/on` — attaches an event listener whose removal is registered with the ownership scope.
394
- - `@abide/abide/ui/dom/attach` — runs an `attach={fn}` attachment and registers its optional teardown.
395
- - `@abide/abide/ui/dom/bindSelectValue` two-way `<select>` binding that re-applies the selection when the option set changes (late-mounting `{#for}`/async options; `multiple` binds an array).
396
- - `@abide/abide/ui/dom/each` keyed `{#for}` runtime: marker-bounded rows reconciled by key.
397
- - `@abide/abide/ui/dom/eachAsync` — `{#for await}` runtime: rows append/reconcile as the AsyncIterable yields.
398
- - `@abide/abide/ui/dom/when` `{#if}` runtime (single-branch swap in a marker-bounded range).
399
- - `@abide/abide/ui/dom/switchBlock` `{#switch}` runtime (also `{#if}` chains with `{:else if}` branches).
400
- - `@abide/abide/ui/dom/awaitBlock` — `{#await}` runtime: pending → resolved/error branch swap, teardown-generation guarded.
401
- - `@abide/abide/ui/dom/tryBlock` — `{#try}` runtime: synchronous error boundary around a subtree build.
402
- - `@abide/abide/ui/dom/applyResolved` — consumes a streamed SSR `<abide-resolve>` chunk, swapping it into its await boundary (the bundle-side counterpart of the doc stream's inline scripts).
403
- - `@abide/abide/ui/dom/mergeProps` — composes a child's props from explicit thunk runs, spread layers, and the trailing children layer.
404
- - `@abide/abide/ui/dom/spreadProps` wraps a `{...source}` spread layer so every key resolves to a live value thunk.
405
- - `@abide/abide/ui/dom/restProps` — the live unconsumed-props object behind `const { …, ...rest } = props()`.
406
- - `@abide/abide/ui/dom/spreadAttrs` — spreads an object's keys onto a native element (`<div {...rest}>`), keys enumerated once.
407
- - `@abide/abide/ui/dom/readCall` guarded method call on a reactive-doc read (the `model.draft.trim()` lowering).
84
+ **RPC** (`src/server/rpc/<name>.ts`). `export const x = METHOD(handler, opts?)`.
85
+ The handler receives the validated args `StandardSchemaV1.InferOutput<schemas.input>`
86
+ when a schema is present, otherwise its own declared first parameter (or `undefined`
87
+ for a nullary handler); you never pass `<Args, Return>` call generics. It reaches
88
+ request context via `request()` (the inbound `Request`) and `cookies()` (the jar),
89
+ and returns a `Response` canonically `json(...)` (success body → the caller's
90
+ `Return`), `jsonl(...)`/`sse(...)` (streaming), `error(...)` / `error.typed(...)()`
91
+ (non-2xx, body typed `never`), `redirect(...)`, or a hand-built `Response`
92
+ (`Return` falls back to `unknown`). Typed errors are inferred from the
93
+ `error.typed(...)` branches a handler returns — there is no `errors:` option.
94
+
95
+ `opts` (`RpcSharedOpts`, all optional): `schemas: { input?, output?, files? }`
96
+ (the ADR-0020 namespace `input` validates args and drives their type, `output`
97
+ is the success-body schema for OpenAPI 200 / MCP `outputSchema` and never drives
98
+ arg inference, `files` validates multipart File parts and merges them into the
99
+ args bag); `clients: { browser?, mcp?, cli? }` (surface-exposure flags);
100
+ `crossOrigin` (exempt a mutating RPC from the same-origin CSRF gate); `maxBodySize`
101
+ (pre-parse body-byte cap, 413 past it); `timeout` (per-RPC handler deadline in ms,
102
+ a 504 on every surface, composed into `request().signal`). Read helpers (GET/HEAD)
103
+ additionally accept `cache` (`ttl`/`tags`/`throttle`/`debounce`/`shared`) and
104
+ `stream` (replay depth); these are a compile error on the mutating helpers. There
105
+ is **no** `outbox` option. Query/path/form args auto-coerce from the endpoint's
106
+ typed shape (ADR-0028 build-time plan) — no `z.coerce` needed; a value that will
107
+ not parse stays a string so the schema raises an honest 422.
108
+
109
+ Consume forms (`RemoteFunction`): the bare `fn(args)` **is** the smart read
110
+ cached, coalesced, SWR-reactive; decodes by Content-Type, throws `HttpError` on
111
+ non-2xx. There is no call-site options argument on the bare call. Members:
112
+ `fn.raw(args, opts?)` (raw `Response`, no decode/throw), `fn.refresh(args?)`
113
+ (refetch keeping the stale value visible), `fn.patch(...)` (in-place cache
114
+ mutation; fetch-only, absent on streaming RPCs), `fn.peek(args?)` (retained value,
115
+ sync), `fn.pending(args?)`, `fn.refreshing(args?)`, `fn.error(args?)` (this RPC's
116
+ last typed error), `fn.isError(caught, 'name')` (typed guard), and client-only
117
+ `fn.watch(handler)` / `fn.watch(args, handler)`. A streaming handler
118
+ (`jsonl`/`sse`) makes the bare call return a `NamedAsyncIterable<Frame>`
119
+ synchronously `for await (… of fn(args))`, never `await`.
120
+
121
+ **Socket** (`src/server/sockets/<name>.ts`). `export const x = socket(opts?)`.
122
+ `opts` (`SocketOptions`): `tail` (retention count kept frames for late joiners
123
+ / reconnects; server default 1), `ttl` (evict retained frames older than N ms,
124
+ lazy), `clientPublish` (allow publishes over the wire; off by default), `schema`
125
+ (validate publish payloads; flips mcp/cli `clients` on), `clients`. A `Socket<T>`
126
+ extends `AsyncIterable<T>` (bare `for await` is the live stream, no replay) with
127
+ `publish(frame)`, `tail(count?)` (subscription seeded from the retained tail),
128
+ `peek()`, `pending()`/`refreshing()`/`done()`/`error()`, `refresh()`, and
129
+ `watch(handler)`. HTTP face at `/__abide/sockets/<name>`: `GET` reads the retained
130
+ tail, `POST` publishes (only when `clientPublish`).
131
+
132
+ **Page / layout** (`src/ui/pages/**`). `page` (isomorphic `PageSnapshot` proxy)
133
+ exposes `route`, `params`, `url` (browser-space `URL`, base-prefixed), and
134
+ `navigating`; read a field inside an effect/derived and it re-runs on navigation.
135
+ `url(path, params?, query?)` builds base-correct links; `navigate(path, )`
136
+ performs typed in-app SPA navigation (params first for `[name]` routes, then
137
+ `{ replace?, keepScroll? }`). A layout renders the active page via `{children()}`.
138
+
139
+ **`app.ts` / `config.ts`.** `app.ts` default- or named-exports the `AppModule`
140
+ hooks (all optional; `init({ server })` may return a cleanup run on
141
+ SIGINT/SIGTERM; `handle(request, next)` is single middleware; `health(request)`
142
+ merges into `/__abide/health`, runs before `handle`, and is public). `config.ts`
143
+ exports `config` `env(schema)` (validated, typed, throws on bad config at boot)
144
+ or the unvalidated `Bun.env` floor.
145
+
146
+ **The isomorphism move.** There is no `cache()` wrapper. A bare smart RPC call
147
+ read inline during SSR is captured in the per-request cache; the runtime
148
+ snapshots each settled entry into a wire-safe form serialized into the HTML, and
149
+ the client seeds its store from it on hydration — the same call hydrates warm
150
+ instead of re-firing. Streaming reads are snapshotted again after the stream
151
+ drains and seeded over the wire.
152
+
153
+ ## .abide template grammar
154
+
155
+ A `.abide` component is HTML with a leading `<script>` (its component script);
156
+ `<script>` and `<style>` may also sit **inside a control-flow branch**, scoped to
157
+ that branch (a nested `<script>` declares branch-local `state`/`state.computed`/
158
+ `state.linked`, re-seeded per mount, and takes **no** module imports imports
159
+ live only in the leading script; a nested `<style>` scopes to its sibling
160
+ subtree). A *root* `<style>` is component-scoped. Reactive primitives are reached
161
+ through their own imported bindings (alias-safe) `state` from `abide/ui/state`,
162
+ `watch` from `abide/ui/watch`, `html` from `abide/ui/html`, `snippet` from
163
+ `abide/shared/snippet` never through `scope()` (internal plumbing). The one
164
+ ambient reader is `props()` (no import).
165
+
166
+ Reactive state:
167
+
168
+ | Form | Meaning |
169
+ |---|---|
170
+ | `state(initial, transform?)` | Writable cell; read/write via `.value`; `transform(next, prev)` gates writes. |
171
+ | `state.computed(fn)` | Read-only cell derived from other cells (lazy, never serialized). |
172
+ | `state.linked(fn, transform?)` | Writable cell reseeded when the thunk's deps change. |
173
+ | `watch(source, handler)` | The single reaction primitive: over a cell, a cell array, a socket/stream, or an RPC; bare `watch(thunk)` is an auto-tracked effect. Client-only. |
174
+ | `props()` | Ambient prop reader: `const { name = fallback, ...rest } = props()`. |
175
+
176
+ Bindings and directives (attribute kinds `event` / `bind` / `class` / `style` /
177
+ `attach` / spread, plus plain `expression` / static):
178
+
179
+ | Form | Meaning |
180
+ |---|---|
181
+ | `{expr}` | Reactive text (escaped); an `html`-branded value inserts unescaped raw HTML. |
182
+ | `name={expr}` | Reactive attribute. |
183
+ | `on<event>={fn}` | Event listener (`onclick`, `oninput`, `onsubmit`, …). |
184
+ | `bind:value` / `bind:checked` / `bind:group` | Two-way form binds. |
185
+ | `bind:value={{ get, set }}` | Derived two-way binding. |
186
+ | `class:name={cond}` | Toggle a class. |
187
+ | `style:property={value}` | Set one style property. |
188
+ | `attach={fn}` | Run `fn(element)` at mount; its return is the teardown. |
189
+ | `{...spread}` | Spread an object's keys as attributes (element) or props (component). |
190
+
191
+ Control flow mustache `{#…}` blocks (NOT `<template>`):
192
+
193
+ | Block | Form |
194
+ |---|---|
195
+ | Conditional | `{#if}` / `{:else if}` / `{:else}` / `{/if}` |
196
+ | Keyed list | `{#for item, i of list by key}` / `{/for}` |
197
+ | Async list | `{#for await item of source}` / `{/for}` (over an `AsyncIterable`) |
198
+ | Promise | `{#await p}` / `{:then v}` / `{:catch e}` / `{:finally}` / `{/await}` |
199
+ | Switch | `{#switch subject}` / `{:case v}` / `{:default}` / `{/switch}` |
200
+ | Error boundary | `{#try}` / `{:catch}` / `{:finally}` / `{/try}` |
201
+ | Snippet | `{#snippet name(args)}…{/snippet}`, called `{name(args)}` |
202
+
203
+ Components are capitalised tags; content nested in them renders where the
204
+ component calls `{children()}` (`{#if children}{children()}{:else}…{/if}` is the
205
+ fallback). The `<slot>` element, the `<template name>` snippet form, and
206
+ `<template if>` / `<template each>` / control flow were **removed** a bare
207
+ `<template>` is now an inert element, and any removed form throws a migration
208
+ error. The branch keyword is `{:else if}` (a space).
209
+
210
+ ## Server surface abide/server/*
211
+
212
+ ### RPC helpers @documentation rpc
213
+
214
+ - `@abide/abide/server/GET` declares a read (GET) RPC; accepts `RpcReadOpts`
215
+ (shared opts + `cache`/`stream`); query args. Bundler-rewritten; calling the
216
+ bare helper throws.
217
+ - `@abide/abide/server/POST` declares a mutating (POST) RPC; `RpcSharedOpts`
218
+ only (`cache`/`stream` are a compile error); JSON-body (or FormData) args.
219
+ - `@abide/abide/server/PUT` — mutating PUT RPC; body args; `RpcSharedOpts`.
220
+ - `@abide/abide/server/PATCH` mutating PATCH RPC; body args; `RpcSharedOpts`.
221
+ - `@abide/abide/server/DELETE` mutating DELETE RPC; query args; `RpcSharedOpts`.
222
+ - `@abide/abide/server/HEAD` read HEAD RPC alongside GET; query args;
223
+ `RpcReadOpts`.
224
+
225
+ ### Responses@documentation response
226
+
227
+ - `@abide/abide/server/json` `json(data, init?)`: JSON `TypedResponse<T>` with
228
+ RPC defaults (`no-store`), wire-encoding Set/Map/bigint/Date; `json(undefined)`
229
+ 204. `T` drives `Return` inference.
230
+ - `@abide/abide/server/jsonl` `jsonl(iterable, init?)`: wraps an
231
+ `AsyncIterable<Frame>` as `application/jsonl` (one JSON value per line); a
232
+ generator error emits a final `{"$error":…}` line.
233
+ - `@abide/abide/server/sse` `sse(iterable, init?)`: wraps an
234
+ `AsyncIterable<Frame>` as `text/event-stream` with 15s keepalive comments;
235
+ errors emit an `event: error` frame.
236
+ - `@abide/abide/server/error` `error(status, message?, init?)`:
237
+ `text/plain` `TypedResponse<never>`. `error.typed(name, status, schema?)`
238
+ declares a reusable typed-error constructor driving `fn.isError(e, 'name')`.
239
+ - `@abide/abide/server/redirect` — `redirect(url, status=302, init?)`:
240
+ `TypedResponse<never>`, accepts relative URLs, `no-store`, status restricted to
241
+ 301/302/303/307/308.
242
+
243
+ ### Request scope @documentation request-scope
244
+
245
+ - `@abide/abide/server/request` `request(): Request` the in-flight inbound
246
+ request (ALS-scoped); throws outside a request scope.
247
+ - `@abide/abide/server/cookies` `cookies(): Bun.CookieMap` the request's
248
+ cookie jar; reads parse `Cookie`, writes flush as `Set-Cookie` on return.
249
+ - `@abide/abide/server/server` `server(): Bun.Server` the active server; a
250
+ no-op in-process server for CLI/MCP/test dispatch; throws before init.
251
+
252
+ ### Configuration@documentation configuration
253
+
254
+ - `@abide/abide/server/env``env(schema)`: validate `Bun.env` against a Standard
255
+ Schema at module top level (synchronous; all issues at once) and return the
256
+ typed config; also registers the schema for the launcher setup form.
257
+
258
+ ### Sockets@documentation sockets
259
+
260
+ - `@abide/abide/server/socket` — `socket(opts?)` / `socket({ schema })` declares a
261
+ broadcast topic returning `Socket<T>`; opts `tail`/`ttl`/`clientPublish`/
262
+ `schema`/`clients` (server-only; the client stub discards them).
263
+
264
+ ### Agent — @documentation agent
265
+
266
+ - `@abide/abide/server/agent` — `agent(engine, messages): AsyncIterable<AgentFrame>`
267
+ runs a provider `AgentEngine` against the current request's MCP surface
268
+ (forwarding caller auth); the handler picks transport via `jsonl`/`sse`. Exports
269
+ `NeutralMessage`, `AgentFrame`, `AgentSurface`, `AgentEngine` types.
270
+
271
+ ### Server plumbing@documentation plumbing
272
+
273
+ - `@abide/abide/server/AppModule` — type of the optional `src/app.ts` hooks
274
+ (`forwardHeaders`/`init`/`handle`/`handleError`/`health`).
275
+ - `@abide/abide/server/InspectorContext` — type of the capability object core
276
+ injects into `@abide/inspector` when `ABIDE_ENABLE_INSPECTOR=true`.
277
+ - `@abide/abide/server/rpc/defineRpc`bundler-emitted RPC builder: resolves
278
+ `clients`, validates input/files, applies `timeout`, registers the entry.
279
+ - `@abide/abide/server/sockets/defineSocket` — bundler-emitted socket builder:
280
+ per-subscriber queue + retained tail, optional `ttl`/`schema`, Bun-native
281
+ fan-out.
282
+ - `@abide/abide/server/prompts/definePrompt` — resolver-emitted prompt builder
283
+ from `src/mcp/prompts/<name>.md`; registers with the MCP dispatcher.
284
+ - `@abide/abide/server/prompts/renderPromptTemplate` — substitutes `{{name}}`
285
+ placeholders in a prompt template body (missing args → empty string).
286
+
287
+ ## Isomorphic surface — abide/shared/*
288
+
289
+ ### RPC schema projection @documentation rpc
290
+
291
+ - `@abide/abide/shared/withJsonSchema` — `withJsonSchema(schema, toJsonSchema)`
292
+ attaches a `toJSONSchema()` projection to a Standard Schema whose library lacks
293
+ one native (feeds OpenAPI / MCP / CLI / setup form).
294
+
295
+ ### Error responses @documentation response
296
+
297
+ - `@abide/abide/shared/HttpError` — the error class thrown by a remote call on
298
+ non-2xx: `status`, `statusText`, raw `response`, optional `kind`/`data` (set for
299
+ a typed error or a 422 validation failure).
300
+ - `@abide/abide/shared/ValidationErrorData`type of `HttpError.data` when
301
+ `kind === 'validation'`: `{ issues, fields }` (raw Standard Schema issues + a
302
+ field→first-message map).
303
+
304
+ ### Cache mutation — @documentation cache
305
+
306
+ - `@abide/abide/shared/patch` — `patch(fn, args?, updater)` / `patch({tags},
307
+ updater)`: reactively mutate the retained value of matching cached reads in
308
+ place, no network — the optimistic-update / real-time primitive.
309
+ - `@abide/abide/shared/refresh` — `refresh(selector?, args?)`: refetch every
310
+ matching cached read, keeping the stale value visible (`refreshing()` true)
311
+ until fresh swaps in.
312
+
313
+ ### Page@documentation page
314
+
315
+ - `@abide/abide/shared/page``page`: isomorphic reactive `PageSnapshot` proxy
316
+ (`route`/`params`/`url`/`navigating`); reading a field in a tracking scope
317
+ re-runs on navigation.
318
+
319
+ ### Probes — @documentation probes
320
+
321
+ - `@abide/abide/shared/pending` — `pending(source?, args?): boolean` — reactive
322
+ "no value yet" over cached calls and tail streams.
323
+ - `@abide/abide/shared/peek` — `peek(source, args?)` — the currently-retained
324
+ value synchronously, triggering nothing; `undefined` when nothing retained.
325
+ - `@abide/abide/shared/refreshing` — `refreshing(source?, args?): boolean` —
326
+ reactive "holding a value while a fresher source is in flight".
327
+ - `@abide/abide/shared/done` — `done(subscribable): boolean`reactive terminal
328
+ read: true once a stream closed.
329
+ - `@abide/abide/shared/online` — `online(): boolean` reactive connectivity
330
+ probe (browser online/offline; server reflects the caller's reported state).
331
+
332
+ ### URL — @documentation url
333
+
334
+ - `@abide/abide/shared/url` — `url(path, ...args): string` resolves any in-app URL
335
+ base-correctly (RPC query, page params, or asset); external paths pass through.
336
+ Exports `PathParams` and augmentable `RpcRoutes`/`PageRoutes`/`PublicAssets`.
337
+
338
+ ### Templating — @documentation templating
339
+
340
+ - `@abide/abide/shared/snippet` — `snippet(payload)` brands a snippet payload so a
341
+ `{expr}` interpolation mounts it (the compiler wraps a `{#snippet}` body); also
342
+ exports `SnippetValue` and `Snippet<Args>` types.
343
+
344
+ ### Observability — @documentation observability
345
+
346
+ - `@abide/abide/shared/health` — `health(): HealthState` — reactive backend-health
347
+ read (reachability + the app's `health()` fields), reader-driven poll of
348
+ `/__abide/health`; composes `navigator.onLine`.
349
+ - `@abide/abide/shared/log``log`: the unified request-scope-aware logger
350
+ (`log(...)`, `.warn`/`.error`/`.trace`, `.channel(name)` for a DEBUG-gated
351
+ channel); TSV by default, JSON under `ABIDE_LOG_FORMAT=json`.
352
+ - `@abide/abide/shared/reachable` — `reachable(host?): Promise<boolean>`
353
+ isomorphic outbound reachability HEAD probe, cached per TTL; any response counts
354
+ reachable, only connection failure/timeout is not.
355
+ - `@abide/abide/shared/trace``trace(): string | undefined` — the current
356
+ request's W3C `traceparent` (server from ALS, browser from `__SSR__`).
357
+
358
+ ### Isomorphic plumbing — @documentation plumbing
359
+
360
+ - `@abide/abide/shared/createSubscriber` — `createSubscriber(start)`: abide-ui
361
+ subscriber grounded in the signal core (open-on-first-tracked-read,
362
+ close-on-last-reader).
363
+
364
+ ## UI surface — abide/ui/* (client-only)
365
+
366
+ ### Reactive state — @documentation reactive-state
367
+
368
+ - `@abide/abide/ui/state` — `state(initial, transform?)` writable cell (`.value`);
369
+ members `state.computed(fn)` (read-only derived), `state.linked(fn, transform?)`
370
+ (writable, reseeded from a thunk), `state.share(key, value)` / `state.shared(key)`
371
+ (ambient scope context).
372
+ - `@abide/abide/ui/watch` — `watch(source, handler)` the single reaction primitive
373
+ (cell / cell array / socket-stream / RPC); bare `watch(thunk)` is an auto-tracked
374
+ effect; returns a scope-tied disposer; SSR-inert.
375
+ - `@abide/abide/ui/props` — `props()` ambient prop reader; compiler-lowered inside
376
+ a component; throws if called directly.
377
+
378
+ ### Templating@documentation templating
379
+
380
+ - `@abide/abide/ui/html` — `html\`…\`` (or `html(string)`) returns branded
381
+ **unescaped** raw HTML for `{expr}` insertion; interpolations are not
382
+ auto-escaped; nullish empty.
383
+
384
+ ### Navigate@documentation navigate
385
+
386
+ - `@abide/abide/ui/navigate` — `navigate(path, ...rest)` typed in-app SPA
387
+ navigation (params first for `[name]` routes, then `{ replace?, keepScroll? }`);
388
+ builds through `url()`.
389
+
390
+ ### UI plumbing @documentation plumbing
391
+
392
+ - `@abide/abide/ui/effect` — `effect(fn)` internal reactive effect (tracks reads,
393
+ re-runs, returns a disposer); compiler-emitted authors use `watch`.
394
+ - `@abide/abide/ui/currentScope` — `scope()` resolves the current lexical scope;
395
+ the internal lowering host the compiler targets.
396
+ - `@abide/abide/ui/enterRenderScope` — `enterScope()` establishes a fresh isolated
397
+ SSR-render scope, returning the previous.
398
+ - `@abide/abide/ui/exitRenderScope` — `exitScope(previous)` restores the scope
399
+ `enterScope` saved.
400
+ - `@abide/abide/ui/router` — `router(host, loaders, layoutLoaders?, probe?)` the
401
+ History-API client router: match, code-split, mount a diffed outlet chain, drive
402
+ SPA nav with scroll restoration.
403
+ - `@abide/abide/ui/startClient` — `startClient(routes, layoutRoutes?, target)` the
404
+ client entry: read `window.__SSR__`, seed cache/streamed/warm state, start the
405
+ router; returns a disposer.
406
+ - `@abide/abide/ui/renderToStream` — `renderToStream(render)` out-of-order SSR
407
+ streaming generator: shell first, then one `<abide-resolve>` fragment per
408
+ streaming `{#await}` in completion order.
409
+ - `@abide/abide/ui/remoteProxy` — `remoteProxy(method, url, options?)`
410
+ bundler-target client substitute for a server RPC (fetch over the network; does
411
+ the client pre-flight input validation; attaches the real `.watch`).
412
+ - `@abide/abide/ui/socketProxy` — `socketProxy(name)` bundler-target client
413
+ substitute for a server socket (subscribes over the multiplexed ws).
414
+ - `@abide/abide/ui/settleAsyncCells` — the SSR await-barrier draining the
415
+ request-scoped pending-cell list; client no-op.
416
+ - `@abide/abide/ui/flight` — server-only flight-starter hoisting a hoistable
417
+ await's promise into the sync prefix so independent flights overlap.
418
+ - `@abide/abide/ui/isolateCellBarrier` — runs a hoisted child render under its own
419
+ async-cell barrier so its cells isolate from siblings; client passthrough.
420
+ - `@abide/abide/ui/finalizeStreamedChildren` — the when-to-stream decision run
421
+ after a component body walk; fills each hoistable child's reserved output slot.
422
+ - `@abide/abide/ui/runtime/withPath` — pushes one escaped render-path segment for
423
+ the duration of a synchronous build.
424
+ - `@abide/abide/ui/runtime/renderPath` — composes a streamed child's ordinal
425
+ segment onto the ambient render path and returns the boundary id.
426
+ - `@abide/abide/ui/runtime/escapeKey` — escapes one key to an RFC 6901
427
+ JSON-Pointer token so a `/`-bearing key survives a `/`-joined render path.
428
+ - `@abide/abide/ui/runtime/nextBlockId` — the next await/try block id in the
429
+ current render pass, namespaced by render path.
430
+ - `@abide/abide/ui/runtime/blockId` — allocates a render-path-namespaced await/try
431
+ block id with a per-path document-order counter.
432
+ - `@abide/abide/ui/runtime/enterRenderPass` — marks entry into a render/mount;
433
+ depth 0 clears the per-path block-id counters.
434
+ - `@abide/abide/ui/runtime/exitRenderPass` — marks exit, unwinding the render-pass
435
+ depth.
436
+ - `@abide/abide/ui/dom/mount` — mounts a top-level page/layout into a host under an
437
+ ownership scope; returns a disposer.
438
+ - `@abide/abide/ui/dom/mountChild` — mounts a `<Child/>` as a marker-bounded range
439
+ (no wrapper element).
440
+ - `@abide/abide/ui/dom/mountStreamedChild` — client adopter for a hoistable child:
441
+ adopts an inlined range or a streamed boundary.
442
+ - `@abide/abide/ui/dom/mergeProps` — composes a child's props from ordered layers
443
+ (explicit runs, spreads, trailing `children`), last-wins per key.
444
+ - `@abide/abide/ui/dom/spreadProps` — wraps a `{...source}` spread layer so each
445
+ key resolves to a live value thunk.
446
+ - `@abide/abide/ui/dom/restProps` — the live `...rest` of a `props()` destructure.
447
+ - `@abide/abide/ui/dom/bindProp` — parent half of a component `bind:prop`
448
+ (annotates the prop thunk with a write-back channel).
449
+ - `@abide/abide/ui/dom/bindableProp` — child half of a two-way prop (the writable
450
+ cell the child writes/forwards).
451
+ - `@abide/abide/ui/dom/spreadAttrs` — spreads an object's keys onto a native
452
+ element (`on<event>` keys attach listeners, others bind as reactive attrs).
453
+ - `@abide/abide/ui/dom/readCall` — guarded method call on a reactive-document read
454
+ so throws name the authored scope path.
455
+ - `@abide/abide/ui/dom/readCell` — unified read for a `computed`/`linked`
456
+ reference (async peek / derive call / sync `.value`).
457
+ - `@abide/abide/ui/dom/cellPending` — whether a `{#if}`/`{#switch}` async subject
458
+ is still loading (render no branch while pending).
459
+ - `@abide/abide/ui/dom/mutateDocContainer` — in-place container mutation lowered to
460
+ clone-mutate-replace so a patch emits and readers wake.
461
+ - `@abide/abide/ui/dom/hydrate` — adopts server-rendered DOM in place with a claim
462
+ cursor (attach listeners/effects, no re-render); returns a disposer.
463
+ - `@abide/abide/ui/dom/appendText` — reactive `{expr}` interpolation (escaped text
464
+ / snippet builder / `html\`\`` raw).
465
+ - `@abide/abide/ui/dom/appendTextAt` — reactive `{expr}` mounted at a skeleton
466
+ anchor comment, interleaved with element siblings.
467
+ - `@abide/abide/ui/dom/appendSnippet` — mounts a `{snippet(args)}` builder's nodes
468
+ in a marker-bounded range.
469
+ - `@abide/abide/ui/dom/appendStatic` — a static text node, created or claimed from
470
+ SSR text.
471
+ - `@abide/abide/ui/dom/cloneStatic` — appends a fully-static bindingless subtree via
472
+ one cached-template deep clone.
473
+ - `@abide/abide/ui/dom/skeleton` — clones a template and locates its bound
474
+ holes/anchors for a subtree carrying bindings/control-flow.
475
+ - `@abide/abide/ui/dom/anchorCursor` — positions a skeleton-anchored control-flow
476
+ block or slot at its `<!--a-->` anchor.
477
+ - `@abide/abide/ui/dom/mountSlot` — mounts a component's `{children()}` content
478
+ (parent children or fallback) as a marker-bounded range.
479
+ - `@abide/abide/ui/dom/outlet` — a layout's outlet boundary the router fills with
480
+ the next chain layer.
481
+ - `@abide/abide/ui/dom/attr` — binds an element attribute to `read()` via one
482
+ effect (`name={expr}`).
483
+ - `@abide/abide/ui/dom/on` — attaches an event listener pinned to the owning scope
484
+ (`on<event>`).
485
+ - `@abide/abide/ui/dom/attach` — runs an `attach={fn}` against an element and
486
+ registers its teardown.
487
+ - `@abide/abide/ui/dom/bindSelectValue` — two-way `bind:value` for `<select>`
488
+ (reactive selection + change write-back; `multiple` = array membership).
489
+ - `@abide/abide/ui/dom/each` — keyed list binding (`{#for … by key}`),
490
+ marker-range rows reconciled by key with minimal DOM moves.
491
+ - `@abide/abide/ui/dom/eachAsync` — async keyed list over an AsyncIterable
492
+ (`{#for await}`); rows append as it yields; SSR renders none.
493
+ - `@abide/abide/ui/dom/when` — conditional swappable range (`{#if}`/`{:else}`) with
494
+ an optional pending state.
495
+ - `@abide/abide/ui/dom/awaitBlock` — await-block runtime across
496
+ pending/resolved/error branches with SSR resume adoption (`{#await}`).
497
+ - `@abide/abide/ui/dom/tryBlock` — error-boundary block catching thrown/async-cell
498
+ errors into a catch branch (`{#try}`).
499
+ - `@abide/abide/ui/dom/switchBlock` — multi-branch swappable range (first matching
500
+ `{:case}` else `{:default}`; also backs `{:else if}` chains).
408
501
 
409
502
  ## Build / tooling
410
503
 
411
- ### Building — `@documentation building`
504
+ ### Building — @documentation building
505
+
506
+ - `@abide/abide/build` — `build(opts?)` builds the client bundle into `dist/` and
507
+ concurrently writes `src/.abide/*.d.ts`; never throws. Options `cwd`/`minify`/
508
+ `compress`/`clean`/`exitOnFailure`/`dev`.
509
+ - `@abide/abide/compile` — `compile(opts?)` produces a standalone Bun server
510
+ executable (runs the client build first to embed assets); returns the binary
511
+ path.
512
+
513
+ ### Tooling — @documentation plumbing
514
+
515
+ - `@abide/abide/ui-plugin` — the `abide-ui` `BunPlugin` that loads and compiles
516
+ `.abide` components (and pulls scoped `<style>` into browser bundles).
517
+ - `@abide/abide/preload` — the Bun preload module registering the UI plugin, the
518
+ resolver plugin, and the `.css` no-op loader (used by `bunfig` `[test]` and the
519
+ CLI `--preload`).
520
+ - `@abide/abide/resolver-plugin` — `abideResolverPlugin({ cwd?, embedAssets?,
521
+ target? })` wiring every `abide:*` virtual module, the `$server`/`$ui`/`$shared`/
522
+ `$mcp`/`$cli` aliases, and the per-target RPC/socket rewrite (with the
523
+ client-side server-code-leak guard).
524
+ - `@abide/abide/tsconfig` — the base `tsconfig.app.json` for consuming apps to
525
+ `extends`.
526
+
527
+ ## Desktop bundle — @documentation bundle
528
+
529
+ - `@abide/abide/server/appDataDir` — `appDataDir()` returns the bundle's per-user
530
+ data dir keyed by the injected program name; cwd-independent, pure.
531
+ - `@abide/abide/bundle/BundleWindow` — type of the default export from
532
+ `src/bundle/window.ts`: `{ title?, width?, height?, menu?, config? }`.
533
+ - `@abide/abide/bundle/BundleMenu` — `{ label, items: BundleMenuItem[] }`, a
534
+ top-level bundle menu.
535
+ - `@abide/abide/bundle/BundleMenuItem` — a menu entry: separator, or `emit` (a
536
+ page `abide:menu` event), or `navigate`.
537
+ - `@abide/abide/bundle/onMenu` — `onMenu(handler)` / `onMenu(name, handler)`
538
+ subscribes to bundle menu-click events; returns an unsubscribe; inert in a
539
+ plain tab / SSR.
540
+ - `@abide/abide/bundle/bundled` — `bundled(): boolean` — am I part of the abide
541
+ desktop bundle (client reads `window.__ABIDE_BUNDLE__`, server the parent-pid
542
+ env).
543
+
544
+ ## MCP — @documentation mcp
545
+
546
+ - `@abide/abide/mcp/createMcpServer` — `createMcpServer(opts?)` constructs the
547
+ framework-generated MCP server bound to the RPC/socket registries; returns
548
+ `{ handle(request) }` (the `/__abide/mcp` handler). Tools derive from surfaces
549
+ with `clients.mcp`.
412
550
 
413
- - `@abide/abide/build` — `build({ cwd, … })`: builds the client bundle into `dist/_app` (`.abide` loader, virtual-module resolver, optional Tailwind); production builds also emit `.gz` siblings; staged and atomically swapped so a live dev server never sees a half-built dist.
414
- - `@abide/abide/compile` — `compile({ cwd, target?, outfile? })`: produces a standalone server executable (runs the client build first and embeds the compressed assets); returns the binary path.
415
-
416
- ### Tooling plumbing — `@documentation plumbing`
417
-
418
- - `@abide/abide/preload` — the Bun preload installing the `.abide` loader, the virtual-module resolver, and a `.css` no-op loader — the same runtime for the server, scripts (`abide run`), and `bun test`.
419
- - `@abide/abide/resolver-plugin` — the resolver plugin itself: `$`-alias + virtual-module (`abide:*`) resolution, rpc/socket module rewriting, side-crossing guards.
420
- - `@abide/abide/ui-plugin` — the Bun plugin that compiles `.abide` single-file components to ES modules (layouts flagged by filename; scoped styles bundled into the entry stylesheet).
421
- - `@abide/abide/tsconfig` — the base tsconfig apps extend (`bundler` resolution, strict, `types: ["bun"]`, erasable syntax only).
422
-
423
- ## Desktop bundle — `@documentation bundle`
424
-
425
- - `@abide/abide/bundle/BundleWindow` — the type of `src/bundle/window.ts`'s default export: window title/size plus custom `menu` entries inserted between the standard Edit and Window menus.
426
- - `@abide/abide/bundle/BundleMenu` — one top-level custom menu (`label` + `items`).
427
- - `@abide/abide/bundle/BundleMenuItem` — one menu entry: a divider, an `emit` item dispatching an `abide:menu` CustomEvent into the page (optional Cmd `shortcut`), or a `navigate` item repointing the window itself.
428
- - `@abide/abide/bundle/onMenu` — `onMenu(handler)` / `onMenu(name, handler)`: subscribes to bundle menu clicks; returns an unsubscribe; inert during SSR and in plain browser tabs.
429
- - `@abide/abide/bundle/bundled` — `bundled()`: true inside the desktop bundle (client: webview init flag; server: launcher-spawned process), false in a plain browser tab or on a remote server.
430
- - `@abide/abide/server/appDataDir` — `appDataDir()`: the running bundle's per-user data dir, keyed by the bundler-injected program name; pure path computation, cwd-independent (`ABIDE_DATA_DIR` overrides).
551
+ ## Testing
431
552
 
432
- ## MCP`@documentation mcp`
553
+ ### Testing@documentation testing
433
554
 
434
- - `@abide/abide/mcp/createMcpServer` — `createMcpServer(opts?)`: the MCP server behind `/__abide/mcp` tools derived from every `clients.mcp` rpc and socket (a `<name>-tail` read tool, plus `<name>-publish` under `clientPublish`), prompts from `src/mcp/prompts/`, auth inherited from the inbound request, optional `authorize` hook. Framework-constructed; there is no user-authored server module.
555
+ - `@abide/abide/test/createTestApp` — `createTestApp()` boots the real app on an
556
+ ephemeral port; returns `{ origin, fetch, rpc, sockets, health, stop,
557
+ [Symbol.asyncDispose] }` (`rpc`/`sockets` typed by generated d.ts). Use
558
+ `await using`.
435
559
 
436
- ## Testing
560
+ ### Testing plumbing — @documentation plumbing
437
561
 
438
- - `@abide/abide/test/createTestApp` — `@documentation testing` — boots the app in-process for `bun test`: typed `app.rpc.<name>` / `app.sockets.<name>` clients (typed via the generated `testRpc.d.ts` / `testSockets.d.ts`), request scope included, no network.
439
- - `@abide/abide/test/createScriptedSurface` — `@documentation plumbing` — a scripted `AgentSurface` for engine tests: declarative tool stubs in, an MCP surface out, every dispatched call recorded for assertions.
440
- - `@abide/abide/test/assertAgentFrameConformance` — `@documentation plumbing` — collects an engine's frame stream and asserts the neutral `AgentFrame` contract (exactly one terminal `done`, paired `tool_use`/`tool_result`, string deltas); returns the frames for provider-specific assertions.
562
+ - `@abide/abide/test/createScriptedSurface` — `createScriptedSurface(tools?)` a
563
+ scripted `AgentSurface` for engine tests; records every `call`.
564
+ - `@abide/abide/test/assertAgentFrameConformance` — collects an engine frame
565
+ stream and asserts the neutral `AgentFrame` contract (one terminal `done`; every
566
+ `tool_use` answered), throwing on violation.
441
567
 
442
568
  ## Generated machine surfaces
443
569
 
570
+ Runtime routes the framework serves:
571
+
444
572
  | Route | Serves |
445
- | --- | --- |
446
- | `/openapi.json` | The OpenAPI document projected from every rpc's method, URL, and schemas |
447
- | `/__abide/mcp` | The MCP endpoint (tools from rpcs/sockets, prompts, resources); auth flows from the inbound request |
448
- | `/__abide/health` | Liveness + identity JSON: framework version, app name/version, plus the app `health(request)` hook's fields; answered ahead of `app.handle` |
449
- | `/__abide/identity` | Compatibility alias for the same payload with the legacy `abide: true` marker |
450
- | `/__abide/sockets` | The single multiplexed WebSocket every client socket rides |
451
- | `/__abide/sockets/<name>` | A socket's HTTP face: `GET` = retained tail as JSON (SSE stream under `Accept: text/event-stream`; `?tail=N` caps/seeds), `POST` = publish gated by `clientPublish`; 404 unless the socket is exposed to mcp/cli |
452
- | `/__abide/cli` | `GET` = shell install script; `/__abide/cli/<platform>` streams the thin-CLI tarball (cli + server binaries, `.env` baked with `ABIDE_APP_URL`/`ABIDE_APP_TOKEN`) |
453
- | `/__abide/inspector` | The `@abide/inspector` UI, mounted only under `ABIDE_ENABLE_INSPECTOR=true` |
454
- | `/__abide/hot/<moduleId>` | Dev-only component hot-module endpoint backing `.abide` HMR |
573
+ |---|---|
574
+ | `/openapi.json` | OpenAPI spec for the public `/rpc/*` surface, built lazily from the frozen RPC registry. |
575
+ | `/__abide/mcp` | MCP endpoint (POST `mcp.handle`), through the app auth/CSRF pipeline; mounted when an MCP is configured. |
576
+ | `/__abide/health` | Health/identity probe answered ahead of app middleware (framework identity + `app.health()` fields), `no-store`. |
577
+ | `/__abide/identity` | Compatibility alias of the health payload, stamped `{ abide: true }` for legacy probers. |
578
+ | `/__abide/inspector` | Operator inspector UI + data/SSE routes, gated by `ABIDE_ENABLE_INSPECTOR` (optional `@abide/inspector`). |
579
+ | `/__abide/sockets` | WebSocket upgrade for the socket multiplex hub; `/__abide/sockets/<name>` is one socket's HTTP face (GET tail, POST publish). |
580
+ | `/__abide/cli` | GET returns the platform-detecting install script; `/__abide/cli/<platform>` streams the built CLI binary tarball. |
455
581
 
456
582
  ## Environment variables
457
583
 
458
584
  | Variable | Effect |
459
- | --- | --- |
460
- | `PORT` | Binds that exact port (a collision fails loudly); unset, the server finds an open port from the default |
461
- | `APP_URL` | The app's public origin and optional mount base path (a bare `/v2` is tolerated); drives `url()` base-prefixing |
462
- | `ABIDE_APP_URL` | The remote server a thin CLI binary talks to (baked into its downloaded `.env`) |
463
- | `ABIDE_APP_TOKEN` | Bearer token the thin CLI sends; baked into the downloaded `.env` when the download request was authenticated |
464
- | `ABIDE_CLIENT_TIMEOUT` | Default browser-side rpc timeout in ms — read at server boot, shipped to the client via the SSR payload |
465
- | `ABIDE_DATA_DIR` | Overrides the per-user data directory on every platform |
466
- | `ABIDE_ENABLE_INSPECTOR` | `true` mounts `@abide/inspector` at `/__abide/inspector` (the package must be installed) |
467
- | `ABIDE_IDLE_TIMEOUT` | Bun per-connection idle timeout in seconds (default 10) |
468
- | `ABIDE_INSPECT` | Enables right-click Inspect in the desktop bundle's webview |
469
- | `ABIDE_LOG_FORMAT` | `json` renders one JSON object per log line (default: tab-separated tsv) |
470
- | `ABIDE_MAX_REQUEST_BODY_SIZE` | Server-wide max request body bytes (a per-rpc `maxBodySize` refines it) |
471
- | `ABIDE_REACHABLE_TTL` | `reachable()` poll cadence / freshness in ms (default 30000) |
472
- | `ABIDE_REACHABLE_TIMEOUT` | `reachable()` per-probe bound in ms (default 3000) |
473
- | `DEBUG` | Channel-gated diagnostics (`DEBUG=abide:rpc`, `abide:sockets`, `abide:build`, …); `DEBUG=-abide` silences the framework's own channel |
585
+ |---|---|
586
+ | `PORT` | Exact TCP port to bind; unset/invalid scans from 3000. |
587
+ | `APP_URL` | Derives the server's mount base path from its pathname (e.g. `/v2`). |
588
+ | `ABIDE_APP_URL` | Default server URL the CLI connects to; its pathname sets the mount base. |
589
+ | `ABIDE_APP_TOKEN` | Sent as `Authorization: Bearer <value>` on CLI→server requests. |
590
+ | `ABIDE_APP_DIR` | Overrides the dir the server serves chunks/shell/assets from (set per dev build generation). |
591
+ | `ABIDE_DATA_DIR` | Overrides the app data directory on all platforms, used as-is (no program-name suffix). |
592
+ | `ABIDE_CLIENT_TIMEOUT` | RPC client timeout in ms (1–600000), shipped to the browser transport. |
593
+ | `ABIDE_MAX_REQUEST_BODY_SIZE` | Server-wide max request body size. |
594
+ | `ABIDE_IDLE_TIMEOUT` | Bun per-connection idle timeout in seconds (default 10). |
595
+ | `ABIDE_LOG_FORMAT` | `json` renders log records as JSON instead of TSV. |
596
+ | `ABIDE_ENABLE_INSPECTOR` | `true` mounts the opt-in operator inspector UI/routes. |
597
+ | `ABIDE_INSPECT` | Enables webview devtools/inspect for desktop bundles. |
598
+ | `ABIDE_DEV_SURFACE` | `1` forces the worker to print its surface map (set by the dev orchestrator). |
599
+ | `DEBUG` | npm-debug-style channel gate for diagnostic log channels (e.g. `abide:cache`, `-abide`). |
474
600
 
475
601
  ---
476
602
 
477
- This file mirrors `package.json`'s `exports`; after adding or renaming an
478
- export, run `bun run packages/abide/scripts/readmeSurfaces.ts` and regenerate.
603
+ Mirrors `package.json`'s `exports`; run
604
+ `bun run packages/abide/scripts/readmeSurfaces.ts` after adding or renaming an
605
+ export to keep this map honest.