@abide/abide 0.46.0 → 0.48.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 (194) hide show
  1. package/AGENTS.md +370 -320
  2. package/CHANGELOG.md +94 -0
  3. package/README.md +203 -168
  4. package/package.json +12 -8
  5. package/src/abideLsp.ts +11 -12
  6. package/src/abideResolverPlugin.ts +9 -4
  7. package/src/lib/bundle/disconnected.abide +3 -0
  8. package/src/lib/cli/printCommandHelp.ts +43 -0
  9. package/src/lib/cli/printSessionHelp.ts +2 -1
  10. package/src/lib/cli/{printHelp.ts → printTopLevelHelp.ts} +3 -40
  11. package/src/lib/cli/runCli.ts +2 -1
  12. package/src/lib/mcp/buildPrompts.ts +25 -0
  13. package/src/lib/mcp/dispatchMcpRequest.ts +8 -6
  14. package/src/lib/mcp/mcpResourceServerSlot.ts +5 -10
  15. package/src/lib/mcp/mcpSurface.ts +13 -252
  16. package/src/lib/mcp/mcpTools.ts +187 -0
  17. package/src/lib/mcp/renderPrompt.ts +25 -0
  18. package/src/lib/mcp/types/McpSurface.ts +15 -0
  19. package/src/lib/mcp/types/PromptDescriptor.ts +5 -0
  20. package/src/lib/mcp/types/PromptMessage.ts +2 -0
  21. package/src/lib/mcp/types/ToolDescriptor.ts +7 -0
  22. package/src/lib/mcp/types/ToolResult.ts +2 -0
  23. package/src/lib/server/agent.ts +1 -1
  24. package/src/lib/server/jsonl.ts +2 -2
  25. package/src/lib/server/rpc/defineRpc.ts +5 -0
  26. package/src/lib/server/runtime/buildCacheSnapshot.ts +6 -6
  27. package/src/lib/server/runtime/containsTraversal.ts +21 -17
  28. package/src/lib/server/runtime/createAppAssetServer.ts +11 -16
  29. package/src/lib/server/runtime/createServer.ts +101 -71
  30. package/src/lib/server/runtime/createUiPageRenderer.ts +23 -4
  31. package/src/lib/server/runtime/devClientFingerprint.ts +3 -3
  32. package/src/lib/server/runtime/devHotModuleResponse.ts +2 -1
  33. package/src/lib/server/runtime/finalizeResponse.ts +32 -0
  34. package/src/lib/server/runtime/snapshotEntryFromCache.ts +8 -17
  35. package/src/lib/server/runtime/types/InspectorCacheEntry.ts +1 -1
  36. package/src/lib/server/runtime/types/InspectorCacheSnapshot.ts +4 -4
  37. package/src/lib/server/runtime/types/InspectorContext.ts +1 -1
  38. package/src/lib/server/socket.ts +1 -1
  39. package/src/lib/server/sockets/createSocketDispatcher.ts +28 -5
  40. package/src/lib/server/sockets/defineSocket.ts +26 -7
  41. package/src/lib/server/sockets/types/SocketRegistryEntry.ts +1 -1
  42. package/src/lib/server/sockets/types/SocketRoutes.ts +1 -1
  43. package/src/lib/server/sse.ts +2 -2
  44. package/src/lib/shared/DEFER.ts +8 -0
  45. package/src/lib/shared/activeCacheStore.ts +2 -2
  46. package/src/lib/shared/activePage.ts +2 -2
  47. package/src/lib/shared/attachRpcSelectorMethods.ts +42 -0
  48. package/src/lib/shared/attachSocketSelectorMethods.ts +34 -0
  49. package/src/lib/shared/basePath.ts +2 -2
  50. package/src/lib/shared/baseSlot.ts +6 -5
  51. package/src/lib/shared/bodyValueForKind.ts +1 -2
  52. package/src/lib/shared/buildSocketOverChannel.ts +40 -4
  53. package/src/lib/shared/cache.ts +522 -117
  54. package/src/lib/shared/cacheEntryFromSnapshot.ts +3 -22
  55. package/src/lib/shared/cacheStoreSlot.ts +9 -5
  56. package/src/lib/shared/cacheStores.ts +3 -3
  57. package/src/lib/{server/runtime → shared}/createReachable.ts +2 -2
  58. package/src/lib/shared/createRemoteFunction.ts +58 -9
  59. package/src/lib/shared/createResolverSlot.ts +24 -31
  60. package/src/lib/shared/createSocketSubRegistry.ts +2 -2
  61. package/src/lib/shared/decodeRefJson.ts +4 -2
  62. package/src/lib/shared/decodeResponse.ts +8 -6
  63. package/src/lib/shared/done.ts +14 -0
  64. package/src/lib/shared/encodeRefJson.ts +4 -2
  65. package/src/lib/shared/hydratingSlot.ts +12 -0
  66. package/src/lib/shared/isLayoutFile.ts +12 -0
  67. package/src/lib/shared/keyForRemoteCall.ts +2 -1
  68. package/src/lib/shared/keyPrefixForRemote.ts +12 -0
  69. package/src/lib/shared/matchRoute.ts +175 -0
  70. package/src/lib/shared/normalizePathname.ts +11 -0
  71. package/src/lib/shared/pageSlot.ts +14 -5
  72. package/src/lib/shared/pageUrlForFile.ts +3 -3
  73. package/src/lib/shared/parseRouteSegments.ts +16 -7
  74. package/src/lib/shared/patch.ts +41 -0
  75. package/src/lib/shared/peek.ts +35 -0
  76. package/src/lib/shared/prepareRemoteExport.ts +40 -0
  77. package/src/lib/shared/prepareRpcModule.ts +98 -16
  78. package/src/lib/shared/prepareSocketModule.ts +11 -15
  79. package/src/lib/shared/queryStringFromArgs.ts +11 -0
  80. package/src/lib/shared/reachable.ts +102 -0
  81. package/src/lib/shared/refresh.ts +22 -0
  82. package/src/lib/shared/requestScopeSlot.ts +10 -7
  83. package/src/lib/shared/routeParamsShape.ts +9 -9
  84. package/src/lib/shared/rpcErrorRegistry.ts +69 -0
  85. package/src/lib/shared/selectorPrefix.ts +3 -10
  86. package/src/lib/shared/setOwnProperty.ts +19 -0
  87. package/src/lib/shared/sharedCacheStore.ts +14 -0
  88. package/src/lib/shared/sharedCacheStoreSlot.ts +10 -0
  89. package/src/lib/shared/snippet.ts +1 -1
  90. package/src/lib/shared/subscribableProbes.ts +111 -0
  91. package/src/lib/shared/tailProbeSlot.ts +8 -1
  92. package/src/lib/shared/types/CacheEntry.ts +9 -15
  93. package/src/lib/shared/types/CacheOptions.ts +23 -11
  94. package/src/lib/shared/types/CacheSnapshotEntry.ts +0 -5
  95. package/src/lib/shared/types/CacheStats.ts +1 -1
  96. package/src/lib/shared/types/RemoteCallable.ts +25 -7
  97. package/src/lib/shared/types/RemoteFunction.ts +48 -13
  98. package/src/lib/shared/types/ResolverSlot.ts +7 -4
  99. package/src/lib/shared/types/RpcError.ts +26 -0
  100. package/src/lib/shared/types/SmartReadOptions.ts +36 -0
  101. package/src/lib/shared/types/Socket.ts +54 -0
  102. package/src/lib/shared/types/SsrBootState.ts +27 -0
  103. package/src/lib/shared/types/SsrPayload.ts +21 -0
  104. package/src/lib/shared/types/Subscribable.ts +13 -13
  105. package/src/lib/shared/types/TailHooks.ts +2 -1
  106. package/src/lib/shared/url.ts +29 -14
  107. package/src/lib/shared/wakeHydrationPeeks.ts +20 -0
  108. package/src/lib/shared/writeTestSocketsDts.ts +1 -1
  109. package/src/lib/test/createScriptedSurface.ts +1 -1
  110. package/src/lib/test/createTestApp.ts +8 -8
  111. package/src/lib/test/createTestSocketChannel.ts +3 -3
  112. package/src/lib/ui/compile/REACTIVE_CALLEES.ts +5 -6
  113. package/src/lib/ui/compile/UI_RUNTIME_IMPORTS.ts +10 -7
  114. package/src/lib/ui/compile/abideUiPlugin.ts +2 -2
  115. package/src/lib/ui/compile/analyzeComponent.ts +28 -6
  116. package/src/lib/ui/compile/compileModule.ts +137 -11
  117. package/src/lib/ui/compile/compileShadow.ts +101 -84
  118. package/src/lib/ui/compile/desugarSignals.ts +120 -107
  119. package/src/lib/ui/compile/generateBuild.ts +48 -16
  120. package/src/lib/ui/compile/generateSSR.ts +117 -14
  121. package/src/lib/ui/compile/identifierReferencePattern.ts +13 -0
  122. package/src/lib/ui/compile/lowerContext.ts +10 -4
  123. package/src/lib/ui/compile/lowerScript.ts +72 -5
  124. package/src/lib/ui/compile/parseTemplate.ts +1 -14
  125. package/src/lib/ui/compile/prepareNestedScript.ts +31 -17
  126. package/src/lib/ui/compile/resolveReactiveExport.ts +95 -0
  127. package/src/lib/ui/compile/signalCallee.ts +24 -0
  128. package/src/lib/ui/compile/stripEffects.ts +14 -11
  129. package/src/lib/ui/compile/types/AnalyzedComponent.ts +5 -0
  130. package/src/lib/ui/compile/types/TemplateNode.ts +0 -5
  131. package/src/lib/ui/dom/appendStatic.ts +9 -1
  132. package/src/lib/ui/dom/appendText.ts +15 -3
  133. package/src/lib/ui/dom/assertClaimedText.ts +20 -0
  134. package/src/lib/ui/dom/awaitBlock.ts +19 -83
  135. package/src/lib/ui/dom/bindSelectValue.ts +48 -0
  136. package/src/lib/ui/dom/each.ts +12 -1
  137. package/src/lib/ui/dom/hydrate.ts +10 -0
  138. package/src/lib/ui/dom/mountChild.ts +2 -5
  139. package/src/lib/ui/dom/mountRange.ts +0 -75
  140. package/src/lib/ui/effect.ts +4 -3
  141. package/src/lib/ui/installHotBridge.ts +4 -2
  142. package/src/lib/ui/remoteProxy.ts +18 -2
  143. package/src/lib/ui/renderToStream.ts +9 -13
  144. package/src/lib/ui/resumeSeedScript.ts +2 -2
  145. package/src/lib/ui/router.ts +21 -2
  146. package/src/lib/ui/runtime/RESUME.ts +0 -6
  147. package/src/lib/ui/runtime/ambientScopeBacking.ts +1 -1
  148. package/src/lib/ui/runtime/types/SsrRender.ts +3 -4
  149. package/src/lib/ui/scope.ts +6 -3
  150. package/src/lib/ui/seedBootState.ts +53 -0
  151. package/src/lib/ui/socketChannel.ts +2 -2
  152. package/src/lib/ui/socketProxy.ts +9 -3
  153. package/src/lib/ui/startClient.ts +17 -31
  154. package/src/lib/ui/state.ts +44 -13
  155. package/src/lib/ui/sync.ts +11 -5
  156. package/src/lib/ui/tryEncodeResume.ts +2 -5
  157. package/src/lib/ui/types/Scope.ts +14 -10
  158. package/src/lib/ui/watch.ts +140 -0
  159. package/src/serverEntry.ts +13 -11
  160. package/template/CLAUDE.md +1 -1
  161. package/template/package.json +1 -1
  162. package/template/src/server/rpc/getHello.ts +3 -3
  163. package/template/src/ui/pages/page.abide +2 -2
  164. package/template/test/app.test.ts +1 -1
  165. package/src/lib/server/reachable.ts +0 -45
  166. package/src/lib/server/sockets/types/Socket.ts +0 -23
  167. package/src/lib/shared/CACHE_WRAPPED.ts +0 -9
  168. package/src/lib/shared/DEFER_MIN_ARRAY_LENGTH.ts +0 -14
  169. package/src/lib/shared/baseResolver.ts +0 -10
  170. package/src/lib/shared/cacheKeyOf.ts +0 -7
  171. package/src/lib/shared/cacheKeyStore.ts +0 -8
  172. package/src/lib/shared/cacheStoreResolver.ts +0 -12
  173. package/src/lib/shared/globalCacheStore.ts +0 -15
  174. package/src/lib/shared/globalCacheStoreResolver.ts +0 -12
  175. package/src/lib/shared/globalCacheStoreSlot.ts +0 -9
  176. package/src/lib/shared/pageResolver.ts +0 -16
  177. package/src/lib/shared/recordCacheKey.ts +0 -6
  178. package/src/lib/shared/requestScopeResolver.ts +0 -12
  179. package/src/lib/shared/setBaseResolver.ts +0 -4
  180. package/src/lib/shared/setCacheStoreResolver.ts +0 -4
  181. package/src/lib/shared/setGlobalCacheStoreResolver.ts +0 -4
  182. package/src/lib/shared/setPageResolver.ts +0 -4
  183. package/src/lib/shared/setRequestScopeResolver.ts +0 -4
  184. package/src/lib/shared/toBunRoutePattern.ts +0 -28
  185. package/src/lib/shared/types/TailOptions.ts +0 -10
  186. package/src/lib/ui/deferResume.ts +0 -36
  187. package/src/lib/ui/dom/firstElementBetween.ts +0 -14
  188. package/src/lib/ui/matchRoute.ts +0 -106
  189. package/src/lib/ui/runtime/scheduleWake.ts +0 -28
  190. package/src/lib/ui/runtime/whenIdle.ts +0 -21
  191. package/src/lib/ui/runtime/whenVisible.ts +0 -105
  192. package/src/lib/ui/tail.ts +0 -324
  193. /package/src/lib/{server/sockets → shared}/types/SocketClientFrame.ts +0 -0
  194. /package/src/lib/{server/sockets → shared}/types/SocketServerFrame.ts +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,99 @@
1
1
  # abide
2
2
 
3
+ ## 0.48.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 182cdf5: server reads coalesce-only by default, SWR retain client-only ([`98a3e84`](https://github.com/briancray/abide/commit/98a3e84b1e6bdb50b8950a4197ec4a255c560d2a))
8
+ - 182cdf5: warn on a request-scoped ttl>0 read (add shared or drop ttl) ([`b55425c`](https://github.com/briancray/abide/commit/b55425c986fb4c7cebcb1e8c29d1c616466d7d63))
9
+
10
+ ### Patch Changes
11
+
12
+ - 182cdf5: rename global option/exports/comments to shared ([`1256934`](https://github.com/briancray/abide/commit/125693481ba23152a44375bbe200d575b66fdf9a))
13
+ - 182cdf5: evict outside-request coalesce-only entries; correct shared help copy ([`1b73e21`](https://github.com/briancray/abide/commit/1b73e21c33c8ee985f7109fdedcbe302322ce7d1))
14
+ - 182cdf5: route outside-a-request server reads to the shared store ([`456cfa6`](https://github.com/briancray/abide/commit/456cfa662405714df99545e02ed6e3b851da84df))
15
+ - 182cdf5: Breaking: the `global` cache option is renamed `shared`, and it no longer implies retention. `{ global: true }` (memoise across requests forever) becomes `{ shared: true, ttl: Infinity }` — `shared` now only selects the process store, while `ttl` alone controls retention (default `0` on the server = coalesce-only, `Infinity` on the client). Reads made with no request in flight resolve to the shared store instead of an orphan fallback. ([`d94f0f9`](https://github.com/briancray/abide/commit/d94f0f9cb15a5822b2d5824db7e32da27368fe04))
16
+
17
+ ## 0.47.0
18
+
19
+ ### Minor Changes
20
+
21
+ - 981c53f: server-side streaming — defineRpc opts.streaming + rewriteForServer injects it ([`00f6fa8`](https://github.com/briancray/abide/commit/00f6fa8fdedc412808108c959c8ad9cced2b643b))
22
+ - 981c53f: socket/rpc instance methods + reactive-surface follow-through ([`0d3d686`](https://github.com/briancray/abide/commit/0d3d686125d7906e1360af4f040f40f39892caa6))
23
+ - 981c53f: on(source, handler) reaction primitive ([`1cfa4f9`](https://github.com/briancray/abide/commit/1cfa4f94bf2e6cd5b612ae26a449007d2b1e1769))
24
+ - 981c53f: syntactic import-resolved reactive recognition (alias-safe, checker-free) ([`1e3e041`](https://github.com/briancray/abide/commit/1e3e041865541058d36c0f5d759d6b09f1256bb4))
25
+ - 981c53f: capture call errors at the boundary, clear on success/invalidate ([`32f3ee7`](https://github.com/briancray/abide/commit/32f3ee7ae495149a7873b5102b215b1d8c07ca04))
26
+ - 981c53f: bare call reads through the cache store (coalesced, reactive) ([`410e552`](https://github.com/briancray/abide/commit/410e552f5b233a814640737954cb01bf5825e816))
27
+ - 981c53f: typed fn.error(args?) probe over the rpc error registry ([`41f4d4a`](https://github.com/briancray/abide/commit/41f4d4a2127a35327bbc573f3656eda9dcf9307a))
28
+ - 981c53f: parked durable writes are pending, not errored ([`41fefbc`](https://github.com/briancray/abide/commit/41fefbc2d4f25c9364bd3e866edb90e1eb82df4e))
29
+ - 981c53f: .refresh/.patch/.peek instance methods ([`55d4051`](https://github.com/briancray/abide/commit/55d40512d6113a20960d128c3d47b5a54d4d6f98))
30
+ - 981c53f: patch() — optimistic/real-time local value mutation ([`67a2df7`](https://github.com/briancray/abide/commit/67a2df733d4e3948e68d253671daf322816737b0))
31
+ - 981c53f: rpc error registry (last error per call identity, reactive) ([`7138e23`](https://github.com/briancray/abide/commit/7138e23bf0285da2f90fdc08e918a4abe3b749ad))
32
+ - 981c53f: export ./ui/state; withdraw scope()/effect from author doc surface ([`77b5221`](https://github.com/briancray/abide/commit/77b52217d5baa9fa22b26d8aba8b0111237434eb))
33
+ - 981c53f: peek() — synchronous non-triggering value probe ([`78469a8`](https://github.com/briancray/abide/commit/78469a89dac0cc014b8958f8e1ffa85e4fb189ba))
34
+ - 981c53f: pre-bound selector methods on rpc instances (pending/refreshing/invalidate/cache) ([`8154dd0`](https://github.com/briancray/abide/commit/8154dd03ca4a1ac1b8c26292a43f72a85f3a9dab))
35
+ - 981c53f: SWR retention always-on for smart reads; type the smart call ([`844a8b7`](https://github.com/briancray/abide/commit/844a8b79a9b526d5d82e5e7febe57f61f23aa98d))
36
+ - 981c53f: done(sub) terminal reader + tail prober reports done/error ([`8a11ab3`](https://github.com/briancray/abide/commit/8a11ab31417edc59f062042b8181e5eaaa6bc140))
37
+ - 981c53f: state() gains .linked/.computed/.share/.shared ([`b02aead`](https://github.com/briancray/abide/commit/b02aeade95173ddea2ea363f6de2f9e1cc23fc90))
38
+ - 981c53f: refresh() — always-refetch over the selector grammar ([`b7bddb2`](https://github.com/briancray/abide/commit/b7bddb2dba94c269449ba2f52193f8c5ace6d5c1))
39
+ - 981c53f: wire client streaming — detectStreaming codegen + proxy flag ([`bac63fe`](https://github.com/briancray/abide/commit/bac63fe94d08a94cbf887600bf2f61764170dbc0))
40
+ - 981c53f: refresh() + peek() ([`d2f8f1a`](https://github.com/briancray/abide/commit/d2f8f1a5f541707a627a9e196ee0b53a060b721a))
41
+ - 981c53f: broadcast() + tail defaults to 1 ([`dc280e9`](https://github.com/briancray/abide/commit/dc280e9e76a781994ee92af8c98967220e4f793e))
42
+ - 981c53f: type-driven streaming — bare call returns Subscribable, drop .stream() ([`f457a2c`](https://github.com/briancray/abide/commit/f457a2c9c716a35ac398217196af92cf5751d650))
43
+ - 981c53f: emit $$watch for bindings; watch recognized + SSR-stripped (renames on) ([`f4f7812`](https://github.com/briancray/abide/commit/f4f781201c82fdfe28d50e5535d93766b0e130c8))
44
+ - c0db71d: Rebuild the data & reactivity surface around one smart call plus thin modifiers/probes/reactions.
45
+
46
+ **Breaking changes:**
47
+
48
+ - The **bare rpc call is now the smart read** — cached, coalesced, reactive, and stale-while-revalidate by default for replayable (GET/HEAD) reads; writes are coalesce-only. `cache(getFoo, args)` → `getFoo(args)`. The `cache()` function and the `./shared/cache` export are removed (the store remains the internal substrate). Per-call transport options (`signal`/`headers`/`keepalive`/`priority`/`cache`) move to `getFoo.raw(args, init)`; the smart call's second arg is cache/stream options (`ttl`/`tags`/`throttle`/`debounce`/`n`).
49
+ - **`.refresh` / `.patch` / `.peek`** — new global functions (`./shared/refresh`, `./shared/patch`, `./shared/peek`) and rpc instance methods. `.refresh` replaces `.invalidate` (refetch keeping the stale value visible, never blanking); `.patch` mutates the retained value locally with no network; `.peek` reads the retained value synchronously. `fn.invalidate`/`fn.cache` instance methods are removed (`fn.refresh` / the bare call replace them).
50
+ - **`watch(source, handler)`** (`./ui/watch`) is the single reaction primitive, replacing `effect`, `socket.on`, and `cache.on` for authors and the compiler (bindings emit `$$watch`). `effect` is off the taught surface (internal plumbing). Instance sugar mirrors the global for the two subscribable/rpc sources — `socket.watch(handler)` and `fn.watch(handler)` / `fn.watch(args, handler)` (client-only; SSR-inert no-op).
51
+ - **Sockets:** `socket.broadcast(msg)` replaces `.publish`; retention (`tail`) defaults to `1`; `socket.peek()` / `socket.refresh()` added. The `tail()` reactive-consumer function (`./ui/tail`) and `socket.tail()` are off the taught surface — consume the socket directly (`for await` / `watch`) and window via `watch` + `state`; `pending`/`refreshing`/`done`(socket) now register on consumption.
52
+
53
+ - 2a42d90: Remove all later-hydration: the inferred deferred `{#await cache()}` inert path and the explicit `client:idle` / `client:visible` component islands. Both traded eager hydration for a wake-later heuristic, and the inference was the source of the "control renders dead until the wake fires" bug class. A blocking `{#await cache()}` now renders on the server, seeds warm, and hydrates eagerly — live on the first frame. `client:idle` / `client:visible` are no longer recognized directives. Very large grids that relied on island deferral should adopt their own virtualization.
54
+ - f0f8d77: Close the `<select>` and boolean/numeric binding gaps.
55
+
56
+ - **`<select bind:value>` now handles late-mounting options.** Options produced by a `{#for}` block or async data mount _after_ the binding runs, and the browser drops a `value` set that names a not-yet-present option — so the initial value silently failed to apply. The bind now routes through a new `bindSelectValue` runtime helper (`./ui/dom/bindSelectValue`) that re-applies the selection whenever the option set changes (via a `MutationObserver`), covering static, `{#for}`, and async options uniformly.
57
+ - **`<select multiple bind:value>`** binds an array of the selected option values (two-way): the bound array drives which options are `selected`, and a user's picks are collected back into it.
58
+ - **SSR selects the matching option.** A server-rendered `<select bind:value>` now emits `selected` on the matching `<option>` (single) or every member (multiple) — the browser ignores a `value="…"` on the select, so the pre-hydration/no-JS state was wrong before. Option value is taken from its `value` attribute, else its static text.
59
+ - **`<details bind:open>` SSR fix.** `open` is a boolean attribute, so the old generic path emitted `open="false"` and rendered the element _open_ when closed. It now emits the bare attribute only when truthy, like `checked`.
60
+ - **Numeric input coercion.** `bind:value` on `<input type="number">` / `type="range"` now writes back a number (via `valueAsNumber`, empty field → `undefined`) instead of a string, so number-typed state stays a number.
61
+
62
+ ### Patch Changes
63
+
64
+ - 981c53f: rpc error registry is client-only — no cross-request SSR leak ([`00c6efb`](https://github.com/briancray/abide/commit/00c6efb0053c0bcd0e44748ebcb85826ec382b27))
65
+ - 981c53f: decide the tool namespace once — mcpTools replaces buildTools/callTool ([`05b31f1`](https://github.com/briancray/abide/commit/05b31f18a02c2ec344f8d17bcbda50d534aad104))
66
+ - 981c53f: migrate .abide fixtures off scope() onto the imported reactive surface ([`0d487db`](https://github.com/briancray/abide/commit/0d487dbf02c6368bd1f889c6679951fc49c364ca))
67
+ - 981c53f: Breaking: re-home subscribable probes onto consume; remove tail() ([`1e0ae9f`](https://github.com/briancray/abide/commit/1e0ae9ff75f2c48921c807c6c4445930933533c6))
68
+ - 981c53f: desugar via import-resolution; bare imported primitives are the surface ([`21a52a5`](https://github.com/briancray/abide/commit/21a52a5b733da810a155eab6863d35b9e9ddd459))
69
+ - 981c53f: keep SSR/client text claims congruent ([`383f7ab`](https://github.com/briancray/abide/commit/383f7ab5582533f7ecaad7c76b01c6ce7c114818))
70
+ - 981c53f: shadow + nested-script recognize imported reactives; effect author-imported ([`415a232`](https://github.com/briancray/abide/commit/415a23223b44ff60776f5f68226d93dc9c2c5c7c))
71
+ - 981c53f: regenerate README + AGENTS.md for the imported-reactive / flat-cache / bare-streaming surface ([`42a7404`](https://github.com/briancray/abide/commit/42a7404c00fa90334d14d715d2a17095f5b5456d))
72
+ - 981c53f: extract finalizeResponse — the dynamic route's wire-handling step ([`4369119`](https://github.com/briancray/abide/commit/4369119ae41e3338af2769790ccd4a2b3cad68de))
73
+ - 981c53f: regenerate README + AGENTS.md for the new data/reactivity surface ([`4c6d6eb`](https://github.com/briancray/abide/commit/4c6d6eb1033f64770c8e2154e856f00f6a5a834f))
74
+ - 981c53f: hoist value consts above __Props so keyof typeof prop annotations resolve ([`581eebe`](https://github.com/briancray/abide/commit/581eebefd41f05d2875f96a3965d90c2c7a1da63))
75
+ - 981c53f: import-resolution is the sole reactive recognition path ([`5b13721`](https://github.com/briancray/abide/commit/5b13721741e5c7bd85844b21f23372b588f05d24))
76
+ - 981c53f: bump scaffold @abide/abide floor to ^0.46.0 ([`5d10cb5`](https://github.com/briancray/abide/commit/5d10cb5b2388fd45e0403b798ca8908b08f0a0e4))
77
+ - 981c53f: reject streaming rpcs at compile time (@deprecated overload → never) ([`7277533`](https://github.com/briancray/abide/commit/7277533c74eec1103c4021844bbb5547f8b452de))
78
+ - 981c53f: narrow the select-bind indexed access so tsgo passes under noUncheckedIndexedAccess ([`796340c`](https://github.com/briancray/abide/commit/796340c5aed2e21a1a46411fda30a4cb9450a95d))
79
+ - 981c53f: resolve full-codebase review findings (22 bugs + cleanups) ([`8523d25`](https://github.com/briancray/abide/commit/8523d25d596d435ffea989ce93da75b4210bb655))
80
+ - 981c53f: server streaming rewrite handles trailing comma / empty opts ([`89eb801`](https://github.com/briancray/abide/commit/89eb80142cae47fc53b3654e0c81f1bd9a394ecc))
81
+ - 981c53f: unify **SSR** payload behind a shared contract + exhaustive boot seed ([`8b242f1`](https://github.com/briancray/abide/commit/8b242f1d0dab035a3bd439370b6a5066d5254d86))
82
+ - 981c53f: sync examples, README, and AGENTS.md to {#snippet} block syntax ([`8fa8e72`](https://github.com/briancray/abide/commit/8fa8e7256eb2ddb1df48ab19c39ebfa1a86ac7c8))
83
+ - 981c53f: repoint scope host to plumbing exports; drop dead reactive imports ([`a58aee6`](https://github.com/briancray/abide/commit/a58aee6418136de675a56190e4b7ee7dc84d54cc))
84
+ - 981c53f: type {#snippet} as Snippet-returning in the check shadow ([`b2bca21`](https://github.com/briancray/abide/commit/b2bca217dfc12a6fe5870bdecf0bca2ef4e4b628))
85
+ - 981c53f: Breaking: flatten cache(fn, args, options) — direct read-through, drop the wrapper ([`b8d356f`](https://github.com/briancray/abide/commit/b8d356fb078a548433311dd19a2bec930c30e29a))
86
+ - 981c53f: migrate template + scaffold to the new surface ([`bc1ccfd`](https://github.com/briancray/abide/commit/bc1ccfdc7699cc0de034bdadcf79b7cc5e3695bd))
87
+ - 981c53f: guard detectStreaming indexed access under noUncheckedIndexedAccess ([`bc5cecb`](https://github.com/briancray/abide/commit/bc5cecbf5b6669297a90e16cf8762110b928b641))
88
+ - 981c53f: one module per resolved runtime value — the slot is the seam ([`c2dcde4`](https://github.com/briancray/abide/commit/c2dcde496e43868aeb94c23773e9f6630820b3c9))
89
+ - 981c53f: declare pending/refreshing/invalidate/cache instance methods ([`c743df8`](https://github.com/briancray/abide/commit/c743df80777f6dcf1c7d326713c6124254a4bca4))
90
+ - 981c53f: record why the type-check shadow stays off the Plan model (ADR-0014) ([`c9f77dd`](https://github.com/briancray/abide/commit/c9f77ddb44832354e71d711d035be497fa258ef6))
91
+ - 981c53f: flatten cache() and update .stream() doc to bare streaming-rpc call ([`cfdf715`](https://github.com/briancray/abide/commit/cfdf71546bf14c2e29714d057307b14081e048a9))
92
+ - 981c53f: Breaking: remove .invalidate/.cache instance methods ([`e809b22`](https://github.com/briancray/abide/commit/e809b22280ff851e51dbbbc3e65c6e8e6fb86a81))
93
+ - 981c53f: Breaking: remove ./shared/cache export; retag ./ui/effect as plumbing ([`e9b5e94`](https://github.com/briancray/abide/commit/e9b5e9463f6f201bbf7e7b9455e5090e4c4c6ba6))
94
+ - 981c53f: fold route matching into shared matchRoute ([`f00630b`](https://github.com/briancray/abide/commit/f00630bd2366f968e412d61b7b791e679d985414))
95
+ - 981c53f: Breaking: remove .publish, migrate all call sites to .broadcast ([`fa0fd00`](https://github.com/briancray/abide/commit/fa0fd00f5f891d9f804155aaf496f6cf0eb9c535))
96
+
3
97
  ## 0.46.0
4
98
 
5
99
  ### Minor Changes
package/README.md CHANGED
@@ -1,242 +1,277 @@
1
1
  # abide
2
2
 
3
- **One typed declaration fans out to HTTP, a CLI, an MCP tool, and an OpenAPI
4
- specon a single Bun runtime.**
3
+ **One typed declaration, every surface: an RPC you write once fans out to HTTP,
4
+ a CLI, an MCP tool, and an OpenAPI operation and the same callable runs
5
+ in-process on the server and over fetch in the browser.**
5
6
 
6
- abide is a type-safe isomorphic framework: you declare an RPC, a socket, and a
7
- `.abide` component once, and the bundler swaps the runtime per side — the same
8
- callable is a direct in-process call on the server and a typed `fetch` in the
9
- browser. Built for humans *and* machines: every schema-carrying RPC is
10
- simultaneously a browser endpoint, a CLI subcommand, an MCP tool, and an
11
- OpenAPI operation.
7
+ abide is an isomorphic multimodal HTTP framework built for humans and machines
8
+ in a single Bun runtime: file-path routing, Standard Schema validation, and a
9
+ compiled `.abide` UI layer that server-renders, streams, and hydrates the same
10
+ components. The bundler swaps each callable's runtime per side — same name,
11
+ same behavior, no client/server forks in app code.
12
12
 
13
- One direct dependency (TypeScript). One runtime (Bun ≥ 1.3.0).
13
+ - One direct dependency (TypeScript). One runtime (Bun ≥ 1.3).
14
14
 
15
15
  ## Quick start
16
16
 
17
17
  ```sh
18
- # Scaffold a project, install it, and start the dev server (in a TTY).
19
- bunx abide scaffold my-app
18
+ bunx abide scaffold my-app # scaffolds, installs, and starts the dev server
19
+ ```
20
+
21
+ Or run the kitchen-sink example, which demos the full surface:
20
22
 
21
- # Or explore the full feature set in the kitchen-sink example.
23
+ ```sh
22
24
  git clone https://github.com/briancray/abide
23
- cd abide/examples/kitchen-sink
24
- bun install
25
- bun run dev
25
+ cd abide && bun install
26
+ cd examples/kitchen-sink && bun run dev
26
27
  ```
27
28
 
28
29
  ## RPCs
29
30
 
30
- An RPC is one export per file under `src/server/rpc/`. The file path is the URL
31
- (under `/rpc/`), the import (`GET`/`POST`/`PUT`/`PATCH`/`DELETE`/`HEAD`) picks
32
- the HTTP method, and an optional `inputSchema` any Standard Schema (zod,
33
- valibot, arktype, unadapted) validates the args and projects the MCP tool, the
34
- CLI flags, and the OpenAPI operation.
31
+ An RPC is one exported handler per file under `src/server/rpc/` the file path
32
+ is the URL. The export wraps the handler in an HTTP-method helper (`GET`,
33
+ `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`); a Standard Schema `inputSchema`
34
+ (zod, valibot, arktype — no adapter) validates the args and projects the CLI
35
+ flags, the MCP tool, and the OpenAPI operation from the one declaration.
35
36
 
36
37
  ```ts
37
38
  // src/server/rpc/getMessages.ts
38
39
  import { GET } from '@abide/abide/server/GET'
39
40
  import { json } from '@abide/abide/server/json'
40
41
  import { z } from 'zod'
42
+ import { listMessages } from '$server/store'
41
43
 
42
- const rooms: Record<string, { id: string; text: string }[]> = {}
43
-
44
- // GET /rpc/getMessages?room=lobby
45
44
  export const getMessages = GET(
46
- (args: { room: string }) => json(rooms[args.room] ?? []),
47
- { inputSchema: z.object({ room: z.coerce.string() }) },
48
- )
49
- ```
50
-
51
- ```ts
52
- // src/server/rpc/postMessage.ts
53
- import { POST } from '@abide/abide/server/POST'
54
- import { json } from '@abide/abide/server/json'
55
- import { error } from '@abide/abide/server/error'
56
- import { z } from 'zod'
57
-
58
- const rateLimited = error.typed('rateLimited', 429)
59
-
60
- // A mutating method never auto-exposes to MCP — opt in explicitly.
61
- export const postMessage = POST(
62
- (args: { room: string; text: string }) => {
63
- if (args.text.length > 500) return rateLimited()
64
- return json({ ok: true })
45
+ async ({ room, limit }) => json(await listMessages(room, limit)),
46
+ {
47
+ inputSchema: z.object({
48
+ room: z.string(),
49
+ limit: z.coerce.number().default(50),
50
+ }),
65
51
  },
66
- { inputSchema: z.object({ room: z.string(), text: z.string() }), clients: { mcp: true } },
67
52
  )
68
53
  ```
69
54
 
70
- One declaration, five surfaces:
55
+ That one file fans out:
71
56
 
72
57
  ```text
73
- export const getMessages = GET(fn, { inputSchema })
74
-
75
- ┌────────────┬─────────────┼─────────────┬────────────┐
76
- ▼ ▼ ▼ ▼ ▼
77
- cache(fn)() typed fetch MCP tool CLI subcmd OpenAPI op
78
- (SSR, warm (browser, (read-only (any schema- (/openapi
79
- hydration) swapped) GET/HEAD) carrying) .json)
58
+ src/server/rpc/getMessages.ts
59
+
60
+ ├─ SSR / server await getMessages({ room }) in-process, no HTTP
61
+ ├─ browser await getMessages({ room }) typed fetch proxy
62
+ ├─ HTTP GET /rpc/getMessages?room=…
63
+ ├─ CLI my-app get-messages --room …
64
+ ├─ MCP tool: get-messages
65
+ └─ OpenAPI operation in /openapi.json
80
66
  ```
81
67
 
82
- A schema unlocks the CLI for any RPC and MCP for read-only methods (`GET`/`HEAD`);
83
- a mutating method (`POST`/`PUT`/`PATCH`/`DELETE`) reaches MCP only with an
84
- explicit `clients: { mcp: true }`. Consume an RPC four ways: `cache(fn)()`
85
- in-process (warm SSR + hydration), the swapped `fetch` in the browser,
86
- `fn.raw(args)` for the underlying `Response`, and `fn.stream(args)` to iterate
87
- `jsonl`/`sse` frames.
88
-
89
- > Query args travel as strings — coerce them (`z.coerce.number()`). A per-RPC
90
- > `timeout` returns 504 on every surface; it is distinct from the client-wide
91
- > `ABIDE_CLIENT_TIMEOUT`.
68
+ A schema is the gate: it unlocks the CLI, and for read-only methods
69
+ (GET/HEAD) — the MCP tool. A mutating method (POST/PUT/PATCH/DELETE) never
70
+ auto-exposes to MCP; it needs an explicit `clients: { mcp: true }`.
71
+
72
+ The bare call **is** the smart read cached, coalesced, reactive,
73
+ stale-while-revalidate — and it is isomorphic: the same callable resolves
74
+ in-process during SSR (its value baked into the HTML for warm hydration) and
75
+ over `fetch` in the browser. Around it:
76
+
77
+ - `getMessages.raw(args, init?)` — the raw `Response`; per-call transport
78
+ options (`signal`, `headers`, …) live here
79
+ - `getMessages.refresh(args?)` — refetch, keeping the stale value visible
80
+ - `getMessages.patch(args?, updater)` — mutate the retained value locally,
81
+ no network
82
+ - `getMessages.peek(args?)` — read the retained value synchronously
83
+ - `getMessages.pending(args?)` / `.refreshing(args?)` / `.error(args?)` —
84
+ reactive probes
85
+ - a handler that returns `jsonl()`/`sse()` makes the bare call return a
86
+ `Subscribable` you `for await` — detected at build time, nothing to declare
87
+
88
+ > Query args travel as strings — use `z.coerce.*` for numbers and booleans on
89
+ > GET/HEAD. The per-RPC `timeout` option is a server-side handler deadline
90
+ > (504, enforced on every surface); `ABIDE_CLIENT_TIMEOUT` bounds browser calls
91
+ > and is separate.
92
92
 
93
93
  ## Sockets
94
94
 
95
- A socket is one broadcast topic per file under `src/server/sockets/`. A
96
- `Socket<T>` is an isomorphic `AsyncIterable<T>` — iterate for the live stream,
97
- `.tail(count)` to seed from the retained tail. Every socket multiplexes onto one
98
- WebSocket at `/__abide/sockets`; the server publishes in-process and fans out,
99
- the browser proxy sends a `pub` frame.
95
+ A socket is one broadcast topic per file under `src/server/sockets/` — the
96
+ export name is the topic. A `Socket<T>` is an isomorphic `AsyncIterable<T>`:
97
+ server code iterates and broadcasts in-process; browser code gets the same
98
+ shape over one multiplexed WebSocket at `/__abide/sockets`.
100
99
 
101
100
  ```ts
102
- // src/server/sockets/messages.ts
101
+ // src/server/sockets/chat.ts
103
102
  import { socket } from '@abide/abide/server/socket'
104
103
  import { z } from 'zod'
105
104
 
106
- export const messages = socket({
107
- schema: z.object({ room: z.string(), text: z.string() }),
108
- tail: 50, // retain the last 50 frames for late joiners / reconnects
109
- ttl: 60_000, // evict retained frames older than 60s before replay
105
+ export const chat = socket({
106
+ schema: z.object({ author: z.string(), text: z.string() }),
107
+ tail: 50, // retain the last 50 frames for late joiners (default 1)
108
+ ttl: 60_000, // retained frames expire after a minute
109
+ clientPublish: true, // allow browser publishes (off by default)
110
110
  })
111
111
  ```
112
112
 
113
- The HTTP face lives at `/__abide/sockets/<name>`: `GET` returns the retained
114
- tail, `POST` publishes (gated by `clientPublish`).
113
+ `chat.broadcast(msg)` publishes from either side schema-validated on the
114
+ server, and client publishes are gated by `clientPublish`. `for await (const
115
+ msg of chat)` is the live stream; `chat.peek()` reads the latest retained
116
+ frame; `chat.refresh()` re-pulls the server tail after a reconnect. A schema
117
+ also advertises the topic to MCP and the CLI.
118
+
119
+ Every exposed socket has an HTTP face at `/__abide/sockets/<name>`: `GET`
120
+ returns the retained tail as JSON (or a live SSE stream under
121
+ `Accept: text/event-stream`), `POST` publishes — gated by `clientPublish`.
115
122
 
116
123
  ## Components
117
124
 
118
- A `.abide` component is a single file: a leading `<script>` (module imports plus
119
- reactive setup), the template, and an optional component-scoped `<style>`. This
120
- page imports the RPC from §RPCs and the socket from §Sockets and drives them
121
- through the whole template grammar.
125
+ Pages are `.abide` single-file components under `src/ui/pages/` (`page.abide` /
126
+ `layout.abide`; a `[id]` folder becomes a route param read via `props()`). A
127
+ component is HTML plus `{expr}` bindings and `{#…}` control-flow blocks.
128
+ Reactive state comes from imported primitives the compiler lowers, so inside a
129
+ component you read and write state as plain variables. The same file renders on
130
+ the server — blocking awaits inline, streaming awaits out of order, cached
131
+ reads seeded warm — and hydrates in the browser.
122
132
 
123
- Reactive state is reached only through `scope()`. The authoring default is the
124
- destructure-once idiom — pull the primitives off `scope()` at the top of the
125
- `<script>`, then call them bare. `computed` is read-only; `effect` is client-only
126
- (stripped from SSR). A bare `state`/`computed`/`linked`/`effect` with no
127
- `scope()` destructure is a compile error.
133
+ A child component renders whatever you nest in it at `{children()}`:
128
134
 
129
135
  ```html
130
136
  <script>
131
- import { cache } from '@abide/abide/shared/cache'
132
- import { tail } from '@abide/abide/ui/tail'
133
- import { html } from '@abide/abide/ui/html'
134
- import { getMessages } from '$server/rpc/getMessages.ts'
135
- import { postMessage } from '$server/rpc/postMessage.ts'
136
- import { messages } from '$server/sockets/messages.ts'
137
- import MessageCard from '$ui/MessageCard.abide'
138
-
139
- const { state, computed, linked, effect } = scope()
140
- const { room = 'lobby' } = props()
141
-
142
- let draft = state('')
143
- let showRaw = state(false)
144
- let filter = state('all')
145
- const trimmed = computed(() => draft.trim())
146
- const outgoing = linked(() => room + ': ' + trimmed)
147
- const banner = html`<strong>#${room}</strong>`
148
-
149
- // client-only reaction — never runs during SSR
150
- effect(() => console.log('draft is now', draft))
151
-
152
- // the derived two-way binding target, read down / written up
153
- const get = () => outgoing
154
- const set = (value) => (draft = value)
155
- // mutating RPC from an event handler
156
- const send = () => {
157
- postMessage({ room, text: trimmed })
158
- draft = ''
159
- }
137
+ const { title } = props()
160
138
  </script>
161
139
 
162
- {#snippet chip(label)}
163
- <span class="rounded bg-slate-100 px-2 text-xs">{label}</span>
140
+ <section>
141
+ <h2>{title}</h2>
142
+ {#if children}{children()}{:else}<p>Nothing here yet.</p>{/if}
143
+ </section>
144
+
145
+ <style>
146
+ section {
147
+ border: 1px solid gray;
148
+ }
149
+ </style>
150
+ ```
151
+
152
+ And one page tying together the RPC from above, the socket, and the whole
153
+ template grammar:
154
+
155
+ ```html
156
+ <script>
157
+ import { getMessages } from '$server/rpc/getMessages'
158
+ import { sendMessage } from '$server/rpc/sendMessage' // POST, declared like getMessages
159
+ import { chat } from '$server/sockets/chat'
160
+ import { state } from '@abide/abide/ui/state'
161
+ import { watch } from '@abide/abide/ui/watch'
162
+ import { html } from '@abide/abide/ui/html'
163
+ import Panel from '$ui/components/Panel.abide'
164
+
165
+ const { room = 'lobby' } = props()
166
+
167
+ let draft = state('')
168
+ let author = state('anon')
169
+ let tone = state('friendly')
170
+ let notify = state(true)
171
+ let volume = state(5, (next, previous) => (Number.isFinite(next) ? next : previous))
172
+ let shout = state.computed(() => draft.toUpperCase())
173
+ let roomDraft = state.linked(() => room)
174
+
175
+ // watch is the one reaction primitive: a socket, an rpc, or a tracked thunk.
176
+ watch(chat, () => getMessages.refresh({ room }))
177
+ watch(() => console.log(author, tone))
178
+
179
+ // a writable computed lives at the binding: bind:value={{ get, set }}
180
+ const get = () => draft.trim()
181
+ const set = (next) => (draft = next)
182
+
183
+ const focus = (element) => element.focus()
184
+ const panelProps = { title: 'Live feed' }
185
+ const draftAttrs = { placeholder: 'say something', autocomplete: 'off' }
186
+
187
+ async function send() {
188
+ await sendMessage({ room: roomDraft, author, text: draft })
189
+ draft = ''
190
+ }
191
+ </script>
192
+
193
+ {#snippet line(message)}
194
+ <li>{message.author}: {message.text}</li>
164
195
  {/snippet}
165
196
 
166
- <section class="p-4" attach={(node) => node.focus()} {...{ id: 'chat' }}>
167
- <h1 class:live={showRaw} style:opacity={showRaw ? 1 : 0.6}>{banner}</h1>
197
+ <Panel {...panelProps}>
198
+ <h1 class:muted={!notify} style:opacity={notify ? '1' : '0.6'}>
199
+ {room} — {shout}
200
+ </h1>
168
201
 
169
- {#await cache(getMessages)({ room })}
202
+ {#await getMessages({ room })}
170
203
  <p>loading…</p>
171
- {:then history}
172
- {#if history.length > 0}
173
- {#for message, i of history by message.id}
174
- <MessageCard {...message}>
175
- {chip(i)}
176
- <p>{message.text}</p>
177
- </MessageCard>
178
- {/for}
179
- {:else if showRaw}
180
- <p>no history yet</p>
204
+ {:then messages}
205
+ {#if messages.length === 0}
206
+ <p>No messages yet.</p>
207
+ {:else if messages.length < 100}
208
+ <ul>
209
+ {#for message, i of messages by message.id}
210
+ {line(message)}
211
+ {/for}
212
+ </ul>
181
213
  {:else}
182
- <p>say hello</p>
214
+ <script>
215
+ // branch-local state, re-seeded each time this branch mounts
216
+ let offset = state(0)
217
+ </script>
218
+ <p>huge room — showing from {offset}</p>
219
+ <button onclick={() => (offset = offset + 100)}>next</button>
220
+ <style>
221
+ p {
222
+ font-variant-numeric: tabular-nums;
223
+ }
224
+ </style>
183
225
  {/if}
184
226
  {:catch error}
185
227
  <p>failed to load: {error.message}</p>
186
228
  {:finally}
187
- <hr />
229
+ <p>checked just now</p>
188
230
  {/await}
189
231
 
190
- {#for await frame of messages.tail(10)}
191
- <p class="live-frame">{frame.text}</p>
192
- {/for}
193
-
194
- {#switch filter}
195
- {:case 'all'}<p>showing all rooms</p>
196
- {:case 'mine'}<p>showing my rooms</p>
197
- {:default}<p>—</p>
232
+ <ol>
233
+ {#for await message of chat}
234
+ {line(message)}
235
+ {/for}
236
+ </ol>
237
+
238
+ {#switch tone}
239
+ {:case 'friendly'}
240
+ <p>{html`<em>be&nbsp;kind</em>`}</p>
241
+ {:default}
242
+ <p>say anything</p>
198
243
  {/switch}
199
244
 
200
245
  {#try}
201
- {#if showRaw}
202
- <script>const { computed } = scope()
203
- const label = computed(() => 'raw view active')</script>
204
- <pre>{label}</pre>
205
- {/if}
206
- {:catch error}
207
- <p>render error: {error.message}</p>
208
- {:finally}
209
- <small>rendered</small>
246
+ <p>{JSON.parse(draft).summary}</p>
247
+ {:catch}
248
+ <p>draft isn't JSON yet</p>
210
249
  {/try}
211
250
 
212
- <form onsubmit={send}>
213
- <input bind:value={draft} placeholder="message" />
214
- <label><input type="checkbox" bind:checked={showRaw} /> raw</label>
215
- <label><input type="radio" bind:group={filter} value="all" /> all</label>
216
- <label><input type="radio" bind:group={filter} value="mine" /> mine</label>
251
+ <form onsubmit={(event) => { event.preventDefault(); send() }}>
252
+ <input attach={focus} bind:value={draft} {...draftAttrs} />
217
253
  <input bind:value={{ get, set }} />
218
- <button type="submit">send</button>
254
+ <label><input type="checkbox" bind:checked={notify} /> notify</label>
255
+ <label><input type="radio" bind:group={tone} value="friendly" /> friendly</label>
256
+ <label><input type="radio" bind:group={tone} value="blunt" /> blunt</label>
257
+ <input type="range" bind:value={volume} min="0" max="10" />
258
+ <select bind:value={author}>
259
+ <option>anon</option>
260
+ <option>me</option>
261
+ </select>
262
+ <button disabled={draft === ''}>send</button>
219
263
  </form>
220
- </section>
264
+ </Panel>
221
265
 
222
266
  <style>
223
- .live { color: teal; }
267
+ form {
268
+ display: flex;
269
+ gap: 0.5rem;
270
+ }
271
+ .muted {
272
+ color: gray;
273
+ }
224
274
  </style>
225
275
  ```
226
276
 
227
- Content nested inside a capitalised child component renders where the child
228
- calls `{children()}` — the single slot, with `{#if children}…{:else}…{/if}` as
229
- its fallback (there is no `<slot>` element and no named slots):
230
-
231
- ```html
232
- <script>
233
- const { text = '' } = props()
234
- </script>
235
-
236
- <article class="card rounded border p-2">
237
- <p class="font-medium">{text}</p>
238
- {#if children}{children()}{:else}<em>no body</em>{/if}
239
- </article>
240
- ```
241
-
242
277
  MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abide/abide",
3
- "version": "0.46.0",
3
+ "version": "0.48.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "Isomorphic multimodal HTTP framework built for humans and machines in a single Bun runtime",
@@ -46,7 +46,6 @@
46
46
  "./server/error": "./src/lib/server/error.ts",
47
47
  "./server/redirect": "./src/lib/server/redirect.ts",
48
48
  "./server/request": "./src/lib/server/request.ts",
49
- "./server/reachable": "./src/lib/server/reachable.ts",
50
49
  "./server/cookies": "./src/lib/server/cookies.ts",
51
50
  "./server/env": "./src/lib/server/env.ts",
52
51
  "./server/appDataDir": "./src/lib/server/appDataDir.ts",
@@ -57,10 +56,13 @@
57
56
  "./server/rpc/defineRpc": "./src/lib/server/rpc/defineRpc.ts",
58
57
  "./server/sockets/defineSocket": "./src/lib/server/sockets/defineSocket.ts",
59
58
  "./server/prompts/definePrompt": "./src/lib/server/prompts/definePrompt.ts",
60
- "./ui/tail": "./src/lib/ui/tail.ts",
61
- "./shared/cache": "./src/lib/shared/cache.ts",
59
+ "./ui/watch": "./src/lib/ui/watch.ts",
62
60
  "./shared/pending": "./src/lib/shared/pending.ts",
61
+ "./shared/patch": "./src/lib/shared/patch.ts",
62
+ "./shared/peek": "./src/lib/shared/peek.ts",
63
+ "./shared/refresh": "./src/lib/shared/refresh.ts",
63
64
  "./shared/refreshing": "./src/lib/shared/refreshing.ts",
65
+ "./shared/done": "./src/lib/shared/done.ts",
64
66
  "./shared/HttpError": "./src/lib/shared/HttpError.ts",
65
67
  "./shared/ValidationErrorData": "./src/lib/shared/types/ValidationErrorData.ts",
66
68
  "./shared/snippet": "./src/lib/shared/snippet.ts",
@@ -68,16 +70,17 @@
68
70
  "./shared/health": "./src/lib/shared/health.ts",
69
71
  "./shared/log": "./src/lib/shared/log.ts",
70
72
  "./shared/online": "./src/lib/shared/online.ts",
73
+ "./shared/reachable": "./src/lib/shared/reachable.ts",
71
74
  "./shared/page": "./src/lib/shared/page.ts",
72
75
  "./shared/trace": "./src/lib/shared/trace.ts",
73
76
  "./shared/url": "./src/lib/shared/url.ts",
74
77
  "./shared/createSubscriber": "./src/lib/shared/createSubscriber.ts",
75
78
  "./ui/html": "./src/lib/ui/html.ts",
79
+ "./ui/state": "./src/lib/ui/state.ts",
76
80
  "./ui/effect": "./src/lib/ui/effect.ts",
77
- "./ui/scope": "./src/lib/ui/scope.ts",
78
- "./ui/deferResume": "./src/lib/ui/deferResume.ts",
79
- "./ui/enterScope": "./src/lib/ui/enterScope.ts",
80
- "./ui/exitScope": "./src/lib/ui/exitScope.ts",
81
+ "./ui/currentScope": "./src/lib/ui/scope.ts",
82
+ "./ui/enterRenderScope": "./src/lib/ui/enterScope.ts",
83
+ "./ui/exitRenderScope": "./src/lib/ui/exitScope.ts",
81
84
  "./ui/outbox": "./src/lib/ui/outbox.ts",
82
85
  "./ui/dom/mount": "./src/lib/ui/dom/mount.ts",
83
86
  "./ui/dom/mountChild": "./src/lib/ui/dom/mountChild.ts",
@@ -100,6 +103,7 @@
100
103
  "./ui/dom/attr": "./src/lib/ui/dom/attr.ts",
101
104
  "./ui/dom/on": "./src/lib/ui/dom/on.ts",
102
105
  "./ui/dom/attach": "./src/lib/ui/dom/attach.ts",
106
+ "./ui/dom/bindSelectValue": "./src/lib/ui/dom/bindSelectValue.ts",
103
107
  "./ui/dom/each": "./src/lib/ui/dom/each.ts",
104
108
  "./ui/dom/eachAsync": "./src/lib/ui/dom/eachAsync.ts",
105
109
  "./ui/dom/when": "./src/lib/ui/dom/when.ts",