@abide/abide 0.49.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 (325) hide show
  1. package/AGENTS.md +70 -52
  2. package/CHANGELOG.md +177 -0
  3. package/README.md +12 -7
  4. package/package.json +14 -5
  5. package/src/abideLsp.ts +5 -6
  6. package/src/abideResolverPlugin.ts +197 -169
  7. package/src/build.ts +79 -56
  8. package/src/buildDisconnected.ts +0 -2
  9. package/src/checkAbide.ts +12 -2
  10. package/src/devEntry.ts +113 -28
  11. package/src/discoveryEntry.ts +3 -2
  12. package/src/lib/bundle/disconnected.abide +69 -73
  13. package/src/lib/bundle/infoPlist.ts +13 -7
  14. package/src/lib/bundle/spawnEmbeddedServer.ts +13 -4
  15. package/src/lib/bundle/waitForServer.ts +6 -1
  16. package/src/lib/mcp/mcpTools.ts +15 -6
  17. package/src/lib/server/error.ts +6 -6
  18. package/src/lib/server/json.ts +17 -3
  19. package/src/lib/server/rpc/defineRpc.ts +58 -24
  20. package/src/lib/server/rpc/parseArgs.ts +98 -13
  21. package/src/lib/server/rpc/resolveRpcJsonSchema.ts +28 -0
  22. package/src/lib/server/rpc/types/RpcHelper.ts +75 -105
  23. package/src/lib/server/rpc/types/RpcRegistryEntry.ts +24 -9
  24. package/src/lib/server/rpc/validationError.ts +1 -1
  25. package/src/lib/server/runtime/DEV_RELOAD_CLIENT_SCRIPT.ts +0 -15
  26. package/src/lib/server/runtime/buildCacheSnapshot.ts +10 -9
  27. package/src/lib/server/runtime/buildInspectorSurface.ts +6 -4
  28. package/src/lib/server/runtime/buildOpenApiSpec.ts +55 -11
  29. package/src/lib/server/runtime/buildPreloadManifest.ts +12 -10
  30. package/src/lib/server/runtime/createAppAssetServer.ts +18 -15
  31. package/src/lib/server/runtime/createAppRouteResolver.ts +145 -0
  32. package/src/lib/server/runtime/createPlumbingRouter.ts +212 -0
  33. package/src/lib/server/runtime/createRouteDispatcher.ts +4 -10
  34. package/src/lib/server/runtime/createServer.ts +120 -324
  35. package/src/lib/server/runtime/createUiPageRenderer.ts +137 -26
  36. package/src/lib/server/runtime/devClientFingerprint.ts +19 -34
  37. package/src/lib/server/runtime/installAmbientScopeStore.ts +30 -9
  38. package/src/lib/server/runtime/logExposedSurfaces.ts +8 -7
  39. package/src/lib/server/runtime/pathStore.ts +15 -0
  40. package/src/lib/server/runtime/registryManifests.ts +1 -1
  41. package/src/lib/server/runtime/renderCellBarrierStore.ts +15 -0
  42. package/src/lib/server/runtime/runWithRequestScope.ts +3 -0
  43. package/src/lib/server/runtime/serializeCacheSnapshot.ts +6 -4
  44. package/src/lib/server/runtime/snapshotEntryFromCache.ts +14 -14
  45. package/src/lib/server/runtime/streamCacheResolutions.ts +14 -7
  46. package/src/lib/server/runtime/streamFromIterator.ts +30 -2
  47. package/src/lib/server/runtime/textResponse.ts +23 -0
  48. package/src/lib/server/runtime/types/DevReloadStamp.ts +6 -10
  49. package/src/lib/server/runtime/types/InspectorCacheEntry.ts +1 -1
  50. package/src/lib/server/runtime/types/RequestStore.ts +27 -0
  51. package/src/lib/server/runtime/warnUnguardedMcp.ts +15 -5
  52. package/src/lib/server/sockets/createSocketDispatcher.ts +9 -11
  53. package/src/lib/server/sockets/defineSocket.ts +5 -4
  54. package/src/lib/server/sse.ts +5 -1
  55. package/src/lib/shared/ASYNC_CELL.ts +5 -0
  56. package/src/lib/shared/DEV_REBUILD_PATH.ts +6 -0
  57. package/src/lib/shared/HTTP_METHODS.ts +6 -0
  58. package/src/lib/shared/HttpError.ts +1 -5
  59. package/src/lib/shared/MCP_PATH.ts +6 -0
  60. package/src/lib/shared/PROXIED_SERVER_SUBDIRS.ts +15 -0
  61. package/src/lib/shared/REF_JSON_TAGS.ts +13 -1
  62. package/src/lib/shared/RPC_SHIM_GLOBALS.ts +9 -0
  63. package/src/lib/shared/activeCacheStore.ts +1 -0
  64. package/src/lib/shared/activePage.ts +1 -0
  65. package/src/lib/shared/buildArtifact.ts +4 -1
  66. package/src/lib/shared/buildSocketOverChannel.ts +4 -3
  67. package/src/lib/shared/bundleGraphFromMetafile.ts +68 -0
  68. package/src/lib/shared/cache.ts +156 -131
  69. package/src/lib/shared/changeAffectsClient.ts +12 -7
  70. package/src/lib/shared/createCacheStore.ts +31 -8
  71. package/src/lib/shared/createLifecycleChannel.ts +7 -1
  72. package/src/lib/shared/createReachable.ts +47 -78
  73. package/src/lib/shared/createRemoteFunction.ts +52 -30
  74. package/src/lib/shared/createRpcServerProgram.ts +820 -0
  75. package/src/lib/shared/decodeRefJson.ts +4 -4
  76. package/src/lib/shared/decodeResponse.ts +2 -2
  77. package/src/lib/shared/decodeWireBody.ts +35 -0
  78. package/src/lib/shared/detectRpcMethod.ts +8 -1
  79. package/src/lib/shared/done.ts +8 -2
  80. package/src/lib/shared/encodeRefJson.ts +4 -4
  81. package/src/lib/shared/encodeWireBody.ts +18 -0
  82. package/src/lib/{server/rpc → shared}/fieldErrorsFromIssues.ts +5 -1
  83. package/src/lib/shared/generateDeclarations.ts +72 -0
  84. package/src/lib/shared/hasSeedableRequest.ts +25 -0
  85. package/src/lib/shared/hydrationWindow.ts +53 -0
  86. package/src/lib/shared/isAsyncCell.ts +11 -0
  87. package/src/lib/shared/isAsyncIterable.ts +8 -0
  88. package/src/lib/shared/isSubscribable.ts +3 -3
  89. package/src/lib/shared/isThenable.ts +9 -0
  90. package/src/lib/shared/jsonSchemaForType.ts +258 -0
  91. package/src/lib/shared/lenientDecode.ts +15 -0
  92. package/src/lib/shared/loadProjectTsConfig.ts +28 -0
  93. package/src/lib/shared/markFrameworkSourcesIgnored.ts +1 -1
  94. package/src/lib/shared/matchRoute.ts +4 -14
  95. package/src/lib/shared/peek.ts +14 -0
  96. package/src/lib/shared/pending.ts +9 -6
  97. package/src/lib/shared/pendingAsyncCellsSlot.ts +13 -0
  98. package/src/lib/shared/prepareRpcModule.ts +91 -93
  99. package/src/lib/shared/prepareSocketModule.ts +3 -2
  100. package/src/lib/shared/probeRegistries.ts +5 -18
  101. package/src/lib/shared/reachable.ts +7 -10
  102. package/src/lib/shared/refresh.ts +12 -2
  103. package/src/lib/shared/refreshing.ts +8 -2
  104. package/src/lib/shared/resolvedCellsSlot.ts +13 -0
  105. package/src/lib/shared/reviveWireField.ts +49 -0
  106. package/src/lib/shared/reviveWireOutput.ts +36 -0
  107. package/src/lib/shared/rpcServerForRoot.ts +25 -0
  108. package/src/lib/shared/scanPages.ts +25 -0
  109. package/src/lib/shared/snapshotShippable.ts +8 -7
  110. package/src/lib/shared/streamedCellsSlot.ts +14 -0
  111. package/src/lib/shared/subscribableFromResponse.ts +4 -4
  112. package/src/lib/shared/subscribableProbes.ts +1 -1
  113. package/src/lib/shared/tailProbeSlot.ts +1 -1
  114. package/src/lib/shared/types/AsyncComputed.ts +20 -0
  115. package/src/lib/shared/types/AsyncState.ts +13 -0
  116. package/src/lib/shared/types/CacheEntry.ts +7 -7
  117. package/src/lib/shared/types/CacheOptions.ts +23 -37
  118. package/src/lib/shared/types/CachePolicy.ts +25 -0
  119. package/src/lib/shared/types/CacheSnapshotEntry.ts +6 -5
  120. package/src/lib/shared/types/ErrorJsonSchemas.ts +8 -0
  121. package/src/lib/shared/types/HttpMethod.ts +3 -1
  122. package/src/lib/shared/types/InputCoercion.ts +17 -0
  123. package/src/lib/shared/types/{Subscribable.ts → NamedAsyncIterable.ts} +1 -1
  124. package/src/lib/shared/types/OutputWirePlan.ts +17 -0
  125. package/src/lib/shared/types/PagesScan.ts +7 -0
  126. package/src/lib/shared/types/PendingAsyncCells.ts +10 -0
  127. package/src/lib/shared/types/RawRemoteFunction.ts +6 -0
  128. package/src/lib/shared/types/RemoteCallable.ts +8 -7
  129. package/src/lib/shared/types/RemoteFunction.ts +16 -15
  130. package/src/lib/shared/types/ResolvedCells.ts +11 -0
  131. package/src/lib/shared/types/ReturnBody.ts +15 -0
  132. package/src/lib/shared/types/RpcBuildStamps.ts +23 -0
  133. package/src/lib/shared/types/RpcErrorGuard.ts +3 -8
  134. package/src/lib/shared/types/Socket.ts +4 -4
  135. package/src/lib/shared/types/SsrPayload.ts +5 -1
  136. package/src/lib/shared/types/StreamPolicy.ts +11 -0
  137. package/src/lib/shared/types/StreamedCells.ts +19 -0
  138. package/src/lib/shared/types/StreamedResolution.ts +24 -6
  139. package/src/lib/shared/types/TailHooks.ts +1 -1
  140. package/src/lib/shared/types/WireKind.ts +11 -0
  141. package/src/lib/shared/validationHttpError.ts +33 -0
  142. package/src/lib/shared/warmSeedKey.ts +16 -0
  143. package/src/lib/shared/wireJsonReplacer.ts +30 -0
  144. package/src/lib/shared/writeRpcDts.ts +11 -1
  145. package/src/lib/shared/writeTestSocketsDts.ts +1 -1
  146. package/src/lib/test/createTestApp.ts +19 -1
  147. package/src/lib/ui/README.md +1 -1
  148. package/src/lib/ui/activePendingCells.ts +14 -0
  149. package/src/lib/ui/compile/BLOCK_KEYWORDS.ts +21 -0
  150. package/src/lib/ui/compile/SSR_ESCAPE.ts +4 -1
  151. package/src/lib/ui/compile/UI_RUNTIME_IMPORTS.ts +35 -8
  152. package/src/lib/ui/compile/abideUiPlugin.ts +19 -2
  153. package/src/lib/ui/compile/analyzeComponent.ts +89 -6
  154. package/src/lib/ui/compile/assignmentTargetNames.ts +54 -0
  155. package/src/lib/ui/compile/asyncInterpolationFields.ts +152 -0
  156. package/src/lib/ui/compile/asyncValuePositionError.ts +21 -0
  157. package/src/lib/ui/compile/asyncValuePositionInterpolations.ts +64 -0
  158. package/src/lib/ui/compile/attrLiftPosition.ts +18 -0
  159. package/src/lib/ui/compile/cachedSourceFile.ts +40 -0
  160. package/src/lib/ui/compile/catchBinding.ts +7 -5
  161. package/src/lib/ui/compile/classifyInterpolationType.ts +46 -0
  162. package/src/lib/ui/compile/collectAbideDiagnostics.ts +96 -0
  163. package/src/lib/ui/compile/compileComponent.ts +19 -3
  164. package/src/lib/ui/compile/compileModule.ts +27 -58
  165. package/src/lib/ui/compile/compileSSR.ts +51 -12
  166. package/src/lib/ui/compile/compileShadow.ts +145 -18
  167. package/src/lib/ui/compile/composeProps.ts +12 -2
  168. package/src/lib/ui/compile/createShadowLanguageService.ts +116 -68
  169. package/src/lib/ui/compile/createShadowProgram.ts +35 -6
  170. package/src/lib/ui/compile/declaredNames.ts +36 -0
  171. package/src/lib/ui/compile/desugarSignals.ts +418 -36
  172. package/src/lib/ui/compile/expressionIsPrefixEvaluable.ts +19 -0
  173. package/src/lib/ui/compile/generateBuild.ts +52 -9
  174. package/src/lib/ui/compile/generateSSR.ts +242 -43
  175. package/src/lib/ui/compile/hoistableAwaits.ts +193 -0
  176. package/src/lib/ui/compile/hoistableChildRenders.ts +112 -0
  177. package/src/lib/ui/compile/interpolatedTemplateLiteral.ts +3 -1
  178. package/src/lib/ui/compile/interpolationClassifierForRoot.ts +37 -0
  179. package/src/lib/ui/compile/isSpuriousAsyncReadDiagnostic.ts +174 -0
  180. package/src/lib/ui/compile/liftAsyncSubExpressions.ts +166 -0
  181. package/src/lib/ui/compile/lowerAsyncInterpolations.ts +92 -0
  182. package/src/lib/ui/compile/lowerContext.ts +10 -0
  183. package/src/lib/ui/compile/lowerDocAccess.ts +26 -23
  184. package/src/lib/ui/compile/lowerScript.ts +40 -4
  185. package/src/lib/ui/compile/nodeAtShadowOffset.ts +28 -0
  186. package/src/lib/ui/compile/parseTemplate.ts +18 -1092
  187. package/src/lib/ui/compile/parseTemplateRecovering.ts +1385 -0
  188. package/src/lib/ui/compile/referencedIdentifiers.ts +35 -0
  189. package/src/lib/ui/compile/renameSignalRefs.ts +21 -22
  190. package/src/lib/ui/compile/seedTypeClassifierForRoot.ts +65 -0
  191. package/src/lib/ui/compile/shadowInterpolationClassifier.ts +47 -0
  192. package/src/lib/ui/compile/sourceFileOptionsSignature.ts +20 -0
  193. package/src/lib/ui/compile/structuralHeadTokens.ts +107 -0
  194. package/src/lib/ui/compile/templateSemanticTokens.ts +21 -0
  195. package/src/lib/ui/compile/templateStartOffset.ts +15 -0
  196. package/src/lib/ui/compile/tryPlan.ts +4 -3
  197. package/src/lib/ui/compile/types/AnalyzedComponent.ts +4 -0
  198. package/src/lib/ui/compile/types/AsyncInterpolationField.ts +19 -0
  199. package/src/lib/ui/compile/types/InterpolationClassifier.ts +12 -0
  200. package/src/lib/ui/compile/types/InterpolationKind.ts +7 -0
  201. package/src/lib/ui/compile/types/ParseDiagnostic.ts +8 -0
  202. package/src/lib/ui/compile/types/SeedTypeClassifier.ts +15 -0
  203. package/src/lib/ui/compile/types/TemplateNode.ts +36 -4
  204. package/src/lib/ui/compile/types/ValuePositionInterpolation.ts +13 -0
  205. package/src/lib/ui/compile/writtenTemplateNames.ts +84 -0
  206. package/src/lib/ui/computed.ts +28 -1
  207. package/src/lib/ui/createScope.ts +35 -68
  208. package/src/lib/ui/dom/anchoredBranch.ts +142 -0
  209. package/src/lib/ui/dom/appendText.ts +8 -3
  210. package/src/lib/ui/dom/awaitBlock.ts +43 -94
  211. package/src/lib/ui/dom/bindProp.ts +22 -0
  212. package/src/lib/ui/dom/bindableProp.ts +47 -0
  213. package/src/lib/ui/dom/cellPending.ts +24 -0
  214. package/src/lib/ui/dom/disposeRange.ts +2 -1
  215. package/src/lib/ui/dom/each.ts +26 -4
  216. package/src/lib/ui/dom/eachAsync.ts +39 -3
  217. package/src/lib/ui/dom/fillBoundary.ts +2 -3
  218. package/src/lib/ui/dom/fillRange.ts +4 -4
  219. package/src/lib/ui/dom/hydrate.ts +6 -12
  220. package/src/lib/ui/dom/isComment.ts +1 -1
  221. package/src/lib/ui/dom/matchingRangeClose.ts +29 -0
  222. package/src/lib/ui/dom/mount.ts +1 -2
  223. package/src/lib/ui/dom/mountChild.ts +9 -40
  224. package/src/lib/ui/dom/mountRange.ts +2 -3
  225. package/src/lib/ui/dom/mountStreamedChild.ts +84 -0
  226. package/src/lib/ui/dom/mountSwappableRange.ts +46 -4
  227. package/src/lib/ui/dom/mutateDocContainer.ts +65 -0
  228. package/src/lib/ui/dom/on.ts +2 -2
  229. package/src/lib/ui/dom/readCell.ts +40 -0
  230. package/src/lib/ui/dom/switchBlock.ts +30 -7
  231. package/src/lib/ui/dom/tryBlock.ts +230 -66
  232. package/src/lib/ui/dom/types/SwitchCase.ts +5 -1
  233. package/src/lib/ui/dom/when.ts +12 -2
  234. package/src/lib/ui/dom/withScope.ts +2 -2
  235. package/src/lib/ui/finalizeStreamedChildren.ts +89 -0
  236. package/src/lib/ui/flight.ts +56 -0
  237. package/src/lib/ui/html.ts +12 -1
  238. package/src/lib/ui/isolateCellBarrier.ts +17 -0
  239. package/src/lib/ui/linked.ts +48 -2
  240. package/src/lib/ui/remoteProxy.ts +84 -159
  241. package/src/lib/ui/renderChain.ts +49 -13
  242. package/src/lib/ui/renderToStream.ts +22 -18
  243. package/src/lib/ui/resumeSeedScript.ts +1 -1
  244. package/src/lib/ui/router.ts +63 -36
  245. package/src/lib/ui/runtime/AsyncCellError.ts +20 -0
  246. package/src/lib/ui/runtime/CELL_SEED.ts +14 -0
  247. package/src/lib/ui/runtime/CURRENT_BOUNDARY.ts +11 -0
  248. package/src/lib/ui/runtime/CURRENT_PATH.ts +29 -0
  249. package/src/lib/ui/runtime/RENDER.ts +10 -7
  250. package/src/lib/ui/runtime/RESUME.ts +4 -4
  251. package/src/lib/ui/runtime/STREAMED_CELLS.ts +57 -0
  252. package/src/lib/ui/runtime/ambientPathBacking.ts +32 -0
  253. package/src/lib/ui/runtime/applyPatchToTree.ts +1 -1
  254. package/src/lib/ui/runtime/blockId.ts +31 -0
  255. package/src/lib/ui/runtime/boundaryFor.ts +11 -0
  256. package/src/lib/ui/runtime/cellBarrierBacking.ts +30 -0
  257. package/src/lib/ui/runtime/createAsyncCell.ts +300 -0
  258. package/src/lib/ui/runtime/createDoc.ts +3 -42
  259. package/src/lib/ui/runtime/createEffectNode.ts +9 -0
  260. package/src/lib/ui/runtime/enterRenderPass.ts +5 -4
  261. package/src/lib/ui/runtime/flushEffects.ts +16 -6
  262. package/src/lib/ui/runtime/isAsyncFunction.ts +14 -0
  263. package/src/lib/ui/runtime/nextBlockId.ts +8 -7
  264. package/src/lib/ui/runtime/renderPath.ts +16 -0
  265. package/src/lib/ui/runtime/types/Boundary.ts +9 -0
  266. package/src/lib/ui/runtime/types/RenderContext.ts +9 -7
  267. package/src/lib/ui/runtime/types/SsrRender.ts +9 -2
  268. package/src/lib/ui/runtime/types/UiComponent.ts +1 -5
  269. package/src/lib/ui/runtime/withOptionalPath.ts +8 -0
  270. package/src/lib/ui/runtime/withPath.ts +16 -0
  271. package/src/lib/ui/runtime/withPathFrom.ts +20 -0
  272. package/src/lib/ui/seedStreamedResolution.ts +31 -6
  273. package/src/lib/ui/settleAsyncCells.ts +24 -0
  274. package/src/lib/ui/socketProxy.ts +1 -1
  275. package/src/lib/ui/startClient.ts +16 -31
  276. package/src/lib/ui/trackedComputed.ts +68 -0
  277. package/src/lib/ui/types/Scope.ts +8 -24
  278. package/src/lib/ui/watch.ts +3 -3
  279. package/src/serverEntry.ts +14 -0
  280. package/template/src/server/rpc/getHello.ts +15 -13
  281. package/template/test/app.test.ts +1 -1
  282. package/src/lib/server/runtime/devHotModuleResponse.ts +0 -41
  283. package/src/lib/shared/DEV_HOT_PREFIX.ts +0 -7
  284. package/src/lib/shared/UNREACHABLE_STATUSES.ts +0 -13
  285. package/src/lib/shared/hasReplayableRequest.ts +0 -17
  286. package/src/lib/shared/hydratingSlot.ts +0 -12
  287. package/src/lib/shared/outboxProbeSlot.ts +0 -20
  288. package/src/lib/shared/types/Outbox.ts +0 -9
  289. package/src/lib/shared/types/OutboxEntry.ts +0 -27
  290. package/src/lib/shared/types/SmartReadOptions.ts +0 -36
  291. package/src/lib/shared/wakeHydrationPeeks.ts +0 -20
  292. package/src/lib/ui/COMPONENT_WRAPPER_PREFIX.ts +0 -5
  293. package/src/lib/ui/compile/REACTIVE_CALLEES.ts +0 -11
  294. package/src/lib/ui/compile/assertRuntimeHelpersBound.ts +0 -80
  295. package/src/lib/ui/compile/markupTokens.ts +0 -313
  296. package/src/lib/ui/compile/structuralBlockTokens.ts +0 -100
  297. package/src/lib/ui/dom/applyResolved.ts +0 -87
  298. package/src/lib/ui/dom/mutateDocArray.ts +0 -38
  299. package/src/lib/ui/dom/scopeLabel.ts +0 -21
  300. package/src/lib/ui/dom/text.ts +0 -20
  301. package/src/lib/ui/history.ts +0 -108
  302. package/src/lib/ui/installHotBridge.ts +0 -95
  303. package/src/lib/ui/installInspectorBridge.ts +0 -140
  304. package/src/lib/ui/outbox.ts +0 -35
  305. package/src/lib/ui/persist.ts +0 -115
  306. package/src/lib/ui/rpcOutbox/createOutboxQueue.ts +0 -281
  307. package/src/lib/ui/rpcOutbox/outboxRegistry.ts +0 -69
  308. package/src/lib/ui/runtime/PATCH_BUS.ts +0 -28
  309. package/src/lib/ui/runtime/captureModelDoc.ts +0 -43
  310. package/src/lib/ui/runtime/hotInstances.ts +0 -10
  311. package/src/lib/ui/runtime/hotReloadEnabled.ts +0 -8
  312. package/src/lib/ui/runtime/hotReplace.ts +0 -38
  313. package/src/lib/ui/runtime/liveScopes.ts +0 -15
  314. package/src/lib/ui/runtime/localStoragePersistence.ts +0 -47
  315. package/src/lib/ui/runtime/registerHotInstance.ts +0 -23
  316. package/src/lib/ui/runtime/seedModelDoc.ts +0 -26
  317. package/src/lib/ui/runtime/types/HotInstance.ts +0 -22
  318. package/src/lib/ui/runtime/types/PatchEvent.ts +0 -16
  319. package/src/lib/ui/seedResolved.ts +0 -28
  320. package/src/lib/ui/sync.ts +0 -48
  321. package/src/lib/ui/types/History.ts +0 -14
  322. package/src/lib/ui/types/PersistHandle.ts +0 -11
  323. package/src/lib/ui/types/PersistenceStore.ts +0 -12
  324. package/src/lib/ui/types/ResolvedFrame.ts +0 -15
  325. package/src/lib/ui/types/SyncTransport.ts +0 -13
@@ -0,0 +1,193 @@
1
+ import ts from 'typescript'
2
+ import { declaredNames } from './declaredNames.ts'
3
+ import { expressionIsPrefixEvaluable } from './expressionIsPrefixEvaluable.ts'
4
+ import type { TemplateNode } from './types/TemplateNode.ts'
5
+
6
+ /* One hoistable await: the node whose promise-start moves to the SSR prefix, and the
7
+ `$flightN` const name that holds the in-flight promise. */
8
+ export type HoistedFlight = {
9
+ node: Extract<TemplateNode, { kind: 'await' }>
10
+ name: string
11
+ }
12
+
13
+ /*
14
+ ADR-0034 — the SSR-only pass that picks which `await` blocks may start their promise in the
15
+ synchronous render prefix so independent flights overlap instead of serializing. An await is
16
+ HOISTABLE iff its promise is evaluable at prefix time and its content is unconditionally rendered:
17
+
18
+ 1. Its promise's referenced identifiers avoid every enclosing TEMPLATE-LOCAL binder (a `{#for}`
19
+ item/index, a `{:then}`/`{:catch}`/`then v` value, a snippet param, a nested `<script>`
20
+ local) — those don't exist in the prefix — AND every ASYNC-CELL name (`cellReadNames`, still
21
+ pending at prefix time).
22
+ 2. It is not inside a CONDITIONALLY-rendered branch (`{#if}`/`{#switch}`/`{#try}`, a snippet/slot
23
+ body, or another await's pending/then/catch branch) — starting its flight eagerly would fetch
24
+ for a branch that may never render, or surface an error prematurely.
25
+ 3. It is STATICALLY-SINGLE: on the top-level spine, or inside a single-element-literal
26
+ `{#for k of [expr] by k}` whose body renders exactly once (so one hoisted flight == one row).
27
+
28
+ Fail-closed: identifiers are over-collected and any doubt blocks the hoist, so a mis-classified
29
+ row-local flight can never collapse a multi-row `{#for}` to one shared value. Server-only — the
30
+ returned nodes are rewired only in `generateSSR`; the client build and the wire are untouched.
31
+ */
32
+ export function hoistableAwaits(
33
+ nodes: TemplateNode[],
34
+ cellReadNames: ReadonlySet<string>,
35
+ ): HoistedFlight[] {
36
+ const flights: HoistedFlight[] = []
37
+ const counter = { next: 0 }
38
+ walkList(nodes, new Set<string>(), false, false, cellReadNames, flights, counter)
39
+ return flights
40
+ }
41
+
42
+ /* Walk a sibling list left-to-right so a nested `<script>`'s declared locals extend the binder
43
+ set for the siblings that follow it (they are in that script's lexical scope). */
44
+ function walkList(
45
+ list: TemplateNode[],
46
+ binders: ReadonlySet<string>,
47
+ conditional: boolean,
48
+ multiRow: boolean,
49
+ cellReadNames: ReadonlySet<string>,
50
+ flights: HoistedFlight[],
51
+ counter: { next: number },
52
+ ): void {
53
+ /* A running binder set for this list: a `<script>` sibling adds its top-level declarations,
54
+ so a later sibling's await that reads one is (conservatively) not hoistable. */
55
+ let listBinders = binders
56
+ for (const node of list) {
57
+ if (node.kind === 'script') {
58
+ listBinders = union(listBinders, declaredNames(node.code))
59
+ continue
60
+ }
61
+ walkNode(node, listBinders, conditional, multiRow, cellReadNames, flights, counter)
62
+ }
63
+ }
64
+
65
+ function walkNode(
66
+ node: TemplateNode,
67
+ binders: ReadonlySet<string>,
68
+ conditional: boolean,
69
+ multiRow: boolean,
70
+ cellReadNames: ReadonlySet<string>,
71
+ flights: HoistedFlight[],
72
+ counter: { next: number },
73
+ ): void {
74
+ if (node.kind === 'await') {
75
+ /* Hoistability is judged in the INCOMING context — the block's promise starts when the
76
+ block mounts (its pending branch always renders), so it is not gated by its own
77
+ branches, only by an enclosing conditional/multi-row. */
78
+ if (
79
+ !conditional &&
80
+ !multiRow &&
81
+ promiseIsPrefixEvaluable(node.promise, binders, cellReadNames)
82
+ ) {
83
+ flights.push({ node, name: `$flight${counter.next++}` })
84
+ }
85
+ /* The resolved/catch/pending branches are conditionally shown and bind the resolved value
86
+ (`node.as` for `{#await p then v}`; a `{:then v}`/`{:catch e}` branch binds via its own
87
+ `as`, added when that branch node is walked). */
88
+ const inner = node.as === undefined ? binders : union(binders, new Set([node.as]))
89
+ walkList(node.children, inner, true, multiRow, cellReadNames, flights, counter)
90
+ return
91
+ }
92
+ if (node.kind === 'each') {
93
+ /* Row/index bindings are template-local; the body is multi-row unless the source is a
94
+ single-element array literal (then it renders exactly once). `{#for await}` drains a
95
+ stream — its body is still per-frame, so treat it as multi-row. */
96
+ const rowBinders = new Set<string>()
97
+ rowBinders.add(node.as)
98
+ if (node.index !== undefined) {
99
+ rowBinders.add(node.index)
100
+ }
101
+ const rowMulti = multiRow || node.async === true || !isSingleElementLiteral(node.items)
102
+ walkList(
103
+ node.children,
104
+ union(binders, rowBinders),
105
+ conditional,
106
+ rowMulti,
107
+ cellReadNames,
108
+ flights,
109
+ counter,
110
+ )
111
+ return
112
+ }
113
+ if (
114
+ node.kind === 'if' ||
115
+ node.kind === 'switch' ||
116
+ node.kind === 'case' ||
117
+ node.kind === 'try'
118
+ ) {
119
+ /* Conditionally-rendered subtrees — never hoist a flight out of them. */
120
+ walkList(node.children, binders, true, multiRow, cellReadNames, flights, counter)
121
+ return
122
+ }
123
+ if (node.kind === 'branch') {
124
+ const inner = node.as === undefined ? binders : union(binders, new Set([node.as]))
125
+ walkList(node.children, inner, true, multiRow, cellReadNames, flights, counter)
126
+ return
127
+ }
128
+ if (node.kind === 'snippet') {
129
+ /* A snippet is a builder called conditionally; its params are locals. */
130
+ const params =
131
+ node.params === undefined
132
+ ? new Set<string>()
133
+ : declaredNames(`const [${node.params}] = []`)
134
+ walkList(
135
+ node.children,
136
+ union(binders, params),
137
+ true,
138
+ multiRow,
139
+ cellReadNames,
140
+ flights,
141
+ counter,
142
+ )
143
+ return
144
+ }
145
+ if (node.kind === 'component') {
146
+ /* Slot content is a fresh, lazily-built scope rendered at the child's `<slot>`. */
147
+ walkList(node.children, binders, true, multiRow, cellReadNames, flights, counter)
148
+ return
149
+ }
150
+ if (node.kind === 'element' && 'children' in node) {
151
+ walkList(node.children, binders, conditional, multiRow, cellReadNames, flights, counter)
152
+ }
153
+ }
154
+
155
+ /* True when EVERY referenced identifier of `promise` is prefix-evaluable: none is a template-local
156
+ binder and none is an async-cell name. Delegates to the shared `expressionIsPrefixEvaluable`. */
157
+ function promiseIsPrefixEvaluable(
158
+ promise: string,
159
+ binders: ReadonlySet<string>,
160
+ cellReadNames: ReadonlySet<string>,
161
+ ): boolean {
162
+ return expressionIsPrefixEvaluable(promise, binders, cellReadNames)
163
+ }
164
+
165
+ /* A single-element array literal like `[remountKey]` — the only `{#for}` source that renders its
166
+ body exactly once, so a hoisted flight inside it maps to one row (no shared-flight hazard). */
167
+ function isSingleElementLiteral(items: string): boolean {
168
+ let source: ts.SourceFile
169
+ try {
170
+ source = ts.createSourceFile('items.ts', items, ts.ScriptTarget.Latest, true)
171
+ } catch {
172
+ return false
173
+ }
174
+ const statement = source.statements[0]
175
+ if (
176
+ statement === undefined ||
177
+ source.statements.length !== 1 ||
178
+ !ts.isExpressionStatement(statement) ||
179
+ !ts.isArrayLiteralExpression(statement.expression)
180
+ ) {
181
+ return false
182
+ }
183
+ const elements = statement.expression.elements
184
+ return elements.length === 1 && !ts.isSpreadElement(elements[0] as ts.Expression)
185
+ }
186
+
187
+ function union(a: ReadonlySet<string>, b: ReadonlySet<string>): Set<string> {
188
+ const result = new Set(a)
189
+ for (const value of b) {
190
+ result.add(value)
191
+ }
192
+ return result
193
+ }
@@ -0,0 +1,112 @@
1
+ import { declaredNames } from './declaredNames.ts'
2
+ import { expressionIsPrefixEvaluable } from './expressionIsPrefixEvaluable.ts'
3
+ import type { TemplateNode } from './types/TemplateNode.ts'
4
+
5
+ type ComponentNode = Extract<TemplateNode, { kind: 'component' }>
6
+
7
+ /*
8
+ ADR-0037 Phase 2 — which `<Child/>` renders may START in the synchronous SSR prefix (as a `$$flight`
9
+ awaited at its structural position) so sibling child renders overlap instead of serializing behind
10
+ each other's `await Child.render(...)`. A child render is HOISTABLE iff it is on the TOP-LEVEL SPINE
11
+ — reachable from the template root through only plain elements, so no control-flow / await-branch /
12
+ snippet / slot binder is in scope — AND:
13
+
14
+ 1. its component reference and every prop value (including a `{...spread}`) are prefix-evaluable:
15
+ no async-cell name (still pending at the prefix, before the barrier) and no nested-`<script>`
16
+ local declared earlier on the spine (those lower at their body position, not the prefix);
17
+ 2. it carries NO slot content (`children` empty) — lazily-built slot content would need its own
18
+ prefix-evaluability proof; a childless card is the common parallel case;
19
+ 3. no `bind:` prop — a two-way write-back channel that must wire at its structural position.
20
+
21
+ Off-spine components (inside `{#if}`/`{#for}`/`{#await}`, a snippet, or another component's slot) stay
22
+ sequential: conservatively excluded rather than proven row/branch-safe. Fail-closed throughout — any
23
+ doubt leaves the render sequential, never wrong. Server-only; the client build is untouched.
24
+ */
25
+ export function hoistableChildRenders(
26
+ nodes: TemplateNode[],
27
+ cellReadNames: ReadonlySet<string>,
28
+ ): Set<ComponentNode> {
29
+ const hoistable = new Set<ComponentNode>()
30
+ /* Every `{#snippet name}` in the component is a hoisted builder that may itself render an
31
+ async cell / await; a prop passing one to a child (`<List item={row}/>`) must therefore NOT
32
+ hoist — calling `row()` early, in the prefix, would render it out of order. Seed the binder set
33
+ with all snippet names (collected tree-wide, since a snippet is in scope for the whole
34
+ component) so any prop referencing one fails the prefix-evaluability check. */
35
+ const binders = new Set<string>()
36
+ collectSnippetNames(nodes, binders)
37
+ walkSpine(nodes, binders, cellReadNames, hoistable)
38
+ return hoistable
39
+ }
40
+
41
+ /* Recursively collect every `{#snippet name}` name in the subtree into `into`. */
42
+ function collectSnippetNames(list: TemplateNode[], into: Set<string>): void {
43
+ for (const node of list) {
44
+ if (node.kind === 'snippet') {
45
+ into.add(node.name)
46
+ }
47
+ if ('children' in node && Array.isArray(node.children)) {
48
+ collectSnippetNames(node.children, into)
49
+ }
50
+ }
51
+ }
52
+
53
+ /* Walk a spine sibling list left-to-right: a `<script>` sibling's locals bind the siblings that
54
+ follow it; a component is judged in the running binder context; a plain element descends (still on
55
+ the spine). Every other node kind leaves the spine and is not descended — its components stay
56
+ sequential. */
57
+ function walkSpine(
58
+ list: TemplateNode[],
59
+ binders: ReadonlySet<string>,
60
+ cellReadNames: ReadonlySet<string>,
61
+ hoistable: Set<ComponentNode>,
62
+ ): void {
63
+ let running = binders
64
+ for (const node of list) {
65
+ if (node.kind === 'script') {
66
+ running = union(running, declaredNames(node.code))
67
+ continue
68
+ }
69
+ if (node.kind === 'component') {
70
+ if (componentIsHoistable(node, running, cellReadNames)) {
71
+ hoistable.add(node)
72
+ }
73
+ /* Slot children are off-spine (built lazily inside the child) — not descended. */
74
+ continue
75
+ }
76
+ if (node.kind === 'element' && 'children' in node) {
77
+ walkSpine(node.children, running, cellReadNames, hoistable)
78
+ }
79
+ /* if / switch / each / await / try / snippet / branch: off the spine, skip. */
80
+ }
81
+ }
82
+
83
+ /* Childless, bind-free, with a prefix-evaluable tag and every prop value prefix-evaluable. */
84
+ function componentIsHoistable(
85
+ node: ComponentNode,
86
+ binders: ReadonlySet<string>,
87
+ cellReadNames: ReadonlySet<string>,
88
+ ): boolean {
89
+ if (node.children.length > 0) {
90
+ return false
91
+ }
92
+ if (!expressionIsPrefixEvaluable(node.name, binders, cellReadNames)) {
93
+ return false
94
+ }
95
+ for (const prop of node.props) {
96
+ if (prop.bind === true) {
97
+ return false
98
+ }
99
+ if (!expressionIsPrefixEvaluable(prop.code, binders, cellReadNames)) {
100
+ return false
101
+ }
102
+ }
103
+ return true
104
+ }
105
+
106
+ function union(a: ReadonlySet<string>, b: ReadonlySet<string>): Set<string> {
107
+ const result = new Set(a)
108
+ for (const value of b) {
109
+ result.add(value)
110
+ }
111
+ return result
112
+ }
@@ -16,9 +16,11 @@ prop value can't diverge on the concatenation. Always yields a string, so an
16
16
  interpolated attribute is always present.
17
17
  */
18
18
  export function interpolatedTemplateLiteral(parts: TextPart[]): string {
19
+ /* A dynamic slot coerces nullish to empty so `attr="a {v} b"` with a pending/undefined
20
+ `v` yields `"a b"`, never `"a undefined b"` (ADR-0032 D3). */
19
21
  const body = parts
20
22
  .map((part) =>
21
- part.kind === 'static' ? escapeTemplateChunk(part.value) : `\${${part.code}}`,
23
+ part.kind === 'static' ? escapeTemplateChunk(part.value) : `\${(${part.code}) ?? ''}`,
22
24
  )
23
25
  .join('')
24
26
  return `\`${body}\``
@@ -0,0 +1,37 @@
1
+ import { createShadowProgram, type ShadowProgram } from './createShadowProgram.ts'
2
+ import { shadowInterpolationClassifier } from './shadowInterpolationClassifier.ts'
3
+ import type { InterpolationClassifier } from './types/InterpolationClassifier.ts'
4
+
5
+ /*
6
+ Builds the type-directed interpolation classifier for one `.abide` file, backed by
7
+ a WARM shadow program kept once per project root (ADR-0019, Stage B). The shadow
8
+ program (checker + every component's source→shadow `mappings`) is expensive to
9
+ build, so it is created lazily on first request for a root and reused for every
10
+ component in that root — the coupling that makes type-directed lowering affordable.
11
+ The incremental `createShadowLanguageService` does not expose the checker, shadow
12
+ `ts.SourceFile`, or per-file mappings publicly, so v1 uses the one-shot
13
+ `createShadowProgram` (reused per root) rather than the LS overlay path.
14
+
15
+ FAIL-OPEN throughout: if the program can't be built, or the file has no shadow, the
16
+ classifier is absent (→ today's plain-value binding); and the returned closure wraps
17
+ its whole body so ANY throw (resolution failure, missing node, checker hiccup) returns
18
+ `'sync'`. A type-resolution problem degrades to today's behavior, never breaks the build.
19
+ */
20
+ export function interpolationClassifierForRoot(
21
+ cache: Map<string, ShadowProgram | undefined>,
22
+ root: string,
23
+ abidePath: string,
24
+ ): InterpolationClassifier | undefined {
25
+ if (!cache.has(root)) {
26
+ try {
27
+ cache.set(root, createShadowProgram(root))
28
+ } catch {
29
+ cache.set(root, undefined)
30
+ }
31
+ }
32
+ const shadowProgram = cache.get(root)
33
+ if (shadowProgram === undefined) {
34
+ return undefined
35
+ }
36
+ return shadowInterpolationClassifier(shadowProgram.program, shadowProgram.shadows, abidePath)
37
+ }
@@ -0,0 +1,174 @@
1
+ import ts from 'typescript'
2
+ import { classifyInterpolationType } from './classifyInterpolationType.ts'
3
+
4
+ /* The two TypeScript diagnostics a bare async read (ADR-0032) raises spuriously in a template.
5
+ 2339 — a property "missing" on the un-awaited `Promise`/`AsyncIterable`; 2801 — a condition the
6
+ raw promise makes "always defined". Both are correct against the raw type but wrong against the
7
+ runtime PEEK (the resolved value, `undefined` while pending), which the shadow can't see. */
8
+ const PROPERTY_DOES_NOT_EXIST = 2339
9
+ const CONDITION_ALWAYS_DEFINED = 2801
10
+
11
+ /*
12
+ Whether a shadow diagnostic is one the ADR-0032 bare-async-read peek makes spurious. The runtime
13
+ lifts a promise/iterable sub-expression to a cell that reads the RESOLVED value (`undefined` while
14
+ pending), so `getFoo()?.name` composes on the resolved object and `{#if getFoo()}` holds while
15
+ pending — but the type-check shadow checks the RAW expression, where the property sits on the
16
+ un-awaited `Promise` and the promise reads as always-truthy. This recognises those two cases so the
17
+ caller can drop them:
18
+
19
+ - 2339 (missing property): an OPTIONAL access (`?.`) on a promise/iterable-typed base whose
20
+ property EXISTS on the awaited (promise) or frame (iterable) type — the peek's real member.
21
+ - 2801 (always-defined condition): the flagged subject itself classifies as a promise/iterable —
22
+ the peek is genuinely nullable (pending → `undefined`), so it is not always-defined.
23
+
24
+ Deliberately narrow so it never hides a real mistake: only these two codes; 2339 only via `?.`
25
+ (a bare `.name` on a promise stays an error, nudging toward the `?.` the peek needs) and only when
26
+ the property resolves on the awaited/frame type (a typo `?.namex` stays); 2801 only when the
27
+ subject is actually async (a bare `{#if someFn}` — a forgotten call — stays). Callers additionally
28
+ gate on the diagnostic mapping into the template region, so `<script>` code (where a forgotten
29
+ `await` must surface) is never touched.
30
+ */
31
+ export function isSpuriousAsyncReadDiagnostic(
32
+ shadowFile: ts.SourceFile,
33
+ checker: ts.TypeChecker,
34
+ code: number,
35
+ start: number,
36
+ length: number,
37
+ ): boolean {
38
+ if (code === PROPERTY_DOES_NOT_EXIST) {
39
+ return isResolvedOptionalAsyncAccess(shadowFile, checker, start, length)
40
+ }
41
+ if (code === CONDITION_ALWAYS_DEFINED) {
42
+ return isAsyncSubject(shadowFile, checker, start, length)
43
+ }
44
+ return false
45
+ }
46
+
47
+ /* A 2339 on the member of an optional (`?.`) access whose base is a promise/iterable and whose
48
+ accessed property exists on the resolved value — the ADR-0032 peek, not an error. */
49
+ function isResolvedOptionalAsyncAccess(
50
+ shadowFile: ts.SourceFile,
51
+ checker: ts.TypeChecker,
52
+ start: number,
53
+ length: number,
54
+ ): boolean {
55
+ const access = enclosingOptionalAccess(deepestNodeAt(shadowFile, start, length))
56
+ if (access === undefined) {
57
+ return false
58
+ }
59
+ const resolved = resolvedAsyncType(access.expression, checker)
60
+ if (resolved === undefined) {
61
+ return false
62
+ }
63
+ const property = accessedPropertyName(access)
64
+ return property !== undefined && checker.getPropertyOfType(resolved, property) !== undefined
65
+ }
66
+
67
+ /* A 2801 whose flagged subject classifies as a promise/iterable — the peek reads `undefined` while
68
+ pending, so it is genuinely nullable, not always-defined. */
69
+ function isAsyncSubject(
70
+ shadowFile: ts.SourceFile,
71
+ checker: ts.TypeChecker,
72
+ start: number,
73
+ length: number,
74
+ ): boolean {
75
+ const node = deepestNodeAt(shadowFile, start, length)
76
+ const kind = classifyInterpolationType(checker.getTypeAtLocation(node), node, checker)
77
+ return kind === 'promise' || kind === 'asyncIterable'
78
+ }
79
+
80
+ /* The resolved value an async base peeks: a promise's awaited type, an iterable's frame type,
81
+ `undefined` when the base isn't actually async. */
82
+ function resolvedAsyncType(base: ts.Node, checker: ts.TypeChecker): ts.Type | undefined {
83
+ const baseType = checker.getTypeAtLocation(base)
84
+ const kind = classifyInterpolationType(baseType, base, checker)
85
+ if (kind === 'promise') {
86
+ return checker.getAwaitedType(baseType)
87
+ }
88
+ if (kind === 'asyncIterable') {
89
+ return asyncFrameType(baseType, base, checker)
90
+ }
91
+ return undefined
92
+ }
93
+
94
+ /* The deepest AST node whose range contains the diagnostic span. */
95
+ function deepestNodeAt(sourceFile: ts.SourceFile, start: number, length: number): ts.Node {
96
+ let best: ts.Node = sourceFile
97
+ const end = start + length
98
+ const visit = (node: ts.Node): void => {
99
+ if (node.getStart(sourceFile) <= start && node.getEnd() >= end) {
100
+ best = node
101
+ node.forEachChild(visit)
102
+ }
103
+ }
104
+ visit(sourceFile)
105
+ return best
106
+ }
107
+
108
+ /* The optional (`?.`) property/element access that OWNS `node` as its accessed member — walking
109
+ up from the name identifier. `undefined` when `node` isn't the member of an optional access
110
+ (a bare `.name`, or the base rather than the member). */
111
+ function enclosingOptionalAccess(
112
+ node: ts.Node,
113
+ ): ts.PropertyAccessExpression | ts.ElementAccessExpression | undefined {
114
+ const parent = node.parent
115
+ if (parent === undefined) {
116
+ return undefined
117
+ }
118
+ if (ts.isPropertyAccessExpression(parent) && parent.name === node) {
119
+ return parent.questionDotToken === undefined ? undefined : parent
120
+ }
121
+ if (ts.isElementAccessExpression(parent) && parent.argumentExpression === node) {
122
+ return parent.questionDotToken === undefined ? undefined : parent
123
+ }
124
+ return undefined
125
+ }
126
+
127
+ /* The property name an access reads — the identifier for `a?.b`, the string literal for
128
+ `a?.['b']`. A computed non-literal element access has no static name. */
129
+ function accessedPropertyName(
130
+ access: ts.PropertyAccessExpression | ts.ElementAccessExpression,
131
+ ): string | undefined {
132
+ if (ts.isPropertyAccessExpression(access)) {
133
+ return access.name.text
134
+ }
135
+ return ts.isStringLiteralLike(access.argumentExpression)
136
+ ? access.argumentExpression.text
137
+ : undefined
138
+ }
139
+
140
+ /* The frame type of an `AsyncIterable<T>` — `T`, which is what the runtime peek reads (the latest
141
+ frame). The named async-iterable types (`AsyncIterable`/`AsyncGenerator`/`AsyncIterableIterator`)
142
+ all carry `T` as their first type argument, so read it directly; a structurally-typed iterable
143
+ with no type arguments falls back to resolving `[Symbol.asyncIterator]().next()`'s yielded value.
144
+ `location` is any node in the program, needed to resolve the iterator symbol's type. `undefined`
145
+ when neither yields a frame, so the caller keeps the diagnostic. */
146
+ function asyncFrameType(
147
+ type: ts.Type,
148
+ location: ts.Node,
149
+ checker: ts.TypeChecker,
150
+ ): ts.Type | undefined {
151
+ const directArgument = checker.getTypeArguments(type as ts.TypeReference)[0]
152
+ if (directArgument !== undefined) {
153
+ return directArgument
154
+ }
155
+ /* The `Symbol.asyncIterator` member mangles to `__@asyncIterator@<id>`, so match by prefix. */
156
+ const iterator = checker
157
+ .getPropertiesOfType(type)
158
+ .find((property) => (property.escapedName as string).startsWith('__@asyncIterator'))
159
+ if (iterator === undefined) {
160
+ return undefined
161
+ }
162
+ const factory = checker.getTypeOfSymbolAtLocation(iterator, location).getCallSignatures()[0]
163
+ const next = factory && checker.getPropertyOfType(factory.getReturnType(), 'next')
164
+ const nextSignature =
165
+ next && checker.getTypeOfSymbolAtLocation(next, location).getCallSignatures()[0]
166
+ if (!nextSignature) {
167
+ return undefined
168
+ }
169
+ /* `next()` resolves to `IteratorResult<T, TReturn>`, whose first type argument is the yielded
170
+ `T` (its `value` PROPERTY widens to `T | TReturn` across the done/not-done union, so read the
171
+ argument, not the property). */
172
+ const result = checker.getAwaitedType(nextSignature.getReturnType())
173
+ return result && checker.getTypeArguments(result as ts.TypeReference)[0]
174
+ }