@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.
- package/dist/server/html-injectors.d.ts.map +1 -1
- package/package.json +7 -6
- package/src/cli.ts +0 -0
- package/src/server/html-injectors.ts +30 -10
- package/LICENSE +0 -8
|
@@ -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;
|
|
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.
|
|
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
|
-
//
|
|
205
|
-
// transform()
|
|
206
|
-
//
|
|
207
|
-
|
|
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
|
|
219
|
-
//
|
|
220
|
-
//
|
|
221
|
-
|
|
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
|
-
//
|
|
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
|
|
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.
|