@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,5 +1,5 @@
1
+ import type { NamedAsyncIterable } from './NamedAsyncIterable.ts'
1
2
  import type { RpcOptions } from './RpcOptions.ts'
2
- import type { Subscribable } from './Subscribable.ts'
3
3
 
4
4
  /*
5
5
  Call signature shared by RemoteFunction and RawRemoteFunction. The base
@@ -10,24 +10,25 @@ optional-arg signature lets call sites write `fn()` instead of
10
10
  stays callable while `Args` is still generic (cache() invokes producers
11
11
  before `Args` resolves). FormData is the multipart upload escape hatch —
12
12
  see RemoteFunction. The trailing `Opts` differs by variant: the smart bare
13
- call (RemoteFunction) carries cache/stream options (SmartReadOptions), while
14
- `.raw` (RawRemoteFunction) carries per-call transport options (RpcOptions:
13
+ call (RemoteFunction) takes no second argument at all (`Opts = never` — all
14
+ cache/stream policy lives on the rpc definition per ADR-0020), while `.raw`
15
+ (RawRemoteFunction) carries per-call transport options (RpcOptions:
15
16
  signal/keepalive/priority/cache/headers). The server ignores either, so the
16
17
  callable stays isomorphic.
17
18
 
18
19
  A streaming `Resolved` (an `AsyncIterable<Frame>`, branded by jsonl()/sse()) makes
19
- the bare call return a `Subscribable<Frame>` synchronously — the iterable IS the
20
+ the bare call return a `NamedAsyncIterable<Frame>` synchronously — the iterable IS the
20
21
  value, consumed by `for await` or `state(fn(args))`, no `.stream()`. Every other
21
22
  `Resolved` stays `Promise<Resolved>`. `await`-ing a streaming call is a compile
22
- error (a Subscribable is not awaitable), which replaces the old runtime guard.
23
+ error (a NamedAsyncIterable is not awaitable), which replaces the old runtime guard.
23
24
  */
24
25
  /* `[Resolved] extends [never]` first: a handler that only ever returns error() resolves to
25
26
  `never`, and `never extends AsyncIterable` is vacuously true — without this guard its call
26
- would mistype as a Subscribable. A real streaming rpc's `Resolved` is `AsyncIterable<Frame>`. */
27
+ would mistype as a NamedAsyncIterable. A real streaming rpc's `Resolved` is `AsyncIterable<Frame>`. */
27
28
  type CallResult<Resolved> = [Resolved] extends [never]
28
29
  ? Promise<Resolved>
29
30
  : Resolved extends AsyncIterable<infer Frame>
30
- ? Subscribable<Frame>
31
+ ? NamedAsyncIterable<Frame>
31
32
  : Promise<Resolved>
32
33
 
33
34
  export type RemoteCallable<Args, Resolved, Opts = RpcOptions> = ((
@@ -1,12 +1,12 @@
1
+ import type { CachePolicy } from './CachePolicy.ts'
1
2
  import type { ClientFlags } from './ClientFlags.ts'
2
3
  import type { ErrorSpec } from './ErrorSpec.ts'
3
4
  import type { HttpMethod } from './HttpMethod.ts'
4
- import type { Outbox } from './Outbox.ts'
5
5
  import type { RawRemoteFunction } from './RawRemoteFunction.ts'
6
6
  import type { RemoteCallable } from './RemoteCallable.ts'
7
7
  import type { RpcError } from './RpcError.ts'
8
8
  import type { RpcErrorGuard } from './RpcErrorGuard.ts'
9
- import type { SmartReadOptions } from './SmartReadOptions.ts'
9
+ import type { StreamPolicy } from './StreamPolicy.ts'
10
10
 
11
11
  /*
12
12
  Remote function reference produced by GET/POST/... inside an `$rpc/**`
@@ -21,7 +21,7 @@ method, same url, same args, no decode. Pass `fn.raw` to cache() to
21
21
  memoise raw Responses against the same cache key as `fn` (both share one
22
22
  stored entry — the decode just happens on the way out for callers of
23
23
  `fn`). A streaming handler (jsonl()/sse()) makes the bare call return a
24
- `Subscribable<Frame>` directly (the iterable IS the value) — consume it with
24
+ `NamedAsyncIterable<Frame>` directly (the iterable IS the value) — consume it with
25
25
  `for await (… of fn(args))` or `state(fn(args))`; there is no `.stream()`, and
26
26
  `await`-ing a streaming call is a compile error. For sustained broadcast /
27
27
  pub-sub use the `abide/server/socket` primitive — HTTP rpc isn't the place for
@@ -44,23 +44,29 @@ export type RemoteFunction<
44
44
  Args,
45
45
  Return,
46
46
  Errors extends ErrorSpec = Record<never, never>,
47
- Durable extends boolean = false,
48
- > = RemoteCallable<Args, Return, SmartReadOptions> & {
47
+ > = RemoteCallable<Args, Return, never> & {
49
48
  readonly method: HttpMethod
50
49
  readonly url: string
51
50
  readonly clients: ClientFlags
52
51
  readonly crossOrigin?: boolean
52
+ /* Endpoint cache/stream policy declared on the rpc definition (ADR-0020), stamped
53
+ here so the smart read (readThrough) reads it as the bottom policy layer. Ships to
54
+ the client too — it governs client cache behaviour and is behaviour, not secrets
55
+ (`shared` is a client no-op). Absent on a plainly-defined rpc (method defaults only). */
56
+ readonly cache?: CachePolicy<Args>
57
+ readonly stream?: StreamPolicy
53
58
  readonly raw: RawRemoteFunction<Args>
54
59
  fetch(request: Request): Promise<Response>
55
60
  /* Type-guard a caught error against this rpc's declared `errors` (plus the framework
56
- `'validation'` / `'queued'`): narrows `.kind` and, for a known kind, `.data` — the
57
- per-rpc replacement for a global guard, since the error name → data type mapping
58
- lives in the rpc's own spec. */
61
+ `'validation'`): narrows `.kind` and, for a known kind, `.data` — the per-rpc
62
+ replacement for a global guard, since the error name → data type mapping lives in
63
+ the rpc's own spec. */
59
64
  readonly isError: RpcErrorGuard<Errors>
60
65
  /* Pre-bound selector sugar: `fn.pending(args?)` ≡ `pending(fn, args?)`, and likewise for
61
66
  refreshing / refresh / peek — the rpc is the leading selector, bound in. The argument is
62
67
  this rpc's typed `Args` (the by-args refinement); tags / cross-cutting selection stay on
63
- the globals. The bare call `fn(args, opts)` IS the cached read (was `fn.cache`), and
68
+ the globals. The bare call `fn(args)` IS the cached read (was `fn.cache`) — all cache
69
+ policy lives on the definition, so there is no second options argument, and
64
70
  `refresh(args?)` refetches keeping the stale value visible (was `invalidate`, which
65
71
  dropped). `peek(args?)` reads the retained value synchronously (a streaming rpc peeks its
66
72
  latest frame). `patch` is fetch-only, so it is omitted for a streaming rpc (a stream isn't
@@ -93,9 +99,4 @@ export type RemoteFunction<
93
99
  : {
94
100
  patch(args: Args | undefined, updater: (current: Return) => Return): void
95
101
  patch(updater: (current: Return) => Return): void
96
- }) /* `outbox` presence follows the `outbox: true` opt: the mutating helper threads `Durable`
97
- into the return type so a DURABLE rpc's `.outbox` is the required queue face (no optional
98
- chain). A non-durable rpc keeps it optional — assignable to a durable one everywhere a
99
- bare `RemoteFunction<Args, Return>` slot (cache selectors, registries) is expected, since
100
- required→optional widens cleanly and no call site had to learn the `Durable` bit. */ &
101
- (Durable extends true ? { readonly outbox: Outbox<Args> } : { readonly outbox?: Outbox<Args> })
102
+ })
@@ -0,0 +1,11 @@
1
+ /*
2
+ The per-request list of async-cell values that RESOLVED during an SSR render, each keyed by the
3
+ cell's serialization-stable render-path id (`${scope.id}:${index}`). `createAsyncCell` pushes an
4
+ entry when its seed settles server-side; the page renderer stamps them into `__SSR__.cells`
5
+ (ref-json-encoded) so the client hydrates the cell WARM — reading the value instead of re-running
6
+ the seed (no refetch, no pending flash). The sibling of `PendingAsyncCells` (the barrier's
7
+ in-flight list); this one holds settled VALUES, read at render-return rather than awaited.
8
+ */
9
+ export type ResolvedCells = {
10
+ entries: { key: string; value: unknown }[]
11
+ }
@@ -0,0 +1,15 @@
1
+ /*
2
+ The build-time success-body descriptor for an rpc, derived from the handler's return type by the
3
+ warm server program (ADR-0030). Mirrors `RpcHelper`'s `SuccessBody<R>` projection: the handler's
4
+ `Awaited<ReturnType>` union has its `TypedError` branches dropped, and each success
5
+ `TypedResponse<Body>` contributes its `Body`. `type` is that body rendered as a TS type string
6
+ (the union of the surviving branches). For a streaming endpoint (`jsonl()`/`sse()` →
7
+ `TypedResponse<AsyncIterable<Frame>>`) `streaming` is true and `type` is the per-FRAME type — the
8
+ AsyncIterable element — so a consumer describes one streamed item rather than the iterable itself.
9
+ The query fails open to undefined (no handler/body resolvable), matching every other server-program
10
+ query.
11
+ */
12
+ export type ReturnBody = {
13
+ type: string
14
+ streaming: boolean
15
+ }
@@ -0,0 +1,23 @@
1
+ import type { ErrorJsonSchemas } from './ErrorJsonSchemas.ts'
2
+ import type { InputCoercion } from './InputCoercion.ts'
3
+ import type { OutputWirePlan } from './OutputWirePlan.ts'
4
+
5
+ /*
6
+ The build-time facts the warm rpc server program resolves for one `$rpc/**` handler, gathered into
7
+ a single named payload that `prepareRpcModule` destructures (each rewrite direction picks the fields
8
+ it needs). Replaces a positional argument list where ordering was load-bearing — a new build stamp
9
+ is now one field on this shape rather than a new positional slot every caller must thread. Every
10
+ field is optional: an absent field means the warm program resolved no verdict (no program built, an
11
+ unresolvable node), and the rewrite falls open to its char-scan / today's behaviour exactly as
12
+ before. `streaming` overrides the char-scan streaming verdict; the rest are stamped as server opts
13
+ (`coercion`→`coerce`, `inputSchema`→`inputJsonSchema`, `outputSchema`→`outputJsonSchema`,
14
+ `errorSchemas`→`errorJsonSchemas`) or the client opt (`outputWirePlan`).
15
+ */
16
+ export type RpcBuildStamps = {
17
+ streaming?: boolean
18
+ coercion?: InputCoercion
19
+ inputSchema?: Record<string, unknown>
20
+ outputSchema?: Record<string, unknown>
21
+ errorSchemas?: ErrorJsonSchemas
22
+ outputWirePlan?: OutputWirePlan
23
+ }
@@ -1,6 +1,5 @@
1
1
  import type { HttpError } from '../HttpError.ts'
2
2
  import type { ErrorSpec } from './ErrorSpec.ts'
3
- import type { OutboxEntry } from './OutboxEntry.ts'
4
3
  import type { StandardSchemaV1 } from './StandardSchemaV1.ts'
5
4
  import type { ValidationErrorData } from './ValidationErrorData.ts'
6
5
 
@@ -16,9 +15,9 @@ type DeclaredErrorData<
16
15
  /*
17
16
  A rpc's `isError` guard — branch a caught error on a kind THIS rpc knows, with the
18
17
  data type narrowed from the rpc's own `errors` spec. `rpc.isError(err, 'outOfStock')`
19
- narrows `.data` to that error's payload; the framework-reserved `'validation'` /
20
- `'queued'` to their fixed shapes; any other string narrows `.kind` only (`.data`
21
- stays `unknown` — it belongs to a different rpc, whose type isn't in scope here).
18
+ narrows `.data` to that error's payload; the framework-reserved `'validation'` to its
19
+ fixed shape; any other string narrows `.kind` only (`.data` stays `unknown` — it
20
+ belongs to a different rpc, whose type isn't in scope here).
22
21
 
23
22
  The declared-name overload comes first so an app error shadows the reserved ones on a
24
23
  name collision, and so the kind argument autocompletes to the rpc's declared names.
@@ -32,9 +31,5 @@ export interface RpcErrorGuard<Errors extends ErrorSpec> {
32
31
  error: unknown,
33
32
  kind: 'validation',
34
33
  ): error is HttpError & { kind: 'validation'; data: ValidationErrorData }
35
- (
36
- error: unknown,
37
- kind: 'queued',
38
- ): error is HttpError & { kind: 'queued'; data: OutboxEntry<unknown> }
39
34
  <Kind extends string>(error: unknown, kind: Kind): error is HttpError & { kind: Kind }
40
35
  }
@@ -9,8 +9,8 @@ fan-out and a client-side ws proxy by build target. Iterating the socket
9
9
  is the live stream — no replay. `.tail(count)` opens a subscription seeded
10
10
  with the last `count` frames of the retained tail (declared via
11
11
  `{ tail: n }`; no-arg = the whole retained tail) before going live — the
12
- optional Subscribable retention capability a reactive `watch(socket, …)`
13
- seeds from. `broadcast` is isomorphic: server code broadcasts in-process
12
+ optional NamedAsyncIterable retention capability a reactive `watch(socket, …)`
13
+ seeds from. `publish` is isomorphic: server code publishes in-process
14
14
  and fans out to remote subscribers; client code sends a `pub` frame the
15
15
  dispatcher validates against the topic's `clientPublish` flag. `clients`
16
16
  exposes which adapter surfaces (browser / mcp / cli) advertise this
@@ -21,8 +21,8 @@ export interface Socket<T> extends AsyncIterable<T> {
21
21
  readonly clients: ClientFlags
22
22
  /* Send a frame to every subscriber (server always; client only when `clientPublish` is
23
23
  set). Isomorphic: server code fans out in-process + to remote subscribers; client code
24
- sends a validated `pub` frame. */
25
- broadcast(message: T): void
24
+ sends a validated `pub` frame. Mirrors Bun's `server.publish()`. */
25
+ publish(message: T): void
26
26
  tail(count?: number, hooks?: TailHooks): AsyncIterable<T>
27
27
  /* The latest retained frame, synchronously — `T | undefined` when none. The value
28
28
  member of the probe family for a stream; `peek(socket)` routes here. */
@@ -10,7 +10,8 @@ write-set and read-set can't drift apart on a matching-string-key handshake.
10
10
 
11
11
  Three partitions:
12
12
  - `SsrBootState` fields seed ambient slots via the exhaustive `seedBootState` map;
13
- - `cache` is the inline warm-seed partition, drained through `seedResolved`;
13
+ - `cache` / `cells` are the warm-seed partitions (settled `cache()` values / resolved async-cell
14
+ values), drained into their client stores before mount;
14
15
  - `route`/`params` are stamped for inspectability and are NOT read by the client
15
16
  (the router re-resolves the route from the URL).
16
17
  */
@@ -18,4 +19,7 @@ export type SsrPayload = SsrBootState & {
18
19
  route: string
19
20
  params: Record<string, string>
20
21
  cache?: CacheSnapshotEntry[]
22
+ /* Async-cell values that resolved during SSR, keyed by render-path id → ref-json string.
23
+ `startClient` seeds these into `CELL_SEED`; a hydrating cell reads its key to warm-hydrate. */
24
+ cells?: Record<string, string>
21
25
  }
@@ -0,0 +1,11 @@
1
+ /*
2
+ Endpoint stream policy, declared once on a streaming rpc definition (ADR-0020).
3
+ `n` is the replay depth — how many retained frames a fresh reader gets before
4
+ going live, pairing with the server-side `tail` retention. Endpoint-fixed: a
5
+ late joiner can't request deeper replay than a fresh view. No runtime consumer
6
+ today (subscribableFromResponse ignores it); threaded onto the definition for
7
+ completeness and future use.
8
+ */
9
+ export type StreamPolicy = {
10
+ n?: number
11
+ }
@@ -0,0 +1,19 @@
1
+ /*
2
+ The per-request list of STREAMING async-cell values that SETTLED during this SSR pass, each keyed by
3
+ the cell's serialization-stable render-path id (`${scope.id}:${index}`) (ADR-0035). A streaming cell
4
+ ships pending in the shell (excluded from the pre-mount `__SSR__.cells` warm-seed, which would
5
+ diverge from that pending markup), so instead `createAsyncCell.settleValue` records its resolved
6
+ VALUE here when it settles server-side. The page renderer reads this AFTER the shell — streaming an
7
+ `__abideResolve({ cellKey, value })` chunk per entry — so the client adopts the server-resolved
8
+ value post-hydration (no `loading…` flash) instead of only cold-re-running the seed.
9
+
10
+ Values (not promises) so the drain never AWAITS — a streaming cell may legitimately stay pending
11
+ through the whole request (the `{#if getFoo()}`-holds case), and awaiting it would hang the
12
+ response. Only cells that settled before the drain point are streamed; one still pending is simply
13
+ not (the client cold-runs it, unchanged). The streaming sibling of `ResolvedCells` (the BLOCKING
14
+ warm-seed, read at render-return and stamped into `__SSR__.cells`); this one is read later in the
15
+ stream and shipped out-of-order as chunks.
16
+ */
17
+ export type StreamedCells = {
18
+ entries: { key: string; value: unknown }[]
19
+ }
@@ -1,10 +1,28 @@
1
1
  import type { CacheSnapshotEntry } from './CacheSnapshotEntry.ts'
2
2
 
3
3
  /*
4
- Payload of one streamed `window.__abideResolve(...)` call. A full
5
- `CacheSnapshotEntry` settles the placeholder with warm data; a `{ key, miss }`
6
- marker means the server couldn't snapshot that body (binary, rejected, evicted)
7
- so the client settles the placeholder with a live re-fetch instead. Discriminate
8
- on the `miss` field.
4
+ Payload of one streamed `window.__abideResolve(...)` call. Three arms:
5
+
6
+ - a full `CacheSnapshotEntry` (has `key` + body) settles a cache placeholder with warm data;
7
+ - a `{ key, miss }` marker means the server couldn't snapshot that cache body (binary, rejected,
8
+ evicted) so the client settles the placeholder with a live re-fetch instead;
9
+ - a `{ cellKey, value }` (ADR-0035) carries a STREAMING CELL's server-resolved value, keyed by its
10
+ render-path warm-key and `encodeRefJson`-encoded, applied post-hydration as a reactive update so
11
+ a non-cache peek stops flashing `loading…` on hydrate.
12
+ - a `{ cellSeed, value }` (ADR-0039) carries a STREAMED CHILD's blocking async-cell value that
13
+ resolved AFTER the head `__SSR__.cells` snapshot (its render was deferred to the drain), seeded
14
+ PRE-mount into `CELL_SEED` — the same warm partition as `__SSR__.cells` — so the child's cell
15
+ constructs resolved on hydrate rather than re-running.
16
+
17
+ Discriminate on `cellSeed` then `cellKey` (cell) then `miss` (cache miss) then default (cache entry).
9
18
  */
10
- export type StreamedResolution = CacheSnapshotEntry | { key: string; miss: true }
19
+
20
+ /* The cache arms — a warm snapshot entry or a `{ key, miss }` marker. `streamCacheResolutions`
21
+ yields exactly these (never the cell arm), so its element type is `CacheResolution`, keeping a
22
+ `.key` access on its results well-typed. */
23
+ export type CacheResolution = CacheSnapshotEntry | { key: string; miss: true }
24
+
25
+ export type StreamedResolution =
26
+ | CacheResolution
27
+ | { cellKey: string; value: string }
28
+ | { cellSeed: string; value: string }
@@ -1,5 +1,5 @@
1
1
  /*
2
- Hooks a `tail(count, hooks)` caller passes to a Subscribable's retention
2
+ Hooks a `tail(count, hooks)` caller passes to a NamedAsyncIterable's retention
3
3
  capability.
4
4
  `replayed` must be signalled in-band exactly once per iteration — after the
5
5
  last replayed frame, before any live frame, and even when nothing was
@@ -0,0 +1,11 @@
1
+ /*
2
+ The wire codec kind for a single top-level Args field (ADR-0029). Generalizes ADR-0028's scalar
3
+ `number`/`boolean` query coercion to the structured value kinds the warm server program can name
4
+ through the type graph — a `Date` / `Set` / `Map` by symbol identity, a `bigint` by type flag.
5
+ parseArgs revives a field's plain-JSON wire form into the runtime value the handler's declared
6
+ type expects: an ISO string → `Date`, a numeric string → `bigint`, a JSON array → `Set`, a JSON
7
+ entries array (or object) → `Map`. Top-level fields only in this increment — a structured value
8
+ NESTED inside another value is not descended into. Reviving is fail-open: an unrevivable value is
9
+ left as its JSON form, never thrown, so the plain-JSON / OpenAPI contract still works.
10
+ */
11
+ export type WireKind = 'number' | 'boolean' | 'date' | 'bigint' | 'set' | 'map'
@@ -116,6 +116,11 @@ function interpolate(segments: RouteSegment[], params: Query): string {
116
116
  if (segment.optional && (value === undefined || value === '')) {
117
117
  return undefined
118
118
  }
119
+ /* A required `[name]`/`[...rest]` with no value would otherwise stringify to the
120
+ literal "undefined" and silently mis-route; fail loudly instead. */
121
+ if (value === undefined || value === null) {
122
+ throw new Error(`abide url(): missing required param "${segment.name}"`)
123
+ }
119
124
  const text = String(value)
120
125
  return segment.catchAll
121
126
  ? text.split('/').map(encodeURIComponent).join('/')
@@ -0,0 +1,33 @@
1
+ import { fieldErrorsFromIssues } from './fieldErrorsFromIssues.ts'
2
+ import { HttpError } from './HttpError.ts'
3
+ import type { StandardSchemaV1 } from './types/StandardSchemaV1.ts'
4
+ import type { ValidationErrorData } from './types/ValidationErrorData.ts'
5
+
6
+ /*
7
+ Builds the HttpError a client-side pre-flight validation failure throws (ADR-0026 D2),
8
+ shaped IDENTICALLY to the one a caller gets when the server rejects the same input with a
9
+ 422: `kind: 'validation'`, `status: 422`, `data: ValidationErrorData` (`{ issues, fields }`),
10
+ so an `error instanceof HttpError && error.kind === 'validation'` branch — and the
11
+ `error.data.fields` form-error map — read the same whether the rejection came from the
12
+ client pre-flight or the server.
13
+
14
+ This is a UX optimization ONLY, never a trust boundary: the server's unconditional
15
+ `inputSchema['~standard'].validate` → `validationError` 422 (defineRpc.ts) stays
16
+ authoritative. A client that skips or fakes this check is still fully validated on the
17
+ server.
18
+
19
+ The `data` mapping (`fieldErrorsFromIssues`) is the same isomorphic helper the server 422
20
+ uses; the response envelope (`{ $abideError, data }`, status 422, reason phrase) mirrors
21
+ `typedErrorResponse('validation', 422, …)` — reconstructed here with a plain web-standard
22
+ `Response` so the browser bundle never imports the server's Response builder. The status
23
+ text mirrors `STATUS_TEXT[422]`.
24
+ */
25
+ export function validationHttpError(issues: readonly StandardSchemaV1.Issue[]): HttpError {
26
+ const data: ValidationErrorData = { issues, fields: fieldErrorsFromIssues(issues) }
27
+ const response = new Response(JSON.stringify({ $abideError: 'validation', data }), {
28
+ status: 422,
29
+ statusText: 'Unprocessable Content',
30
+ headers: { 'content-type': 'application/json' },
31
+ })
32
+ return new HttpError(response, 'validation', data)
33
+ }
@@ -0,0 +1,16 @@
1
+ /*
2
+ The single source of the async-cell warm-seed key format: the `${scopeId}:${cellIndex}` string that
3
+ pairs a cell's serialization-stable render-path scope id with its per-scope declaration ordinal.
4
+
5
+ Both sides of the SSR→client handoff mint the key through THIS callable — the isomorphic
6
+ `createAsyncCell` runs it server-side to key its `resolvedCells`/`streamedCells` entries (which the
7
+ page renderer stamps into `__SSR__.cells` / streams as `{ cellKey }` / `{ cellSeed }` chunks), and
8
+ client-side to look the value up in `CELL_SEED`. Because the two sides now invoke one function, the
9
+ byte-for-byte agreement the warm-seed depends on is a shared definition rather than a template
10
+ literal re-typed at each call site: changing the delimiter here changes it for both sides at once,
11
+ so the SSR-text-≡-first-client-render mismatch class (ADR-0033) cannot silently reopen from a
12
+ one-sided edit. Pure so it stays trivially testable and inlinable.
13
+ */
14
+ export function warmSeedKey(scopeId: string, cellIndex: number): string {
15
+ return `${scopeId}:${cellIndex}`
16
+ }
@@ -0,0 +1,30 @@
1
+ /*
2
+ The value-directed wire ENCODE step for a rpc response body (ADR-0029 output path). A
3
+ `JSON.stringify` replacer that rewrites the structured runtime values plain JSON can't carry into
4
+ their honest-JSON wire form, so `json()` never drops a `Set`/`Map` to `{}` or throws on a `bigint`:
5
+
6
+ - `bigint` → its digit string (plain JSON.stringify throws on a bigint)
7
+ - `Set<T>` → a JSON array of its values (plain JSON.stringify emits `{}`)
8
+ - `Map<K,V>`→ a JSON array of `[K,V]` entries (uniform, carries non-string keys)
9
+
10
+ A `Date` needs no branch — its native `toJSON` already rewrote it to an ISO string before the
11
+ replacer runs. Value-directed (not type-directed), so it runs for EVERY client — a non-abide caller
12
+ (curl / an OpenAPI SDK) receives the same honest JSON the projected schema (`jsonSchemaForType`)
13
+ describes. It is naturally recursive: a returned array/object member surfaces through the same
14
+ replacer, so a `Set` nested inside an object still encodes. Never throws — an ordinary value falls
15
+ through untouched.
16
+ */
17
+ export function wireJsonReplacer(_key: string, value: unknown): unknown {
18
+ if (typeof value === 'bigint') {
19
+ return value.toString()
20
+ }
21
+ if (value instanceof Set) {
22
+ return [...value]
23
+ }
24
+ if (value instanceof Map) {
25
+ /* A Map iterates as `[key, value]` pairs, so the spread yields the entries array the
26
+ client output plan (and the projected schema) expects. */
27
+ return [...value]
28
+ }
29
+ return value
30
+ }
@@ -1,9 +1,11 @@
1
+ import { resolve } from 'node:path'
1
2
  import { augmentModule } from './augmentModule.ts'
2
3
  import { carriesBodyArgs } from './carriesBodyArgs.ts'
3
4
  import { detectRpcMethod } from './detectRpcMethod.ts'
4
5
  import { fileStem } from './fileStem.ts'
5
6
  import { RPC_ARGS_TYPE } from './RPC_ARGS_TYPE.ts'
6
7
  import { rpcUrlForFile } from './rpcUrlForFile.ts'
8
+ import type { HttpMethod } from './types/HttpMethod.ts'
7
9
  import { writeDts } from './writeDts.ts'
8
10
 
9
11
  /*
@@ -22,15 +24,23 @@ export async function writeRpcDts({
22
24
  rpcDir,
23
25
  rpcFiles,
24
26
  importName,
27
+ methodForModule,
25
28
  }: {
26
29
  cwd: string
27
30
  rpcDir: string
28
31
  rpcFiles: string[]
29
32
  importName: string
33
+ /* Warm server-program method resolver (ADR-0025 D2): reads the method off the export's
34
+ helper symbol so an aliased/re-exported helper resolves where `detectRpcMethod`'s
35
+ `RPC_EXPORT` regex misses it. Absent (no warm program) or undefined-per-file falls open to
36
+ the regex, byte-identical to before. */
37
+ methodForModule?: (modulePath: string) => HttpMethod | undefined
30
38
  }): Promise<void> {
31
39
  const pairs = await Promise.all(
32
40
  rpcFiles.map(async (file): Promise<[string, string] | undefined> => {
33
- const method = detectRpcMethod(await Bun.file(`${rpcDir}/${file}`).text())
41
+ const method =
42
+ methodForModule?.(resolve(rpcDir, file)) ??
43
+ detectRpcMethod(await Bun.file(`${rpcDir}/${file}`).text())
34
44
  // A body rpc's args can't ride a URL — leave it out of the url() rpc map.
35
45
  if (!method || carriesBodyArgs(method)) {
36
46
  return undefined
@@ -8,7 +8,7 @@ Emits a `.d.ts` that augments createTestApp's `SocketClient` interface with one
8
8
  entry per $socket, keyed by socket name (the same key `app.sockets.<name>`
9
9
  resolves at runtime). The `socket(...)` helper statically returns `Socket<T>`,
10
10
  so each entry is the export's own type directly — `app.sockets.chat` types as
11
- `Socket<ChatMessage>`, iterable for the live stream with `.tail`/`.broadcast`.
11
+ `Socket<ChatMessage>`, iterable for the live stream with `.tail`/`.publish`.
12
12
  Written to `src/.abide/testSockets.d.ts` like its rpc sibling.
13
13
  */
14
14
  export async function writeTestSocketsDts({
@@ -39,8 +39,11 @@ import { commandNameForUrl } from '../shared/commandNameForUrl.ts'
39
39
  import { createCacheStore } from '../shared/createCacheStore.ts'
40
40
  import { HEALTH_PATH } from '../shared/HEALTH_PATH.ts'
41
41
  import { pageSlot } from '../shared/pageSlot.ts'
42
+ import { pendingAsyncCellsSlot } from '../shared/pendingAsyncCellsSlot.ts'
43
+ import { resolvedCellsSlot } from '../shared/resolvedCellsSlot.ts'
42
44
  import { SOCKETS_PATH } from '../shared/SOCKETS_PATH.ts'
43
45
  import { sharedCacheStoreSlot } from '../shared/sharedCacheStoreSlot.ts'
46
+ import { streamedCellsSlot } from '../shared/streamedCellsSlot.ts'
44
47
  import type { RemoteFunction } from '../shared/types/RemoteFunction.ts'
45
48
  import type { Socket } from '../shared/types/Socket.ts'
46
49
  import { createTestSocketChannel } from './createTestSocketChannel.ts'
@@ -72,7 +75,7 @@ export type TestApp = {
72
75
  /* Rpc calls over HTTP, keyed by command name: `app.rpc.getProduct({ id })`. */
73
76
  rpc: RpcClient
74
77
  /* Sockets keyed by name: `app.sockets.ticker` is the Socket — iterate it for
75
- the live stream, `.tail(n)` to seed, `.broadcast(m)` to send. */
78
+ the live stream, `.tail(n)` to seed, `.publish(m)` to send. */
76
79
  sockets: SocketClient
77
80
  /* The /__abide/health payload, decoded. */
78
81
  health: () => Promise<unknown>
@@ -101,12 +104,24 @@ export async function createTestApp(): Promise<TestApp> {
101
104
  sharedResolver: sharedCacheStoreSlot.resolver,
102
105
  pageResolver: pageSlot.resolver,
103
106
  baseResolver: baseSlot.resolver,
107
+ pendingAsyncCellsResolver: pendingAsyncCellsSlot.resolver,
108
+ resolvedCellsResolver: resolvedCellsSlot.resolver,
109
+ streamedCellsResolver: streamedCellsSlot.resolver,
104
110
  activeServer: serverSlot.active,
105
111
  }
106
112
 
107
113
  const sharedStore = createCacheStore()
108
114
  sharedCacheStoreSlot.resolver = () => sharedStore
109
115
  cacheStoreSlot.resolver = () => requestContext.getStore()?.cache ?? sharedStore
116
+ const sharedPendingAsyncCells = { promises: [] }
117
+ pendingAsyncCellsSlot.resolver = () =>
118
+ requestContext.getStore()?.pendingAsyncCells ?? sharedPendingAsyncCells
119
+ const sharedResolvedCells = { entries: [] }
120
+ resolvedCellsSlot.resolver = () =>
121
+ requestContext.getStore()?.resolvedCells ?? sharedResolvedCells
122
+ const sharedStreamedCells = { entries: [] }
123
+ streamedCellsSlot.resolver = () =>
124
+ requestContext.getStore()?.streamedCells ?? sharedStreamedCells
110
125
  pageSlot.resolver = resolvePageSnapshot
111
126
 
112
127
  /* Eager env validation, exactly as serverEntry: a top-level env(schema) in
@@ -188,6 +203,9 @@ export async function createTestApp(): Promise<TestApp> {
188
203
  sharedCacheStoreSlot.resolver = previous.sharedResolver
189
204
  pageSlot.resolver = previous.pageResolver
190
205
  baseSlot.resolver = previous.baseResolver
206
+ pendingAsyncCellsSlot.resolver = previous.pendingAsyncCellsResolver
207
+ resolvedCellsSlot.resolver = previous.resolvedCellsResolver
208
+ streamedCellsSlot.resolver = previous.streamedCellsResolver
191
209
  serverSlot.active = previous.activeServer
192
210
  }
193
211
 
@@ -61,7 +61,7 @@ write-path microbench this runs ~20× faster than a deep-proxy signal baseline.
61
61
 
62
62
  - `compileSSR` renders to an HTML string (byte-identical to the client DOM).
63
63
  - `renderToStream` streams: pending shell first, then resolved `<template await>`
64
- fragments out of order as their promises settle; `applyResolved` swaps them in.
64
+ fragments out of order as their promises settle; the inline swap script swaps them in.
65
65
  - `hydrate` adopts the server DOM in place (no re-render) for static-structure
66
66
  components — claims existing nodes, splits merged text, wires effects/listeners.
67
67
 
@@ -0,0 +1,14 @@
1
+ import { pendingAsyncCellsSlot } from '../shared/pendingAsyncCellsSlot.ts'
2
+ import type { PendingAsyncCells } from '../shared/types/PendingAsyncCells.ts'
3
+ import { cellBarrierBacking } from './runtime/cellBarrierBacking.ts'
4
+
5
+ /*
6
+ The async-cell barrier list a registration (`createAsyncCell`) or a drain (`settleAsyncCells`) should
7
+ use RIGHT NOW: a per-render isolated list if one is active (a hoisted concurrent child render — see
8
+ `isolateCellBarrier`), else the per-request list from `pendingAsyncCellsSlot`. The single seam both
9
+ barrier consumers read, so an isolated render can't leak its in-flight cells into a sibling's or the
10
+ page's drain. Undefined only when neither a render override nor a request/fallback list is installed.
11
+ */
12
+ export function activePendingCells(): PendingAsyncCells | undefined {
13
+ return cellBarrierBacking.active.current() ?? pendingAsyncCellsSlot.get()
14
+ }
@@ -0,0 +1,21 @@
1
+ /*
2
+ The control-block vocabulary — the ONE source of truth shared by the parser
3
+ (`parseTemplate`'s dispatch/error) and the LSP highlighter (`structuralHeadTokens`, driven
4
+ by the parse walk), so the keywords the highlighter colors can't drift from the blocks the
5
+ parser accepts (the `{#snippet}` head once went uncolored because a hand-copy omitted it).
6
+
7
+ `BLOCK_OPENERS` follow `{#`, `BLOCK_CONNECTORS` follow `{:`. `for await` is the async-each
8
+ opener form the highlighter colors as one phrase (the parser reads the `await` inside a
9
+ `for` head); it is not a distinct opener.
10
+ */
11
+ export const BLOCK_OPENERS = ['if', 'for', 'await', 'switch', 'try', 'snippet'] as const
12
+
13
+ export const BLOCK_CONNECTORS = [
14
+ 'else if',
15
+ 'else',
16
+ 'then',
17
+ 'catch',
18
+ 'finally',
19
+ 'case',
20
+ 'default',
21
+ ] as const
@@ -17,7 +17,10 @@ across bundles. Emitted inline (not imported) so the generated render module is
17
17
  self-contained.
18
18
  */
19
19
  export const SSR_ESCAPE =
20
- 'const $esc = (v) => String(v).replace(/[&<>"\']/g, (c) => ' +
20
+ // Nullish escapes to empty text, never the literal `"undefined"` — a pending async
21
+ // read (ADR-0032 D3) renders nothing. `$attr` guards nullish before it calls `$esc`,
22
+ // so its whole-value undefined still drops the attribute.
23
+ 'const $esc = (v) => (v == null ? "" : String(v)).replace(/[&<>"\']/g, (c) => ' +
21
24
  "({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' })[c]);\n" +
22
25
  'const $attr = (n, v) => (v === false || v === null || v === undefined) ? "" ' +
23
26
  ': v === true ? (" " + n) : (" " + n + \'="\' + $esc(v) + \'"\');\n' +