@timber-js/app 0.2.0-alpha.13 → 0.2.0-alpha.14
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/LICENSE +8 -0
- package/dist/_chunks/{debug-gwlJkDuf.js → debug-B3Gypr3D.js} +2 -2
- package/dist/_chunks/debug-B3Gypr3D.js.map +1 -0
- package/dist/_chunks/{format-DviM89f0.js → format-RyoGQL74.js} +2 -2
- package/dist/_chunks/{format-DviM89f0.js.map → format-RyoGQL74.js.map} +1 -1
- package/dist/_chunks/{request-context-DIkVh_jG.js → request-context-BQUC8PHn.js} +2 -2
- package/dist/_chunks/{request-context-DIkVh_jG.js.map → request-context-BQUC8PHn.js.map} +1 -1
- package/dist/cookies/index.js +1 -1
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/plugins/entries.d.ts.map +1 -1
- package/dist/server/debug.d.ts +1 -1
- package/dist/server/index.js +13 -13
- package/dist/server/index.js.map +1 -1
- package/dist/server/pipeline.d.ts +7 -4
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/package.json +6 -7
- package/src/cli.ts +0 -0
- package/src/index.ts +16 -0
- package/src/plugins/entries.ts +1 -0
- package/src/server/debug.ts +1 -1
- package/src/server/pipeline.ts +27 -20
- package/src/server/rsc-entry/index.ts +16 -1
- package/dist/_chunks/debug-gwlJkDuf.js.map +0 -1
|
@@ -63,12 +63,15 @@ export interface PipelineConfig {
|
|
|
63
63
|
*/
|
|
64
64
|
interceptionRewrites?: import('#/routing/interception.js').InterceptionRewrite[];
|
|
65
65
|
/**
|
|
66
|
-
*
|
|
67
|
-
* Only enable in dev mode — exposes internal timing data.
|
|
66
|
+
* Control Server-Timing header output.
|
|
68
67
|
*
|
|
69
|
-
*
|
|
68
|
+
* - `'detailed'` — per-phase breakdown (proxy, middleware, render).
|
|
69
|
+
* - `'total'` — single `total;dur=N` entry (production-safe).
|
|
70
|
+
* - `false` — no Server-Timing header at all.
|
|
71
|
+
*
|
|
72
|
+
* Default: `'total'`.
|
|
70
73
|
*/
|
|
71
|
-
|
|
74
|
+
serverTiming?: 'detailed' | 'total' | false;
|
|
72
75
|
/**
|
|
73
76
|
* Dev pipeline error callback — called when a pipeline phase (proxy,
|
|
74
77
|
* middleware, render) catches an unhandled error. Used to wire the error
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/server/pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAY,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAiB,KAAK,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAgC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAItD,sEAAsE;AACtE,MAAM,WAAW,UAAU;IACzB,mDAAmD;IACnD,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC1C,uDAAuD;IACvD,UAAU,CAAC,EAAE,YAAY,CAAC;CAC3B;AAED,6DAA6D;AAC7D,MAAM,MAAM,YAAY,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,UAAU,GAAG,IAAI,CAAC;AAEnE,sEAAsE;AACtE,MAAM,MAAM,oBAAoB,GAAG,CACjC,QAAQ,EAAE,MAAM,KACb,OAAO,oBAAoB,EAAE,kBAAkB,GAAG,IAAI,CAAC;AAE5D,iEAAiE;AACjE,MAAM,WAAW,mBAAmB;IAClC,iEAAiE;IACjE,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,6DAA6D;AAC7D,MAAM,MAAM,aAAa,GAAG,CAC1B,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,UAAU,EACjB,eAAe,EAAE,OAAO,EACxB,oBAAoB,EAAE,OAAO,EAC7B,YAAY,CAAC,EAAE,mBAAmB,KAC/B,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AAElC,+DAA+D;AAC/D,MAAM,MAAM,iBAAiB,GAAG,CAC9B,KAAK,EAAE,UAAU,EACjB,GAAG,EAAE,OAAO,EACZ,eAAe,EAAE,OAAO,KACrB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAI1B,MAAM,WAAW,cAAc;IAC7B,iFAAiF;IACjF,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,CAAC,CAAC;IACtD,qEAAqE;IACrE,UAAU,EAAE,YAAY,CAAC;IACzB,iGAAiG;IACjG,kBAAkB,CAAC,EAAE,oBAAoB,CAAC;IAC1C,kEAAkE;IAClE,MAAM,EAAE,aAAa,CAAC;IACtB,kEAAkE;IAClE,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,eAAe,EAAE,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IACzF,kFAAkF;IAClF,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,gFAAgF;IAChF,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,yGAAyG;IACzG,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,OAAO,2BAA2B,EAAE,mBAAmB,EAAE,CAAC;IACjF
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/server/pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAY,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAiB,KAAK,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAgC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAItD,sEAAsE;AACtE,MAAM,WAAW,UAAU;IACzB,mDAAmD;IACnD,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC1C,uDAAuD;IACvD,UAAU,CAAC,EAAE,YAAY,CAAC;CAC3B;AAED,6DAA6D;AAC7D,MAAM,MAAM,YAAY,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,UAAU,GAAG,IAAI,CAAC;AAEnE,sEAAsE;AACtE,MAAM,MAAM,oBAAoB,GAAG,CACjC,QAAQ,EAAE,MAAM,KACb,OAAO,oBAAoB,EAAE,kBAAkB,GAAG,IAAI,CAAC;AAE5D,iEAAiE;AACjE,MAAM,WAAW,mBAAmB;IAClC,iEAAiE;IACjE,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,6DAA6D;AAC7D,MAAM,MAAM,aAAa,GAAG,CAC1B,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,UAAU,EACjB,eAAe,EAAE,OAAO,EACxB,oBAAoB,EAAE,OAAO,EAC7B,YAAY,CAAC,EAAE,mBAAmB,KAC/B,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AAElC,+DAA+D;AAC/D,MAAM,MAAM,iBAAiB,GAAG,CAC9B,KAAK,EAAE,UAAU,EACjB,GAAG,EAAE,OAAO,EACZ,eAAe,EAAE,OAAO,KACrB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAI1B,MAAM,WAAW,cAAc;IAC7B,iFAAiF;IACjF,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,CAAC,CAAC;IACtD,qEAAqE;IACrE,UAAU,EAAE,YAAY,CAAC;IACzB,iGAAiG;IACjG,kBAAkB,CAAC,EAAE,oBAAoB,CAAC;IAC1C,kEAAkE;IAClE,MAAM,EAAE,aAAa,CAAC;IACtB,kEAAkE;IAClE,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,eAAe,EAAE,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IACzF,kFAAkF;IAClF,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,gFAAgF;IAChF,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,yGAAyG;IACzG,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,OAAO,2BAA2B,EAAE,mBAAmB,EAAE,CAAC;IACjF;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,UAAU,GAAG,OAAO,GAAG,KAAK,CAAC;IAC5C;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxD;;;;;;;;;;OAUG;IACH,mBAAmB,CAAC,EAAE,CACpB,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,OAAO,EACZ,eAAe,EAAE,OAAO,KACrB,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CACnC;AAID;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAuW1F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/index.ts"],"names":[],"mappings":"AA0FA;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAE/F;AAoZD,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAIzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;8BAtQ3C,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC;AAwQhD,wBAAiE"}
|
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.14",
|
|
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,11 +79,6 @@
|
|
|
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
|
-
},
|
|
87
82
|
"dependencies": {
|
|
88
83
|
"@opentelemetry/api": "^1.9.0",
|
|
89
84
|
"@opentelemetry/context-async-hooks": "^2.6.0",
|
|
@@ -122,5 +117,9 @@
|
|
|
122
117
|
},
|
|
123
118
|
"engines": {
|
|
124
119
|
"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"
|
|
125
124
|
}
|
|
126
|
-
}
|
|
125
|
+
}
|
package/src/cli.ts
CHANGED
|
File without changes
|
package/src/index.ts
CHANGED
|
@@ -107,6 +107,22 @@ export interface TimberUserConfig {
|
|
|
107
107
|
/** Array of signing secrets for key rotation. Index 0 signs; all verify. */
|
|
108
108
|
secrets?: string[];
|
|
109
109
|
};
|
|
110
|
+
/**
|
|
111
|
+
* Control Server-Timing header output.
|
|
112
|
+
*
|
|
113
|
+
* - `'detailed'` — per-phase breakdown (proxy, middleware, render). Useful
|
|
114
|
+
* for APM / network monitoring. Exposes phase names to clients.
|
|
115
|
+
* - `'total'` — single `total;dur=N` entry. Safe to expose, gives
|
|
116
|
+
* browser DevTools useful timing without internal details.
|
|
117
|
+
* - `false` — no Server-Timing header at all.
|
|
118
|
+
*
|
|
119
|
+
* Default: `'detailed'` in dev, `'total'` in production.
|
|
120
|
+
*
|
|
121
|
+
* This is separate from `debug` / `TIMBER_DEBUG` — it's an intentional
|
|
122
|
+
* decision to expose timing data to clients, not a side effect of debug
|
|
123
|
+
* logging.
|
|
124
|
+
*/
|
|
125
|
+
serverTiming?: 'detailed' | 'total' | false;
|
|
110
126
|
/**
|
|
111
127
|
* Override the app directory location. By default, timber auto-detects
|
|
112
128
|
* `app/` at the project root, falling back to `src/app/`.
|
package/src/plugins/entries.ts
CHANGED
package/src/server/debug.ts
CHANGED
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
*
|
|
49
49
|
* This is the ONLY function that should gate client-visible dev behavior:
|
|
50
50
|
* - Dev error pages with stack traces
|
|
51
|
-
* -
|
|
51
|
+
* - Server-Timing mode default (`'detailed'` in dev, `'total'` in prod)
|
|
52
52
|
* - Error messages in action `INTERNAL_ERROR` payloads
|
|
53
53
|
* - Pipeline error handler wiring (Vite overlay)
|
|
54
54
|
*
|
package/src/server/pipeline.ts
CHANGED
|
@@ -117,12 +117,15 @@ export interface PipelineConfig {
|
|
|
117
117
|
*/
|
|
118
118
|
interceptionRewrites?: import('#/routing/interception.js').InterceptionRewrite[];
|
|
119
119
|
/**
|
|
120
|
-
*
|
|
121
|
-
* Only enable in dev mode — exposes internal timing data.
|
|
120
|
+
* Control Server-Timing header output.
|
|
122
121
|
*
|
|
123
|
-
*
|
|
122
|
+
* - `'detailed'` — per-phase breakdown (proxy, middleware, render).
|
|
123
|
+
* - `'total'` — single `total;dur=N` entry (production-safe).
|
|
124
|
+
* - `false` — no Server-Timing header at all.
|
|
125
|
+
*
|
|
126
|
+
* Default: `'total'`.
|
|
124
127
|
*/
|
|
125
|
-
|
|
128
|
+
serverTiming?: 'detailed' | 'total' | false;
|
|
126
129
|
/**
|
|
127
130
|
* Dev pipeline error callback — called when a pipeline phase (proxy,
|
|
128
131
|
* middleware, render) catches an unhandled error. Used to wire the error
|
|
@@ -165,7 +168,7 @@ export function createPipeline(config: PipelineConfig): (req: Request) => Promis
|
|
|
165
168
|
earlyHints,
|
|
166
169
|
stripTrailingSlash = true,
|
|
167
170
|
slowRequestMs = 3000,
|
|
168
|
-
|
|
171
|
+
serverTiming = 'total',
|
|
169
172
|
onPipelineError,
|
|
170
173
|
} = config;
|
|
171
174
|
|
|
@@ -216,25 +219,25 @@ export function createPipeline(config: PipelineConfig): (req: Request) => Promis
|
|
|
216
219
|
// DevSpanProcessor reads this for tree/summary output.
|
|
217
220
|
await setSpanAttribute('http.response.status_code', result.status);
|
|
218
221
|
|
|
219
|
-
// Append Server-Timing header.
|
|
220
|
-
// In dev mode: detailed per-phase breakdown (proxy, middleware, render).
|
|
221
|
-
// In production: single total duration — safe to expose, no phase names.
|
|
222
|
+
// Append Server-Timing header based on configured mode.
|
|
222
223
|
// Response.redirect() creates immutable headers, so we must
|
|
223
224
|
// ensure mutability before writing Server-Timing.
|
|
224
|
-
if (
|
|
225
|
-
|
|
226
|
-
|
|
225
|
+
if (serverTiming === 'detailed') {
|
|
226
|
+
// Detailed: per-phase breakdown (proxy, middleware, render).
|
|
227
|
+
const timingHeader = getServerTimingHeader();
|
|
228
|
+
if (timingHeader) {
|
|
227
229
|
result = ensureMutableResponse(result);
|
|
228
|
-
result.headers.set('Server-Timing',
|
|
230
|
+
result.headers.set('Server-Timing', timingHeader);
|
|
229
231
|
}
|
|
230
|
-
} else {
|
|
231
|
-
//
|
|
232
|
-
//
|
|
233
|
-
//
|
|
232
|
+
} else if (serverTiming === 'total') {
|
|
233
|
+
// Total only: single `total;dur=N` — no phase names.
|
|
234
|
+
// Prevents information disclosure while giving browser
|
|
235
|
+
// DevTools useful timing data.
|
|
234
236
|
const totalMs = Math.round(performance.now() - startTime);
|
|
235
237
|
result = ensureMutableResponse(result);
|
|
236
238
|
result.headers.set('Server-Timing', `total;dur=${totalMs}`);
|
|
237
239
|
}
|
|
240
|
+
// serverTiming === false: no header at all
|
|
238
241
|
|
|
239
242
|
return result;
|
|
240
243
|
}
|
|
@@ -254,7 +257,7 @@ export function createPipeline(config: PipelineConfig): (req: Request) => Promis
|
|
|
254
257
|
return response;
|
|
255
258
|
};
|
|
256
259
|
|
|
257
|
-
return
|
|
260
|
+
return serverTiming === 'detailed' ? runWithTimingCollector(runRequest) : runRequest();
|
|
258
261
|
});
|
|
259
262
|
});
|
|
260
263
|
};
|
|
@@ -272,7 +275,7 @@ export function createPipeline(config: PipelineConfig): (req: Request) => Promis
|
|
|
272
275
|
}
|
|
273
276
|
const proxyFn = () => runProxy(proxyExport, req, () => handleRequest(req, method, path));
|
|
274
277
|
return await withSpan('timber.proxy', {}, () =>
|
|
275
|
-
|
|
278
|
+
serverTiming === 'detailed' ? withTiming('proxy', 'proxy.ts', proxyFn) : proxyFn()
|
|
276
279
|
);
|
|
277
280
|
} catch (error) {
|
|
278
281
|
// Uncaught proxy.ts error → bare HTTP 500
|
|
@@ -421,7 +424,9 @@ export function createPipeline(config: PipelineConfig): (req: Request) => Promis
|
|
|
421
424
|
setMutableCookieContext(true);
|
|
422
425
|
const middlewareFn = () => runMiddleware(match.middleware!, ctx);
|
|
423
426
|
const middlewareResponse = await withSpan('timber.middleware', {}, () =>
|
|
424
|
-
|
|
427
|
+
serverTiming === 'detailed'
|
|
428
|
+
? withTiming('mw', 'middleware.ts', middlewareFn)
|
|
429
|
+
: middlewareFn()
|
|
425
430
|
);
|
|
426
431
|
setMutableCookieContext(false);
|
|
427
432
|
if (middlewareResponse) {
|
|
@@ -476,7 +481,9 @@ export function createPipeline(config: PipelineConfig): (req: Request) => Promis
|
|
|
476
481
|
const renderFn = () =>
|
|
477
482
|
render(req, match, responseHeaders, requestHeaderOverlay, interception);
|
|
478
483
|
const response = await withSpan('timber.render', { 'http.route': canonicalPathname }, () =>
|
|
479
|
-
|
|
484
|
+
serverTiming === 'detailed'
|
|
485
|
+
? withTiming('render', 'RSC + SSR render', renderFn)
|
|
486
|
+
: renderFn()
|
|
480
487
|
);
|
|
481
488
|
markResponseFlushed();
|
|
482
489
|
return response;
|
|
@@ -69,6 +69,21 @@ import { renderSsrResponse } from './ssr-renderer.js';
|
|
|
69
69
|
import { callSsr } from './ssr-bridge.js';
|
|
70
70
|
import { isDebug, isDevMode, setDebugFromConfig } from '#/server/debug.js';
|
|
71
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Resolve the Server-Timing mode from timber.config.ts.
|
|
74
|
+
*
|
|
75
|
+
* If the user set `serverTiming` explicitly, use that value.
|
|
76
|
+
* Otherwise: `'detailed'` in dev, `'total'` in production.
|
|
77
|
+
*/
|
|
78
|
+
function resolveServerTimingMode(
|
|
79
|
+
config: Record<string, unknown>,
|
|
80
|
+
isDev: boolean
|
|
81
|
+
): 'detailed' | 'total' | false {
|
|
82
|
+
const userValue = config.serverTiming as 'detailed' | 'total' | false | undefined;
|
|
83
|
+
if (userValue !== undefined) return userValue;
|
|
84
|
+
return isDev ? 'detailed' : 'total';
|
|
85
|
+
}
|
|
86
|
+
|
|
72
87
|
// Dev-only pipeline error handler, set by the dev server after import.
|
|
73
88
|
// In production this is always undefined — no overhead.
|
|
74
89
|
let _devPipelineErrorHandler: ((error: Error, phase: string) => void) | undefined;
|
|
@@ -205,7 +220,7 @@ async function createRequestHandler(manifest: typeof routeManifest, runtimeConfi
|
|
|
205
220
|
// Slow request threshold from timber.config.ts. Default 3000ms, 0 to disable.
|
|
206
221
|
// See design/17-logging.md §"slowRequestMs"
|
|
207
222
|
slowRequestMs: (runtimeConfig as Record<string, unknown>).slowRequestMs as number | undefined,
|
|
208
|
-
|
|
223
|
+
serverTiming: resolveServerTimingMode(runtimeConfig, isDev),
|
|
209
224
|
onPipelineError: isDev
|
|
210
225
|
? (error: Error, phase: string) => {
|
|
211
226
|
if (_devPipelineErrorHandler) _devPipelineErrorHandler(error, phase);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"debug-gwlJkDuf.js","names":[],"sources":["../../src/server/debug.ts"],"sourcesContent":["/**\n * Runtime debug flag for timber.js.\n *\n * Two distinct functions for two distinct security levels:\n *\n * ## `isDebug()` — server-side logging only\n *\n * Returns true when timber's debug/warning messages should be written to\n * stderr / the server console. This NEVER affects what is sent to the\n * client (no error details, no timing headers, no stack traces).\n *\n * Active when any of:\n * - `NODE_ENV !== 'production'` (standard dev mode)\n * - `TIMBER_DEBUG` env var is set to a truthy value at runtime\n * - `timber.config.ts` has `debug: true`\n *\n * ## `isDevMode()` — client-visible dev behavior\n *\n * Returns true ONLY when `NODE_ENV !== 'production'`. This gates anything\n * that changes what clients can observe:\n * - Dev error pages with stack traces (fallback-error.ts)\n * - Detailed Server-Timing headers (pipeline.ts)\n * - Error messages in action INTERNAL_ERROR payloads (action-client.ts)\n * - Pipeline error handler wiring (Vite overlay)\n *\n * `isDevMode()` is statically replaced in production builds → the guarded\n * code is tree-shaken to zero bytes. TIMBER_DEBUG cannot enable it.\n *\n * Usage:\n * In Cloudflare Workers wrangler.toml:\n * [vars]\n * TIMBER_DEBUG = \"1\"\n *\n * In Node.js:\n * TIMBER_DEBUG=1 node server.js\n *\n * In timber.config.ts:\n * export default { debug: true }\n *\n * See design/13-security.md for the security taxonomy.\n * See design/18-build-system.md for build pipeline details.\n */\n\n// ─── Dev Mode (client-visible) ──────────────────────────────────────────────\n\n/**\n * Check if the application is running in development mode.\n *\n * This is the ONLY function that should gate client-visible dev behavior:\n * - Dev error pages with stack traces\n * - Detailed Server-Timing response headers\n * - Error messages in action `INTERNAL_ERROR` payloads\n * - Pipeline error handler wiring (Vite overlay)\n *\n * Returns `process.env.NODE_ENV !== 'production'`, which is statically\n * replaced by the bundler in production builds. Code guarded by this\n * function is tree-shaken to zero bytes in production.\n *\n * TIMBER_DEBUG does NOT enable this — that would leak server internals\n * to clients. Use `isDebug()` for server-side-only logging.\n */\nexport function isDevMode(): boolean {\n return process.env.NODE_ENV !== 'production';\n}\n\n// ─── Debug Flag (server-side logging only) ──────────────────────────────────\n\n/**\n * Config-level debug override. Set via `setDebugFromConfig()` during\n * initialization when timber.config.ts has `debug: true`.\n */\nlet _configDebug = false;\n\n/**\n * Set the debug flag from timber.config.ts.\n * Called during handler initialization.\n */\nexport function setDebugFromConfig(debug: boolean): void {\n _configDebug = debug;\n}\n\n/**\n * Check if timber debug logging is active (server-side only).\n *\n * Returns true if ANY of these conditions hold:\n * - NODE_ENV is not 'production' (standard dev mode)\n * - TIMBER_DEBUG environment variable is set to a truthy value at runtime\n * - timber.config.ts has `debug: true`\n *\n * This function controls ONLY server-side logging — messages written to\n * stderr or the server console. It NEVER affects client-visible behavior\n * (error pages, response headers, action payloads). For client-visible\n * behavior, use `isDevMode()`.\n *\n * The TIMBER_DEBUG check is deliberately written as a dynamic property\n * access so bundlers cannot statically replace it.\n */\nexport function isDebug(): boolean {\n // Fast path: dev mode (statically replaced to `true` in dev, `false` in prod)\n if (process.env.NODE_ENV !== 'production') return true;\n\n // Config override\n if (_configDebug) return true;\n\n // Runtime env var check — uses dynamic access to prevent static replacement.\n // In production builds, process.env.NODE_ENV is statically replaced, but\n // TIMBER_DEBUG must survive as a runtime check. The dynamic key access\n // pattern ensures the bundler treats this as opaque.\n return _readTimberDebugEnv();\n}\n\n/**\n * Read TIMBER_DEBUG from the environment at runtime.\n *\n * Extracted to a separate function to:\n * 1. Prevent bundler inlining (cross-module function calls are not inlined)\n * 2. Handle platforms where `process` may not exist (Cloudflare Workers)\n * 3. Support globalThis.__TIMBER_DEBUG for programmatic control\n */\nfunction _readTimberDebugEnv(): boolean {\n // globalThis override — useful for programmatic control and testing\n if ((globalThis as Record<string, unknown>).__TIMBER_DEBUG) return true;\n\n // process.env — works in Node.js and platforms that polyfill process\n try {\n const key = 'TIMBER_DEBUG';\n const val =\n typeof process !== 'undefined' && process.env\n ? (process.env as Record<string, string | undefined>)[key]\n : undefined;\n if (val && val !== '0' && val !== 'false') return true;\n } catch {\n // process may not exist or env may throw — safe to ignore\n }\n\n return false;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6DA,SAAgB,YAAqB;AACnC,QAAA,QAAA,IAAA,aAAgC;;;;;;AASlC,IAAI,eAAe;;;;;;;;;;;;;;;;;AA0BnB,SAAgB,UAAmB;AAEjC,KAAA,QAAA,IAAA,aAA6B,aAAc,QAAO;AAGlD,KAAI,aAAc,QAAO;AAMzB,QAAO,qBAAqB;;;;;;;;;;AAW9B,SAAS,sBAA+B;AAEtC,KAAK,WAAuC,eAAgB,QAAO;AAGnE,KAAI;EAEF,MAAM,MACJ,OAAO,YAAY,eAAe,QAAQ,MACrC,QAAQ,IAHH,kBAIN,KAAA;AACN,MAAI,OAAO,QAAQ,OAAO,QAAQ,QAAS,QAAO;SAC5C;AAIR,QAAO"}
|