brustjs 0.1.55-alpha → 0.1.57-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.55-alpha",
3
+ "version": "0.1.57-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": {
@@ -41,12 +41,12 @@
41
41
  "typescript": "^6.0.3"
42
42
  },
43
43
  "optionalDependencies": {
44
- "brustjs-darwin-x64": "0.1.55-alpha",
45
- "brustjs-darwin-arm64": "0.1.55-alpha",
46
- "brustjs-linux-x64-gnu": "0.1.55-alpha",
47
- "brustjs-linux-arm64-gnu": "0.1.55-alpha",
48
- "brustjs-linux-x64-musl": "0.1.55-alpha",
49
- "brustjs-linux-arm64-musl": "0.1.55-alpha"
44
+ "brustjs-darwin-x64": "0.1.57-alpha",
45
+ "brustjs-darwin-arm64": "0.1.57-alpha",
46
+ "brustjs-linux-x64-gnu": "0.1.57-alpha",
47
+ "brustjs-linux-arm64-gnu": "0.1.57-alpha",
48
+ "brustjs-linux-x64-musl": "0.1.57-alpha",
49
+ "brustjs-linux-arm64-musl": "0.1.57-alpha"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "react": "^19.2.6",
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.55-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.55-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
80
+ if (bindingPackageVersion !== '0.1.57-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.57-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.55-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.55-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
96
+ if (bindingPackageVersion !== '0.1.57-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.57-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.55-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.55-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
117
+ if (bindingPackageVersion !== '0.1.57-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.57-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.55-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.55-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
133
+ if (bindingPackageVersion !== '0.1.57-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.57-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.55-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.55-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
150
+ if (bindingPackageVersion !== '0.1.57-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.57-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.55-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.55-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
166
+ if (bindingPackageVersion !== '0.1.57-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.57-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.55-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.55-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
185
+ if (bindingPackageVersion !== '0.1.57-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.57-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.55-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.55-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
201
+ if (bindingPackageVersion !== '0.1.57-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.57-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.55-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.55-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
217
+ if (bindingPackageVersion !== '0.1.57-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.57-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.55-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.55-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
237
+ if (bindingPackageVersion !== '0.1.57-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.57-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.55-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.55-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
253
+ if (bindingPackageVersion !== '0.1.57-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.57-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.55-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.55-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
274
+ if (bindingPackageVersion !== '0.1.57-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.57-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.55-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.55-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
290
+ if (bindingPackageVersion !== '0.1.57-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.57-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.55-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.55-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
308
+ if (bindingPackageVersion !== '0.1.57-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.57-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.55-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.55-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
324
+ if (bindingPackageVersion !== '0.1.57-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.57-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.55-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.55-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
342
+ if (bindingPackageVersion !== '0.1.57-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.57-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.55-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.55-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
358
+ if (bindingPackageVersion !== '0.1.57-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.57-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.55-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.55-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
376
+ if (bindingPackageVersion !== '0.1.57-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.57-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.55-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.55-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
392
+ if (bindingPackageVersion !== '0.1.57-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.57-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.55-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.55-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
410
+ if (bindingPackageVersion !== '0.1.57-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.57-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.55-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.55-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
426
+ if (bindingPackageVersion !== '0.1.57-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.57-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.55-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.55-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
443
+ if (bindingPackageVersion !== '0.1.57-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.57-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.55-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.55-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
459
+ if (bindingPackageVersion !== '0.1.57-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.57-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.55-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.55-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
479
+ if (bindingPackageVersion !== '0.1.57-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.57-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.55-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.55-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
495
+ if (bindingPackageVersion !== '0.1.57-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.57-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.55-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.55-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
511
+ if (bindingPackageVersion !== '0.1.57-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.57-alpha but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
513
513
  }
514
514
  return binding
515
515
  } catch (e) {
package/runtime/index.ts CHANGED
@@ -461,6 +461,17 @@ export const brust = {
461
461
  const scanRoot = opts.scanRoot ?? path.dirname(fileURLToPath(opts.entry))
462
462
 
463
463
  const dev = opts.dev === true || process.env.BRUST_DEV === '1'
464
+ // Unify the `dev: true` option with the BRUST_DEV env signal. The dev
465
+ // coordinator/watcher (which calls worker-registry spawnAll() on hot reload)
466
+ // keys off this `dev` variable, but serve()'s registerInitialPool — plus
467
+ // every other `process.env.BRUST_DEV === '1'` gate (dev-client injection,
468
+ // store/css `no-store`, cookie dev warnings) — keys off the env var ALONE.
469
+ // The `brust dev` CLI sets BRUST_DEV=1; the programmatic `run({ dev: true })`
470
+ // path must too, or hot reload calls spawnAll() against a pool that
471
+ // registerInitialPool() never recorded ("spawnAll called before
472
+ // registerInitialPool"). Set it here, before serve() spawns workers, so they
473
+ // inherit it via baseEnv.
474
+ if (dev) process.env.BRUST_DEV = '1'
464
475
  let routes = opts.routes
465
476
  if (dev) {
466
477
  const { createDevWsRoute } = await import('./dev/ws-channel.ts')
@@ -5,3 +5,9 @@
5
5
  // client.js`) exposes only `createRoot` and `hydrateRoot`; name them
6
6
  // explicitly so the bundler has a static export list to emit.
7
7
  export { createRoot, hydrateRoot } from 'react-dom/client'
8
+ // Defensive parity with react.ts: re-expose the namespace as the default so a
9
+ // third-party island dep that does `import ReactDOM from 'react-dom/client'`
10
+ // resolves a real object (createRoot/hydrateRoot as members) instead of
11
+ // failing to hydrate on a missing default export.
12
+ import * as ReactDOM from 'react-dom/client'
13
+ export default ReactDOM
@@ -9,3 +9,11 @@
9
9
  // same object).
10
10
  export * from 'react'
11
11
  export { jsx, jsxs } from 'react/jsx-runtime'
12
+ // `export *` omits the default per spec, so the importmap-targeted `react`
13
+ // chunk had named exports but NO default — third-party ESM that does
14
+ // `import React from 'react'` (e.g. @dnd-kit) then fails to hydrate with
15
+ // "module 'react' does not provide an export named 'default'". Re-expose the
16
+ // React namespace as the default: it carries createElement/useState/etc. as
17
+ // members, matching what a normal `import React from 'react'` yields.
18
+ import * as React from 'react'
19
+ export default React
@@ -0,0 +1,22 @@
1
+ // runtime/navigation/server-context.ts
2
+ // Server-only: imports a Node builtin (node:async_hooks), so it must NOT be
3
+ // reachable from browser/island bundles (brustjs/client) or the React-free,
4
+ // DOM-free navigation entry (brustjs/navigation = ./index.ts). It mirrors
5
+ // store/server-context.ts: the server pulls this module in via routes.ts, where
6
+ // each render is wrapped in a per-request nav scope; navigation/store.ts reaches
7
+ // the active scope through the resolver registered below (__setServerNavResolver),
8
+ // which stays out of the client bundle (store.ts never imports this file).
9
+ import { AsyncLocalStorage } from 'node:async_hooks'
10
+ import { __setServerNavResolver, type ServerNavScope } from './store.ts'
11
+
12
+ const navContext = new AsyncLocalStorage<ServerNavScope>()
13
+
14
+ /** Open a per-request nav scope so SSR — React islands AND native island SSR —
15
+ * sees the current request path via useNav()/getNavState() instead of the idle
16
+ * singleton default. `search` is the raw query string INCLUDING the leading '?'
17
+ * (so it matches the client's `location.search`), or '' when there is none. */
18
+ export function runInNavContext<T>(path: string, search: string, fn: () => T): T {
19
+ return navContext.run({ path, search }, fn)
20
+ }
21
+
22
+ __setServerNavResolver(() => navContext.getStore())
@@ -101,7 +101,46 @@ export const nav: NavStore = new Proxy({} as NavStore, {
101
101
  },
102
102
  })
103
103
 
104
+ // ─── Server-side request-scoped path (SSR seeding) ──────────────────────────
105
+ // The `nav` singleton is shared by every concurrent request in a worker, so on
106
+ // the server it CANNOT hold a per-request path. A server-only module
107
+ // (navigation/server-context.ts) registers a resolver that returns the active
108
+ // request's nav scope via AsyncLocalStorage — mirroring store/server-context.ts'
109
+ // __setServerResolver. The resolver stays null in browser/island bundles (which
110
+ // never import the Node module), so client behavior is byte-identical.
111
+ export interface ServerNavScope {
112
+ path: string
113
+ search: string
114
+ // Per-scope memo of the built NavState: getServerSnapshot may be called more
115
+ // than once per render and useSyncExternalStore requires referential stability.
116
+ _snap?: NavState
117
+ }
118
+ let serverNavResolver: (() => ServerNavScope | undefined) | null = null
119
+ export function __setServerNavResolver(fn: () => ServerNavScope | undefined): void {
120
+ serverNavResolver = fn
121
+ }
122
+
104
123
  export function getNavState(): NavState {
124
+ // Server SSR: when a request-scoped nav context is active, derive path/search
125
+ // from it (the singleton is not per-request-safe). `phase` is always 'idle'
126
+ // during SSR — no navigation is in flight — and from/to/error stay null. The
127
+ // client then seeds the SAME path via __navInit before hydration, so
128
+ // getServerSnapshot (this getter) and the server HTML agree → no mismatch,
129
+ // active from first paint.
130
+ const scope = serverNavResolver?.()
131
+ if (scope) {
132
+ if (!scope._snap) {
133
+ scope._snap = {
134
+ path: canonicalPath(scope.path),
135
+ search: scope.search,
136
+ phase: 'idle',
137
+ error: null,
138
+ from: null,
139
+ to: null,
140
+ }
141
+ }
142
+ return scope._snap
143
+ }
105
144
  const s = store()
106
145
  // Return the memoized snapshot if no transition has happened since it was
107
146
  // built (referential stability for useSyncExternalStore).
@@ -19,6 +19,7 @@ import { Buffer } from 'node:buffer'
19
19
  import { runInRequestScope } from '../request-context.ts'
20
20
  import { runInRequestCache } from '../loader-cache.ts'
21
21
  import { runInStoreContext } from '../store/server-context.ts'
22
+ import { runInNavContext } from '../navigation/server-context.ts'
22
23
 
23
24
  /** Render a React element to a single HTML string, awaiting all Suspense
24
25
  * boundaries via onAllReady. Used by navigationBranch — renderToString
@@ -59,6 +60,13 @@ export function renderToAwaitedString(element: ReactNode): Promise<string> {
59
60
  export interface RenderFragmentOpts {
60
61
  /** Request cookies visible to cookies()/session helpers inside the tree. */
61
62
  cookies?: Record<string, string>
63
+ /** Seed `useNav()` / `getNavState()` for islands inside the fragment. Provide
64
+ * when rendering for a known URL (e.g. a server-side preview of a specific
65
+ * route) so active-nav resolves; omit for a context-free fragment, where
66
+ * `useNav()` reports the idle defaults (path ''). `search` is the raw query
67
+ * string including the leading '?'. */
68
+ path?: string
69
+ search?: string
62
70
  }
63
71
 
64
72
  /** Render a component to an HTML string with framework contexts scoped:
@@ -76,11 +84,16 @@ export async function renderFragment<P extends object>(
76
84
  opts?: RenderFragmentOpts,
77
85
  ): Promise<string> {
78
86
  // Composition mirrors routes.ts runInRequestContext: scope outermost, then
79
- // the request-scoped loader cache, then the per-call store context.
87
+ // the request-scoped loader cache, then the per-call store context, then —
88
+ // only when the caller seeds a URL — the nav scope (innermost), so useNav()
89
+ // inside the fragment resolves active-nav. Without opts.path, no nav scope is
90
+ // opened and useNav() reports idle (the context-free default).
91
+ const render = () =>
92
+ renderToAwaitedString(createElement(Component as ComponentType, props as object))
80
93
  return runInRequestScope(opts?.cookies ?? {}, () =>
81
94
  runInRequestCache(() =>
82
95
  runInStoreContext(() =>
83
- renderToAwaitedString(createElement(Component as ComponentType, props as object)),
96
+ opts?.path !== undefined ? runInNavContext(opts.path, opts.search ?? '', render) : render(),
84
97
  ),
85
98
  ),
86
99
  )
package/runtime/routes.ts CHANGED
@@ -4,6 +4,7 @@ import * as native from './index.js'
4
4
  import { renderBranchStreaming } from './render/stream.ts'
5
5
  import { renderToAwaitedString } from './render/fragment.ts'
6
6
  import { runInStoreContext, collectSnapshot } from './store/server-context.ts'
7
+ import { runInNavContext } from './navigation/server-context.ts'
7
8
  import { buildStoreScripts } from './render/inject-store.ts'
8
9
  import { getCssHrefsForRoute } from './css.ts'
9
10
  import { runInRequestCache } from './loader-cache.ts'
@@ -45,8 +46,34 @@ export { slugifyHeading } from './md/slug.ts'
45
46
  // store scope (per-request store instance). `reqCookies` is the parsed incoming
46
47
  // Cookie header so a loader's `cookies.get` reads it and `cookies.set` stages
47
48
  // onto this scope (flushed onto response headers via flushSetCookie).
48
- function runInRequestContext<T>(reqCookies: Record<string, string>, fn: () => T): T {
49
- return runInRequestScope(reqCookies, () => runInRequestCache(() => runInStoreContext(fn)))
49
+ /** Raw query string (including the leading '?') of a `path+query` URL, or ''
50
+ * when there is none. Mirrors the client's `location.search` so the SSR-seeded
51
+ * nav.search matches what the client re-seeds via __navInit at hydration. */
52
+ function searchStringOf(url: string | undefined): string {
53
+ if (!url) return ''
54
+ const q = url.indexOf('?')
55
+ if (q === -1) return ''
56
+ // Strip any fragment so the seeded nav.search matches location.search (which
57
+ // never includes '#...'). HTTP request lines don't carry fragments today, but
58
+ // this keeps searchStringOf correct-by-construction.
59
+ const h = url.indexOf('#', q)
60
+ return h === -1 ? url.slice(q) : url.slice(q, h)
61
+ }
62
+
63
+ // `navPath`/`navSearch` (optional, trailing) open a per-request nav scope so
64
+ // island / React SSR sees the current request path via useNav()/getNavState()
65
+ // — active-nav at first paint with no hydration flash. They default to '' (the
66
+ // idle nav state), so loader-only / action scopes are unchanged. Trailing args
67
+ // keep the callback in the conventional 2nd position at the bare call sites.
68
+ function runInRequestContext<T>(
69
+ reqCookies: Record<string, string>,
70
+ fn: () => T,
71
+ navPath = '',
72
+ navSearch = '',
73
+ ): T {
74
+ return runInRequestScope(reqCookies, () =>
75
+ runInRequestCache(() => runInStoreContext(() => runInNavContext(navPath, navSearch, fn))),
76
+ )
50
77
  }
51
78
 
52
79
  // Sub-project J — island ISR cache, backed by the Rust-side store (shared across
@@ -1290,20 +1317,33 @@ export function makeRenderer(
1290
1317
  const compManifest = loadComponentManifest(flat.nativeTemplate)
1291
1318
  if ((manifest && manifest.length > 0) || (compManifest && compManifest.length > 0)) {
1292
1319
  const rt = JSON.parse(json) as Record<string, unknown>
1293
- const [islandExtra, componentExtra] = await Promise.all([
1294
- manifest && manifest.length > 0
1295
- ? resolveIslandContext(manifest, rt, islandCache)
1296
- : Promise.resolve({} as Record<string, string>),
1297
- compManifest && compManifest.length > 0
1298
- ? resolveComponentContext(
1299
- compManifest,
1300
- rt,
1301
- flat.nativeTemplate,
1302
- undefined,
1303
- islandCache,
1304
- )
1305
- : Promise.resolve({} as Record<string, string>),
1306
- ])
1320
+ // Capture the (narrowed) template name in this scope: the runInNavContext
1321
+ // closure below would otherwise lose the `flat.nativeTemplate !== undefined`
1322
+ // narrowing (TS doesn't carry control-flow narrowing into a nested fn).
1323
+ const nativeTemplate = flat.nativeTemplate
1324
+ // Seed the request path so island / SSR-component renderToString sees
1325
+ // the active route via useNav() (active-nav at first paint). The native
1326
+ // template itself is Rust-rendered and needs no scope. The loaders ran
1327
+ // in their own request scope above; this island SSR is outside it.
1328
+ const [islandExtra, componentExtra] = await runInNavContext(
1329
+ call.path,
1330
+ searchStringOf(call.req?.url),
1331
+ () =>
1332
+ Promise.all([
1333
+ manifest && manifest.length > 0
1334
+ ? resolveIslandContext(manifest, rt, islandCache)
1335
+ : Promise.resolve({} as Record<string, string>),
1336
+ compManifest && compManifest.length > 0
1337
+ ? resolveComponentContext(
1338
+ compManifest,
1339
+ rt,
1340
+ nativeTemplate,
1341
+ undefined,
1342
+ islandCache,
1343
+ )
1344
+ : Promise.resolve({} as Record<string, string>),
1345
+ ]),
1346
+ )
1307
1347
  const ctx = { ...rt, ...islandExtra, ...componentExtra }
1308
1348
  const finalBytes = encoder.encode(JSON.stringify(ctx))
1309
1349
  // The original size check guarded the pre-island bytes; the merged
@@ -1373,59 +1413,77 @@ export function makeRenderer(
1373
1413
  // or render resolves the same per-request instance. Snapshot is collected
1374
1414
  // after loaders (buildRenderElement resolved) — that's where Spec A stores
1375
1415
  // are seeded — and threaded into the render for <script> injection.
1376
- return await runInRequestContext(call.req?.cookies ?? {}, async () => {
1377
- // Computed ONCE and shared by buildRenderElement (leaf swap) and
1378
- // renderBranchStreaming (forceIslands) so the two can never diverge.
1379
- let shellMode = wantsSsgFallbackShell(flat, call)
1380
- let element: ReactNode = null
1381
- // The route actually rendered + its HTTP status. Normally the matched
1382
- // `flat` at the verdict status (404 when the matched route IS a
1383
- // catch-all). A React loader that `throw`s `notFound()` swaps both to
1384
- // the nearest catch-all at 404 below.
1385
- let renderFlat = flat
1386
- let renderStatus = flat.notFound === true ? 404 : verdict.status
1387
- try {
1388
- element = await buildRenderElement(call, flat, opts.getWorkerId, shellMode)
1389
- } catch (err) {
1390
- if (isHttpErrorTrigger(err)) {
1391
- // React loader threw httpError() — short-circuit with the trigger's
1392
- // response. NOT the errorBoundary, NOT the 500 path below: this is
1393
- // a deliberate response, like a middleware short-circuit.
1394
- return await emitSingleChunkResponse(
1395
- slotView,
1396
- napi,
1397
- workerId,
1398
- encoder,
1399
- {
1400
- status: err.status,
1401
- contentType: err.contentType,
1402
- body: err.body,
1403
- headers: err.headers,
1404
- },
1405
- slot,
1406
- )
1407
- }
1408
- if (isNotFoundTrigger(err)) {
1409
- // React `notFound()` trigger: abandon the matched route and render
1410
- // the NEAREST catch-all (same selection as a Rust-unmatched path)
1411
- // at HTTP 404 NOT the route's own Component, NOT a 500. Reuse the
1412
- // existing 404-render machinery by re-running buildRenderElement
1413
- // against the catch-all's chain.
1414
- // nfId is the array index, which IS the route_id (catch-alls keep
1415
- // their natural slot — see FlatRoute.notFound), so byRouteId resolves
1416
- // the same flat route the Rust tier picks for an unmatched path.
1417
- const nfId = selectNotFound(routes, call.path)
1418
- const nfFlat = nfId !== undefined ? byRouteId.get(nfId) : undefined
1419
- renderStatus = 404
1420
- if (nfFlat) {
1421
- renderFlat = nfFlat
1422
- shellMode = wantsSsgFallbackShell(nfFlat, call)
1423
- try {
1424
- element = await buildRenderElement(call, nfFlat, opts.getWorkerId, shellMode)
1425
- } catch (nfErr) {
1426
- // The catch-all's OWN loader/render setup failed — don't loop;
1427
- // fall back to the framework default 404 body at 404.
1428
- console.error(`[brust] catch-all render setup failed:`, nfErr)
1416
+ return await runInRequestContext(
1417
+ call.req?.cookies ?? {},
1418
+ async () => {
1419
+ // Computed ONCE and shared by buildRenderElement (leaf swap) and
1420
+ // renderBranchStreaming (forceIslands) so the two can never diverge.
1421
+ let shellMode = wantsSsgFallbackShell(flat, call)
1422
+ let element: ReactNode = null
1423
+ // The route actually rendered + its HTTP status. Normally the matched
1424
+ // `flat` at the verdict status (404 when the matched route IS a
1425
+ // catch-all). A React loader that `throw`s `notFound()` swaps both to
1426
+ // the nearest catch-all at 404 below.
1427
+ let renderFlat = flat
1428
+ let renderStatus = flat.notFound === true ? 404 : verdict.status
1429
+ try {
1430
+ element = await buildRenderElement(call, flat, opts.getWorkerId, shellMode)
1431
+ } catch (err) {
1432
+ if (isHttpErrorTrigger(err)) {
1433
+ // React loader threw httpError() short-circuit with the trigger's
1434
+ // response. NOT the errorBoundary, NOT the 500 path below: this is
1435
+ // a deliberate response, like a middleware short-circuit.
1436
+ return await emitSingleChunkResponse(
1437
+ slotView,
1438
+ napi,
1439
+ workerId,
1440
+ encoder,
1441
+ {
1442
+ status: err.status,
1443
+ contentType: err.contentType,
1444
+ body: err.body,
1445
+ headers: err.headers,
1446
+ },
1447
+ slot,
1448
+ )
1449
+ }
1450
+ if (isNotFoundTrigger(err)) {
1451
+ // React `notFound()` trigger: abandon the matched route and render
1452
+ // the NEAREST catch-all (same selection as a Rust-unmatched path)
1453
+ // at HTTP 404 — NOT the route's own Component, NOT a 500. Reuse the
1454
+ // existing 404-render machinery by re-running buildRenderElement
1455
+ // against the catch-all's chain.
1456
+ // nfId is the array index, which IS the route_id (catch-alls keep
1457
+ // their natural slot — see FlatRoute.notFound), so byRouteId resolves
1458
+ // the same flat route the Rust tier picks for an unmatched path.
1459
+ const nfId = selectNotFound(routes, call.path)
1460
+ const nfFlat = nfId !== undefined ? byRouteId.get(nfId) : undefined
1461
+ renderStatus = 404
1462
+ if (nfFlat) {
1463
+ renderFlat = nfFlat
1464
+ shellMode = wantsSsgFallbackShell(nfFlat, call)
1465
+ try {
1466
+ element = await buildRenderElement(call, nfFlat, opts.getWorkerId, shellMode)
1467
+ } catch (nfErr) {
1468
+ // The catch-all's OWN loader/render setup failed — don't loop;
1469
+ // fall back to the framework default 404 body at 404.
1470
+ console.error(`[brust] catch-all render setup failed:`, nfErr)
1471
+ return await emitSingleChunkResponse(
1472
+ slotView,
1473
+ napi,
1474
+ workerId,
1475
+ encoder,
1476
+ {
1477
+ status: 404,
1478
+ contentType: 'text/html; charset=utf-8',
1479
+ body: DEFAULT_NOT_FOUND_BODY,
1480
+ },
1481
+ slot,
1482
+ )
1483
+ }
1484
+ } else {
1485
+ // No catch-all registered for this prefix → framework default 404
1486
+ // body at status 404 (don't crash, don't 500).
1429
1487
  return await emitSingleChunkResponse(
1430
1488
  slotView,
1431
1489
  napi,
@@ -1440,65 +1498,56 @@ export function makeRenderer(
1440
1498
  )
1441
1499
  }
1442
1500
  } else {
1443
- // No catch-all registered for this prefix framework default 404
1444
- // body at status 404 (don't crash, don't 500).
1501
+ // Setup failure BEFORE renderToPipeableStream loader throw, params
1502
+ // bind throw. Shape matches the legacy "internal error" path so
1503
+ // existing integration tests stay green.
1504
+ console.error(`[brust] render setup failed:`, err)
1445
1505
  return await emitSingleChunkResponse(
1446
1506
  slotView,
1447
1507
  napi,
1448
1508
  workerId,
1449
1509
  encoder,
1450
1510
  {
1451
- status: 404,
1511
+ status: 500,
1452
1512
  contentType: 'text/html; charset=utf-8',
1453
- body: DEFAULT_NOT_FOUND_BODY,
1513
+ body: 'internal error',
1454
1514
  },
1455
1515
  slot,
1456
1516
  )
1457
1517
  }
1458
- } else {
1459
- // Setup failure BEFORE renderToPipeableStream — loader throw, params
1460
- // bind throw. Shape matches the legacy "internal error" path so
1461
- // existing integration tests stay green.
1462
- console.error(`[brust] render setup failed:`, err)
1463
- return await emitSingleChunkResponse(
1464
- slotView,
1465
- napi,
1466
- workerId,
1467
- encoder,
1468
- {
1469
- status: 500,
1470
- contentType: 'text/html; charset=utf-8',
1471
- body: 'internal error',
1472
- },
1473
- slot,
1474
- )
1475
1518
  }
1476
- }
1477
- const errorBoundary: ComponentType<{ error: Error }> =
1478
- renderFlat.errorBoundary ??
1479
- (({ error }) => createElement('div', null, `Internal Server Error: ${error.message}`))
1480
- const storeSnapshot = collectSnapshot()
1481
- await renderBranchStreaming({
1482
- element,
1483
- view: slotView,
1484
- slot,
1485
- workerId,
1486
- napi,
1487
- errorBoundary,
1488
- // Catch-all (`path: '*'`) leaf rendered on an unmatched path OR a
1489
- // React `notFound()` swap: stamp HTTP 404 (mirrors the native path).
1490
- status: renderStatus,
1491
- headers: flushSetCookie(verdict.headers),
1492
- routePath: renderFlat.fullPath,
1493
- shellId: renderFlat.shellId,
1494
- storeSnapshot,
1495
- // SSG fallback shells have zero islands on the page but the
1496
- // client-loader runtime still needs the importmap + bootstrap.
1497
- forceIslands: shellMode,
1498
- })
1499
- // renderBranchStreaming wrote via the chunk channel.
1500
- return 0
1501
- })
1519
+ const errorBoundary: ComponentType<{ error: Error }> =
1520
+ renderFlat.errorBoundary ??
1521
+ (({ error }) => createElement('div', null, `Internal Server Error: ${error.message}`))
1522
+ const storeSnapshot = collectSnapshot()
1523
+ await renderBranchStreaming({
1524
+ element,
1525
+ view: slotView,
1526
+ slot,
1527
+ workerId,
1528
+ napi,
1529
+ errorBoundary,
1530
+ // Catch-all (`path: '*'`) leaf rendered on an unmatched path OR a
1531
+ // React `notFound()` swap: stamp HTTP 404 (mirrors the native path).
1532
+ status: renderStatus,
1533
+ headers: flushSetCookie(verdict.headers),
1534
+ routePath: renderFlat.fullPath,
1535
+ shellId: renderFlat.shellId,
1536
+ storeSnapshot,
1537
+ // SSG fallback shells have zero islands on the page but the
1538
+ // client-loader runtime still needs the importmap + bootstrap.
1539
+ forceIslands: shellMode,
1540
+ })
1541
+ // renderBranchStreaming wrote via the chunk channel.
1542
+ return 0
1543
+ },
1544
+ // Seed nav with the REQUEST path (call.path), not renderFlat.fullPath: on
1545
+ // a React notFound() swap the catch-all renders, but the user is still at
1546
+ // the unmatched URL — and the client __navInit's from location.pathname
1547
+ // (= that URL), so both sides agree. nav = where you are, not what handled it.
1548
+ call.path,
1549
+ searchStringOf(call.req?.url),
1550
+ )
1502
1551
  }
1503
1552
  if (call.kind === 'navigation') {
1504
1553
  await navigationBranch(call, byRouteId, routes, slotView, encoder, opts.getWorkerId, slot)
@@ -1717,23 +1766,28 @@ async function navigationBranch(
1717
1766
  // document that already booted the bootstrap (the navigator IS the
1718
1767
  // bootstrap), and the takeover runtime imports its chunk itself.
1719
1768
  const renderFlatToHtml = (target: FlatRoute): Promise<string> =>
1720
- runInRequestContext(call.req?.cookies ?? {}, async () => {
1721
- const element = await buildRenderElement(
1722
- call as any,
1723
- target,
1724
- getWorkerId,
1725
- wantsSsgFallbackShell(target, call as any),
1726
- )
1727
- if (!element) throw new Error('render setup failed')
1728
- // Use renderToPipeableStream + onAllReady so pages with <Suspense> emit
1729
- // their RESOLVED markup, not the fallback. renderToString would only
1730
- // capture the shell navigating SPA-style to a Suspense-using route
1731
- // would otherwise ship "loading…" and never recover.
1732
- const html = await renderToAwaitedString(element)
1733
- store = collectSnapshot()
1734
- navHeaders = flushSetCookie(undefined)
1735
- return html
1736
- })
1769
+ runInRequestContext(
1770
+ call.req?.cookies ?? {},
1771
+ async () => {
1772
+ const element = await buildRenderElement(
1773
+ call as any,
1774
+ target,
1775
+ getWorkerId,
1776
+ wantsSsgFallbackShell(target, call as any),
1777
+ )
1778
+ if (!element) throw new Error('render setup failed')
1779
+ // Use renderToPipeableStream + onAllReady so pages with <Suspense> emit
1780
+ // their RESOLVED markup, not the fallback. renderToString would only
1781
+ // capture the shell — navigating SPA-style to a Suspense-using route
1782
+ // would otherwise ship "loading…" and never recover.
1783
+ const html = await renderToAwaitedString(element)
1784
+ store = collectSnapshot()
1785
+ navHeaders = flushSetCookie(undefined)
1786
+ return html
1787
+ },
1788
+ call.path,
1789
+ searchStringOf(call.req?.url),
1790
+ )
1737
1791
  try {
1738
1792
  fullHtml = await renderFlatToHtml(flat)
1739
1793
  } catch (err) {
@@ -1898,14 +1952,22 @@ async function renderNativeRouteToHtml(
1898
1952
  const manifest = loadIslandManifest(templateName)
1899
1953
  const compManifest = loadComponentManifest(templateName)
1900
1954
  if ((manifest && manifest.length > 0) || (compManifest && compManifest.length > 0)) {
1901
- const [islandExtra, componentExtra] = await Promise.all([
1902
- manifest && manifest.length > 0
1903
- ? resolveIslandContext(manifest, ctx, islandCache)
1904
- : Promise.resolve({} as Record<string, string>),
1905
- compManifest && compManifest.length > 0
1906
- ? resolveComponentContext(compManifest, ctx, templateName, undefined, islandCache)
1907
- : Promise.resolve({} as Record<string, string>),
1908
- ])
1955
+ // Seed the request path so island SSR sees the active route via useNav()
1956
+ // (same as the full-document native branch — this runs outside the loader
1957
+ // request scope opened above).
1958
+ const [islandExtra, componentExtra] = await runInNavContext(
1959
+ call.path,
1960
+ searchStringOf(call.req?.url),
1961
+ () =>
1962
+ Promise.all([
1963
+ manifest && manifest.length > 0
1964
+ ? resolveIslandContext(manifest, ctx, islandCache)
1965
+ : Promise.resolve({} as Record<string, string>),
1966
+ compManifest && compManifest.length > 0
1967
+ ? resolveComponentContext(compManifest, ctx, templateName, undefined, islandCache)
1968
+ : Promise.resolve({} as Record<string, string>),
1969
+ ]),
1970
+ )
1909
1971
  ctx = { ...ctx, ...islandExtra, ...componentExtra }
1910
1972
  }
1911
1973
 
@@ -0,0 +1,5 @@
1
+ /** Open a per-request nav scope so SSR — React islands AND native island SSR —
2
+ * sees the current request path via useNav()/getNavState() instead of the idle
3
+ * singleton default. `search` is the raw query string INCLUDING the leading '?'
4
+ * (so it matches the client's `location.search`), or '' when there is none. */
5
+ export declare function runInNavContext<T>(path: string, search: string, fn: () => T): T;
@@ -26,6 +26,12 @@ type ErrorCb = (e: {
26
26
  error: Error;
27
27
  }) => void;
28
28
  export declare const nav: NavStore;
29
+ export interface ServerNavScope {
30
+ path: string;
31
+ search: string;
32
+ _snap?: NavState;
33
+ }
34
+ export declare function __setServerNavResolver(fn: () => ServerNavScope | undefined): void;
29
35
  export declare function getNavState(): NavState;
30
36
  export declare function subscribe(cb: NavCb): () => void;
31
37
  export declare function onBeforeNavigate(cb: BeforeCb): () => void;
@@ -7,6 +7,13 @@ export declare function renderToAwaitedString(element: ReactNode): Promise<strin
7
7
  export interface RenderFragmentOpts {
8
8
  /** Request cookies visible to cookies()/session helpers inside the tree. */
9
9
  cookies?: Record<string, string>;
10
+ /** Seed `useNav()` / `getNavState()` for islands inside the fragment. Provide
11
+ * when rendering for a known URL (e.g. a server-side preview of a specific
12
+ * route) so active-nav resolves; omit for a context-free fragment, where
13
+ * `useNav()` reports the idle defaults (path ''). `search` is the raw query
14
+ * string including the leading '?'. */
15
+ path?: string;
16
+ search?: string;
10
17
  }
11
18
  /** Render a component to an HTML string with framework contexts scoped:
12
19
  * request scope (cookies) ∘ request cache (cachedFetch/dedupe) ∘ store