Skip to content

Commit e9c489c

Browse files
committed
feat: register plugin on nested apps
1 parent 3ef3a60 commit e9c489c

2 files changed

Lines changed: 179 additions & 1 deletion

File tree

src/tracing.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,15 @@ export const tracingPlugin: (traceOpts?: TracingPluginOptions) => H3Plugin =
137137
return originalUse.call(h3, wrapMiddleware(fn), opts);
138138
};
139139

140-
// TODO: Trace mount
140+
const originalMount = h3.mount;
141+
h3.mount = (base, input) => {
142+
// If the input is an H3 instance, then we can register the tracing plugin on it to
143+
if ("register" in input) {
144+
input.register(tracingPlugin(traceOpts));
145+
}
146+
147+
return originalMount.call(h3, base, input);
148+
};
141149

142150
return h3;
143151
});

test/tracing.test.ts

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,176 @@ describeMatrix(
477477
listener.cleanup();
478478
}
479479
});
480+
481+
it("traces routes from mounted nested app", async () => {
482+
const listener = createTracingListener();
483+
484+
try {
485+
const nestedApp = new H3({
486+
plugins: [tracingPlugin()],
487+
});
488+
nestedApp.get("/nested", () => "nested response");
489+
490+
t.app.mount("/api", nestedApp);
491+
492+
const response = await t.fetch("/api/nested");
493+
expect(response.status).toBe(200);
494+
expect(await response.text()).toBe("nested response");
495+
496+
// Wait for tracing events to be processed
497+
await new Promise((resolve) => setTimeout(resolve, 10));
498+
499+
const routeEvents = listener.events.filter(
500+
(e) => e.asyncStart?.data.type === "route",
501+
);
502+
503+
// Should have traced the nested app route
504+
expect(routeEvents.length).toBeGreaterThan(0);
505+
const nestedRouteEvent = routeEvents.find(
506+
(e) => e.asyncStart?.data.event.url.pathname === "/api/nested",
507+
);
508+
expect(nestedRouteEvent).toBeDefined();
509+
} finally {
510+
listener.cleanup();
511+
}
512+
});
513+
514+
it("traces middleware from mounted nested app", async () => {
515+
const listener = createTracingListener();
516+
517+
try {
518+
const nestedApp = new H3({
519+
plugins: [tracingPlugin()],
520+
});
521+
nestedApp.use((event) => {
522+
event.context.nestedMiddleware = true;
523+
});
524+
nestedApp.get("/nested", (event) => ({
525+
nestedMiddleware: event.context.nestedMiddleware,
526+
}));
527+
528+
t.app.mount("/api", nestedApp);
529+
530+
const response = await t.fetch("/api/nested");
531+
expect(response.status).toBe(200);
532+
const body = await response.json();
533+
expect(body.nestedMiddleware).toBe(true);
534+
535+
// Wait for tracing events to be processed
536+
await new Promise((resolve) => setTimeout(resolve, 10));
537+
538+
const middlewareEvents = listener.events.filter(
539+
(e) => e.asyncStart?.data.type === "middleware",
540+
);
541+
542+
// Should have traced the nested app middleware
543+
expect(middlewareEvents.length).toBeGreaterThan(0);
544+
} finally {
545+
listener.cleanup();
546+
}
547+
});
548+
549+
it("traces both parent and nested app routes and middleware", async () => {
550+
const listener = createTracingListener();
551+
552+
try {
553+
// Parent app route
554+
t.app.get("/parent", () => "parent response");
555+
556+
// Nested app with route and middleware
557+
const nestedApp = new H3();
558+
nestedApp.use((event) => {
559+
event.context.nested = true;
560+
});
561+
nestedApp.get("/nested", () => "nested response");
562+
563+
t.app.mount("/api", nestedApp);
564+
565+
// Make requests to both
566+
const parentResponse = await t.fetch("/parent");
567+
expect(parentResponse.status).toBe(200);
568+
569+
const nestedResponse = await t.fetch("/api/nested");
570+
expect(nestedResponse.status).toBe(200);
571+
572+
// Wait for tracing events to be processed
573+
await new Promise((resolve) => setTimeout(resolve, 10));
574+
575+
const routeEvents = listener.events.filter(
576+
(e) => e.asyncStart?.data.type === "route",
577+
);
578+
const middlewareEvents = listener.events.filter(
579+
(e) => e.asyncStart?.data.type === "middleware",
580+
);
581+
582+
// Should have traced both parent and nested routes
583+
expect(routeEvents.length).toBeGreaterThanOrEqual(2);
584+
585+
// Should have traced nested middleware
586+
expect(middlewareEvents.length).toBeGreaterThan(0);
587+
588+
// Verify parent route was traced
589+
const parentRouteEvent = routeEvents.find(
590+
(e) => e.asyncStart?.data.event.url.pathname === "/parent",
591+
);
592+
expect(parentRouteEvent).toBeDefined();
593+
594+
// Verify nested route was traced
595+
const nestedRouteEvent = routeEvents.find(
596+
(e) => e.asyncStart?.data.event.url.pathname === "/api/nested",
597+
);
598+
expect(nestedRouteEvent).toBeDefined();
599+
} finally {
600+
listener.cleanup();
601+
}
602+
});
603+
604+
it("traces deeply nested mounted apps", async () => {
605+
const listener = createTracingListener();
606+
607+
try {
608+
// Create a deeply nested app structure
609+
const deepApp = new H3();
610+
deepApp.use((event) => {
611+
event.context.deep = true;
612+
});
613+
deepApp.get("/deep", () => "deep response");
614+
615+
const midApp = new H3();
616+
midApp.use((event) => {
617+
event.context.mid = true;
618+
});
619+
midApp.mount("/v1", deepApp);
620+
621+
t.app.mount("/api", midApp);
622+
623+
const response = await t.fetch("/api/v1/deep");
624+
expect(response.status).toBe(200);
625+
expect(await response.text()).toBe("deep response");
626+
627+
// Wait for tracing events to be processed
628+
await new Promise((resolve) => setTimeout(resolve, 10));
629+
630+
const routeEvents = listener.events.filter(
631+
(e) => e.asyncStart?.data.type === "route",
632+
);
633+
const middlewareEvents = listener.events.filter(
634+
(e) => e.asyncStart?.data.type === "middleware",
635+
);
636+
637+
// Should have traced the deep route
638+
expect(routeEvents.length).toBeGreaterThan(0);
639+
const deepRouteEvent = routeEvents.find(
640+
(e) => e.asyncStart?.data.event.url.pathname === "/api/v1/deep",
641+
);
642+
expect(deepRouteEvent).toBeDefined();
643+
644+
// Should have traced middleware from both mid and deep apps
645+
expect(middlewareEvents.length).toBeGreaterThanOrEqual(2);
646+
} finally {
647+
listener.cleanup();
648+
}
649+
});
480650
},
481651
testOpts,
482652
);

0 commit comments

Comments
 (0)