Strata architecture and SDDMenu
On this page

Angular DI with Analog guards, interceptors and validation

Qualification for the existing consumer-owned Angular controller lifecycle on Node. Starting synchronized main SHA: eb1d17a81283d57569e9dc1b4712dea2623f6fd6. This extends the SPEC-003 fixture; it adds no DI container and changes no public package API.

Runtime tuple and reused fixture

The lockfile tuple exercised by pnpm test:analog is Node 22.23.1, Angular core/common/compiler 22.1.7, Angular platform-browser 22.2.1, platform-server/router 22.2.2, Analog 2.7.2, Nitro 2.13.4, H3 1.15.11, TypeScript 6.0.3, Vite 8.3.0, Wrangler 4.135.0 and workerd compatibility date 2026-09-21.

CatalogController, CatalogService, RequestIdentity, RequestAudit, STRATA_REQUEST, the consumer-owned application injector, the existing controllerFactory and its onCleanup disposal are reused. Its original SPEC-003 routes and rendezvous checks remain intact. The Angular request injector is a child of the fixture's application injector; it is separate from Angular SSR and Analog server-function injectors.

HTTP composition

Only /api/strata/angular-di/protected/** uses the new synthetic policy. The existing SPEC-003 paths remain public and keep their previous response shape. The guard recognizes only fixture-owned opaque credentials mapped to alpha and beta, checks the requested product owner, and associates the verified principal with that exact StrataAnalogRequest. A raw identity header or query parameter is not used. Missing/invalid credentials return 401, an authenticated wrong owner returns 403, and denials run guard cleanup without entering an interceptor or creating an Angular request injector.

Two fixture interceptors run through the existing StrataAnalogInterceptor contract. The timing observer and outcome mapper enter in configuration order and unwind in reverse order. They each register cleanup in the shared CleanupScope. The response mapping preserves native Response objects and adds a trace only to plain JSON objects on the composed routes. Downstream errors are rethrown so Strata can preserve input-error translation and cleanup precedence; fixture errors use generic messages. Checks cover a pre-next interceptor failure, an after-next interceptor failure, downstream sync/async throws, and rethrowing a downstream failure.

GET validates numeric route params and transforms a query tag to uppercase through Standard Schema. POST validates params and a JSON body. Invalid schema, malformed JSON, oversized JSON and unsupported media type return the existing safe 400/413/415 responses. They do not expose schema issues or private fixture values and destroy any request injector already created. A native Response from the handler is preserved without a second JSON serialization.

Injector lifetime, overrides and cleanup

Each authorized invocation creates one Angular EnvironmentInjector, resolves CatalogController synchronously inside runInInjectionContext, and registers destruction through onCleanup. The application-scoped CatalogService stays at one instance. RequestIdentity and RequestAudit share one request-local instance; the identity retains the active request and verified principal. ngOnDestroy and DestroyRef run on success and the tested failure paths. Controller methods access captured dependencies after await; the injector remains alive through both interceptor unwind callbacks.

One request can override a synthetic REQUEST_CONFIGURATION child provider. Its sibling inherits the unchanged application value. No request-specific provider is installed in the root injector.

Factory failure creates no request injector. Provider-factory and controller construction failures destroy the injector they created. Successful, denied, validation-failure, handler-failure, interceptor-failure and multiple-cleanup failure cases run through the existing CleanupScope. Instrumented LIFO checks observe injector disposal, outcome cleanup, timing cleanup and guard cleanup in that order. Cleanup failures remain 500 failures and are not converted into client 400/401/403 responses.

Concurrent isolation

The runner sends 24 mixed requests in three concurrent batches of eight. The first batch contains eight authorized requests held at an explicit eight-party rendezvous. The second mixes denials and invalid schema input. The third mixes valid credentials with asynchronous handler and interceptor failures. It asserts unique identities for the rendezvous requests, correct principal to request association, no DI construction on denial, stable application service identity, safe response bodies, zero cleanup-order violations, and zero live request injectors after each batch. The fixture's lifetime counters also assert that every created injector is eventually destroyed.

Results

pnpm test:analog runs the real Analog development server and Nitro node-server production output. The final clean run passed 110/110 checks across both arms; the baseline, existing route checks and every composition scenario passed in production and development:

QualificationDevelopmentProduction
Existing SPEC-003 routes and checksGOGO
Guarded Angular DI success and denialGOGO
Interceptor order and error behaviorGOGO
Standard Schema params/query/body and safe failuresGOGO
Provider identity, overrides and destructionGOGO
24-request mixed isolation matrixGOGO

The runner sends independent synthetic requests with connection: close to avoid reusing an idle Node HTTP connection between larger fixture scenarios. The matrix itself uses no retries. Its first eight-request batch completes only after the deterministic rendezvous has all eight participants.

The exact runner output is also retained in the generated Analog integration baseline.

Workerd and external consumers

The known local workerd limitation remains a NO-GO: Angular JIT attempts dynamic code generation and workerd throws EvalError: Code generation from strings disallowed for this context. No eval flag, nodejs_compat workaround, Angular patch or Worker support claim is part of this change. The existing workerd characterization must continue to show no request injector is created on that failure and unrelated Worker routes remain available.

The packed core/Analog and full-stack consumers do not exercise this Angular-backed route. Packed Angular DI therefore remains NOT QUALIFIED. No package was published and no deployment was attempted. No Changeset is warranted because no production package API or implementation changed.

Limitations and next increment

This is a fixture qualification, not real-application security certification. It does not establish shared SSR/controller or server-function DI, production authentication, Worker DI, cancellation/abort behavior, Angular root shutdown with active requests, packed Angular DI consumer support, or Strata 1.0 readiness.

Next increment: prove the existing Angular component compiler and strict server/client graph boundary in the minimal Analog fixture, retaining the workerd JIT NO-GO.

Source: docs/research/analog-di-http-composition.md

Generated by Entwine · Markdown remains the source of truth.