@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
@@ -3,7 +3,7 @@ import type { effect } from '../effect.ts'
3
3
  import type { linked } from '../linked.ts'
4
4
  import type { Patch } from '../runtime/types/Patch.ts'
5
5
  import type { state } from '../state.ts'
6
- import type { SyncTransport } from './SyncTransport.ts'
6
+ import type { trackedComputed } from '../trackedComputed.ts'
7
7
 
8
8
  /*
9
9
  A lexical scope: the unit that owns a region's reactive data, its lifetime, and
@@ -14,21 +14,14 @@ surface is the imported `state`/`state.linked`/`state.computed`/`effect` (see
14
14
  scope (`$$scope().derive`/`.linked`/`.effect`, `state.share`/`.shared` → `share`/
15
15
  `shared`). The data surface MIRRORS `Doc` (read/replace/add/remove/derive/apply/
16
16
  snapshot) so the compiler can target a scope as a component's data binding directly;
17
- it nests (`child`/`root`) and passes values down the tree as context (`share`/`shared`).
17
+ it passes values down the tree as context (`share`/`shared`).
18
18
 
19
- The reactive primitives and capabilities remain on this internal shape because the
20
- lowered runtime calls them (`$$scope().linked(...)`, generated undo/persist) — they are
21
- withdrawn from the AUTHOR-facing public surface (docs/examples), not the runtime object.
22
- Capabilities route where the scope's changes go: `record()` to an undo journal,
23
- `persist()` to durable storage, `broadcast()` to peers — declared once, then `undo`/
24
- `redo` act on a recorded scope. The `history`/`persist`/`sync` helpers it composes are
25
- internal.
19
+ The reactive primitives remain on this internal shape because the lowered runtime calls
20
+ them (`$$scope().linked(...)`) — they are withdrawn from the AUTHOR-facing public surface
21
+ (docs/examples), not the runtime object.
26
22
  */
27
23
  export type Scope = {
28
24
  readonly id: string
29
- /* Dev-only display name (the host component/element it mounted into) for the
30
- inspector's Reactive tab; undefined for SSR/detached/child scopes. */
31
- readonly label?: string
32
25
  readonly parent: Scope | undefined
33
26
  /* data — mirrors Doc */
34
27
  read: <T>(path: string) => T
@@ -54,15 +47,15 @@ export type Scope = {
54
47
  state: typeof state
55
48
  linked: typeof linked
56
49
  computed: typeof computed
50
+ /* The eager stream-classifying read-only computed a bare-call `computed(getStream())`
51
+ lowers to — auto-tracks a stream/promise producer, falls back to a lazy computed. */
52
+ trackedComputed: typeof trackedComputed
57
53
  effect: typeof effect
58
54
  /* Adopts a teardown into this scope's lifetime — the build's reactivity stopper
59
55
  (effects/listeners), run first on `dispose` (before children, before capabilities).
60
56
  Internal: the mount core registers the build's disposer here so a component has ONE
61
57
  teardown (`dispose`) rather than a separate stop + dispose composed at every site. */
62
58
  own: (dispose: () => void) => void
63
- /* tree */
64
- child: (initial?: unknown) => Scope
65
- root: () => Scope
66
59
  /* context — values shared DOWN the tree (not in the reactive doc, which doesn't
67
60
  inherit): `share` puts a named value on this scope; `shared` reads the closest
68
61
  ancestor (self included) that has the key, undefined if none. The value is held
@@ -70,15 +63,6 @@ export type Scope = {
70
63
  plain object snapshot. */
71
64
  share: (key: string, value: unknown) => void
72
65
  shared: <T>(key: string) => T | undefined
73
- /* capabilities — enable where the scope's changes go */
74
- record: (options?: { limit?: number }) => void
75
- persist: (key?: string) => void
76
- broadcast: (transport: SyncTransport) => void
77
- /* undo/redo — act on a recorded scope */
78
- undo: () => void
79
- redo: () => void
80
- canUndo: () => boolean
81
- canRedo: () => boolean
82
66
  /* lifetime */
83
67
  dispose: () => void
84
68
  }
@@ -1,8 +1,8 @@
1
1
  import { cache } from '../shared/cache.ts'
2
2
  import { REMOTE_FUNCTION } from '../shared/REMOTE_FUNCTION.ts'
3
3
  import type { CacheOnContext } from '../shared/types/CacheOnContext.ts'
4
+ import type { NamedAsyncIterable } from '../shared/types/NamedAsyncIterable.ts'
4
5
  import type { RemoteFunction } from '../shared/types/RemoteFunction.ts'
5
- import type { Subscribable } from '../shared/types/Subscribable.ts'
6
6
  import { effect } from './effect.ts'
7
7
  import { generationGuard } from './runtime/generationGuard.ts'
8
8
  import type { EffectResult } from './runtime/types/EffectResult.ts'
@@ -40,7 +40,7 @@ export function watch(
40
40
  handler: (values: unknown[]) => void,
41
41
  ): () => void
42
42
  export function watch<Frame>(
43
- source: Subscribable<Frame>,
43
+ source: NamedAsyncIterable<Frame>,
44
44
  handler: (frame: Frame, context: CacheOnContext) => void | Promise<void>,
45
45
  ): () => void
46
46
  export function watch<Args, Return>(
@@ -74,7 +74,7 @@ export function watch(
74
74
  existing cache.on loop is the single implementation this branch delegates to. */
75
75
  if (isSubscribable(source)) {
76
76
  return cache.on(
77
- source as Subscribable<unknown>,
77
+ source as NamedAsyncIterable<unknown>,
78
78
  handler as (frame: unknown, context: CacheOnContext) => void | Promise<void>,
79
79
  )
80
80
  }
@@ -33,8 +33,11 @@ import { cacheStoreSlot } from './lib/shared/cacheStoreSlot.ts'
33
33
  import { createCacheStore } from './lib/shared/createCacheStore.ts'
34
34
  import { loadEnvFromDataDir } from './lib/shared/loadEnvFromDataDir.ts'
35
35
  import { pageSlot } from './lib/shared/pageSlot.ts'
36
+ import { pendingAsyncCellsSlot } from './lib/shared/pendingAsyncCellsSlot.ts'
37
+ import { resolvedCellsSlot } from './lib/shared/resolvedCellsSlot.ts'
36
38
  import { runningAsStandaloneBinary } from './lib/shared/runningAsStandaloneBinary.ts'
37
39
  import { sharedCacheStoreSlot } from './lib/shared/sharedCacheStoreSlot.ts'
40
+ import { streamedCellsSlot } from './lib/shared/streamedCellsSlot.ts'
38
41
 
39
42
  /*
40
43
  Resolve config into process.env before anything reads it (createServer reads
@@ -76,6 +79,17 @@ sharedCacheStoreSlot.resolver = () => sharedCacheStore
76
79
 
77
80
  cacheStoreSlot.resolver = () => requestContext.getStore()?.cache ?? sharedCacheStore
78
81
 
82
+ /* One process-wide fallback list for reads with no request in flight (boot/cron/socket) —
83
+ real requests each carry their own `pendingAsyncCells`, so the SSR barrier drains a
84
+ per-request list and concurrent renders never cross-contaminate. */
85
+ const sharedPendingAsyncCells = { promises: [] }
86
+ pendingAsyncCellsSlot.resolver = () =>
87
+ requestContext.getStore()?.pendingAsyncCells ?? sharedPendingAsyncCells
88
+ const sharedResolvedCells = { entries: [] }
89
+ resolvedCellsSlot.resolver = () => requestContext.getStore()?.resolvedCells ?? sharedResolvedCells
90
+ const sharedStreamedCells = { entries: [] }
91
+ streamedCellsSlot.resolver = () => requestContext.getStore()?.streamedCells ?? sharedStreamedCells
92
+
79
93
  pageSlot.resolver = resolvePageSnapshot
80
94
 
81
95
  await createServer({
@@ -6,17 +6,18 @@ the HTTP method. The bundler swaps the runtime per build target: direct call on
6
6
  the server, fetch over the network on the client.
7
7
 
8
8
  Args (what the caller passes in) come from the handler's parameter type —
9
- either an inline annotation or an explicit `GET<{ id: string }>(...)`
10
- generic. Return (what the caller receives after Content-Type-driven
11
- decoding) is inferred from the handler's return type via the
12
- `TypedResponse<T>` brand on `json`/`error`/`redirect`/`jsonl`/`sse`, so
13
- plain `GET(() => json({...}))` already types end-to-end.
9
+ annotate the parameter and the body infers; a typed generic on the helper is a
10
+ compile error. Return (what the caller receives after Content-Type-driven
11
+ decoding) is inferred from the handler's return type via the `TypedResponse<T>`
12
+ brand on `json`/`error`/`redirect`/`jsonl`/`sse`, so plain
13
+ `GET(() => json({...}))` already types end-to-end.
14
14
 
15
- For inbound validation pass a Standard Schema as `inputSchema` in the
16
- second argument: `GET(fn, { inputSchema })`. Args then infers from the
17
- schema's output type and the server replies with 422 on validation
18
- failure. An optional `outputSchema` describes the success body for the
19
- OpenAPI 200 response and the MCP tool output.
15
+ For inbound validation pass a Standard Schema under `schemas.input` in the
16
+ second argument: `GET(fn, { schemas: { input } })`. Args then infers from the
17
+ schema's output type and the server replies with 422 on validation failure. An
18
+ optional `schemas.output` describes the success body for the OpenAPI 200
19
+ response and the MCP tool output; without one the handler's return type is
20
+ projected to JSON Schema.
20
21
 
21
22
  `json(...)` from `abide/server/json` is a thin wrapper over `Response.json`
22
23
  that defaults `Cache-Control: no-store`, since intermediary caches shouldn't
@@ -24,10 +25,11 @@ memoise rpc replies (the framework's per-request cache handles in-process
24
25
  dedupe). Other helpers are siblings, one per file: `abide/server/error`,
25
26
  `abide/server/redirect`, `abide/server/sse`, `abide/server/jsonl`.
26
27
 
27
- Every rpc value also exposes `.raw(args?)` (returns the underlying
28
+ Every rpc value also exposes `.raw(args?, init?)` (returns the underlying
28
29
  `Response`) for callers that need headers or status. A streaming rpc — a
29
- jsonl/sse handler — returns a `Subscribable` over the frames from its bare
30
- call (`fn(args)`); iterate it with `for await` or react to it with `watch(fn(args), frame => …)`.
30
+ jsonl/sse handler — returns a `NamedAsyncIterable` over the frames from its bare
31
+ call (`fn(args)`); iterate it with `for await` or react to it with
32
+ `watch(fn(args), frame => …)`.
31
33
  */
32
34
 
33
35
  import { GET } from '@abide/abide/server/GET'
@@ -5,7 +5,7 @@ same wiring as `abide start`, no fixtures) and hands back the whole surface:
5
5
  app.fetch(path) — pages and raw HTTP, origin + mount base resolved
6
6
  app.rpc.<name>(args) — RPCs over the real pipeline (CSRF, cookies, base),
7
7
  typed and decoded; .raw(args) for the Response
8
- app.sockets.<name> — a live Socket: iterate it (for await), .peek(), .broadcast(m)
8
+ app.sockets.<name> — a live Socket: iterate it (for await), .peek(), .publish(m)
9
9
  app.health() — the /__abide/health payload
10
10
 
11
11
  `app.rpc` / `app.sockets` are typed from your own RPCs and sockets — generated
@@ -1,41 +0,0 @@
1
- import { resolve } from 'node:path'
2
- import { NO_STORE } from '../../shared/CACHE_CONTROL_VALUES.ts'
3
- import { isLayoutFile } from '../../shared/isLayoutFile.ts'
4
- import { compileModule } from '../../ui/compile/compileModule.ts'
5
-
6
- // Reused across requests — strips the embedded author TypeScript to browser JS.
7
- const TRANSPILER = new Bun.Transpiler({ loader: 'ts' })
8
-
9
- /*
10
- Serves one edited `.abide`'s hot module (dev component HMR). Compiles it in hot
11
- mode — runtime sourced from `window.__abide`, self-invoking `hotReplace` — and
12
- transpiles the embedded author TypeScript to plain JS the browser can import
13
- directly (the normal pipeline relies on the bundler's `ts` loader for this). The
14
- browser imports this in place of a reload; on load it swaps the component's live
15
- instances. The id is guarded as a project-relative `.abide` under cwd; a bad
16
- module ID returns 400; a missing file 404s so the client falls back to a reload.
17
- */
18
- export async function devHotModuleResponse(moduleId: string): Promise<Response> {
19
- const root = process.cwd()
20
- const path = resolve(root, moduleId)
21
- if (!moduleId.endsWith('.abide') || !path.startsWith(`${root}/`)) {
22
- return new Response('bad request', { status: 400 })
23
- }
24
- const source = await Bun.file(path)
25
- .text()
26
- .catch(() => undefined)
27
- if (source === undefined) {
28
- return new Response('not found', { status: 404 })
29
- }
30
- const { code } = compileModule(source, {
31
- isLayout: isLayoutFile(moduleId),
32
- moduleId,
33
- hot: true,
34
- })
35
- return new Response(TRANSPILER.transformSync(code), {
36
- headers: {
37
- 'Content-Type': 'application/javascript; charset=utf-8',
38
- 'Cache-Control': NO_STORE,
39
- },
40
- })
41
- }
@@ -1,7 +0,0 @@
1
- /*
2
- Path prefix of the dev-only component hot-module endpoint (`/__abide/hot/<moduleId>`).
3
- The browser imports `<prefix><moduleId>?v=<hash>` to fetch one edited component's
4
- hot module instead of reloading. Shared so the server route (createServer) and the
5
- live-reload client (DEV_RELOAD_CLIENT_SCRIPT) agree.
6
- */
7
- export const DEV_HOT_PREFIX = '/__abide/hot/'
@@ -1,13 +0,0 @@
1
- /*
2
- HTTP statuses that mean "the server didn't process this request" — the gateway /
3
- availability family. A response with one of these is treated like a transport failure
4
- by a durable RPC: the call still throws (the error framework is unchanged), and the
5
- request is parked for replay on recovery. Everything else (4xx, 500, …) means the
6
- server received and handled it — that flows to the error framework, never the outbox.
7
-
8
- 502 Bad Gateway · 503 Service Unavailable · 504 Gateway Timeout (abide's own client
9
- timeout surfaces here too) · 520–527, 530 — Cloudflare/CDN origin-unreachable.
10
- */
11
- export const UNREACHABLE_STATUSES: ReadonlySet<number> = new Set([
12
- 502, 503, 504, 520, 521, 522, 523, 524, 525, 526, 527, 530,
13
- ])
@@ -1,17 +0,0 @@
1
- import { isReplayableMethod } from './isReplayableMethod.ts'
2
- import type { CacheEntry } from './types/CacheEntry.ts'
3
-
4
- /*
5
- Whether a cache entry carries a wire request that can be replayed from a warm
6
- snapshot — a replayable (GET-only) method, so the seed never re-fires a write or
7
- loses a body. Producer entries (no request) and write methods fail it.
8
-
9
- The request half of snapshot shippability, factored out so the two gates that
10
- need it cannot drift: `snapshotShippable` composes it with `settled` for the
11
- store-filter sites, and `snapshotEntryFromCache` calls it alone — its
12
- streaming-drain caller hands it still-pending entries it then awaits, so it can't
13
- require `settled` up front.
14
- */
15
- export function hasReplayableRequest(entry: CacheEntry): boolean {
16
- return entry.request !== undefined && isReplayableMethod(entry.request.method.toUpperCase())
17
- }
@@ -1,12 +0,0 @@
1
- /*
2
- Set true only while the client adopts the server-rendered DOM (the hydration
3
- render pass — see the router's hydrate branch and `hydrate`). Read by `peek`: the
4
- server materializes no cache value (materializeRetained/cacheEntryFromSnapshot are
5
- client-only), so server-side peek is uniformly undefined and the SSR render always
6
- shows the fallback. A snapshot-seeded warm value surfacing DURING hydration would
7
- diverge from that server text and corrupt the claimed text node, so peek withholds
8
- it until the pass ends — `wakeHydrationPeeks` then re-runs the scope on the now-
9
- congruent value. A plain boolean, save/restore-nested so a child hydrate can't clear
10
- an outer pass early; false on the server (no hydration) and after boot.
11
- */
12
- export const hydratingSlot: { active: boolean } = { active: false }
@@ -1,20 +0,0 @@
1
- /*
2
- Internal slot the browser outbox registry registers its prober into, so the
3
- shared pending() probe can count parked durable-rpc writes without shared/
4
- importing browser/. Mirrors tailProbeSlot. The prober reads the doc-backed
5
- queue entries (reactive inside scope().computed() / scope().effect()) and reports whether any
6
- undelivered entry matches the selector: a durable rpc selector (carries a
7
- `url`) narrows to its own queue, the bare form spans every registered queue,
8
- optional args narrow to one parked call by structural compare. Outbox state is
9
- pending-only — a parked write has no value, so it never contributes to
10
- refreshing(); probeRegistries reads this slot only on its `field === 'pending'`
11
- branch, which is why the slot mirrors tailProbeSlot but reports a bare boolean
12
- rather than a { pending, refreshing } pair. When no prober is registered (server
13
- render, or no durable rpc was ever used) pending() sees no parked writes,
14
- exactly as on the server where there are no client queues.
15
- */
16
- export const outboxProbeSlot: {
17
- probe: ((selector: unknown, args: unknown) => boolean) | undefined
18
- } = {
19
- probe: undefined,
20
- }
@@ -1,9 +0,0 @@
1
- import type { OutboxEntry } from './OutboxEntry.ts'
2
-
3
- /*
4
- The `.outbox` face on a durable RPC's client proxy: callable to read the reactive list
5
- of undelivered entries (`rpc.outbox()`), with an awaitable `retry()` that drains the whole
6
- queue and resolves when the replay settles. Server-side there is no client queue, so the
7
- face is absent.
8
- */
9
- export type Outbox<Args> = (() => OutboxEntry<Args>[]) & { retry: () => Promise<void> }
@@ -1,27 +0,0 @@
1
- /* The lifecycle status of a parked mutation: waiting to (re)send, or currently sending.
2
- A write lives in the outbox ONLY while it can't reach the server; the instant the server
3
- responds at all — a 2xx OR a real 4xx/500 — the entry leaves the queue, so there is no
4
- terminal `error` state. Only the unreachable family (transport failure / 502/503/504/52x)
5
- keeps it `queued`. */
6
- export type OutboxStatus = 'queued' | 'sending'
7
-
8
- /* One durable, replayable mutation in an RPC's outbox — parked because the original
9
- call couldn't reach the server (transport failure, or a 502/503/504/52x). `controller`
10
- is the entry's own abort handle (cancel = `controller.abort()`); `request` is the
11
- synthesized, persisted Request the drain re-sends; `args` is the typed input (for
12
- rendering); `error` is why it's parked (the `kind: 'queued'` HttpError from the
13
- unreachable attempt); `retry()` kicks a FIFO drain. `settled` is the eventual outcome
14
- of THIS write as if the original call had reached the server — it resolves with the
15
- decoded result on delivery and rejects with the real HttpError on a server refusal (or
16
- an AbortError on cancel). It may stay pending indefinitely if the write is never
17
- replayed; reading it is what arms it (a never-read `settled` never rejects). */
18
- export type OutboxEntry<Args> = {
19
- id: string
20
- controller: AbortController
21
- request: Request
22
- args: Args
23
- status: OutboxStatus
24
- error?: unknown
25
- retry: () => Promise<void>
26
- settled: Promise<unknown>
27
- }
@@ -1,36 +0,0 @@
1
- /*
2
- Second-argument options for the smart bare rpc call (`getFoo(args, opts)`). The
3
- call is cached/coalesced/reactive with SWR always on for replayable reads, so
4
- these govern retention and the refetch clock — NOT transport. Per-call transport
5
- options (signal/keepalive/priority/cache/headers) live on `.raw(args, init)`.
6
-
7
- Fetch reads:
8
- - `ttl` — retention/staleness in ms. Default: 0 on the server (coalesce-only —
9
- the request is the atomic unit, nothing is retained past it), Infinity on the
10
- client (retain until invalidate/refresh — the tab is the atomic unit). On the
11
- client, N ms marks a staleness deadline: the retained value goes stale after N
12
- ms and the next access triggers a background revalidation (stale stays visible,
13
- `refreshing()` true); the display value is never dropped. On the server, N ms
14
- is a plain expiry and only takes effect in the shared store (pair with `shared`).
15
- - `tags` — free-form invalidation-group labels (see the selector grammar).
16
- - `throttle` / `debounce` — rate-limit the background refetch clock (set one, not
17
- both). Leading-edge-then-coalesce, or fire-after-quiet respectively.
18
- - `shared` — opts the entry into the process-level store instead of the default
19
- request-scoped one (server) — a store that outlives every request. It selects
20
- the store only; it does NOT retain (pair it with `ttl` to memoise across
21
- requests). The shared store is keyed by method+url+args, never by user, so do
22
- not put per-user data in it — it would be served to other users. Omit `shared`
23
- for per-request data. Write only `shared: true`; there is no `false` form. On
24
- the client there is a single tab store, so the flag is a no-op there.
25
-
26
- Streaming reads (jsonl/sse):
27
- - `n` — how many retained frames to replay before going live.
28
- */
29
- export type SmartReadOptions = {
30
- ttl?: number
31
- tags?: string[]
32
- throttle?: number
33
- debounce?: number
34
- shared?: boolean
35
- n?: number
36
- }
@@ -1,20 +0,0 @@
1
- import { cacheStores } from './cacheStores.ts'
2
-
3
- /*
4
- Fired once when the outermost hydration pass ends (see the router's hydrate branch
5
- and `hydrate`). During the pass `peek` withheld every snapshot-seeded warm value
6
- for SSR congruence (see hydratingSlot); now that the pass is over those values are
7
- congruent to show, so re-run the scopes that read them. A peek scope taps the exact
8
- call key's lifecycle channel (peek's trackLifecycle), so marking every live entry's
9
- key wakes them — cheap, one-time, and harmless for non-peek lifecycle readers (they
10
- recompute the same value). Marks by key, not the bare store-wide channel, because a
11
- peek(fn, args) scope taps the per-key prefix channel, which a keyless mark misses.
12
- */
13
- export function wakeHydrationPeeks(): void {
14
- const stores = cacheStores()
15
- for (const store of stores) {
16
- for (const key of store.entries.keys()) {
17
- store.markLifecycle(key)
18
- }
19
- }
20
- }
@@ -1,5 +0,0 @@
1
- /* The `abide-` tag prefix on framework-owned custom elements (the router's
2
- `abide-outlet`, the streaming `abide-resolve`/`abide-cache` fragments). `scopeLabel`
3
- strips it to derive the bare name from a framework-owned host tag (e.g. `abide-resolve` → `resolve`). Child components no longer mount into
4
- an `abide-<name>` wrapper — they build as marker ranges (see `mountRange`). */
5
- export const COMPONENT_WRAPPER_PREFIX = 'abide-'
@@ -1,11 +0,0 @@
1
- /* The author-facing reactive primitive names (`state`, `state.linked`, `state.computed`,
2
- and the destructuring `const {…} = props()`). The compiler no longer reads this —
3
- recognition is import-resolved (see `resolveReactiveExport.ts`). It remains the canonical
4
- inventory the docs surface-weighting tool enumerates (`scripts/surfaceWeight.ts`), so the
5
- grammar's "primitives" bucket stays in sync with the language without hardcoding a list. */
6
- export const REACTIVE_CALLEES: ReadonlySet<string> = new Set([
7
- 'state',
8
- 'linked',
9
- 'computed',
10
- 'props',
11
- ])
@@ -1,80 +0,0 @@
1
- import ts from 'typescript'
2
- import { UI_RUNTIME_IMPORTS } from './UI_RUNTIME_IMPORTS.ts'
3
-
4
- /*
5
- Independent backstop on the per-component dead-import filter (`compileModule`). The filter
6
- decides which runtime helpers to import by reading the generated output's identifiers; if it
7
- ever undercounts — as a raw token scan once did, mis-reading the tail of a module after a
8
- `${…}` template substitution — a helper gets CALLED but never imported, and the bundle throws
9
- `ReferenceError` the instant `build()` runs. The router escalates that into a reload loop, so
10
- the failure is both opaque and unrecoverable.
11
-
12
- This re-derives the same question a DIFFERENT way (so it can't share the filter's blind spot):
13
- walk the final module's AST, find every call whose callee is a bare runtime-helper identifier,
14
- and require that name to be imported or locally bound. A helper name inside a string / template /
15
- comment is not a `CallExpression` callee, so a docs component quoting framework code
16
- (`mount(...)` in a snippet) never false-positives. Compile-time only; never on the hot path.
17
- */
18
- /* `module` is either the source string (standalone use — parsed here) or the already-parsed
19
- tree of the bodies WITHOUT the prepended import block (compile pipeline — shares the one
20
- parse the dead-import filter made). In the latter case the import block's bindings aren't in
21
- the tree, so `importedHelpers` supplies the helper names that block will bind. */
22
- export function assertRuntimeHelpersBound(
23
- module: string | ts.SourceFile,
24
- importedHelpers: Set<string>,
25
- context: string,
26
- ): void {
27
- /* The EMITTED local names (the `$$` alias when set) — codegen calls those, and the
28
- aliased import binds them, so the bound/called check must use the same form. */
29
- const helperNames = new Set(UI_RUNTIME_IMPORTS.map((entry) => entry.alias ?? entry.name))
30
- const source =
31
- typeof module === 'string'
32
- ? ts.createSourceFile(
33
- 'module.ts',
34
- module,
35
- ts.ScriptTarget.Latest,
36
- /* setParentNodes */ true,
37
- )
38
- : module
39
- /* Names a bare call can resolve to: the import block's helper bindings (supplied when the
40
- tree omits them) plus every import binding and declared name in the tree. Collected
41
- generously (any identifier in a binding position) — over-approximating the bound set only
42
- risks missing a defect, never raising a false alarm on valid output. */
43
- const bound = new Set<string>(importedHelpers)
44
- const calledHelpers: { name: string; position: number }[] = []
45
- const visit = (node: ts.Node): void => {
46
- if (
47
- (ts.isVariableDeclaration(node) ||
48
- ts.isFunctionDeclaration(node) ||
49
- ts.isParameter(node) ||
50
- ts.isBindingElement(node) ||
51
- ts.isImportSpecifier(node) ||
52
- ts.isImportClause(node) ||
53
- ts.isNamespaceImport(node)) &&
54
- node.name !== undefined &&
55
- ts.isIdentifier(node.name)
56
- ) {
57
- bound.add(node.name.text)
58
- }
59
- if (
60
- ts.isCallExpression(node) &&
61
- ts.isIdentifier(node.expression) &&
62
- helperNames.has(node.expression.text)
63
- ) {
64
- calledHelpers.push({
65
- name: node.expression.text,
66
- position: node.expression.getStart(source),
67
- })
68
- }
69
- node.forEachChild(visit)
70
- }
71
- visit(source)
72
-
73
- const unbound = calledHelpers.find((call) => !bound.has(call.name))
74
- if (unbound !== undefined) {
75
- const { line, character } = source.getLineAndCharacterOfPosition(unbound.position)
76
- throw new Error(
77
- `[abide] ${context} calls runtime helper \`${unbound.name}\` at line ${line + 1}:${character + 1} but never imports it — the dead-import filter dropped it. Please report this with the component source.\nOutput:\n${source.text}`,
78
- )
79
- }
80
- }