@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.
- package/AGENTS.md +234 -294
- package/CHANGELOG.md +82 -0
- package/README.md +51 -95
- package/package.json +3 -2
- package/src/abideLsp.ts +46 -8
- package/src/abideResolverPlugin.ts +107 -19
- package/src/buildCli.ts +1 -1
- package/src/discoveryEntry.ts +3 -3
- package/src/lib/bundle/BundleMenuItem.ts +1 -1
- package/src/lib/cli/createClient.ts +12 -12
- package/src/lib/cli/dispatchCommand.ts +1 -1
- package/src/lib/cli/printSessionStatus.ts +1 -1
- package/src/lib/cli/resolveCliTarget.ts +1 -1
- package/src/lib/cli/runCli.ts +1 -1
- package/src/lib/cli/types/CliManifest.ts +1 -1
- package/src/lib/cli/types/CliManifestEntry.ts +2 -2
- package/src/lib/mcp/annotationsForMethod.ts +5 -5
- package/src/lib/mcp/createMcpServer.ts +3 -3
- package/src/lib/mcp/mcpSurface.ts +14 -14
- package/src/lib/mcp/types/McpServerOptions.ts +1 -1
- package/src/lib/server/DELETE.ts +4 -4
- package/src/lib/server/GET.ts +4 -4
- package/src/lib/server/HEAD.ts +4 -4
- package/src/lib/server/PATCH.ts +4 -4
- package/src/lib/server/POST.ts +4 -4
- package/src/lib/server/PUT.ts +4 -4
- package/src/lib/server/agent.ts +4 -4
- package/src/lib/server/env.ts +1 -1
- package/src/lib/server/error.ts +49 -7
- package/src/lib/server/json.ts +1 -1
- package/src/lib/server/rpc/buildErrorConstructors.ts +19 -0
- package/src/lib/server/rpc/{defineVerb.ts → defineRpc.ts} +52 -34
- package/src/lib/server/rpc/{dispatchVerbInProcess.ts → dispatchRpcInProcess.ts} +3 -3
- package/src/lib/server/rpc/fieldErrorsFromIssues.ts +23 -0
- package/src/lib/server/rpc/{findVerbByCommandName.ts → findRpcByCommandName.ts} +5 -5
- package/src/lib/server/rpc/parseArgs.ts +6 -6
- package/src/lib/server/rpc/readBodyWithinLimit.ts +2 -2
- package/src/lib/server/rpc/registerRpc.ts +6 -0
- package/src/lib/server/rpc/{verbRegistry.ts → rpcRegistry.ts} +4 -4
- package/src/lib/server/rpc/{runWithVerbTimeout.ts → runWithRpcTimeout.ts} +4 -4
- package/src/lib/server/rpc/types/RemoteHandler.ts +9 -3
- package/src/lib/server/rpc/types/{VerbHelper.ts → RpcHelper.ts} +57 -23
- package/src/lib/server/rpc/types/{VerbRegistryEntry.ts → RpcRegistryEntry.ts} +3 -3
- package/src/lib/server/rpc/types/TypedResponse.ts +2 -2
- package/src/lib/server/rpc/unprocessed.ts +6 -7
- package/src/lib/server/rpc/validationError.ts +17 -0
- package/src/lib/server/runtime/buildInspectorSurface.ts +7 -7
- package/src/lib/server/runtime/buildOpenApiSpec.ts +6 -6
- package/src/lib/server/runtime/createRouteDispatcher.ts +7 -7
- package/src/lib/server/runtime/createServer.ts +4 -4
- package/src/lib/server/runtime/crossOriginForbidden.ts +1 -1
- package/src/lib/server/runtime/crossOriginGate.ts +4 -4
- package/src/lib/server/runtime/devClientFingerprint.ts +2 -1
- package/src/lib/server/runtime/logExposedSurfaces.ts +4 -4
- package/src/lib/server/runtime/registryManifests.ts +2 -2
- package/src/lib/server/runtime/types/InspectorCacheEntry.ts +1 -1
- package/src/lib/server/runtime/types/InspectorRpc.ts +27 -0
- package/src/lib/server/runtime/types/InspectorSurface.ts +4 -4
- package/src/lib/server/runtime/types/RequestStore.ts +1 -1
- package/src/lib/server/runtime/warnUnguardedMcp.ts +2 -2
- package/src/lib/server/sockets/createSocketDispatcher.ts +15 -14
- package/src/lib/server/sockets/types/SocketOperation.ts +2 -2
- package/src/lib/shared/HttpError.ts +15 -1
- package/src/lib/shared/UNREACHABLE_STATUSES.ts +13 -0
- package/src/lib/shared/buildRpcRequest.ts +5 -5
- package/src/lib/shared/cache.ts +50 -168
- package/src/lib/shared/carriesBodyArgs.ts +4 -4
- package/src/lib/shared/createCacheStore.ts +12 -0
- package/src/lib/shared/createRemoteFunction.ts +11 -6
- package/src/lib/shared/decodeResponse.ts +2 -2
- package/src/lib/shared/detectRpcMethod.ts +17 -0
- package/src/lib/shared/emitLogRecord.ts +3 -3
- package/src/lib/shared/escapeRegex.ts +9 -0
- package/src/lib/shared/extraForwardHeaders.ts +1 -1
- package/src/lib/shared/fileName.ts +9 -0
- package/src/lib/shared/fileStem.ts +3 -1
- package/src/lib/shared/findExportCallSite.ts +30 -273
- package/src/lib/shared/forwardHeaders.ts +2 -2
- package/src/lib/shared/httpErrorFor.ts +26 -0
- package/src/lib/shared/isAsciiWhitespace.ts +5 -0
- package/src/lib/shared/isIdentPart.ts +9 -0
- package/src/lib/shared/isIdentStart.ts +8 -0
- package/src/lib/shared/isReadOnlyMethod.ts +4 -4
- package/src/lib/shared/keyForRemoteCall.ts +4 -4
- package/src/lib/shared/log.ts +1 -1
- package/src/lib/shared/outboxProbeSlot.ts +20 -0
- package/src/lib/shared/pending.ts +4 -1
- package/src/lib/shared/prepareRpcModule.ts +96 -14
- package/src/lib/shared/probeRegistries.ts +17 -4
- package/src/lib/shared/programNameForPackage.ts +3 -1
- package/src/lib/shared/queryStringFromArgs.ts +1 -1
- package/src/lib/shared/remoteMetaStore.ts +2 -2
- package/src/lib/shared/resolveClientFlags.ts +1 -1
- package/src/lib/shared/skipNonCode.ts +230 -0
- package/src/lib/shared/streamResponse.ts +9 -6
- package/src/lib/shared/stripImport.ts +6 -4
- package/src/lib/shared/types/CacheEntry.ts +2 -4
- package/src/lib/shared/types/CacheOnContext.ts +1 -11
- package/src/lib/shared/types/CacheStore.ts +13 -7
- package/src/lib/shared/types/ClientFlags.ts +1 -1
- package/src/lib/shared/types/ErrorConstructors.ts +17 -0
- package/src/lib/shared/types/ErrorDescriptor.ts +10 -0
- package/src/lib/shared/types/ErrorSpec.ts +11 -0
- package/src/lib/shared/types/HttpMethod.ts +1 -0
- package/src/lib/shared/types/Outbox.ts +9 -0
- package/src/lib/shared/types/OutboxEntry.ts +27 -0
- package/src/lib/shared/types/RawRemoteFunction.ts +2 -2
- package/src/lib/shared/types/RemoteCallable.ts +2 -2
- package/src/lib/shared/types/RemoteFunction.ts +21 -6
- package/src/lib/shared/types/RequestScopeInfo.ts +1 -1
- package/src/lib/shared/types/RpcErrorGuard.ts +40 -0
- package/src/lib/shared/types/RpcInvoker.ts +1 -1
- package/src/lib/shared/types/StandardSchemaV1.ts +1 -1
- package/src/lib/shared/types/ValidationErrorData.ts +20 -0
- package/src/lib/shared/url.ts +3 -3
- package/src/lib/shared/writeRpcDts.ts +7 -7
- package/src/lib/shared/writeTestRpcDts.ts +3 -3
- package/src/lib/test/createTestApp.ts +10 -10
- package/src/lib/ui/compile/ABIDE_SEMANTIC_TOKENS_LEGEND.ts +45 -0
- package/src/lib/ui/compile/abideUiPlugin.ts +2 -1
- package/src/lib/ui/compile/awaitPlan.ts +48 -0
- package/src/lib/ui/compile/compileShadow.ts +117 -31
- package/src/lib/ui/compile/createShadowLanguageService.ts +48 -0
- package/src/lib/ui/compile/desugarSignals.ts +42 -6
- package/src/lib/ui/compile/encodeSemanticTokens.ts +37 -0
- package/src/lib/ui/compile/generateBuild.ts +45 -62
- package/src/lib/ui/compile/generateSSR.ts +90 -60
- package/src/lib/ui/compile/ifPlan.ts +30 -0
- package/src/lib/ui/compile/makeVarNamer.ts +10 -0
- package/src/lib/ui/compile/offsetToPosition.ts +9 -0
- package/src/lib/ui/compile/parseTemplate.ts +565 -115
- package/src/lib/ui/compile/stripEffects.ts +17 -9
- package/src/lib/ui/compile/structuralBlockTokens.ts +101 -0
- package/src/lib/ui/compile/switchPlan.ts +22 -0
- package/src/lib/ui/compile/tryPlan.ts +29 -0
- package/src/lib/ui/compile/types/SemanticToken.ts +11 -0
- package/src/lib/ui/compile/types/TemplateAttr.ts +8 -2
- package/src/lib/ui/compile/types/TemplateNode.ts +15 -2
- package/src/lib/ui/createScope.ts +20 -2
- package/src/lib/ui/dom/appendText.ts +1 -5
- package/src/lib/ui/dom/applyResolved.ts +1 -5
- package/src/lib/ui/dom/disposeRange.ts +6 -10
- package/src/lib/ui/dom/hydrate.ts +2 -5
- package/src/lib/ui/dom/isComment.ts +6 -0
- package/src/lib/ui/dom/mount.ts +1 -2
- package/src/lib/ui/dom/withScope.ts +6 -9
- package/src/lib/ui/effect.ts +4 -2
- package/src/lib/ui/navigate.ts +40 -13
- package/src/lib/ui/outbox.ts +30 -110
- package/src/lib/ui/remoteProxy.ts +160 -10
- package/src/lib/ui/router.ts +3 -3
- package/src/lib/ui/rpcOutbox/createOutboxQueue.ts +281 -0
- package/src/lib/ui/rpcOutbox/outboxRegistry.ts +69 -0
- package/src/lib/ui/runtime/createDoc.ts +3 -3
- package/src/lib/ui/runtime/pathSegments.ts +10 -0
- package/src/lib/ui/socketChannel.ts +7 -1
- package/src/lib/ui/types/Scope.ts +20 -5
- package/template/.zed/settings.json +4 -0
- package/template/CLAUDE.md +1 -1
- package/template/src/server/rpc/getHello.ts +3 -3
- package/template/src/ui/pages/page.abide +4 -4
- package/template/test/app.test.ts +2 -2
- package/src/lib/server/rpc/registerVerb.ts +0 -6
- package/src/lib/server/runtime/types/InspectorVerb.ts +0 -27
- package/src/lib/shared/detectVerbMethod.ts +0 -17
- package/src/lib/shared/types/HttpVerb.ts +0 -1
- 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
|
|
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
|
|
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
|
-
|
|
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
|
-
/*
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
package/template/CLAUDE.md
CHANGED
|
@@ -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
|
-
-
|
|
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
|
|
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
|
|
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`
|
|
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` *
|
|
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
|
-
|
|
16
|
+
{#await cache(getHello)() then hello}
|
|
17
17
|
<h1>{hello.message}</h1>
|
|
18
|
-
|
|
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.<
|
|
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
|
|
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,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
|
-
}
|