@rangojs/router 0.0.0-experimental.129 → 0.0.0-experimental.130
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/vite/index.js +1 -1
- package/package.json +1 -1
- package/src/cloudflare/tracing.ts +7 -10
- package/src/router/instrument.ts +25 -199
- package/src/router/middleware.ts +3 -3
- package/src/router/tracing.ts +9 -12
- package/src/rsc/handler.ts +6 -13
- package/src/rsc/rsc-rendering.ts +3 -3
- package/src/rsc/server-action.ts +2 -2
- package/src/server/request-context.ts +0 -10
package/dist/vite/index.js
CHANGED
|
@@ -2133,7 +2133,7 @@ import { resolve } from "node:path";
|
|
|
2133
2133
|
// package.json
|
|
2134
2134
|
var package_default = {
|
|
2135
2135
|
name: "@rangojs/router",
|
|
2136
|
-
version: "0.0.0-experimental.
|
|
2136
|
+
version: "0.0.0-experimental.130",
|
|
2137
2137
|
description: "Django-inspired RSC router with composable URL patterns",
|
|
2138
2138
|
keywords: [
|
|
2139
2139
|
"react",
|
package/package.json
CHANGED
|
@@ -26,16 +26,13 @@
|
|
|
26
26
|
* recorded is governed by the `observability`/tracing block in wrangler config.
|
|
27
27
|
*
|
|
28
28
|
* Span duration note: enterSpan ends a span when its callback's returned value
|
|
29
|
-
* (or promise) settles.
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* the middleware own-time, handler:total) stay construction-bound — they ship in
|
|
37
|
-
* the Server-Timing header, flushed before drain — so a span reads at least as
|
|
38
|
-
* long as its same-named metric, the difference being post-construction streaming.
|
|
29
|
+
* (or promise) settles. For the streaming phases (request/render/ssr) that is at
|
|
30
|
+
* stream CONSTRUCTION, not body-drain. Instrumentation is best-effort and never
|
|
31
|
+
* wraps or buffers the response body, so it cannot regress streaming or latency.
|
|
32
|
+
* A loader/Suspense child that resolves mid-stream therefore keeps a rango.loader
|
|
33
|
+
* span that can extend past its render parent — overlapping spans are valid. Uses
|
|
34
|
+
* only the typed enterSpan API; spans bound work up to stream-handoff, matching
|
|
35
|
+
* the co-emitted perf metric.
|
|
39
36
|
*/
|
|
40
37
|
|
|
41
38
|
import { _getRequestContext } from "../server/request-context.js";
|
package/src/router/instrument.ts
CHANGED
|
@@ -42,9 +42,7 @@ import {
|
|
|
42
42
|
runThenSettle,
|
|
43
43
|
type TracePhase,
|
|
44
44
|
type TraceSpan,
|
|
45
|
-
type ResolvedTracing,
|
|
46
45
|
} from "./tracing.js";
|
|
47
|
-
import { isWebSocketUpgradeResponse } from "../response-utils.js";
|
|
48
46
|
|
|
49
47
|
/**
|
|
50
48
|
* Perf-metric boundary for a phase, or `false` for span-only. `false` means the
|
|
@@ -208,11 +206,15 @@ function recordPhaseMetric(
|
|
|
208
206
|
* unchanged and thrown errors / rejected promises propagate unchanged. When fn
|
|
209
207
|
* returns a promise both the metric duration and the span end when it settles.
|
|
210
208
|
*
|
|
211
|
-
* This is the
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
*
|
|
209
|
+
* This is the ONLY phase primitive: every phase (request/middleware/action/
|
|
210
|
+
* loader/handler/render/ssr) is construction-bound — the span and metric settle
|
|
211
|
+
* when fn's own work completes (for the streaming phases, when the RSC/HTML
|
|
212
|
+
* stream is constructed, NOT when the body drains). Instrumentation is strictly
|
|
213
|
+
* best-effort: it never wraps or buffers the response and adds no work on the
|
|
214
|
+
* streaming path, so it cannot regress response latency or streaming. A loader
|
|
215
|
+
* that resolves while the body streams therefore keeps a rango.loader span that
|
|
216
|
+
* may extend past its render parent — overlapping spans are valid; the loader
|
|
217
|
+
* really did take that long.
|
|
216
218
|
*
|
|
217
219
|
* Reads the metrics store + tracing off the RequestContext ALS, which is active
|
|
218
220
|
* for the WHOLE request — contrast observeEvent, which reads the RouterContext
|
|
@@ -231,12 +233,25 @@ export function observePhase<T>(
|
|
|
231
233
|
|
|
232
234
|
// Attributes only land on a real span, so skip the wrapper when only the perf
|
|
233
235
|
// surface is active (traceSpan would apply them to NOOP_TRACE_SPAN for nothing).
|
|
236
|
+
// `lazyAttributes` resolve AFTER fn runs (e.g. rango.route, known post-match).
|
|
234
237
|
const attributes = spec.attributes;
|
|
238
|
+
const lazy = spec.lazyAttributes;
|
|
235
239
|
const wrapped: (span: TraceSpan) => T =
|
|
236
|
-
attributes && tracing
|
|
240
|
+
(attributes || lazy) && tracing
|
|
237
241
|
? (span) => {
|
|
238
|
-
applyAttributes(span, attributes);
|
|
239
|
-
|
|
242
|
+
if (attributes) applyAttributes(span, attributes);
|
|
243
|
+
const out = fn(span);
|
|
244
|
+
if (!lazy) return out;
|
|
245
|
+
if (out instanceof Promise) {
|
|
246
|
+
return out.then((value) => {
|
|
247
|
+
const late = lazy();
|
|
248
|
+
if (late) applyAttributes(span, late);
|
|
249
|
+
return value;
|
|
250
|
+
}) as T;
|
|
251
|
+
}
|
|
252
|
+
const late = lazy();
|
|
253
|
+
if (late) applyAttributes(span, late);
|
|
254
|
+
return out;
|
|
240
255
|
}
|
|
241
256
|
: fn;
|
|
242
257
|
|
|
@@ -254,195 +269,6 @@ export function observePhase<T>(
|
|
|
254
269
|
return runThenSettle(runSpan, () => recordPhaseMetric(store, metric, start));
|
|
255
270
|
}
|
|
256
271
|
|
|
257
|
-
/**
|
|
258
|
-
* Re-stream `response`'s body through a pass-through that fires `onDrain` exactly
|
|
259
|
-
* once when the body finishes — on natural end, a stream error, or a client
|
|
260
|
-
* cancel (so a span can never leak on an aborted response). A bodyless response
|
|
261
|
-
* fires immediately. Only used while instrumentation is active, so the per-chunk
|
|
262
|
-
* relay cost never touches an untraced request.
|
|
263
|
-
*/
|
|
264
|
-
function instrumentResponseDrain(
|
|
265
|
-
response: Response,
|
|
266
|
-
onDrain: () => void,
|
|
267
|
-
): Response {
|
|
268
|
-
// WS-upgrade responses (status 101 / workerd `webSocket` property) must never
|
|
269
|
-
// be reconstructed: `new Response(body, { status: 101 })` throws and a copy
|
|
270
|
-
// drops the non-standard webSocket handoff (the invariant every other Response
|
|
271
|
-
// reconstruction site honors). A bodyless response has nothing to drain.
|
|
272
|
-
const source = response.body;
|
|
273
|
-
if (!source || isWebSocketUpgradeResponse(response)) {
|
|
274
|
-
onDrain();
|
|
275
|
-
return response;
|
|
276
|
-
}
|
|
277
|
-
let fired = false;
|
|
278
|
-
const fire = (): void => {
|
|
279
|
-
if (fired) return;
|
|
280
|
-
fired = true;
|
|
281
|
-
onDrain();
|
|
282
|
-
};
|
|
283
|
-
const reader = source.getReader();
|
|
284
|
-
const wrapped = new ReadableStream<Uint8Array>({
|
|
285
|
-
async pull(controller) {
|
|
286
|
-
try {
|
|
287
|
-
const { done, value } = await reader.read();
|
|
288
|
-
if (done) {
|
|
289
|
-
controller.close();
|
|
290
|
-
fire();
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
controller.enqueue(value);
|
|
294
|
-
} catch (error) {
|
|
295
|
-
controller.error(error);
|
|
296
|
-
fire();
|
|
297
|
-
}
|
|
298
|
-
},
|
|
299
|
-
cancel(reason) {
|
|
300
|
-
fire();
|
|
301
|
-
return reader.cancel(reason);
|
|
302
|
-
},
|
|
303
|
-
});
|
|
304
|
-
return new Response(wrapped, response);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
/**
|
|
308
|
-
* Shared engine for the streaming phases (request, middleware, render, ssr). It
|
|
309
|
-
* opens the span, runs fn, records the phase's perf metric at CONSTRUCTION (so it
|
|
310
|
-
* still reaches the Server-Timing header / [RSC Perf] table, both built before
|
|
311
|
-
* the body drains), hands the constructed value to the caller via a side channel
|
|
312
|
-
* (streaming preserved), then holds the span open until `drain` resolves. The
|
|
313
|
-
* SPAN therefore ends at body-drain — keeping the trace tree valid (a loader
|
|
314
|
-
* child that resolves mid-stream ends before its parent) — while the perf metric
|
|
315
|
-
* stays the construction work-time. `onDeliver` lets the request phase instrument
|
|
316
|
-
* the final body before handing it back; `onError` lets it release the barrier on
|
|
317
|
-
* failure. Fire-and-forget: the value reaches the caller via the returned
|
|
318
|
-
* promise, so the span promise's rejection is swallowed (already surfaced there).
|
|
319
|
-
*/
|
|
320
|
-
function runDrainBoundPhase<R>(
|
|
321
|
-
spec: PhaseSpec,
|
|
322
|
-
fn: (span: TraceSpan) => R | Promise<R>,
|
|
323
|
-
tracing: ResolvedTracing | undefined,
|
|
324
|
-
store: MetricsStore | undefined,
|
|
325
|
-
drain: Promise<void>,
|
|
326
|
-
onDeliver: (value: R) => R,
|
|
327
|
-
onError?: () => void,
|
|
328
|
-
): Promise<R> {
|
|
329
|
-
let deliver!: (value: R) => void;
|
|
330
|
-
let reject!: (error: unknown) => void;
|
|
331
|
-
const delivered = new Promise<R>((res, rej) => {
|
|
332
|
-
deliver = res;
|
|
333
|
-
reject = rej;
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
const start = performance.now();
|
|
337
|
-
const attributes = spec.attributes;
|
|
338
|
-
const metric = spec.metric;
|
|
339
|
-
const record = (): void => {
|
|
340
|
-
if (store && metric !== false) recordPhaseMetric(store, metric, start);
|
|
341
|
-
};
|
|
342
|
-
const spanCallback = async (span: TraceSpan): Promise<void> => {
|
|
343
|
-
if (attributes && tracing) applyAttributes(span, attributes);
|
|
344
|
-
let value: R;
|
|
345
|
-
try {
|
|
346
|
-
value = await fn(span);
|
|
347
|
-
} catch (error) {
|
|
348
|
-
record(); // a failed phase still shows its (construction) timing
|
|
349
|
-
onError?.();
|
|
350
|
-
reject(error);
|
|
351
|
-
throw error; // settle the span with the error, at construction
|
|
352
|
-
}
|
|
353
|
-
// Late attributes (e.g. rango.route) — resolved now that the work has run,
|
|
354
|
-
// so they can read state like the matched route name that match sets midway.
|
|
355
|
-
const lazy =
|
|
356
|
-
tracing && spec.lazyAttributes ? spec.lazyAttributes() : undefined;
|
|
357
|
-
if (lazy) applyAttributes(span, lazy);
|
|
358
|
-
record(); // construction-bound metric, before the response/header is built
|
|
359
|
-
deliver(onDeliver(value));
|
|
360
|
-
await drain; // hold the span open until the response body drains
|
|
361
|
-
};
|
|
362
|
-
|
|
363
|
-
traceSpan(tracing, spec.tracePhase, spec.spanName, spanCallback).catch(
|
|
364
|
-
() => {},
|
|
365
|
-
);
|
|
366
|
-
return delivered;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* The request phase (rango.request, metric:false). Owns the drain barrier: it
|
|
371
|
-
* runs fn to construct the final Response, instruments that Response's body so
|
|
372
|
-
* the barrier resolves at drain, hands the Response to the caller immediately
|
|
373
|
-
* (streaming preserved), and holds the span open until the body drains. Every
|
|
374
|
-
* streaming inner phase awaits the same barrier (via observeStreamingPhase), so
|
|
375
|
-
* the request/middleware/render/ssr chain ends at body-drain together and the
|
|
376
|
-
* trace tree is valid (no child span outlives its parent). The perf metrics
|
|
377
|
-
* (render:total, …) are recorded at construction so they still reach the
|
|
378
|
-
* Server-Timing header; only the SPANS are drain-bound. ctx.waitUntil holds the
|
|
379
|
-
* worker alive until drain so the span end runs. Pass-through when no surface is
|
|
380
|
-
* active.
|
|
381
|
-
*/
|
|
382
|
-
export function observeRequestPhase(
|
|
383
|
-
spec: PhaseSpec,
|
|
384
|
-
fn: (span: TraceSpan) => Promise<Response>,
|
|
385
|
-
): Promise<Response> {
|
|
386
|
-
const reqCtx = _getRequestContext();
|
|
387
|
-
const store = reqCtx?._metricsStore;
|
|
388
|
-
const tracing = reqCtx?._tracing;
|
|
389
|
-
|
|
390
|
-
if ((!store && !tracing) || !reqCtx) return fn(NOOP_TRACE_SPAN);
|
|
391
|
-
|
|
392
|
-
let resolveDrain!: () => void;
|
|
393
|
-
const finalDrain = new Promise<void>((resolve) => {
|
|
394
|
-
resolveDrain = resolve;
|
|
395
|
-
});
|
|
396
|
-
reqCtx._finalDrain = finalDrain;
|
|
397
|
-
|
|
398
|
-
// Keep the worker alive until the body drains, so the drain-bound span end
|
|
399
|
-
// (and the inner phases' settle) runs before the runtime can reclaim it.
|
|
400
|
-
const ec = reqCtx.executionContext;
|
|
401
|
-
if (typeof ec?.waitUntil === "function") ec.waitUntil(finalDrain);
|
|
402
|
-
|
|
403
|
-
return runDrainBoundPhase<Response>(
|
|
404
|
-
spec,
|
|
405
|
-
fn,
|
|
406
|
-
tracing,
|
|
407
|
-
store,
|
|
408
|
-
finalDrain,
|
|
409
|
-
(response) => instrumentResponseDrain(response, resolveDrain),
|
|
410
|
-
resolveDrain, // release the barrier if fn fails before constructing a body
|
|
411
|
-
);
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
/**
|
|
415
|
-
* A streaming inner phase (rango.middleware / render / ssr). Its SPAN settles
|
|
416
|
-
* when the request's final response body drains (the barrier owned by
|
|
417
|
-
* observeRequestPhase), not when fn returns the constructed stream — so
|
|
418
|
-
* loader/Suspense children that resolve mid-stream nest under a still-open
|
|
419
|
-
* parent. fn's result is delivered at construction (streaming preserved) and the
|
|
420
|
-
* perf metric is recorded at construction (Server-Timing parity). Falls back to
|
|
421
|
-
* observePhase (construction-bound span) when there is no barrier — a
|
|
422
|
-
* non-streaming request, or instrumentation off.
|
|
423
|
-
*/
|
|
424
|
-
export function observeStreamingPhase<R>(
|
|
425
|
-
spec: PhaseSpec,
|
|
426
|
-
fn: (span: TraceSpan) => R | Promise<R>,
|
|
427
|
-
): Promise<R> {
|
|
428
|
-
const reqCtx = _getRequestContext();
|
|
429
|
-
const store = reqCtx?._metricsStore;
|
|
430
|
-
const tracing = reqCtx?._tracing;
|
|
431
|
-
const finalDrain = reqCtx?._finalDrain;
|
|
432
|
-
|
|
433
|
-
if ((!store && !tracing) || !finalDrain) {
|
|
434
|
-
return Promise.resolve(observePhase(spec, fn));
|
|
435
|
-
}
|
|
436
|
-
return runDrainBoundPhase<R>(
|
|
437
|
-
spec,
|
|
438
|
-
fn,
|
|
439
|
-
tracing,
|
|
440
|
-
store,
|
|
441
|
-
finalDrain,
|
|
442
|
-
(value) => value,
|
|
443
|
-
);
|
|
444
|
-
}
|
|
445
|
-
|
|
446
272
|
/**
|
|
447
273
|
* Emit one discrete telemetry event (the event-shaped counterpart to
|
|
448
274
|
* observePhase). Resolves the sink from the active router context and stamps the
|
package/src/router/middleware.ts
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
} from "../redirect-origin.js";
|
|
20
20
|
import { isAutoGeneratedRouteName } from "../route-name.js";
|
|
21
21
|
import { appendMetric, createMetricsStore } from "./metrics.js";
|
|
22
|
-
import {
|
|
22
|
+
import { observePhase, PHASES } from "./instrument.js";
|
|
23
23
|
import { stripInternalParams } from "./handler-context.js";
|
|
24
24
|
import { isWebSocketUpgradeResponse } from "../response-utils.js";
|
|
25
25
|
|
|
@@ -487,7 +487,7 @@ export async function executeMiddleware<TEnv>(
|
|
|
487
487
|
// when neither surface is active.
|
|
488
488
|
let result: Response | void;
|
|
489
489
|
try {
|
|
490
|
-
result = await
|
|
490
|
+
result = await observePhase(PHASES.middleware(metricLabel), () =>
|
|
491
491
|
entry.handler(ctx, wrappedNext),
|
|
492
492
|
);
|
|
493
493
|
} catch (error) {
|
|
@@ -664,7 +664,7 @@ export async function executeInterceptMiddleware<TEnv>(
|
|
|
664
664
|
|
|
665
665
|
let result: Response | void;
|
|
666
666
|
try {
|
|
667
|
-
result = await
|
|
667
|
+
result = await observePhase(PHASES.middleware(label), () =>
|
|
668
668
|
middleware(ctx, guardedNext),
|
|
669
669
|
);
|
|
670
670
|
} catch (error) {
|
package/src/router/tracing.ts
CHANGED
|
@@ -31,18 +31,15 @@
|
|
|
31
31
|
* track() at the call site), rango.render (render:total:<route>; normal AND
|
|
32
32
|
* action-revalidation renders), rango.ssr (ssr:render-html).
|
|
33
33
|
*
|
|
34
|
-
*
|
|
35
|
-
* promise) settles.
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* valid
|
|
42
|
-
*
|
|
43
|
-
* still recorded at construction — it ships in the Server-Timing header, flushed
|
|
44
|
-
* before the body drains — so a streaming span legitimately reads longer than its
|
|
45
|
-
* same-named metric by the time the body spent streaming after construction.
|
|
34
|
+
* Span-duration caveat (best-effort, never buffers): a span ends when its
|
|
35
|
+
* callback's value (or promise) settles. For the streaming phases (request,
|
|
36
|
+
* render, ssr) that is when the Response / HTML / RSC stream is CONSTRUCTED, not
|
|
37
|
+
* when the body finishes draining — instrumentation never wraps or buffers the
|
|
38
|
+
* response body, so it cannot regress response latency or streaming. A loader /
|
|
39
|
+
* Suspense child that resolves while the body streams therefore keeps a
|
|
40
|
+
* rango.loader span that can extend past its render parent; overlapping spans are
|
|
41
|
+
* valid (the loader really did take that long). Phase spans bound the work up to
|
|
42
|
+
* stream-handoff, which is also what the co-emitted perf metric measures.
|
|
46
43
|
*
|
|
47
44
|
* Both shipped runners (Cloudflare, OTel) keep the core agnostic: the
|
|
48
45
|
* platform-specific bridge lives at the edge behind the SpanRunner contract.
|
package/src/rsc/handler.ts
CHANGED
|
@@ -82,12 +82,7 @@ import {
|
|
|
82
82
|
appendMetric,
|
|
83
83
|
buildMetricsTiming,
|
|
84
84
|
} from "../router/metrics.js";
|
|
85
|
-
import {
|
|
86
|
-
observePhase,
|
|
87
|
-
observeRequestPhase,
|
|
88
|
-
observeEvent,
|
|
89
|
-
PHASES,
|
|
90
|
-
} from "../router/instrument.js";
|
|
85
|
+
import { observePhase, observeEvent, PHASES } from "../router/instrument.js";
|
|
91
86
|
import {
|
|
92
87
|
startSSRSetup,
|
|
93
88
|
getSSRSetup,
|
|
@@ -501,14 +496,12 @@ export function createRSCHandler<
|
|
|
501
496
|
// The "rango.request" span is opened inside the request context so the
|
|
502
497
|
// Cloudflare runner can read executionContext.tracing, and so every nested
|
|
503
498
|
// phase span (and the platform's automatic KV/D1/fetch spans) nests under
|
|
504
|
-
// it.
|
|
505
|
-
//
|
|
506
|
-
//
|
|
507
|
-
// is
|
|
508
|
-
// timings) and stays construction-bound (it ships as a Server-Timing header,
|
|
509
|
-
// flushed before drain). When no surface is active this is a pass-through.
|
|
499
|
+
// it. Construction-bound: the span ends when the Response is built, never
|
|
500
|
+
// wrapping the streamed body. metric:false — handler:total is metered
|
|
501
|
+
// directly below (a grand total incl. the pre-context bootstrap timings).
|
|
502
|
+
// When tracing is off this is a direct pass-through.
|
|
510
503
|
return runWithRequestContext(requestContext, () =>
|
|
511
|
-
|
|
504
|
+
observePhase(PHASES.request, async (span) => {
|
|
512
505
|
span.setAttribute("http.method", request.method);
|
|
513
506
|
// The matched route template is not known until match() runs later, so
|
|
514
507
|
// emit the concrete path as url.path (low-level), NOT http.route — the
|
package/src/rsc/rsc-rendering.ts
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
setRequestContextParams,
|
|
12
12
|
} from "../server/request-context.js";
|
|
13
13
|
import { appendMetric } from "../router/metrics.js";
|
|
14
|
-
import {
|
|
14
|
+
import { observePhase, PHASES } from "../router/instrument.js";
|
|
15
15
|
import { getSSRSetup, isRscRequest } from "./ssr-setup.js";
|
|
16
16
|
import type { RscPayload } from "./types.js";
|
|
17
17
|
import type { MatchResult } from "../types.js";
|
|
@@ -36,7 +36,7 @@ export function handleRscRendering<TEnv>(
|
|
|
36
36
|
// same boundary (match -> serialize -> SSR), so the two surfaces agree.
|
|
37
37
|
// Loaders kicked off during matching nest under the span; the SSR HTML pass
|
|
38
38
|
// below opens "rango.ssr" the same way.
|
|
39
|
-
return
|
|
39
|
+
return observePhase(PHASES.render, () =>
|
|
40
40
|
handleRscRenderingInner(
|
|
41
41
|
ctx,
|
|
42
42
|
request,
|
|
@@ -248,7 +248,7 @@ async function handleRscRenderingInner<TEnv>(
|
|
|
248
248
|
|
|
249
249
|
// ssr-render-html metric + rango.ssr span from one boundary. render:total is
|
|
250
250
|
// recorded by the observePhase wrapper around this function.
|
|
251
|
-
const htmlStream = await
|
|
251
|
+
const htmlStream = await observePhase(PHASES.ssr, () =>
|
|
252
252
|
ssrModule.renderHTML(rscStream, {
|
|
253
253
|
nonce,
|
|
254
254
|
streamMode,
|
package/src/rsc/server-action.ts
CHANGED
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
setRequestContextParams,
|
|
21
21
|
} from "../server/request-context.js";
|
|
22
22
|
import { appendMetric } from "../router/metrics.js";
|
|
23
|
-
import {
|
|
23
|
+
import { observePhase, PHASES } from "../router/instrument.js";
|
|
24
24
|
import type { RscPayload } from "./types.js";
|
|
25
25
|
import {
|
|
26
26
|
hasBodyContent,
|
|
@@ -270,7 +270,7 @@ export function revalidateAfterAction<TEnv>(
|
|
|
270
270
|
// "render:total" AND opens "rango.render" from one boundary covering
|
|
271
271
|
// matchPartial -> serialize, so the revalidation loaders' rango.loader spans
|
|
272
272
|
// nest under a rango.render parent instead of dangling at the request root.
|
|
273
|
-
return
|
|
273
|
+
return observePhase(PHASES.render, () =>
|
|
274
274
|
revalidateAfterActionInner(
|
|
275
275
|
ctx,
|
|
276
276
|
request,
|
|
@@ -368,16 +368,6 @@ export interface RequestContext<
|
|
|
368
368
|
/** @internal Resolved platform phase-span tracing for this request (Cloudflare or OTel) */
|
|
369
369
|
_tracing?: ResolvedTracing;
|
|
370
370
|
|
|
371
|
-
/**
|
|
372
|
-
* @internal Drain barrier for streaming phase spans. The request phase
|
|
373
|
-
* (observeRequestPhase) sets this to a promise that resolves when the final
|
|
374
|
-
* response body finishes draining; the streaming inner phases
|
|
375
|
-
* (observeStreamingPhase: middleware/render/ssr) await it so their span AND
|
|
376
|
-
* perf metric end at body-drain rather than at stream construction. Undefined
|
|
377
|
-
* when neither the perf store nor tracing is active (no instrumentation).
|
|
378
|
-
*/
|
|
379
|
-
_finalDrain?: Promise<void>;
|
|
380
|
-
|
|
381
371
|
/** @internal Router basename for this request (used by redirect()) */
|
|
382
372
|
_basename?: string;
|
|
383
373
|
|