@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,166 @@
1
+ import ts from 'typescript'
2
+ import { AbideCompileError } from './AbideCompileError.ts'
3
+ import type { InjectedCell } from './lowerAsyncInterpolations.ts'
4
+ import type { InterpolationClassifier } from './types/InterpolationClassifier.ts'
5
+ import type { InterpolationKind } from './types/InterpolationKind.ts'
6
+
7
+ /* The value/content position a lifted (sub)expression sits in — only `each` (a plain `{#for}`
8
+ source) forbids an `AsyncIterable` (D4a). Every other position renders a stream's latest frame. */
9
+ export type LiftPosition = 'content' | 'attribute' | 'if' | 'switch' | 'each'
10
+
11
+ /* A lifted (sub)expression's span within the interpolation `code` and how it lifts: `start`/`end`
12
+ index `code`, `kind` picks the peek flavour, `blocking` marks a leading `await`. The runtime uses
13
+ these to rewrite the span to a cell ref; the type-check shadow uses the non-blocking ones to wrap
14
+ the seed in a peek helper so its resolved type composes (ADR-0032). */
15
+ export type LiftSpan = {
16
+ start: number
17
+ end: number
18
+ kind: 'promise' | 'asyncIterable'
19
+ blocking: boolean
20
+ }
21
+
22
+ /* The binary operators whose result reacts to a pending-`undefined` operand: the walk does NOT
23
+ lift the composition, it descends into the operands so the operator composes the peek(s)
24
+ (ADR-0032 D1). `?.` needs no entry — a member/call on a promise is sync-typed, so the default
25
+ sync-recurse already reaches and lifts the async base. */
26
+ const PENDING_TOLERANT = new Set<ts.SyntaxKind>([
27
+ ts.SyntaxKind.QuestionQuestionToken,
28
+ ts.SyntaxKind.BarBarToken,
29
+ ts.SyntaxKind.AmpersandAmpersandToken,
30
+ ])
31
+
32
+ /*
33
+ ADR-0032 — the sub-expression walk. Given an interpolation's verbatim `code`, its absolute source
34
+ offset `loc`, and the warm classifier, lifts every promise/`AsyncIterable`-typed (sub)expression
35
+ into an injected peek-cell and rewrites it in place to a bare `__vN` reference, returning the
36
+ rewritten code and the cells to inject. Pending is `undefined` at the value level, so `??` / `?.` /
37
+ member access compose around the peek (`getFoo() ?? 'Loading…'` shows the fallback while pending).
38
+
39
+ Three rules, top-down (ADR D1 "The walk"):
40
+ 1. `await X` → lift `X` BLOCKING (SSR-inline); syntactic, needs no classifier.
41
+ 2. `a ?? b` / `||` / `&&`→ NOT lifted; recurse into the operands (pending stays visible to the op).
42
+ 3. any other promise/`AsyncIterable` node → lift as a unit, STREAMING; a sync node recurses
43
+ (a sync parent can wrap an async child: `String(getFoo())`, `` `${getFoo()}` ``).
44
+
45
+ `classify` absent (no warm program) ⇒ only rule 1 fires — fail-open: `await` still works, a bare
46
+ async position degrades to today's stringify. A promise seed is emitted `await X` so the cell
47
+ UNWRAPS to the resolved value; an `AsyncIterable` seed stays bare (its latest frame). D4: an
48
+ `AsyncIterable` in a plain `{#for}` source, or an `await` on an `AsyncIterable`, throw.
49
+ */
50
+ export function liftAsyncSubExpressions(
51
+ code: string,
52
+ loc: number,
53
+ classify: InterpolationClassifier | undefined,
54
+ mint: () => string,
55
+ position: LiftPosition = 'content',
56
+ ): { code: string; lifts: InjectedCell[]; spans: LiftSpan[] } {
57
+ const source = ts.createSourceFile('__lift.ts', code, ts.ScriptTarget.Latest, true)
58
+ const first = source.statements[0]
59
+ /* Only a single bare expression is walkable; anything else (a statement, empty) passes through. */
60
+ if (first === undefined || source.statements.length !== 1 || !ts.isExpressionStatement(first)) {
61
+ return { code, lifts: [], spans: [] }
62
+ }
63
+ const lifts: InjectedCell[] = []
64
+ /* Each lifted span, in walk (source) order; spliced right-to-left so earlier offsets stay valid.
65
+ `start`/`end` index `code`; `name` replaces that span. */
66
+ const edits: { start: number; end: number; name: string }[] = []
67
+ /* The same lifted spans with their kind/tier — the shadow's peek-wrap consumes these directly. */
68
+ const spans: LiftSpan[] = []
69
+
70
+ const text = (node: ts.Node): string => code.slice(node.getStart(source), node.getEnd())
71
+
72
+ /* The checker kind of a sub-node, keyed by its absolute source offset through the linear
73
+ interpolation→shadow mapping. `undefined` when no classifier (only `await` lifts then). */
74
+ const kindOf = (node: ts.Node): InterpolationKind | undefined => {
75
+ if (classify === undefined) {
76
+ return undefined
77
+ }
78
+ const start = node.getStart(source)
79
+ return classify(loc + start, code.slice(start, node.getEnd()))
80
+ }
81
+
82
+ /* Record a lift of `node`'s span. `seed` is the RAW (sub)expression text — `analyzeComponent`
83
+ wraps a `promise` seed as `computed(async () => await (<seed>))` (so `await` is a keyword in
84
+ an async context — a bare-text `await (X)` reparsed at module scope becomes a call to a
85
+ function `await`) and an `asyncIterable` seed as `computed(<seed>)` (a bare stream thunk). */
86
+ const lift = (
87
+ node: ts.Node,
88
+ seed: string,
89
+ kind: 'promise' | 'asyncIterable',
90
+ blocking: boolean,
91
+ ): void => {
92
+ const name = mint()
93
+ const start = node.getStart(source)
94
+ const end = node.getEnd()
95
+ lifts.push({ name, code: seed, kind, blocking })
96
+ edits.push({ start, end, name })
97
+ spans.push({ start, end, kind, blocking })
98
+ }
99
+
100
+ const visit = (node: ts.Node): void => {
101
+ /* A nested function is its own evaluation scope — an async (sub)expression inside a callback
102
+ (`items.map(x => fetchName(x))`, `items.map(async x => await load(x))`) must NOT be hoisted
103
+ to a top-level cell: its parameters/closure vars would become free identifiers, and it
104
+ would run once instead of per row. Stop at the boundary, mirroring
105
+ `desugarSignals.hasTopLevelAwait`. */
106
+ if (
107
+ ts.isArrowFunction(node) ||
108
+ ts.isFunctionExpression(node) ||
109
+ ts.isFunctionDeclaration(node)
110
+ ) {
111
+ return
112
+ }
113
+ if (ts.isAwaitExpression(node)) {
114
+ /* `await X` → a BLOCKING promise cell (its operand). D4b: `await` on a stream is
115
+ meaningless. The operand text is already a bounded unary — no parens needed. */
116
+ if (kindOf(node.expression) === 'asyncIterable') {
117
+ throw new AbideCompileError(
118
+ '[abide] `await` unwraps a promise, but this is an `AsyncIterable` — drop the `await`; a stream auto-tracks (render its frames, or `{#for await}`).',
119
+ loc + node.getStart(source),
120
+ )
121
+ }
122
+ lift(node, text(node.expression), 'promise', true)
123
+ return
124
+ }
125
+ if (ts.isBinaryExpression(node) && PENDING_TOLERANT.has(node.operatorToken.kind)) {
126
+ /* Pending-tolerant: don't lift the composition — descend so the operator sees the
127
+ operands' `undefined` (`getFoo() ?? 'Loading…'`, `x || getFoo()`). */
128
+ visit(node.left)
129
+ visit(node.right)
130
+ return
131
+ }
132
+ const kind = kindOf(node)
133
+ if (kind === 'promise') {
134
+ /* Lift as a unit, streaming; the cell unwraps the seed to its resolved value. */
135
+ lift(node, text(node), 'promise', false)
136
+ return
137
+ }
138
+ if (kind === 'asyncIterable') {
139
+ if (position === 'each') {
140
+ throw new AbideCompileError(
141
+ "[abide] an `AsyncIterable` can't drive a plain `{#for}` — iterate its frames with `{#for await}`.",
142
+ loc + node.getStart(source),
143
+ )
144
+ }
145
+ /* A stream cell: its latest frame. */
146
+ lift(node, text(node), 'asyncIterable', false)
147
+ return
148
+ }
149
+ /* Sync (or classifier-absent): a sync parent can still wrap an async child. */
150
+ ts.forEachChild(node, visit)
151
+ }
152
+
153
+ visit(first.expression)
154
+
155
+ if (edits.length === 0) {
156
+ return { code, lifts: [], spans: [] }
157
+ }
158
+ edits.sort((a, b) => b.start - a.start)
159
+ let rewritten = code
160
+ for (const edit of edits) {
161
+ rewritten = rewritten.slice(0, edit.start) + edit.name + rewritten.slice(edit.end)
162
+ }
163
+ /* Spans in ascending source order — the shadow's peek-wrap walks them left-to-right. */
164
+ spans.sort((a, b) => a.start - b.start)
165
+ return { code: rewritten, lifts, spans }
166
+ }
@@ -0,0 +1,92 @@
1
+ import { asyncInterpolationFields } from './asyncInterpolationFields.ts'
2
+ import { liftAsyncSubExpressions } from './liftAsyncSubExpressions.ts'
3
+ import type { InterpolationClassifier } from './types/InterpolationClassifier.ts'
4
+ import type { TemplateNode } from './types/TemplateNode.ts'
5
+
6
+ /* A synthetic peek-cell an async (sub)expression lowers to: a unique local name, the RAW seed
7
+ expression, its `kind` (a `promise` cell UNWRAPS to the resolved value; an `asyncIterable` cell
8
+ tracks its latest frame), and whether the author wrote `await` (BLOCKING: joins the SSR barrier,
9
+ resolved inline; else STREAMING: ships pending-`undefined`, resolves on the client). By kind,
10
+ `analyzeComponent` prepends `const <name> = computed(async () => await (<code>))` (promise) or
11
+ `const <name> = computed(<code>)` (stream); `desugarSignals` routes it (by injected name) to a
12
+ `trackedComputed` cell read via `$$readCell(<name>)`. */
13
+ export type InjectedCell = {
14
+ name: string
15
+ code: string
16
+ kind: 'promise' | 'asyncIterable'
17
+ blocking: boolean
18
+ }
19
+
20
+ /* The result of the async-interpolation lowering: the (mutated-in-place) template plus the cells
21
+ the lifted (sub)expressions were rewritten into (empty when none). */
22
+ export type LoweredInterpolations = { nodes: TemplateNode[]; cells: InjectedCell[] }
23
+
24
+ /*
25
+ ADR-0032 type-directed lowering of async (sub)expressions, in EVERY position — content
26
+ interpolations and value positions (attribute, `{#if}`/`{#switch}` subject, plain `{#for}` source)
27
+ alike. Each interpolation is walked (`liftAsyncSubExpressions`): a promise/`AsyncIterable`-typed
28
+ (sub)expression is lifted to an injected peek-cell and rewritten IN PLACE to a bare `__vN`
29
+ reference, so pending reads as `undefined` and composes with `??`/`?.`. A leading `await` marks the
30
+ SSR BLOCKING tier; no `await` is STREAMING. Runs even without a classifier — then only syntactic
31
+ `await`s lift (fail-open), matching the pre-classifier path for everything else.
32
+ */
33
+ export function lowerAsyncInterpolations(
34
+ nodes: TemplateNode[],
35
+ classify: InterpolationClassifier | undefined,
36
+ ): LoweredInterpolations {
37
+ const counter = { n: 0 }
38
+ const mint = (): string => `__v${counter.n++}`
39
+ const cells: InjectedCell[] = []
40
+ lowerList(nodes, classify, mint, cells)
41
+ return { nodes, cells }
42
+ }
43
+
44
+ /* Lifts every async (sub)expression in a sibling list, then recurses into children. Each node's
45
+ async-liftable fields come from the shared `asyncInterpolationFields` plan (the single reading the
46
+ shadow front-end also drives from), so the two can't disagree on which interpolations are async;
47
+ this side owns the RENDERING — lift the field to a peek-cell, write the `__vN` reference back, and
48
+ for a control-flow subject that collapsed to one bare cell mark `asyncSubject`. */
49
+ function lowerList(
50
+ nodes: TemplateNode[],
51
+ classify: InterpolationClassifier | undefined,
52
+ mint: () => string,
53
+ cells: InjectedCell[],
54
+ ): void {
55
+ for (const node of nodes) {
56
+ for (const field of asyncInterpolationFields(node)) {
57
+ const lifted = cells.length
58
+ const result = liftAsyncSubExpressions(
59
+ field.code,
60
+ field.loc,
61
+ classify,
62
+ mint,
63
+ field.position,
64
+ )
65
+ for (const lift of result.lifts) {
66
+ cells.push(lift)
67
+ }
68
+ field.write(result.code)
69
+ if (field.subject) {
70
+ field.setAsyncSubject(isBareLiftedCell(result.code, cells, lifted))
71
+ }
72
+ }
73
+ if ('children' in node) {
74
+ lowerList(node.children, classify, mint, cells)
75
+ }
76
+ }
77
+ }
78
+
79
+ /* True when a control-flow subject's WHOLE expression was lifted to one peek-cell — the
80
+ rewritten code is exactly a single injected name (`{#if getX()}` → `__v3`), not a compound
81
+ the peek composes into (`{#if getX()?.ok}` → `__v3?.ok`, still the falsy-else peek). `cells`
82
+ past index `lifted` are the ones this subject minted, so an equal name means the subject IS
83
+ that bare cell. Only then do the back-ends read its pending facet to hold the block. */
84
+ function isBareLiftedCell(code: string, cells: InjectedCell[], lifted: number): boolean {
85
+ const name = code.trim()
86
+ for (let index = lifted; index < cells.length; index++) {
87
+ if (cells[index]?.name === name) {
88
+ return true
89
+ }
90
+ }
91
+ return false
92
+ }
@@ -25,6 +25,8 @@ export function lowerContext(
25
25
  stateNames: ReadonlySet<string>,
26
26
  derivedNames: ReadonlySet<string>,
27
27
  computedNames: ReadonlySet<string> = new Set(),
28
+ /* `linked` / async `computed` names, read through `$$readCell(name)`. */
29
+ cellReadNames: ReadonlySet<string> = new Set(),
28
30
  ) {
29
31
  /* The typed branch-local shadow stack: one auto-popping value owning both kinds.
30
32
  `derived` names deref to `.value` like a `computed` (block value params the client
@@ -48,6 +50,7 @@ export function lowerContext(
48
50
  computedNames,
49
51
  new Set(scope.names('derived')),
50
52
  new Set(scope.names('plain')),
53
+ cellReadNames,
51
54
  ),
52
55
  docAccessTransformer('$$model'),
53
56
  ])
@@ -101,6 +104,13 @@ export function lowerContext(
101
104
  return `(${expression(code)}).set(${valueExpr});`
102
105
  }
103
106
  guardWritableBind(code)
107
+ /* A `linked` cell (a writable `State`) is read through `$$readCell`, but that call is
108
+ not an lvalue — the WRITE goes straight to the cell's `.value` setter (`NAME.value =
109
+ …`). The name is a real local holding the cell, so it needs no reference lowering. */
110
+ const name = code.trim()
111
+ if (cellReadNames.has(name)) {
112
+ return `${name}.value = ${valueExpr};`
113
+ }
104
114
  return statement(`${code} = ${valueExpr}`)
105
115
  }
106
116
 
@@ -1,6 +1,5 @@
1
1
  import ts from 'typescript'
2
2
  import { escapeKey } from '../runtime/escapeKey.ts'
3
- import { TS_PRINTER } from './TS_PRINTER.ts'
4
3
 
5
4
  /*
6
5
  The linchpin compiler pass. Rewrites idiomatic data access on a reactive document
@@ -23,15 +22,10 @@ many — literal keys at compile time, dynamic ones wrapped in a runtime
23
22
  `escapeKey(...)`. Reads are lowered to `read(path)`; a later pass hoists
24
23
  static-path reads to a `cell` bound once at component init (the string-free hot
25
24
  path the bench measured). Index expressions are themselves visited, so a read
26
- used as an index lowers too.
25
+ used as an index lowers too. Exposed as `docAccessTransformer` (a
26
+ `ts.TransformerFactory`) so the script pipeline chains it after
27
+ `signalRefsTransformer` over a single parsed tree (see `lowerScript`).
27
28
  */
28
- export function lowerDocAccess(code: string, docName: string): string {
29
- const source = ts.createSourceFile('component.ts', code, ts.ScriptTarget.Latest, true)
30
- const result = ts.transform(source, [docAccessTransformer(docName)])
31
- const output = TS_PRINTER.printFile(result.transformed[0] as ts.SourceFile)
32
- result.dispose()
33
- return output
34
- }
35
29
 
36
30
  /* A path segment is either a literal key or a runtime expression (a dynamic index). */
37
31
  type Segment = { kind: 'literal'; value: string } | { kind: 'expression'; node: ts.Expression }
@@ -39,6 +33,30 @@ type Segment = { kind: 'literal'; value: string } | { kind: 'expression'; node:
39
33
  /* Maps a compound-assignment operator to its plain binary counterpart. Logical
40
34
  assignments (`||=`/`&&=`/`??=`) lower to an unconditional replace of the
41
35
  combined value — consistent with how `+=` lowers (the patch always writes). */
36
+ /* Methods that mutate a doc-rooted container in place — Array, Map and Set (the three
37
+ mutable containers the doc codec serializes). Called on a doc value these can't lower to
38
+ a bare `readCall` (which would mutate the live tree by reference and never emit a patch —
39
+ so nothing re-renders); they route through `$$mutateDocContainer`, which
40
+ clones-applies-replaces so a real patch fires. The names are disjoint across the three
41
+ container kinds, so one set routes them all and the runtime helper decides the kind by the
42
+ value's type. `push` is handled separately above (fine-grained `add` patches). */
43
+ const MUTATING_CONTAINER_METHODS = new Set([
44
+ // Array
45
+ 'pop',
46
+ 'shift',
47
+ 'unshift',
48
+ 'splice',
49
+ 'sort',
50
+ 'reverse',
51
+ 'fill',
52
+ 'copyWithin',
53
+ // Map / Set (`delete` + `clear` shared; `set` is Map, `add` is Set)
54
+ 'set',
55
+ 'add',
56
+ 'delete',
57
+ 'clear',
58
+ ])
59
+
42
60
  const COMPOUND_OPERATORS = new Map<ts.SyntaxKind, ts.BinaryOperator>([
43
61
  [ts.SyntaxKind.PlusEqualsToken, ts.SyntaxKind.PlusToken],
44
62
  [ts.SyntaxKind.MinusEqualsToken, ts.SyntaxKind.MinusToken],
@@ -151,6 +169,7 @@ export function docAccessTransformer(docName: string): ts.TransformerFactory<ts.
151
169
  )
152
170
  /* `node.arguments.length > 0` above guarantees `adds` is non-empty, so the
153
171
  single-element branch's `adds[0]` is defined. */
172
+ // biome-ignore lint/style/noNonNullAssertion: length === 1 guarantees adds[0] is defined
154
173
  return adds.length === 1 ? adds[0]! : ts.factory.createCommaListExpression(adds)
155
174
  }
156
175
  }
@@ -162,10 +181,32 @@ export function docAccessTransformer(docName: string): ts.TransformerFactory<ts.
162
181
  const access = node.expression
163
182
  const segments = pathSegments(access.expression)
164
183
  if (segments) {
165
- const read = docCall(docName, 'read', [buildPath(segments)])
166
184
  const args = node.arguments.map(
167
185
  (arg) => ts.visitNode(arg, visit) as ts.Expression,
168
186
  )
187
+ /* An in-place-mutating container method (Array/Map/Set) on a doc path →
188
+ route through `$$mutateDocContainer(doc, path, member, [args])` so the
189
+ mutation lands as a patch instead of silently mutating the live tree by
190
+ reference. Optional chaining (`model.items?.splice(…)`) keeps the bare-call
191
+ semantics below — skip-if-absent is the author's explicit choice, and a
192
+ nullish container has nothing to mutate. */
193
+ if (
194
+ MUTATING_CONTAINER_METHODS.has(access.name.text) &&
195
+ !access.questionDotToken &&
196
+ !node.questionDotToken
197
+ ) {
198
+ return ts.factory.createCallExpression(
199
+ ts.factory.createIdentifier('$$mutateDocContainer'),
200
+ undefined,
201
+ [
202
+ ts.factory.createIdentifier(docName),
203
+ buildPath(segments),
204
+ ts.factory.createStringLiteral(access.name.text),
205
+ ts.factory.createArrayLiteralExpression(args),
206
+ ],
207
+ )
208
+ }
209
+ const read = docCall(docName, 'read', [buildPath(segments)])
169
210
  /* Optional chaining is the author's explicit skip-if-absent: a nullish
170
211
  read short-circuits the whole call to `undefined`. Keep it bare —
171
212
  routing it through the throwing guard would invert that semantics.
@@ -7,12 +7,13 @@ import { docAccessTransformer } from './lowerDocAccess.ts'
7
7
  import { signalRefsTransformer } from './renameSignalRefs.ts'
8
8
  import { stripEffectsTransformer } from './stripEffects.ts'
9
9
  import { TS_PRINTER } from './TS_PRINTER.ts'
10
+ import type { SeedTypeClassifier } from './types/SeedTypeClassifier.ts'
10
11
 
11
12
  /* The `abide/ui/*` modules the reactive surface is imported from. An author's import of
12
13
  one is compiler-recognised and lowered, so its binding is often fully consumed — a plain
13
14
  `state(...)` becomes `$$model`/`$$scope` with no `state` reference left. Such a dead
14
- import is dropped from the emitted module (see `deadReactiveImport`) so the output has no
15
- spurious static `@abide/ui` dependency — load-bearing for hot modules, which forbid one. */
15
+ import is dropped from the emitted module (see `deadReactiveImport`) so the output carries
16
+ no spurious static `@abide/ui` dependency — a dead import a bundler would still resolve. */
16
17
  const REACTIVE_IMPORT_SPECIFIERS = new Set([
17
18
  `${ABIDE_PACKAGE_NAME}/ui/state`,
18
19
  `${ABIDE_PACKAGE_NAME}/ui/effect`,
@@ -61,6 +62,25 @@ export function lowerScript(
61
62
  desugared away. Folded into the dead-import usage check so a live import isn't dropped
62
63
  (→ `ReferenceError: state is not defined` in the branch). Empty for a nested script. */
63
64
  externalUsage = '',
65
+ /* Names of synthetic `const __cN = computed(...)` cells `analyzeComponent` prepended for
66
+ asyncIterable interpolations (ADR-0019 Stage D). `computed` here is an unimported callee,
67
+ so `desugarSignals` recognizes these declarations by name — routing each to an eager
68
+ `trackedComputed` stream cell — rather than by import resolution. Empty on every other path. */
69
+ injectedCellNames: ReadonlySet<string> = new Set(),
70
+ /* The subset of `injectedCellNames` whose author wrote `await` (ADR-0032): a BLOCKING peek-cell
71
+ that joins the SSR barrier (resolved inline), vs a streaming one that ships pending. Threaded
72
+ to `desugarSignals` so `injectedComputedStatements` passes the right `trackedComputed`
73
+ streaming flag. Empty on every other path. */
74
+ blockingCellNames: ReadonlySet<string> = new Set(),
75
+ /* Names the template writes/forwards (`writtenTemplateNames`), so a `props()` binding used
76
+ two-way is desugared to a writable cell rather than a read-only derive. Empty for a nested
77
+ script (which declares no props). */
78
+ templateWrittenNames: ReadonlySet<string> = new Set(),
79
+ /* Type-directed seed classifier + this script's absolute source base (ADR-0023), threaded
80
+ to `desugarSignals` so a no-marker `computed(seed)` routes on the seed's checker type.
81
+ Absent ⇒ fail-open to the `isBareCallComputed` syntax heuristic (today's behavior). */
82
+ seedClassify?: SeedTypeClassifier,
83
+ scriptBase = 0,
64
84
  ): {
65
85
  body: string
66
86
  imports: string
@@ -68,13 +88,28 @@ export function lowerScript(
68
88
  stateNames: Set<string>
69
89
  derivedNames: Set<string>
70
90
  computedNames: Set<string>
91
+ cellReadNames: Set<string>
71
92
  droppedReactiveImports: Set<string>
72
93
  } {
73
94
  const source = ts.createSourceFile('component.ts', scriptBody, ts.ScriptTarget.Latest, true)
74
- const { transformer, stateNames, derivedNames, computedNames } = desugarSignals(source)
95
+ const { transformer, stateNames, derivedNames, computedNames, cellReadNames } = desugarSignals(
96
+ source,
97
+ injectedCellNames,
98
+ blockingCellNames,
99
+ templateWrittenNames,
100
+ seedClassify,
101
+ scriptBase,
102
+ )
75
103
  const result = ts.transform(source, [
76
104
  transformer,
77
- signalRefsTransformer(stateNames, derivedNames, computedNames),
105
+ signalRefsTransformer(
106
+ stateNames,
107
+ derivedNames,
108
+ computedNames,
109
+ new Set(),
110
+ new Set(),
111
+ cellReadNames,
112
+ ),
78
113
  docAccessTransformer('$$model'),
79
114
  ])
80
115
  const transformed = result.transformed[0] as ts.SourceFile
@@ -126,6 +161,7 @@ export function lowerScript(
126
161
  stateNames,
127
162
  derivedNames,
128
163
  computedNames,
164
+ cellReadNames,
129
165
  droppedReactiveImports,
130
166
  }
131
167
  }
@@ -0,0 +1,28 @@
1
+ import type ts from 'typescript'
2
+
3
+ /*
4
+ Finds the shadow AST node for a template interpolation given its span in shadow
5
+ coordinates. An interpolation is emitted as a parenthesised expression, so the
6
+ node we want starts and ends exactly on the span; we descend only into nodes that
7
+ still cover the whole span and keep the deepest exact match (the innermost node
8
+ whose `getStart()/getEnd()` equal the span bounds).
9
+ */
10
+ export function nodeAtShadowOffset(
11
+ sourceFile: ts.SourceFile,
12
+ offset: number,
13
+ length: number,
14
+ ): ts.Node | undefined {
15
+ let best: ts.Node | undefined
16
+ const visit = (node: ts.Node): void => {
17
+ const start = node.getStart(sourceFile)
18
+ const end = node.getEnd()
19
+ if (start <= offset && end >= offset + length) {
20
+ if (start === offset && end === offset + length) {
21
+ best = node
22
+ }
23
+ node.forEachChild(visit)
24
+ }
25
+ }
26
+ visit(sourceFile)
27
+ return best
28
+ }