@timber-js/app 0.2.0-alpha.43 → 0.2.0-alpha.44

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.
@@ -1 +1 @@
1
- {"version":3,"file":"rsc-stream.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/rsc-stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,EAAE,UAAU,EAAE,cAAc,EAAe,MAAM,wBAAwB,CAAC;AAGjF,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,cAAc,CAAC;AAItB;;;;;;;;;GASG;AACH,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,cAAc,EAAE,cAAc,GAAG,IAAI,CAAC;IACtC,WAAW,EAAE;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACvD,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,SAAS,CAAC;IAClD,OAAO,EAAE,aAAa,CAAC;IACvB,2EAA2E;IAC3E,kBAAkB,CAAC,EAAE,MAAM,mBAAmB,EAAE,CAAC;CAClD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,KAAK,CAAC,YAAY,EAAE,GAAG,EAAE,OAAO,GAAG,eAAe,CAyH1F"}
1
+ {"version":3,"file":"rsc-stream.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/rsc-stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,EAAE,UAAU,EAAE,cAAc,EAAe,MAAM,wBAAwB,CAAC;AAGjF,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,cAAc,CAAC;AAItB;;;;;;;;;GASG;AACH,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,cAAc,EAAE,cAAc,GAAG,IAAI,CAAC;IACtC,WAAW,EAAE;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACvD,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,SAAS,CAAC;IAClD,OAAO,EAAE,aAAa,CAAC;IACvB,2EAA2E;IAC3E,kBAAkB,CAAC,EAAE,MAAM,mBAAmB,EAAE,CAAC;CAClD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,KAAK,CAAC,YAAY,EAAE,GAAG,EAAE,OAAO,GAAG,eAAe,CAqI1F"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timber-js/app",
3
- "version": "0.2.0-alpha.43",
3
+ "version": "0.2.0-alpha.44",
4
4
  "description": "Vite-native React framework built for Servers and Serverless Platforms — correct HTTP semantics, real status codes, pages that work without JavaScript",
5
5
  "keywords": [
6
6
  "cloudflare-workers",
@@ -321,9 +321,23 @@ function bootstrap(runtimeConfig: typeof config): void {
321
321
  if (document.readyState === 'loading') {
322
322
  document.addEventListener('DOMContentLoaded', onDOMContentLoaded, false);
323
323
  } else {
324
- // DOM already parsed close after a microtask to ensure
325
- // any pending push() calls from inline scripts have executed.
326
- setTimeout(onDOMContentLoaded);
324
+ // DOM already parsed. All inline RSC <script> tags have already
325
+ // executed and pushed their data into the buffer. The buffer was
326
+ // flushed into the stream during start() above.
327
+ //
328
+ // Close via queueMicrotask rather than setTimeout. setTimeout
329
+ // defers to the next macrotask, which can race with React's
330
+ // Flight client read loop — if React finishes reading all queued
331
+ // chunks and issues a reader.read() that pends, the stream is
332
+ // NOT closed yet (setTimeout hasn't fired), so React sees an
333
+ // open stream and waits. Then setTimeout fires and closes it.
334
+ // This works in theory but some React Flight builds interpret
335
+ // a mid-read close as "Connection closed" rather than clean EOF.
336
+ // queueMicrotask fires at the end of the current microtask
337
+ // checkpoint — after start() and createFromReadableStream
338
+ // initialization but before any macrotask, giving React a
339
+ // consistent close signal. See TIM-524.
340
+ queueMicrotask(onDOMContentLoaded);
327
341
  }
328
342
 
329
343
  const element = createFromReadableStream(rscPayload);
@@ -138,11 +138,23 @@ export function renderRscStream(element: React.ReactElement, req: Request): RscS
138
138
  checkAndWarnRscPropError(error, new URL(req.url).pathname);
139
139
  }
140
140
 
141
- // Track unhandled errors for pre-flush handling (500 status)
142
- if (!signals.renderError) {
143
- signals.renderError = { error, status: 500 };
144
- }
141
+ // Log the error but do NOT track it as a page-level render error.
142
+ // If this error is inside a <Suspense> boundary, React will emit
143
+ // an error row in the Flight stream and the Suspense fallback will
144
+ // render on the client. Tracking it as signals.renderError would
145
+ // cause the pipeline to treat the entire page as a 500, even though
146
+ // the shell rendered successfully. See TIM-524.
147
+ //
148
+ // Only track as renderError if no Suspense boundary contains it —
149
+ // React will call onShellError for truly unrecoverable errors.
145
150
  logRenderError({ method: req.method, path: new URL(req.url).pathname, error });
151
+
152
+ // Return a digest so React emits a per-row error in the Flight
153
+ // stream instead of leaving the lazy reference unresolved.
154
+ return JSON.stringify({
155
+ type: 'error',
156
+ message: error instanceof Error ? error.message : String(error),
157
+ });
146
158
  },
147
159
  debugChannel,
148
160
  },