@brust/core 0.0.0-stage → 2.0.0-alpha.10

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 (72) hide show
  1. package/README.md +151 -2
  2. package/bin/brust +4 -0
  3. package/native/index.d.ts +226 -0
  4. package/native/index.js +796 -0
  5. package/package.json +102 -4
  6. package/src/actions/client.ts +71 -0
  7. package/src/actions/elysia.ts +15 -0
  8. package/src/actions/load.ts +23 -0
  9. package/src/actions/prefix.ts +45 -0
  10. package/src/actions-bridge.ts +173 -0
  11. package/src/browser.ts +31 -0
  12. package/src/build/actions-only.ts +86 -0
  13. package/src/build/build-eval.ts +319 -0
  14. package/src/build/build-icons.ts +69 -0
  15. package/src/build/bundle.ts +630 -0
  16. package/src/build/cf-assets.ts +83 -0
  17. package/src/build/cf-core.ts +90 -0
  18. package/src/build/cf-entry.ts +76 -0
  19. package/src/build/cf-wrangler.ts +169 -0
  20. package/src/build/cloudflare.ts +317 -0
  21. package/src/build/compile.ts +215 -0
  22. package/src/build/config.ts +332 -0
  23. package/src/build/css.ts +275 -0
  24. package/src/build/errors.ts +11 -0
  25. package/src/build/features.ts +110 -0
  26. package/src/build/index.ts +402 -0
  27. package/src/build/manifest.ts +371 -0
  28. package/src/build/pkg-src.ts +383 -0
  29. package/src/build/scan.ts +83 -0
  30. package/src/build/server-only.ts +39 -0
  31. package/src/build/ssg-render.ts +65 -0
  32. package/src/build/ssg.ts +146 -0
  33. package/src/build/static-worker.ts +91 -0
  34. package/src/build/static.ts +258 -0
  35. package/src/build/tailwind.ts +117 -0
  36. package/src/build/tsconfig-paths.ts +67 -0
  37. package/src/build-time.ts +10 -0
  38. package/src/cache-adapter/bridge.ts +91 -0
  39. package/src/cache-adapter/conformance.ts +226 -0
  40. package/src/cache-adapter/index.ts +10 -0
  41. package/src/cache-adapter/load.ts +144 -0
  42. package/src/cache-adapter/memory.ts +229 -0
  43. package/src/cache-adapter/runtime.ts +124 -0
  44. package/src/cache-adapter/types.ts +96 -0
  45. package/src/cache-adapter/workers.ts +118 -0
  46. package/src/cache-core.ts +15 -0
  47. package/src/cache.ts +32 -0
  48. package/src/cf.ts +19 -0
  49. package/src/cli.ts +144 -0
  50. package/src/config.ts +302 -0
  51. package/src/css-modules.d.ts +8 -0
  52. package/src/dev/bun.ts +365 -0
  53. package/src/dev/client.ts +92 -0
  54. package/src/dev/devtools-data.ts +23 -0
  55. package/src/dev/devtools.ts +356 -0
  56. package/src/dev/diag.ts +30 -0
  57. package/src/dev/proxy.ts +236 -0
  58. package/src/dev/tui.ts +144 -0
  59. package/src/dev/watch.ts +81 -0
  60. package/src/dev.ts +93 -0
  61. package/src/handlers.ts +282 -0
  62. package/src/index.ts +20 -0
  63. package/src/native.ts +132 -0
  64. package/src/routes.ts +276 -0
  65. package/src/run.ts +194 -0
  66. package/src/server-handlers.ts +32 -0
  67. package/src/server.ts +5 -0
  68. package/src/store/core-adapter.ts +32 -0
  69. package/src/store/index.ts +4 -0
  70. package/src/store/react.ts +1 -0
  71. package/src/store/server-context.ts +55 -0
  72. package/src/worker.ts +229 -0
package/README.md CHANGED
@@ -1,3 +1,152 @@
1
- # Temporary Holding Version
1
+ # @brust/core
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Rust serves the pages; N Bun workers answer only `loader` and `jobs` calls. One napi addon
4
+ (`crates/brust-napi`, loader generated into `native/` by `napi build`) binds `brust-server` and
5
+ `brust-compiler`. Workspace-private (0.0.0) until m2e publishes it; peers `react`/`react-dom` 19.
6
+
7
+ ## Routes (`routes.tsx`)
8
+
9
+ ```tsx
10
+ import { defineRoutes, notFound } from '@brust/core/routes'
11
+ export const routes = defineRoutes([
12
+ { Component: AppLayout, children: [ // layout renders <Outlet />
13
+ { path: '/', Component: HomePage }, // static: never calls Bun
14
+ { path: '/items/{id}', Component: ItemPage,
15
+ loader: async ({ params }) => (params.id === 'x' ? notFound({}) : { item: await load(params.id) }),
16
+ cache: { ttl_seconds: 60, tags: ['items'] } }, // a HIT never calls Bun
17
+ ] },
18
+ ])
19
+ ```
20
+
21
+ Route fields are exactly `path`, `Component`, `loader`, `cache`, `ssg`, `children` (`ssg` only matters
22
+ to `--target static`, see [docs/static.md](../../docs/static.md)); anything else fails the
23
+ build with `<field> is not supported in M2 (M3)`. Loaders run root → leaf, results merge flat
24
+ (later keys win); return `notFound(data)` / `redirect(location, status)` or throw
25
+ `httpError(status, body)` for a verdict. `cache(Comp, opts)` and `cache.invalidate(…)` ship too.
26
+
27
+ ## `brust build [entry=routes.tsx] [--out-dir dist]`
28
+
29
+ `--target static [--strict]` also renders every page to `dist/static/` for Cloudflare Pages or Workers
30
+ Static Assets: see [docs/static.md](../../docs/static.md).
31
+
32
+ Compiles every route component through lowering, then writes (always from scratch):
33
+
34
+ ```
35
+ dist/manifest.json the only build → server contract (spec S6)
36
+ dist/jinja/<id>.jinja templates dist/jobs.js + jobs/*.server.ts worker jobs
37
+ dist/client/*-<hex>.js runtime, native chunks, react-<id> island chunks (served immutable)
38
+ dist/client/*-<hex>.css one minified stylesheet per imported .css / .module.css (see docs/css.md)
39
+ dist/public/ copied from ./public dist/index.js `bun dist/index.js` = start
40
+ ```
41
+
42
+ Every route component's `children[]` also gets one static entry per native descendant (at any depth) that has a client chunk, so its `<script>` is linked once per page.
43
+
44
+ A diagnostic (`nested-instance`, `outlet-in-react`, `outlet-outside-layout`, unsupported route
45
+ field, unresolvable Component) prints `error <rule> <message>` and exits 1. The route tree is
46
+ validated whether or not the entry used `defineRoutes` (`route-config`, `duplicate-route` for two
47
+ patterns the router cannot hold together, e.g. `/a` and `/a/`).
48
+
49
+ Server-only code never reaches a browser bundle (`error server-only-in-client <importer> imports
50
+ <spec>`, at any import depth, react islands included): `node:*` / `bun:*` / bare Node builtins
51
+ (unless an npm package of that name is installed), `@brust/core/server|native`, any
52
+ `*.server.*` file, and the paths/prefixes listed in `brust.toml`:
53
+
54
+ ```toml
55
+ [build]
56
+ server_only = ["lib/server", "@acme/db"] # import prefixes, or paths relative to the app root
57
+ ```
58
+
59
+ The `server-only` marker package is honoured the same way: a browser build that reaches `import 'server-only'` (any depth, islands included) fails with `server-only-in-client` whether or not the package throws, and on the server side (route scan, jobs, actions, the Worker) it resolves to an empty module, so a loader may import a module guarded by it.
60
+
61
+ Loaders and precompute jobs may use all of it (they run in Bun). In the browser `@brust/core`
62
+ resolves to a side-effect-free entry (`cache`, route helpers, verdicts; `cache.invalidate` throws).
63
+
64
+ The out dir is rebuilt in a sibling temp dir and swapped in only on success (a failed build leaves
65
+ the previous one untouched); an out dir that is the filesystem root, the home dir, the app root or
66
+ an ancestor of it, or that holds the entry / a route Component / `public/`, is refused
67
+ (`out-dir-unsafe`). Relative imports of
68
+ generated files resolve against the component's source dir, bare imports against the app root, so
69
+ `--out-dir` may point anywhere; at run time `dist/` must still resolve `react` and `@brust/core`
70
+ (keep it inside the app, or next to a `node_modules` that has them).
71
+
72
+ ## `brust dev [entry=routes.tsx] [--port N] [--workers N]`
73
+
74
+ The Bun-target dev server. A long-lived parent owns your port and proxies to the newest build; every
75
+ successful build is booted as a new child (`brust start` on a loopback port, `--workers 1` unless you pass
76
+ `--workers`), the proxy switches over, browsers are told to `reload`, and the old child is stopped. A failed
77
+ build **or a failed boot** keeps the old child serving and shows the error as an overlay above the stale page
78
+ (a page opened while builds fail shows it too); the next good build clears it and reloads.
79
+
80
+ - Source changes do a full reload (debounced ~150 ms; an edit made during a rebuild is not dropped).
81
+ - A change to `public/*.css` skips the build: the file is copied into the running dist and the `<link>` is
82
+ swapped (`?v=`), the page keeps its state. Other `public/` files reload.
83
+ - Every `text/html` response, including error pages, gets `/_brust/dev/client.js` (a WebSocket at
84
+ `/_brust/dev/ws`); responses are `cache-control: no-store`. `Host` and `Origin` reach the child unchanged,
85
+ so the actions cross-site guard behaves as in production.
86
+ - Dev builds use React in development mode (`NODE_ENV=development`); `brust build` / `brust start` are unchanged.
87
+ - The terminal shows request lines (`method path status ms cache`) and a status block of at most 6 lines at the
88
+ bottom (state, URL, generation, build time, last request, warnings, hotkeys `r` reload, `o` open, `w` warnings,
89
+ `c` clear, `q` quit). It never uses the alternate screen. Not a TTY, `CI` or `NO_COLOR`: plain lines only.
90
+ - **Trace for dev tools (L14).** The dev proxy generates a token per session and gives it to each child (`BRUST_DEV_TOKEN`,
91
+ napi `devToken`) and to every upstream request (`x-brust-dev-token`). Only then the core answers `x-brust-dev: base64(json)`
92
+ (route, pattern, chain, params, cache outcome, loader and render ms, jobs with kind/key/tags/hit, bun calls, the components the
93
+ page is made of). It is added after the L1 store, so it is never cached; a core without a token (production) never makes it.
94
+ The proxy strips the header, puts the trace in the page as `<script type=application/json id=__brust_trace>` and keeps the
95
+ last 50 (actions included) at `/_brust/dev/traces`. `brust build --dev` (used by `brust dev`) also writes `dev.json`
96
+ (component id → source file, tier, the react reason, diagnostics with rule/line/col/remediation; served at `/_brust/dev/meta`);
97
+ a production build writes none. In dev only, every chain level is wrapped in `<!--brust:<id>-->…<!--/brust-->` (never inside a
98
+ `<brust-island>`), and the runtime exposes its live instances on `globalThis.__brustDev.instances` (`__BRUST_DEV__` is a
99
+ build define that is `false`, and removed by the minifier, in every other build).
100
+ - **`<brust-devtools>` (L15).** The dev proxy serves `/_brust/dev/devtools.js` (bundled from `src/dev/devtools.ts` on first request) and
101
+ injects it into every HTML response, so `brust start` and production builds contain none of it. A corner indicator shows the build state
102
+ and the page's tier counts (`ready · 2s 1n 1r`); `Alt+Shift+B` toggles the panel (real buttons and tabs, arrow keys, `Esc` closes and
103
+ restores focus, contrast ≥ 4.5:1 in light and dark). Panels, each from the trace, `dev.json`, the DOM or `__brustDev` only: **Page**
104
+ (route, params, cache, timings), **Components** (tree with tier badges; a react component shows its reason and diagnostic location;
105
+ hover highlights it), **Jobs**, **Store** (seed from `data-brust-store`, live values of mounted native stores), **Islands**
106
+ (hydrated or not), **Build** (diagnostics with `file:line:col`, the current build error), **Requests** (the last 50). The terminal
107
+ status block shows the same tier counts for the last page.
108
+ - Not hot module replacement: component state does not survive an edit. `brust dev --target cloudflare` keeps
109
+ its wrangler loop (now on the same watch loop).
110
+
111
+ ## `brust start [--port N] [--workers N] [--dist-dir dist] [--entry routes.tsx]`
112
+
113
+ Prints `[brust] listening on <addr>` then `[brust] ready (N workers)`. `--port 0` picks a free port.
114
+ SIGINT/SIGTERM drains gracefully (`BRUST_DRAIN_TIMEOUT_MS`); a second signal exits at once.
115
+ Config precedence: env (`BRUST_ADDR`, `BRUST_PORT`, `BRUST_WORKERS`, `BRUST_RENDER_SLOTS`,
116
+ `BRUST_DRAIN_TIMEOUT_MS`, `BRUST_BOOT_TIMEOUT_MS`, `BRUST_CALL_TIMEOUT_MS`, `BRUST_SLOT_BYTES`,
117
+ `BRUST_MAX_LOADER_RESPONSE_BYTES`, `BRUST_DIST_DIR`, `BRUST_APP_ENTRY`) > flags
118
+ (`--port`, `--workers`, `--dist-dir`, `--entry`) and `run({ config })` > `brust.toml` (`[server] address/port/max_loader_response_bytes`,
119
+ `[workers] count/slot_bytes`, `[cors]`) > defaults (`localhost:1337`, one worker per CPU, `min(cores, 16)` slots per worker (each slot is a `slotBytes` shared response buffer, 256 KiB by default, so 10 slots = 2.5 MiB per worker), 10 s drain, 30 s
120
+ worker boot timeout, 30 s call timeout, 8 MiB loader response limit).
121
+ `slotBytes` (`[workers] slot_bytes`, env `BRUST_SLOT_BYTES`, `run({ config: { slotBytes } })`) is 64 KiB..16 MiB.
122
+ A loader, jobs or planned reply that does not fit its slot is not an error: the worker hands it to the server
123
+ over the heap, up to `maxLoaderResponseBytes` (`[server] max_loader_response_bytes`, env
124
+ `BRUST_MAX_LOADER_RESPONSE_BYTES`, `run({ config: { maxLoaderResponseBytes } })`; 256 KiB..64 MiB, default 8 MiB).
125
+ A bigger reply answers `{"error":"response too large: <n> > <limit> (maxLoaderResponseBytes)"}` (a 500). This
126
+ limit applies to loader and jobs replies; `[actions]` has its own `max_response_bytes`.
127
+ `[cors]` can also be given as `run({ config: { cors: { origins, methods, headers, exposeHeaders, credentials,
128
+ maxAgeSeconds } } })` (camelCase, an unknown key is rejected). It replaces a `brust.toml` `[cors]` as a whole,
129
+ never merging with it; `cors: null` turns the toml table off.
130
+ A loader or job call that has not settled within `BRUST_CALL_TIMEOUT_MS` answers **504**; its worker
131
+ slot stays claimed until the call settles (the late result is discarded), so a stuck call costs one
132
+ slot, never a frozen worker. Waiting for a free slot is the separate 503 "all workers busy".
133
+ `GET /_brust/cache/stats` reports L1/job cache counters plus `loader_calls` / `job_calls` /
134
+ `timed_out_calls`.
135
+
136
+ ## Worker wire (contract 7, `crates/brust-server/src/protocol.rs`)
137
+
138
+ Requests arrive as inline JSON; responses go into the worker's SharedArrayBuffer slot (never
139
+ past `floor(len / slots)` bytes). A `loader`/`jobs` reply that does not fit crosses by heap: the worker
140
+ puts the bytes with `putFetchReply` and writes the 5-byte marker `[0x00][u32 LE n]` into the slot (JSON
141
+ never starts with 0x00); the server reads the `n` bytes it was handed. A loader context too large for the
142
+ slot is planned from the bytes (`planJobsBuf`). Over `maxLoaderResponseBytes` the reply becomes
143
+ `{"error":…}`. A handler never rejects.
144
+
145
+ `loader` `{routeId, params, path, req}` → `{ok:true, data}` | `{verdict, …}` | `{error}`;
146
+ `jobs` `{jobs:[{id, componentId, kind, inputs, target?, row?}]}` → `{results:[{id, value}|{id, error}]}`
147
+ (an `ssr` job renders `jobs[target]` with the manifest job's `literals` merged over `inputs`).
148
+
149
+ **M2 limits:** the route fields above only; per-row child instances one level deep (a nested
150
+ instance is a build error); a react child's props must be props paths or literals; no worker respawn.
151
+ **Gates:** `bun run build:debug && bun run typecheck && bun test` (CI runs each server-starting
152
+ file alone; e2e: `bun test --timeout 120000 test/e2e.test.ts`).
package/bin/brust ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env bun
2
+ import { main } from '../src/cli.ts'
3
+
4
+ await main(process.argv.slice(2))
@@ -0,0 +1,226 @@
1
+ /* auto-generated by NAPI-RS */
2
+ /* eslint-disable */
3
+
4
+ /**
5
+ * Which binding artifact the generated loader actually loaded: `'native'` for
6
+ * a native addon, otherwise the `platformArchABI` of the WASI flavor. Every
7
+ * flavor napi-rs can build is listed, because `NAPI_RS_NATIVE_LIBRARY_PATH`
8
+ * can point the loader at a WASI artifact this package does not build itself.
9
+ */
10
+ export declare const __napiBindingTarget: 'native' | 'wasm32-wasi' | 'wasm32-wasip1'
11
+
12
+ /**
13
+ * Graceful drain of the current server: stop accepting, wait for in-flight
14
+ * connections up to `timeout_ms`.
15
+ */
16
+ export declare function beginDrain(timeoutMs: number): Promise<void>
17
+
18
+ /** Drop both tier-1 caches (a broadcast gap, R10); `stats.l2.resets`++. The L1 entries removed. */
19
+ export declare function cacheClear(): number
20
+
21
+ /**
22
+ * Static output (gaps round R9.4): the request reads of one `cache.prefix` / `cache.bypass`
23
+ * expression (`header(x)`, `cookie(x)`, `query(x)`, `request(host)`), parsed by the server's own
24
+ * grammar. Errors when the expression does not parse. Needs no running server.
25
+ */
26
+ export declare function cacheExprReads(src: string): Array<string>
27
+
28
+ /** `cache.invalidate({ key?, tags?, path?, method? })` (spec §5). */
29
+ export declare function cacheInvalidate(args: NapiInvalidateArgs): NapiInvalidateResult
30
+
31
+ /**
32
+ * Bump the `stats.l2` counter `name` (the JS side's counts, R9). `false` = unknown name, no
33
+ * adapter, or no server. Never throws.
34
+ */
35
+ export declare function cacheL2Count(name: string): boolean
36
+
37
+ /**
38
+ * A broadcast invalidation another process published (worker 0's subscriber, cache-adapter R10):
39
+ * tier 1 + the floor at `seq`. Never re-published (L3). Same args/result as `cache_invalidate`.
40
+ */
41
+ export declare function cacheReplay(args: NapiInvalidateArgs, seq?: number | undefined | null): NapiInvalidateResult
42
+
43
+ /** `/_brust/cache/stats` JSON (snake_case keys, as the server serialises it). */
44
+ export declare function cacheStats(): string
45
+
46
+ export interface CompiledComponent {
47
+ id: string
48
+ source: string
49
+ /** `ComponentIR` as JSON (snake_case keys, as the compiler serialises it). */
50
+ ir: string
51
+ jinja: string
52
+ serverTs?: string
53
+ /** Build-time evaluation module (L2): one thunk per `ir.build_eval` slot. */
54
+ evalTs?: string
55
+ clientJs?: string
56
+ diagnostics: Array<NapiDiagnostic>
57
+ }
58
+
59
+ export interface CompiledTree {
60
+ components: Array<CompiledComponent>
61
+ error?: NapiDiagnostic
62
+ }
63
+
64
+ /**
65
+ * Compiles `path` (relative to `root`) and every child it reaches through lowering. `css_modules` (L20): the class
66
+ * map of each `*.module.css`, keyed by its path relative to `root`; `styles.x` folds to the literal class.
67
+ * `packages` (F153c): the JSON resolution map of the package sources the build wrote (`pkg-src.ts`).
68
+ */
69
+ export declare function compileTree(path: string, root: string, runtimeImport: string, serverOnly: Array<string>, slotValues?: string | undefined | null, cssModules?: Record<string, Record<string, string>> | undefined | null, iconSvgs?: string | undefined | null, routeRoots?: Array<string> | undefined | null, pathAliases?: string | undefined | null, packages?: string | undefined | null): CompiledTree
70
+
71
+ /** The bound address as `host:port` (port 0 resolves here). */
72
+ export declare function localAddr(): string
73
+
74
+ /** L16: what the page tells the browser client. `credentials`: `"same-origin"` (default), `"include"` or `"omit"`. */
75
+ export interface NapiActionsClient {
76
+ prefix?: string
77
+ baseUrl?: string
78
+ credentials?: string
79
+ }
80
+
81
+ /** M3-F A3 consumer `{ prefix?, maxBodyBytes?, maxResponseBytes? }` (defaults `/_brust/action`, 1 MiB, 8 MiB). Present = actions on. */
82
+ export interface NapiActionsOptions {
83
+ prefix?: string
84
+ maxBodyBytes?: number
85
+ /** F82: cap on an action response body (default 8388608). */
86
+ maxResponseBytes?: number
87
+ /** `"reject"` (default) or `"allow"` (F87): cross-site mutating action requests. */
88
+ crossSite?: string
89
+ }
90
+
91
+ /**
92
+ * Global CORS (0.1.x `NapiCorsOptions`, brust/src/lib.rs:79-98); napi camelCases the keys:
93
+ * `origins, methods, headers, exposeHeaders, credentials, maxAgeSeconds`. Validated by `start` (`cors: …`).
94
+ */
95
+ export interface NapiCorsOptions {
96
+ origins: Array<string>
97
+ methods?: Array<string>
98
+ headers?: Array<string>
99
+ exposeHeaders?: Array<string>
100
+ credentials?: boolean
101
+ maxAgeSeconds?: number
102
+ }
103
+
104
+ export interface NapiDiagnostic {
105
+ class: string
106
+ rule: string
107
+ message: string
108
+ line: number
109
+ col: number
110
+ remediation: string
111
+ }
112
+
113
+ export interface NapiInvalidateArgs {
114
+ key?: string
115
+ tags?: Array<string>
116
+ path?: string
117
+ method?: string
118
+ }
119
+
120
+ export interface NapiInvalidateResult {
121
+ l1Removed: number
122
+ jobRemoved: number
123
+ }
124
+
125
+ /** The cache adapter (`[cache]`, cache-adapter lane A); built by `brust start` from `manifest.cache`. */
126
+ export interface NapiL2Options {
127
+ adapter: string
128
+ /** The `store.get` budget; absent = 50. */
129
+ getTimeoutMs?: number
130
+ /** A bigger rendered body is not written; absent = 1 048 576. */
131
+ maxBodyBytes?: number
132
+ namespace: string
133
+ buildId: string
134
+ }
135
+
136
+ /**
137
+ * M3-P P6: plan the calling worker's in-flight loader call for `slot` from the `len` bytes it
138
+ * just wrote there. Returns the misses' `JobsRequest` JSON, `""` when every job hit, or `"!"`
139
+ * (declined: answer with the loader response already in the slot). Sync, on the worker's JS
140
+ * thread; never throws (a napi Error return is unreliable under Bun).
141
+ */
142
+ export declare function planJobs(slot: number, len: number): string
143
+
144
+ /**
145
+ * L10: [`plan_jobs`] for a loader response too large for the slot: the worker hands the bytes
146
+ * over in `buf` instead of writing them to the slot. Same answers (`""`, the misses' JSON, or
147
+ * `"!"`); a declined plan leaves the response for the worker to hand over with `putFetchReply`.
148
+ */
149
+ export declare function planJobsBuf(slot: number, buf: Uint8Array): string
150
+
151
+ /**
152
+ * F82: hand Rust the response body of the calling worker's in-flight `fetch` call for `slot`
153
+ * (too large for the SAB slot). `false` = declined (over `max_response_bytes`, not in a `fetch`
154
+ * call, already put, nothing bound). Sync, on the worker's JS thread; never throws.
155
+ */
156
+ export declare function putFetchReply(slot: number, body: Uint8Array): boolean
157
+
158
+ /**
159
+ * Register the calling worker with the current server. `buf` is a view of
160
+ * the worker's SharedArrayBuffer, which must outlive the process's use of
161
+ * this worker (`brust start` allocates it on the main thread and keeps it for
162
+ * the process lifetime: `dispatch.rs` BufPtr); it is split into
163
+ * `slots` disjoint sub-regions of `floor(buf.byteLength / slots)` bytes.
164
+ * `f(kind, requestJson, slot)` writes the response JSON at offset
165
+ * `slot * sub` and resolves with its byte length (> 0, ≤ sub). Returns the
166
+ * worker id.
167
+ */
168
+ export declare function registerWorker(buf: Uint8Array, slots: number, f: (arg0: string, arg1: string, arg2: number) => Promise<number>): number
169
+
170
+ export interface StartOptions {
171
+ host: string
172
+ port: number
173
+ distDir: string
174
+ /** Workers to wait for before accepting; `0` = serve at once. */
175
+ workers: number
176
+ claimTimeoutMs?: number
177
+ /**
178
+ * A claimed worker call that has not settled after this long answers
179
+ * 504 (the slot stays claimed until JS settles). Absent = 30 000.
180
+ */
181
+ callTimeoutMs?: number
182
+ /**
183
+ * L10: the biggest loader/jobs reply that may cross the heap when it does not fit the slot
184
+ * (256 KiB..=64 MiB; boot error otherwise). Absent = 8 MiB.
185
+ */
186
+ maxLoaderResponseBytes?: number
187
+ /**
188
+ * tokio threads of the HTTP runtime (every page renders there); absent =
189
+ * one per core (`Tuning::default`).
190
+ */
191
+ workerThreads?: number
192
+ l1Capacity?: number
193
+ jobCacheCapacity?: number
194
+ /** `X-Powered-By` value; absent = header not stamped. */
195
+ generator?: string
196
+ /** Global CORS policy; absent = CORS off. */
197
+ cors?: NapiCorsOptions
198
+ /** The actions subtree (M3-F); absent = no actions (byte-identical server). */
199
+ actions?: NapiActionsOptions
200
+ /** L16: base URL / credentials / prefix injected for the browser `client()`; absent = from `actions` alone. */
201
+ actionsClient?: NapiActionsClient
202
+ /** The cache adapter; absent = none (byte-identical server). */
203
+ l2?: NapiL2Options
204
+ /** `brust dev` only (L14): a request carrying this token in `x-brust-dev-token` is traced. Absent in production. */
205
+ devToken?: string
206
+ }
207
+
208
+ /**
209
+ * Boot the server (manifest, routes, templates, bind). Throws on any boot
210
+ * error (`manifest: …`, `routes: …`, `render: …`, `cors: …`, `actions: …`,
211
+ * `bind failed on …`).
212
+ */
213
+ export declare function startServer(opts: StartOptions): void
214
+
215
+ /**
216
+ * F82: the heap request body of the calling worker's in-flight `fetch` call for `slot`, once.
217
+ * `None` when there is none, nothing is bound, or the binding is ambiguous. Sync, on the
218
+ * worker's JS thread; never throws.
219
+ */
220
+ export declare function takeFetchBody(slot: number): Uint8Array | null
221
+
222
+ /**
223
+ * Resolves once `workers` workers registered with the current server and the server has printed its
224
+ * `[brust] listening on` line (so a caller's `ready` always follows it, F128); rejects after `timeout_ms` (the caller decides the exit policy).
225
+ */
226
+ export declare function untilReady(timeoutMs: number): Promise<void>