@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
@@ -0,0 +1,49 @@
1
+ import type { WireKind } from './types/WireKind.ts'
2
+
3
+ /*
4
+ One field's honest-JSON wire value → its declared runtime type, fail-open — the decode core
5
+ shared by the rpc RESPONSE revival (`reviveWireOutput`) and the INPUT revival (`parseArgs`), so
6
+ the two ends can't drift on how a wire form decodes (ADR-0029). A `Set`/`Map` already carried by
7
+ an abide ref-json body passes through; a wire array becomes the `Set`/`Map`; an ISO string becomes
8
+ a `Date`; a digit string becomes a `bigint`. An already-typed or non-matching value is returned
9
+ untouched so the codec never throws.
10
+
11
+ Callers layer their own-side extras on top: the input side additionally accepts a plain object as
12
+ `Map` entries and coerces query/form STRINGS to number/boolean (see `parseArgs`). Top-level fields
13
+ only — a structured value nested deeper is not descended into (deferred, ADR-0029).
14
+ */
15
+ export function reviveWireField(value: unknown, kind: WireKind): unknown {
16
+ if (kind === 'set') {
17
+ if (value instanceof Set) {
18
+ return value
19
+ }
20
+ return Array.isArray(value) ? new Set(value) : value
21
+ }
22
+ if (kind === 'map') {
23
+ if (value instanceof Map) {
24
+ return value
25
+ }
26
+ return Array.isArray(value) ? new Map(value as [unknown, unknown][]) : value
27
+ }
28
+ if (kind === 'date') {
29
+ if (typeof value !== 'string') {
30
+ return value
31
+ }
32
+ const date = new Date(value)
33
+ return Number.isNaN(date.getTime()) ? value : date
34
+ }
35
+ if (kind === 'bigint') {
36
+ if (typeof value !== 'string' || value.trim() === '') {
37
+ return value
38
+ }
39
+ try {
40
+ return BigInt(value)
41
+ } catch {
42
+ /* A non-integer literal throws — keep the string rather than crash the decode. */
43
+ return value
44
+ }
45
+ }
46
+ /* number/boolean ride as their JSON type on the wire; nothing to revive here (the input side
47
+ coerces them from query/form strings separately). */
48
+ return value
49
+ }
@@ -0,0 +1,36 @@
1
+ import { reviveWireField } from './reviveWireField.ts'
2
+ import type { OutputWirePlan } from './types/OutputWirePlan.ts'
3
+
4
+ /*
5
+ The type-directed wire DECODE step for a rpc success response (ADR-0029 output path) — the
6
+ response-side sibling of parseArgs' input revival. The server encoded a handler's structured
7
+ return into honest JSON (`wireJsonReplacer`): a `Set` → array, a `Map` → `[K,V]` entries, a
8
+ `bigint` → digit string, a `Date` → ISO string. A wire array is ambiguous (a real `T[]` vs an
9
+ encoded `Set<T>`), so the abide client revives from the DECLARED kind the warm server program baked
10
+ onto the stub (`OutputWirePlan`), applied to a DECODED response body here. Only the fields named in
11
+ the plan are touched; a genuine array / scalar the plan omits is left as-is.
12
+
13
+ Mutates the decoded body in place — the value is call-private (a fresh JSON.parse result or a
14
+ freshly cloned warm value), never shared. Fail-open at every branch: no plan, a non-object body, or
15
+ an unrevivable field value keeps the honest-JSON form so a type/runtime mismatch degrades visibly
16
+ rather than throwing. Top-level fields only; a structured value nested deeper is not descended into
17
+ (deferred, ADR-0029).
18
+ */
19
+ export function reviveWireOutput(value: unknown, plan: OutputWirePlan | undefined): unknown {
20
+ if (plan === undefined) {
21
+ return value
22
+ }
23
+ /* Only a plain object carries the named fields; a top-level array/scalar/null has none. */
24
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
25
+ return value
26
+ }
27
+ const record = value as Record<string, unknown>
28
+ for (const key in plan) {
29
+ const fieldPlan = plan[key]
30
+ if (fieldPlan === undefined || !(key in record)) {
31
+ continue
32
+ }
33
+ record[key] = reviveWireField(record[key], fieldPlan)
34
+ }
35
+ return value
36
+ }
@@ -0,0 +1,25 @@
1
+ import { createRpcServerProgram, type RpcServerProgram } from './createRpcServerProgram.ts'
2
+
3
+ /*
4
+ Warm per-root accessor for the rpc server program (ADR-0025 D1), mirroring the UI shadow's
5
+ `interpolationClassifierForRoot`: the first rpc transform in a root builds the `ts.Program`,
6
+ every later transform (streaming/method query) reuses it. Fails open in two layers — a
7
+ program that can't build stores `undefined` so the root is not retried and every query falls
8
+ back to the char-scan (each query method also catches per-query throws). The cache lives
9
+ in the resolver plugin's `setup` closure, so it persists across a whole build and is dropped
10
+ between plugin instances.
11
+ */
12
+ export function rpcServerForRoot(
13
+ cache: Map<string, RpcServerProgram | undefined>,
14
+ cwd: string,
15
+ rpcDir: string,
16
+ ): RpcServerProgram | undefined {
17
+ if (!cache.has(cwd)) {
18
+ try {
19
+ cache.set(cwd, createRpcServerProgram(cwd, rpcDir))
20
+ } catch {
21
+ cache.set(cwd, undefined)
22
+ }
23
+ }
24
+ return cache.get(cwd)
25
+ }
@@ -0,0 +1,25 @@
1
+ import { existsSync } from 'node:fs'
2
+ import { Glob } from 'bun'
3
+ import { fileName } from './fileName.ts'
4
+ import type { PagesScan } from './types/PagesScan.ts'
5
+
6
+ /*
7
+ Walks src/ui/pages once and classifies each `.abide` leaf by filename: a
8
+ `page.abide` is a route (its URL is the folder path), a `layout.abide` is a
9
+ layout that wraps every page at or below its folder (keyed by the same folder
10
+ URL). Any other `.abide` file (a shared component) is ignored here — free to
11
+ live anywhere and be imported relatively. Shared by the resolver plugin's
12
+ pages/layouts manifests and generateDeclarations' routes.d.ts so the two never
13
+ diverge on what counts as a route.
14
+ */
15
+ export async function scanPages(pagesDir: string): Promise<PagesScan> {
16
+ if (!existsSync(pagesDir)) {
17
+ return { pageFiles: [], layoutFiles: [] }
18
+ }
19
+ const allFiles = await Array.fromAsync(new Glob('**/*.abide').scan({ cwd: pagesDir }))
20
+ const leafIs = (name: string) => (file: string) => fileName(file) === name
21
+ return {
22
+ pageFiles: allFiles.filter(leafIs('page.abide')),
23
+ layoutFiles: allFiles.filter(leafIs('layout.abide')),
24
+ }
25
+ }
@@ -1,18 +1,30 @@
1
- // Quote only values that wouldn't round-trip bare through parseEnv empties and
2
- // anything carrying whitespace or a `#` (which would otherwise read as a comment).
1
+ // Quote any value that wouldn't round-trip bare through parseEnv: empties, anything
2
+ // carrying whitespace/`#` (a comment)/a quote/a backslash, or one that starts or ends with
3
+ // a quote char (parseEnv strips a surrounding quote pair). Everything else stays bare.
3
4
  function needsQuoting(value: string): boolean {
4
- return value === '' || /[\s#]/.test(value)
5
+ return value === '' || /[\s#"\\]/.test(value) || /^['"]|['"]$/.test(value)
6
+ }
7
+
8
+ // Escape a value for inside double quotes: backslash and quote so the closing quote can't
9
+ // be faked, and newlines so an embedded `\n` can't inject a whole new `KEY=value` line
10
+ // (the security fix — an unescaped token newline used to forge extra env entries).
11
+ function escapeQuoted(value: string): string {
12
+ return value
13
+ .replace(/\\/g, '\\\\')
14
+ .replace(/"/g, '\\"')
15
+ .replace(/\n/g, '\\n')
16
+ .replace(/\r/g, '\\r')
5
17
  }
6
18
 
7
19
  /*
8
20
  Serializes a key→value record to `.env` text — the inverse of parseEnv, used by
9
21
  the connect-screen config form to persist the user's answers to the data-dir
10
- `.env`. One `KEY=value` per line; values that need it are wrapped in double
11
- quotes so parseEnv reads them back unchanged.
22
+ `.env`. One `KEY=value` per line; values that need it are double-quoted and escaped
23
+ so parseEnv reads them back unchanged and a value can never inject extra lines.
12
24
  */
13
25
  export function serializeEnv(values: Record<string, string>): string {
14
26
  const lines = Object.entries(values).map(([key, value]) =>
15
- needsQuoting(value) ? `${key}="${value}"` : `${key}=${value}`,
27
+ needsQuoting(value) ? `${key}="${escapeQuoted(value)}"` : `${key}=${value}`,
16
28
  )
17
29
  return `${lines.join('\n')}\n`
18
30
  }
@@ -1,14 +1,15 @@
1
- import { hasReplayableRequest } from './hasReplayableRequest.ts'
1
+ import { hasSeedableRequest } from './hasSeedableRequest.ts'
2
2
  import type { CacheEntry } from './types/CacheEntry.ts'
3
3
 
4
4
  /*
5
5
  The one store-filter predicate selecting which cache entries are candidates for a
6
- warm snapshot: settled AND carrying a replayable (GET-only) wire request, so it
7
- can ship without re-firing a write or losing a body. Every selection site
8
- (serializeCacheSnapshot, the renderer's render-return and streaming-drain filters)
9
- calls this instead of re-spelling the guard, so they cannot drift.
6
+ warm snapshot: settled AND carrying a seedable wire request (any method), so an
7
+ inline call hydrates warm on the client instead of re-firing after render. Every
8
+ selection site (serializeCacheSnapshot, the renderer's render-return and
9
+ streaming-drain filters) calls this instead of re-spelling the guard, so they
10
+ cannot drift.
10
11
 
11
- Composed from `hasReplayableRequest` — the request half snapshotEntryFromCache
12
+ Composed from `hasSeedableRequest` — the request half snapshotEntryFromCache
12
13
  also gates on. snapshotEntryFromCache itself uses ONLY the request half (not
13
14
  `settled`): the streaming-drain path hands it still-pending entries and awaits
14
15
  them, so it can't require `settled` up front.
@@ -18,5 +19,5 @@ awaiting its body. The async response-level half (non-streaming + warmable kind)
18
19
  lives in snapshotEntryFromCache, reading the resolved Response.
19
20
  */
20
21
  export function snapshotShippable(entry: CacheEntry): boolean {
21
- return entry.settled === true && hasReplayableRequest(entry)
22
+ return entry.settled === true && hasSeedableRequest(entry)
22
23
  }
@@ -1,18 +1,23 @@
1
1
  const SNIPPET = Symbol.for('abide.snippet')
2
2
 
3
- /* A `{#snippet name(args)}` snippet, called like a function: it carries a payload a
4
- `{expr}` interpolation mounts in place — a DOM builder `(host) => void` on the
3
+ /* The internal payload a snippet carries a DOM builder `(host) => void` on the
5
4
  client, the pre-rendered HTML string on the server. The brand is a registered
6
5
  Symbol so it survives across module/bundle copies (same idiom as `html\`\``). */
7
- export type Snippet<Payload> = { readonly [SNIPPET]: Payload }
6
+ export type SnippetValue = { readonly [SNIPPET]: unknown }
7
+
8
+ /* The author-facing snippet type: a builder invoked with its arguments, yielding a
9
+ mountable value. `children` is `Snippet` (no args, invoked `children()`); a row
10
+ renderer is `Snippet<[Item]>` (invoked `row(item)`). The side-specific payload is
11
+ hidden behind `SnippetValue`. */
12
+ export type Snippet<Args extends unknown[] = []> = (...args: Args) => SnippetValue
8
13
 
9
14
  /* Brands a snippet payload so a `{expr}` interpolation mounts it instead of
10
15
  inserting escaped text. The compiler wraps a snippet's body in this — the client
11
16
  builder closes over the defining component's scope, the server string is its SSR
12
17
  render — so a snippet value passes through props like any other value. */
13
18
  // @documentation templating
14
- export function snippet<Payload>(payload: Payload): Snippet<Payload> {
15
- return { [SNIPPET]: payload }
19
+ export function snippet<Payload>(payload: Payload): SnippetValue {
20
+ return { [SNIPPET]: payload } as SnippetValue
16
21
  }
17
22
 
18
23
  /* The payload of a snippet-branded value, or undefined for anything else — so a
@@ -20,6 +25,6 @@ export function snippet<Payload>(payload: Payload): Snippet<Payload> {
20
25
  reads a builder function; the server reads the rendered string. */
21
26
  export function snippetPayload(value: unknown): unknown {
22
27
  return value !== null && typeof value === 'object' && SNIPPET in value
23
- ? (value as Snippet<unknown>)[SNIPPET]
28
+ ? (value as SnippetValue)[SNIPPET]
24
29
  : undefined
25
30
  }
@@ -0,0 +1,14 @@
1
+ import { createResolverSlot } from './createResolverSlot.ts'
2
+ import type { StreamedCells } from './types/StreamedCells.ts'
3
+
4
+ /*
5
+ The active streamed-cells slot — mirrors `resolvedCellsSlot`/`pendingAsyncCellsSlot`. The server
6
+ entry installs an ALS-backed resolver (a fresh list per request, so concurrent SSR renders never
7
+ mix their streamed values); with no resolver a single fallback list is created lazily so isolated
8
+ tests work without booting the runtime. `createAsyncCell` pushes a `{ key, promise }` when a
9
+ STREAMING cell constructs server-side; the page renderer drains it during the stream, awaiting each
10
+ promise and streaming an `__abideResolve({ cellKey, value })` chunk (ADR-0035).
11
+ */
12
+ export const streamedCellsSlot = createResolverSlot<StreamedCells>(() => ({
13
+ entries: [],
14
+ }))
@@ -1,19 +1,19 @@
1
1
  import { streamResponse } from './streamResponse.ts'
2
- import type { Subscribable } from './types/Subscribable.ts'
2
+ import type { NamedAsyncIterable } from './types/NamedAsyncIterable.ts'
3
3
 
4
4
  /*
5
- Builds the Subscribable returned by `fn.stream(args)`. The carried
5
+ Builds the NamedAsyncIterable returned by `fn.stream(args)`. The carried
6
6
  `name` is the cache-style key for (method, url, args) so tail()
7
7
  dedupes multiple readers of identical args into one underlying
8
8
  fetch. The fetch is deferred until the first iterator pull so
9
- constructing the Subscribable (which happens on every $derived
9
+ constructing the NamedAsyncIterable (which happens on every $derived
10
10
  re-evaluation) doesn't open a connection — tail()'s registry
11
11
  short-circuits the second instance before it iterates.
12
12
  */
13
13
  export function subscribableFromResponse<T>(
14
14
  name: string,
15
15
  fetchResponse: () => Promise<Response>,
16
- ): Subscribable<T> {
16
+ ): NamedAsyncIterable<T> {
17
17
  return {
18
18
  name,
19
19
  [Symbol.asyncIterator]() {
@@ -29,7 +29,7 @@ re-derive by scanning.
29
29
  const lifecycle = createLifecycleChannel()
30
30
 
31
31
  /*
32
- Installs the prober the shared probes call for a Subscribable (probeRegistries →
32
+ Installs the prober the shared probes call for a NamedAsyncIterable (probeRegistries →
33
33
  tailProbeSlot). Register-on-consume: an entry exists only while something is
34
34
  consuming the stream, exactly as a cache entry exists only while a call is
35
35
  retained. A name with no entry reads as "no value yet" (pending) without opening
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  Internal slot the browser tail() registry registers its prober into, so the
3
- shared probes (pending / refreshing) can answer for Subscribables without
3
+ shared probes (pending / refreshing) can answer for NamedAsyncIterables without
4
4
  shared/ importing browser/. The prober taps the registry's lifecycle channel
5
5
  (reactive inside $derived / $effect) and reports one stream by source name —
6
6
  spanning its latest-wins and window entries — or, with no name, whether any
@@ -0,0 +1,20 @@
1
+ import type { ASYNC_CELL } from '../ASYNC_CELL.ts'
2
+
3
+ /*
4
+ A read-only async cell — what `computed(await …)` / `computed(getStream())` produce.
5
+ It *tracks* an async source (a promise it unwrapped, or a NamedAsyncIterable whose
6
+ frames it follows) rather than holding a value. Unlike sync `Computed<T>` it has NO
7
+ `.value`; it is read through the probe family, always method-form — `peek()` (the
8
+ retained value or latest frame, or `undefined`), `pending()` (no value yet),
9
+ `refreshing()` (a value held while a fresher source is in flight), `error()` (the
10
+ last rejection), `refresh()` (re-invoke the source). The standalone probes route
11
+ here too: `peek(cell)` ≡ `cell.peek()`. Branded so those probes recognise it.
12
+ */
13
+ export interface AsyncComputed<T> {
14
+ readonly [ASYNC_CELL]: true
15
+ peek(): T | undefined
16
+ pending(): boolean
17
+ refreshing(): boolean
18
+ error(): unknown
19
+ refresh(): void
20
+ }
@@ -0,0 +1,13 @@
1
+ import type { AsyncComputed } from './AsyncComputed.ts'
2
+
3
+ /*
4
+ A writable async cell — what `linked(await …)` / `linked(getStream())` produce. Same
5
+ probe surface as `AsyncComputed<T>` plus `set(value)`, following the normal `linked`
6
+ write rule: a local write latches until the next *reseed* (a dependency change that
7
+ produces a new source). An arriving frame or a settling promise updates the value only
8
+ while unwritten, so a high-frequency stream never clobbers an in-progress edit; a
9
+ reseed clears the write and snaps the cell back to the live source.
10
+ */
11
+ export interface AsyncState<T> extends AsyncComputed<T> {
12
+ set(value: T): void
13
+ }
@@ -37,20 +37,20 @@ into `__SSR__`; ones still pending were consumed via `{#await}` (render emitted
37
37
  the pending branch without blocking) and stream a resolve chunk instead.
38
38
 
39
39
  `refreshing` flips true while this entry is reloading data it already held —
40
- either an `swr` refetch (stale value still visible) or the default
40
+ either a keep-stale refetch (stale value still visible) or the default
41
41
  drop-then-reload (the prior entry was invalidated and dropped, this is its
42
42
  replacement read). It backs refreshing(), distinguishing a reload from a
43
43
  first-ever load; cleared when the read settles.
44
44
 
45
- `invalidation` holds the entry's `swr` policy: the refetch thunk (the call
45
+ `invalidation` holds the entry's refetch policy: the refetch thunk (the call
46
46
  captured with its args) plus its optional throttle/debounce window and runtime
47
47
  timer state, so invalidate() keeps the stale value and revalidates this key —
48
48
  rate-limited by the window — instead of dropping the entry. Set at registration
49
- when the creating read declared `swr`, or attached by a later read declaring it
50
- on an entry that lacks it (hydrated snapshot entries always start without one)
51
- first wins. An armed `timer` is cleared if the entry is evicted, so a dead key
52
- never refetches. Wrap-time validation guarantees `swr` never coexists with
53
- ttl: 0 and never sits on a non-replayable remote method.
49
+ for a smart retained read (SWR unconditional for a replayable read ADR-0020) or
50
+ a producer declaring a throttle/debounce window, or attached by a later read on an
51
+ entry that lacks it (hydrated snapshot entries always start without one) first
52
+ wins. An armed `timer` is cleared if the entry is evicted, so a dead key never
53
+ refetches.
54
54
  */
55
55
  export type CacheEntry = {
56
56
  key: string
@@ -1,14 +1,17 @@
1
1
  /*
2
- Options for cache(). The key is always auto-derived (method+url+args for a remote
3
- function, producer-reference+args for a plain producer): hoist a producer to a
4
- stable reference to share its entry across calls. `ttl` is the
5
- milliseconds-past-resolve that the entry stays live: 0 = dedupe only (entry
6
- dropped once the promise settles the mutation idiom: in-flight coalescing and
7
- pending() visibility, nothing retained), any other number = TTL. Omitted = forever
8
- for a producer and for a remote call on the client; a remote call on the SERVER
9
- with neither `ttl` nor `swr` stated defaults to 0 (coalesce-only — the request is
10
- the atomic unit, nothing is retained past it; pair with `shared` + an explicit
11
- `ttl` to memoise across requests).
2
+ Options for the explicit `cache(producer, args, options?)` call over a plain
3
+ producer, and the value-shape of an endpoint's `cache` policy (CachePolicy adds
4
+ only the arg-derived `tags` function form on top). A remote rpc has no options
5
+ argument any more its policy lives on the definition (ADR-0020); this type is
6
+ the producer's only policy home. The key is always auto-derived
7
+ (producer-reference+args): hoist a producer to a stable reference to share its
8
+ entry across calls.
9
+
10
+ `ttl` is the milliseconds-past-resolve the entry stays live: 0 = dedupe only
11
+ (entry dropped once the promise settles — in-flight coalescing and pending()
12
+ visibility, nothing retained), any other number = TTL. Omitted = forever for a
13
+ producer.
14
+
12
15
  `tags` is an array of free-form labels grouping unrelated calls so one
13
16
  `cache.invalidate({ tags })` drops every entry sharing any of them — list
14
17
  multiple when a call belongs to multiple invalidation groups. A unique tag (e.g.
@@ -17,39 +20,22 @@ a uuid) shared by a set of calls gives them their own private invalidation group
17
20
  `shared` opts the entry into the process-level store instead of the default
18
21
  request-scoped one (server) — a store that outlives every request. It selects
19
22
  the store only; it does NOT retain (pair it with `ttl` to memoise across
20
- requests). The shared store is keyed by method+url+args, never by user, so do
21
- not put per-user data in it — it would be served to other users. Omit `shared`
22
- for per-request data. Write only `shared: true`; there is no `false` form. On
23
- the client there is a single tab store, so the flag is a no-op there.
24
-
25
- `swr` is stale-while-revalidate: it changes what a `cache.invalidate` hit does
26
- to this key. Without it, an invalidate drops the entry and the next read shows
27
- `pending()`. With it, the entry is kept and refetched in the background — the
28
- existing (stale) value stays visible and `refreshing()` reports the in-flight
29
- reload — so the reader never blanks. It governs only the refetch-after-invalidate;
30
- the first fetch and arg-change fetches stay immediate regardless.
31
-
32
- `swr: true` refetches immediately on every invalidate. An optional window
33
- coalesces a burst (e.g. a socket spraying `cache.invalidate`) into far fewer
34
- calls: `swr: { throttle: N }` refetches on the leading edge then at most once
35
- per N ms while invalidations keep arriving; `swr: { debounce: N }` refetches
36
- only after N ms of quiet. `swr` declares the call safe to re-run unprompted:
37
- cache() throws at wrap time on throttle+debounce set at once, on ttl: 0 (nothing
38
- retained, nothing to revalidate), and on a non-replayable remote method
39
- (replaying a write is a state change disguised as a refresh). Producers are
40
- uncheckable — set `swr` only on a producer that is a pure read.
23
+ requests). The shared store is keyed by producer-reference+args, never by user,
24
+ so do not put per-user data in it — it would be served to other users. Omit
25
+ `shared` for per-request data. Write only `shared: true`; there is no `false`
26
+ form. On the client there is a single tab store, so the flag is a no-op there.
41
27
 
42
- `throttle`/`debounce` are root rate-limit windows for the refetch clock. For the
43
- smart bare call where SWR is unconditional for replayable reads, so there is no
44
- `swr` toggle to hang a window off they cap how often a background revalidation
45
- fires (leading-edge-then-coalesce, or fire-after-quiet respectively). They pair
46
- with the same wrap-time guard as the `swr` window: set one, not both.
28
+ `throttle`/`debounce` arm the keep-stale-and-revalidate refetch clock for a
29
+ producer: with one set, a `cache.invalidate` hit keeps the stale value visible
30
+ and coalesces a background refetch (leading-edge-then-coalesce, or
31
+ fire-after-quiet respectively) instead of dropping the entry. Set one, not both.
32
+ SWR is otherwise unconditional for replayable remote reads; there is no `swr`
33
+ toggle.
47
34
  */
48
35
  export type CacheOptions = {
49
36
  ttl?: number
50
37
  tags?: string[]
51
38
  shared?: boolean
52
- swr?: boolean | { throttle?: number; debounce?: number }
53
39
  throttle?: number
54
40
  debounce?: number
55
41
  }
@@ -0,0 +1,25 @@
1
+ /*
2
+ Endpoint cache policy, declared once on the rpc definition (ADR-0020). Same value
3
+ shape as the producer-side `CacheOptions` (`ttl` / `tags` / `throttle` / `debounce`
4
+ / `shared`, no `swr` — SWR is unconditional for replayable reads), except `tags`
5
+ also accepts an arg-derived function so the group can vary by call. That makes the
6
+ type generic over the rpc's `Args`: `(args) => ['rates:' + args.base]` is typed
7
+ against the endpoint's own argument shape.
8
+
9
+ - `ttl` — retention/staleness in ms. Default Infinity: the entry is retained for its
10
+ store's lifetime — the request on the server (a non-shared read dies with the request,
11
+ regardless of ttl), the tab on the client (until invalidate/refresh). An explicit ttl
12
+ is a hard expiry on the server, a staleness deadline (SWR) on the client.
13
+ - `tags` — static invalidation-group labels, or a function of the call's args.
14
+ - `throttle` / `debounce` — the background refetch clock (set one, not both).
15
+ - `shared` — opt the entry into the process-level store (never per-user data — the store
16
+ is keyed by method+url+args, not by user). With the default Infinity ttl this memoises
17
+ across requests; an explicit ttl bounds it. A client no-op (one tab store).
18
+ */
19
+ export type CachePolicy<Args> = {
20
+ ttl?: number
21
+ tags?: string[] | ((args: Args) => string[])
22
+ throttle?: number
23
+ debounce?: number
24
+ shared?: boolean
25
+ }
@@ -1,15 +1,16 @@
1
- import type { ReplayableMethod } from './ReplayableMethod.ts'
1
+ import type { HttpMethod } from './HttpMethod.ts'
2
2
 
3
3
  /*
4
4
  Wire format for a single cached response shipped from SSR to client hydration.
5
- Only replayable entries (see REPLAYABLE_METHODS) with a textual Content-Type
6
- are emitted writes must not re-fire from a snapshot, and binary bodies don't
7
- survive a JSON round-trip.
5
+ Any entry called inline during render (any method) with a textual Content-Type is
6
+ emitted, so the client hydrates warm instead of re-firing the call after render;
7
+ binary bodies don't survive a JSON round-trip. The seeded value is read warm once
8
+ — it is never auto-replayed (that stays GET-only, see REPLAYABLE_METHODS).
8
9
  */
9
10
  export type CacheSnapshotEntry = {
10
11
  key: string
11
12
  url: string
12
- method: ReplayableMethod
13
+ method: HttpMethod
13
14
  status: number
14
15
  statusText: string
15
16
  headers: Array<[string, string]>
@@ -0,0 +1,8 @@
1
+ /*
2
+ Status-keyed map of a handler's typed-error data schemas (ADR-0030), baked at build time from the
3
+ handler's `error.typed(name, status, schema?)` return branches. Each key is an HTTP status, each
4
+ value the error's `data` type projected to JSON Schema (multiple errors sharing a status combine
5
+ under `anyOf`; a nullary error with no data contributes a bare `{}`). Feeds the OpenAPI
6
+ `responses[status]` surface — the error-branch sibling of the success `outputJsonSchema`.
7
+ */
8
+ export type ErrorJsonSchemas = Record<number, Record<string, unknown>>
@@ -1 +1,3 @@
1
- export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD'
1
+ import type { HTTP_METHODS } from '../HTTP_METHODS.ts'
2
+
3
+ export type HttpMethod = (typeof HTTP_METHODS)[number]
@@ -0,0 +1,17 @@
1
+ import type { WireKind } from './WireKind.ts'
2
+
3
+ /*
4
+ The build-time wire codec plan for a rpc's input args, keyed by field name (ADR-0028 for the
5
+ scalar `number`/`boolean` kinds, generalized by ADR-0029 to the structured `date`/`bigint`/`set`/
6
+ `map` kinds). A GET/form request delivers every field as a string, and a non-abide JSON client
7
+ sends a `Date` as an ISO string / a `Set` as an array, so parseArgs uses this plan to revive each
8
+ declared field into the typed value the input schema expects — before validation, on the exact
9
+ fields the author declared. String and other-typed fields are never listed, so a value that merely
10
+ looks numeric (an id, a zip code, `'1.0'`) stays a string. The plan is derived by the warm server
11
+ program from the real type graph and stamped into the server `defineRpc` call; a field is absent
12
+ when its type is anything but a recognized WireKind (or an array / optional thereof), so the codec
13
+ is precise and fail-open — no plan ⇒ today's string-through behavior. The abide client's own
14
+ POST/PUT/PATCH body already round-trips these kinds via ref-json; the plan covers the query-string
15
+ and non-abide-client paths where ref-json isn't in play.
16
+ */
17
+ export type InputCoercion = Record<string, WireKind>
@@ -20,7 +20,7 @@ HTTP/SSE read face); one-shot rpc streams omit it. Implementers must signal
20
20
  `hooks.replayed` in-band once the seed portion is delivered (even when
21
21
  empty) so a seeded reader can commit its window atomically; see TailHooks.
22
22
  */
23
- export interface Subscribable<T> extends AsyncIterable<T> {
23
+ export interface NamedAsyncIterable<T> extends AsyncIterable<T> {
24
24
  readonly name: string
25
25
  tail?(count: number, hooks?: TailHooks): AsyncIterable<T>
26
26
  }
@@ -0,0 +1,17 @@
1
+ import type { WireKind } from './WireKind.ts'
2
+
3
+ /*
4
+ The build-time wire codec plan for a rpc's success RESPONSE body, keyed by top-level field name
5
+ (ADR-0029 output path — the response-side sibling of `InputCoercion`). The server serializes a
6
+ handler's structured return through a value-directed replacer (`wireJsonReplacer`), so a `Set`
7
+ crosses as a JSON array, a `Map` as an `[K,V]` entries array, and a `bigint` as a digit string —
8
+ honest JSON a non-abide client still reads. But a wire array is ambiguous (a real `T[]` vs an
9
+ encoded `Set<T>`), so an abide client needs the DECLARED kind to revive: this plan carries only the
10
+ structured kinds (`date`/`bigint`/`set`/`map`) the client proxy revives from a decoded response body.
11
+ It is resolved by the warm server program from the handler's success-body type and baked onto the
12
+ CLIENT `remoteProxy` stub (next to the live `schemas`, ADR-0022 D2). A field absent from the plan (a
13
+ genuine array, a scalar, a `number`/`boolean` JSON already carries) is left untouched. Fail-open —
14
+ no plan / an unrevivable value keeps the honest-JSON form, never a throw. Top-level fields only in
15
+ this increment; a structured value nested inside another value is not descended into.
16
+ */
17
+ export type OutputWirePlan = Record<string, WireKind>
@@ -0,0 +1,7 @@
1
+ /* The classification scanPages returns for src/ui/pages: route leaves
2
+ (page.abide) and layout leaves (layout.abide), each an array of paths
3
+ relative to the pages dir. */
4
+ export type PagesScan = {
5
+ pageFiles: string[]
6
+ layoutFiles: string[]
7
+ }
@@ -0,0 +1,10 @@
1
+ /*
2
+ The per-request list of in-flight async-cell promises registered during an SSR
3
+ render. `createAsyncCell` pushes each settling promise here (server-side only);
4
+ `settleAsyncCells` drains and awaits them between a component's cell declarations
5
+ and its template, so the resolved values bake into the first-pass HTML (ADR-0019
6
+ Tier-2) instead of the template peeking `undefined`.
7
+ */
8
+ export type PendingAsyncCells = {
9
+ promises: Promise<unknown>[]
10
+ }
@@ -1,5 +1,7 @@
1
+ import type { CachePolicy } from './CachePolicy.ts'
1
2
  import type { HttpMethod } from './HttpMethod.ts'
2
3
  import type { RemoteCallable } from './RemoteCallable.ts'
4
+ import type { StreamPolicy } from './StreamPolicy.ts'
3
5
 
4
6
  /*
5
7
  Bare-response remote function — same call shape as RemoteFunction but
@@ -11,4 +13,8 @@ implement custom error handling can opt out of the decode.
11
13
  export type RawRemoteFunction<Args> = RemoteCallable<Args, Response> & {
12
14
  readonly method: HttpMethod
13
15
  readonly url: string
16
+ /* Endpoint cache/stream policy (ADR-0020), stamped on this variant too so readThrough can
17
+ read it whether the caller passed `fn` or `fn.raw`. Same value as the decoded sibling's. */
18
+ readonly cache?: CachePolicy<Args>
19
+ readonly stream?: StreamPolicy
14
20
  }