@timber-js/app 0.2.0-alpha.14 → 0.2.0-alpha.15

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":"html-injectors.d.ts","sourceRoot":"","sources":["../../src/server/html-injectors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAiEH;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAClC,QAAQ,EAAE,MAAM,GACf,cAAc,CAAC,UAAU,CAAC,CAE5B;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAClC,WAAW,EAAE,MAAM,GAClB,cAAc,CAAC,UAAU,CAAC,CAE5B;AAkBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,GACpC,cAAc,CAAC,UAAU,CAAC,CA4B5B;AAyID;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,cAAc,CAAC,UAAU,CAAC,EACtC,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,SAAS,GAChD,cAAc,CAAC,UAAU,CAAC,CAS5B;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,qBAAqB;IACpC,sBAAsB,EAAE,MAAM,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;CACtB;AAqBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,aAAa,EAAE;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,cAAc,EAAE,OAAO,CAAA;KAAE,CAAC;IACjE,GAAG,EAAE,OAAO,CAAC;IACb,aAAa,CAAC,EAAE,OAAO,qBAAqB,EAAE,aAAa,CAAC;CAC7D,GAAG,qBAAqB,CA8DxB"}
1
+ {"version":3,"file":"html-injectors.d.ts","sourceRoot":"","sources":["../../src/server/html-injectors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAiEH;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAClC,QAAQ,EAAE,MAAM,GACf,cAAc,CAAC,UAAU,CAAC,CAE5B;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAClC,WAAW,EAAE,MAAM,GAClB,cAAc,CAAC,UAAU,CAAC,CAE5B;AAkBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,GACpC,cAAc,CAAC,UAAU,CAAC,CA4B5B;AA6JD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,cAAc,CAAC,UAAU,CAAC,EACtC,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,SAAS,GAChD,cAAc,CAAC,UAAU,CAAC,CAS5B;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,qBAAqB;IACpC,sBAAsB,EAAE,MAAM,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;CACtB;AAqBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,aAAa,EAAE;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,cAAc,EAAE,OAAO,CAAA;KAAE,CAAC;IACjE,GAAG,EAAE,OAAO,CAAC;IACb,aAAa,CAAC,EAAE,OAAO,qBAAqB,EAAE,aAAa,CAAC;CAC7D,GAAG,qBAAqB,CA8DxB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timber-js/app",
3
- "version": "0.2.0-alpha.14",
3
+ "version": "0.2.0-alpha.15",
4
4
  "description": "Vite-native React framework for Cloudflare Workers — correct HTTP semantics, real status codes, pages that work without JavaScript",
5
5
  "keywords": [
6
6
  "cloudflare-workers",
@@ -79,6 +79,11 @@
79
79
  "publishConfig": {
80
80
  "access": "public"
81
81
  },
82
+ "scripts": {
83
+ "build": "vite build --config vite.lib.config.ts && tsc --emitDeclarationOnly --project tsconfig.json --outDir dist",
84
+ "typecheck": "tsgo --noEmit",
85
+ "prepublishOnly": "pnpm run build"
86
+ },
82
87
  "dependencies": {
83
88
  "@opentelemetry/api": "^1.9.0",
84
89
  "@opentelemetry/context-async-hooks": "^2.6.0",
@@ -117,9 +122,5 @@
117
122
  },
118
123
  "engines": {
119
124
  "node": ">=20.0.0"
120
- },
121
- "scripts": {
122
- "build": "vite build --config vite.lib.config.ts && tsc --emitDeclarationOnly --project tsconfig.json --outDir dist",
123
- "typecheck": "tsgo --noEmit"
124
125
  }
125
- }
126
+ }
package/src/cli.ts CHANGED
File without changes
@@ -196,15 +196,25 @@ function createFlightInjectionTransform(
196
196
  // Once the suffix is stripped, all content is body-level and
197
197
  // scripts can safely be drained after any HTML chunk.
198
198
  let foundSuffix = false;
199
+ // Set to true in flush() — once all HTML chunks have been emitted,
200
+ // there's no need to yield between RSC reads. This eliminates
201
+ // ~36 macrotask yields per request (18 chunks × 2 yields each)
202
+ // that were the primary source of SSR overhead vs Next.js.
203
+ let htmlStreamFinished = false;
199
204
 
200
205
  // RSC script chunks waiting to be injected at the body level.
201
206
  const pending: Uint8Array[] = [];
202
207
 
203
208
  async function pullLoop(): Promise<void> {
204
- // Wait one macrotask so the HTML shell chunk flows through
205
- // transform() first. The browser needs the shell HTML before
206
- // RSC data script tags arrive.
207
- await new Promise<void>((r) => setTimeout(r, 0));
209
+ // Yield once so the first HTML shell chunk flows through
210
+ // transform() before we start reading RSC data. Uses
211
+ // setImmediate (check phase end of current event loop
212
+ // iteration) instead of setTimeout(0) (timer phase — next
213
+ // iteration). Under concurrency, setTimeout(0) yields to
214
+ // ALL pending timer callbacks from other requests, adding
215
+ // 1-4ms per yield. setImmediate fires before timers.
216
+ // Available on both Node.js and Cloudflare Workers.
217
+ await new Promise<void>((r) => setImmediate(r));
208
218
 
209
219
  try {
210
220
  for (;;) {
@@ -215,10 +225,12 @@ function createFlightInjectionTransform(
215
225
  }
216
226
  pending.push(value);
217
227
  // Yield between reads so HTML chunks get a chance to flow
218
- // through transform() first. RSC and HTML are driven by the
219
- // same source each RSC chunk typically produces a
220
- // corresponding HTML chunk from SSR.
221
- await new Promise<void>((r) => setTimeout(r, 0));
228
+ // through transform() first but only while HTML is still
229
+ // streaming. Once flush() fires (all HTML emitted), drain
230
+ // remaining RSC chunks without yielding.
231
+ if (!htmlStreamFinished) {
232
+ await new Promise<void>((r) => setImmediate(r));
233
+ }
222
234
  }
223
235
  } catch (err) {
224
236
  pullError = err;
@@ -240,7 +252,11 @@ function createFlightInjectionTransform(
240
252
 
241
253
  return new TransformStream<Uint8Array, Uint8Array>({
242
254
  transform(chunk, controller) {
243
- // Start pulling RSC scripts into the buffer (if not started)
255
+ // Pull-based start: don't begin reading RSC until the first
256
+ // HTML chunk flows through. This matches Next.js's approach
257
+ // and ensures the shell HTML is enqueued before any RSC
258
+ // script tags. Without this, the pull loop starts eagerly
259
+ // and may read RSC data before the browser has any HTML.
244
260
  if (!pullPromise) {
245
261
  pullPromise = pullLoop();
246
262
  }
@@ -274,7 +290,11 @@ function createFlightInjectionTransform(
274
290
  }
275
291
  },
276
292
  flush(controller) {
277
- // HTML stream is done drain remaining RSC chunks at body level
293
+ // All HTML chunks have been emitted. Signal the pull loop to
294
+ // stop yielding between RSC reads — no more HTML to interleave.
295
+ htmlStreamFinished = true;
296
+
297
+ // Drain remaining RSC chunks at body level
278
298
  const finish = () => {
279
299
  drainPending(controller);
280
300
  // Re-emit the suffix at the very end so HTML is well-formed
package/LICENSE DELETED
@@ -1,8 +0,0 @@
1
- DONTFUCKINGUSE LICENSE
2
-
3
- Copyright (c) 2025 Daniel Saewitz
4
-
5
- This software may not be used, copied, modified, merged, published,
6
- distributed, sublicensed, or sold by anyone other than the copyright holder.
7
-
8
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.