brustjs 0.1.49-alpha → 0.1.51-alpha
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/package.json +39 -15
- package/runtime/cache-sync.ts +291 -0
- package/runtime/cache.ts +4 -0
- package/runtime/cli/build.ts +7 -0
- package/runtime/cli/dev.ts +7 -0
- package/runtime/cli/native-routes-emit.ts +147 -1
- package/runtime/cli/ssg.ts +94 -23
- package/runtime/config.ts +42 -0
- package/runtime/index.d.ts +63 -0
- package/runtime/index.js +57 -52
- package/runtime/index.ts +114 -9
- package/runtime/islands/page-cache.ts +32 -2
- package/runtime/native/runtime.ts +220 -7
- package/runtime/render/fragment.ts +87 -0
- package/runtime/routes.ts +482 -95
- package/runtime/templates.ts +47 -0
- package/runtime/treaty.ts +24 -1
- package/types/action-error.d.ts +18 -0
- package/types/cache-sync.d.ts +42 -0
- package/types/cache.d.ts +20 -0
- package/types/cli/help.d.ts +28 -0
- package/types/cli/jinja-staleness.d.ts +14 -0
- package/types/cli/native-routes-emit.d.ts +217 -0
- package/types/cli/new.d.ts +30 -0
- package/types/cli/templates.d.ts +39 -0
- package/types/client/index.d.ts +14 -0
- package/types/config.d.ts +42 -0
- package/types/cookies.d.ts +25 -0
- package/types/create.d.ts +1 -0
- package/types/css/build.d.ts +11 -0
- package/types/css/component-build.d.ts +17 -0
- package/types/css/component-loader.d.ts +8 -0
- package/types/css/manifest.d.ts +21 -0
- package/types/css/process-modules.d.ts +31 -0
- package/types/css/route-deps.d.ts +20 -0
- package/types/css/scan-imports.d.ts +13 -0
- package/types/css.d.ts +16 -0
- package/types/define-actions.d.ts +133 -0
- package/types/dev/client.d.ts +8 -0
- package/types/dev/coordinator.d.ts +33 -0
- package/types/dev/inject.d.ts +6 -0
- package/types/dev/jinja-reload.d.ts +7 -0
- package/types/dev/tui.d.ts +35 -0
- package/types/dev/watcher.d.ts +34 -0
- package/types/dev/worker-registry.d.ts +17 -0
- package/types/dev/ws-channel.d.ts +39 -0
- package/types/generator.d.ts +23 -0
- package/types/index.d.ts +222 -0
- package/types/islands/brust-page.d.ts +74 -0
- package/types/islands/build.d.ts +49 -0
- package/types/islands/chunk-id.d.ts +10 -0
- package/types/islands/importmap.d.ts +2 -0
- package/types/islands/island.d.ts +65 -0
- package/types/islands/isr-jsx.d.ts +31 -0
- package/types/islands/native-render.d.ts +89 -0
- package/types/loader-cache.d.ts +18 -0
- package/types/mcp/extractor.d.ts +14 -0
- package/types/mcp/manifest.d.ts +23 -0
- package/types/mcp/schema.d.ts +19 -0
- package/types/mcp/server.d.ts +15 -0
- package/types/md/emit.d.ts +72 -0
- package/types/md/render.d.ts +80 -0
- package/types/md/routes.d.ts +119 -0
- package/types/md/scan.d.ts +34 -0
- package/types/md/slug.d.ts +1 -0
- package/types/native/build.d.ts +30 -0
- package/types/native/index.d.ts +2 -0
- package/types/native/runtime.d.ts +52 -0
- package/types/navigation/active-nav.d.ts +2 -0
- package/types/navigation/index.d.ts +5 -0
- package/types/navigation/navigate.d.ts +14 -0
- package/types/navigation/react.d.ts +15 -0
- package/types/navigation/store.d.ts +44 -0
- package/types/render/fragment.d.ts +20 -0
- package/types/render/inject-action-prefix.d.ts +9 -0
- package/types/render/inject-css-link.d.ts +8 -0
- package/types/render/inject-dev-client.d.ts +6 -0
- package/types/render/inject-generator.d.ts +7 -0
- package/types/render/inject-store.d.ts +9 -0
- package/types/render/stream.d.ts +45 -0
- package/types/request-context.d.ts +16 -0
- package/types/routes.d.ts +506 -0
- package/types/sse/handler.d.ts +22 -0
- package/types/standard-schema.d.ts +31 -0
- package/types/store/define-store.d.ts +31 -0
- package/types/store/index.d.ts +5 -0
- package/types/store/react.d.ts +2 -0
- package/types/store/serialize.d.ts +5 -0
- package/types/store/server-context.d.ts +4 -0
- package/types/store/signal.d.ts +18 -0
- package/types/templates.d.ts +18 -0
- package/types/treaty.d.ts +70 -0
- package/types/ws/handler.d.ts +26 -0
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import * as native from './index.js';
|
|
2
|
+
/** Global CORS policy (one policy for the whole deployment — pages, actions,
|
|
3
|
+
* static assets alike; brust has no per-route CORS). Opt-in: omit to keep CORS
|
|
4
|
+
* disabled (byte-identical default — no `Access-Control-*` headers, OPTIONS
|
|
5
|
+
* still 405). Threaded into Rust, which answers preflights before the render
|
|
6
|
+
* pipeline and stamps actual responses at a single chokepoint. */
|
|
7
|
+
export interface CorsOptions {
|
|
8
|
+
/** Allowed origins, exact scheme+host+port match (no wildcard subdomains).
|
|
9
|
+
* A list CONTAINING `'*'` is treated as wildcard: every origin allowed,
|
|
10
|
+
* echoed as the literal `*`. */
|
|
11
|
+
origins: string[];
|
|
12
|
+
/** Preflight `Access-Control-Allow-Methods`. Default: GET,POST,PUT,PATCH,DELETE,OPTIONS. */
|
|
13
|
+
methods?: string[];
|
|
14
|
+
/** Preflight `Access-Control-Allow-Headers`. Default: echo the request's
|
|
15
|
+
* `Access-Control-Request-Headers`. */
|
|
16
|
+
headers?: string[];
|
|
17
|
+
/** `Access-Control-Expose-Headers` on actual responses. Default: none. */
|
|
18
|
+
exposeHeaders?: string[];
|
|
19
|
+
/** Emit `Access-Control-Allow-Credentials: true`. INVALID combined with a
|
|
20
|
+
* wildcard origin — serve() throws at boot (browsers silently reject
|
|
21
|
+
* `Allow-Credentials` with `Allow-Origin: *`; we make it loud). */
|
|
22
|
+
credentials?: boolean;
|
|
23
|
+
/** Preflight `Access-Control-Max-Age` seconds. Default 600. */
|
|
24
|
+
maxAgeSeconds?: number;
|
|
25
|
+
}
|
|
26
|
+
/** Fail-fast CORS validation, mirroring Rust's `CorsConfig::validate` so a bad
|
|
27
|
+
* config throws in TS before the native call (clearer stack, same message
|
|
28
|
+
* shape). Exported for unit testing. */
|
|
29
|
+
export declare function validateCorsOptions(cors: CorsOptions): void;
|
|
30
|
+
export interface ServeOptions {
|
|
31
|
+
/** Host/address to bind on. A hostname (e.g. `localhost`, resolved Rust-side)
|
|
32
|
+
* or a literal IP such as `0.0.0.0` / `127.0.0.1`. */
|
|
33
|
+
host: string;
|
|
34
|
+
port: number;
|
|
35
|
+
workers: number;
|
|
36
|
+
entry: string;
|
|
37
|
+
bootTimeoutMs?: number;
|
|
38
|
+
/** Actions builder from `defineActions(...)`. When present, `serve`
|
|
39
|
+
* registers its endpoints (method/path, keyed by registration index)
|
|
40
|
+
* before the listener binds. Optional — omit if the app has no actions. */
|
|
41
|
+
actions?: import('./define-actions.ts').ActionsBuilder;
|
|
42
|
+
/** URL prefix the action router mounts under (e.g. `/_actions`). Threaded
|
|
43
|
+
* into Rust's ServeOptions.action_prefix. */
|
|
44
|
+
actionPrefix?: string;
|
|
45
|
+
/** Optional global CORS policy — see {@link CorsOptions}. Validated at boot
|
|
46
|
+
* (TS first, Rust mirrors): `origins` non-empty; `credentials` + wildcard
|
|
47
|
+
* origin throws. */
|
|
48
|
+
cors?: CorsOptions;
|
|
49
|
+
/** MCP support — pass a manifest built via brust.buildMcpManifest. brust.serve
|
|
50
|
+
* does NOT auto-wire MCP into workers; the worker branch of the entry file must
|
|
51
|
+
* call brust.loadMcpManifest() + makeMcpServer() itself and pass the McpServer
|
|
52
|
+
* to makeRenderer via `opts.mcp`. See example/pokedex/index.ts for the
|
|
53
|
+
* pattern. The field here is currently unused inside serve() and is reserved
|
|
54
|
+
* for future IPC-based propagation of the manifest. */
|
|
55
|
+
mcp?: {
|
|
56
|
+
manifest: import('./mcp/manifest.ts').McpManifest;
|
|
57
|
+
};
|
|
58
|
+
/** Performance tunables. All optional — omitting any field keeps the
|
|
59
|
+
* framework default, so the whole object is opt-in. `connWorkers` sets the
|
|
60
|
+
* Rust-side connection-handling concurrency (accept→parse→dispatch), which is
|
|
61
|
+
* INDEPENDENT of `workers` (the Bun render threads): I/O-bound paths like
|
|
62
|
+
* `/ping` scale with `connWorkers`, render-bound paths scale with `workers`.
|
|
63
|
+
* Setting `connWorkers > workers` is safe: a render dispatch that finds every
|
|
64
|
+
* worker busy QUEUES (awaits a free worker up to `claimTimeoutMs`) instead of
|
|
65
|
+
* 503-ing, so excess conn-workers just wait their turn rather than dropping
|
|
66
|
+
* requests. */
|
|
67
|
+
tuning?: {
|
|
68
|
+
/** Rust accept/dispatch concurrency. Default = `workers`. */
|
|
69
|
+
connWorkers?: number;
|
|
70
|
+
/** Max request bytes before header terminator. Default 16384 (16 KB). */
|
|
71
|
+
maxRequestBytes?: number;
|
|
72
|
+
/** Max action/RPC body size. Default 262144 (256 KB). */
|
|
73
|
+
maxActionBodyBytes?: number;
|
|
74
|
+
/** Accept-side queue depth (TCP backpressure point). Default 1024. */
|
|
75
|
+
connQueueCap?: number;
|
|
76
|
+
/** Initial per-connection read buffer capacity. Default 4096. */
|
|
77
|
+
readBufCap?: number;
|
|
78
|
+
/** Max ms a render waits for a free worker before 503 (AllBusy queues
|
|
79
|
+
* instead of failing fast). Default 10000. */
|
|
80
|
+
claimTimeoutMs?: number;
|
|
81
|
+
/** tokio I/O runtime worker-thread count for the hyper server. Runs inside
|
|
82
|
+
* Bun (which has its own threads + render workers), so this is NOT
|
|
83
|
+
* one-per-core. Default `min(availableParallelism, 4)` (fallback 2). */
|
|
84
|
+
workerThreads?: number;
|
|
85
|
+
/** Render slots per Bun worker — concurrent in-flight renders per isolate.
|
|
86
|
+
* Default `min(cores, 16)` (set `BRUST_RENDER_SLOTS` to go higher on >16-core
|
|
87
|
+
* hosts). Only speeds renders that `await` during render (e.g. Suspense with
|
|
88
|
+
* async data); CPU-bound/native/cache routes serialize on the one isolate and
|
|
89
|
+
* are unaffected. The count is propagated to each worker via the
|
|
90
|
+
* `BRUST_RENDER_SLOTS` env var and scales the per-worker SAB (one region per
|
|
91
|
+
* slot, so the cap bounds memory). slots > 1 is byte-identical to slots = 1. */
|
|
92
|
+
renderSlots?: number;
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
export type RenderFn = (envelopeJson: string, slot: number) => Promise<number>;
|
|
96
|
+
export declare const isWorker: boolean;
|
|
97
|
+
export declare function workerId(): number | null;
|
|
98
|
+
export declare const brust: {
|
|
99
|
+
serve(opts: ServeOptions): Promise<void>;
|
|
100
|
+
/** Install the route table in Rust. MUST be called before `serve()`.
|
|
101
|
+
* Pass an array of FlatRoutes from `defineRoutes(...)` — each is JSON-encoded
|
|
102
|
+
* with its optional cache config. Rust matches against `fullPath`. */
|
|
103
|
+
registerRoutes(routes: import("./routes.ts").FlatRoute[]): number;
|
|
104
|
+
/** Register the list of literal route paths that should be dispatched as
|
|
105
|
+
* SSE (text/event-stream) instead of going through the render pipeline.
|
|
106
|
+
* Call from the main process after defineRoutes — the worker only needs
|
|
107
|
+
* the call if it also accepts SSE traffic, but in the current model the
|
|
108
|
+
* main accept loop owns dispatch so this is main-only. MVP supports only
|
|
109
|
+
* literal paths; parameterized routes (e.g. `/sse/{room}`) are a follow-up. */
|
|
110
|
+
registerSsePaths(paths: string[]): void;
|
|
111
|
+
/** Register the list of literal route paths that should be dispatched as
|
|
112
|
+
* WebSocket upgrades. Call from the main process after defineRoutes.
|
|
113
|
+
* MVP supports only literal paths — parameterized routes (e.g.
|
|
114
|
+
* `/ws/chat/{room}`) are a follow-up. */
|
|
115
|
+
registerWsPaths(paths: string[]): void;
|
|
116
|
+
/** Set the L1 response-cache and L2 page-cache capacities (entries).
|
|
117
|
+
* Default is 1000 each. Rust reconstructs both caches at the given
|
|
118
|
+
* capacities (moka fixes capacity at construction), so call this once at
|
|
119
|
+
* boot before serving begins. */
|
|
120
|
+
configureCache(opts: {
|
|
121
|
+
maxEntries: number;
|
|
122
|
+
pageMaxEntries: number;
|
|
123
|
+
}): void;
|
|
124
|
+
/** Tell Rust where to read `/_brust/islands/<file>` from. Called once at
|
|
125
|
+
* boot after buildIslands() emits chunks. Path must be absolute. */
|
|
126
|
+
configureIslandsDir(dir: string): void;
|
|
127
|
+
/** Tell Rust where to read `/_brust/css/<file>` from. Called from the
|
|
128
|
+
* main thread when CSS is configured. Path must be absolute. */
|
|
129
|
+
configureCssDir(dir: string): void;
|
|
130
|
+
/** Tell Rust where to read root-mapped static assets (`/favicon.ico`, …) from.
|
|
131
|
+
* Path must be absolute. */
|
|
132
|
+
configurePublicDir(dir: string): void;
|
|
133
|
+
/** Extract the MCP manifest from TypeScript source using the compiler API,
|
|
134
|
+
* write it to `.brust/mcp-manifest.json`, and return it. Call once in the
|
|
135
|
+
* main process after `brust.registerRoutes(routes)`. Workers must read the
|
|
136
|
+
* persisted manifest themselves via `brust.loadMcpManifest()` in the worker
|
|
137
|
+
* branch and pass an `McpServer` (built via `makeMcpServer`) to
|
|
138
|
+
* `makeRenderer` via `opts.mcp`. See example/pokedex/index.ts. */
|
|
139
|
+
buildMcpManifest(opts: {
|
|
140
|
+
actionsFile?: string;
|
|
141
|
+
routesFile: string;
|
|
142
|
+
sourceRoots: string[];
|
|
143
|
+
routes: import("./routes.ts").FlatRoute[];
|
|
144
|
+
cwd?: string;
|
|
145
|
+
}): Promise<import("./mcp/manifest.ts").McpManifest>;
|
|
146
|
+
/** Read the MCP manifest from `.brust/mcp-manifest.json`. Returns null if
|
|
147
|
+
* the file does not exist (i.e. the main process hasn't called
|
|
148
|
+
* `brust.buildMcpManifest` yet). Throws if the file is malformed. */
|
|
149
|
+
loadMcpManifest(cwd?: string): Promise<import("./mcp/manifest.ts").McpManifest | null>;
|
|
150
|
+
registerRenderer(buf: Uint8Array, slots: number, fn: RenderFn): number;
|
|
151
|
+
/**
|
|
152
|
+
* One-call lifecycle: scans actions, builds islands (if `routes.tsx` uses
|
|
153
|
+
* `<Island>`), registers routes + SSE/WS paths, builds the MCP manifest, then
|
|
154
|
+
* branches to `serve()` (main thread) or registers a renderer (worker
|
|
155
|
+
* thread). Replaces ~70 lines of boilerplate; the lower-level helpers
|
|
156
|
+
* (`scanActions`, `registerRoutes`, `makeRenderer`, etc.) are still exported
|
|
157
|
+
* for apps that need finer control.
|
|
158
|
+
*
|
|
159
|
+
* Conventions assumed (override via the corresponding option if your layout
|
|
160
|
+
* differs):
|
|
161
|
+
* - `<scanRoot>/routes.tsx` — scanned for `<Island>` usage (islands)
|
|
162
|
+
* and referenced by the MCP manifest builder
|
|
163
|
+
*
|
|
164
|
+
* `scanRoot` defaults to the directory of `entry` so the typical caller
|
|
165
|
+
* passes only `{ routes, entry: import.meta.url }`.
|
|
166
|
+
*/
|
|
167
|
+
run(opts: {
|
|
168
|
+
routes: import("./routes.ts").FlatRoute[];
|
|
169
|
+
entry: string;
|
|
170
|
+
scanRoot?: string;
|
|
171
|
+
/** Host/address to bind on. Default `localhost`. A `brust dev --port` /
|
|
172
|
+
* BRUST_PORT / BRUST_ADDR / brust.toml all override this app-level value
|
|
173
|
+
* (precedence: env > toml > this > framework default). */
|
|
174
|
+
address?: string;
|
|
175
|
+
/** TCP port to bind on. Default 1337. Overridable by env/toml — see
|
|
176
|
+
* `address`. */
|
|
177
|
+
port?: number;
|
|
178
|
+
/** Actions builder from `defineActions(...)`. Registered with Rust and
|
|
179
|
+
* threaded to each worker's renderer. Omit if the app has no actions. */
|
|
180
|
+
actions?: import("./define-actions.ts").ActionsBuilder;
|
|
181
|
+
/** URL prefix the action router mounts under. Threaded to serve(). */
|
|
182
|
+
actionPrefix?: string;
|
|
183
|
+
/** Optional global CORS policy — see {@link CorsOptions}. Threaded to
|
|
184
|
+
* serve() like `actionPrefix`. */
|
|
185
|
+
cors?: CorsOptions;
|
|
186
|
+
/** Overrides merged into the underlying `serve()` call (main thread). */
|
|
187
|
+
serve?: Partial<Omit<ServeOptions, "entry" | "actions" | "mcp">>;
|
|
188
|
+
/** Per-worker SAB size in bytes. Default 256 KB. */
|
|
189
|
+
sabBytes?: number;
|
|
190
|
+
/** When true, prepend the dev WS route, install file watcher, set the
|
|
191
|
+
* dev-client snippet, and start the TUI. Default false.
|
|
192
|
+
* Also activated by BRUST_DEV=1 environment variable. */
|
|
193
|
+
dev?: boolean;
|
|
194
|
+
}): Promise<void>;
|
|
195
|
+
};
|
|
196
|
+
export { defineRoutes, makeRenderer, Outlet, notFound, redirect, isNativeVerdict, httpError, isHttpErrorTrigger, } from './routes.ts';
|
|
197
|
+
export type { Route, RouteCall, RouteContext, ErrorBoundaryProps, RouteCacheConfig, BrustRequest, RouteResponse, Middleware, NativeVerdict, HttpErrorOpts, HttpErrorTrigger, } from './routes.ts';
|
|
198
|
+
export { defineActions, isValidEndpointPath } from './define-actions.ts';
|
|
199
|
+
export type { EndpointDef, ActionContext, EndpointOptions, ActionsBuilder, } from './define-actions.ts';
|
|
200
|
+
export { ActionError, isActionError } from './action-error.ts';
|
|
201
|
+
export type { ActionErrorBody } from './action-error.ts';
|
|
202
|
+
export { loadConfig, BrustConfigError } from './config.ts';
|
|
203
|
+
export type { BrustConfig } from './config.ts';
|
|
204
|
+
export { Island } from './islands/island.tsx';
|
|
205
|
+
export type { IslandProps, HydrateTrigger } from './islands/island.tsx';
|
|
206
|
+
import './islands/isr-jsx.ts';
|
|
207
|
+
export type { IsrConfig } from './islands/isr-jsx.ts';
|
|
208
|
+
export { BrustPage } from './islands/brust-page.tsx';
|
|
209
|
+
export type { BrustPageProps, HeadEntry } from './islands/brust-page.tsx';
|
|
210
|
+
export { defineStore, signal, computed, effect, batch } from './store/index.ts';
|
|
211
|
+
export type { StoreHandle, Snapshot, Signal, Computed } from './store/index.ts';
|
|
212
|
+
export { dedupe, cachedFetch } from './loader-cache.ts';
|
|
213
|
+
export { renderFragment } from './render/fragment.ts';
|
|
214
|
+
export type { RenderFragmentOpts } from './render/fragment.ts';
|
|
215
|
+
export { buildIslands } from './islands/build.ts';
|
|
216
|
+
export type { IslandsBuildResult, BuildIslandsOptions } from './islands/build.ts';
|
|
217
|
+
export { cache } from './cache.ts';
|
|
218
|
+
export type { InvalidateArgs } from './cache.ts';
|
|
219
|
+
export { templates } from './templates.ts';
|
|
220
|
+
export { getRequestContext } from './request-context.ts';
|
|
221
|
+
export { cookies } from './cookies.ts';
|
|
222
|
+
export type { CookieOptions } from './cookies.ts';
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
/** Props for the built-in `<BrustPage>` document shell.
|
|
3
|
+
*
|
|
4
|
+
* `<BrustPage>` is a NATIVE-route document component: in a `native: true` route
|
|
5
|
+
* the Rust JSX compiler intercepts the `BrustPage` tag and emits the whole
|
|
6
|
+
* `<html>/<head>/<body>` skeleton itself, auto-injecting the framework head tags
|
|
7
|
+
* (charset, viewport, the `/_brust/css/app.css` stylesheet link). The head is
|
|
8
|
+
* configured ENTIRELY through these props — you never write `<head>` markup, so
|
|
9
|
+
* brust keeps full ownership of `<head>` and can add more tags later (importmap,
|
|
10
|
+
* preloads) without colliding with hand-written head elements.
|
|
11
|
+
*
|
|
12
|
+
* On the native path each prop accepts a compile-time string literal OR a
|
|
13
|
+
* loader member-path (`title={data.title}`), interpolated into the Rust-rendered
|
|
14
|
+
* shell as `{{ path }}` (S8). Calls/arithmetic are still rejected. This React
|
|
15
|
+
* implementation mirrors the compiled output for the rare non-native use. */
|
|
16
|
+
/** One extra `<head>` element for `<BrustPage head={[…]}>`. Discriminated by
|
|
17
|
+
* `tag`. On the native path, attribute values may be a string literal or a
|
|
18
|
+
* loader member-path (HTML-escaped); `text` (inner content of style/script/
|
|
19
|
+
* noscript) must be a static string literal (raw — never user input). */
|
|
20
|
+
export type HeadEntry = {
|
|
21
|
+
tag: 'link';
|
|
22
|
+
rel: string;
|
|
23
|
+
href: string;
|
|
24
|
+
type?: string;
|
|
25
|
+
sizes?: string;
|
|
26
|
+
as?: string;
|
|
27
|
+
media?: string;
|
|
28
|
+
crossOrigin?: string;
|
|
29
|
+
} | {
|
|
30
|
+
tag: 'meta';
|
|
31
|
+
name?: string;
|
|
32
|
+
property?: string;
|
|
33
|
+
httpEquiv?: string;
|
|
34
|
+
content: string;
|
|
35
|
+
} | {
|
|
36
|
+
tag: 'base';
|
|
37
|
+
href?: string;
|
|
38
|
+
target?: string;
|
|
39
|
+
} | {
|
|
40
|
+
tag: 'style';
|
|
41
|
+
text: string;
|
|
42
|
+
media?: string;
|
|
43
|
+
} | {
|
|
44
|
+
tag: 'script';
|
|
45
|
+
src?: string;
|
|
46
|
+
text?: string;
|
|
47
|
+
type?: string;
|
|
48
|
+
defer?: boolean;
|
|
49
|
+
async?: boolean;
|
|
50
|
+
crossOrigin?: string;
|
|
51
|
+
} | {
|
|
52
|
+
tag: 'noscript';
|
|
53
|
+
text: string;
|
|
54
|
+
};
|
|
55
|
+
export interface BrustPageProps {
|
|
56
|
+
/** `<html lang>` — defaults to `"en"`. */
|
|
57
|
+
lang?: string;
|
|
58
|
+
/** `<html class>` (e.g. `"dark"`). */
|
|
59
|
+
className?: string;
|
|
60
|
+
/** `<body class>`. */
|
|
61
|
+
bodyClassName?: string;
|
|
62
|
+
/** `<title>…</title>`. Omitted when absent. */
|
|
63
|
+
title?: string;
|
|
64
|
+
/** `<meta name="description" content="…">`. Omitted when absent. */
|
|
65
|
+
description?: string;
|
|
66
|
+
/** Extra `<head>` elements (link/meta/base/style/script/noscript). */
|
|
67
|
+
head?: HeadEntry[];
|
|
68
|
+
/** Page body — rendered inside `<body>`. */
|
|
69
|
+
children?: ReactNode;
|
|
70
|
+
/** Arbitrary `data-*` on `<html>` (e.g. `data-mode="dark"`). String literal
|
|
71
|
+
* or loader member-path on the native path. */
|
|
72
|
+
[dataAttr: `data-${string}`]: string | undefined;
|
|
73
|
+
}
|
|
74
|
+
export declare function BrustPage({ lang, className, bodyClassName, title, description, head, children, ...rest }: BrustPageProps): ReactNode;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { BunPlugin } from 'bun';
|
|
2
|
+
export interface IslandsBuildResult {
|
|
3
|
+
/** Absolute path to the output directory passed to brust's Rust side. */
|
|
4
|
+
outDir: string;
|
|
5
|
+
/** Number of island chunks emitted (excludes runtime + bootstrap). */
|
|
6
|
+
islandCount: number;
|
|
7
|
+
/** id → content-addressed chunk URL (`/_brust/islands/<id>_<hash>.js`). Also
|
|
8
|
+
* written to `_islands.js` for the client bootstrap to resolve at runtime. */
|
|
9
|
+
chunks: Record<string, string>;
|
|
10
|
+
}
|
|
11
|
+
export interface BuildIslandsOptions {
|
|
12
|
+
/** Override the output directory. Default: `<cwd>/.brust/islands`. */
|
|
13
|
+
outDir?: string;
|
|
14
|
+
/** Build plugins passed straight to `Bun.build` for the per-island chunks.
|
|
15
|
+
* Needed for the component-CSS loader: global `Bun.plugin()` registrations do
|
|
16
|
+
* NOT apply to `Bun.build`, so an island that `import`s a `.module.css` must
|
|
17
|
+
* get the resolver here or Bun emits the CSS as a separate asset and collides
|
|
18
|
+
* on the output filename (X.module.css + X.tsx → both X.js). */
|
|
19
|
+
plugins?: BunPlugin[];
|
|
20
|
+
}
|
|
21
|
+
/** Scan a routes entry file for `<Island component={X} />` usage and derive the
|
|
22
|
+
* island chunk list (componentName → absolute source path). Replaces the old
|
|
23
|
+
* static config-file lookup — the chunk set is derived from source.
|
|
24
|
+
*
|
|
25
|
+
* 1. Resolve the entry's page imports via {@link scanImports}.
|
|
26
|
+
* 2. For each page, slice every `<Island … />` tag and capture its
|
|
27
|
+
* `component={Ident}`. A tag with no `component` is a hard error (F3:
|
|
28
|
+
* never silently skip an island).
|
|
29
|
+
* 3. Resolve each captured ident through that page's OWN imports.
|
|
30
|
+
* 4. Key by the content-addressed {@link islandChunkBasename} (`<Name>_<hash>`),
|
|
31
|
+
* NOT the bare name — so two DIFFERENT files exporting a same-named component
|
|
32
|
+
* produce two distinct chunks. Same name + same file dedups (same id). The
|
|
33
|
+
* marker carries this same id (native: reconcileIslandManifest rewrite;
|
|
34
|
+
* React: the Component→id registry seeded at worker boot), so there is no
|
|
35
|
+
* app-unique-name requirement.
|
|
36
|
+
*
|
|
37
|
+
* `extraIslands` (task 2.8) merges additional islands the routes-graph scan
|
|
38
|
+
* cannot see — md-route islands resolved by `emitMdTemplates` (`name →
|
|
39
|
+
* absolute source path`, the bare-name map it returns). Each is keyed by the
|
|
40
|
+
* same content-addressed id, so same name + same path dedups against the scan
|
|
41
|
+
* result; same name + different path yields a distinct id (two chunks, the
|
|
42
|
+
* shipped same-name parity). A same-id-different-path collision is a hard
|
|
43
|
+
* error — never silently rebind a chunk id.
|
|
44
|
+
*/
|
|
45
|
+
export declare function scanIslandChunks(routesEntryFile: string, extraIslands?: Map<string, string>): Map<string, string>;
|
|
46
|
+
/** Build the runtime chunks + all island chunks + bootstrap. Returns the
|
|
47
|
+
* absolute output directory; caller passes it to `brust.configureIslandsDir`. */
|
|
48
|
+
export declare function buildIslands(islands: Map<string, string>, options?: BuildIslandsOptions): Promise<IslandsBuildResult>;
|
|
49
|
+
export declare function buildOne(entrypoints: string[], outdir: string, naming: string, external: string[], plugins?: BunPlugin[]): Promise<void>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Content-addressed island id / chunk basename = `<Name>_<8hex(sha256
|
|
2
|
+
* cwd-relative source path)>`. The SINGLE name contract (mirrors the directive
|
|
3
|
+
* chunk scheme): the chunk filename, the `data-brust-island` marker (native
|
|
4
|
+
* rewrite in reconcileIslandManifest + the React island-id onLoad plugin), and
|
|
5
|
+
* the bootstrap import all derive from this — so two same-named components from
|
|
6
|
+
* different files get distinct ids and never collide.
|
|
7
|
+
*
|
|
8
|
+
* Lives in its own module (no `scanImports` dep) so both `islands/build.ts` and
|
|
9
|
+
* `cli/native-routes-emit.ts` can import it without a circular dependency. */
|
|
10
|
+
export declare function islandChunkBasename(name: string, absSourcePath: string): string;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { type ComponentType, type ReactNode } from 'react';
|
|
2
|
+
import type { IsrConfig } from './isr-jsx.ts';
|
|
3
|
+
/** Triggers that activate hydration of an island marker. */
|
|
4
|
+
export type HydrateTrigger = 'load' | 'idle' | 'visible' | 'interaction';
|
|
5
|
+
export interface IslandProps<P> {
|
|
6
|
+
/** Component rendered server-side INSIDE the marker. Same component
|
|
7
|
+
* the client chunk default-exports — SSR HTML must match the post-hydrate
|
|
8
|
+
* tree to avoid React reconciliation warnings. Its `Component.name` is the
|
|
9
|
+
* island id: it names the chunk (`<name>.js`) and the `data-brust-island`
|
|
10
|
+
* marker the client bootstrap reads, so it must be a stable, named
|
|
11
|
+
* component (no anonymous default export). */
|
|
12
|
+
component: ComponentType<P>;
|
|
13
|
+
/** Props passed to the component on both server and client. Must be
|
|
14
|
+
* JSON-serializable (no functions, classes, DOM nodes, etc.). Optional — a
|
|
15
|
+
* propless island (e.g. one that reads only global/client state) may omit it;
|
|
16
|
+
* it defaults to `{}`. On native routes, omitting `props` lowers to an empty
|
|
17
|
+
* props_path the renderer fills with `{}`. */
|
|
18
|
+
props?: P;
|
|
19
|
+
/** When to hydrate. Default 'load'. */
|
|
20
|
+
hydrate?: HydrateTrigger;
|
|
21
|
+
/** Native routes only: render this island server-side (renderToString during
|
|
22
|
+
* the loader crossing) so its markup ships in the HTML, then hydrate. Ignored
|
|
23
|
+
* on the React path (the whole tree already SSRs there). Default false. */
|
|
24
|
+
ssr?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Optional ISR (incremental static regeneration) cache for an `ssr` island on
|
|
27
|
+
* a native-jinja route. When present, the island's `renderToString` runs ONCE
|
|
28
|
+
* per `key`; later requests serve the frozen markup from the Rust-side cache.
|
|
29
|
+
* Ignored unless `ssr` is set (caching a client-only island is meaningless).
|
|
30
|
+
*
|
|
31
|
+
* - `key` (required): unique string identifying this cache entry. A different
|
|
32
|
+
* key is a different cached render. Compute it in the loader and pass it
|
|
33
|
+
* through, e.g. `isr={{ key: data.cacheKey }}`.
|
|
34
|
+
* - `tags` (optional): groups for bulk invalidation —
|
|
35
|
+
* `import { cache } from 'brustjs'; cache.invalidate({ tags: ['blog'] })`
|
|
36
|
+
* evicts every entry carrying that tag. `cache.invalidate({ key })` evicts one.
|
|
37
|
+
* - `revalidate` (optional): TTL in **seconds** (integer). Omit to cache until
|
|
38
|
+
* explicitly invalidated.
|
|
39
|
+
*
|
|
40
|
+
* Example: `isr={{ key: data.cacheKey, tags: ['blog'], revalidate: 60 }}`
|
|
41
|
+
*/
|
|
42
|
+
isr?: IsrConfig;
|
|
43
|
+
}
|
|
44
|
+
/** Per-render box tracking whether any `<Island>` rendered. Created fresh per
|
|
45
|
+
* render and provided through {@link IslandUsedContext}; the renderer reads
|
|
46
|
+
* `box.used` once at the end to decide whether to prepend the importmap +
|
|
47
|
+
* bootstrap script.
|
|
48
|
+
*
|
|
49
|
+
* This is REQUEST-SCOPED (not a module-scope flag) so concurrent renders in one
|
|
50
|
+
* isolate (renderSlots>1) never cross-contaminate: React restores each render's
|
|
51
|
+
* context stack across Suspense resumption, so an interleaved peer setting its
|
|
52
|
+
* own box never flips ours. A module `let` could not give that guarantee. */
|
|
53
|
+
export interface IslandUsedBox {
|
|
54
|
+
used: boolean;
|
|
55
|
+
}
|
|
56
|
+
/** Fresh per-render box. */
|
|
57
|
+
export declare function createIslandUsedBox(): IslandUsedBox;
|
|
58
|
+
/** Seed the Component→id registry (worker boot). */
|
|
59
|
+
export declare function configureIslandIdRegistry(entries: Iterable<[unknown, string]>): void;
|
|
60
|
+
/** Carries the per-render {@link IslandUsedBox} down to every `<Island>`. The
|
|
61
|
+
* renderer wraps the tree in a Provider with a fresh box; an `<Island>` rendered
|
|
62
|
+
* with no Provider (e.g. a standalone `renderToString` outside the React render
|
|
63
|
+
* path) reads `null` and is a no-op. */
|
|
64
|
+
export declare const IslandUsedContext: import("react").Context<IslandUsedBox>;
|
|
65
|
+
export declare function Island<P extends object>({ component: Component, props, hydrate, }: IslandProps<P>): ReactNode;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Shape of the `isr` attribute on an SSR component or `ssr` island on a
|
|
2
|
+
* native-jinja route. `renderToString` runs ONCE per `key`; later same-key
|
|
3
|
+
* requests serve the frozen markup from the Rust-side cache. */
|
|
4
|
+
export interface IsrConfig {
|
|
5
|
+
/** Required unique cache key. A different key is a different cached render.
|
|
6
|
+
* Compute it in the loader and pass it through (e.g. `data.cacheKey`). */
|
|
7
|
+
key: string;
|
|
8
|
+
/** Optional groups for bulk invalidation —
|
|
9
|
+
* `import { cache } from 'brustjs'; cache.invalidate({ tags: ['blog'] })`
|
|
10
|
+
* evicts every entry carrying a tag. `cache.invalidate({ key })` evicts one. */
|
|
11
|
+
tags?: string[];
|
|
12
|
+
/** Optional TTL in SECONDS (integer). Omit to cache until invalidated. */
|
|
13
|
+
revalidate?: number;
|
|
14
|
+
}
|
|
15
|
+
declare module 'react' {
|
|
16
|
+
namespace JSX {
|
|
17
|
+
interface IntrinsicAttributes {
|
|
18
|
+
/** brust ISR cache directive — compiler-consumed (stripped before the
|
|
19
|
+
* component is called), valid on any SSR component or `ssr` island on a
|
|
20
|
+
* native route. See {@link IsrConfig}. */
|
|
21
|
+
isr?: IsrConfig;
|
|
22
|
+
/** brust `native` inline directive — compiler-consumed (stripped at lower
|
|
23
|
+
* time). On a native-jinja route, `<Comp native />` expands the component
|
|
24
|
+
* inline at compile time (no JS-worker render) when it is pure
|
|
25
|
+
* (props→JSX, no hooks/side-effects); otherwise it degrades to a normal
|
|
26
|
+
* SSR component with a build warning. Bare boolean, like `ssr` on an
|
|
27
|
+
* island. */
|
|
28
|
+
native?: boolean;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/** Set the directory the sidecar-manifest readers resolve against. Called once
|
|
2
|
+
* at boot from `index.ts`, mirroring `configureIslandsDir`/`configureCssDir`. */
|
|
3
|
+
export declare function configureJinjaDir(dir: string): void;
|
|
4
|
+
/** One entry of a `<template>.islands.json` manifest (enriched by T6). */
|
|
5
|
+
export interface NativeIslandEntry {
|
|
6
|
+
component: string;
|
|
7
|
+
instance: number;
|
|
8
|
+
propsPath: string;
|
|
9
|
+
/** Literal props value baked at build time (md pages). When present
|
|
10
|
+
* (`!== undefined` — `null` and falsy literals are valid), it is used as the
|
|
11
|
+
* props value and `propsPath` is ignored (md entries carry `propsPath: ''`
|
|
12
|
+
* since the field is required). */
|
|
13
|
+
propsLiteral?: unknown;
|
|
14
|
+
ssr: boolean;
|
|
15
|
+
hydrate: string;
|
|
16
|
+
sourcePath: string;
|
|
17
|
+
/** Dotted path into loader data yielding the ISR cache key (string). */
|
|
18
|
+
keyPath?: string;
|
|
19
|
+
/** Dotted path into loader data yielding the ISR cache tags (string[]). */
|
|
20
|
+
tagsPath?: string;
|
|
21
|
+
/** Revalidate window in SECONDS; converted to ttlMs on cache.set. */
|
|
22
|
+
revalidate?: number;
|
|
23
|
+
/** Static ISR cache key (string literal in JSX). Takes precedence over keyPath. */
|
|
24
|
+
keyLiteral?: string;
|
|
25
|
+
/** Static ISR cache tags (array literal in JSX). Takes precedence over tagsPath. */
|
|
26
|
+
tagsLiteral?: string[];
|
|
27
|
+
}
|
|
28
|
+
/** Rust-side ISR cache, injected as a port for testability. A `get` hit
|
|
29
|
+
* returns a FROZEN {html,props} pair (the props are the entity-encoded attr
|
|
30
|
+
* string, identical to what was stored) so the served markup and the hydrated
|
|
31
|
+
* props stay byte-identical regardless of live loader-data mutation. */
|
|
32
|
+
export interface IslandCache {
|
|
33
|
+
get(key: string): {
|
|
34
|
+
html: string;
|
|
35
|
+
props: string;
|
|
36
|
+
} | null;
|
|
37
|
+
set(key: string, tags: string[], ttlMs: number | undefined, html: string, props: string): void;
|
|
38
|
+
}
|
|
39
|
+
/** Walk a dotted path into `data`. Each segment must be an OWN enumerable
|
|
40
|
+
* property — inherited keys (`constructor`, `__proto__`, `toString`, …) yield
|
|
41
|
+
* `undefined` rather than traversing the prototype chain. This blocks both
|
|
42
|
+
* prototype-pollution-style reads AND the downstream crash where a resolved
|
|
43
|
+
* function (`Object`) makes `JSON.stringify` return `undefined`. A missing
|
|
44
|
+
* segment, a nullish/primitive cursor, or a non-own key all yield `undefined`.
|
|
45
|
+
* An empty path returns `data` itself. */
|
|
46
|
+
export declare function pathInto(data: unknown, propsPath: string): unknown;
|
|
47
|
+
/** HTML-entity-encode a string for a double-quoted attribute value. Order is
|
|
48
|
+
* load-bearing: `&` MUST be replaced first so the entities introduced by the
|
|
49
|
+
* later replacements aren't themselves double-encoded. Matches the compiler's
|
|
50
|
+
* `push_attr_escaped` charset (& < > ") so server-rendered markup and these
|
|
51
|
+
* props attrs stay consistent. */
|
|
52
|
+
export declare function entityEncode(s: string): string;
|
|
53
|
+
/** Read `<jinjaDir>/<templateName>.islands.json` and return the parsed entry
|
|
54
|
+
* array, or `null` if the file doesn't exist. `jinjaDir` defaults to the
|
|
55
|
+
* boot-configured jinja dir (`configureJinjaDir`), or `cwd/.brust/jinja` when
|
|
56
|
+
* unset; tests pass a temp dir. Both hits and misses are cached by absolute path. */
|
|
57
|
+
export declare function loadIslandManifest(templateName: string, jinjaDir?: string): NativeIslandEntry[] | null;
|
|
58
|
+
/** Build the per-island context additions for a manifest. Each entry
|
|
59
|
+
* contributes `island_<instance>_props` — the resolved props, JSON-stringified
|
|
60
|
+
* (undefined → null so it stays valid JSON) and entity-encoded. SSR entries
|
|
61
|
+
* (`ssr:true`) ALSO contribute `island_<instance>_html` — the island source
|
|
62
|
+
* component imported by absolute path and renderToString'd. The `instance` is a
|
|
63
|
+
* per-occurrence integer, so it's a safe key fragment. */
|
|
64
|
+
export declare function resolveIslandContext(manifest: NativeIslandEntry[], data: unknown, cache?: IslandCache): Promise<Record<string, string>>;
|
|
65
|
+
/** One entry in `<Name>.components.json` as enriched by `emitComponentArtifacts`. */
|
|
66
|
+
export interface NativeComponentEntry {
|
|
67
|
+
component: string;
|
|
68
|
+
instance: number;
|
|
69
|
+
sourcePath: string;
|
|
70
|
+
/** Dotted path into loader data yielding the ISR cache key (string). */
|
|
71
|
+
keyPath?: string;
|
|
72
|
+
/** Dotted path into loader data yielding the ISR cache tags (string[]). */
|
|
73
|
+
tagsPath?: string;
|
|
74
|
+
/** Revalidate window in SECONDS; converted to ttlMs on cache.set. */
|
|
75
|
+
revalidate?: number;
|
|
76
|
+
/** Static ISR cache key (string literal in JSX). Takes precedence over keyPath. */
|
|
77
|
+
keyLiteral?: string;
|
|
78
|
+
/** Static ISR cache tags (array literal in JSX). Takes precedence over tagsPath. */
|
|
79
|
+
tagsLiteral?: string[];
|
|
80
|
+
}
|
|
81
|
+
/** Read `<jinjaDir>/<templateName>.components.json` and return the parsed entry
|
|
82
|
+
* array, or `null` if the file doesn't exist. Both hits and misses are cached
|
|
83
|
+
* by absolute path (same invariant as `loadIslandManifest`). */
|
|
84
|
+
export declare function loadComponentManifest(templateName: string, jinjaDir?: string): NativeComponentEntry[] | null;
|
|
85
|
+
/** Build the per-component context additions for a manifest. Each entry
|
|
86
|
+
* contributes `comp_<instance>_html` — the component rendered to HTML by
|
|
87
|
+
* the route's factory function. On `renderToString` failure: degrade to
|
|
88
|
+
* `comp_N_html = ""` and log, mirrors SSR island failure behaviour. */
|
|
89
|
+
export declare function resolveComponentContext(manifest: NativeComponentEntry[], data: unknown, templateName: string, jinjaDir?: string, cache?: IslandCache): Promise<Record<string, string>>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare function runInRequestCache<T>(fn: () => T): T;
|
|
2
|
+
/** Request-scoped memoize: share the in-flight promise + cache result for the
|
|
3
|
+
* scope's lifetime. Outside a scope → passthrough. Reject → guarded delete
|
|
4
|
+
* (identity-checked) so a stale catch can't evict a newer entry.
|
|
5
|
+
*
|
|
6
|
+
* NOTE: the reject cleanup runs one microtask after rejection, so a caller that
|
|
7
|
+
* dedupes the SAME key within that gap receives the about-to-reject promise (and
|
|
8
|
+
* thus the rejection) — acceptable: the result is one shared failure, not a hang. */
|
|
9
|
+
export declare function dedupe<T>(key: string, fn: () => Promise<T>): Promise<T>;
|
|
10
|
+
/** Idempotent (GET/HEAD) fetch deduped per request; non-idempotent → bypass.
|
|
11
|
+
* Returns a fresh clone every call (the stored Response is never exposed).
|
|
12
|
+
*
|
|
13
|
+
* NOTE: the cache key is `method + url` ONLY — it does NOT include `init`
|
|
14
|
+
* headers/body. Two `cachedFetch(sameUrl, {headers:…})` calls with DIFFERENT
|
|
15
|
+
* headers in one request share the FIRST call's response. Intended for plain
|
|
16
|
+
* idempotent GETs (the common loader case); if a caller varies `init` per call
|
|
17
|
+
* on the same URL, use `fetch` directly. */
|
|
18
|
+
export declare function cachedFetch(url: string, init?: RequestInit): Promise<Response>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { FlatRoute } from '../routes.ts';
|
|
2
|
+
import type { McpManifest } from './manifest.ts';
|
|
3
|
+
export interface ExtractOptions {
|
|
4
|
+
/** Module exporting `defineActions(...)`. Convention `<scanRoot>/actions.ts`.
|
|
5
|
+
* Absent → zero tools (resources still extracted). */
|
|
6
|
+
actionsFile?: string;
|
|
7
|
+
/** The routes module file. */
|
|
8
|
+
routesFile: string;
|
|
9
|
+
/** User source roots. Reserved for future tsconfig resolution. */
|
|
10
|
+
sourceRoots: string[];
|
|
11
|
+
/** Result of `defineRoutes(...)`. */
|
|
12
|
+
routes: FlatRoute[];
|
|
13
|
+
}
|
|
14
|
+
export declare function extractMcpManifest(opts: ExtractOptions): Promise<McpManifest>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { JsonSchema } from './schema.ts';
|
|
2
|
+
export interface ToolSchema {
|
|
3
|
+
name: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD';
|
|
6
|
+
path: string;
|
|
7
|
+
inputSchema: JsonSchema;
|
|
8
|
+
outputSchema?: JsonSchema;
|
|
9
|
+
}
|
|
10
|
+
export interface ResourceSchema {
|
|
11
|
+
uriTemplate: string;
|
|
12
|
+
name: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
outputSchema?: JsonSchema;
|
|
15
|
+
routeIndex: number;
|
|
16
|
+
}
|
|
17
|
+
export interface McpManifest {
|
|
18
|
+
version: 1;
|
|
19
|
+
tools: ToolSchema[];
|
|
20
|
+
resources: ResourceSchema[];
|
|
21
|
+
}
|
|
22
|
+
export declare function writeManifest(cwd: string, m: McpManifest): Promise<void>;
|
|
23
|
+
export declare function readManifest(cwd: string): Promise<McpManifest | null>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
export interface JsonSchema {
|
|
3
|
+
type?: string | string[];
|
|
4
|
+
properties?: Record<string, JsonSchema>;
|
|
5
|
+
required?: string[];
|
|
6
|
+
items?: JsonSchema;
|
|
7
|
+
prefixItems?: JsonSchema[];
|
|
8
|
+
minItems?: number;
|
|
9
|
+
maxItems?: number;
|
|
10
|
+
additionalProperties?: JsonSchema | boolean;
|
|
11
|
+
anyOf?: JsonSchema[];
|
|
12
|
+
enum?: unknown[];
|
|
13
|
+
format?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ToJsonSchemaOptions {
|
|
16
|
+
unwrapPromise?: boolean;
|
|
17
|
+
checker?: ts.TypeChecker;
|
|
18
|
+
}
|
|
19
|
+
export declare function tsTypeToJsonSchema(type: ts.Type, opts?: ToJsonSchemaOptions): JsonSchema | undefined;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { FlatRoute, BrustRequest } from '../routes.ts';
|
|
2
|
+
import type { EndpointDef } from '../define-actions.ts';
|
|
3
|
+
import type { McpManifest } from './manifest.ts';
|
|
4
|
+
export interface McpServerOptions {
|
|
5
|
+
manifest: McpManifest;
|
|
6
|
+
endpoints: EndpointDef[];
|
|
7
|
+
routes: FlatRoute[];
|
|
8
|
+
packageVersion?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface McpServer {
|
|
11
|
+
handleRequest(jsonRpcBody: string, req: BrustRequest): Promise<string>;
|
|
12
|
+
}
|
|
13
|
+
export declare function makeMcpServer(opts: McpServerOptions): McpServer;
|
|
14
|
+
export declare function makeResult(id: number | string | null, result: unknown): string;
|
|
15
|
+
export declare function makeError(id: number | string | null, code: number, message: string): string;
|