Skip to content

feat(browser): Angular integration (@maple-dev/browser/angular, /angular/server) - #1133

Open
JeremyFunk wants to merge 2 commits into
feat/browser-server-nextjsfrom
feat/browser-angular
Open

JeremyFunk wants to merge 2 commits into
feat/browser-server-nextjsfrom
feat/browser-angular

fix(browser): Angular review fixes

16f3767
Select commit
Loading
Failed to load commit list.
Maple Review Bot / Maple / review completed Sep 29, 2026 in 5m 9s

Confidence 3/5 · 1 issue to address

Confidence 3/5 · needs attention
New span-producing integration in a published SDK; the router state machine is well tested, the zone.js workaround is not complete.
quality 90/100 · 1 warning · tests covered · risk medium · 4/4 new units observable

Adds two Angular subpaths to @maple-dev/browser: router navigation and resolver spans, an ErrorHandler, and an SSR render wrapper that writes Server-Timing. The router bookkeeping holds up under supersede, redirect and skip, but the zone.js workaround only covers the navigation span.

  • provideMapleTracing() spans each navigation as pageload/navigate, named after the matched route template
  • tracedResolver(name, fn) spans resolver loading under the navigation; RedirectCommand is not a failure
  • MapleErrorHandler and reportAngularError report as angular.error
  • tracedRender(req, render) wraps angularApp.handle() in an ssr span and appends the page's Server-Timing

Findings

Warning · F1 · tracedResolver ends its span inside NgZone, defeating the zone.js fix

correctness · packages/browser/src/angular/index.ts:121-123

provideMapleTracing ends the navigation span outside Angular's zone (index.ts:53) because, as its own comment says, a span that ends in the zone starts the exporter's flush timer as a zone macrotask and keeps ApplicationRef.isStable false. A resolver's span is not covered: traced runs at navigation.ts:91 and runTraced ends the span at failures.ts:79, both inside the zone the router runs resolvers in, so on a zone.js app every route with a resolver (or any traced call during the navigation) is back to waiting on the export timer after the navigation — the exact behaviour zone.browser.test.ts:37 asserts the fix removes, tested only on a route table with no resolver. The angular.error span has the same problem.

End resolver spans outside the zone as well: keep the `NgZone` captured in `provideMapleTracing` and run resolver span endings (or `tracedResolver`'s whole `traced` call, via `inject(NgZone, { optional: true })?.runOutsideAngular(...)`) through it. Worth one zone.js test with a resolver on the route.
What was checked
  • Superseded navigations end through startNavigation's interruptNavigation (navigation.ts:60), so the id check at angular/index.ts:95 does not double-end them
  • pathOf on absolute, origin-only, * and relative URLs (server.test.ts:99)
  • No PII in the new spans: event.url.split(/[?#]/)[0] and scrubUrl strip query and hash before url.path
Observability coverage: 4 of 4 changes observable
Change Kind Observable Evidence
Angular router navigations (provideMapleTracing) client span yes angular/index.ts:68 starts pageload/navigate, ended at :97 by routeTemplate
Resolver data loading (tracedResolver) client span yes angular/index.ts:122 delegates to traced (navigation.ts:91)
Angular errors (MapleErrorHandler, reportAngularError) error span yes angular/index.ts:134 captureException(error, { name: "angular.error" })
Angular SSR render (tracedRender) server span yes angular/server.ts:24 startActiveSpan("ssr", { attributes }) with url.path

16f3767 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple to ask about one.

Annotations

Check warning on line 123 in packages/browser/src/angular/index.ts

See this annotation in the file changed.

@maple-review-bot maple-review-bot / Maple / review

correctness: `tracedResolver` ends its span inside NgZone, defeating the zone.js fix

`provideMapleTracing` ends the navigation span outside Angular's zone (`index.ts:53`) because, as its own comment says, a span that ends in the zone starts the exporter's flush timer as a zone macrotask and keeps `ApplicationRef.isStable` false. A resolver's span is not covered: `traced` runs at `navigation.ts:91` and `runTraced` ends the span at `failures.ts:79`, both inside the zone the router runs resolvers in, so on a zone.js app every route with a resolver (or any `traced` call during the navigation) is back to waiting on the export timer after the navigation — the exact behaviour `zone.browser.test.ts:37` asserts the fix removes, tested only on a route table with no resolver. The `angular.error` span has the same problem.