@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;AAmCH;;;;;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,CAwHnB;AAED,eAAe,SAAS,CAAC"}
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,CAkDzC;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"}
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.26",
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",
@@ -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 (useNodeStreams) {
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
  }
@@ -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);