@timber-js/app 0.2.0-alpha.26 → 0.2.0-alpha.28
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":"ssr-entry.d.ts","sourceRoot":"","sources":["../../src/server/ssr-entry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;
|
|
1
|
+
{"version":3,"file":"ssr-entry.d.ts","sourceRoot":"","sources":["../../src/server/ssr-entry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAiEH;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC1C,iCAAiC;IACjC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,eAAe,EAAE,OAAO,CAAC;IACzB,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,sBAAsB,EAAE,MAAM,CAAC;IAC/B,qEAAqE;IACrE,SAAS,CAAC,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;IACvC;;;0DAGsD;IACtD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;iFAE6E;IAC7E,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;4DACwD;IACxD,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,SAAS,CAC7B,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,EACrC,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,QAAQ,CAAC,CA6HnB;AAED,eAAe,SAAS,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr-render.d.ts","sourceRoot":"","sources":["../../src/server/ssr-render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAqEvC;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,SAAS,EAClB,OAAO,CAAC,EAAE;IAAE,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GAC7F,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAErC;AAED,wEAAwE;AACxE,eAAO,MAAM,cAAc,SAAkB,CAAC;AAU9C;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,SAAS,EAClB,OAAO,CAAC,EAAE;IAAE,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GAC7F,OAAO,CAAC,OAAO,aAAa,EAAE,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"ssr-render.d.ts","sourceRoot":"","sources":["../../src/server/ssr-render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAqEvC;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,SAAS,EAClB,OAAO,CAAC,EAAE;IAAE,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GAC7F,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAErC;AAED,wEAAwE;AACxE,eAAO,MAAM,cAAc,SAAkB,CAAC;AAU9C;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,SAAS,EAClB,OAAO,CAAC,EAAE;IAAE,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GAC7F,OAAO,CAAC,OAAO,aAAa,EAAE,QAAQ,CAAC,CAsDzC;AAED,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,OAAO,aAAa,EAAE,QAAQ,GACvC,cAAc,CAAC,UAAU,CAAC,CAE5B;AA0CD;;;;;;;;;;;;GAYG;AACH,2CAA2C;AAC3C,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAClC,MAAM,CAAC,EAAE,WAAW,GACnB,cAAc,CAAC,UAAU,CAAC,CA2B5B;AAWD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,cAAc,CAAC,UAAU,CAAC,EACtC,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,OAAO,GACvB,QAAQ,CASV"}
|
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.28",
|
|
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",
|
package/src/server/ssr-entry.ts
CHANGED
|
@@ -32,6 +32,36 @@ import { withSpan } from './tracing.js';
|
|
|
32
32
|
import { setCurrentParams } from '#/client/use-params.js';
|
|
33
33
|
import { registerSsrDataProvider, type SsrData } from '#/client/ssr-data.js';
|
|
34
34
|
|
|
35
|
+
// Pre-import Node.js stream modules at module load time — not per-request.
|
|
36
|
+
// Dynamic imports of node-stream-transforms and node:stream/promises were
|
|
37
|
+
// costing 3-17ms per request due to module resolution overhead.
|
|
38
|
+
let _nodeStreamImports: {
|
|
39
|
+
createNodeHeadInjector: typeof import('./node-stream-transforms.js').createNodeHeadInjector;
|
|
40
|
+
createNodeFlightInjector: typeof import('./node-stream-transforms.js').createNodeFlightInjector;
|
|
41
|
+
createNodeErrorHandler: typeof import('./node-stream-transforms.js').createNodeErrorHandler;
|
|
42
|
+
pipeline: typeof import('node:stream/promises').pipeline;
|
|
43
|
+
PassThrough: typeof import('node:stream').PassThrough;
|
|
44
|
+
} | null = null;
|
|
45
|
+
|
|
46
|
+
if (useNodeStreams) {
|
|
47
|
+
try {
|
|
48
|
+
const [transforms, streamPromises, stream] = await Promise.all([
|
|
49
|
+
import('./node-stream-transforms.js'),
|
|
50
|
+
import('node:stream/promises'),
|
|
51
|
+
import('node:stream'),
|
|
52
|
+
]);
|
|
53
|
+
_nodeStreamImports = {
|
|
54
|
+
createNodeHeadInjector: transforms.createNodeHeadInjector,
|
|
55
|
+
createNodeFlightInjector: transforms.createNodeFlightInjector,
|
|
56
|
+
createNodeErrorHandler: transforms.createNodeErrorHandler,
|
|
57
|
+
pipeline: streamPromises.pipeline,
|
|
58
|
+
PassThrough: stream.PassThrough,
|
|
59
|
+
};
|
|
60
|
+
} catch {
|
|
61
|
+
// Fall back to Web Streams path
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
35
65
|
// ─── SSR Data ALS ─────────────────────────────────────────────────────────
|
|
36
66
|
//
|
|
37
67
|
// Per-request SSR data stored in AsyncLocalStorage, ensuring correct
|
|
@@ -143,7 +173,9 @@ export async function handleSsr(
|
|
|
143
173
|
// createFromReadableStream resolves client component references
|
|
144
174
|
// (from "use client" modules) using the SSR environment's module
|
|
145
175
|
// map, importing the actual components for server-side rendering.
|
|
176
|
+
const _s0 = performance.now();
|
|
146
177
|
const element = createFromReadableStream(rscStream) as React.ReactNode;
|
|
178
|
+
const _s1 = performance.now();
|
|
147
179
|
|
|
148
180
|
// Wrap with a server-safe nuqs adapter so that 'use client' components
|
|
149
181
|
// that call nuqs hooks (useQueryStates, useQueryState) can SSR correctly.
|
|
@@ -151,6 +183,7 @@ export async function handleSsr(
|
|
|
151
183
|
// over after hydration. This provider supplies the request's search params
|
|
152
184
|
// as a static snapshot so nuqs renders the right initial values on the server.
|
|
153
185
|
const wrappedElement = withNuqsSsrAdapter(navContext.searchParams, element);
|
|
186
|
+
const _s2 = performance.now();
|
|
154
187
|
|
|
155
188
|
// Render to HTML stream (waits for onShellReady).
|
|
156
189
|
// Pass bootstrapScriptContent so React injects a non-deferred <script>
|
|
@@ -163,12 +196,11 @@ export async function handleSsr(
|
|
|
163
196
|
// Entire pipeline stays in C++ native streams until the Response boundary.
|
|
164
197
|
// - Workers: renderToReadableStream → Web TransformStream pipeline → Response
|
|
165
198
|
// Web Streams are V8-native C++ built-ins on Workers.
|
|
166
|
-
if (
|
|
199
|
+
if (_nodeStreamImports) {
|
|
167
200
|
// Node.js fast path: full pipeline in native streams
|
|
168
|
-
const { createNodeHeadInjector, createNodeFlightInjector, createNodeErrorHandler } =
|
|
169
|
-
await import('./node-stream-transforms.js');
|
|
170
|
-
const { pipeline } = await import('node:stream/promises');
|
|
201
|
+
const { createNodeHeadInjector, createNodeFlightInjector, createNodeErrorHandler, pipeline, PassThrough } = _nodeStreamImports;
|
|
171
202
|
|
|
203
|
+
const _s3 = performance.now();
|
|
172
204
|
let nodeHtmlStream: import('node:stream').Readable;
|
|
173
205
|
try {
|
|
174
206
|
nodeHtmlStream = await renderSsrNodeStream(wrappedElement, {
|
|
@@ -195,12 +227,15 @@ export async function handleSsr(
|
|
|
195
227
|
// Pipe through the chain. pipeline() handles backpressure and error propagation.
|
|
196
228
|
// The last stream in the chain is the output — convert to Web ReadableStream
|
|
197
229
|
// only at the Response boundary.
|
|
198
|
-
const { PassThrough } = await import('node:stream');
|
|
199
230
|
const output = new PassThrough();
|
|
200
231
|
pipeline(nodeHtmlStream, errorHandler, headInjector, flightInjector, output).catch(() => {
|
|
201
232
|
// Pipeline errors are handled by errorHandler transform
|
|
202
233
|
});
|
|
203
234
|
|
|
235
|
+
const _s4 = performance.now();
|
|
236
|
+
// eslint-disable-next-line no-console
|
|
237
|
+
console.log(`[ssr-perf] decode=${(_s1 - _s0).toFixed(1)}ms nuqs=${(_s2 - _s1).toFixed(1)}ms imports=${(_s3 - _s2).toFixed(1)}ms renderToPipeable=${(_s4 - _s3).toFixed(1)}ms pipeline=${(performance.now() - _s4).toFixed(1)}ms total=${(performance.now() - _s0).toFixed(1)}ms`);
|
|
238
|
+
|
|
204
239
|
const webStream = nodeReadableToWeb(output);
|
|
205
240
|
return buildSsrResponse(webStream, navContext.statusCode, navContext.responseHeaders);
|
|
206
241
|
}
|
package/src/server/ssr-render.ts
CHANGED
|
@@ -136,6 +136,7 @@ export async function renderSsrNodeStream(
|
|
|
136
136
|
const deferMs = options?.deferSuspenseFor;
|
|
137
137
|
|
|
138
138
|
return new Promise<import('node:stream').Readable>((resolve, reject) => {
|
|
139
|
+
const _startTime = performance.now();
|
|
139
140
|
const passthrough = new _PassThrough!();
|
|
140
141
|
|
|
141
142
|
let allReadyResolve: (() => void) | null = null;
|
|
@@ -148,6 +149,9 @@ export async function renderSsrNodeStream(
|
|
|
148
149
|
bootstrapScriptContent: options?.bootstrapScriptContent || undefined,
|
|
149
150
|
|
|
150
151
|
onShellReady() {
|
|
152
|
+
const _shellReady = performance.now();
|
|
153
|
+
// eslint-disable-next-line no-console
|
|
154
|
+
console.log(`[ssr-perf] onShellReady in ${(_shellReady - _startTime).toFixed(1)}ms`);
|
|
151
155
|
if (deferMs && deferMs > 0) {
|
|
152
156
|
Promise.race([allReady, new Promise<void>((r) => setTimeout(r, deferMs))]).then(() => {
|
|
153
157
|
pipe(passthrough);
|