@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,35 @@
1
+ import ts from 'typescript'
2
+
3
+ /* Every identifier read as a value in `code` — excludes property member names (`.name`) and object
4
+ literal keys, which are not free variable references. Deliberately generous elsewhere: an inner
5
+ arrow's param is still collected, so an expression shadowing a binder name inside a callback is
6
+ judged conservatively (NOT prefix-evaluable) rather than risking a false hoist. Shared by the
7
+ await-flight and child-render-flight prefix-evaluability checks (ADR-0034 / ADR-0037). */
8
+ export function referencedIdentifiers(code: string): Set<string> {
9
+ const names = new Set<string>()
10
+ let source: ts.SourceFile
11
+ try {
12
+ source = ts.createSourceFile('expression.ts', code, ts.ScriptTarget.Latest, true)
13
+ } catch {
14
+ /* Unparseable expression → treat as non-hoistable by returning a sentinel the caller's
15
+ binder check can never clear; simplest is a name no scope defines. */
16
+ return new Set(['\0unparseable'])
17
+ }
18
+ const visit = (node: ts.Node): void => {
19
+ if (ts.isPropertyAccessExpression(node)) {
20
+ visit(node.expression)
21
+ return
22
+ }
23
+ if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name)) {
24
+ visit(node.initializer)
25
+ return
26
+ }
27
+ if (ts.isIdentifier(node)) {
28
+ names.add(node.text)
29
+ return
30
+ }
31
+ ts.forEachChild(node, visit)
32
+ }
33
+ visit(source)
34
+ return names
35
+ }
@@ -1,10 +1,9 @@
1
1
  import ts from 'typescript'
2
- import { TS_PRINTER } from './TS_PRINTER.ts'
3
2
 
4
3
  /*
5
4
  Rewrites references to a component's signal bindings into the document form the
6
5
  rest of the pipeline understands: a `state` binding `count` becomes `$$model.count`
7
- (data access `lowerDocAccess` then lowers to a patch/read), and a `computed`
6
+ (data access `docAccessTransformer` then lowers to a patch/read), and a `computed`
8
7
  binding `total` becomes `total.value`. Only value-position identifiers are
9
8
  touched — declaration names, parameter names, and property names are collected
10
9
  into a skip set first, and object shorthand (`{ count }`) is expanded to
@@ -17,27 +16,10 @@ to that inner binding, not the component signal, so it is left untouched. Withou
17
16
  this, a callback like `list.map(option => option.toUpperCase())` in a component
18
17
  that also has an `option` prop (`const { option } = props()`) would have its loop
19
18
  variable rewritten to `option()` and blow up at runtime (`option is not a function`).
20
- */
21
- export function renameSignalRefs(
22
- code: string,
23
- stateNames: ReadonlySet<string>,
24
- derivedNames: ReadonlySet<string>,
25
- computedNames: ReadonlySet<string> = new Set(),
26
- ): string {
27
- const source = ts.createSourceFile('component.ts', code, ts.ScriptTarget.Latest, true)
28
- const result = ts.transform(source, [
29
- signalRefsTransformer(stateNames, derivedNames, computedNames),
30
- ])
31
- const output = TS_PRINTER.printFile(result.transformed[0] as ts.SourceFile)
32
- result.dispose()
33
- return output
34
- }
35
19
 
36
- /*
37
- The signal-rewrite as a `ts.TransformerFactory`, so the script pipeline can chain it
38
- with `docAccessTransformer` over a SINGLE parsed tree (see `lowerScript`) instead of
39
- print-then-reparse between passes. `renameSignalRefs` is the standalone string wrapper
40
- kept for test and one-off callers.
20
+ Exposed as a `ts.TransformerFactory`, so the script pipeline can chain it with
21
+ `docAccessTransformer` over a SINGLE parsed tree (see `lowerScript`) instead of
22
+ print-then-reparse between passes.
41
23
  */
42
24
  export function signalRefsTransformer(
43
25
  stateNames: ReadonlySet<string>,
@@ -53,6 +35,10 @@ export function signalRefsTransformer(
53
35
  bare identifier, not `.value`: SSR declares `const foo = <resolved>`, so a reference
54
36
  reads the local directly. Seeded into the root shadow set so it is left untouched. */
55
37
  blockLocalPlain: ReadonlySet<string> = new Set(),
38
+ /* Component signals read through `$$readCell(name)` — every `linked` and every async
39
+ `computed` (see `desugarSignals`). A nearer lexical binding of the same name shadows
40
+ them like any other signal. */
41
+ cellReadNames: ReadonlySet<string> = new Set(),
56
42
  ): ts.TransformerFactory<ts.SourceFile> {
57
43
  /* The signal names that a nested binding can shadow — only these matter for
58
44
  scope tracking, so we ignore every other local binding. */
@@ -60,6 +46,7 @@ export function signalRefsTransformer(
60
46
  ...stateNames,
61
47
  ...derivedNames,
62
48
  ...computedNames,
49
+ ...cellReadNames,
63
50
  ...blockLocal,
64
51
  /* `scope` is the author-facing reactive entry (`scope().state(...)`), lowered to the
65
52
  reserved `$$scope` import so a user variable named `scope` can never collide — but
@@ -109,6 +96,7 @@ export function signalRefsTransformer(
109
96
  derivedNames,
110
97
  computedNames,
111
98
  blockLocal,
99
+ cellReadNames,
112
100
  )
113
101
  if (replacement !== undefined) {
114
102
  return ts.factory.createPropertyAssignment(node.name.text, replacement)
@@ -121,6 +109,7 @@ export function signalRefsTransformer(
121
109
  derivedNames,
122
110
  computedNames,
123
111
  blockLocal,
112
+ cellReadNames,
124
113
  )
125
114
  if (replacement !== undefined) {
126
115
  return replacement
@@ -338,6 +327,7 @@ function referenceFor(
338
327
  derivedNames: ReadonlySet<string>,
339
328
  computedNames: ReadonlySet<string>,
340
329
  blockLocal: ReadonlySet<string> = new Set(),
330
+ cellReadNames: ReadonlySet<string> = new Set(),
341
331
  ): ts.Expression | undefined {
342
332
  if (blockLocal.has(name)) {
343
333
  return ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(name), 'value')
@@ -347,6 +337,15 @@ function referenceFor(
347
337
  if (name === 'scope') {
348
338
  return ts.factory.createIdentifier('$$scope')
349
339
  }
340
+ /* A `linked` / async `computed` cell → `$$readCell(name)`: one read shape that peeks an
341
+ async cell and reads `.value` off a sync one. */
342
+ if (cellReadNames.has(name)) {
343
+ return ts.factory.createCallExpression(
344
+ ts.factory.createIdentifier('$$readCell'),
345
+ undefined,
346
+ [ts.factory.createIdentifier(name)],
347
+ )
348
+ }
350
349
  if (stateNames.has(name)) {
351
350
  return ts.factory.createPropertyAccessExpression(
352
351
  ts.factory.createIdentifier('$$model'),
@@ -0,0 +1,65 @@
1
+ import { classifyInterpolationType } from './classifyInterpolationType.ts'
2
+ import { createShadowProgram, type ShadowProgram } from './createShadowProgram.ts'
3
+ import { nodeAtShadowOffset } from './nodeAtShadowOffset.ts'
4
+ import { shadowNaming } from './shadowNaming.ts'
5
+ import { sourceToShadowOffset } from './sourceToShadowOffset.ts'
6
+ import type { SeedTypeClassifier } from './types/SeedTypeClassifier.ts'
7
+
8
+ /*
9
+ Builds the type-directed SEED classifier for one `.abide` file (ADR-0023) — the cell
10
+ transform's counterpart to `interpolationClassifierForRoot`, resolving a `computed`/`linked`
11
+ seed's checker type through the SAME warm shadow program (kept once per project root, ADR-0019
12
+ Stage B). Shares the `cache` the interpolation classifier populates, so no second program is
13
+ built: whichever classifier is requested first for a root warms it, the other reuses it.
14
+
15
+ A `computed(SEED)` declaration is emitted into the shadow's script region with `SEED` wrapped
16
+ in parens (verbatim if the callee is unrecognized, or projected as `(SEED)()` when recognized
17
+ as `state.computed` — either way the seed sub-expression maps back to source and resolves its
18
+ own type), so the identical `sourceToShadowOffset → nodeAtShadowOffset → classifyInterpolation
19
+ Type` pipeline the interpolation half uses resolves a seed with no node-finder variant.
20
+
21
+ FAIL-OPEN, but NOT to `'sync'`: if the program can't build, the file has no shadow, the seed's
22
+ location doesn't map, no node is found, or the checker throws, the classifier is absent /
23
+ returns `undefined` — the signal the caller reads as "degrade to `isBareCallComputed`", NOT as
24
+ "the seed is sync". Collapsing failure to `'sync'` (as the interpolation classifier does) would
25
+ mis-route a failed stream seed to the lazy `derive` slot instead of today's syntax heuristic.
26
+ */
27
+ export function seedTypeClassifierForRoot(
28
+ cache: Map<string, ShadowProgram | undefined>,
29
+ root: string,
30
+ abidePath: string,
31
+ ): SeedTypeClassifier | undefined {
32
+ if (!cache.has(root)) {
33
+ try {
34
+ cache.set(root, createShadowProgram(root))
35
+ } catch {
36
+ cache.set(root, undefined)
37
+ }
38
+ }
39
+ const shadowProgram = cache.get(root)
40
+ if (shadowProgram === undefined) {
41
+ return undefined
42
+ }
43
+ const { program, shadows } = shadowProgram
44
+ const shadowFile = program.getSourceFile(shadowNaming.suffixed(abidePath))
45
+ if (shadowFile === undefined) {
46
+ return undefined
47
+ }
48
+ const checker = program.getTypeChecker()
49
+ return (loc, code) => {
50
+ try {
51
+ const mappings = shadows.get(abidePath)?.mappings ?? []
52
+ const offset = sourceToShadowOffset(mappings, loc)
53
+ if (offset === undefined) {
54
+ return undefined
55
+ }
56
+ const node = nodeAtShadowOffset(shadowFile, offset, code.length)
57
+ if (node === undefined) {
58
+ return undefined
59
+ }
60
+ return classifyInterpolationType(checker.getTypeAtLocation(node), node, checker)
61
+ } catch {
62
+ return undefined
63
+ }
64
+ }
65
+ }
@@ -0,0 +1,47 @@
1
+ import type ts from 'typescript'
2
+ import { classifyInterpolationType } from './classifyInterpolationType.ts'
3
+ import { nodeAtShadowOffset } from './nodeAtShadowOffset.ts'
4
+ import { shadowNaming } from './shadowNaming.ts'
5
+ import { sourceToShadowOffset } from './sourceToShadowOffset.ts'
6
+ import type { CompiledShadow } from './types/CompiledShadow.ts'
7
+ import type { InterpolationClassifier } from './types/InterpolationClassifier.ts'
8
+
9
+ /*
10
+ Builds the type-directed interpolation classifier for one `.abide` file against an already-built
11
+ shadow `program` (its `shadows` map carries each file's source→shadow `mappings`). Given an
12
+ interpolation's absolute source offset and text, it maps into shadow coordinates, finds the emitted
13
+ expression node, and reads its checker type — the shared core behind both `interpolationClassifier
14
+ ForRoot` (the runtime lowering, over `createShadowProgram`) and the type-check shadow's own peek-wrap
15
+ (over the check/LSP program). The classifier reads a VERBATIM shadow — one whose interpolations were
16
+ NOT peek-wrapped — so `getFoo()` still types as `Promise<…>` and classifies as `promise`.
17
+
18
+ FAIL-OPEN: no shadow file for the path ⇒ `undefined` (caller degrades to no wrap / plain binding);
19
+ the returned closure wraps its body so ANY throw (missing node, checker hiccup) returns `'sync'`.
20
+ */
21
+ export function shadowInterpolationClassifier(
22
+ program: ts.Program,
23
+ shadows: Map<string, CompiledShadow>,
24
+ abidePath: string,
25
+ ): InterpolationClassifier | undefined {
26
+ const shadowFile = program.getSourceFile(shadowNaming.suffixed(abidePath))
27
+ if (shadowFile === undefined) {
28
+ return undefined
29
+ }
30
+ const checker = program.getTypeChecker()
31
+ return (loc, code) => {
32
+ try {
33
+ const mappings = shadows.get(abidePath)?.mappings ?? []
34
+ const offset = sourceToShadowOffset(mappings, loc)
35
+ if (offset === undefined) {
36
+ return 'sync'
37
+ }
38
+ const node = nodeAtShadowOffset(shadowFile, offset, code.length)
39
+ if (node === undefined) {
40
+ return 'sync'
41
+ }
42
+ return classifyInterpolationType(checker.getTypeAtLocation(node), node, checker)
43
+ } catch {
44
+ return 'sync'
45
+ }
46
+ }
47
+ }
@@ -0,0 +1,20 @@
1
+ import type ts from 'typescript'
2
+
3
+ /* The compiler-option fields that change how a source file parses or binds — the only inputs
4
+ (besides the text) that make two parses of the same path differ, so the `cachedSourceFile`
5
+ key folds them in to keep two projects with different targets/libs from sharing a file. */
6
+ export function sourceFileOptionsSignature(options: ts.CompilerOptions): string {
7
+ return JSON.stringify([
8
+ options.target,
9
+ options.module,
10
+ options.moduleDetection,
11
+ options.jsx,
12
+ options.jsxImportSource,
13
+ options.useDefineForClassFields,
14
+ options.alwaysStrict,
15
+ options.strict,
16
+ options.verbatimModuleSyntax,
17
+ options.isolatedModules,
18
+ options.lib,
19
+ ])
20
+ }
@@ -0,0 +1,107 @@
1
+ import { BLOCK_CONNECTORS, BLOCK_OPENERS } from './BLOCK_KEYWORDS.ts'
2
+ import type { SemanticToken } from './types/SemanticToken.ts'
3
+
4
+ /*
5
+ Semantic tokens for a `{#…}`/`{:…}`/`{/…}` control-block head, driven by the parse
6
+ walk (`readBlockToken`) rather than a raw source regex — so the SAME grammar that
7
+ builds the block tree colors it, and a `{#for}` sitting inside a `{expr}`/string/
8
+ backtick region (which the walk skips as opaque expression text) is never miscolored.
9
+ Emits an `operator` token for the `{`+sigil opener and a `keyword` token for the
10
+ block word, plus the `of`/`by` connectors inside a `{#for …}` head. A keyword
11
+ allowlist after the sigil keeps an arbitrary `{:foo}` uncolored (nothing emitted).
12
+ */
13
+ /* The scan vocabulary, derived from the shared BLOCK_KEYWORDS source of truth (plus the
14
+ `for await` async-each phrase) so it can't drift from the parser. Sorted longest-first so
15
+ the match takes the whole phrase — `for await` beats `for`, `else if` beats `else` —
16
+ since a shorter prefix checked first would win otherwise. */
17
+ const BLOCK_KEYWORDS = [...BLOCK_OPENERS, ...BLOCK_CONNECTORS, 'for await'].sort(
18
+ (a, b) => b.length - a.length,
19
+ )
20
+
21
+ const WORD_CHAR = /\w/
22
+
23
+ /*
24
+ Emits the head tokens for a block whose `{` sits at `braceStart`, whose sigil is
25
+ `#`/`:`/`/`, and whose first directive word begins at `keywordStart` (already past the
26
+ leading whitespace `{# for}` allows). Matches the longest allowlist keyword at
27
+ `keywordStart` requiring a trailing word boundary; if no keyword matches, emits NOTHING
28
+ (not even the opener) — mirroring the old regex, which only colored an allowlist match.
29
+ */
30
+ export function structuralHeadTokens(
31
+ source: string,
32
+ braceStart: number,
33
+ sigil: string,
34
+ keywordStart: number,
35
+ ): SemanticToken[] {
36
+ let matched: string | undefined
37
+ for (const keyword of BLOCK_KEYWORDS) {
38
+ if (
39
+ source.startsWith(keyword, keywordStart) &&
40
+ !WORD_CHAR.test(source.charAt(keywordStart + keyword.length))
41
+ ) {
42
+ matched = keyword
43
+ break
44
+ }
45
+ }
46
+ if (matched === undefined) {
47
+ return []
48
+ }
49
+ const tokens: SemanticToken[] = [
50
+ { start: braceStart, length: 2, type: 'operator', modifiers: [] },
51
+ { start: keywordStart, length: matched.length, type: 'keyword', modifiers: [] },
52
+ ]
53
+ /* A `{#for …}` head carries abide-only connectors `of`/`by` that the shadow lowers
54
+ away (`of`) or never emits (`by`), so color them here. */
55
+ if (sigil === '#' && (matched === 'for' || matched === 'for await')) {
56
+ forHeadConnectors(source, keywordStart + matched.length, tokens)
57
+ }
58
+ return tokens
59
+ }
60
+
61
+ /*
62
+ Scans a `{#for …}` head from `from` to its closing `}`, pushing `keyword` tokens for the
63
+ `of`/`by` connectors at brace depth 0 — skipping any `of`/`by` nested in a destructure,
64
+ call, or string so an identifier or object key is never miscolored.
65
+ */
66
+ function forHeadConnectors(source: string, from: number, tokens: SemanticToken[]): void {
67
+ let depth = 0
68
+ let cursor = from
69
+ while (cursor < source.length) {
70
+ const char = source.charAt(cursor)
71
+ if (char === '"' || char === "'" || char === '`') {
72
+ cursor += 1
73
+ while (cursor < source.length && source.charAt(cursor) !== char) {
74
+ if (source.charAt(cursor) === '\\') {
75
+ cursor += 1
76
+ }
77
+ cursor += 1
78
+ }
79
+ } else if (char === '{' || char === '(' || char === '[') {
80
+ depth += 1
81
+ } else if (char === ')' || char === ']') {
82
+ depth -= 1
83
+ } else if (char === '}') {
84
+ if (depth === 0) {
85
+ break
86
+ }
87
+ depth -= 1
88
+ } else if (depth === 0 && (char === 'o' || char === 'b')) {
89
+ const word = source.startsWith('of', cursor)
90
+ ? 'of'
91
+ : source.startsWith('by', cursor)
92
+ ? 'by'
93
+ : undefined
94
+ const isWordBoundary = (offset: number): boolean => /\s/.test(source.charAt(offset))
95
+ if (
96
+ word !== undefined &&
97
+ isWordBoundary(cursor - 1) &&
98
+ isWordBoundary(cursor + word.length)
99
+ ) {
100
+ tokens.push({ start: cursor, length: word.length, type: 'keyword', modifiers: [] })
101
+ cursor += word.length
102
+ continue
103
+ }
104
+ }
105
+ cursor += 1
106
+ }
107
+ }
@@ -0,0 +1,21 @@
1
+ import { parseTemplateRecovering } from './parseTemplateRecovering.ts'
2
+ import type { SemanticToken } from './types/SemanticToken.ts'
3
+
4
+ /*
5
+ The LSP's markup + structural highlighting for a full `.abide` component, driven by
6
+ the ONE parse walk (`parseTemplateRecovering`) instead of a pair of hand-rolled
7
+ lexers — so the same grammar that builds the tree colors it, and no coloring can
8
+ drift from what the parser accepts. Runs over the FULL source at `baseOffset 0` with
9
+ token collection on: element/component tag names (`tag`/`type`), attribute names
10
+ (`attribute`), quoted/unquoted values (`string`), comments (`comment`), the
11
+ `<`/`>`/`=`/`/` punctuation (`operator`), and the `{#…}`/`{:…}`/`{/…}` block framing
12
+ (`operator` + `keyword`). The leading `<script>…</script>` reads through the same
13
+ `readElement` path as a nested script, so its open+close tags color with no separate
14
+ leading-region lexer. `{…}` expression interiors and raw `<script>`/`<style>` bodies
15
+ are skipped — those are the type-checking shadow's job. The parse's `nodes` and
16
+ `diagnostics` (including the spurious leading-script diagnostics a full-source parse
17
+ raises) are discarded; only `.tokens` is used.
18
+ */
19
+ export function templateSemanticTokens(source: string): SemanticToken[] {
20
+ return parseTemplateRecovering(source, 0, true).tokens
21
+ }
@@ -0,0 +1,15 @@
1
+ /* The regex that isolates a `.abide` file's leading `<script>` block, capturing its body (`[1]`) —
2
+ the single source `analyzeComponent`, `compileShadow`, and `templateStartOffset` all match on to
3
+ split script from template, so the three can never drift. Stateless (no `/g`), safe to share. */
4
+ export const LEADING_SCRIPT = /^\s*<script[^>]*>([\s\S]*?)<\/script>/
5
+
6
+ /*
7
+ The source offset where the template markup begins — just past the closing `</script>` of a
8
+ leading `<script>` block, or `0` when the file is template-only. A component's template
9
+ interpolations all map to source offsets at or after this point, so it doubles as the boundary
10
+ that separates template expressions from `<script>` code.
11
+ */
12
+ export function templateStartOffset(source: string): number {
13
+ const leadingScript = source.match(LEADING_SCRIPT)
14
+ return leadingScript ? (leadingScript.index ?? 0) + leadingScript[0].length : 0
15
+ }
@@ -16,8 +16,9 @@ export type TryPlan = {
16
16
  finallyChildren: TemplateNode[]
17
17
  /* No catch → a throw propagates to the enclosing boundary (re-throw / `undefined` thunk). */
18
18
  hasCatch: boolean
19
- /* The catch branch's binding (`catchAs`, `plain`); empty when no catch branch. The
20
- guarded and `finally` branches bind nothing. */
19
+ /* The catch branch's binding (`catchAs`, `reactive`); empty when no catch branch. The
20
+ error is a `.value` cell so a catch→catch swap updates `err` in place. The guarded and
21
+ `finally` branches bind nothing. */
21
22
  catchBindings: Binding[]
22
23
  }
23
24
 
@@ -31,6 +32,6 @@ export function tryPlan(node: Extract<TemplateNode, { kind: 'try' }>): TryPlan {
31
32
  catchAs,
32
33
  finallyChildren: finallyBranch?.children ?? [],
33
34
  hasCatch: catchBranch !== undefined,
34
- catchBindings: catchBinding(catchAs, catchBranch !== undefined),
35
+ catchBindings: catchBinding(catchAs, catchBranch !== undefined, true),
35
36
  }
36
37
  }
@@ -24,6 +24,10 @@ export type AnalyzedComponent = {
24
24
  stateNames: Set<string>
25
25
  derivedNames: Set<string>
26
26
  computedNames: Set<string>
27
+ /* Component signals read through `$$readCell(name)`: every `linked` and every async
28
+ `computed`. Threaded to both back-ends so a template reference (`{draft}`) lowers to
29
+ the unified cell read consistently on client and server. */
30
+ cellReadNames: Set<string>
27
31
  nodes: TemplateNode[]
28
32
  /* One entry per non-empty `<style>` in the template (in source order): the scope
29
33
  attribute its covered elements carry (annotated onto `nodes`) and the scoped
@@ -0,0 +1,19 @@
1
+ import type { LiftPosition } from '../liftAsyncSubExpressions.ts'
2
+
3
+ /* One async-liftable interpolation field of a template node — the shared reading both compile
4
+ front-ends drive their async lowering from (the block-Plan pattern of `ifPlan`/`awaitPlan`,
5
+ applied to interpolations instead of block structure). `code`/`loc` are the authored expression
6
+ and its absolute source offset; `position` is its value/content position (which feeds the shared
7
+ sub-expression walk's `each`-forbids-`AsyncIterable` rule). `subject` marks a control-flow test
8
+ (`{#if}` / `{:elseif}` / `{#switch}`) whose WHOLE expression lifting to one cell makes the block
9
+ hold while the cell is pending. `write` reseeds the field with the runtime's rewritten reference
10
+ and `setAsyncSubject` records the subject verdict — both are no-ops for a READER like the shadow,
11
+ which needs only `code`/`loc`/`position` to peek-wrap its projection. */
12
+ export type AsyncInterpolationField = {
13
+ code: string
14
+ loc: number
15
+ position: LiftPosition
16
+ subject: boolean
17
+ write: (code: string) => void
18
+ setAsyncSubject: (asyncSubject: boolean) => void
19
+ }
@@ -0,0 +1,12 @@
1
+ import type { InterpolationKind } from './InterpolationKind.ts'
2
+
3
+ /*
4
+ Classifies a text-position `{expr}` interpolation from its checker type, keyed by
5
+ the expression's source offset (`loc`) and verbatim `code`. Built by the bundler
6
+ plugin over a warm shadow program (see `abideUiPlugin`) and threaded, OPTIONAL,
7
+ through the compile front-end: when absent the pipeline behaves exactly as before
8
+ (every interpolation binds as a plain value). Always fail-open — a resolution
9
+ failure returns `'sync'`, never throws — so a type hiccup degrades to today's
10
+ behavior instead of breaking the build.
11
+ */
12
+ export type InterpolationClassifier = (loc: number, code: string) => InterpolationKind
@@ -0,0 +1,7 @@
1
+ /*
2
+ How a `{expr}` template interpolation resolves at runtime, from its checker type:
3
+ `promise` (thenable — awaited), `asyncIterable` (drained over time), or `sync`
4
+ (bound as a plain value). Stage A of type-directed interpolation lowering
5
+ classifies into this; later stages pick the binding back-end from it.
6
+ */
7
+ export type InterpolationKind = 'promise' | 'asyncIterable' | 'sync'
@@ -0,0 +1,8 @@
1
+ /*
2
+ A parser-local diagnostic — deliberately smaller than `AbideDiagnostic`, which
3
+ additionally needs the `file` + `category` the parser cannot know. `start` is the
4
+ absolute, `baseOffset`-relative offset (the same coordinate space every node `loc`
5
+ uses, so it round-trips through the shadow `mappings`); `length` is 0 for a point
6
+ diagnostic. Callers (`collectAbideDiagnostics`) adapt it to `AbideDiagnostic`.
7
+ */
8
+ export type ParseDiagnostic = { message: string; start: number; length: number }
@@ -0,0 +1,15 @@
1
+ import type { InterpolationKind } from './InterpolationKind.ts'
2
+
3
+ /*
4
+ Classifies a `computed`/`linked` SEED expression from its checker type, keyed by the
5
+ seed's source offset (`loc`) and verbatim `code` — the cell-transform counterpart of
6
+ `InterpolationClassifier` (ADR-0023). Built by the bundler plugin over the SAME warm
7
+ shadow program and threaded, OPTIONAL, through the compile front-end to `desugarSignals`.
8
+
9
+ Unlike `InterpolationClassifier`, a resolution failure returns `undefined`, NOT `'sync'`:
10
+ for a seed, fail-open means degrade to today's `isBareCallComputed` syntax heuristic, so a
11
+ failure must be DISTINGUISHABLE from a genuine `sync` type (which routes to the lazy
12
+ `derive` slot). Absent classifier ⇒ every seed resolves `undefined` ⇒ exactly today's
13
+ behavior.
14
+ */
15
+ export type SeedTypeClassifier = (loc: number, code: string) => InterpolationKind | undefined
@@ -46,7 +46,17 @@ export type TemplateNode =
46
46
  keyLoc?: number
47
47
  indexLoc?: number
48
48
  }
49
- | { kind: 'if'; condition: string; children: TemplateNode[]; loc?: number }
49
+ | {
50
+ kind: 'if'
51
+ condition: string
52
+ children: TemplateNode[]
53
+ loc?: number
54
+ /* `condition` is a bare async subject — the WHOLE test was one promise/stream that
55
+ `lowerAsyncInterpolations` lifted to a single peek-cell (`{#if getX()}`). The binary
56
+ back-ends then read its pending facet so a still-loading subject renders NO branch,
57
+ not the falsy `{:else}`. Unset (or on an `{:elseif}` chain) → the plain truthy test. */
58
+ asyncSubject?: boolean
59
+ }
50
60
  | {
51
61
  kind: 'await'
52
62
  promise: string
@@ -77,11 +87,29 @@ export type TemplateNode =
77
87
  loc?: number
78
88
  /* Each authored attribute as a prop. A `spread` entry (`{...code}`) carries no
79
89
  `name`; its keys merge in at runtime (`mergeProps`/`spreadProps`). `nameLoc` is
80
- the prop name's source offset — the anchor for an excess-prop diagnostic. */
81
- props: { name: string; code: string; loc?: number; nameLoc?: number; spread?: boolean }[]
90
+ the prop name's source offset — the anchor for an excess-prop diagnostic. `bind`
91
+ marks a `bind:<name>={target}` two-way prop: `code` is the writable target (an
92
+ lvalue or a `{ get, set }` accessor), passed with a write-back channel so the
93
+ child can push changes upstream (`bindProp`/`bindableProp`). */
94
+ props: {
95
+ name: string
96
+ code: string
97
+ loc?: number
98
+ nameLoc?: number
99
+ spread?: boolean
100
+ bind?: true
101
+ }[]
82
102
  children: TemplateNode[]
83
103
  }
84
- | { kind: 'switch'; subject: string; children: TemplateNode[]; loc?: number }
104
+ | {
105
+ kind: 'switch'
106
+ subject: string
107
+ children: TemplateNode[]
108
+ loc?: number
109
+ /* `subject` is a bare async subject (the whole expression lifted to one peek-cell) —
110
+ a still-loading subject selects no case (not even the default) until it settles. */
111
+ asyncSubject?: boolean
112
+ }
85
113
  /* A branch of a `<template switch>` (`match` set) or `<template if>` chain. Inside an
86
114
  `if`, `<template elseif={c}>` sets `condition` (match-less, truthy-tested), and
87
115
  `<template else>` leaves both unset (the default). `loc` points at whichever
@@ -92,6 +120,10 @@ export type TemplateNode =
92
120
  condition?: string
93
121
  children: TemplateNode[]
94
122
  loc?: number
123
+ /* An `{:elseif}` whose `condition` is a bare async subject (the whole test lifted to one
124
+ peek-cell) — the cond-chain holds at this branch while the cell is pending, resuming the
125
+ top-down walk (skip it / match it / fall to the next branch) once it settles. */
126
+ asyncSubject?: boolean
95
127
  }
96
128
  /* A `{#snippet row(item)}` snippet: a named, scope-capturing builder declared
97
129
  once and called like a function (`{row(item)}`). `params` is the raw
@@ -0,0 +1,13 @@
1
+ /*
2
+ A template interpolation sitting in a NON-content value position — where a
3
+ `Promise`/`AsyncIterable` can't be rendered over time and would silently stringify
4
+ to `[object Promise]` (Stage E of type-directed lowering, ADR-0019). `loc`/`code`
5
+ are the expression's source offset and verbatim text (the classifier's key), and
6
+ `position` names the position for the diagnostic. `for await` is the sanctioned
7
+ `{#for await}` iterable — the one value position where an `AsyncIterable` is allowed.
8
+ */
9
+ export type ValuePositionInterpolation = {
10
+ loc: number
11
+ code: string
12
+ position: 'attribute' | 'if' | 'switch' | 'each' | 'for await'
13
+ }