brustjs 0.1.12-alpha → 0.1.13-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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brustjs",
3
- "version": "0.1.12-alpha",
3
+ "version": "0.1.13-alpha",
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": {
@@ -40,12 +40,12 @@
40
40
  "typescript": "^6.0.3"
41
41
  },
42
42
  "optionalDependencies": {
43
- "brustjs-darwin-x64": "0.1.12-alpha",
44
- "brustjs-darwin-arm64": "0.1.12-alpha",
45
- "brustjs-linux-x64-gnu": "0.1.12-alpha",
46
- "brustjs-linux-arm64-gnu": "0.1.12-alpha",
47
- "brustjs-linux-x64-musl": "0.1.12-alpha",
48
- "brustjs-linux-arm64-musl": "0.1.12-alpha"
43
+ "brustjs-darwin-x64": "0.1.13-alpha",
44
+ "brustjs-darwin-arm64": "0.1.13-alpha",
45
+ "brustjs-linux-x64-gnu": "0.1.13-alpha",
46
+ "brustjs-linux-arm64-gnu": "0.1.13-alpha",
47
+ "brustjs-linux-x64-musl": "0.1.13-alpha",
48
+ "brustjs-linux-arm64-musl": "0.1.13-alpha"
49
49
  },
50
50
  "peerDependencies": {
51
51
  "react": "^19.2.6",
@@ -219,8 +219,8 @@ export async function runBuild(args: string[]): Promise<void> {
219
219
  // `import { actions } from './actions'` path, no scan and no `_actions-prebuilt`
220
220
  // file. (The old `'use server'` filesystem scanner is gone.)
221
221
 
222
- // routes.tsx is the scan target for both islands (§3) and the MCP manifest
223
- // (§4). Computed once here and reused below.
222
+ // routes.tsx is the scan target for both islands (S3) and the MCP manifest
223
+ // (S4). Computed once here and reused below.
224
224
  const routesFile = path.join(entryDir, 'routes.tsx')
225
225
 
226
226
  // 3. Build islands (if any <Island> usage is found in the routes graph).
@@ -279,7 +279,7 @@ export async function runBuild(args: string[]): Promise<void> {
279
279
  // deploy ships the templates. The prebuilt runtime reads them from
280
280
  // `<BRUST_DIST_DIR>/jinja` (see index.ts loadJinjaOnce / configureJinjaDir).
281
281
  const jinjaDir = path.join(outDir, 'jinja')
282
- // Spec §7 Component-source resolution: scan the routes module's source for
282
+ // Spec S7 Component-source resolution: scan the routes module's source for
283
283
  // ImportDeclarations, NOT the app entry's. The app entry only imports the
284
284
  // routes module + brust; the page components are imported by routes.tsx.
285
285
  // If routes.tsx doesn't exist (no routes module), we still write an empty
@@ -60,7 +60,7 @@ export async function runDev(args: string[]): Promise<void> {
60
60
 
61
61
  // Sub-project J — emit .brust/jinja/<Name>.jinja templates BEFORE handing
62
62
  // off to the user's entry. The runtime loads these on boot. Dev-mode HMR
63
- // on .tsx edit is deferred per spec §12 (restart-to-reload for v2).
63
+ // on .tsx edit is deferred per spec S12 (restart-to-reload for v2).
64
64
  const entryDir = dirname(entry)
65
65
  const routesFile = path.join(entryDir, 'routes.tsx')
66
66
  let loadedRoutes: any[] = []
@@ -74,7 +74,7 @@ export async function runDev(args: string[]): Promise<void> {
74
74
  )
75
75
  }
76
76
  }
77
- // Spec §7 — scan routes.tsx (where page imports live), not the app entry.
77
+ // Spec S7 — scan routes.tsx (where page imports live), not the app entry.
78
78
  // outDir = process.cwd() to align with the runtime's loadJinjaOnce which
79
79
  // reads from `cwd + '.brust/jinja'`. When user runs `bun run dev
80
80
  // example/hello-world/index.ts` from repo root, cwd != entryDir; writing
@@ -108,11 +108,11 @@ function injectDevClientIntoTemplate(template: string): string {
108
108
  * `.brust/jinja/<Name>.jinja` templates. Invoked from `brust build` and
109
109
  * `brust dev` after the user's routes are flattened.
110
110
  *
111
- * Limitations (spec §7 + §13.10):
111
+ * Limitations (spec S7 + S13.10):
112
112
  * - Regex-based import scanner — handles `import Name from './path'` only.
113
113
  * Full swc AST + re-export chain support deferred to v2.x.
114
114
  * - Dev mode does NOT hot-reload templates on .tsx edit. Boot-only; restart
115
- * required. Deferred per spec §12.
115
+ * required. Deferred per spec S12.
116
116
  */
117
117
 
118
118
  export interface NativeRouteEmitOpts {
@@ -395,7 +395,7 @@ export async function emitNativeTemplates(opts: NativeRouteEmitOpts): Promise<vo
395
395
  export function scanImports(entryFile: string): Map<string, string> {
396
396
  const source = readFileSync(entryFile, 'utf8')
397
397
  const map = new Map<string, string>()
398
- // Regex-based scanner; full swc AST scan deferred per spec §7 + §13.10.
398
+ // Regex-based scanner; full swc AST scan deferred per spec S7 + S13.10.
399
399
  const re = /^import\s+(\w+)\s+from\s+['"]([^'"]+)['"]/gm
400
400
  for (let m = re.exec(source); m !== null; m = re.exec(source)) {
401
401
  const localName = m[1]!
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "lib": ["ESNext"],
3
+ "lib": ["ESNext", "DOM", "DOM.Iterable"],
4
4
  "target": "ESNext",
5
5
  "module": "Preserve",
6
6
  "moduleDetection": "force",
@@ -104,7 +104,7 @@ export declare function napiRegisterWsPaths(paths: Array<string>): NapiResult<un
104
104
  * Worker-driven render chunk delivery. Worker calls this once per chunk
105
105
  * it wants to emit; final call uses `len = 0` to close the channel.
106
106
  *
107
- * Contract (spec §5.2):
107
+ * Contract (spec S5.2):
108
108
  * - `len > 0`: read SAB[0..len], send Bytes through render_slot.chunk_tx,
109
109
  * await ack. Resolves after Rust writes the chunk to the socket.
110
110
  * - `len == 0`: send Final, await ack. Closes the response.
@@ -132,7 +132,7 @@ export declare function napiRenderChunkFinal(workerId: number, len: number): Pro
132
132
  /**
133
133
  * Sub-project J — render via minijinja using SAB-side-channeled loader data.
134
134
  *
135
- * SAB convention (spec §6 last paragraph): per-napi-call. `napi_render_jinja`
135
+ * SAB convention (spec S6 last paragraph): per-napi-call. `napi_render_jinja`
136
136
  * treats SAB[0..data_len] as INBOUND raw JSON written by the JS worker. It
137
137
  * renders the template Rust-side, assembles a `[meta_len: u16 BE][meta JSON]
138
138
  * [body]` payload, writes it back into the SAB, and returns its byte length —
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.12-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.12-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
80
+ if (bindingPackageVersion !== '0.1.13-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.13-alpha 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.12-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.12-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
96
+ if (bindingPackageVersion !== '0.1.13-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.13-alpha 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.12-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.12-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
117
+ if (bindingPackageVersion !== '0.1.13-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.13-alpha 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.12-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.12-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
133
+ if (bindingPackageVersion !== '0.1.13-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.13-alpha 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.12-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.12-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
150
+ if (bindingPackageVersion !== '0.1.13-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.13-alpha 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.12-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.12-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
166
+ if (bindingPackageVersion !== '0.1.13-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.13-alpha 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.12-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.12-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
185
+ if (bindingPackageVersion !== '0.1.13-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.13-alpha 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.12-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.12-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
201
+ if (bindingPackageVersion !== '0.1.13-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.13-alpha 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.12-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.12-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
217
+ if (bindingPackageVersion !== '0.1.13-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.13-alpha 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.12-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.12-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
237
+ if (bindingPackageVersion !== '0.1.13-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.13-alpha 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.12-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.12-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
253
+ if (bindingPackageVersion !== '0.1.13-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.13-alpha 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.12-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.12-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
274
+ if (bindingPackageVersion !== '0.1.13-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.13-alpha 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.12-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.12-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
290
+ if (bindingPackageVersion !== '0.1.13-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.13-alpha 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.12-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.12-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
308
+ if (bindingPackageVersion !== '0.1.13-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.13-alpha 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.12-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.12-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
324
+ if (bindingPackageVersion !== '0.1.13-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.13-alpha 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.12-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.12-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
342
+ if (bindingPackageVersion !== '0.1.13-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.13-alpha 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.12-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.12-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
358
+ if (bindingPackageVersion !== '0.1.13-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.13-alpha 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.12-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.12-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
376
+ if (bindingPackageVersion !== '0.1.13-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.13-alpha 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.12-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.12-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
392
+ if (bindingPackageVersion !== '0.1.13-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.13-alpha 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.12-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.12-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
410
+ if (bindingPackageVersion !== '0.1.13-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.13-alpha 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.12-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.12-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
426
+ if (bindingPackageVersion !== '0.1.13-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.13-alpha 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.12-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.12-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
443
+ if (bindingPackageVersion !== '0.1.13-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.13-alpha 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.12-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.12-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
459
+ if (bindingPackageVersion !== '0.1.13-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.13-alpha 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.12-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.12-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
479
+ if (bindingPackageVersion !== '0.1.13-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.13-alpha 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.12-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.12-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
495
+ if (bindingPackageVersion !== '0.1.13-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.13-alpha 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.12-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.12-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
511
+ if (bindingPackageVersion !== '0.1.13-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.13-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
513
513
  }
514
514
  return binding
515
515
  } catch (e) {
@@ -10,8 +10,9 @@ import { createElement, type ReactNode } from 'react'
10
10
  * brust keeps full ownership of `<head>` and can add more tags later (importmap,
11
11
  * preloads) without colliding with hand-written head elements.
12
12
  *
13
- * All props are compile-time string literals on the native path (the shell is
14
- * rendered in Rust, so they can't be dynamic expressions there). This React
13
+ * On the native path each prop accepts a compile-time string literal OR a
14
+ * loader member-path (`title={data.title}`), interpolated into the Rust-rendered
15
+ * shell as `{{ path }}` (S8). Calls/arithmetic are still rejected. This React
15
16
  * implementation mirrors the compiled output for the rare non-native use. */
16
17
  export interface BrustPageProps {
17
18
  /** `<html lang>` — defaults to `"en"`. */
@@ -53,7 +53,13 @@ export function consumeIslandUsedFlag(): boolean {
53
53
  return v
54
54
  }
55
55
 
56
- export function Island<P extends Record<string, unknown>>({
56
+ // Constraint is `object`, NOT `Record<string, unknown>`: a TS `interface` has no
57
+ // implicit index signature, so it does NOT satisfy `extends Record<string, unknown>`
58
+ // — that made `P` fall back to the constraint and rejected any component whose
59
+ // props are declared as an interface (`<Island component={C} props={p} />` →
60
+ // ts(2322)). `extends object` accepts interfaces, still rejects primitives, and
61
+ // lets `P` infer from `component`/`props` so the two stay type-tied.
62
+ export function Island<P extends object>({
57
63
  component: Component,
58
64
  props,
59
65
  hydrate = 'load',
@@ -13,7 +13,7 @@ export function _resetWarnedForTests(): void {
13
13
  * before the first occurrence of `</head>` (case-insensitive). Returns the
14
14
  * original body untouched if `hrefs` is empty or if `</head>` is absent
15
15
  * (warns once in the latter case). Renderer calls this on the first chunk
16
- * only — see spec §"SSR <link> injection". */
16
+ * only — see spec S"SSR <link> injection". */
17
17
  export function injectCssLink(body: Uint8Array, hrefs: readonly string[]): Uint8Array {
18
18
  if (hrefs.length === 0) return body
19
19
  const pos = findHeadCloseTag(body)
package/runtime/routes.ts CHANGED
@@ -336,7 +336,7 @@ function validateRoute(r: Route, basePath: string): void {
336
336
 
337
337
  /** Walk the nested route tree, emitting one FlatRoute per leaf or index node.
338
338
  * Composes paths, middleware, errorBoundary, and cache per the rules in
339
- * the design spec (§3). */
339
+ * the design spec (S3). */
340
340
  export function flattenRoutes(routes: Route[]): FlatRoute[] {
341
341
  const out: FlatRoute[] = []
342
342
  walkRoutes(routes, [], '', out)
@@ -593,7 +593,7 @@ export function makeRenderer(
593
593
  // path above and works for native routes. But middleware that calls
594
594
  // next() then mutates status/headers (e.g. adds Cache-Control) is NOT
595
595
  // forwarded; napi_render_jinja hardcodes status: 200 and empty headers.
596
- // Spec §4 doesn't define post-next() mutation semantics for native;
596
+ // Spec S4 doesn't define post-next() mutation semantics for native;
597
597
  // deferred to v2.x. If your middleware needs to mutate, use a React
598
598
  // route for now.
599
599
  if (flat.nativeTemplate !== undefined) {
@@ -884,13 +884,24 @@ async function navigationBranch(
884
884
  }
885
885
 
886
886
  try {
887
- const element = await buildRenderElement(call as any, flat, getWorkerId)
888
- if (!element) throw new Error('render setup failed')
889
- // Use renderToPipeableStream + onAllReady so pages with <Suspense> emit
890
- // their RESOLVED markup, not the fallback. renderToString would only
891
- // capture the shell navigating SPA-style to a Suspense-using route
892
- // would otherwise ship "loading…" and never recover.
893
- const fullHtml = await renderToAwaitedString(element)
887
+ // Native (jinja) routes have no React tree, so we can't renderToString them.
888
+ // Render the template Rust-side (same path as a full document load) and use
889
+ // its HTML; React routes keep the renderToAwaitedString path below. Without
890
+ // this branch, a SPA navigation to a native route React-renders a component
891
+ // whose loader fields arrive undefined throws 500 → full-reload fallback
892
+ // on every internal link.
893
+ let fullHtml: string
894
+ if (flat.nativeTemplate !== undefined) {
895
+ fullHtml = await renderNativeRouteToHtml(call, flat, view, encoder, workerId)
896
+ } else {
897
+ const element = await buildRenderElement(call as any, flat, getWorkerId)
898
+ if (!element) throw new Error('render setup failed')
899
+ // Use renderToPipeableStream + onAllReady so pages with <Suspense> emit
900
+ // their RESOLVED markup, not the fallback. renderToString would only
901
+ // capture the shell — navigating SPA-style to a Suspense-using route
902
+ // would otherwise ship "loading…" and never recover.
903
+ fullHtml = await renderToAwaitedString(element)
904
+ }
894
905
 
895
906
  // Extract <main> inner content. If the page didn't render a <main>,
896
907
  // ship the full HTML — the client's no-main check will fire its
@@ -925,6 +936,65 @@ async function navigationBranch(
925
936
  }
926
937
  }
927
938
 
939
+ /** Render a native (jinja) route to its full HTML document for a SPA navigation.
940
+ *
941
+ * Mirrors the render-branch native path: run the leaf loader, merge island /
942
+ * component manifest context, then call the SYNC `napiRenderJinja`, which writes
943
+ * a framed `[meta_len u16 BE][meta JSON][body]` response into the SAB and returns
944
+ * its length. Here — unlike the render branch, which returns that length so Rust
945
+ * streams the SAB straight to the socket — we read the body back out of the SAB
946
+ * and hand it to navigationBranch, which extracts `<main>` + `<title>` from it.
947
+ *
948
+ * Throws on loader failure / oversized data / non-200 render; navigationBranch's
949
+ * catch turns that into a 500 (→ client full-reload fallback). */
950
+ async function renderNativeRouteToHtml(
951
+ call: Extract<RouteCall, { kind: 'navigation' }>,
952
+ flat: FlatRoute,
953
+ view: Uint8Array,
954
+ encoder: TextEncoder,
955
+ workerId: bigint,
956
+ ): Promise<string> {
957
+ const templateName = flat.nativeTemplate as string
958
+ const leaf = flat.chain[flat.chain.length - 1]
959
+
960
+ let data: unknown = {}
961
+ if (leaf.loader) {
962
+ data = await leaf.loader({ params: call.params, path: call.path, req: call.req } as any)
963
+ }
964
+
965
+ let ctx = (data ?? {}) as Record<string, unknown>
966
+ const manifest = loadIslandManifest(templateName)
967
+ const compManifest = loadComponentManifest(templateName)
968
+ if ((manifest && manifest.length > 0) || (compManifest && compManifest.length > 0)) {
969
+ const [islandExtra, componentExtra] = await Promise.all([
970
+ manifest && manifest.length > 0
971
+ ? resolveIslandContext(manifest, ctx, islandCache)
972
+ : Promise.resolve({} as Record<string, string>),
973
+ compManifest && compManifest.length > 0
974
+ ? resolveComponentContext(compManifest, ctx, templateName, undefined, islandCache)
975
+ : Promise.resolve({} as Record<string, string>),
976
+ ])
977
+ ctx = { ...ctx, ...islandExtra, ...componentExtra }
978
+ }
979
+
980
+ const bytes = encoder.encode(JSON.stringify(ctx))
981
+ if (bytes.length > view.length) throw new Error('native loader data too large for SAB')
982
+ view.set(bytes, 0)
983
+
984
+ const len = (native as any).napiRenderJinja(
985
+ Number(workerId),
986
+ bytes.length,
987
+ templateName,
988
+ ) as number
989
+
990
+ // Parse the framed response the SAB now holds: [meta_len u16 BE][meta][body].
991
+ const metaLen = (view[0] << 8) | view[1]
992
+ const decoder = new TextDecoder()
993
+ const meta = JSON.parse(decoder.decode(view.subarray(2, 2 + metaLen))) as { status?: number }
994
+ if (meta.status !== 200) throw new Error(`native render returned status ${meta.status}`)
995
+ return decoder.decode(view.subarray(2 + metaLen, len))
996
+ }
997
+
928
998
  /** Build the React element for a render or navigation call: runs loaders
929
999
  * top-down, builds the element bottom-up wrapping in OutletContext.Provider
930
1000
  * so nested routes receive the deeper element via <Outlet />. The caller
package/runtime/treaty.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import type { ActionsBuilder } from './define-actions.ts'
2
+
1
3
  const METHODS = new Set(['get', 'post', 'put', 'patch', 'delete', 'head'])
2
4
 
3
5
  export interface TreatyResponse<Data = unknown, Err = unknown> {
@@ -13,6 +15,29 @@ export interface ClientOptions {
13
15
  fetch?: typeof fetch
14
16
  }
15
17
 
18
+ type FirstSegment<S extends string> = S extends `/${infer Rest}`
19
+ ? FirstSegment<Rest>
20
+ : S extends `${infer T}/${infer _U}`
21
+ ? T
22
+ : S
23
+
24
+ export type PermissiveProxy = {
25
+ (arg?: any): PermissiveProxy
26
+ [key: string]: PermissiveProxy
27
+ } & {
28
+ get: (options?: any) => Promise<TreatyResponse<any, any>>
29
+ post: (body?: any, options?: any) => Promise<TreatyResponse<any, any>>
30
+ put: (body?: any, options?: any) => Promise<TreatyResponse<any, any>>
31
+ patch: (body?: any, options?: any) => Promise<TreatyResponse<any, any>>
32
+ delete: (body?: any, options?: any) => Promise<TreatyResponse<any, any>>
33
+ head: (options?: any) => Promise<TreatyResponse<any, any>>
34
+ }
35
+
36
+ export type Treaty<App> =
37
+ App extends ActionsBuilder<infer Acc>
38
+ ? { [K in FirstSegment<keyof Acc & string>]: PermissiveProxy } & PermissiveProxy
39
+ : PermissiveProxy
40
+
16
41
  function resolvePrefix(opts?: ClientOptions): string {
17
42
  if (opts?.prefix) return opts.prefix
18
43
  const g = (globalThis as { __BRUST_ACTION_PREFIX__?: string }).__BRUST_ACTION_PREFIX__
@@ -23,7 +48,7 @@ function resolvePrefix(opts?: ClientOptions): string {
23
48
  * with an object fills the next {param}(s) positionally (in insertion order); a
24
49
  * terminal method key (.get/.post/…) performs the request. URL is composed from
25
50
  * the literal accumulated segments — never from any inferred type. */
26
- export function client<App = unknown>(opts?: ClientOptions): App {
51
+ export function client<App = unknown>(opts?: ClientOptions): Treaty<App> {
27
52
  const prefix = resolvePrefix(opts)
28
53
  const doFetch = opts?.fetch ?? fetch
29
54
  function make(segments: string[]): any {
@@ -101,7 +126,7 @@ export function client<App = unknown>(opts?: ClientOptions): App {
101
126
  },
102
127
  })
103
128
  }
104
- return make([]) as App
129
+ return make([]) as any as Treaty<App>
105
130
  }
106
131
 
107
132
  function hasFilePart(b: unknown): boolean {