brustjs 0.1.0-alpha โ†’ 0.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <div align="center">
2
2
 
3
- # ๐Ÿ’ฅ Brust
3
+ # Brust
4
4
 
5
5
  ### **B**un + **Rust** โ€” an SSR framework that bursts.
6
6
 
@@ -23,7 +23,23 @@ per-worker `SharedArrayBuffer`. `tokio-uring` (io_uring) on Linux, `tokio` on ma
23
23
 
24
24
  ## Quick start
25
25
 
26
- **Add to a project** (prebuilt native binary per platform โ€” no Rust toolchain):
26
+ **Scaffold a new project** (prebuilt native binary per platform โ€” no Rust toolchain):
27
+
28
+ ```bash
29
+ bun create brustjs my-app
30
+ cd my-app
31
+ bun install
32
+ bun run dev # โ†’ http://127.0.0.1:3000
33
+ ```
34
+
35
+ **Or, with the CLI on your PATH:**
36
+
37
+ ```bash
38
+ bun add --global brustjs@alpha
39
+ brustjs new my-app
40
+ ```
41
+
42
+ **Or add to an existing project:**
27
43
 
28
44
  ```bash
29
45
  bun add brustjs@alpha
@@ -97,8 +113,9 @@ bench/ ยท docs/ ยท architecture.md
97
113
  ## Status
98
114
 
99
115
  Alpha, solo-developed. Linux is tier-1 (io_uring; glibc + musl, 6 prebuilt platform
100
- binaries). Known partials: `brustjs new` scaffold install, `brustjs dev` TS reload,
101
- islands + `.module.css`. Deployment note: the io_uring server needs `io_uring_*`
116
+ binaries). Known partials: `brustjs dev` TS reload, islands + `.module.css`. Tailwind
117
+ is opt-in โ€” the scaffold adds it as a project dependency; `@import "tailwindcss"`
118
+ resolves from your own `node_modules`. Deployment note: the io_uring server needs `io_uring_*`
102
119
  syscalls permitted โ€” a default-seccomp container (Docker/k8s) must allow them or run
103
120
  `--security-opt seccomp=unconfined`. Roadmap and limitations in
104
121
  [`architecture.md`](./architecture.md).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brustjs",
3
- "version": "0.1.0-alpha",
3
+ "version": "0.1.0-alpha.1",
4
4
  "description": "Bun + Rust SSR framework โ€” React on the server, Rust everywhere else (napi cdylib + per-worker SharedArrayBuffer).",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -39,12 +39,12 @@
39
39
  "smol-toml": "^1.6.1"
40
40
  },
41
41
  "optionalDependencies": {
42
- "brustjs-darwin-x64": "0.1.0-alpha",
43
- "brustjs-darwin-arm64": "0.1.0-alpha",
44
- "brustjs-linux-x64-gnu": "0.1.0-alpha",
45
- "brustjs-linux-arm64-gnu": "0.1.0-alpha",
46
- "brustjs-linux-x64-musl": "0.1.0-alpha",
47
- "brustjs-linux-arm64-musl": "0.1.0-alpha"
42
+ "brustjs-darwin-x64": "0.1.0-alpha.1",
43
+ "brustjs-darwin-arm64": "0.1.0-alpha.1",
44
+ "brustjs-linux-x64-gnu": "0.1.0-alpha.1",
45
+ "brustjs-linux-arm64-gnu": "0.1.0-alpha.1",
46
+ "brustjs-linux-x64-musl": "0.1.0-alpha.1",
47
+ "brustjs-linux-arm64-musl": "0.1.0-alpha.1"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "react": "^19.2.6",
@@ -64,7 +64,8 @@
64
64
  "types": "./runtime/index.d.ts",
65
65
  "exports": {
66
66
  ".": "./runtime/index.ts",
67
- "./routes": "./runtime/routes.ts"
67
+ "./routes": "./runtime/routes.ts",
68
+ "./create": "./runtime/create.ts"
68
69
  },
69
70
  "files": [
70
71
  "runtime",
@@ -1,5 +1,6 @@
1
1
  import { existsSync } from 'node:fs'
2
2
  import { copyFile, mkdir, readdir, rm } from 'node:fs/promises'
3
+ import { createRequire } from 'node:module'
3
4
  import path, { isAbsolute, resolve } from 'node:path'
4
5
  import {
5
6
  actionsPrebuiltPlugin,
@@ -12,6 +13,52 @@ import { nativeShimPlugin } from './native-shim-plugin.ts'
12
13
  * runtime/cli/build.ts so two dirname() steps get us there. */
13
14
  const REPO_ROOT = path.resolve(import.meta.dir, '..', '..')
14
15
 
16
+ /** Detect musl (mirrors the napi loader + native-shim) so we pick the right
17
+ * platform package on Linux. */
18
+ function linuxIsMusl(): boolean {
19
+ try {
20
+ const report = process.report as { excludeNetwork?: boolean; getReport?: () => unknown }
21
+ if (report && typeof report.getReport === 'function') {
22
+ report.excludeNetwork = true
23
+ const r = report.getReport() as { header?: { glibcVersionRuntime?: string } }
24
+ return !r?.header?.glibcVersionRuntime
25
+ }
26
+ } catch {}
27
+ return false
28
+ }
29
+
30
+ /** The npm package that carries this platform's prebuilt binary. */
31
+ function platformPackageName(): string {
32
+ const { platform, arch } = process
33
+ if (platform === 'linux') return `brustjs-linux-${arch}-${linuxIsMusl() ? 'musl' : 'gnu'}`
34
+ return `brustjs-${platform}-${arch}`
35
+ }
36
+
37
+ /** Absolute paths of every `brust.*.node` to copy into the dist. Source-tree /
38
+ * CI-matrix builds emit them into runtime/; an installed project carries them
39
+ * in the brustjs-<platform> optionalDependency package. We gather from both so
40
+ * a dist Just Works in either layout. */
41
+ async function collectNativeBinaries(): Promise<string[]> {
42
+ const out: string[] = []
43
+ const isNode = (f: string) => /^brust\..+\.node$/.test(f)
44
+
45
+ // (a) runtime/ โ€” local napi build output (single- or multi-platform CI).
46
+ const runtimeDir = path.join(REPO_ROOT, 'runtime')
47
+ try {
48
+ for (const f of await readdir(runtimeDir)) if (isNode(f)) out.push(path.join(runtimeDir, f))
49
+ } catch {}
50
+
51
+ // (b) installed platform package โ€” resolved from the brustjs install root so
52
+ // it survives node_modules hoisting.
53
+ try {
54
+ const req = createRequire(path.join(REPO_ROOT, '__brust_resolve__.js'))
55
+ const pkgDir = path.dirname(req.resolve(`${platformPackageName()}/package.json`))
56
+ for (const f of await readdir(pkgDir)) if (isNode(f)) out.push(path.join(pkgDir, f))
57
+ } catch {}
58
+
59
+ return out
60
+ }
61
+
15
62
  interface ParsedArgs {
16
63
  entry: string // absolute path to the entry file
17
64
  outDir: string // absolute path to the output dir
@@ -230,22 +277,27 @@ export async function runBuild(args: string[]): Promise<void> {
230
277
  const nativeDir = path.join(outDir, 'native')
231
278
  await mkdir(nativeDir, { recursive: true })
232
279
 
233
- // napi-rs emits `runtime/brust.<platform>-<arch>[-libc].node` (binaryName
234
- // "brust", from the root napi config). We copy every `brust.*.node` we find
235
- // in runtime/ so a multi-platform pre-build (CI matrix) Just Works without
236
- // further wiring; in single-platform local builds this is just one file.
237
- const runtimeDir = path.join(REPO_ROOT, 'runtime')
238
- const nodeFiles = (await readdir(runtimeDir)).filter((f) => /^brust\..+\.node$/.test(f))
239
- if (nodeFiles.length === 0) {
280
+ // napi-rs names the binary `brust.<platform>-<arch>[-libc].node` (binaryName
281
+ // "brust", from the root napi config). Source/CI builds leave it in runtime/;
282
+ // an installed project carries it in the brustjs-<platform> package. Copy
283
+ // every match from both โ€” multi-platform (CI matrix) and single-platform
284
+ // (local/installed) layouts both Just Work.
285
+ const nativeBinaries = await collectNativeBinaries()
286
+ if (nativeBinaries.length === 0) {
240
287
  console.error(
241
- `brust build: no native binary found in ${runtimeDir}. ` +
242
- `Run \`bun --filter runtime run build\` (or :debug) first.`,
288
+ `brust build: no native binary found. Looked in ${path.join(REPO_ROOT, 'runtime')} and ` +
289
+ `the ${platformPackageName()} package. From source run \`bun --filter runtime run build\` ` +
290
+ `(or :debug) first; in an installed project ensure the platform package is present.`,
243
291
  )
244
292
  process.exit(1)
245
293
  }
246
- for (const f of nodeFiles) {
247
- await copyFile(path.join(runtimeDir, f), path.join(nativeDir, f))
248
- console.log(`[brust build] native: ${f}`)
294
+ const seen = new Set<string>()
295
+ for (const src of nativeBinaries) {
296
+ const name = path.basename(src)
297
+ if (seen.has(name)) continue
298
+ seen.add(name)
299
+ await copyFile(src, path.join(nativeDir, name))
300
+ console.log(`[brust build] native: ${name}`)
249
301
  }
250
302
 
251
303
  console.log(`[brust build] done.`)
@@ -1,4 +1,4 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
1
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
2
2
  import { dirname, resolve } from 'node:path'
3
3
  import { ISLANDS_IMPORTMAP_AND_BOOTSTRAP } from '../islands/importmap.ts'
4
4
 
@@ -21,7 +21,8 @@ export interface NativeRouteEmitOpts {
21
21
  flatRoutes: { nativeTemplate?: string }[]
22
22
  /** `.brust/jinja` absolute output dir. Created if missing. */
23
23
  outDir: string
24
- /** Repo root โ€” used to resolve the `jsx-rustc` binary. */
24
+ /** Repo root. Retained for call-site compatibility; native compilation now
25
+ * goes through the napi addon's `compileJsx`, not a target/ binary. */
25
26
  repoRoot: string
26
27
  }
27
28
 
@@ -45,20 +46,23 @@ export async function emitNativeTemplates(opts: NativeRouteEmitOpts): Promise<vo
45
46
 
46
47
  const nativeRoutes = opts.flatRoutes.filter((r) => r.nativeTemplate)
47
48
 
48
- // Resolve jsx-rustc binary: prefer release, fall back to debug.
49
- const jsxRustcRelease = resolve(opts.repoRoot, 'target/release/jsx-rustc')
50
- const jsxRustcDebug = resolve(opts.repoRoot, 'target/debug/jsx-rustc')
51
- const jsxRustc = existsSync(jsxRustcRelease)
52
- ? jsxRustcRelease
53
- : existsSync(jsxRustcDebug)
54
- ? jsxRustcDebug
55
- : null
56
-
57
- if (!jsxRustc && nativeRoutes.length > 0) {
58
- throw new Error(
59
- 'jsx-rustc binary not found in target/{release,debug}/; ' +
60
- 'run `cargo build -p jsx-rust-compiler --bin jsx-rustc`',
61
- )
49
+ // Compile through the napi addon's `compileJsx` rather than spawning the
50
+ // `jsx-rustc` binary. The binary only exists in the source tree's target/
51
+ // dir, so spawning it broke `native: true` routes in a published npm install;
52
+ // the addon (`.node`) ships with every platform package, so this path works
53
+ // for source builds and installed projects alike.
54
+ let compileJsx:
55
+ | ((source: string, path: string) => { template: string; islandsJson: string })
56
+ | null = null
57
+ if (nativeRoutes.length > 0) {
58
+ const native = await import('../index.js')
59
+ compileJsx = (native as { compileJsx?: typeof compileJsx }).compileJsx ?? null
60
+ if (typeof compileJsx !== 'function') {
61
+ throw new Error(
62
+ 'brust: the native addon does not expose compileJsx โ€” rebuild it with ' +
63
+ '`cd runtime && bun run build` (or update brustjs to a build that ships it).',
64
+ )
65
+ }
62
66
  }
63
67
 
64
68
  const importMap =
@@ -75,26 +79,27 @@ export async function emitNativeTemplates(opts: NativeRouteEmitOpts): Promise<vo
75
79
  continue
76
80
  }
77
81
  const outPath = resolve(opts.outDir, `${name}.jinja`)
78
- const result = Bun.spawnSync({
79
- cmd: [jsxRustc!, sourcePath, '-o', outPath],
80
- stdout: 'pipe',
81
- stderr: 'pipe',
82
- })
83
- if (result.exitCode !== 0) {
84
- const stderr = result.stderr ? new TextDecoder().decode(result.stderr) : ''
85
- const stdout = result.stdout ? new TextDecoder().decode(result.stdout) : ''
86
- throw new Error(`jsx-rustc failed for ${sourcePath}:\n${stdout}\n${stderr}`)
82
+ let compiled: { template: string; islandsJson: string }
83
+ try {
84
+ compiled = compileJsx!(readFileSync(sourcePath, 'utf8'), sourcePath)
85
+ } catch (e) {
86
+ throw new Error(`native route "${name}" failed to compile (${sourcePath}):\n${String(e)}`)
87
87
  }
88
+ writeFileSync(outPath, compiled.template)
88
89
  built.push(name)
89
90
 
90
- // Islands post-processing. jsx-rustc writes `<Name>.islands.json` ONLY when
91
- // the route uses <Island>; absent file โ‡’ no islands โ‡’ leave the .jinja
92
- // byte-identical (no-island regression).
91
+ // Islands post-processing. The compiler reports an island manifest ONLY
92
+ // when the route uses <Island>; `"[]"` โ‡’ no islands โ‡’ leave the .jinja
93
+ // byte-identical (no-island regression). Remove any stale sibling so a
94
+ // route that dropped its islands doesn't reconcile against an old manifest.
93
95
  const islandsJsonPath = resolve(opts.outDir, `${name}.islands.json`)
94
- if (existsSync(islandsJsonPath)) {
96
+ if (compiled.islandsJson && compiled.islandsJson !== '[]') {
97
+ writeFileSync(islandsJsonPath, compiled.islandsJson)
95
98
  // Island source paths resolve from the PAGE file's own imports.
96
99
  const pageImports = scanImports(sourcePath)
97
100
  reconcileIslandManifest(outPath, islandsJsonPath, pageImports, name)
101
+ } else if (existsSync(islandsJsonPath)) {
102
+ rmSync(islandsJsonPath, { force: true })
98
103
  }
99
104
  }
100
105
 
@@ -1,6 +1,6 @@
1
1
  # __PROJECT_NAME__
2
2
 
3
- Scaffolded with `brust new`.
3
+ Scaffolded with `brustjs new`.
4
4
 
5
5
  ## Develop
6
6
 
@@ -1,13 +1,15 @@
1
+ 'use client'
1
2
  import { useState } from 'react'
2
3
 
3
4
  export default function Counter({ start = 0, label = 'count' }: { start?: number; label?: string }) {
4
- const [n, setN] = useState(start)
5
+ const [count, setCount] = useState(start)
5
6
  return (
6
7
  <button
7
- onClick={() => setN(n + 1)}
8
- className="px-3 py-1.5 bg-brand text-white rounded text-sm hover:opacity-90 transition-opacity"
8
+ type="button"
9
+ onClick={() => setCount((c) => c + 1)}
10
+ className="rounded border px-3 py-1 hover:bg-gray-50"
9
11
  >
10
- {label}: {n}
12
+ {label}: {count}
11
13
  </button>
12
14
  )
13
15
  }
@@ -10,7 +10,8 @@
10
10
  "dependencies": {
11
11
  "brustjs": __BRUST_DEP__,
12
12
  "react": "^19.2.6",
13
- "react-dom": "^19.2.6"
13
+ "react-dom": "^19.2.6",
14
+ "tailwindcss": "^4.3.0"
14
15
  },
15
16
  "devDependencies": {
16
17
  "@types/bun": "latest",
@@ -1,16 +1,54 @@
1
1
  import { Island } from 'brustjs'
2
- import Layout from '../components/Layout'
3
2
  import Counter from '../components/Counter'
4
3
 
5
- export default function Home() {
4
+ // `native: true` page โ€” compiled to a jinja template and rendered in Rust
5
+ // (minijinja), no React on the server. Native pages use host elements only (no
6
+ // custom components like Layout), so the document shell is written inline here;
7
+ // the Tailwind stylesheet is linked directly. Each island's `props` is a path
8
+ // into the route loader's return value.
9
+ export default function Home({
10
+ clientProps,
11
+ serverProps,
12
+ }: {
13
+ clientProps: { start: number; label: string }
14
+ serverProps: { start: number; label: string }
15
+ }) {
6
16
  return (
7
- <Layout title="__PROJECT_NAME__">
8
- <h1 className="text-3xl font-bold mb-4">Welcome to brust</h1>
9
- <p className="mb-4 text-gray-700">
10
- Edit <code className="bg-gray-100 px-1 rounded">pages/Home.tsx</code> and save โ€”
11
- <code className="bg-gray-100 px-1 rounded ml-1">brustjs dev</code> will reload.
12
- </p>
13
- <Island component={Counter} props={{ start: 0, label: 'clicks' }} hydrate="load" />
14
- </Layout>
17
+ <html lang="en">
18
+ <head>
19
+ <meta charSet="utf-8" />
20
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
21
+ <title>__PROJECT_NAME__</title>
22
+ <link rel="stylesheet" href="/_brust/css/app.css" />
23
+ </head>
24
+ <body className="bg-white text-gray-900 font-sans">
25
+ <main className="max-w-3xl mx-auto px-5 py-8">
26
+ <h1 className="text-3xl font-bold mb-4">Welcome to Brust</h1>
27
+ <p className="mb-6 text-gray-700">
28
+ This page is a <strong>native route</strong> โ€” its shell is rendered in Rust
29
+ (minijinja), no React on the server. The counters below are{' '}
30
+ <strong>islands</strong>: only their subtrees boot React in the browser. Edit{' '}
31
+ <code className="bg-gray-100 px-1 rounded">pages/Home.tsx</code> and save โ€”{' '}
32
+ <code className="bg-gray-100 px-1 rounded">brustjs dev</code> reloads.
33
+ </p>
34
+
35
+ <section className="mb-6">
36
+ <h2 className="text-xl font-semibold mb-1">Client island</h2>
37
+ <p className="mb-2 text-sm text-gray-600">
38
+ No server markup; mounts and hydrates in the browser.
39
+ </p>
40
+ <Island component={Counter} props={clientProps} hydrate="load" />
41
+ </section>
42
+
43
+ <section>
44
+ <h2 className="text-xl font-semibold mb-1">Server island (SSR + hydrate)</h2>
45
+ <p className="mb-2 text-sm text-gray-600">
46
+ Rendered on the server during the loader crossing, then hydrated โ€” no empty flash.
47
+ </p>
48
+ <Island component={Counter} props={serverProps} ssr hydrate="load" />
49
+ </section>
50
+ </main>
51
+ </body>
52
+ </html>
15
53
  )
16
54
  }
@@ -1,6 +1,17 @@
1
- import { defineRoutes } from 'brust/routes'
1
+ import { defineRoutes } from 'brustjs/routes'
2
2
  import Home from './pages/Home'
3
3
 
4
4
  export const routes = defineRoutes([
5
- { path: '/', Component: Home },
6
- ])
5
+ // `native: true` โ€” Home is compiled to a jinja template and rendered in Rust
6
+ // (no React on the server). The loader's return value is the template
7
+ // context; each island's `props` is a path into it.
8
+ {
9
+ path: '/',
10
+ Component: Home,
11
+ native: true,
12
+ loader: async () => ({
13
+ clientProps: { start: 0, label: 'client clicks' },
14
+ serverProps: { start: 100, label: 'server clicks' },
15
+ }),
16
+ },
17
+ ] as const)
@@ -0,0 +1,4 @@
1
+ // Public entry for the `create-brustjs` package (`bun create brustjs <dir>`).
2
+ // Kept as a thin re-export so the create package never reaches into brust's
3
+ // CLI internals and the templates stay owned by this package alone.
4
+ export { runNew } from './cli/new.ts'
@@ -1,16 +1,25 @@
1
1
  import { mkdir, readFile, writeFile } from 'node:fs/promises'
2
2
  import path from 'node:path'
3
+ import { createRequire } from 'node:module'
3
4
  import { compile } from '@tailwindcss/node'
4
5
  import { Scanner } from '@tailwindcss/oxide'
5
6
 
6
- /** The directory that contains this file โ€” runtime/css/. Two dirname() steps
7
- * reach the brust repo root where node_modules/tailwindcss is installed. */
8
- const BRUST_ROOT = path.resolve(import.meta.dir, '..', '..')
9
-
10
- /** Absolute path to tailwindcss/index.css inside brust's own node_modules.
11
- * Used by the customCssResolver so that `@import "tailwindcss"` always
12
- * resolves via brust's bundled copy regardless of where the entry CSS lives. */
13
- const TAILWINDCSS_INDEX = path.join(BRUST_ROOT, 'node_modules', 'tailwindcss', 'index.css')
7
+ /** Resolve `tailwindcss/index.css` starting from `fromDir`. Uses package.json
8
+ * resolution (always present, unlike index.css in the exports map) then joins
9
+ * index.css. Returns undefined if tailwindcss is not installed in that context.
10
+ *
11
+ * Tailwind is an opt-in dependency of the *consumer* project (the scaffold
12
+ * declares it), NOT a hard dependency of brust core โ€” so we resolve it from
13
+ * the entry CSS's project first, falling back to brust's own context (which
14
+ * covers the dev repo and unit tests where tailwindcss is hoisted). */
15
+ function resolveTailwindIndex(fromDir: string): string | undefined {
16
+ try {
17
+ const req = createRequire(path.join(fromDir, '__brust_resolve__.js'))
18
+ return path.join(path.dirname(req.resolve('tailwindcss/package.json')), 'index.css')
19
+ } catch {
20
+ return undefined
21
+ }
22
+ }
14
23
 
15
24
  export interface BuildCssOptions {
16
25
  /** Absolute path to the entry CSS file (typically <scanRoot>/app.css). */
@@ -33,12 +42,13 @@ export async function buildCss(opts: BuildCssOptions): Promise<CssBuildResult> {
33
42
  // the same folder the CSS lives in).
34
43
  base: path.dirname(opts.entry),
35
44
  onDependency: () => {}, // unused โ€” no watch
36
- // Redirect `@import "tailwindcss"` to brust's own bundled copy so
37
- // resolution succeeds even when tailwindcss is not installed in the
38
- // entry directory (e.g. a tmp dir in tests).
45
+ // Resolve `@import "tailwindcss"` to the consumer project's own copy
46
+ // (resolved from the entry CSS's directory), falling back to brust's own
47
+ // context โ€” the latter covers the dev repo and unit tests where the entry
48
+ // lives in a tmp dir without its own tailwindcss install.
39
49
  customCssResolver: async (id: string) => {
40
- if (id === 'tailwindcss') return TAILWINDCSS_INDEX
41
- return undefined
50
+ if (id !== 'tailwindcss') return undefined
51
+ return resolveTailwindIndex(path.dirname(opts.entry)) ?? resolveTailwindIndex(import.meta.dir)
42
52
  },
43
53
  })
44
54
 
@@ -1,14 +1,12 @@
1
1
  const ESC = '\x1b['
2
- const HIDE_CURSOR = ESC + '?25l'
3
- const SHOW_CURSOR = ESC + '?25h'
4
- const CLEAR_SCREEN = ESC + '2J' + ESC + 'H'
5
2
  const RESET = ESC + '0m'
6
3
  const DIM = ESC + '2m'
7
4
  const BRAND = ESC + '38;2;138;51;36m'
8
5
  const GREEN = ESC + '32m'
9
6
  const RED = ESC + '31m'
10
- const YELLOW = ESC + '33m'
7
+ const BAR = BRAND + 'โ”ƒ' + RESET
11
8
 
9
+ const VERSION = '0.1.0'
12
10
  const DEFAULT_CAPACITY = 10
13
11
 
14
12
  interface Status {
@@ -23,11 +21,14 @@ export interface TuiOptions {
23
21
  capacity?: number
24
22
  }
25
23
 
24
+ /** Astro-style dev output: a clean one-time banner on ready, then event lines
25
+ * that scroll naturally below it. Deliberately NOT a full-screen TUI โ€” it never
26
+ * hides the cursor or clears the screen, so the terminal is left exactly as it
27
+ * was found when the dev server exits (no more vanished cursor on Ctrl-C). */
26
28
  export class Tui {
27
29
  private events: string[] = []
28
- private status: Status | null = null
29
30
  private capacity: number
30
- private state: 'idle' | 'building' | 'failed' = 'idle'
31
+ private bannerShown = false
31
32
 
32
33
  constructor(private opts: TuiOptions) {
33
34
  this.capacity = opts.capacity ?? DEFAULT_CAPACITY
@@ -37,73 +38,66 @@ export class Tui {
37
38
  return [...this.events]
38
39
  }
39
40
 
41
+ /** Print the ready banner. Idempotent โ€” only the first call emits it. */
40
42
  updateStatus(s: Status): void {
41
- this.status = s
42
- this.render()
43
- }
43
+ if (this.bannerShown) return
44
+ this.bannerShown = true
44
45
 
45
- setState(s: 'idle' | 'building' | 'failed'): void {
46
- this.state = s
47
- this.render()
46
+ if (!this.opts.isTty) {
47
+ this.opts.write(
48
+ `brust ${VERSION} ยท dev โ€” http://127.0.0.1:${s.port}/ (workers: ${s.workers})\n`,
49
+ )
50
+ return
51
+ }
52
+
53
+ const lines = [
54
+ '',
55
+ ` ${BRAND}brust${RESET} ${DIM}v${VERSION}${RESET}`,
56
+ '',
57
+ ` ${BAR} ${DIM}Local${RESET} http://127.0.0.1:${s.port}/`,
58
+ ` ${BAR} ${DIM}Workers${RESET} ${s.workers}`,
59
+ ` ${BAR} ${DIM}Watching${RESET} ${s.watching.join(', ')}`,
60
+ '',
61
+ '',
62
+ ]
63
+ this.opts.write(lines.join('\n'))
48
64
  }
49
65
 
66
+ /** Append one event. Scrolls below the banner (TTY) or prints a plain line
67
+ * (non-TTY / CI). The in-memory ring buffer backs `eventsForTests`. */
50
68
  appendEvent(line: string): void {
51
69
  this.events.push(line)
52
70
  if (this.events.length > this.capacity) this.events.shift()
53
- this.render()
54
- }
55
71
 
56
- stop(): void {
57
- if (this.opts.isTty) this.opts.write(SHOW_CURSOR + RESET)
58
- }
59
-
60
- private render(): void {
61
72
  if (!this.opts.isTty) {
62
- const latest = this.events[this.events.length - 1]
63
- if (latest) this.opts.write(latest + '\n')
73
+ this.opts.write(line + '\n')
64
74
  return
65
75
  }
66
- let out = HIDE_CURSOR + CLEAR_SCREEN
67
- out += this.renderHeader()
68
- out += this.renderEvents()
69
- out += this.renderStatusBar()
70
- this.opts.write(out)
76
+ this.opts.write(this.format(line) + '\n')
71
77
  }
72
78
 
73
- private renderHeader(): string {
74
- if (!this.status) return BRAND + 'brust 0.1.0 ยท dev mode' + RESET + '\n\n'
75
- return (
76
- BRAND +
77
- 'brust 0.1.0 ยท dev mode' +
78
- RESET +
79
- '\n' +
80
- DIM +
81
- 'port: ' +
82
- RESET +
83
- this.status.port +
84
- '\n' +
85
- DIM +
86
- 'workers: ' +
87
- RESET +
88
- this.status.workers +
89
- '\n' +
90
- DIM +
91
- 'watching: ' +
92
- RESET +
93
- this.status.watching.join(', ') +
94
- '\n\n'
95
- )
79
+ /** Called on shutdown. There is nothing to restore (we never altered the
80
+ * terminal mode); the reset is belt-and-braces so a half-written colored
81
+ * line can't bleed into the user's prompt. */
82
+ stop(): void {
83
+ if (this.opts.isTty) this.opts.write(RESET)
96
84
  }
97
85
 
98
- private renderEvents(): string {
99
- let out = ''
100
- for (const ev of this.events) out += ev + '\n'
101
- return out + '\n'
86
+ /** Colorize an event line in TTY mode: a dim timestamp prefix, with ok/error
87
+ * markers tinted. The coordinator emits ` โ†’ ok (Nms)` and ` โœ— <msg>`. */
88
+ private format(line: string): string {
89
+ const ts = `${DIM}${timestamp()}${RESET} `
90
+ const trimmed = line.trimStart()
91
+ if (trimmed.startsWith('โœ—')) return ts + RED + line.trim() + RESET
92
+ if (trimmed.startsWith('โ†’ ok') || trimmed.startsWith('ok')) {
93
+ return ts + GREEN + line.trim() + RESET
94
+ }
95
+ return ts + line
102
96
  }
97
+ }
103
98
 
104
- private renderStatusBar(): string {
105
- if (this.state === 'building') return YELLOW + 'โ—‰ Buildingโ€ฆ' + RESET + '\n'
106
- if (this.state === 'failed') return RED + 'โœ— Build failed' + RESET + '\n'
107
- return GREEN + 'โ—‰ Serving' + RESET + '\n'
108
- }
99
+ function timestamp(): string {
100
+ const d = new Date()
101
+ const p = (n: number) => String(n).padStart(2, '0')
102
+ return `${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`
109
103
  }
@@ -2,6 +2,14 @@
2
2
  /* eslint-disable */
3
3
  export declare function beginServe(opts: ServeOptions): NapiResult<undefined>
4
4
 
5
+ /**
6
+ * Compile a single native-route source to its jinja template + island
7
+ * manifest. `path` is used only in error messages. Mirrors `jsx-rustc`'s
8
+ * `compile_full` + `islands_to_json` so the TS emit step can write the same
9
+ * `<Name>.jinja` and `<Name>.islands.json` files.
10
+ */
11
+ export declare function compileJsx(source: string, path: string): NapiCompiledJsx
12
+
5
13
  export declare function configureCache(maxEntries: number): NapiResult<undefined>
6
14
 
7
15
  export declare function configureCssDir(path: string): NapiResult<undefined>
@@ -10,6 +18,17 @@ export declare function configureIslandsDir(path: string): NapiResult<undefined>
10
18
 
11
19
  export declare function isWorker(): boolean
12
20
 
21
+ /** Result of compiling one `pages/<Name>.tsx` source. */
22
+ export interface NapiCompiledJsx {
23
+ /** The emitted jinja template. */
24
+ template: string
25
+ /**
26
+ * Island manifest as JSON (`"[]"` when the route uses no `<Island>`). The
27
+ * camelCase keys match `RawIslandEntry` in native-routes-emit.ts.
28
+ */
29
+ islandsJson: string
30
+ }
31
+
13
32
  /**
14
33
  * Sub-project J โ€” boot-time listing of registered minijinja templates.
15
34
  * JS dispatcher uses this to validate every `native: true` route's
package/runtime/index.js CHANGED
@@ -77,8 +77,8 @@ function requireNative() {
77
77
  try {
78
78
  const binding = require('brustjs-android-arm64')
79
79
  const bindingPackageVersion = require('brustjs-android-arm64/package.json').version
80
- if (bindingPackageVersion !== '0.1.0-alpha' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
81
- throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
80
+ if (bindingPackageVersion !== '0.1.0-alpha.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
81
+ throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
82
82
  }
83
83
  return binding
84
84
  } catch (e) {
@@ -93,8 +93,8 @@ function requireNative() {
93
93
  try {
94
94
  const binding = require('brustjs-android-arm-eabi')
95
95
  const bindingPackageVersion = require('brustjs-android-arm-eabi/package.json').version
96
- if (bindingPackageVersion !== '0.1.0-alpha' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
97
- throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
96
+ if (bindingPackageVersion !== '0.1.0-alpha.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
97
+ throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
98
98
  }
99
99
  return binding
100
100
  } catch (e) {
@@ -114,8 +114,8 @@ function requireNative() {
114
114
  try {
115
115
  const binding = require('brustjs-win32-x64-gnu')
116
116
  const bindingPackageVersion = require('brustjs-win32-x64-gnu/package.json').version
117
- if (bindingPackageVersion !== '0.1.0-alpha' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
118
- throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
117
+ if (bindingPackageVersion !== '0.1.0-alpha.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
118
+ throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
119
119
  }
120
120
  return binding
121
121
  } catch (e) {
@@ -130,8 +130,8 @@ function requireNative() {
130
130
  try {
131
131
  const binding = require('brustjs-win32-x64-msvc')
132
132
  const bindingPackageVersion = require('brustjs-win32-x64-msvc/package.json').version
133
- if (bindingPackageVersion !== '0.1.0-alpha' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
134
- throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
133
+ if (bindingPackageVersion !== '0.1.0-alpha.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
134
+ throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
135
135
  }
136
136
  return binding
137
137
  } catch (e) {
@@ -147,8 +147,8 @@ function requireNative() {
147
147
  try {
148
148
  const binding = require('brustjs-win32-ia32-msvc')
149
149
  const bindingPackageVersion = require('brustjs-win32-ia32-msvc/package.json').version
150
- if (bindingPackageVersion !== '0.1.0-alpha' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
151
- throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
150
+ if (bindingPackageVersion !== '0.1.0-alpha.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
151
+ throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
152
152
  }
153
153
  return binding
154
154
  } catch (e) {
@@ -163,8 +163,8 @@ function requireNative() {
163
163
  try {
164
164
  const binding = require('brustjs-win32-arm64-msvc')
165
165
  const bindingPackageVersion = require('brustjs-win32-arm64-msvc/package.json').version
166
- if (bindingPackageVersion !== '0.1.0-alpha' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
167
- throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
166
+ if (bindingPackageVersion !== '0.1.0-alpha.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
167
+ throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
168
168
  }
169
169
  return binding
170
170
  } catch (e) {
@@ -182,8 +182,8 @@ function requireNative() {
182
182
  try {
183
183
  const binding = require('brustjs-darwin-universal')
184
184
  const bindingPackageVersion = require('brustjs-darwin-universal/package.json').version
185
- if (bindingPackageVersion !== '0.1.0-alpha' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
186
- throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
185
+ if (bindingPackageVersion !== '0.1.0-alpha.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
186
+ throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
187
187
  }
188
188
  return binding
189
189
  } catch (e) {
@@ -198,8 +198,8 @@ function requireNative() {
198
198
  try {
199
199
  const binding = require('brustjs-darwin-x64')
200
200
  const bindingPackageVersion = require('brustjs-darwin-x64/package.json').version
201
- if (bindingPackageVersion !== '0.1.0-alpha' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
202
- throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
201
+ if (bindingPackageVersion !== '0.1.0-alpha.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
202
+ throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
203
203
  }
204
204
  return binding
205
205
  } catch (e) {
@@ -214,8 +214,8 @@ function requireNative() {
214
214
  try {
215
215
  const binding = require('brustjs-darwin-arm64')
216
216
  const bindingPackageVersion = require('brustjs-darwin-arm64/package.json').version
217
- if (bindingPackageVersion !== '0.1.0-alpha' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
218
- throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
217
+ if (bindingPackageVersion !== '0.1.0-alpha.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
218
+ throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
219
219
  }
220
220
  return binding
221
221
  } catch (e) {
@@ -234,8 +234,8 @@ function requireNative() {
234
234
  try {
235
235
  const binding = require('brustjs-freebsd-x64')
236
236
  const bindingPackageVersion = require('brustjs-freebsd-x64/package.json').version
237
- if (bindingPackageVersion !== '0.1.0-alpha' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
238
- throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
237
+ if (bindingPackageVersion !== '0.1.0-alpha.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
238
+ throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
239
239
  }
240
240
  return binding
241
241
  } catch (e) {
@@ -250,8 +250,8 @@ function requireNative() {
250
250
  try {
251
251
  const binding = require('brustjs-freebsd-arm64')
252
252
  const bindingPackageVersion = require('brustjs-freebsd-arm64/package.json').version
253
- if (bindingPackageVersion !== '0.1.0-alpha' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
254
- throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
253
+ if (bindingPackageVersion !== '0.1.0-alpha.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
254
+ throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
255
255
  }
256
256
  return binding
257
257
  } catch (e) {
@@ -271,8 +271,8 @@ function requireNative() {
271
271
  try {
272
272
  const binding = require('brustjs-linux-x64-musl')
273
273
  const bindingPackageVersion = require('brustjs-linux-x64-musl/package.json').version
274
- if (bindingPackageVersion !== '0.1.0-alpha' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
275
- throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
274
+ if (bindingPackageVersion !== '0.1.0-alpha.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
275
+ throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
276
276
  }
277
277
  return binding
278
278
  } catch (e) {
@@ -287,8 +287,8 @@ function requireNative() {
287
287
  try {
288
288
  const binding = require('brustjs-linux-x64-gnu')
289
289
  const bindingPackageVersion = require('brustjs-linux-x64-gnu/package.json').version
290
- if (bindingPackageVersion !== '0.1.0-alpha' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
291
- throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
290
+ if (bindingPackageVersion !== '0.1.0-alpha.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
291
+ throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
292
292
  }
293
293
  return binding
294
294
  } catch (e) {
@@ -305,8 +305,8 @@ function requireNative() {
305
305
  try {
306
306
  const binding = require('brustjs-linux-arm64-musl')
307
307
  const bindingPackageVersion = require('brustjs-linux-arm64-musl/package.json').version
308
- if (bindingPackageVersion !== '0.1.0-alpha' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
309
- throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
308
+ if (bindingPackageVersion !== '0.1.0-alpha.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
309
+ throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
310
310
  }
311
311
  return binding
312
312
  } catch (e) {
@@ -321,8 +321,8 @@ function requireNative() {
321
321
  try {
322
322
  const binding = require('brustjs-linux-arm64-gnu')
323
323
  const bindingPackageVersion = require('brustjs-linux-arm64-gnu/package.json').version
324
- if (bindingPackageVersion !== '0.1.0-alpha' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
325
- throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
324
+ if (bindingPackageVersion !== '0.1.0-alpha.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
325
+ throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
326
326
  }
327
327
  return binding
328
328
  } catch (e) {
@@ -339,8 +339,8 @@ function requireNative() {
339
339
  try {
340
340
  const binding = require('brustjs-linux-arm-musleabihf')
341
341
  const bindingPackageVersion = require('brustjs-linux-arm-musleabihf/package.json').version
342
- if (bindingPackageVersion !== '0.1.0-alpha' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
343
- throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
342
+ if (bindingPackageVersion !== '0.1.0-alpha.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
343
+ throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
344
344
  }
345
345
  return binding
346
346
  } catch (e) {
@@ -355,8 +355,8 @@ function requireNative() {
355
355
  try {
356
356
  const binding = require('brustjs-linux-arm-gnueabihf')
357
357
  const bindingPackageVersion = require('brustjs-linux-arm-gnueabihf/package.json').version
358
- if (bindingPackageVersion !== '0.1.0-alpha' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
359
- throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
358
+ if (bindingPackageVersion !== '0.1.0-alpha.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
359
+ throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
360
360
  }
361
361
  return binding
362
362
  } catch (e) {
@@ -373,8 +373,8 @@ function requireNative() {
373
373
  try {
374
374
  const binding = require('brustjs-linux-loong64-musl')
375
375
  const bindingPackageVersion = require('brustjs-linux-loong64-musl/package.json').version
376
- if (bindingPackageVersion !== '0.1.0-alpha' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
377
- throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
376
+ if (bindingPackageVersion !== '0.1.0-alpha.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
377
+ throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
378
378
  }
379
379
  return binding
380
380
  } catch (e) {
@@ -389,8 +389,8 @@ function requireNative() {
389
389
  try {
390
390
  const binding = require('brustjs-linux-loong64-gnu')
391
391
  const bindingPackageVersion = require('brustjs-linux-loong64-gnu/package.json').version
392
- if (bindingPackageVersion !== '0.1.0-alpha' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
393
- throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
392
+ if (bindingPackageVersion !== '0.1.0-alpha.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
393
+ throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
394
394
  }
395
395
  return binding
396
396
  } catch (e) {
@@ -407,8 +407,8 @@ function requireNative() {
407
407
  try {
408
408
  const binding = require('brustjs-linux-riscv64-musl')
409
409
  const bindingPackageVersion = require('brustjs-linux-riscv64-musl/package.json').version
410
- if (bindingPackageVersion !== '0.1.0-alpha' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
411
- throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
410
+ if (bindingPackageVersion !== '0.1.0-alpha.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
411
+ throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
412
412
  }
413
413
  return binding
414
414
  } catch (e) {
@@ -423,8 +423,8 @@ function requireNative() {
423
423
  try {
424
424
  const binding = require('brustjs-linux-riscv64-gnu')
425
425
  const bindingPackageVersion = require('brustjs-linux-riscv64-gnu/package.json').version
426
- if (bindingPackageVersion !== '0.1.0-alpha' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
427
- throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
426
+ if (bindingPackageVersion !== '0.1.0-alpha.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
427
+ throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
428
428
  }
429
429
  return binding
430
430
  } catch (e) {
@@ -440,8 +440,8 @@ function requireNative() {
440
440
  try {
441
441
  const binding = require('brustjs-linux-ppc64-gnu')
442
442
  const bindingPackageVersion = require('brustjs-linux-ppc64-gnu/package.json').version
443
- if (bindingPackageVersion !== '0.1.0-alpha' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
444
- throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
443
+ if (bindingPackageVersion !== '0.1.0-alpha.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
444
+ throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
445
445
  }
446
446
  return binding
447
447
  } catch (e) {
@@ -456,8 +456,8 @@ function requireNative() {
456
456
  try {
457
457
  const binding = require('brustjs-linux-s390x-gnu')
458
458
  const bindingPackageVersion = require('brustjs-linux-s390x-gnu/package.json').version
459
- if (bindingPackageVersion !== '0.1.0-alpha' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
460
- throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
459
+ if (bindingPackageVersion !== '0.1.0-alpha.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
460
+ throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
461
461
  }
462
462
  return binding
463
463
  } catch (e) {
@@ -476,8 +476,8 @@ function requireNative() {
476
476
  try {
477
477
  const binding = require('brustjs-openharmony-arm64')
478
478
  const bindingPackageVersion = require('brustjs-openharmony-arm64/package.json').version
479
- if (bindingPackageVersion !== '0.1.0-alpha' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
480
- throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
479
+ if (bindingPackageVersion !== '0.1.0-alpha.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
480
+ throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
481
481
  }
482
482
  return binding
483
483
  } catch (e) {
@@ -492,8 +492,8 @@ function requireNative() {
492
492
  try {
493
493
  const binding = require('brustjs-openharmony-x64')
494
494
  const bindingPackageVersion = require('brustjs-openharmony-x64/package.json').version
495
- if (bindingPackageVersion !== '0.1.0-alpha' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
496
- throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
495
+ if (bindingPackageVersion !== '0.1.0-alpha.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
496
+ throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
497
497
  }
498
498
  return binding
499
499
  } catch (e) {
@@ -508,8 +508,8 @@ function requireNative() {
508
508
  try {
509
509
  const binding = require('brustjs-openharmony-arm')
510
510
  const bindingPackageVersion = require('brustjs-openharmony-arm/package.json').version
511
- if (bindingPackageVersion !== '0.1.0-alpha' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
512
- throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
511
+ if (bindingPackageVersion !== '0.1.0-alpha.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
512
+ throw new Error(`Native binding package version mismatch, expected 0.1.0-alpha.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
513
513
  }
514
514
  return binding
515
515
  } catch (e) {
@@ -577,6 +577,7 @@ if (!nativeBinding) {
577
577
 
578
578
  module.exports = nativeBinding
579
579
  module.exports.beginServe = nativeBinding.beginServe
580
+ module.exports.compileJsx = nativeBinding.compileJsx
580
581
  module.exports.configureCache = nativeBinding.configureCache
581
582
  module.exports.configureCssDir = nativeBinding.configureCssDir
582
583
  module.exports.configureIslandsDir = nativeBinding.configureIslandsDir
package/runtime/index.ts CHANGED
@@ -419,8 +419,8 @@ export const brust = {
419
419
  isTty: process.stdout.isTTY === true,
420
420
  write: (s: string) => process.stdout.write(s),
421
421
  })
422
+ // The banner's "Local" line is the ready signal โ€” no separate event.
422
423
  tui.updateStatus({ port, workers, watching: [scanRoot] })
423
- tui.appendEvent(`โ–ถ serving on http://127.0.0.1:${port}`)
424
424
 
425
425
  const coordinator = new Coordinator({
426
426
  workers: {
@@ -1,16 +0,0 @@
1
- import type { ReactNode } from 'react'
2
-
3
- export default function Layout({ title, children }: { title: string; children: ReactNode }) {
4
- return (
5
- <html lang="en">
6
- <head>
7
- <meta charSet="utf-8" />
8
- <meta name="viewport" content="width=device-width, initial-scale=1" />
9
- <title>{title}</title>
10
- </head>
11
- <body className="bg-white text-gray-900 font-sans">
12
- <main className="max-w-3xl mx-auto px-5 py-8">{children}</main>
13
- </body>
14
- </html>
15
- )
16
- }