@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,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,12 +46,8 @@ export function signalRefsTransformer(
60
46
  ...stateNames,
61
47
  ...derivedNames,
62
48
  ...computedNames,
49
+ ...cellReadNames,
63
50
  ...blockLocal,
64
- /* The reserved slot reader (`{#if children}` → `$props?.$children`) is rewritten
65
- through `referenceFor` like a signal, so it inherits the same lexical-scope
66
- tracking: a `{#snippet row(children)}` arg, a `{#for children of …}` item, a
67
- script param named `children` re-binds it for that subtree and is left untouched. */
68
- 'children',
69
51
  /* `scope` is the author-facing reactive entry (`scope().state(...)`), lowered to the
70
52
  reserved `$$scope` import so a user variable named `scope` can never collide — but
71
53
  shadowably: a `const scope`/param `scope` re-binds it for that subtree and is left
@@ -114,6 +96,7 @@ export function signalRefsTransformer(
114
96
  derivedNames,
115
97
  computedNames,
116
98
  blockLocal,
99
+ cellReadNames,
117
100
  )
118
101
  if (replacement !== undefined) {
119
102
  return ts.factory.createPropertyAssignment(node.name.text, replacement)
@@ -126,6 +109,7 @@ export function signalRefsTransformer(
126
109
  derivedNames,
127
110
  computedNames,
128
111
  blockLocal,
112
+ cellReadNames,
129
113
  )
130
114
  if (replacement !== undefined) {
131
115
  return replacement
@@ -332,34 +316,36 @@ function functionParameters(node: ts.Node): ts.NodeArray<ts.ParameterDeclaration
332
316
 
333
317
  /* `$$model.<name>` for a state binding, `<name>()` for a computed doc-slot (the
334
318
  string-free reader `scope().derive` returns), `<name>.value` for a runtime cell
335
- (linked / lens / transform-state), `$props?.$children` for the reserved slot reader,
336
- else undefined. A `blockLocal` binding shadows any same-named component signal — it is
337
- a nearer lexical scope — so it derefs as a cell (`<name>.value`) regardless of a
338
- colliding `state`/`computed`/`derived`, and ahead of the `children` slot reader. */
319
+ (linked / lens / transform-state), else undefined. A `blockLocal` binding shadows
320
+ any same-named component signal — it is a nearer lexical scope — so it derefs as a
321
+ cell (`<name>.value`) regardless of a colliding `state`/`computed`/`derived`.
322
+ `children` is an ordinary destructured prop now — it rewrites via the `derivedNames`
323
+ branch below like any other, with no special case. */
339
324
  function referenceFor(
340
325
  name: string,
341
326
  stateNames: ReadonlySet<string>,
342
327
  derivedNames: ReadonlySet<string>,
343
328
  computedNames: ReadonlySet<string>,
344
329
  blockLocal: ReadonlySet<string> = new Set(),
330
+ cellReadNames: ReadonlySet<string> = new Set(),
345
331
  ): ts.Expression | undefined {
346
332
  if (blockLocal.has(name)) {
347
333
  return ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(name), 'value')
348
334
  }
349
- /* `{children()}` is a slot NODE the parser handles; any other bare `children` read
350
- (notably `{#if children}`) is the reserved reader. `?.` guards a propless mount. */
351
- if (name === 'children') {
352
- return ts.factory.createPropertyAccessChain(
353
- ts.factory.createIdentifier('$props'),
354
- ts.factory.createToken(ts.SyntaxKind.QuestionDotToken),
355
- ts.factory.createIdentifier('$children'),
356
- )
357
- }
358
335
  /* The author-facing reactive entry `scope` lowers to its reserved `$$scope` import (the
359
336
  value read only — a `.scope` member or a shadowing local is never reached here). */
360
337
  if (name === 'scope') {
361
338
  return ts.factory.createIdentifier('$$scope')
362
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
+ }
363
349
  if (stateNames.has(name)) {
364
350
  return ts.factory.createPropertyAccessExpression(
365
351
  ts.factory.createIdentifier('$$model'),
@@ -9,9 +9,7 @@ export type ReactivePrimitive = 'state' | 'linked' | 'computed' | 'effect' | 'wa
9
9
 
10
10
  /* The `abide/ui/*` specifier each importable reactive primitive is published at,
11
11
  mapped to its canonical name. Built from the package name so a rename is one edit.
12
- `linked`/`computed` are members of `state`, not standalone imports; `props` is
13
- ambient sugar today (no module) — kept here so a future `abide/ui/props` resolves
14
- with no code change. */
12
+ `linked`/`computed` are members of `state`, not standalone imports. */
15
13
  const REACTIVE_SPECIFIERS: Record<string, ReactivePrimitive> = {
16
14
  [`${ABIDE_PACKAGE_NAME}/ui/state`]: 'state',
17
15
  [`${ABIDE_PACKAGE_NAME}/ui/effect`]: 'effect',
@@ -39,6 +37,7 @@ export const NESTED_REACTIVE_BINDINGS: ReactiveImportBindings = {
39
37
  ['state', 'state'],
40
38
  ['effect', 'effect'],
41
39
  ['watch', 'watch'],
40
+ ['props', 'props'],
42
41
  ]),
43
42
  stateRoots: new Set(['state']),
44
43
  }
@@ -70,9 +69,7 @@ export function reactiveImportBindings(source: ts.SourceFile): ReactiveImportBin
70
69
  return { direct, stateRoots }
71
70
  }
72
71
 
73
- /* The reactive primitive a call's callee resolves to, or undefined. A bare `props`
74
- identifier is always the ambient prop reader — the one primitive with no runtime module
75
- (pure compiler sugar), so it resolves with no import. Every other bare identifier
72
+ /* The reactive primitive a call's callee resolves to, or undefined. Every bare identifier
76
73
  resolves through the direct import bindings (alias-safe); a `stateRoot.linked` /
77
74
  `.computed` member call resolves off a local bound to `state`. Every other callee is
78
75
  undefined (a user's own function, an unrelated member access). */
@@ -81,7 +78,7 @@ export function resolveReactiveExport(
81
78
  bindings: ReactiveImportBindings,
82
79
  ): ReactivePrimitive | undefined {
83
80
  if (ts.isIdentifier(callee)) {
84
- return callee.text === 'props' ? 'props' : bindings.direct.get(callee.text)
81
+ return bindings.direct.get(callee.text)
85
82
  }
86
83
  if (
87
84
  ts.isPropertyAccessExpression(callee) &&
@@ -79,12 +79,35 @@ function scopeSelector(selector: string, attribute: string): string {
79
79
  if (selector === '') {
80
80
  return selector
81
81
  }
82
- const match = selector.match(/^(.*?)([^\s>+~]+)$/)
83
- if (match === null) {
82
+ /* The last compound begins just after the final combinator (whitespace / `>` / `+` / `~`)
83
+ at bracket depth zero. Bracket-aware so a space inside a quoted attribute value
84
+ (`[data-label="Read more"]`) is NOT mistaken for a descendant combinator — a plain
85
+ `/\s/` split there desyncs pseudoIndex's bracket count and drops the whole rule. */
86
+ let depth = 0
87
+ let lastCompoundStart = 0
88
+ for (let index = 0; index < selector.length; index += 1) {
89
+ const char = selector[index] as string
90
+ if (char === '[') {
91
+ depth += 1
92
+ } else if (char === ']') {
93
+ depth -= 1
94
+ } else if (
95
+ depth === 0 &&
96
+ (char === ' ' ||
97
+ char === '\t' ||
98
+ char === '\n' ||
99
+ char === '>' ||
100
+ char === '+' ||
101
+ char === '~')
102
+ ) {
103
+ lastCompoundStart = index + 1
104
+ }
105
+ }
106
+ const prefix = selector.slice(0, lastCompoundStart)
107
+ const last = selector.slice(lastCompoundStart)
108
+ if (last === '') {
84
109
  return `${selector}[${attribute}]`
85
110
  }
86
- const prefix = match[1] ?? ''
87
- const last = match[2] ?? ''
88
111
  const pseudo = pseudoIndex(last)
89
112
  const scopedLast =
90
113
  pseudo === -1
@@ -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