@abide/abide 0.42.0 → 0.43.1

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 (146) hide show
  1. package/AGENTS.md +234 -300
  2. package/CHANGELOG.md +41 -0
  3. package/README.md +50 -93
  4. package/package.json +3 -2
  5. package/src/abideResolverPlugin.ts +104 -14
  6. package/src/buildCli.ts +1 -1
  7. package/src/discoveryEntry.ts +3 -3
  8. package/src/lib/bundle/BundleMenuItem.ts +1 -1
  9. package/src/lib/cli/createClient.ts +12 -12
  10. package/src/lib/cli/dispatchCommand.ts +1 -1
  11. package/src/lib/cli/printSessionStatus.ts +1 -1
  12. package/src/lib/cli/resolveCliTarget.ts +1 -1
  13. package/src/lib/cli/runCli.ts +1 -1
  14. package/src/lib/cli/types/CliManifest.ts +1 -1
  15. package/src/lib/cli/types/CliManifestEntry.ts +2 -2
  16. package/src/lib/mcp/annotationsForMethod.ts +5 -5
  17. package/src/lib/mcp/createMcpServer.ts +3 -3
  18. package/src/lib/mcp/mcpSurface.ts +14 -14
  19. package/src/lib/mcp/types/McpServerOptions.ts +1 -1
  20. package/src/lib/server/DELETE.ts +4 -4
  21. package/src/lib/server/GET.ts +4 -4
  22. package/src/lib/server/HEAD.ts +4 -4
  23. package/src/lib/server/PATCH.ts +4 -4
  24. package/src/lib/server/POST.ts +4 -4
  25. package/src/lib/server/PUT.ts +4 -4
  26. package/src/lib/server/agent.ts +4 -4
  27. package/src/lib/server/env.ts +1 -1
  28. package/src/lib/server/error.ts +49 -7
  29. package/src/lib/server/json.ts +1 -1
  30. package/src/lib/server/rpc/buildErrorConstructors.ts +19 -0
  31. package/src/lib/server/rpc/{defineVerb.ts → defineRpc.ts} +52 -34
  32. package/src/lib/server/rpc/{dispatchVerbInProcess.ts → dispatchRpcInProcess.ts} +3 -3
  33. package/src/lib/server/rpc/fieldErrorsFromIssues.ts +23 -0
  34. package/src/lib/server/rpc/{findVerbByCommandName.ts → findRpcByCommandName.ts} +5 -5
  35. package/src/lib/server/rpc/parseArgs.ts +6 -6
  36. package/src/lib/server/rpc/readBodyWithinLimit.ts +2 -2
  37. package/src/lib/server/rpc/registerRpc.ts +6 -0
  38. package/src/lib/server/rpc/{verbRegistry.ts → rpcRegistry.ts} +4 -4
  39. package/src/lib/server/rpc/{runWithVerbTimeout.ts → runWithRpcTimeout.ts} +4 -4
  40. package/src/lib/server/rpc/types/RemoteHandler.ts +9 -3
  41. package/src/lib/server/rpc/types/{VerbHelper.ts → RpcHelper.ts} +57 -23
  42. package/src/lib/server/rpc/types/{VerbRegistryEntry.ts → RpcRegistryEntry.ts} +3 -3
  43. package/src/lib/server/rpc/types/TypedResponse.ts +2 -2
  44. package/src/lib/server/rpc/unprocessed.ts +6 -7
  45. package/src/lib/server/rpc/validationError.ts +17 -0
  46. package/src/lib/server/runtime/buildInspectorSurface.ts +7 -7
  47. package/src/lib/server/runtime/buildOpenApiSpec.ts +6 -6
  48. package/src/lib/server/runtime/createRouteDispatcher.ts +7 -7
  49. package/src/lib/server/runtime/createServer.ts +4 -4
  50. package/src/lib/server/runtime/crossOriginForbidden.ts +1 -1
  51. package/src/lib/server/runtime/crossOriginGate.ts +4 -4
  52. package/src/lib/server/runtime/logExposedSurfaces.ts +4 -4
  53. package/src/lib/server/runtime/registryManifests.ts +2 -2
  54. package/src/lib/server/runtime/types/InspectorCacheEntry.ts +1 -1
  55. package/src/lib/server/runtime/types/InspectorRpc.ts +27 -0
  56. package/src/lib/server/runtime/types/InspectorSurface.ts +4 -4
  57. package/src/lib/server/runtime/types/RequestStore.ts +1 -1
  58. package/src/lib/server/runtime/warnUnguardedMcp.ts +2 -2
  59. package/src/lib/server/sockets/createSocketDispatcher.ts +15 -14
  60. package/src/lib/server/sockets/types/SocketOperation.ts +2 -2
  61. package/src/lib/shared/HttpError.ts +15 -1
  62. package/src/lib/shared/UNREACHABLE_STATUSES.ts +13 -0
  63. package/src/lib/shared/buildRpcRequest.ts +5 -5
  64. package/src/lib/shared/cache.ts +50 -168
  65. package/src/lib/shared/carriesBodyArgs.ts +4 -4
  66. package/src/lib/shared/createCacheStore.ts +12 -0
  67. package/src/lib/shared/createRemoteFunction.ts +11 -6
  68. package/src/lib/shared/decodeResponse.ts +2 -2
  69. package/src/lib/shared/detectRpcMethod.ts +17 -0
  70. package/src/lib/shared/emitLogRecord.ts +3 -3
  71. package/src/lib/shared/extraForwardHeaders.ts +1 -1
  72. package/src/lib/shared/findExportCallSite.ts +30 -273
  73. package/src/lib/shared/forwardHeaders.ts +2 -2
  74. package/src/lib/shared/httpErrorFor.ts +26 -0
  75. package/src/lib/shared/isAsciiWhitespace.ts +5 -0
  76. package/src/lib/shared/isIdentPart.ts +9 -0
  77. package/src/lib/shared/isIdentStart.ts +8 -0
  78. package/src/lib/shared/isReadOnlyMethod.ts +4 -4
  79. package/src/lib/shared/keyForRemoteCall.ts +4 -4
  80. package/src/lib/shared/log.ts +1 -1
  81. package/src/lib/shared/outboxProbeSlot.ts +20 -0
  82. package/src/lib/shared/pending.ts +4 -1
  83. package/src/lib/shared/prepareRpcModule.ts +96 -14
  84. package/src/lib/shared/probeRegistries.ts +17 -4
  85. package/src/lib/shared/queryStringFromArgs.ts +1 -1
  86. package/src/lib/shared/remoteMetaStore.ts +2 -2
  87. package/src/lib/shared/resolveClientFlags.ts +1 -1
  88. package/src/lib/shared/skipNonCode.ts +230 -0
  89. package/src/lib/shared/streamResponse.ts +9 -6
  90. package/src/lib/shared/stripImport.ts +3 -3
  91. package/src/lib/shared/types/CacheEntry.ts +2 -4
  92. package/src/lib/shared/types/CacheOnContext.ts +1 -11
  93. package/src/lib/shared/types/CacheStore.ts +13 -7
  94. package/src/lib/shared/types/ClientFlags.ts +1 -1
  95. package/src/lib/shared/types/ErrorConstructors.ts +17 -0
  96. package/src/lib/shared/types/ErrorDescriptor.ts +10 -0
  97. package/src/lib/shared/types/ErrorSpec.ts +11 -0
  98. package/src/lib/shared/types/HttpMethod.ts +1 -0
  99. package/src/lib/shared/types/Outbox.ts +9 -0
  100. package/src/lib/shared/types/OutboxEntry.ts +27 -0
  101. package/src/lib/shared/types/RawRemoteFunction.ts +2 -2
  102. package/src/lib/shared/types/RemoteCallable.ts +2 -2
  103. package/src/lib/shared/types/RemoteFunction.ts +21 -6
  104. package/src/lib/shared/types/RequestScopeInfo.ts +1 -1
  105. package/src/lib/shared/types/RpcErrorGuard.ts +40 -0
  106. package/src/lib/shared/types/RpcInvoker.ts +1 -1
  107. package/src/lib/shared/types/StandardSchemaV1.ts +1 -1
  108. package/src/lib/shared/types/ValidationErrorData.ts +20 -0
  109. package/src/lib/shared/url.ts +3 -3
  110. package/src/lib/shared/writeRpcDts.ts +7 -7
  111. package/src/lib/shared/writeTestRpcDts.ts +3 -3
  112. package/src/lib/test/createTestApp.ts +10 -10
  113. package/src/lib/ui/compile/awaitPlan.ts +48 -0
  114. package/src/lib/ui/compile/compileShadow.ts +89 -21
  115. package/src/lib/ui/compile/desugarSignals.ts +42 -6
  116. package/src/lib/ui/compile/generateBuild.ts +43 -60
  117. package/src/lib/ui/compile/generateSSR.ts +84 -54
  118. package/src/lib/ui/compile/ifPlan.ts +30 -0
  119. package/src/lib/ui/compile/parseTemplate.ts +41 -11
  120. package/src/lib/ui/compile/stripEffects.ts +17 -9
  121. package/src/lib/ui/compile/switchPlan.ts +22 -0
  122. package/src/lib/ui/compile/tryPlan.ts +29 -0
  123. package/src/lib/ui/compile/types/TemplateAttr.ts +8 -2
  124. package/src/lib/ui/compile/types/TemplateNode.ts +6 -2
  125. package/src/lib/ui/createScope.ts +20 -2
  126. package/src/lib/ui/dom/disposeRange.ts +6 -10
  127. package/src/lib/ui/dom/hydrate.ts +2 -5
  128. package/src/lib/ui/dom/mount.ts +1 -2
  129. package/src/lib/ui/dom/withScope.ts +6 -9
  130. package/src/lib/ui/effect.ts +4 -2
  131. package/src/lib/ui/navigate.ts +40 -13
  132. package/src/lib/ui/outbox.ts +30 -110
  133. package/src/lib/ui/remoteProxy.ts +160 -10
  134. package/src/lib/ui/router.ts +45 -15
  135. package/src/lib/ui/rpcOutbox/createOutboxQueue.ts +281 -0
  136. package/src/lib/ui/rpcOutbox/outboxRegistry.ts +69 -0
  137. package/src/lib/ui/socketChannel.ts +7 -1
  138. package/src/lib/ui/types/Scope.ts +20 -5
  139. package/template/CLAUDE.md +1 -1
  140. package/template/src/server/rpc/getHello.ts +3 -3
  141. package/template/test/app.test.ts +2 -2
  142. package/src/lib/server/rpc/registerVerb.ts +0 -6
  143. package/src/lib/server/runtime/types/InspectorVerb.ts +0 -27
  144. package/src/lib/shared/detectVerbMethod.ts +0 -17
  145. package/src/lib/shared/types/HttpVerb.ts +0 -1
  146. package/src/lib/ui/types/Outbox.ts +0 -14
@@ -80,7 +80,13 @@ export function getSocketChannel(): SocketChannel {
80
80
  if (document.hidden) {
81
81
  return
82
82
  }
83
- if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) {
83
+ /* A socket reference in ANY non-closed state means one is already in play — bail.
84
+ CONNECTING/OPEN are healthy; CLOSING means a close event is still pending and the
85
+ close handler (which alone nulls `ws` and re-arms the backoff) hasn't run yet.
86
+ Spawning here would orphan that socket: its deferred close fires against the shared
87
+ `ws`, draining the fresh connection's subs and nulling it. The close handler always
88
+ clears `ws`, so reconnection is never stranded — it just sequences after the close. */
89
+ if (ws) {
84
90
  return
85
91
  }
86
92
  const scheme = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
@@ -1,4 +1,5 @@
1
1
  import type { computed } from '../computed.ts'
2
+ import type { effect } from '../effect.ts'
2
3
  import type { linked } from '../linked.ts'
3
4
  import type { Cell } from '../runtime/types/Cell.ts'
4
5
  import type { Patch } from '../runtime/types/Patch.ts'
@@ -35,14 +36,28 @@ export type Scope = {
35
36
  cell: <T>(path: string) => Cell<T>
36
37
  derive: <T>(path: string, compute: () => T) => () => T
37
38
  snapshot: () => unknown
38
- /* the `.value`-cell signal forms, reachable only through a scope (the standalone
39
- `state`/`linked`/`computed` are no longer exported): a writable `state(x, transform)`
40
- gate, a reseeding `linked`, and the read-only `computed(compute)`. A writable
41
- computed does not exist that write is expressed at the binding (`bind:value={{
42
- get, set }}`). The serializable computed doc slot is `derive` above. */
39
+ /* The reactive primitives namespaced under the scope, but AMBIENT-bound, not
40
+ receiver-bound (unlike the data methods above, which act on THIS scope's doc).
41
+ `state`/`linked`/`computed`/`effect` create their cell in whatever scope is currently
42
+ rendering and own their teardown to the finest ambient BUILD WINDOW a control-flow
43
+ branch or list row, not the whole component. So `someScope.computed(fn)` does NOT
44
+ create state in someScope; the receiver is namespacing only, the cells bind the
45
+ ambient window. Binding them to the receiver instead would leak a branch-local cell
46
+ past a branch flip (the build window is finer-grained than the lexical scope — see
47
+ ADR-0012). Forms: a writable `state(x, transform)` gate, a reseeding `linked`, the
48
+ read-only `computed(compute)` (a writable computed does not exist — that write is
49
+ expressed at the binding `bind:value={{ get, set }}`; the serializable computed doc
50
+ slot is `derive` above), and `effect`, the reaction re-run on dep change (the SSR
51
+ back-end strips it — effects are client lifecycle). */
43
52
  state: typeof state
44
53
  linked: typeof linked
45
54
  computed: typeof computed
55
+ effect: typeof effect
56
+ /* Adopts a teardown into this scope's lifetime — the build's reactivity stopper
57
+ (effects/listeners), run first on `dispose` (before children, before capabilities).
58
+ Internal: the mount core registers the build's disposer here so a component has ONE
59
+ teardown (`dispose`) rather than a separate stop + dispose composed at every site. */
60
+ own: (dispose: () => void) => void
46
61
  /* tree */
47
62
  child: (initial?: unknown) => Scope
48
63
  root: () => Scope
@@ -15,7 +15,7 @@ at `node_modules/@abide/abide/README.md`.
15
15
 
16
16
  - One export per file, named after the file. No barrels — import each name by its
17
17
  own path (`@abide/abide/server/GET`, `@abide/abide/shared/cache`, …).
18
- - RPC verbs live in `src/server/rpc/<name>.ts`; sockets in `src/server/sockets/`;
18
+ - RPCs live in `src/server/rpc/<name>.ts`; sockets in `src/server/sockets/`;
19
19
  pages are `**/page.abide`, layouts `**/layout.abide`.
20
20
  - Generated types land in `src/.abide/` — do not hand-edit them; run `abide dev`/
21
21
  `abide check` to regenerate.
@@ -1,8 +1,8 @@
1
1
  /*
2
- RPC module — every file under src/server/rpc/ exposes exactly one verb-bound remote
2
+ RPC module — every file under src/server/rpc/ exposes exactly one RPC: a remote
3
3
  function. The filename is the export name and the URL path (under `/rpc/`),
4
- and the imported verb (GET / POST / PUT / PATCH / DELETE / HEAD) picks the
5
- HTTP method. The bundler swaps the runtime per build target: direct call on
4
+ and the imported method helper (GET / POST / PUT / PATCH / DELETE / HEAD) picks
5
+ the HTTP method. The bundler swaps the runtime per build target: direct call on
6
6
  the server, fetch over the network on the client.
7
7
 
8
8
  Args (what the caller passes in) come from the handler's parameter type —
@@ -3,12 +3,12 @@ Test module — `createTestApp()` boots your real app on an ephemeral port (the
3
3
  same wiring as `abide start`, no fixtures) and hands back the whole surface:
4
4
 
5
5
  app.fetch(path) — pages and raw HTTP, origin + mount base resolved
6
- app.rpc.<verb>(args) — verbs over the real pipeline (CSRF, cookies, base),
6
+ app.rpc.<name>(args) — RPCs over the real pipeline (CSRF, cookies, base),
7
7
  typed and decoded; .raw(args) for the Response
8
8
  app.sockets.<name> — a live Socket: iterate it, .tail(n), .publish(m)
9
9
  app.health() — the /__abide/health payload
10
10
 
11
- `app.rpc` / `app.sockets` are typed from your own verbs and sockets — generated
11
+ `app.rpc` / `app.sockets` are typed from your own RPCs and sockets — generated
12
12
  into src/.abide during a build, so the keys exist with no imports. The abide
13
13
  preload (see bunfig.toml) makes the project's routes resolvable under bun test.
14
14
 
@@ -1,6 +0,0 @@
1
- import type { VerbRegistryEntry } from './types/VerbRegistryEntry.ts'
2
- import { verbRegistry } from './verbRegistry.ts'
3
-
4
- export function registerVerb(entry: VerbRegistryEntry): void {
5
- verbRegistry.set(entry.remote.url, entry)
6
- }
@@ -1,27 +0,0 @@
1
- /*
2
- One RPC verb projected for the inspector: the registry entry reduced to the
3
- serializable facts the UI renders — where it mounts, its method, which
4
- non-browser surfaces advertise it, and its argument/result shapes as JSON
5
- Schema. Schemas project through jsonSchemaForSchema (same as MCP/OpenAPI), so
6
- a verb whose library can't render a schema still lists with an opaque shape.
7
- */
8
- export type InspectorVerb = {
9
- /* Registry key — the rpc URL the verb mounts at (e.g. /rpc/users/create). */
10
- url: string
11
- /* HTTP method bound to the verb. */
12
- method: string
13
- /* Which surfaces advertise it (browser/mcp/cli), from the verb's ClientFlags. */
14
- clients: Record<string, boolean>
15
- /* Argument-bag shape as JSON Schema; undefined when the verb declares none. */
16
- inputSchema: Record<string, unknown> | undefined
17
- /* Success-body shape as JSON Schema; undefined when the verb declares none. */
18
- outputSchema: Record<string, unknown> | undefined
19
- /* True when the verb declares a filesSchema — it accepts multipart File parts. */
20
- files: boolean
21
- /* Per-verb handler deadline in ms; undefined = no deadline. */
22
- timeout: number | undefined
23
- /* Per-verb received-body cap in bytes; undefined = Bun's server-wide ceiling. */
24
- maxBodySize: number | undefined
25
- /* True when the verb opts out of the same-origin CSRF gate. */
26
- crossOrigin: boolean | undefined
27
- }
@@ -1,17 +0,0 @@
1
- import type { HttpVerb } from './types/HttpVerb.ts'
2
-
3
- /*
4
- Reads the HTTP verb of an $rpc module from its source. Every file under
5
- src/server/rpc/ follows the convention `export const <name> = GET(fn)` (the
6
- verb helper picks the method, possibly with an explicit generic
7
- `GET<{…}>(fn)`), so the helper name at the export is the method. Returns
8
- undefined when no verb export matches — the caller skips the file rather
9
- than guessing. Used by the rpc.d.ts codegen to type url() against
10
- query-carrying verbs; matching the same convention the bundler rewrites
11
- keeps the two from drifting.
12
- */
13
- const VERB_EXPORT = /export\s+const\s+\w+\s*=\s*(GET|POST|PUT|PATCH|DELETE|HEAD)\s*[<(]/
14
-
15
- export function detectVerbMethod(source: string): HttpVerb | undefined {
16
- return (source.match(VERB_EXPORT)?.[1] as HttpVerb | undefined) ?? undefined
17
- }
@@ -1 +0,0 @@
1
- export type HttpVerb = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD'
@@ -1,14 +0,0 @@
1
- /*
2
- A durable, FIFO queue of pending mutations that drains when the client is online —
3
- the local-first half of "act now, sync later". `enqueue` records a serializable
4
- payload (persisted, so it survives a reload) and tries to drain; `pending` is a
5
- reactive read of the payloads still in flight (for a "3 unsynced" indicator);
6
- `flush` drains on demand (and is what the reconnect effect calls); `dispose` stops
7
- draining and detaches persistence.
8
- */
9
- export type Outbox<T> = {
10
- enqueue: (payload: T) => void
11
- pending: () => T[]
12
- flush: () => Promise<void>
13
- dispose: () => void
14
- }