@abide/abide 0.41.1 → 0.43.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 (167) hide show
  1. package/AGENTS.md +234 -294
  2. package/CHANGELOG.md +82 -0
  3. package/README.md +51 -95
  4. package/package.json +3 -2
  5. package/src/abideLsp.ts +46 -8
  6. package/src/abideResolverPlugin.ts +107 -19
  7. package/src/buildCli.ts +1 -1
  8. package/src/discoveryEntry.ts +3 -3
  9. package/src/lib/bundle/BundleMenuItem.ts +1 -1
  10. package/src/lib/cli/createClient.ts +12 -12
  11. package/src/lib/cli/dispatchCommand.ts +1 -1
  12. package/src/lib/cli/printSessionStatus.ts +1 -1
  13. package/src/lib/cli/resolveCliTarget.ts +1 -1
  14. package/src/lib/cli/runCli.ts +1 -1
  15. package/src/lib/cli/types/CliManifest.ts +1 -1
  16. package/src/lib/cli/types/CliManifestEntry.ts +2 -2
  17. package/src/lib/mcp/annotationsForMethod.ts +5 -5
  18. package/src/lib/mcp/createMcpServer.ts +3 -3
  19. package/src/lib/mcp/mcpSurface.ts +14 -14
  20. package/src/lib/mcp/types/McpServerOptions.ts +1 -1
  21. package/src/lib/server/DELETE.ts +4 -4
  22. package/src/lib/server/GET.ts +4 -4
  23. package/src/lib/server/HEAD.ts +4 -4
  24. package/src/lib/server/PATCH.ts +4 -4
  25. package/src/lib/server/POST.ts +4 -4
  26. package/src/lib/server/PUT.ts +4 -4
  27. package/src/lib/server/agent.ts +4 -4
  28. package/src/lib/server/env.ts +1 -1
  29. package/src/lib/server/error.ts +49 -7
  30. package/src/lib/server/json.ts +1 -1
  31. package/src/lib/server/rpc/buildErrorConstructors.ts +19 -0
  32. package/src/lib/server/rpc/{defineVerb.ts → defineRpc.ts} +52 -34
  33. package/src/lib/server/rpc/{dispatchVerbInProcess.ts → dispatchRpcInProcess.ts} +3 -3
  34. package/src/lib/server/rpc/fieldErrorsFromIssues.ts +23 -0
  35. package/src/lib/server/rpc/{findVerbByCommandName.ts → findRpcByCommandName.ts} +5 -5
  36. package/src/lib/server/rpc/parseArgs.ts +6 -6
  37. package/src/lib/server/rpc/readBodyWithinLimit.ts +2 -2
  38. package/src/lib/server/rpc/registerRpc.ts +6 -0
  39. package/src/lib/server/rpc/{verbRegistry.ts → rpcRegistry.ts} +4 -4
  40. package/src/lib/server/rpc/{runWithVerbTimeout.ts → runWithRpcTimeout.ts} +4 -4
  41. package/src/lib/server/rpc/types/RemoteHandler.ts +9 -3
  42. package/src/lib/server/rpc/types/{VerbHelper.ts → RpcHelper.ts} +57 -23
  43. package/src/lib/server/rpc/types/{VerbRegistryEntry.ts → RpcRegistryEntry.ts} +3 -3
  44. package/src/lib/server/rpc/types/TypedResponse.ts +2 -2
  45. package/src/lib/server/rpc/unprocessed.ts +6 -7
  46. package/src/lib/server/rpc/validationError.ts +17 -0
  47. package/src/lib/server/runtime/buildInspectorSurface.ts +7 -7
  48. package/src/lib/server/runtime/buildOpenApiSpec.ts +6 -6
  49. package/src/lib/server/runtime/createRouteDispatcher.ts +7 -7
  50. package/src/lib/server/runtime/createServer.ts +4 -4
  51. package/src/lib/server/runtime/crossOriginForbidden.ts +1 -1
  52. package/src/lib/server/runtime/crossOriginGate.ts +4 -4
  53. package/src/lib/server/runtime/devClientFingerprint.ts +2 -1
  54. package/src/lib/server/runtime/logExposedSurfaces.ts +4 -4
  55. package/src/lib/server/runtime/registryManifests.ts +2 -2
  56. package/src/lib/server/runtime/types/InspectorCacheEntry.ts +1 -1
  57. package/src/lib/server/runtime/types/InspectorRpc.ts +27 -0
  58. package/src/lib/server/runtime/types/InspectorSurface.ts +4 -4
  59. package/src/lib/server/runtime/types/RequestStore.ts +1 -1
  60. package/src/lib/server/runtime/warnUnguardedMcp.ts +2 -2
  61. package/src/lib/server/sockets/createSocketDispatcher.ts +15 -14
  62. package/src/lib/server/sockets/types/SocketOperation.ts +2 -2
  63. package/src/lib/shared/HttpError.ts +15 -1
  64. package/src/lib/shared/UNREACHABLE_STATUSES.ts +13 -0
  65. package/src/lib/shared/buildRpcRequest.ts +5 -5
  66. package/src/lib/shared/cache.ts +50 -168
  67. package/src/lib/shared/carriesBodyArgs.ts +4 -4
  68. package/src/lib/shared/createCacheStore.ts +12 -0
  69. package/src/lib/shared/createRemoteFunction.ts +11 -6
  70. package/src/lib/shared/decodeResponse.ts +2 -2
  71. package/src/lib/shared/detectRpcMethod.ts +17 -0
  72. package/src/lib/shared/emitLogRecord.ts +3 -3
  73. package/src/lib/shared/escapeRegex.ts +9 -0
  74. package/src/lib/shared/extraForwardHeaders.ts +1 -1
  75. package/src/lib/shared/fileName.ts +9 -0
  76. package/src/lib/shared/fileStem.ts +3 -1
  77. package/src/lib/shared/findExportCallSite.ts +30 -273
  78. package/src/lib/shared/forwardHeaders.ts +2 -2
  79. package/src/lib/shared/httpErrorFor.ts +26 -0
  80. package/src/lib/shared/isAsciiWhitespace.ts +5 -0
  81. package/src/lib/shared/isIdentPart.ts +9 -0
  82. package/src/lib/shared/isIdentStart.ts +8 -0
  83. package/src/lib/shared/isReadOnlyMethod.ts +4 -4
  84. package/src/lib/shared/keyForRemoteCall.ts +4 -4
  85. package/src/lib/shared/log.ts +1 -1
  86. package/src/lib/shared/outboxProbeSlot.ts +20 -0
  87. package/src/lib/shared/pending.ts +4 -1
  88. package/src/lib/shared/prepareRpcModule.ts +96 -14
  89. package/src/lib/shared/probeRegistries.ts +17 -4
  90. package/src/lib/shared/programNameForPackage.ts +3 -1
  91. package/src/lib/shared/queryStringFromArgs.ts +1 -1
  92. package/src/lib/shared/remoteMetaStore.ts +2 -2
  93. package/src/lib/shared/resolveClientFlags.ts +1 -1
  94. package/src/lib/shared/skipNonCode.ts +230 -0
  95. package/src/lib/shared/streamResponse.ts +9 -6
  96. package/src/lib/shared/stripImport.ts +6 -4
  97. package/src/lib/shared/types/CacheEntry.ts +2 -4
  98. package/src/lib/shared/types/CacheOnContext.ts +1 -11
  99. package/src/lib/shared/types/CacheStore.ts +13 -7
  100. package/src/lib/shared/types/ClientFlags.ts +1 -1
  101. package/src/lib/shared/types/ErrorConstructors.ts +17 -0
  102. package/src/lib/shared/types/ErrorDescriptor.ts +10 -0
  103. package/src/lib/shared/types/ErrorSpec.ts +11 -0
  104. package/src/lib/shared/types/HttpMethod.ts +1 -0
  105. package/src/lib/shared/types/Outbox.ts +9 -0
  106. package/src/lib/shared/types/OutboxEntry.ts +27 -0
  107. package/src/lib/shared/types/RawRemoteFunction.ts +2 -2
  108. package/src/lib/shared/types/RemoteCallable.ts +2 -2
  109. package/src/lib/shared/types/RemoteFunction.ts +21 -6
  110. package/src/lib/shared/types/RequestScopeInfo.ts +1 -1
  111. package/src/lib/shared/types/RpcErrorGuard.ts +40 -0
  112. package/src/lib/shared/types/RpcInvoker.ts +1 -1
  113. package/src/lib/shared/types/StandardSchemaV1.ts +1 -1
  114. package/src/lib/shared/types/ValidationErrorData.ts +20 -0
  115. package/src/lib/shared/url.ts +3 -3
  116. package/src/lib/shared/writeRpcDts.ts +7 -7
  117. package/src/lib/shared/writeTestRpcDts.ts +3 -3
  118. package/src/lib/test/createTestApp.ts +10 -10
  119. package/src/lib/ui/compile/ABIDE_SEMANTIC_TOKENS_LEGEND.ts +45 -0
  120. package/src/lib/ui/compile/abideUiPlugin.ts +2 -1
  121. package/src/lib/ui/compile/awaitPlan.ts +48 -0
  122. package/src/lib/ui/compile/compileShadow.ts +117 -31
  123. package/src/lib/ui/compile/createShadowLanguageService.ts +48 -0
  124. package/src/lib/ui/compile/desugarSignals.ts +42 -6
  125. package/src/lib/ui/compile/encodeSemanticTokens.ts +37 -0
  126. package/src/lib/ui/compile/generateBuild.ts +45 -62
  127. package/src/lib/ui/compile/generateSSR.ts +90 -60
  128. package/src/lib/ui/compile/ifPlan.ts +30 -0
  129. package/src/lib/ui/compile/makeVarNamer.ts +10 -0
  130. package/src/lib/ui/compile/offsetToPosition.ts +9 -0
  131. package/src/lib/ui/compile/parseTemplate.ts +565 -115
  132. package/src/lib/ui/compile/stripEffects.ts +17 -9
  133. package/src/lib/ui/compile/structuralBlockTokens.ts +101 -0
  134. package/src/lib/ui/compile/switchPlan.ts +22 -0
  135. package/src/lib/ui/compile/tryPlan.ts +29 -0
  136. package/src/lib/ui/compile/types/SemanticToken.ts +11 -0
  137. package/src/lib/ui/compile/types/TemplateAttr.ts +8 -2
  138. package/src/lib/ui/compile/types/TemplateNode.ts +15 -2
  139. package/src/lib/ui/createScope.ts +20 -2
  140. package/src/lib/ui/dom/appendText.ts +1 -5
  141. package/src/lib/ui/dom/applyResolved.ts +1 -5
  142. package/src/lib/ui/dom/disposeRange.ts +6 -10
  143. package/src/lib/ui/dom/hydrate.ts +2 -5
  144. package/src/lib/ui/dom/isComment.ts +6 -0
  145. package/src/lib/ui/dom/mount.ts +1 -2
  146. package/src/lib/ui/dom/withScope.ts +6 -9
  147. package/src/lib/ui/effect.ts +4 -2
  148. package/src/lib/ui/navigate.ts +40 -13
  149. package/src/lib/ui/outbox.ts +30 -110
  150. package/src/lib/ui/remoteProxy.ts +160 -10
  151. package/src/lib/ui/router.ts +3 -3
  152. package/src/lib/ui/rpcOutbox/createOutboxQueue.ts +281 -0
  153. package/src/lib/ui/rpcOutbox/outboxRegistry.ts +69 -0
  154. package/src/lib/ui/runtime/createDoc.ts +3 -3
  155. package/src/lib/ui/runtime/pathSegments.ts +10 -0
  156. package/src/lib/ui/socketChannel.ts +7 -1
  157. package/src/lib/ui/types/Scope.ts +20 -5
  158. package/template/.zed/settings.json +4 -0
  159. package/template/CLAUDE.md +1 -1
  160. package/template/src/server/rpc/getHello.ts +3 -3
  161. package/template/src/ui/pages/page.abide +4 -4
  162. package/template/test/app.test.ts +2 -2
  163. package/src/lib/server/rpc/registerVerb.ts +0 -6
  164. package/src/lib/server/runtime/types/InspectorVerb.ts +0 -27
  165. package/src/lib/shared/detectVerbMethod.ts +0 -17
  166. package/src/lib/shared/types/HttpVerb.ts +0 -1
  167. package/src/lib/ui/types/Outbox.ts +0 -14
@@ -0,0 +1,69 @@
1
+ import { canonicalJson } from '../../shared/canonicalJson.ts'
2
+ import { outboxProbeSlot } from '../../shared/outboxProbeSlot.ts'
3
+ import type { OutboxQueue } from './createOutboxQueue.ts'
4
+
5
+ /* One registered durable RPC: its url, the RemoteFunction it belongs to (tag for the
6
+ global aggregate), and its live queue. */
7
+ type RegisteredOutbox = { url: string; rpc: unknown; queue: OutboxQueue<unknown> }
8
+
9
+ /*
10
+ Client registry of every durable RPC's outbox queue, keyed by url. A durable RPC
11
+ (`outbox: true`) registers on first use so the global `outbox()` aggregate can
12
+ flatten every queue and `pending()` can see queued entries. App-scoped, not component-
13
+ scoped — the queues outlive any mount.
14
+ */
15
+ const registered = new Map<string, RegisteredOutbox>()
16
+
17
+ export const outboxRegistry = {
18
+ register(url: string, queue: OutboxQueue<unknown>, rpc: unknown): void {
19
+ registered.set(url, { url, rpc, queue })
20
+ },
21
+ get(url: string): OutboxQueue<unknown> | undefined {
22
+ return registered.get(url)?.queue
23
+ },
24
+ all(): RegisteredOutbox[] {
25
+ return [...registered.values()]
26
+ },
27
+ /* Drop every registered queue. App-scoped state outlives any mount, so tests that
28
+ park writes share one process-global registry; reset() gives each its own clean
29
+ slate. Only the in-memory registry is dropped — `queue.dispose()` keeps the
30
+ persisted snapshot (a reload rehydrates it), so a sign-out that must abandon
31
+ unsynced work has to clear persistence separately. */
32
+ reset(): void {
33
+ for (const { queue } of registered.values()) {
34
+ queue.dispose?.()
35
+ }
36
+ registered.clear()
37
+ },
38
+ }
39
+
40
+ /* The queues a pending() selector spans: a durable rpc selector carries the queue's
41
+ `url`, so it narrows to that one; the bare form spans every registered queue; any
42
+ other selector (producer fn, tags, subscribable) carries no url and matches none. */
43
+ const queuesFor = (selector: unknown): OutboxQueue<unknown>[] => {
44
+ if (selector === undefined) {
45
+ return outboxRegistry.all().map(({ queue }) => queue)
46
+ }
47
+ const url = (selector as { url?: unknown }).url
48
+ if (typeof url !== 'string') {
49
+ return []
50
+ }
51
+ const queue = outboxRegistry.get(url)
52
+ return queue === undefined ? [] : [queue]
53
+ }
54
+
55
+ /* Prober for the shared pending() probe (see outboxProbeSlot). A parked write has no
56
+ value yet, so it counts as pending. Reads queue.entries() (doc-backed), so the probe
57
+ re-runs as writes park, drain, fail, or cancel; optional args narrow to one parked
58
+ call by structural compare — the double-submit guard for a form. The compare uses
59
+ canonicalJson (key-sorted, the encoder keyForRemoteCall keys body args with), not
60
+ JSON.stringify, so a selector's args match a parked entry regardless of key order —
61
+ the same equality the cache-side selector grammar already uses. */
62
+ outboxProbeSlot.probe = (selector, args): boolean => {
63
+ const entries = queuesFor(selector).flatMap((queue) => queue.entries())
64
+ if (args === undefined) {
65
+ return entries.length > 0
66
+ }
67
+ const key = canonicalJson(args)
68
+ return entries.some((entry) => canonicalJson(entry.args) === key)
69
+ }
@@ -3,13 +3,13 @@ import { batch } from './batch.ts'
3
3
  import { createComputedNode } from './createComputedNode.ts'
4
4
  import { createSignalNode } from './createSignalNode.ts'
5
5
  import { PATCH_BUS } from './PATCH_BUS.ts'
6
+ import { pathSegments } from './pathSegments.ts'
6
7
  import { readNode } from './readNode.ts'
7
8
  import { trigger } from './trigger.ts'
8
9
  import type { Cell } from './types/Cell.ts'
9
10
  import type { Doc } from './types/Doc.ts'
10
11
  import type { Patch } from './types/Patch.ts'
11
12
  import type { ReactiveNode } from './types/ReactiveNode.ts'
12
- import { unescapeKey } from './unescapeKey.ts'
13
13
  import { walkPath } from './walkPath.ts'
14
14
  import { writeNode } from './writeNode.ts'
15
15
 
@@ -132,7 +132,7 @@ export function createDoc(initial: unknown): Doc {
132
132
  function apply(patch: Patch): void {
133
133
  /* Segments index the tree, so they carry the REAL keys (unescaped); the path
134
134
  strings (parentPath, node-map keys) stay escaped, re-walked through walkPath. */
135
- const segments = patch.path === '' ? [] : patch.path.split('/').map(unescapeKey)
135
+ const segments = patch.path === '' ? [] : pathSegments(patch.path)
136
136
  /* Capture the pre-image only when a consumer is listening (the inverse's only
137
137
  cost): a replace/remove inverts to the value it overwrote, an add to a
138
138
  remove (computed post-apply, below, to resolve an array append's index). */
@@ -220,7 +220,7 @@ export function createDoc(initial: unknown): Doc {
220
220
  */
221
221
  function cell<T>(path: string): Cell<T> {
222
222
  const node = nodeFor(path)
223
- const segments = path.split('/').map(unescapeKey)
223
+ const segments = pathSegments(path)
224
224
  const leafKey = segments[segments.length - 1] as string
225
225
  /* Auto-vivify missing ancestor objects so binding a nested path on a doc
226
226
  booted shallow (e.g. `state({})`) doesn't crash, and a later `set` writes
@@ -0,0 +1,10 @@
1
+ import { unescapeKey } from './unescapeKey.ts'
2
+
3
+ /*
4
+ Splits an escaped JSON-Pointer-style doc path into its REAL key segments
5
+ (each segment unescaped). The path strings (parentPath, node-map keys) stay
6
+ escaped and re-walked through `walkPath`; segments index the live tree.
7
+ */
8
+ export function pathSegments(path: string): string[] {
9
+ return path.split('/').map(unescapeKey)
10
+ }
@@ -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
@@ -0,0 +1,4 @@
1
+ {
2
+ "auto_install_extensions": { "abide": true },
3
+ "semantic_tokens": "combined"
4
+ }
@@ -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,17 +3,17 @@
3
3
  Root page — served at GET /. Every folder under src/ui/pages/ that contains a
4
4
  page.abide mounts at that folder's URL; the root layout.abide wraps it.
5
5
 
6
- The blocking await-block below (the `then` attribute sits ON the <template await>)
6
+ The blocking await-block below (the `then` clause sits in the `{#await}` head)
7
7
  resolves on the server during SSR and renders inline — no pending placeholder. The
8
8
  decoded body is captured into the per-request cache, serialized into the HTML, and
9
- replayed on the client during hydration with no second fetch. A `then` *child*
9
+ replayed on the client during hydration with no second fetch. A `{:then}` *branch*
10
10
  instead would stream the resolution in out of order.
11
11
  */
12
12
  import { cache } from '@abide/abide/shared/cache'
13
13
  import { getHello } from '$server/rpc/getHello.ts'
14
14
  </script>
15
15
 
16
- <template await={cache(getHello)()} then="hello">
16
+ {#await cache(getHello)() then hello}
17
17
  <h1>{hello.message}</h1>
18
- </template>
18
+ {/await}
19
19
  <p>Edit <code>src/ui/pages/page.abide</code> and the page hot-reloads.</p>
@@ -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
- }