@abide/abide 0.48.0 → 0.50.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (360) hide show
  1. package/AGENTS.md +85 -59
  2. package/CHANGELOG.md +216 -0
  3. package/README.md +21 -10
  4. package/package.json +15 -4
  5. package/src/abideLsp.ts +5 -6
  6. package/src/abideResolverPlugin.ts +197 -169
  7. package/src/build.ts +79 -56
  8. package/src/buildCli.ts +3 -5
  9. package/src/buildDisconnected.ts +2 -5
  10. package/src/bundleApp.ts +2 -3
  11. package/src/checkAbide.ts +12 -2
  12. package/src/compile.ts +2 -4
  13. package/src/devEntry.ts +113 -28
  14. package/src/discoveryEntry.ts +3 -2
  15. package/src/lib/bundle/disconnected.abide +69 -73
  16. package/src/lib/bundle/infoPlist.ts +13 -7
  17. package/src/lib/bundle/installDownloads.ts +13 -3
  18. package/src/lib/bundle/installMacMenu.ts +13 -3
  19. package/src/lib/bundle/spawnEmbeddedServer.ts +13 -4
  20. package/src/lib/bundle/waitForServer.ts +6 -1
  21. package/src/lib/cli/parseArgvForRpc.ts +36 -9
  22. package/src/lib/cli/tokenizeArgvFlags.ts +4 -2
  23. package/src/lib/mcp/createMcpResourceServer.ts +13 -2
  24. package/src/lib/mcp/mcpTools.ts +15 -6
  25. package/src/lib/mcp/toolResultFromResponse.ts +40 -12
  26. package/src/lib/server/error.ts +6 -6
  27. package/src/lib/server/json.ts +17 -3
  28. package/src/lib/server/rpc/defineRpc.ts +58 -24
  29. package/src/lib/server/rpc/parseArgs.ts +113 -14
  30. package/src/lib/server/rpc/resolveRpcJsonSchema.ts +28 -0
  31. package/src/lib/server/rpc/runWithRpcTimeout.ts +12 -1
  32. package/src/lib/server/rpc/types/RpcHelper.ts +75 -105
  33. package/src/lib/server/rpc/types/RpcRegistryEntry.ts +24 -9
  34. package/src/lib/server/rpc/validationError.ts +1 -1
  35. package/src/lib/server/runtime/DEV_RELOAD_CLIENT_SCRIPT.ts +0 -15
  36. package/src/lib/server/runtime/buildCacheSnapshot.ts +10 -9
  37. package/src/lib/server/runtime/buildInspectorSurface.ts +6 -4
  38. package/src/lib/server/runtime/buildOpenApiSpec.ts +55 -11
  39. package/src/lib/server/runtime/buildPreloadManifest.ts +12 -10
  40. package/src/lib/server/runtime/createAppAssetServer.ts +18 -15
  41. package/src/lib/server/runtime/createAppRouteResolver.ts +145 -0
  42. package/src/lib/server/runtime/createPlumbingRouter.ts +212 -0
  43. package/src/lib/server/runtime/createRouteDispatcher.ts +4 -10
  44. package/src/lib/server/runtime/createServer.ts +120 -324
  45. package/src/lib/server/runtime/createUiPageRenderer.ts +137 -26
  46. package/src/lib/server/runtime/devClientFingerprint.ts +19 -34
  47. package/src/lib/server/runtime/finalizeResponse.ts +11 -1
  48. package/src/lib/server/runtime/installAmbientScopeStore.ts +30 -9
  49. package/src/lib/server/runtime/logExposedSurfaces.ts +8 -7
  50. package/src/lib/server/runtime/pathStore.ts +15 -0
  51. package/src/lib/server/runtime/registryManifests.ts +1 -1
  52. package/src/lib/server/runtime/renderCellBarrierStore.ts +15 -0
  53. package/src/lib/server/runtime/runWithRequestScope.ts +3 -0
  54. package/src/lib/server/runtime/serializeCacheSnapshot.ts +6 -4
  55. package/src/lib/server/runtime/snapshotEntryFromCache.ts +24 -16
  56. package/src/lib/server/runtime/streamCacheResolutions.ts +14 -7
  57. package/src/lib/server/runtime/streamFromIterator.ts +30 -2
  58. package/src/lib/server/runtime/textResponse.ts +23 -0
  59. package/src/lib/server/runtime/types/DevReloadStamp.ts +6 -10
  60. package/src/lib/server/runtime/types/InspectorCacheEntry.ts +1 -1
  61. package/src/lib/server/runtime/types/RequestStore.ts +27 -0
  62. package/src/lib/server/runtime/warnUnguardedMcp.ts +15 -5
  63. package/src/lib/server/sockets/createSocketDispatcher.ts +9 -11
  64. package/src/lib/server/sockets/defineSocket.ts +5 -4
  65. package/src/lib/server/sse.ts +5 -1
  66. package/src/lib/shared/ASYNC_CELL.ts +5 -0
  67. package/src/lib/shared/DEV_REBUILD_PATH.ts +6 -0
  68. package/src/lib/shared/HTTP_METHODS.ts +6 -0
  69. package/src/lib/shared/HttpError.ts +1 -5
  70. package/src/lib/shared/MCP_PATH.ts +6 -0
  71. package/src/lib/shared/PROXIED_SERVER_SUBDIRS.ts +15 -0
  72. package/src/lib/shared/REF_JSON_TAGS.ts +13 -1
  73. package/src/lib/shared/RPC_SHIM_GLOBALS.ts +9 -0
  74. package/src/lib/shared/activeCacheStore.ts +1 -0
  75. package/src/lib/shared/activePage.ts +1 -0
  76. package/src/lib/shared/buildArtifact.ts +20 -0
  77. package/src/lib/shared/buildRpcRequest.ts +6 -2
  78. package/src/lib/shared/buildSocketOverChannel.ts +4 -3
  79. package/src/lib/shared/bundleGraphFromMetafile.ts +68 -0
  80. package/src/lib/shared/cache.ts +156 -131
  81. package/src/lib/shared/canonicalJson.ts +24 -1
  82. package/src/lib/shared/changeAffectsClient.ts +12 -7
  83. package/src/lib/shared/createCacheStore.ts +31 -8
  84. package/src/lib/shared/createChannelLog.ts +24 -0
  85. package/src/lib/shared/createLifecycleChannel.ts +7 -1
  86. package/src/lib/shared/createReachable.ts +47 -78
  87. package/src/lib/shared/createRemoteFunction.ts +52 -30
  88. package/src/lib/shared/createRpcServerProgram.ts +820 -0
  89. package/src/lib/shared/decodeRefJson.ts +4 -4
  90. package/src/lib/shared/decodeResponse.ts +2 -2
  91. package/src/lib/shared/decodeWireBody.ts +35 -0
  92. package/src/lib/shared/detectRpcMethod.ts +8 -1
  93. package/src/lib/shared/done.ts +8 -2
  94. package/src/lib/shared/encodeRefJson.ts +4 -4
  95. package/src/lib/shared/encodeWireBody.ts +18 -0
  96. package/src/lib/shared/exitOnBuildFailure.ts +4 -3
  97. package/src/lib/{server/rpc → shared}/fieldErrorsFromIssues.ts +5 -1
  98. package/src/lib/shared/generateDeclarations.ts +72 -0
  99. package/src/lib/shared/hasSeedableRequest.ts +25 -0
  100. package/src/lib/shared/hydrationWindow.ts +53 -0
  101. package/src/lib/shared/isAsyncCell.ts +11 -0
  102. package/src/lib/shared/isAsyncIterable.ts +8 -0
  103. package/src/lib/shared/isSubscribable.ts +3 -3
  104. package/src/lib/shared/isThenable.ts +9 -0
  105. package/src/lib/shared/jsonSchemaForType.ts +258 -0
  106. package/src/lib/shared/lenientDecode.ts +15 -0
  107. package/src/lib/shared/loadProjectTsConfig.ts +28 -0
  108. package/src/lib/shared/markFrameworkSourcesIgnored.ts +1 -1
  109. package/src/lib/shared/matchRoute.ts +4 -14
  110. package/src/lib/shared/parseEnv.ts +17 -6
  111. package/src/lib/shared/peek.ts +14 -0
  112. package/src/lib/shared/pending.ts +9 -6
  113. package/src/lib/shared/pendingAsyncCellsSlot.ts +13 -0
  114. package/src/lib/shared/prepareRpcModule.ts +91 -93
  115. package/src/lib/shared/prepareSocketModule.ts +3 -2
  116. package/src/lib/shared/probeRegistries.ts +5 -18
  117. package/src/lib/shared/reachable.ts +7 -10
  118. package/src/lib/shared/refresh.ts +12 -2
  119. package/src/lib/shared/refreshing.ts +8 -2
  120. package/src/lib/shared/resolvedCellsSlot.ts +13 -0
  121. package/src/lib/shared/reviveWireField.ts +49 -0
  122. package/src/lib/shared/reviveWireOutput.ts +36 -0
  123. package/src/lib/shared/rpcServerForRoot.ts +25 -0
  124. package/src/lib/shared/scanPages.ts +25 -0
  125. package/src/lib/shared/serializeEnv.ts +18 -6
  126. package/src/lib/shared/snapshotShippable.ts +8 -7
  127. package/src/lib/shared/snippet.ts +11 -6
  128. package/src/lib/shared/streamedCellsSlot.ts +14 -0
  129. package/src/lib/shared/subscribableFromResponse.ts +4 -4
  130. package/src/lib/shared/subscribableProbes.ts +1 -1
  131. package/src/lib/shared/tailProbeSlot.ts +1 -1
  132. package/src/lib/shared/types/AsyncComputed.ts +20 -0
  133. package/src/lib/shared/types/AsyncState.ts +13 -0
  134. package/src/lib/shared/types/CacheEntry.ts +7 -7
  135. package/src/lib/shared/types/CacheOptions.ts +23 -37
  136. package/src/lib/shared/types/CachePolicy.ts +25 -0
  137. package/src/lib/shared/types/CacheSnapshotEntry.ts +6 -5
  138. package/src/lib/shared/types/ErrorJsonSchemas.ts +8 -0
  139. package/src/lib/shared/types/HttpMethod.ts +3 -1
  140. package/src/lib/shared/types/InputCoercion.ts +17 -0
  141. package/src/lib/shared/types/{Subscribable.ts → NamedAsyncIterable.ts} +1 -1
  142. package/src/lib/shared/types/OutputWirePlan.ts +17 -0
  143. package/src/lib/shared/types/PagesScan.ts +7 -0
  144. package/src/lib/shared/types/PendingAsyncCells.ts +10 -0
  145. package/src/lib/shared/types/RawRemoteFunction.ts +6 -0
  146. package/src/lib/shared/types/RemoteCallable.ts +8 -7
  147. package/src/lib/shared/types/RemoteFunction.ts +16 -15
  148. package/src/lib/shared/types/ResolvedCells.ts +11 -0
  149. package/src/lib/shared/types/ReturnBody.ts +15 -0
  150. package/src/lib/shared/types/RpcBuildStamps.ts +23 -0
  151. package/src/lib/shared/types/RpcErrorGuard.ts +3 -8
  152. package/src/lib/shared/types/Socket.ts +4 -4
  153. package/src/lib/shared/types/SsrPayload.ts +5 -1
  154. package/src/lib/shared/types/StreamPolicy.ts +11 -0
  155. package/src/lib/shared/types/StreamedCells.ts +19 -0
  156. package/src/lib/shared/types/StreamedResolution.ts +24 -6
  157. package/src/lib/shared/types/TailHooks.ts +1 -1
  158. package/src/lib/shared/types/WireKind.ts +11 -0
  159. package/src/lib/shared/url.ts +5 -0
  160. package/src/lib/shared/validationHttpError.ts +33 -0
  161. package/src/lib/shared/warmSeedKey.ts +16 -0
  162. package/src/lib/shared/wireJsonReplacer.ts +30 -0
  163. package/src/lib/shared/writeRpcDts.ts +11 -1
  164. package/src/lib/shared/writeTestSocketsDts.ts +1 -1
  165. package/src/lib/test/createTestApp.ts +19 -1
  166. package/src/lib/ui/README.md +1 -1
  167. package/src/lib/ui/activePendingCells.ts +14 -0
  168. package/src/lib/ui/compile/BLOCK_KEYWORDS.ts +21 -0
  169. package/src/lib/ui/compile/SSR_ESCAPE.ts +4 -1
  170. package/src/lib/ui/compile/UI_RUNTIME_IMPORTS.ts +35 -7
  171. package/src/lib/ui/compile/abideUiPlugin.ts +19 -2
  172. package/src/lib/ui/compile/analyzeComponent.ts +89 -6
  173. package/src/lib/ui/compile/assignmentTargetNames.ts +54 -0
  174. package/src/lib/ui/compile/asyncInterpolationFields.ts +152 -0
  175. package/src/lib/ui/compile/asyncValuePositionError.ts +21 -0
  176. package/src/lib/ui/compile/asyncValuePositionInterpolations.ts +64 -0
  177. package/src/lib/ui/compile/attrLiftPosition.ts +18 -0
  178. package/src/lib/ui/compile/bindListenEvent.ts +5 -6
  179. package/src/lib/ui/compile/cachedSourceFile.ts +40 -0
  180. package/src/lib/ui/compile/catchBinding.ts +7 -5
  181. package/src/lib/ui/compile/classifyInterpolationType.ts +46 -0
  182. package/src/lib/ui/compile/collectAbideDiagnostics.ts +96 -0
  183. package/src/lib/ui/compile/compileComponent.ts +19 -3
  184. package/src/lib/ui/compile/compileModule.ts +27 -58
  185. package/src/lib/ui/compile/compileSSR.ts +52 -13
  186. package/src/lib/ui/compile/compileShadow.ts +265 -42
  187. package/src/lib/ui/compile/composeProps.ts +15 -4
  188. package/src/lib/ui/compile/createShadowLanguageService.ts +116 -68
  189. package/src/lib/ui/compile/createShadowProgram.ts +35 -6
  190. package/src/lib/ui/compile/declaredNames.ts +36 -0
  191. package/src/lib/ui/compile/desugarSignals.ts +419 -37
  192. package/src/lib/ui/compile/expressionIsPrefixEvaluable.ts +19 -0
  193. package/src/lib/ui/compile/generateBuild.ts +74 -17
  194. package/src/lib/ui/compile/generateSSR.ts +286 -75
  195. package/src/lib/ui/compile/hoistableAwaits.ts +193 -0
  196. package/src/lib/ui/compile/hoistableChildRenders.ts +112 -0
  197. package/src/lib/ui/compile/interpolatedTemplateLiteral.ts +3 -1
  198. package/src/lib/ui/compile/interpolationClassifierForRoot.ts +37 -0
  199. package/src/lib/ui/compile/isSpuriousAsyncReadDiagnostic.ts +174 -0
  200. package/src/lib/ui/compile/isWhitespaceText.ts +10 -2
  201. package/src/lib/ui/compile/liftAsyncSubExpressions.ts +166 -0
  202. package/src/lib/ui/compile/lowerAsyncInterpolations.ts +92 -0
  203. package/src/lib/ui/compile/lowerContext.ts +10 -0
  204. package/src/lib/ui/compile/lowerDocAccess.ts +51 -10
  205. package/src/lib/ui/compile/lowerScript.ts +40 -4
  206. package/src/lib/ui/compile/nodeAtShadowOffset.ts +28 -0
  207. package/src/lib/ui/compile/parseTemplate.ts +18 -1090
  208. package/src/lib/ui/compile/parseTemplateRecovering.ts +1385 -0
  209. package/src/lib/ui/compile/referencedIdentifiers.ts +35 -0
  210. package/src/lib/ui/compile/renameSignalRefs.ts +26 -40
  211. package/src/lib/ui/compile/resolveReactiveExport.ts +4 -7
  212. package/src/lib/ui/compile/scopeCss.ts +27 -4
  213. package/src/lib/ui/compile/seedTypeClassifierForRoot.ts +65 -0
  214. package/src/lib/ui/compile/shadowInterpolationClassifier.ts +47 -0
  215. package/src/lib/ui/compile/sourceFileOptionsSignature.ts +20 -0
  216. package/src/lib/ui/compile/structuralHeadTokens.ts +107 -0
  217. package/src/lib/ui/compile/templateSemanticTokens.ts +21 -0
  218. package/src/lib/ui/compile/templateStartOffset.ts +15 -0
  219. package/src/lib/ui/compile/tryPlan.ts +4 -3
  220. package/src/lib/ui/compile/types/AnalyzedComponent.ts +4 -0
  221. package/src/lib/ui/compile/types/AsyncInterpolationField.ts +19 -0
  222. package/src/lib/ui/compile/types/InterpolationClassifier.ts +12 -0
  223. package/src/lib/ui/compile/types/InterpolationKind.ts +7 -0
  224. package/src/lib/ui/compile/types/ParseDiagnostic.ts +8 -0
  225. package/src/lib/ui/compile/types/SeedTypeClassifier.ts +15 -0
  226. package/src/lib/ui/compile/types/TemplateNode.ts +36 -4
  227. package/src/lib/ui/compile/types/ValuePositionInterpolation.ts +13 -0
  228. package/src/lib/ui/compile/writtenTemplateNames.ts +84 -0
  229. package/src/lib/ui/computed.ts +28 -1
  230. package/src/lib/ui/createScope.ts +35 -68
  231. package/src/lib/ui/dom/anchoredBranch.ts +142 -0
  232. package/src/lib/ui/dom/appendText.ts +8 -3
  233. package/src/lib/ui/dom/awaitBlock.ts +62 -75
  234. package/src/lib/ui/dom/bindProp.ts +22 -0
  235. package/src/lib/ui/dom/bindableProp.ts +47 -0
  236. package/src/lib/ui/dom/cellPending.ts +24 -0
  237. package/src/lib/ui/dom/discardBoundary.ts +24 -6
  238. package/src/lib/ui/dom/disposeRange.ts +2 -1
  239. package/src/lib/ui/dom/each.ts +41 -12
  240. package/src/lib/ui/dom/eachAsync.ts +39 -3
  241. package/src/lib/ui/dom/fillBefore.ts +6 -7
  242. package/src/lib/ui/dom/fillBoundary.ts +2 -3
  243. package/src/lib/ui/dom/fillRange.ts +4 -4
  244. package/src/lib/ui/dom/hydrate.ts +6 -12
  245. package/src/lib/ui/dom/isComment.ts +1 -1
  246. package/src/lib/ui/dom/matchingRangeClose.ts +29 -0
  247. package/src/lib/ui/dom/mergeProps.ts +1 -1
  248. package/src/lib/ui/dom/mount.ts +1 -2
  249. package/src/lib/ui/dom/mountChild.ts +9 -40
  250. package/src/lib/ui/dom/mountRange.ts +2 -3
  251. package/src/lib/ui/dom/mountSlot.ts +1 -1
  252. package/src/lib/ui/dom/mountStreamedChild.ts +84 -0
  253. package/src/lib/ui/dom/mountSwappableRange.ts +46 -4
  254. package/src/lib/ui/dom/mutateDocContainer.ts +65 -0
  255. package/src/lib/ui/dom/on.ts +2 -2
  256. package/src/lib/ui/dom/readCell.ts +40 -0
  257. package/src/lib/ui/dom/restProps.ts +2 -2
  258. package/src/lib/ui/dom/spreadProps.ts +3 -4
  259. package/src/lib/ui/dom/switchBlock.ts +30 -7
  260. package/src/lib/ui/dom/tryBlock.ts +233 -70
  261. package/src/lib/ui/dom/types/SwitchCase.ts +5 -1
  262. package/src/lib/ui/dom/when.ts +12 -2
  263. package/src/lib/ui/dom/withScope.ts +9 -2
  264. package/src/lib/ui/finalizeStreamedChildren.ts +89 -0
  265. package/src/lib/ui/flight.ts +56 -0
  266. package/src/lib/ui/html.ts +12 -1
  267. package/src/lib/ui/isolateCellBarrier.ts +17 -0
  268. package/src/lib/ui/linked.ts +48 -2
  269. package/src/lib/ui/props.ts +17 -0
  270. package/src/lib/ui/remoteProxy.ts +84 -159
  271. package/src/lib/ui/renderChain.ts +53 -13
  272. package/src/lib/ui/renderToStream.ts +22 -18
  273. package/src/lib/ui/resumeSeedScript.ts +1 -1
  274. package/src/lib/ui/router.ts +86 -53
  275. package/src/lib/ui/runtime/AsyncCellError.ts +20 -0
  276. package/src/lib/ui/runtime/CELL_SEED.ts +14 -0
  277. package/src/lib/ui/runtime/CHILD_PRESENT.ts +2 -2
  278. package/src/lib/ui/runtime/CURRENT_BOUNDARY.ts +11 -0
  279. package/src/lib/ui/runtime/CURRENT_PATH.ts +29 -0
  280. package/src/lib/ui/runtime/RENDER.ts +10 -7
  281. package/src/lib/ui/runtime/RESUME.ts +4 -4
  282. package/src/lib/ui/runtime/STREAMED_CELLS.ts +57 -0
  283. package/src/lib/ui/runtime/ambientPathBacking.ts +32 -0
  284. package/src/lib/ui/runtime/applyPatchToTree.ts +17 -4
  285. package/src/lib/ui/runtime/blockId.ts +31 -0
  286. package/src/lib/ui/runtime/boundaryFor.ts +11 -0
  287. package/src/lib/ui/runtime/cellBarrierBacking.ts +30 -0
  288. package/src/lib/ui/runtime/createAsyncCell.ts +300 -0
  289. package/src/lib/ui/runtime/createDoc.ts +31 -54
  290. package/src/lib/ui/runtime/createEffectNode.ts +9 -0
  291. package/src/lib/ui/runtime/enterRenderPass.ts +5 -4
  292. package/src/lib/ui/runtime/flushEffects.ts +33 -1
  293. package/src/lib/ui/runtime/isAsyncFunction.ts +14 -0
  294. package/src/lib/ui/runtime/nextBlockId.ts +8 -7
  295. package/src/lib/ui/runtime/renderPath.ts +16 -0
  296. package/src/lib/ui/runtime/scope.ts +11 -0
  297. package/src/lib/ui/runtime/toTeardown.ts +10 -3
  298. package/src/lib/ui/runtime/types/Boundary.ts +9 -0
  299. package/src/lib/ui/runtime/types/RenderContext.ts +9 -7
  300. package/src/lib/ui/runtime/types/SsrRender.ts +9 -2
  301. package/src/lib/ui/runtime/types/UiComponent.ts +1 -5
  302. package/src/lib/ui/runtime/types/UiProps.ts +6 -5
  303. package/src/lib/ui/runtime/withOptionalPath.ts +8 -0
  304. package/src/lib/ui/runtime/withPath.ts +16 -0
  305. package/src/lib/ui/runtime/withPathFrom.ts +20 -0
  306. package/src/lib/ui/runtime/withoutHydration.ts +22 -0
  307. package/src/lib/ui/seedStreamedResolution.ts +31 -6
  308. package/src/lib/ui/settleAsyncCells.ts +24 -0
  309. package/src/lib/ui/socketProxy.ts +1 -1
  310. package/src/lib/ui/startClient.ts +16 -31
  311. package/src/lib/ui/state.ts +9 -3
  312. package/src/lib/ui/trackedComputed.ts +68 -0
  313. package/src/lib/ui/types/Scope.ts +8 -24
  314. package/src/lib/ui/watch.ts +3 -3
  315. package/src/serverEntry.ts +14 -0
  316. package/template/src/server/rpc/getHello.ts +15 -13
  317. package/template/test/app.test.ts +1 -1
  318. package/src/lib/server/runtime/devHotModuleResponse.ts +0 -41
  319. package/src/lib/shared/DEV_HOT_PREFIX.ts +0 -7
  320. package/src/lib/shared/UNREACHABLE_STATUSES.ts +0 -13
  321. package/src/lib/shared/hasReplayableRequest.ts +0 -17
  322. package/src/lib/shared/hydratingSlot.ts +0 -12
  323. package/src/lib/shared/outboxProbeSlot.ts +0 -20
  324. package/src/lib/shared/types/Outbox.ts +0 -9
  325. package/src/lib/shared/types/OutboxEntry.ts +0 -27
  326. package/src/lib/shared/types/SmartReadOptions.ts +0 -36
  327. package/src/lib/shared/wakeHydrationPeeks.ts +0 -20
  328. package/src/lib/ui/COMPONENT_WRAPPER_PREFIX.ts +0 -5
  329. package/src/lib/ui/compile/REACTIVE_CALLEES.ts +0 -11
  330. package/src/lib/ui/compile/assertRuntimeHelpersBound.ts +0 -80
  331. package/src/lib/ui/compile/markupTokens.ts +0 -313
  332. package/src/lib/ui/compile/structuralBlockTokens.ts +0 -100
  333. package/src/lib/ui/dom/applyResolved.ts +0 -87
  334. package/src/lib/ui/dom/scopeLabel.ts +0 -21
  335. package/src/lib/ui/dom/text.ts +0 -20
  336. package/src/lib/ui/history.ts +0 -101
  337. package/src/lib/ui/installHotBridge.ts +0 -93
  338. package/src/lib/ui/installInspectorBridge.ts +0 -140
  339. package/src/lib/ui/outbox.ts +0 -35
  340. package/src/lib/ui/persist.ts +0 -115
  341. package/src/lib/ui/rpcOutbox/createOutboxQueue.ts +0 -281
  342. package/src/lib/ui/rpcOutbox/outboxRegistry.ts +0 -69
  343. package/src/lib/ui/runtime/PATCH_BUS.ts +0 -28
  344. package/src/lib/ui/runtime/captureModelDoc.ts +0 -28
  345. package/src/lib/ui/runtime/hotInstances.ts +0 -10
  346. package/src/lib/ui/runtime/hotReloadEnabled.ts +0 -8
  347. package/src/lib/ui/runtime/hotReplace.ts +0 -38
  348. package/src/lib/ui/runtime/liveScopes.ts +0 -15
  349. package/src/lib/ui/runtime/localStoragePersistence.ts +0 -47
  350. package/src/lib/ui/runtime/registerHotInstance.ts +0 -23
  351. package/src/lib/ui/runtime/seedModelDoc.ts +0 -26
  352. package/src/lib/ui/runtime/types/HotInstance.ts +0 -22
  353. package/src/lib/ui/runtime/types/PatchEvent.ts +0 -16
  354. package/src/lib/ui/seedResolved.ts +0 -28
  355. package/src/lib/ui/sync.ts +0 -48
  356. package/src/lib/ui/types/History.ts +0 -14
  357. package/src/lib/ui/types/PersistHandle.ts +0 -11
  358. package/src/lib/ui/types/PersistenceStore.ts +0 -12
  359. package/src/lib/ui/types/ResolvedFrame.ts +0 -15
  360. package/src/lib/ui/types/SyncTransport.ts +0 -13
@@ -1,8 +1,10 @@
1
1
  /* The per-request SSR render context, threaded through a layout chain and every
2
- child component a page inlines. `next` is the block-id counter: each `await`/`try`
3
- block draws from it in depth-first document order the SAME order the client
4
- allocates ids during its synchronous hydration walk, so the streamed fragments and
5
- the `RESUME` manifest line up. Request-local (not a module global) because SSR
6
- render is async a blocking `await` yields, and a shared global counter would
7
- interleave across concurrent requests. */
8
- export type RenderContext = { next: number }
2
+ child component a page inlines. It is the block-id counter MAP (ADR-0037): each
3
+ `await`/`try` block draws a path-namespaced id via `blockId(ctx)`keyed by the
4
+ ambient render-path, counting 0,1,2… in document order WITHIN each path. Both sides
5
+ compose the same ids (the client mounts each child under the same path), so the
6
+ streamed fragments and the `RESUME` manifest line up. Request-local (not a module
7
+ global) because SSR render is async and concurrent but sibling child renders write
8
+ different path keys, so the shared map needs no locking and survives parallel renders
9
+ (which a single flat counter could not). */
10
+ export type RenderContext = Map<string, number>
@@ -9,10 +9,17 @@ import type { ResumeEntry } from '../RESUME.ts'
9
9
  on the tag — never land here: they render inline during the async render pass and
10
10
  seed `SsrRender.resume`.) */
11
11
  export type SsrAwait = {
12
- id: number
12
+ id: string
13
13
  promise: () => unknown
14
14
  then: (value: unknown) => Promise<string>
15
15
  catch?: (error: unknown) => Promise<string>
16
+ /* ADR-0039 (spike): a STANDALONE-UNIT boundary — a streamed child COMPONENT — carries no
17
+ resume value of its own (its `then` returns the child's rendered html and merges the child's
18
+ own awaits/resume for nested composition; the child re-mounts client-side rather than
19
+ adopting a RESUME[id] value). Marks `settle` to emit an html-only fragment with no resume seed
20
+ script, so the client never tries to decode a whole SsrRender as an await value. Absent for a
21
+ normal await block (which seeds its resolved value). */
22
+ htmlOnly?: boolean
16
23
  }
17
24
 
18
25
  /* The result of a component's server `render()`: the pending-shell HTML, the
@@ -23,5 +30,5 @@ export type SsrRender = {
23
30
  html: string
24
31
  state: unknown
25
32
  awaits: SsrAwait[]
26
- resume: Record<number, ResumeEntry>
33
+ resume: Record<string, ResumeEntry>
27
34
  }
@@ -18,11 +18,7 @@ export type UiComponent = ((host: Element, props?: UiProps) => () => void) & {
18
18
  hydrate?: (host: Element, props?: UiProps) => () => void
19
19
  /* The bare client build (`(host, props) => void`) — appends the component's nodes
20
20
  to `host`. A nested child mounts it into a marker range (`mountRange`/`mountChild`)
21
- instead of the wrapped `mount`, and `hotReplace` re-fills a range with the new
22
- module's `build` on edit. */
21
+ instead of the wrapped `mount`. */
23
22
  build: (host: Node, props?: UiProps) => void
24
23
  hydratable?: boolean
25
- /* Stable module id (project-relative source path) stamped by `compileModule`,
26
- keying the component in the hot-reload registry — see `mountChild`. */
27
- __abideId?: string
28
24
  }
@@ -2,10 +2,11 @@
2
2
  What a component is invoked with. A top-level page/layout is called by the router
3
3
  (client) and `renderChain` (SSR) with its route params as reactive thunks, shaped
4
4
  identically to the thunk map `mountChild` passes a nested child — so `props()` reads
5
- (`$props[name]?.()`) work uniformly on both. `$children` is the slot builder a parent
6
- component passes (carrying the component's `{children()}` content), or `CHILD_PRESENT`
7
- the router/SSR set on a layout that has a child layer below it.
5
+ (`$props[name]?.()`) work uniformly on both. `children` is an ordinary prop thunk like
6
+ any other: on a component it returns the parent's `Snippet`; on a layout with a child
7
+ layer below it, the router/SSR set it to `() => CHILD_PRESENT` (a presence sentinel, not
8
+ a slot builder) so `{#if children}` reads truthy.
8
9
  */
9
- export type UiProps = Record<string, (() => unknown) | ((host: Element) => void)> & {
10
- $children?: (host: Element) => void
10
+ export type UiProps = Record<string, () => unknown> & {
11
+ children?: () => unknown
11
12
  }
@@ -0,0 +1,8 @@
1
+ import { withPath } from './withPath.ts'
2
+
3
+ /* Runs `build` under the render-path segment for a compiler `ordinal`, or plainly when `ordinal`
4
+ is undefined (a non-compiled caller that supplied no site id). Shared by the child-mount paths so
5
+ the "push a path segment iff an ordinal is present" convention lives in one place. */
6
+ export function withOptionalPath<T>(ordinal: number | undefined, build: () => T): T {
7
+ return ordinal === undefined ? build() : withPath(ordinal, build)
8
+ }
@@ -0,0 +1,16 @@
1
+ import { CURRENT_PATH } from './CURRENT_PATH.ts'
2
+ import { withPathFrom } from './withPathFrom.ts'
3
+
4
+ /*
5
+ Pushes one render-path segment for the duration of `build`, RELATIVE to the live ambient path,
6
+ restoring after (synchronous build, so the save/restore is exact and nesting is a strict stack —
7
+ the same shape `withScope`/`enterRenderPass` use). The segment is `escapeKey`-escaped and joined
8
+ with `/`, so a segment carrying a `/` (a URL-shaped `{#each}` key) survives as one element. Used
9
+ at sites that run DURING the initial render walk (a layout layer, a `<Child/>` mount) where the
10
+ ambient path is the correct base. A control-flow block that rebuilds reactively after mount uses
11
+ `withPathFrom` with a captured base instead.
12
+ */
13
+ // @documentation plumbing
14
+ export function withPath<T>(segment: string | number, build: () => T): T {
15
+ return withPathFrom(CURRENT_PATH.current, segment, build)
16
+ }
@@ -0,0 +1,20 @@
1
+ import { ambientPathBacking } from './ambientPathBacking.ts'
2
+ import { escapeKey } from './escapeKey.ts'
3
+
4
+ /*
5
+ Runs `build` with the render path set to `base` + one escaped `segment` (ADR-0033 D1). Unlike
6
+ `withPath` (which pushes relative to the LIVE ambient), this composes from an EXPLICIT base — for
7
+ a control-flow block that rebuilds content reactively AFTER mount, when the ambient path is no
8
+ longer on the stack. The block captures `CURRENT_PATH.current` at construction (its render-time
9
+ ancestry) and re-establishes `base/segment` for every branch/row build — initial and every later
10
+ swap — so a component or cell built on a swap still gets its full, stable id, not a bare segment.
11
+
12
+ Pushes through the backing's `run` (not a set-then-restore slot): the server's AsyncLocalStorage
13
+ backing makes the composed path survive an `await` inside `build`, so a render body resuming after
14
+ a cell barrier or child render reads its own path; the client's synchronous module-var `run` is a
15
+ plain save/restore. `run` returns `build()`'s value (a value on the client, a promise on the server).
16
+ */
17
+ export function withPathFrom<T>(base: string, segment: string | number, build: () => T): T {
18
+ const composedPath = base === '' ? escapeKey(segment) : `${base}/${escapeKey(segment)}`
19
+ return ambientPathBacking.active.run(composedPath, build)
20
+ }
@@ -0,0 +1,22 @@
1
+ import { RENDER } from './RENDER.ts'
2
+
3
+ /*
4
+ Runs `build` with the hydration claim cursor cleared, restoring it after. The
5
+ control-flow blocks route FRESH builds through here — a pending/resolved await
6
+ branch, a try's catch branch, a fillBefore insert — so their build helpers
7
+ create nodes instead of trying to claim server DOM that isn't there. A rebuild
8
+ that fires while an outer hydrate pass is still active (e.g. a synchronous write
9
+ that flips a `when`/`switch` mid-hydrate) would otherwise make `cloneStatic` /
10
+ text claim discarded or nonexistent nodes and silently render nothing. The
11
+ `finally` restore lets a fresh build sit inside an ongoing hydrate pass without
12
+ ending it early.
13
+ */
14
+ export function withoutHydration<T>(build: () => T): T {
15
+ const previous = RENDER.hydration
16
+ RENDER.hydration = undefined
17
+ try {
18
+ return build()
19
+ } finally {
20
+ RENDER.hydration = previous
21
+ }
22
+ }
@@ -1,6 +1,9 @@
1
1
  import { activeCacheStore } from '../shared/activeCacheStore.ts'
2
2
  import { cacheEntryFromSnapshot } from '../shared/cacheEntryFromSnapshot.ts'
3
+ import { decodeRefJson } from '../shared/decodeRefJson.ts'
3
4
  import type { StreamedResolution } from '../shared/types/StreamedResolution.ts'
5
+ import { CELL_SEED } from './runtime/CELL_SEED.ts'
6
+ import { receiveStreamedCell } from './runtime/STREAMED_CELLS.ts'
4
7
 
5
8
  /*
6
9
  Seeds one streamed cache resolution into the active store — the single sink for the
@@ -9,12 +12,27 @@ so a `cache()` read resolves synchronously (no wire round-trip) and `<template a
9
12
  adopts without a refetch; a `{ key, miss }` marker — a body the server couldn't snapshot
10
13
  (binary / rejected / evicted) — is a no-op, so that read falls back to a live fetch.
11
14
 
12
- Shared by startClient's boot drain, the live `window.__abideResolve`, and applyResolved,
13
- so no resolved-frame consumer can swap DOM while silently dropping the cache channel —
14
- the asymmetry that made streamed reads cold-miss to the network.
15
+ Shared by startClient's boot drain and the live `window.__abideResolve`, so no
16
+ resolved-frame consumer can swap DOM while silently dropping the cache channel — the
17
+ asymmetry that made streamed reads cold-miss to the network.
15
18
  */
16
19
  // @documentation plumbing
17
20
  export function seedStreamedResolution(resolution: StreamedResolution): void {
21
+ /* ADR-0039: a STREAMED CHILD's blocking async-cell value that resolved after the head snapshot.
22
+ Seed it into `CELL_SEED` (the same warm partition `__SSR__.cells` fills, storing the raw
23
+ ref-json string decoded at cell read) so the child's cell constructs RESOLVED when its
24
+ deferred mount runs — no flash, no re-run. Checked before `cellKey` (a distinct arm). */
25
+ if ('cellSeed' in resolution) {
26
+ CELL_SEED[resolution.cellSeed] = resolution.value
27
+ return
28
+ }
29
+ /* ADR-0035: a streaming CELL's server-resolved value, keyed by render-path id. Route it to the
30
+ mounted cell (or buffer until it registers) — decoded through the same ref-json codec the
31
+ server encoded it with, so a Set/Map/Date/bigint/cyclic value survives. */
32
+ if ('cellKey' in resolution) {
33
+ receiveStreamedCell(resolution.cellKey, decodeRefJson(resolution.value))
34
+ return
35
+ }
18
36
  if ('miss' in resolution) {
19
37
  return
20
38
  }
@@ -22,10 +40,17 @@ export function seedStreamedResolution(resolution: StreamedResolution): void {
22
40
  an unconsumed hydrated seed (`hydrated === true`, cleared by the first cache()
23
41
  read). A live/settled non-hydrated entry is authoritative; clobbering it with a
24
42
  stale snapshot would drop a fresher value (e.g. one a live fetch already wrote). */
25
- const { entries } = activeCacheStore()
26
- const existing = entries.get(resolution.key)
43
+ const store = activeCacheStore()
44
+ const existing = store.entries.get(resolution.key)
27
45
  if (existing !== undefined && existing.hydrated !== true) {
28
46
  return
29
47
  }
30
- entries.set(resolution.key, cacheEntryFromSnapshot(resolution))
48
+ store.entries.set(resolution.key, cacheEntryFromSnapshot(resolution))
49
+ /* Wake any reader subscribed to this key's lifecycle channel. Seeding used to rely on
50
+ seed-before-mount ordering (valid for the await path, whose subscription reads the
51
+ resume manifest, not the cache); an auto-streamed BARE read (ADR-0024) has already
52
+ mounted and its throwing-peek subscribed the key, so without an explicit dispatch the
53
+ peek that read `undefined` never re-runs on the streamed value. markLifecycle re-derives
54
+ the peek scope — the same wake materializeRetained relies on for a late-landing value. */
55
+ store.markLifecycle(resolution.key)
31
56
  }
@@ -0,0 +1,24 @@
1
+ import { activePendingCells } from './activePendingCells.ts'
2
+
3
+ /*
4
+ The SSR await-barrier the compiler lowers to `$$settleAsyncCells()` between a
5
+ component's cell declarations and its template (ADR-0019 Tier-2). It DRAINS the
6
+ request-scoped pending list (`splice(0)`) so each barrier awaits only the cells
7
+ declared since the last one — `renderChain` renders layout layers sequentially,
8
+ each with its own barrier, and a drain keeps a later layer from re-awaiting an
9
+ earlier settled layer's cells. `Promise.allSettled` never rejects the render: a
10
+ rejected cell settles into its `error()` state, surfaced at the read site (via
11
+ `$$readCell`), not here. On the client the list is always empty (cells register
12
+ server-side only), so the barrier is a no-op await.
13
+ */
14
+ // @documentation plumbing
15
+ export async function settleAsyncCells(): Promise<void> {
16
+ const pending = activePendingCells()
17
+ if (pending === undefined) {
18
+ return
19
+ }
20
+ const promises = pending.promises.splice(0)
21
+ if (promises.length > 0) {
22
+ await Promise.allSettled(promises)
23
+ }
24
+ }
@@ -11,7 +11,7 @@ over the multiplexed ws channel). Both paths produce identical Socket
11
11
  shapes so user code reads the same on either side.
12
12
 
13
13
  The Socket surface — bare iteration as the live stream, `.tail(n)` seeded
14
- from the retained tail, `.broadcast` sending a server-validated `pub` frame —
14
+ from the retained tail, `.publish` sending a server-validated `pub` frame —
15
15
  is built by buildSocketOverChannel over the page's lazily-opened singleton
16
16
  channel; this module only binds that builder to the browser channel so the
17
17
  test harness can reuse the identical surface over its own channel.
@@ -5,18 +5,12 @@ import { sharedCacheStoreSlot } from '../shared/sharedCacheStoreSlot.ts'
5
5
  import type { SsrPayload } from '../shared/types/SsrPayload.ts'
6
6
  import type { StreamedResolution } from '../shared/types/StreamedResolution.ts'
7
7
  import { probeNavigation } from './probeNavigation.ts'
8
- import { seedBootState } from './seedBootState.ts'
9
-
10
- /* Build-time flag the production client defines false (see build.ts `define`) so the dev-only
11
- hot bridge — and the entire DOM runtime it statically pulls in for re-builds — is dead-code-
12
- eliminated, not even emitted as a chunk. Dev defines it true; the test preload sets it on
13
- globalThis so the bare reference resolves there. */
14
- declare const __ABIDE_DEV__: boolean
15
-
16
8
  import { router } from './router.ts'
9
+ import { CELL_SEED } from './runtime/CELL_SEED.ts'
17
10
  import { clientPage } from './runtime/clientPage.ts'
18
11
  import type { RouteLoader } from './runtime/types/RouteLoader.ts'
19
- import { seedResolved } from './seedResolved.ts'
12
+ import { seedBootState } from './seedBootState.ts'
13
+ import { seedStreamedResolution } from './seedStreamedResolution.ts'
20
14
 
21
15
  /*
22
16
  The official abide-ui client entry. Reads the server's `window.__SSR__` payload,
@@ -40,21 +34,6 @@ export function startClient(
40
34
  if (target === null) {
41
35
  throw new Error('[abide] startClient: missing #app target')
42
36
  }
43
- /* Dev only: the live-reload script sets `__abideDev` before this module runs, so the
44
- runtime bridge is in place before any component mounts and records its instances
45
- (mountChild) for hot replacement. Lazy-imported and `__ABIDE_DEV__`-gated so the
46
- bridge (and the DOM runtime it drags in) is fully dead-code-eliminated in production
47
- rather than shipped behind a runtime flag the minifier can't prove false. */
48
- if (__ABIDE_DEV__ && (globalThis as { __abideDev?: boolean }).__abideDev) {
49
- import('./installHotBridge.ts').then((module) => module.installHotBridge())
50
- }
51
- /* Inspector only: the server injects `__abideInspect` when ABIDE_ENABLE_INSPECTOR is on,
52
- so the scope/router bridge arms before the router builds any scope. Inspector can be
53
- enabled in production, so this stays a lazy chunk (not `__ABIDE_DEV__`-gated) — emitted
54
- but fetched only when the flag is set, never weighing down the default client load. */
55
- if ((globalThis as { __abideInspect?: boolean }).__abideInspect) {
56
- import('./installInspectorBridge.ts').then((module) => module.installInspectorBridge())
57
- }
58
37
  const ssr = (globalThis as { __SSR__?: Partial<SsrPayload> }).__SSR__ ?? {}
59
38
  /* Seed the per-page __SSR__ stamps into their shared slots before mount: the mount
60
39
  base, app name (default log channel), health payload (so health()'s first probe is
@@ -78,16 +57,22 @@ export function startClient(
78
57
  const streamed =
79
58
  (globalThis as { __abideResumeCache?: StreamedResolution[] }).__abideResumeCache ?? []
80
59
  for (const resolution of [...(ssr.cache ?? []), ...streamed]) {
81
- seedResolved({ kind: 'cache', resolution })
60
+ seedStreamedResolution(resolution)
61
+ }
62
+ /* Seed the async-cell warm partition into `CELL_SEED` before mount: a hydrating
63
+ `createAsyncCell` reads its render-path key here to adopt the SSR-resolved value warm
64
+ instead of re-running its seed cold (Object.assign so an inline pre-bundle script that
65
+ already populated `__abideCells` isn't clobbered). */
66
+ if (ssr.cells !== undefined) {
67
+ Object.assign(CELL_SEED, ssr.cells)
82
68
  }
83
69
  /* Keep the cache channel live past boot: replace the head's buffering collector with
84
- the store-connected sink so a post-load resolution — streaming SPA navigation or a
85
- socket-delivered SSR frame, both routed through applyResolved seeds the store
86
- directly instead of pushing to a buffer nothing drains again. The inline doc-stream
87
- script only ever hands this a cache `StreamedResolution`, so wrap it as a cache frame
88
- through the one intake seam. */
70
+ the store-connected sink so a post-boot resolution — the inline doc-stream cache
71
+ script seeds the store through `seedStreamedResolution` directly instead of pushing
72
+ to a buffer nothing drains again. The inline doc-stream script only ever hands this a
73
+ cache `StreamedResolution`. */
89
74
  ;(globalThis as { __abideResolve?: (resolution: StreamedResolution) => void }).__abideResolve =
90
- (resolution) => seedResolved({ kind: 'cache', resolution })
75
+ seedStreamedResolution
91
76
 
92
77
  return router(target, routes, layoutRoutes, probeNavigation)
93
78
  }
@@ -14,11 +14,17 @@ import { scope } from './scope.ts'
14
14
  reached ambiently so a component can pass a named value down its subtree. */
15
15
  type StateFn = {
16
16
  /* No-arg form for an undefined initial with a declared type: `state<Foo>()` is
17
- `State<Foo | undefined>`. Without it `state<Foo>(undefined)` is an arity/assign
18
- error and `state(undefined)` infers `T = undefined` (every `.value` access then
19
- narrows to `never`). */
17
+ `State<Foo | undefined>`. Without it `state(undefined)` infers `T = undefined`
18
+ (every `.value` access then narrows to `never`). */
20
19
  <T>(): State<T | undefined>
21
20
  <T>(initial: T, transform?: (next: T, previous: T) => T): State<T>
21
+ /* The no-arg form spelled out: `state<Foo>(undefined)` is `State<Foo | undefined>`
22
+ too. Kept a distinct overload (not `initial?: T`) BELOW the general form — a real
23
+ initial still binds `T` from the value; only an explicit `undefined` (which the
24
+ general form rejects as "not assignable to T") falls through to here. A bare
25
+ `state(undefined)` still resolves against the general form (`T = undefined`), so
26
+ this overload changes nothing for it. */
27
+ <T>(initial: undefined): State<T | undefined>
22
28
  /* A writable cell reseeded from a reactive thunk (`state.linked(() => src())`). */
23
29
  linked: typeof linked
24
30
  /* A read-only cell computed from other cells (`state.computed(() => a() + b())`). */
@@ -0,0 +1,68 @@
1
+ import { isAsyncIterable } from '../shared/isAsyncIterable.ts'
2
+ import type { AsyncComputed } from '../shared/types/AsyncComputed.ts'
3
+ import type { NamedAsyncIterable } from '../shared/types/NamedAsyncIterable.ts'
4
+ import { createAsyncCell } from './runtime/createAsyncCell.ts'
5
+ import { createComputedNode } from './runtime/createComputedNode.ts'
6
+ import { isAsyncFunction } from './runtime/isAsyncFunction.ts'
7
+ import { OWNER } from './runtime/OWNER.ts'
8
+ import { readNode } from './runtime/readNode.ts'
9
+ import type { Computed } from './runtime/types/Computed.ts'
10
+ import { unlinkDeps } from './runtime/unlinkDeps.ts'
11
+ import { untrack } from './runtime/untrack.ts'
12
+
13
+ /*
14
+ The eager, stream-classifying read-only computed the compiler routes a bare-call /
15
+ identifier seed to — `computed(getStream())` / `computed(ref)` (ADR-0019 D1). Unlike the
16
+ lazy `computed` primitive it PROBES its seed once to self-identify the source: a
17
+ `NamedAsyncIterable` (socket / streaming rpc) auto-tracks its frames as an `AsyncComputed`,
18
+ an `async () => await …` seed unwraps its promise, and anything else — a sync value, a bare
19
+ opaque promise — falls back to the lazy `Computed`, so laziness is preserved on the miss and
20
+ only a real stream/promise producer becomes a cell. It lives apart from the `computed`
21
+ primitive so a DIRECT `computed(() => …)` call stays lazy (the primitive never probes); the
22
+ eager probe is reached only through this compiler-emitted entry, matching how `linked`
23
+ already classifies its seed. The probe runs untracked — a throwaway read must not subscribe
24
+ the enclosing build.
25
+ */
26
+ export function trackedComputed<T>(
27
+ compute: () => T | Promise<T> | NamedAsyncIterable<T>,
28
+ /* ADR-0032: when the compiler lifts a no-`await` async (sub)expression to a cell, it passes
29
+ `streaming: true` so the resolved promise does not join the SSR blocking barrier (the shell
30
+ ships pending, the client resolves). Default false = today's blocking registration; a stream
31
+ seed never registers regardless, so it only matters for a promise-unwrapping seed. */
32
+ streaming = false,
33
+ ): Computed<T> | AsyncComputed<T> {
34
+ /* `await` marker: an async-function seed unwraps its promise into a read-only async cell. */
35
+ if (isAsyncFunction(compute)) {
36
+ return createAsyncCell(compute as () => unknown, {
37
+ writable: false,
38
+ streaming,
39
+ }) as AsyncComputed<T>
40
+ }
41
+ /* Probe the seed once (untracked) to detect a self-identifying stream. A discarded stream
42
+ iterable costs nothing — a streaming rpc defers its fetch to the first pull, which never
43
+ happens here; a throw means a plain reactive compute, so fall through to the lazy path
44
+ (it throws at read, unchanged). */
45
+ let probe: unknown
46
+ let threw = false
47
+ try {
48
+ probe = untrack(compute)
49
+ } catch {
50
+ threw = true
51
+ }
52
+ if (!threw && isAsyncIterable(probe)) {
53
+ return createAsyncCell(compute as () => unknown, {
54
+ writable: false,
55
+ streaming,
56
+ }) as AsyncComputed<T>
57
+ }
58
+ /* Sync value or a bare (opaque) promise: the lazy computed, identical to the primitive. */
59
+ const node = createComputedNode(compute as () => unknown)
60
+ if (OWNER.current !== undefined) {
61
+ OWNER.current.push(() => unlinkDeps(node))
62
+ }
63
+ return {
64
+ get value(): T {
65
+ return readNode(node) as T
66
+ },
67
+ }
68
+ }
@@ -3,7 +3,7 @@ import type { effect } from '../effect.ts'
3
3
  import type { linked } from '../linked.ts'
4
4
  import type { Patch } from '../runtime/types/Patch.ts'
5
5
  import type { state } from '../state.ts'
6
- import type { SyncTransport } from './SyncTransport.ts'
6
+ import type { trackedComputed } from '../trackedComputed.ts'
7
7
 
8
8
  /*
9
9
  A lexical scope: the unit that owns a region's reactive data, its lifetime, and
@@ -14,21 +14,14 @@ surface is the imported `state`/`state.linked`/`state.computed`/`effect` (see
14
14
  scope (`$$scope().derive`/`.linked`/`.effect`, `state.share`/`.shared` → `share`/
15
15
  `shared`). The data surface MIRRORS `Doc` (read/replace/add/remove/derive/apply/
16
16
  snapshot) so the compiler can target a scope as a component's data binding directly;
17
- it nests (`child`/`root`) and passes values down the tree as context (`share`/`shared`).
17
+ it passes values down the tree as context (`share`/`shared`).
18
18
 
19
- The reactive primitives and capabilities remain on this internal shape because the
20
- lowered runtime calls them (`$$scope().linked(...)`, generated undo/persist) — they are
21
- withdrawn from the AUTHOR-facing public surface (docs/examples), not the runtime object.
22
- Capabilities route where the scope's changes go: `record()` to an undo journal,
23
- `persist()` to durable storage, `broadcast()` to peers — declared once, then `undo`/
24
- `redo` act on a recorded scope. The `history`/`persist`/`sync` helpers it composes are
25
- internal.
19
+ The reactive primitives remain on this internal shape because the lowered runtime calls
20
+ them (`$$scope().linked(...)`) — they are withdrawn from the AUTHOR-facing public surface
21
+ (docs/examples), not the runtime object.
26
22
  */
27
23
  export type Scope = {
28
24
  readonly id: string
29
- /* Dev-only display name (the host component/element it mounted into) for the
30
- inspector's Reactive tab; undefined for SSR/detached/child scopes. */
31
- readonly label?: string
32
25
  readonly parent: Scope | undefined
33
26
  /* data — mirrors Doc */
34
27
  read: <T>(path: string) => T
@@ -54,15 +47,15 @@ export type Scope = {
54
47
  state: typeof state
55
48
  linked: typeof linked
56
49
  computed: typeof computed
50
+ /* The eager stream-classifying read-only computed a bare-call `computed(getStream())`
51
+ lowers to — auto-tracks a stream/promise producer, falls back to a lazy computed. */
52
+ trackedComputed: typeof trackedComputed
57
53
  effect: typeof effect
58
54
  /* Adopts a teardown into this scope's lifetime — the build's reactivity stopper
59
55
  (effects/listeners), run first on `dispose` (before children, before capabilities).
60
56
  Internal: the mount core registers the build's disposer here so a component has ONE
61
57
  teardown (`dispose`) rather than a separate stop + dispose composed at every site. */
62
58
  own: (dispose: () => void) => void
63
- /* tree */
64
- child: (initial?: unknown) => Scope
65
- root: () => Scope
66
59
  /* context — values shared DOWN the tree (not in the reactive doc, which doesn't
67
60
  inherit): `share` puts a named value on this scope; `shared` reads the closest
68
61
  ancestor (self included) that has the key, undefined if none. The value is held
@@ -70,15 +63,6 @@ export type Scope = {
70
63
  plain object snapshot. */
71
64
  share: (key: string, value: unknown) => void
72
65
  shared: <T>(key: string) => T | undefined
73
- /* capabilities — enable where the scope's changes go */
74
- record: (options?: { limit?: number }) => void
75
- persist: (key?: string) => void
76
- broadcast: (transport: SyncTransport) => void
77
- /* undo/redo — act on a recorded scope */
78
- undo: () => void
79
- redo: () => void
80
- canUndo: () => boolean
81
- canRedo: () => boolean
82
66
  /* lifetime */
83
67
  dispose: () => void
84
68
  }
@@ -1,8 +1,8 @@
1
1
  import { cache } from '../shared/cache.ts'
2
2
  import { REMOTE_FUNCTION } from '../shared/REMOTE_FUNCTION.ts'
3
3
  import type { CacheOnContext } from '../shared/types/CacheOnContext.ts'
4
+ import type { NamedAsyncIterable } from '../shared/types/NamedAsyncIterable.ts'
4
5
  import type { RemoteFunction } from '../shared/types/RemoteFunction.ts'
5
- import type { Subscribable } from '../shared/types/Subscribable.ts'
6
6
  import { effect } from './effect.ts'
7
7
  import { generationGuard } from './runtime/generationGuard.ts'
8
8
  import type { EffectResult } from './runtime/types/EffectResult.ts'
@@ -40,7 +40,7 @@ export function watch(
40
40
  handler: (values: unknown[]) => void,
41
41
  ): () => void
42
42
  export function watch<Frame>(
43
- source: Subscribable<Frame>,
43
+ source: NamedAsyncIterable<Frame>,
44
44
  handler: (frame: Frame, context: CacheOnContext) => void | Promise<void>,
45
45
  ): () => void
46
46
  export function watch<Args, Return>(
@@ -74,7 +74,7 @@ export function watch(
74
74
  existing cache.on loop is the single implementation this branch delegates to. */
75
75
  if (isSubscribable(source)) {
76
76
  return cache.on(
77
- source as Subscribable<unknown>,
77
+ source as NamedAsyncIterable<unknown>,
78
78
  handler as (frame: unknown, context: CacheOnContext) => void | Promise<void>,
79
79
  )
80
80
  }
@@ -33,8 +33,11 @@ import { cacheStoreSlot } from './lib/shared/cacheStoreSlot.ts'
33
33
  import { createCacheStore } from './lib/shared/createCacheStore.ts'
34
34
  import { loadEnvFromDataDir } from './lib/shared/loadEnvFromDataDir.ts'
35
35
  import { pageSlot } from './lib/shared/pageSlot.ts'
36
+ import { pendingAsyncCellsSlot } from './lib/shared/pendingAsyncCellsSlot.ts'
37
+ import { resolvedCellsSlot } from './lib/shared/resolvedCellsSlot.ts'
36
38
  import { runningAsStandaloneBinary } from './lib/shared/runningAsStandaloneBinary.ts'
37
39
  import { sharedCacheStoreSlot } from './lib/shared/sharedCacheStoreSlot.ts'
40
+ import { streamedCellsSlot } from './lib/shared/streamedCellsSlot.ts'
38
41
 
39
42
  /*
40
43
  Resolve config into process.env before anything reads it (createServer reads
@@ -76,6 +79,17 @@ sharedCacheStoreSlot.resolver = () => sharedCacheStore
76
79
 
77
80
  cacheStoreSlot.resolver = () => requestContext.getStore()?.cache ?? sharedCacheStore
78
81
 
82
+ /* One process-wide fallback list for reads with no request in flight (boot/cron/socket) —
83
+ real requests each carry their own `pendingAsyncCells`, so the SSR barrier drains a
84
+ per-request list and concurrent renders never cross-contaminate. */
85
+ const sharedPendingAsyncCells = { promises: [] }
86
+ pendingAsyncCellsSlot.resolver = () =>
87
+ requestContext.getStore()?.pendingAsyncCells ?? sharedPendingAsyncCells
88
+ const sharedResolvedCells = { entries: [] }
89
+ resolvedCellsSlot.resolver = () => requestContext.getStore()?.resolvedCells ?? sharedResolvedCells
90
+ const sharedStreamedCells = { entries: [] }
91
+ streamedCellsSlot.resolver = () => requestContext.getStore()?.streamedCells ?? sharedStreamedCells
92
+
79
93
  pageSlot.resolver = resolvePageSnapshot
80
94
 
81
95
  await createServer({
@@ -6,17 +6,18 @@ the HTTP method. The bundler swaps the runtime per build target: direct call on
6
6
  the server, fetch over the network on the client.
7
7
 
8
8
  Args (what the caller passes in) come from the handler's parameter type —
9
- either an inline annotation or an explicit `GET<{ id: string }>(...)`
10
- generic. Return (what the caller receives after Content-Type-driven
11
- decoding) is inferred from the handler's return type via the
12
- `TypedResponse<T>` brand on `json`/`error`/`redirect`/`jsonl`/`sse`, so
13
- plain `GET(() => json({...}))` already types end-to-end.
9
+ annotate the parameter and the body infers; a typed generic on the helper is a
10
+ compile error. Return (what the caller receives after Content-Type-driven
11
+ decoding) is inferred from the handler's return type via the `TypedResponse<T>`
12
+ brand on `json`/`error`/`redirect`/`jsonl`/`sse`, so plain
13
+ `GET(() => json({...}))` already types end-to-end.
14
14
 
15
- For inbound validation pass a Standard Schema as `inputSchema` in the
16
- second argument: `GET(fn, { inputSchema })`. Args then infers from the
17
- schema's output type and the server replies with 422 on validation
18
- failure. An optional `outputSchema` describes the success body for the
19
- OpenAPI 200 response and the MCP tool output.
15
+ For inbound validation pass a Standard Schema under `schemas.input` in the
16
+ second argument: `GET(fn, { schemas: { input } })`. Args then infers from the
17
+ schema's output type and the server replies with 422 on validation failure. An
18
+ optional `schemas.output` describes the success body for the OpenAPI 200
19
+ response and the MCP tool output; without one the handler's return type is
20
+ projected to JSON Schema.
20
21
 
21
22
  `json(...)` from `abide/server/json` is a thin wrapper over `Response.json`
22
23
  that defaults `Cache-Control: no-store`, since intermediary caches shouldn't
@@ -24,10 +25,11 @@ memoise rpc replies (the framework's per-request cache handles in-process
24
25
  dedupe). Other helpers are siblings, one per file: `abide/server/error`,
25
26
  `abide/server/redirect`, `abide/server/sse`, `abide/server/jsonl`.
26
27
 
27
- Every rpc value also exposes `.raw(args?)` (returns the underlying
28
+ Every rpc value also exposes `.raw(args?, init?)` (returns the underlying
28
29
  `Response`) for callers that need headers or status. A streaming rpc — a
29
- jsonl/sse handler — returns a `Subscribable` over the frames from its bare
30
- call (`fn(args)`); iterate it with `for await` or react to it with `watch(fn(args), frame => …)`.
30
+ jsonl/sse handler — returns a `NamedAsyncIterable` over the frames from its bare
31
+ call (`fn(args)`); iterate it with `for await` or react to it with
32
+ `watch(fn(args), frame => …)`.
31
33
  */
32
34
 
33
35
  import { GET } from '@abide/abide/server/GET'
@@ -5,7 +5,7 @@ same wiring as `abide start`, no fixtures) and hands back the whole surface:
5
5
  app.fetch(path) — pages and raw HTTP, origin + mount base resolved
6
6
  app.rpc.<name>(args) — RPCs over the real pipeline (CSRF, cookies, base),
7
7
  typed and decoded; .raw(args) for the Response
8
- app.sockets.<name> — a live Socket: iterate it (for await), .peek(), .broadcast(m)
8
+ app.sockets.<name> — a live Socket: iterate it (for await), .peek(), .publish(m)
9
9
  app.health() — the /__abide/health payload
10
10
 
11
11
  `app.rpc` / `app.sockets` are typed from your own RPCs and sockets — generated