@rangojs/router 0.0.0-experimental.130 → 0.0.0-experimental.131
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
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.131",
|
|
2137
2137
|
description: "Django-inspired RSC router with composable URL patterns",
|
|
2138
2138
|
keywords: [
|
|
2139
2139
|
"react",
|
package/package.json
CHANGED
package/src/router/instrument.ts
CHANGED
|
@@ -231,26 +231,46 @@ export function observePhase<T>(
|
|
|
231
231
|
// Neither surface active: direct call, zero overhead.
|
|
232
232
|
if (!store && !tracing) return fn(NOOP_TRACE_SPAN);
|
|
233
233
|
|
|
234
|
-
// Attributes only land on a real span
|
|
235
|
-
//
|
|
236
|
-
//
|
|
234
|
+
// Attributes only land on a real span. Build the attribute/lazy wrapper only
|
|
235
|
+
// when this phase's span is actually enabled (not toggled off via `spans`), and
|
|
236
|
+
// short-circuit inside when the runner hands back the no-op span (tracing
|
|
237
|
+
// configured but off at runtime — e.g. no executionContext.tracing). That keeps
|
|
238
|
+
// the "configured but effectively off" path free of per-call attribute loops
|
|
239
|
+
// and lazy `.then()` allocations. `lazyAttributes` resolve AFTER fn runs (e.g.
|
|
240
|
+
// rango.route, known post-match) and apply on BOTH success and failure so an
|
|
241
|
+
// errored phase span is still tagged.
|
|
237
242
|
const attributes = spec.attributes;
|
|
238
243
|
const lazy = spec.lazyAttributes;
|
|
244
|
+
const spanEnabled =
|
|
245
|
+
tracing !== undefined && tracing.phases[spec.tracePhase] !== false;
|
|
239
246
|
const wrapped: (span: TraceSpan) => T =
|
|
240
|
-
(attributes || lazy) &&
|
|
247
|
+
(attributes || lazy) && spanEnabled
|
|
241
248
|
? (span) => {
|
|
249
|
+
if (span === NOOP_TRACE_SPAN) return fn(span);
|
|
242
250
|
if (attributes) applyAttributes(span, attributes);
|
|
251
|
+
// A SYNCHRONOUS throw from fn skips applyLate — fine by design: the only
|
|
252
|
+
// lazyAttributes phase (render) is always async, so any internal throw
|
|
253
|
+
// surfaces as a rejection that the onReject branch below DOES tag. If a
|
|
254
|
+
// sync lazyAttributes phase is ever added, wrap this in try/catch.
|
|
243
255
|
const out = fn(span);
|
|
244
256
|
if (!lazy) return out;
|
|
257
|
+
const applyLate = () => {
|
|
258
|
+
const late = lazy();
|
|
259
|
+
if (late) applyAttributes(span, late);
|
|
260
|
+
};
|
|
245
261
|
if (out instanceof Promise) {
|
|
246
|
-
return out.then(
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
262
|
+
return out.then(
|
|
263
|
+
(value) => {
|
|
264
|
+
applyLate();
|
|
265
|
+
return value;
|
|
266
|
+
},
|
|
267
|
+
(error) => {
|
|
268
|
+
applyLate();
|
|
269
|
+
throw error;
|
|
270
|
+
},
|
|
271
|
+
) as T;
|
|
251
272
|
}
|
|
252
|
-
|
|
253
|
-
if (late) applyAttributes(span, late);
|
|
273
|
+
applyLate();
|
|
254
274
|
return out;
|
|
255
275
|
}
|
|
256
276
|
: fn;
|
|
@@ -269,6 +289,24 @@ export function observePhase<T>(
|
|
|
269
289
|
return runThenSettle(runSpan, () => recordPhaseMetric(store, metric, start));
|
|
270
290
|
}
|
|
271
291
|
|
|
292
|
+
/**
|
|
293
|
+
* Open a rango.handler span around one segment route/layout handler call. The
|
|
294
|
+
* segment-resolution hot path runs this PER SEGMENT, so it checks the
|
|
295
|
+
* perf/tracing surface FIRST and calls the handler directly when neither is
|
|
296
|
+
* active — building neither the PhaseSpec (PHASES.handler allocates) nor the
|
|
297
|
+
* wrapper closure on the off path. The handler:<id> perf metric is owned by the
|
|
298
|
+
* track() at the call site, so this is span-only (metric:false).
|
|
299
|
+
*/
|
|
300
|
+
export function observeHandler<C, R>(
|
|
301
|
+
id: string,
|
|
302
|
+
handler: (ctx: C) => R,
|
|
303
|
+
ctx: C,
|
|
304
|
+
): R {
|
|
305
|
+
const reqCtx = _getRequestContext();
|
|
306
|
+
if (!reqCtx?._metricsStore && !reqCtx?._tracing) return handler(ctx);
|
|
307
|
+
return observePhase(PHASES.handler(id), () => handler(ctx));
|
|
308
|
+
}
|
|
309
|
+
|
|
272
310
|
/**
|
|
273
311
|
* Emit one discrete telemetry event (the event-shaped counterpart to
|
|
274
312
|
* observePhase). Resolves the sink from the active router context and stamps the
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
import { applyViewTransitionDefault } from "./view-transition-default.js";
|
|
31
31
|
import { getRouterContext } from "../router-context.js";
|
|
32
32
|
import { observeStreamedHandler } from "./streamed-handler-telemetry.js";
|
|
33
|
-
import {
|
|
33
|
+
import { observeHandler } from "../instrument.js";
|
|
34
34
|
import {
|
|
35
35
|
track,
|
|
36
36
|
RangoContext,
|
|
@@ -260,7 +260,7 @@ export async function resolveSegment<TEnv>(
|
|
|
260
260
|
const doneRouteHandler = track(`handler:${entry.id}`, 2);
|
|
261
261
|
if (entry.loading) {
|
|
262
262
|
const result = handleHandlerResult(
|
|
263
|
-
|
|
263
|
+
observeHandler(entry.id, handler, context),
|
|
264
264
|
);
|
|
265
265
|
if (result instanceof Promise) {
|
|
266
266
|
warnOnStreamedResponse(result, entry.id);
|
|
@@ -284,7 +284,7 @@ export async function resolveSegment<TEnv>(
|
|
|
284
284
|
}
|
|
285
285
|
} else {
|
|
286
286
|
component = handleHandlerResult(
|
|
287
|
-
await
|
|
287
|
+
await observeHandler(entry.id, handler, context),
|
|
288
288
|
);
|
|
289
289
|
doneRouteHandler();
|
|
290
290
|
}
|
|
@@ -511,9 +511,7 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
511
511
|
if (hasLoadingFallback) {
|
|
512
512
|
const result =
|
|
513
513
|
typeof handler === "function"
|
|
514
|
-
?
|
|
515
|
-
handler(context),
|
|
516
|
-
)
|
|
514
|
+
? observeHandler(`${parallelEntry.id}.${slot}`, handler, context)
|
|
517
515
|
: handler;
|
|
518
516
|
if (result instanceof Promise) {
|
|
519
517
|
result.finally(doneParallelHandler).catch(() => {});
|
|
@@ -537,9 +535,10 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
537
535
|
} else {
|
|
538
536
|
component =
|
|
539
537
|
typeof handler === "function"
|
|
540
|
-
? await
|
|
541
|
-
|
|
542
|
-
|
|
538
|
+
? await observeHandler(
|
|
539
|
+
`${parallelEntry.id}.${slot}`,
|
|
540
|
+
handler,
|
|
541
|
+
context,
|
|
543
542
|
)
|
|
544
543
|
: handler;
|
|
545
544
|
doneParallelHandler();
|
|
@@ -23,7 +23,7 @@ import type { ResolvedSegment, ErrorInfo, HandlerContext } from "../../types";
|
|
|
23
23
|
import type { SegmentResolutionDeps } from "../types.js";
|
|
24
24
|
import { debugLog } from "../logging.js";
|
|
25
25
|
import { tryStaticLookup } from "./static-store.js";
|
|
26
|
-
import {
|
|
26
|
+
import { observeHandler } from "../instrument.js";
|
|
27
27
|
import type { TelemetrySink } from "../telemetry.js";
|
|
28
28
|
import { resolveSink, safeEmit, getRequestId } from "../telemetry.js";
|
|
29
29
|
|
|
@@ -131,15 +131,16 @@ export async function resolveLayoutComponent<TEnv>(
|
|
|
131
131
|
entry: EntryData,
|
|
132
132
|
context: HandlerContext<any, TEnv>,
|
|
133
133
|
): Promise<ReactNode> {
|
|
134
|
-
//
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
134
|
+
// Static/prerender hit: no handler runs, so emit no rango.handler span.
|
|
135
|
+
const staticComponent = await tryStaticHandler(entry, entry.shortCode);
|
|
136
|
+
if (staticComponent !== undefined) return staticComponent;
|
|
137
|
+
const handler = entry.handler;
|
|
138
|
+
if (typeof handler !== "function") return handler as ReactNode;
|
|
139
|
+
// Wrap ONLY the handler call in the rango.handler span (the perf metric is owned
|
|
140
|
+
// by track("handler:<id>") at the call site). handleHandlerResult stays OUTSIDE
|
|
141
|
+
// the span so a handler that returns a Response (redirect control flow, which it
|
|
142
|
+
// rethrows) is not recorded as a span error — mirrors the route-handler sites.
|
|
143
|
+
return handleHandlerResult(await observeHandler(entry.id, handler, context));
|
|
143
144
|
}
|
|
144
145
|
|
|
145
146
|
// ---------------------------------------------------------------------------
|
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
} from "./helpers.js";
|
|
44
44
|
import { applyViewTransitionDefault } from "./view-transition-default.js";
|
|
45
45
|
import { getRouterContext } from "../router-context.js";
|
|
46
|
-
import { observeEvent,
|
|
46
|
+
import { observeEvent, observeHandler } from "../instrument.js";
|
|
47
47
|
import { observeStreamedHandler } from "./streamed-handler-telemetry.js";
|
|
48
48
|
import {
|
|
49
49
|
track,
|
|
@@ -794,14 +794,14 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
794
794
|
: routeEntry.handler;
|
|
795
795
|
if (!routeEntry.loading) {
|
|
796
796
|
const result = handleHandlerResult(
|
|
797
|
-
await
|
|
797
|
+
await observeHandler(entry.id, handler, context),
|
|
798
798
|
);
|
|
799
799
|
doneHandler();
|
|
800
800
|
return result;
|
|
801
801
|
}
|
|
802
802
|
if (!actionContext) {
|
|
803
803
|
const result = handleHandlerResult(
|
|
804
|
-
|
|
804
|
+
observeHandler(entry.id, handler, context),
|
|
805
805
|
);
|
|
806
806
|
if (result instanceof Promise) {
|
|
807
807
|
warnOnStreamedResponse(result, routeEntry.id);
|
|
@@ -827,7 +827,7 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
827
827
|
entryId: entry.id,
|
|
828
828
|
});
|
|
829
829
|
const actionResult = handleHandlerResult(
|
|
830
|
-
await
|
|
830
|
+
await observeHandler(entry.id, handler, context),
|
|
831
831
|
);
|
|
832
832
|
doneHandler();
|
|
833
833
|
return {
|