@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
@@ -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
- }
@@ -1,313 +0,0 @@
1
- import type { SemanticToken } from './types/SemanticToken.ts'
2
-
3
- /*
4
- Lexical highlighting for the HTML-shaped markup of a `.abide` component — element
5
- and component tag names, attribute names and string values, comments, and the
6
- `<`/`>`/`=` punctuation. A pure scan of raw source (independent of a successful
7
- parse, so it survives mid-edit), it exists so the LSP owns the *structure* too,
8
- not just `{…}` interiors: editors reuse tree-sitter-html as the grammar of record,
9
- which has no production for abide's `attr={expr}` values — a multiline
10
- template-literal attribute (`code={`…`}`) drops it into error recovery that never
11
- re-syncs, miscoloring every element below. Emitting these tokens overrides that
12
- broken parse wherever real structure sits.
13
-
14
- `{…}` expression regions (attribute values, interpolations, `{#…}` block heads) and
15
- the raw bodies of `<script>`/`<style>` are skipped here — those are the shadow
16
- type-checker's and the structural block tokenizer's jobs. Lowercase tags get the
17
- `tag` type, uppercase (components) get `type` (a constructor), mirroring the
18
- tree-sitter highlight split.
19
- */
20
-
21
- /* Raw-text elements whose body is JS/CSS, not markup, so the scan jumps past it. */
22
- const RAW_ELEMENTS = new Set(['script', 'style'])
23
-
24
- const operator = (start: number, length: number): SemanticToken => ({
25
- start,
26
- length,
27
- type: 'operator',
28
- modifiers: [],
29
- })
30
-
31
- const isNameStart = (char: string): boolean => /[A-Za-z]/.test(char)
32
-
33
- /* Characters that end a tag or attribute name. */
34
- const isNameBreak = (char: string): boolean => /[\s=/>{}"'<]/.test(char)
35
-
36
- export function markupTokens(source: string): SemanticToken[] {
37
- const tokens: SemanticToken[] = []
38
- let cursor = 0
39
- while (cursor < source.length) {
40
- const char = source.charAt(cursor)
41
- /* An expression region — interiors are the shadow classifier's job. */
42
- if (char === '{') {
43
- cursor = skipExpression(source, cursor)
44
- continue
45
- }
46
- if (char === '<') {
47
- if (source.startsWith('<!--', cursor)) {
48
- const end = source.indexOf('-->', cursor + 4)
49
- const close = end === -1 ? source.length : end + 3
50
- tokens.push({
51
- start: cursor,
52
- length: close - cursor,
53
- type: 'comment',
54
- modifiers: [],
55
- })
56
- cursor = close
57
- continue
58
- }
59
- const isClose = source.charAt(cursor + 1) === '/'
60
- const nameStart = cursor + (isClose ? 2 : 1)
61
- if (isNameStart(source.charAt(nameStart))) {
62
- tokens.push(operator(cursor, isClose ? 2 : 1))
63
- const name = readName(source, nameStart)
64
- const type = /[A-Z]/.test(name.charAt(0)) ? 'type' : 'tag'
65
- tokens.push({ start: nameStart, length: name.length, type, modifiers: [] })
66
- const afterName = nameStart + name.length
67
- if (isClose) {
68
- cursor = scanToClose(source, afterName, tokens)
69
- continue
70
- }
71
- const { end, selfClosed } = scanAttributes(source, afterName, tokens)
72
- cursor =
73
- RAW_ELEMENTS.has(name) && !selfClosed ? skipRawBody(source, end, name) : end
74
- continue
75
- }
76
- }
77
- cursor += 1
78
- }
79
- return tokens
80
- }
81
-
82
- /* Reads a tag/element name from `start` (letters, digits, `-`, `_`). */
83
- function readName(source: string, start: number): string {
84
- let cursor = start
85
- while (cursor < source.length && /[A-Za-z0-9_-]/.test(source.charAt(cursor))) {
86
- cursor += 1
87
- }
88
- return source.slice(start, cursor)
89
- }
90
-
91
- /* From after a close tag's name, emits the `>` operator and returns past it. */
92
- function scanToClose(source: string, from: number, tokens: SemanticToken[]): number {
93
- let cursor = from
94
- while (cursor < source.length && source.charAt(cursor) !== '>') {
95
- cursor += 1
96
- }
97
- if (cursor < source.length) {
98
- tokens.push(operator(cursor, 1))
99
- return cursor + 1
100
- }
101
- return cursor
102
- }
103
-
104
- /* Scans an open tag's attributes from after its name to the closing `>`/`/>`,
105
- emitting an `attribute` token per name, `operator` for `=`, and string tokens
106
- for quoted values; `{…}` values and spreads are left to the shadow. */
107
- function scanAttributes(
108
- source: string,
109
- from: number,
110
- tokens: SemanticToken[],
111
- ): { end: number; selfClosed: boolean } {
112
- let cursor = from
113
- while (cursor < source.length) {
114
- const char = source.charAt(cursor)
115
- if (/\s/.test(char)) {
116
- cursor += 1
117
- continue
118
- }
119
- if (char === '>') {
120
- tokens.push(operator(cursor, 1))
121
- return { end: cursor + 1, selfClosed: false }
122
- }
123
- if (char === '/' && source.charAt(cursor + 1) === '>') {
124
- tokens.push(operator(cursor, 2))
125
- return { end: cursor + 2, selfClosed: true }
126
- }
127
- /* `{…code}` spread / expression attribute — shadow's job. */
128
- if (char === '{') {
129
- cursor = skipExpression(source, cursor)
130
- continue
131
- }
132
- const nameStart = cursor
133
- while (cursor < source.length && !isNameBreak(source.charAt(cursor))) {
134
- cursor += 1
135
- }
136
- if (cursor === nameStart) {
137
- /* No progress on a stray delimiter — advance to avoid a stall. */
138
- cursor += 1
139
- continue
140
- }
141
- tokens.push({
142
- start: nameStart,
143
- length: cursor - nameStart,
144
- type: 'attribute',
145
- modifiers: [],
146
- })
147
- let eq = cursor
148
- while (eq < source.length && /\s/.test(source.charAt(eq))) {
149
- eq += 1
150
- }
151
- if (source.charAt(eq) === '=') {
152
- tokens.push(operator(eq, 1))
153
- eq += 1
154
- while (eq < source.length && /\s/.test(source.charAt(eq))) {
155
- eq += 1
156
- }
157
- cursor = scanAttributeValue(source, eq, tokens)
158
- } else {
159
- cursor = eq
160
- }
161
- }
162
- return { end: cursor, selfClosed: false }
163
- }
164
-
165
- /* Scans an attribute value: a `{…}` expression (skipped), a quoted string (split
166
- around any `{…}` interpolations), or a bare unquoted run. Returns past it. */
167
- function scanAttributeValue(source: string, from: number, tokens: SemanticToken[]): number {
168
- const char = source.charAt(from)
169
- if (char === '{') {
170
- return skipExpression(source, from)
171
- }
172
- if (char === '"' || char === "'") {
173
- return scanQuotedValue(source, from, tokens)
174
- }
175
- let cursor = from
176
- /* Unquoted values run to whitespace or `>`; a `/` is part of the value (a URL)
177
- UNLESS it's the self-close `/>`, so stop before that `/` and leave it for the
178
- attribute scanner to read as the self-close marker. */
179
- while (cursor < source.length) {
180
- const valueChar = source.charAt(cursor)
181
- if (/[\s>]/.test(valueChar) || (valueChar === '/' && source.charAt(cursor + 1) === '>')) {
182
- break
183
- }
184
- cursor += 1
185
- }
186
- if (cursor > from) {
187
- tokens.push({ start: from, length: cursor - from, type: 'string', modifiers: [] })
188
- }
189
- return cursor
190
- }
191
-
192
- /* Emits string tokens for the literal runs of a quoted value (delimiters
193
- included), skipping `{…}` interpolations so the shadow colors their interiors. */
194
- function scanQuotedValue(source: string, start: number, tokens: SemanticToken[]): number {
195
- const quote = source.charAt(start)
196
- let segmentStart = start
197
- let cursor = start + 1
198
- const flush = (end: number): void => {
199
- if (end > segmentStart) {
200
- tokens.push({
201
- start: segmentStart,
202
- length: end - segmentStart,
203
- type: 'string',
204
- modifiers: [],
205
- })
206
- }
207
- }
208
- while (cursor < source.length) {
209
- const char = source.charAt(cursor)
210
- if (char === quote) {
211
- flush(cursor + 1)
212
- return cursor + 1
213
- }
214
- if (char === '{') {
215
- flush(cursor)
216
- cursor = skipExpression(source, cursor)
217
- segmentStart = cursor
218
- continue
219
- }
220
- cursor += 1
221
- }
222
- flush(cursor)
223
- return cursor
224
- }
225
-
226
- /* From the close `>` of a raw-text element's open tag, returns the index of its
227
- `</tag` so the close tag is scanned as markup but the body is left untouched. */
228
- function skipRawBody(source: string, from: number, tag: string): number {
229
- const close = source.indexOf(`</${tag}`, from)
230
- return close === -1 ? source.length : close
231
- }
232
-
233
- /* From the opening `{` at `start`, returns the index past the matching `}`,
234
- tracking nested braces and skipping strings, template literals (with `${…}`),
235
- and JS comments so their contents never sway the depth. A regex literal holding
236
- an unbalanced brace is the one unhandled case — rare, accepted for a highlighter. */
237
- function skipExpression(source: string, start: number): number {
238
- let depth = 0
239
- let cursor = start
240
- while (cursor < source.length) {
241
- const char = source.charAt(cursor)
242
- if (char === '"' || char === "'") {
243
- cursor = skipString(source, cursor)
244
- continue
245
- }
246
- if (char === '`') {
247
- cursor = skipTemplate(source, cursor)
248
- continue
249
- }
250
- if (char === '/' && source.charAt(cursor + 1) === '/') {
251
- const newline = source.indexOf('\n', cursor)
252
- cursor = newline === -1 ? source.length : newline
253
- continue
254
- }
255
- if (char === '/' && source.charAt(cursor + 1) === '*') {
256
- const end = source.indexOf('*/', cursor + 2)
257
- cursor = end === -1 ? source.length : end + 2
258
- continue
259
- }
260
- if (char === '{') {
261
- depth += 1
262
- } else if (char === '}') {
263
- depth -= 1
264
- if (depth === 0) {
265
- return cursor + 1
266
- }
267
- }
268
- cursor += 1
269
- }
270
- return cursor
271
- }
272
-
273
- /* From the opening quote at `start`, returns the index past the matching quote;
274
- a backslash escapes the next character. */
275
- function skipString(source: string, start: number): number {
276
- const quote = source.charAt(start)
277
- let cursor = start + 1
278
- while (cursor < source.length) {
279
- const char = source.charAt(cursor)
280
- if (char === '\\') {
281
- cursor += 2
282
- continue
283
- }
284
- if (char === quote) {
285
- return cursor + 1
286
- }
287
- cursor += 1
288
- }
289
- return cursor
290
- }
291
-
292
- /* From the opening backtick at `start`, returns the index past the matching
293
- backtick. `${…}` re-enters expression scanning so its inner braces balance;
294
- braces in the literal text are ignored. */
295
- function skipTemplate(source: string, start: number): number {
296
- let cursor = start + 1
297
- while (cursor < source.length) {
298
- const char = source.charAt(cursor)
299
- if (char === '\\') {
300
- cursor += 2
301
- continue
302
- }
303
- if (char === '`') {
304
- return cursor + 1
305
- }
306
- if (char === '$' && source.charAt(cursor + 1) === '{') {
307
- cursor = skipExpression(source, cursor + 1)
308
- continue
309
- }
310
- cursor += 1
311
- }
312
- return cursor
313
- }