Analog integration baseline
Generated by pnpm test:analog at 2026-10-28T16:56:05.925Z. This report is regenerated on every run;
it reflects the most recent execution against baseline 02b06a17dbd5e9221bf02367beadabfbf84e54ee.
Scope. One question: how does the current @strata-sc/core / @strata-sc/h3 fit inside a real
Angular + Analog application (apps/analog-fixture), built and run with the stock Analog
pipeline? No Strata Vite plugin and no decorator transform. @strata-sc/core decorators are used
in src/server/**, and @strata-sc/analog registers a controller on the Nitro router through Nitro's
public plugin API. It makes no claim beyond the single tuple below and the two modes exercised (dev
server, production server).
Result of this run: every recorded expectation held.
Exact dependency tuple
Node v22.23.1, pnpm 10.30.1. The fixture's manifests follow the official
create-analog@2.7.2 template-latest. "Resolved" is read from the installed packages on disk
(symlinks followed), not from package.json.
| Package | Declared | Resolved | Note |
|---|---|---|---|
@angular/core | ^22.2.3 | 22.2.3 | Angular runtime |
@angular/compiler-cli | ^22.2.3 | 22.2.3 | Angular compiler (ngtsc) |
@angular/build | ^22.0.0 | 22.1.8 | Angular build tooling used by the Analog plugin |
@analogjs/platform | ^2.7.2 | 2.7.2 | Analog (Vite platform plugin) |
@analogjs/vite-plugin-angular | ^2.7.2 | 2.7.2 | Analog's Angular compiler integration |
@analogjs/router | ^2.8.1 | 2.8.1 | Analog file router |
vite | ^8.0.0 | 8.3.0 | Fixture-local Vite |
typescript | ~6.0.0 | 6.0.3 | TypeScript |
tslib | ^2.3.0 | 2.8.1 | TypeScript helper library |
@analogjs/vite-plugin-nitro | (transitive) | 2.7.2 | Analog's Nitro integration |
nitropack | (transitive) | 2.13.4 | Nitro 2 — the HTTP runtime |
vite | (transitive) | 8.1.5 | Vite bundled by @angular/build |
@strata-sc/core | workspace:* | 0.2.2 | workspace link |
h3 | ^1.15.11 | 1.15.11 | H3 used by nitropack (Nitro 2) |
h3 | ^2.0.1-rc.1 | 2.0.1-rc.32 | H3 used by @strata-sc/h3 |
Lockfile (pnpm-lock.yaml, packages: section) — every version present in the whole workspace graph:
| Package | Versions in lockfile |
|---|---|
@analogjs/platform | 2.7.2 |
@angular/build | 22.1.8 |
@angular/core | 22.2.3 |
h3 | 1.15.11, 2.0.1-rc.32 |
nitro | none |
nitropack | 2.13.4 |
rolldown | 1.1.5, 1.2.0, 1.2.8 |
typescript | 6.0.3 |
vite | 8.1.5, 8.3.0 |
Runtime graph: which H3?
h3 as resolved from each consumer, and the top-level APIs that copy actually exports
(loaded from disk, not inferred):
| Resolved from | h3 | createApp | createRouter | createEvent | H3 class |
|---|---|---|---|---|---|
nitropack | 1.15.11 | yes | yes | yes | no |
@analogjs/vite-plugin-nitro | 1.15.11 | yes | yes | yes | no |
@strata-sc/h3 | 2.0.1-rc.32 | yes | yes | no | yes |
- Resolved Nitro: 2.13.4 (
nitropack, presetnode-server), recorded by the built server indist/analog/nitro.json. Production start command:node dist/analog/server/index.mjs. - The built production server does not load an installed
h3: Nitro inlines it intodist/analog/server/chunks/nitro/nitro.mjs, which definescreateApp: yes,createRouter: yes and an H3 v2H3class: no. - Analog/Nitro run H3 v1;
@strata-sc/h3targets H3 v2. They are different majors with different APIs (createApp/createRoutervs theH3class).
Angular/Analog compilation and standard decorators
The same consumer-authored @Controller() / @Get() (standard TC39 decorators from @strata-sc/core)
was observed in the two places Analog compiles TypeScript. Classes: A works, B compile-time
failure, C build succeeds but runtime fails, D other.
| Arm | Pipeline | Mode | Expected | Observed | Evidence |
|---|---|---|---|---|---|
| S-dev | Strata controller via @strata-sc/analog (nitroApp.router) | dev server | A | A | 200 [{"id":"1","name":"Ada"}] |
| S-prod | Strata controller via @strata-sc/analog (nitroApp.router) | production server | A | A | 200 [{"id":"1","name":"Ada"}] |
| N-dev | Nitro server route (src/server/**) | dev server | A | A | 200 {"source":"strata-core","definition":{"path":"/hello","routes":[{"method":"GET","path":"/","handler":"hello"}]},"invoked":{"message":"Hello from Strata","marker":"STRATA_ANALOG_SERVER_ONLY_MARKER"}} |
| N-prod | Nitro server route (src/server/**) | production server | A | A | 200 {"source":"strata-core","definition":{"path":"/hello","routes":[{"method":"GET","path":"/","handler":"hello"}]},"invoked":{"message":"Hello from Strata","marker":"STRATA_ANALOG_SERVER_ONLY_MARKER"}} |
| G1 | Angular-compiled graph (page → controller) | production build, stock tsconfig | B | B | RolldownError: <repo>/apps/analog-fixture/src/probes/angular-graph/probe.controller.ts:7:1: Unable to resolve signature of class decorator when called as an expression. / The runtime will invoke the decorator with 1 arguments, but the decorator expects 2. / <repo>/apps/analog-fixture/src/probes/angular-graph/probe.controller.ts:9:4: Unable to resolve signature of method decorator when called as an expression. |
| G2-build | Angular-compiled graph (page → controller) | production build, experimentalDecorators: false | A | A | build succeeded |
| G2-prod | Angular-compiled graph (page → controller) | production server (SSR of the probe page) | C | C | HTTP 200; page did not render the expected definition; server log: ERROR Error [ERR_MODULE_NOT_FOUND]: Cannot find module '<repo>/apps/analog-fixture/dist/analog/server/node_modules/tslib/modules/index.js' imported from /Volumes/WorkStation/projects/Strata/apps/analog-fixture/dist/analog/server/chunks/_/decorator-probe.page-LA4lckFK.mjs; tslib files traced into dist/analog/server/node_modules: package.json, tslib.es6.mjs |
| G2-dev | Angular-compiled graph (page → controller) | dev server (SSR of the probe page) | A | A | HTTP 200; page rendered the expected definition |
- Nitro server routes (
src/server/**, native Analog API routes) are bundled by Nitro's own Rollup + esbuild pipeline, not by the Angular compiler. esbuild lowers standard decorators on its own;getControllerDefinition()returns the right metadata in dev and production. - The Angular-compiled graph (pages, components, anything a page imports) uses the stock
Analog template's
experimentalDecorators: true: the Angular compiler type-checks the consumer's decorators as legacy decorators and fails the build (class B). WithexperimentalDecorators: falsethe decorators are lowered and the metadata is correct in the dev server, but the production server's SSR of that page fails (class C). - Nothing here modified Strata or added a transform. The baseline build uses the stock
vite.config.ts; the G arms usevite.probe.config.ts, which differs only by also building the probe page and by selecting which tsconfig Analog's Angular plugin reads.
Production build
pnpm --filter @strata-fixtures/analog run build (vite build) exit status: 0.
| Artifact | Present | Role |
|---|---|---|
dist/client/index.html | yes | Vite client build (browser bundle entry) |
dist/ssr/main.server.js | yes | Vite SSR build of the Angular app |
dist/analog/public/index.html | yes | Nitro public assets (client + prerendered pages) |
dist/analog/server/index.mjs | yes | Nitro production server entry |
dist/analog/server/chunks/routes/api/native.mjs | yes | Native Analog API route |
dist/analog/server/chunks/routes/api/strata-core.mjs | yes | Strata experiment route |
dist/analog/nitro.json | yes | Nitro build metadata |
Native Analog server route (/api/native)
| Mode | Status | Body |
|---|---|---|
| production server | 200 | {"source":"analog"} |
| dev server | 200 | {"source":"analog"} |
Strata metadata through @strata-sc/core only (/api/strata-core)
| Mode | Status | Body |
|---|---|---|
| production server | 200 | {"source":"strata-core","definition":{"path":"/hello","routes":[{"method":"GET","path":"/","handler":"hello"}]},"invoked":{"message":"Hello from Strata","marker":"STRATA_ANALOG_SERVER_ONLY_MARKER"}} |
| dev server | 200 | {"source":"strata-core","definition":{"path":"/hello","routes":[{"method":"GET","path":"/","handler":"hello"}]},"invoked":{"message":"Hello from Strata","marker":"STRATA_ANALOG_SERVER_ONLY_MARKER"}} |
H3 seam and @strata-sc/h3
Nitro hands its app to server plugins through the public defineNitroPlugin() from
nitropack/runtime; NitroApp.h3App and NitroApp.router are part of Nitro's exported
NitroApp type. A fixture plugin observed (never modified) what a real production server passes:
{
"nitroAppKeys": ["captureError", "h3App", "hooks", "localCall", "localFetch", "router"],
"h3App": {
"typeofValue": "object",
"constructorName": "Object",
"ownKeys": ["handler", "options", "resolve", "stack", "use", "websocket"],
"hasOn": false,
"hasUse": true,
"hasStack": true,
"hasFetch": false,
"hasRoutes": false
},
"router": {
"typeofValue": "object",
"constructorName": "Object",
"ownKeys": [
"add",
"connect",
"delete",
"get",
"handler",
"head",
"options",
"patch",
"post",
"put",
"trace",
"use"
],
"hasOn": false,
"hasUse": true,
"hasStack": false,
"hasFetch": false,
"hasRoutes": false
}
}
registerControllers() in @strata-sc/h3 (packages/h3/src/register-controllers.ts) calls app.on(...): yes.
The Nitro h3App has .on: no.
Outcome B. Nitro exposes its H3 through a public plugin hook, but as an H3 v1 App (use/stack) and v1 Router, not an H3 v2 H3 instance. The current @strata-sc/h3 cannot mount directly.
@strata-sc/h3 was not mounted in Analog: the only H3 app the public seam provides is an H3 v1
App, and passing it to a function typed for the H3 v2 H3 class would need an unsafe cast
between majors, a fake H3 instance, or a second router — all ruled out for this experiment.
@strata-sc/analog: a Strata controller through the public Nitro router seam
src/server/plugins/strata.ts is a Nitro server plugin that calls
registerControllers(nitroApp.router, [UsersController, LifecycleController]) and, separately,
registerControllers(nitroApp.router, [GreetingController], { controllerFactory }). None of these
controllers is reachable from any file under src/server/routes/**: no file-system route wraps
them. The routes below exist only because @strata-sc/analog read their @strata-sc/core metadata and
registered them on nitroApp.router.
Controllers are request-scoped: LifecycleController and GreetingController increment an
instance field, and each is requested twice in a row per mode. Both answers carry calls: 1, so
each request got a new instance. GreetingController receives a plain GreetingService only from
the fixture's controllerFactory (Strata's default factory would leave it null).
| Route | Mode | Status | Content-Type | Body |
|---|---|---|---|---|
GET /api/strata/users | production server | 200 | application/json | [{"id":"1","name":"Ada"}] |
GET /api/strata/users | dev server | 200 | application/json | [{"id":"1","name":"Ada"}] |
GET /api/strata/users/42 | production server | 200 | application/json | {"id":"42","name":"Ada"} |
GET /api/strata/users/42 | dev server | 200 | application/json | {"id":"42","name":"Ada"} |
GET /api/strata/lifecycle (1st) | production server | 200 | application/json | {"calls":1} |
GET /api/strata/lifecycle (2nd) | production server | 200 | application/json | {"calls":1} |
GET /api/strata/greeting (1st) | production server | 200 | application/json | {"greeting":"hello","calls":1,"intercepted":true} |
GET /api/strata/greeting (2nd) | production server | 200 | application/json | {"greeting":"hello","calls":1,"intercepted":true} |
GET /api/strata/methods | production server | 200 | application/json | {"handler":"list","method":"GET"} |
POST /api/strata/methods {"name":"Ada"} | production server | 200 | application/json | {"handler":"create","method":"POST","name":"Ada"} |
PUT /api/strata/methods/7 | production server | 200 | application/json | {"handler":"replace","method":"PUT","id":"7"} |
PATCH /api/strata/methods/42 {"name":"Grace"} | production server | 200 | application/json | {"handler":"update","method":"PATCH","id":"42","name":"Grace"} |
DELETE /api/strata/methods/42 | production server | 200 | application/json | {"handler":"remove","method":"DELETE","deleted":"42"} |
GET /api/strata/lifecycle (1st) | dev server | 200 | application/json | {"calls":1} |
GET /api/strata/lifecycle (2nd) | dev server | 200 | application/json | {"calls":1} |
GET /api/strata/greeting (1st) | dev server | 200 | application/json | {"greeting":"hello","calls":1,"intercepted":true} |
GET /api/strata/greeting (2nd) | dev server | 200 | application/json | {"greeting":"hello","calls":1,"intercepted":true} |
GET /api/strata/methods | dev server | 200 | application/json | {"handler":"list","method":"GET"} |
POST /api/strata/methods {"name":"Ada"} | dev server | 200 | application/json | {"handler":"create","method":"POST","name":"Ada"} |
PUT /api/strata/methods/7 | dev server | 200 | application/json | {"handler":"replace","method":"PUT","id":"7"} |
PATCH /api/strata/methods/42 {"name":"Grace"} | dev server | 200 | application/json | {"handler":"update","method":"PATCH","id":"42","name":"Grace"} |
DELETE /api/strata/methods/42 | dev server | 200 | application/json | {"handler":"remove","method":"DELETE","deleted":"42"} |
The native Analog route (/api/native, above) answers in the same run, on the same router.
Experiment (SPEC-003): Angular DI through controllerFactory
Specified by SPEC-003; the source trace,
candidate comparison and verdict are in Analog DI feasibility.
src/server/plugins/strata-angular-di.ts registers CatalogController with a factory owned by
the fixture, not by @strata-sc/analog (which has no Angular dependency). The plugin bootstraps an
application injector with Angular's public createApplication() on a platformServer(), as
Analog does for its server functions, so @Injectable({ providedIn: 'root' }) CatalogService
resolves there once per process. For each request the factory creates a child
createEnvironmentInjector() providing STRATA_REQUEST, RequestIdentity and
RequestAudit, registers onCleanup(() => requestInjector.destroy()), and builds the controller
with runInInjectionContext(requestInjector, () => new Controller()). The controller resolves its
dependencies with inject() in field initializers and keeps them across await; it also calls
inject() inside its handler, before and after an await, and reports the outcome. No Nitro
request/afterResponse/error hook or event.context slot is used (checked on the
sources); only the application is destroyed in Nitro's close hook.
Each scenario is bracketed by two reads of /api/strata-angular-di-stats, once each, with no
retry; the table shows after − before, except for levels shown as read: injectorsAlive,
maxInjectorsAlive and catalogInstances (the providedIn: 'root' service is created lazily by
the first request and must stay at one). Overlap is enforced, not assumed: ?meet=<key> holds a handler at a rendezvous that
answers met: true only if a second request with the same key reaches it while the first is
still suspended, and met: false after 5 s otherwise. The lone request is the negative control.
| Mode | Scenario | Responses | Counter delta | Result |
|---|---|---|---|---|
| production server | two requests held at a rendezvous | 200 {"met":true,"product":{"id":"a","name":"Product a"},"catalogInstance":1,"identity":2,"identityProductId":"a","auditSharesIdentity":true,"injectedRequestIsArgument":true,"injectInHandler":"NG0203","injectAfterAwait":"NG0203","destroyedBeforeResponse":false}<br>200 {"met":true,"product":{"id":"b","name":"Product b"},"catalogInstance":1,"identity":1,"identityProductId":"b","auditSharesIdentity":true,"injectedRequestIsArgument":true,"injectInHandler":"NG0203","injectAfterAwait":"NG0203","destroyedBeforeResponse":false} | {"injectorsCreated":2,"injectorsDestroyed":2,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":2,"controllersReleased":2,"catalogInstances":1,"guardEntered":0,"guardCleanups":0,"interceptorEvents":0,"interceptorCleanups":0,"controllerConstructions":2,"handlerCalls":2,"cleanupOrderChecks":0,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} | pass |
| production server | one request alone at a rendezvous (control) | 200 {"met":false,"product":{"id":"solo","name":"Product solo"},"catalogInstance":1,"identity":3,"identityProductId":"solo","auditSharesIdentity":true,"injectedRequestIsArgument":true,"injectInHandler":"NG0203","injectAfterAwait":"NG0203","destroyedBeforeResponse":false} | {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":1,"controllersReleased":1,"catalogInstances":1,"guardEntered":0,"guardCleanups":0,"interceptorEvents":0,"interceptorCleanups":0,"controllerConstructions":1,"handlerCalls":1,"cleanupOrderChecks":0,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} | pass |
| production server | handler throws synchronously | 500 {"error":true,"message":"Server Error"} | {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":1,"controllersReleased":1,"catalogInstances":1,"guardEntered":0,"guardCleanups":0,"interceptorEvents":0,"interceptorCleanups":0,"controllerConstructions":1,"handlerCalls":1,"cleanupOrderChecks":0,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} | pass |
| production server | async handler rejects after an await | 500 {"error":true,"message":"Server Error"} | {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":1,"controllersReleased":1,"catalogInstances":1,"guardEntered":0,"guardCleanups":0,"interceptorEvents":0,"interceptorCleanups":0,"controllerConstructions":1,"handlerCalls":1,"cleanupOrderChecks":0,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} | pass |
| production server | authorized DI routes compose verified principals, interceptors and Standard Schema | 200 {"product":{"id":"101","name":"Product 101"},"tag":"TRANSFORMED","met":null,"principal":"alpha","identity":7,"identityProductId":"101","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]}<br>200 {"id":102,"count":7,"principal":"beta","identity":6,"identityProductId":"102","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} | {"injectorsCreated":2,"injectorsDestroyed":2,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":2,"controllersReleased":2,"catalogInstances":1,"guardEntered":2,"guardCleanups":2,"interceptorEvents":4,"interceptorCleanups":4,"controllerConstructions":2,"handlerCalls":2,"cleanupOrderChecks":2,"cleanupOrderFailures":0,"interceptorLiveChecks":4,"interceptorLiveFailures":0} | pass |
| production server | 401 and 403 denials run guard cleanup before any interceptor or Angular construction | 401 {"error":"unauthorized"}<br>401 {"error":"unauthorized"}<br>403 {"error":"forbidden"}<br>403 {"error":"forbidden"} | {"injectorsCreated":0,"injectorsDestroyed":0,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":0,"controllersReleased":0,"catalogInstances":1,"guardEntered":4,"guardCleanups":4,"interceptorEvents":0,"interceptorCleanups":0,"controllerConstructions":0,"handlerCalls":0,"cleanupOrderChecks":0,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} | pass |
| production server | invalid Standard Schema input is safe and destroys the authorized request injector | 400 {"error":"invalid_request","source":"params"} | {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":1,"controllersReleased":1,"catalogInstances":1,"guardEntered":1,"guardCleanups":1,"interceptorEvents":2,"interceptorCleanups":2,"controllerConstructions":1,"handlerCalls":1,"cleanupOrderChecks":1,"cleanupOrderFailures":0,"interceptorLiveChecks":2,"interceptorLiveFailures":0} | pass |
| production server | malformed, unsupported and oversized JSON failures clean up request injectors | 400 {"error":"invalid_request","source":"body"}<br>415 {"error":"unsupported_media_type","source":"body"}<br>413 {"error":"payload_too_large","source":"body"} | {"injectorsCreated":3,"injectorsDestroyed":3,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":3,"controllersReleased":3,"catalogInstances":1,"guardEntered":3,"guardCleanups":3,"interceptorEvents":6,"interceptorCleanups":6,"controllerConstructions":3,"handlerCalls":3,"cleanupOrderChecks":3,"cleanupOrderFailures":0,"interceptorLiveChecks":6,"interceptorLiveFailures":0} | pass |
| production server | request provider override remains local to one child injector | 200 {"product":{"id":"101","name":"Product 101"},"tag":"X","met":null,"principal":"alpha","identity":13,"identityProductId":"101","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"only-this-request","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]}<br>200 {"product":{"id":"102","name":"Product 102"},"tag":"X","met":null,"principal":"beta","identity":12,"identityProductId":"102","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} | {"injectorsCreated":2,"injectorsDestroyed":2,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":2,"controllersReleased":2,"catalogInstances":1,"guardEntered":2,"guardCleanups":2,"interceptorEvents":4,"interceptorCleanups":4,"controllerConstructions":2,"handlerCalls":2,"cleanupOrderChecks":2,"cleanupOrderFailures":0,"interceptorLiveChecks":4,"interceptorLiveFailures":0} | pass |
| production server | controller factory failure runs guard and interceptor cleanup without an injector | 500 {"error":true,"message":"Server Error"} | {"injectorsCreated":0,"injectorsDestroyed":0,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":0,"controllersReleased":0,"catalogInstances":1,"guardEntered":1,"guardCleanups":1,"interceptorEvents":2,"interceptorCleanups":2,"controllerConstructions":0,"handlerCalls":0,"cleanupOrderChecks":1,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} | pass |
| production server | provider factory failure destroys the created request injector | 500 {"error":true,"message":"Server Error"} | {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":0,"controllersReleased":0,"catalogInstances":1,"guardEntered":1,"guardCleanups":1,"interceptorEvents":2,"interceptorCleanups":2,"controllerConstructions":0,"handlerCalls":0,"cleanupOrderChecks":1,"cleanupOrderFailures":0,"interceptorLiveChecks":2,"interceptorLiveFailures":0} | pass |
| production server | controller constructor failure destroys its request injector | 500 {"error":true,"message":"Server Error"} | {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":1,"controllersReleased":0,"catalogInstances":1,"guardEntered":1,"guardCleanups":1,"interceptorEvents":2,"interceptorCleanups":2,"controllerConstructions":1,"handlerCalls":0,"cleanupOrderChecks":1,"cleanupOrderFailures":0,"interceptorLiveChecks":2,"interceptorLiveFailures":0} | pass |
| production server | handler, interceptor and multiple cleanup failures settle without leaking private errors | 500 {"error":true,"message":"Server Error"}<br>500 {"error":true,"message":"Server Error"}<br>500 {"error":true,"message":"Server Error"}<br>500 {"error":true,"message":"Server Error"}<br>500 {"error":true,"message":"Server Error"} | {"injectorsCreated":5,"injectorsDestroyed":5,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":5,"controllersReleased":5,"catalogInstances":1,"guardEntered":5,"guardCleanups":5,"interceptorEvents":10,"interceptorCleanups":10,"controllerConstructions":5,"handlerCalls":5,"cleanupOrderChecks":5,"cleanupOrderFailures":0,"interceptorLiveChecks":11,"interceptorLiveFailures":0} | pass |
| production server | interceptor failure before next cleans up without constructing a controller | 500 {"error":true,"message":"Server Error"} | {"injectorsCreated":0,"injectorsDestroyed":0,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":0,"controllersReleased":0,"catalogInstances":1,"guardEntered":1,"guardCleanups":1,"interceptorEvents":2,"interceptorCleanups":2,"controllerConstructions":0,"handlerCalls":0,"cleanupOrderChecks":1,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} | pass |
| production server | native Web Response is preserved through interceptors | 200 native-response-preserved | {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":1,"controllersReleased":1,"catalogInstances":1,"guardEntered":1,"guardCleanups":1,"interceptorEvents":2,"interceptorCleanups":2,"controllerConstructions":1,"handlerCalls":1,"cleanupOrderChecks":1,"cleanupOrderFailures":0,"interceptorLiveChecks":2,"interceptorLiveFailures":0} | pass |
| production server | 24 mixed requests in three overlapping eight-request batches isolate state | 200 {"product":{"id":"101","name":"Product 101"},"tag":"M0","met":true,"principal":"alpha","identity":22,"identityProductId":"101","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]}<br>200 {"product":{"id":"102","name":"Product 102"},"tag":"M1","met":true,"principal":"beta","identity":23,"identityProductId":"102","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]}<br>200 {"product":{"id":"103","name":"Product 103"},"tag":"M2","met":true,"principal":"alpha","identity":24,"identityProductId":"103","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]}<br>200 {"product":{"id":"104","name":"Product 104"},"tag":"M3","met":true,"principal":"beta","identity":26,"identityProductId":"104","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]}<br>200 {"product":{"id":"105","name":"Product 105"},"tag":"M4","met":true,"principal":"alpha","identity":25,"identityProductId":"105","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]}<br>200 {"product":{"id":"106","name":"Product 106"},"tag":"M5","met":true,"principal":"beta","identity":27,"identityProductId":"106","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]}<br>200 {"product":{"id":"107","name":"Product 107"},"tag":"M6","met":true,"principal":"alpha","identity":28,"identityProductId":"107","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]}<br>200 {"product":{"id":"108","name":"Product 108"},"tag":"M7","met":true,"principal":"beta","identity":29,"identityProductId":"108","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]}<br>401 {"error":"unauthorized"}<br>401 {"error":"unauthorized"}<br>401 {"error":"unauthorized"}<br>401 {"error":"unauthorized"}<br>400 {"error":"invalid_request","source":"params"}<br>400 {"error":"invalid_request","source":"params"}<br>400 {"error":"invalid_request","source":"params"}<br>400 {"error":"invalid_request","source":"params"}<br>500 {"error":true,"message":"Server Error"}<br>500 {"error":true,"message":"Server Error"}<br>500 {"error":true,"message":"Server Error"}<br>500 {"error":true,"message":"Server Error"}<br>500 {"error":true,"message":"Server Error"}<br>500 {"error":true,"message":"Server Error"}<br>500 {"error":true,"message":"Server Error"}<br>500 {"error":true,"message":"Server Error"} | {"injectorsCreated":20,"injectorsDestroyed":20,"injectorsAlive":0,"maxInjectorsAlive":8,"identitiesDestroyed":20,"controllersReleased":20,"catalogInstances":1,"guardEntered":24,"guardCleanups":24,"interceptorEvents":40,"interceptorCleanups":40,"controllerConstructions":20,"handlerCalls":20,"cleanupOrderChecks":20,"cleanupOrderFailures":0,"interceptorLiveChecks":44,"interceptorLiveFailures":0} | pass |
| dev server | two requests held at a rendezvous | 200 {"met":true,"product":{"id":"a","name":"Product a"},"catalogInstance":1,"identity":1,"identityProductId":"a","auditSharesIdentity":true,"injectedRequestIsArgument":true,"injectInHandler":"NG0203","injectAfterAwait":"NG0203","destroyedBeforeResponse":false}<br>200 {"met":true,"product":{"id":"b","name":"Product b"},"catalogInstance":1,"identity":2,"identityProductId":"b","auditSharesIdentity":true,"injectedRequestIsArgument":true,"injectInHandler":"NG0203","injectAfterAwait":"NG0203","destroyedBeforeResponse":false} | {"injectorsCreated":2,"injectorsDestroyed":2,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":2,"controllersReleased":2,"catalogInstances":1,"guardEntered":0,"guardCleanups":0,"interceptorEvents":0,"interceptorCleanups":0,"controllerConstructions":2,"handlerCalls":2,"cleanupOrderChecks":0,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} | pass |
| dev server | one request alone at a rendezvous (control) | 200 {"met":false,"product":{"id":"solo","name":"Product solo"},"catalogInstance":1,"identity":3,"identityProductId":"solo","auditSharesIdentity":true,"injectedRequestIsArgument":true,"injectInHandler":"NG0203","injectAfterAwait":"NG0203","destroyedBeforeResponse":false} | {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":1,"controllersReleased":1,"catalogInstances":1,"guardEntered":0,"guardCleanups":0,"interceptorEvents":0,"interceptorCleanups":0,"controllerConstructions":1,"handlerCalls":1,"cleanupOrderChecks":0,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} | pass |
| dev server | handler throws synchronously | 500 {"error":true,"message":"thrown by request 4"} | {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":1,"controllersReleased":1,"catalogInstances":1,"guardEntered":0,"guardCleanups":0,"interceptorEvents":0,"interceptorCleanups":0,"controllerConstructions":1,"handlerCalls":1,"cleanupOrderChecks":0,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} | pass |
| dev server | async handler rejects after an await | 500 {"error":true,"message":"rejected by request 5"} | {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":1,"controllersReleased":1,"catalogInstances":1,"guardEntered":0,"guardCleanups":0,"interceptorEvents":0,"interceptorCleanups":0,"controllerConstructions":1,"handlerCalls":1,"cleanupOrderChecks":0,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} | pass |
| dev server | authorized DI routes compose verified principals, interceptors and Standard Schema | 200 {"product":{"id":"101","name":"Product 101"},"tag":"TRANSFORMED","met":null,"principal":"alpha","identity":7,"identityProductId":"101","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]}<br>200 {"id":102,"count":7,"principal":"beta","identity":6,"identityProductId":"102","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} | {"injectorsCreated":2,"injectorsDestroyed":2,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":2,"controllersReleased":2,"catalogInstances":1,"guardEntered":2,"guardCleanups":2,"interceptorEvents":4,"interceptorCleanups":4,"controllerConstructions":2,"handlerCalls":2,"cleanupOrderChecks":2,"cleanupOrderFailures":0,"interceptorLiveChecks":4,"interceptorLiveFailures":0} | pass |
| dev server | 401 and 403 denials run guard cleanup before any interceptor or Angular construction | 401 {"error":"unauthorized"}<br>401 {"error":"unauthorized"}<br>403 {"error":"forbidden"}<br>403 {"error":"forbidden"} | {"injectorsCreated":0,"injectorsDestroyed":0,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":0,"controllersReleased":0,"catalogInstances":1,"guardEntered":4,"guardCleanups":4,"interceptorEvents":0,"interceptorCleanups":0,"controllerConstructions":0,"handlerCalls":0,"cleanupOrderChecks":0,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} | pass |
| dev server | invalid Standard Schema input is safe and destroys the authorized request injector | 400 {"error":"invalid_request","source":"params"} | {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":1,"controllersReleased":1,"catalogInstances":1,"guardEntered":1,"guardCleanups":1,"interceptorEvents":2,"interceptorCleanups":2,"controllerConstructions":1,"handlerCalls":1,"cleanupOrderChecks":1,"cleanupOrderFailures":0,"interceptorLiveChecks":2,"interceptorLiveFailures":0} | pass |
| dev server | malformed, unsupported and oversized JSON failures clean up request injectors | 400 {"error":"invalid_request","source":"body"}<br>415 {"error":"unsupported_media_type","source":"body"}<br>413 {"error":"payload_too_large","source":"body"} | {"injectorsCreated":3,"injectorsDestroyed":3,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":3,"controllersReleased":3,"catalogInstances":1,"guardEntered":3,"guardCleanups":3,"interceptorEvents":6,"interceptorCleanups":6,"controllerConstructions":3,"handlerCalls":3,"cleanupOrderChecks":3,"cleanupOrderFailures":0,"interceptorLiveChecks":6,"interceptorLiveFailures":0} | pass |
| dev server | request provider override remains local to one child injector | 200 {"product":{"id":"101","name":"Product 101"},"tag":"X","met":null,"principal":"alpha","identity":12,"identityProductId":"101","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"only-this-request","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]}<br>200 {"product":{"id":"102","name":"Product 102"},"tag":"X","met":null,"principal":"beta","identity":13,"identityProductId":"102","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} | {"injectorsCreated":2,"injectorsDestroyed":2,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":2,"controllersReleased":2,"catalogInstances":1,"guardEntered":2,"guardCleanups":2,"interceptorEvents":4,"interceptorCleanups":4,"controllerConstructions":2,"handlerCalls":2,"cleanupOrderChecks":2,"cleanupOrderFailures":0,"interceptorLiveChecks":4,"interceptorLiveFailures":0} | pass |
| dev server | controller factory failure runs guard and interceptor cleanup without an injector | 500 {"error":true,"message":"Request failed."} | {"injectorsCreated":0,"injectorsDestroyed":0,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":0,"controllersReleased":0,"catalogInstances":1,"guardEntered":1,"guardCleanups":1,"interceptorEvents":2,"interceptorCleanups":2,"controllerConstructions":0,"handlerCalls":0,"cleanupOrderChecks":1,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} | pass |
| dev server | provider factory failure destroys the created request injector | 500 {"error":true,"message":"Request failed."} | {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":0,"controllersReleased":0,"catalogInstances":1,"guardEntered":1,"guardCleanups":1,"interceptorEvents":2,"interceptorCleanups":2,"controllerConstructions":0,"handlerCalls":0,"cleanupOrderChecks":1,"cleanupOrderFailures":0,"interceptorLiveChecks":2,"interceptorLiveFailures":0} | pass |
| dev server | controller constructor failure destroys its request injector | 500 {"error":true,"message":"Request failed."} | {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":1,"controllersReleased":0,"catalogInstances":1,"guardEntered":1,"guardCleanups":1,"interceptorEvents":2,"interceptorCleanups":2,"controllerConstructions":1,"handlerCalls":0,"cleanupOrderChecks":1,"cleanupOrderFailures":0,"interceptorLiveChecks":2,"interceptorLiveFailures":0} | pass |
| dev server | handler, interceptor and multiple cleanup failures settle without leaking private errors | 500 {"error":true,"message":"Request failed."}<br>500 {"error":true,"message":"Request failed."}<br>500 {"error":true,"message":"Request failed."}<br>500 {"error":true,"message":"Request failed."}<br>500 {"error":true,"message":"3 Strata cleanup callbacks failed."} | {"injectorsCreated":5,"injectorsDestroyed":5,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":5,"controllersReleased":5,"catalogInstances":1,"guardEntered":5,"guardCleanups":5,"interceptorEvents":10,"interceptorCleanups":10,"controllerConstructions":5,"handlerCalls":5,"cleanupOrderChecks":5,"cleanupOrderFailures":0,"interceptorLiveChecks":11,"interceptorLiveFailures":0} | pass |
| dev server | interceptor failure before next cleans up without constructing a controller | 500 {"error":true,"message":"Request failed."} | {"injectorsCreated":0,"injectorsDestroyed":0,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":0,"controllersReleased":0,"catalogInstances":1,"guardEntered":1,"guardCleanups":1,"interceptorEvents":2,"interceptorCleanups":2,"controllerConstructions":0,"handlerCalls":0,"cleanupOrderChecks":1,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} | pass |
| dev server | native Web Response is preserved through interceptors | 200 native-response-preserved | {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":1,"controllersReleased":1,"catalogInstances":1,"guardEntered":1,"guardCleanups":1,"interceptorEvents":2,"interceptorCleanups":2,"controllerConstructions":1,"handlerCalls":1,"cleanupOrderChecks":1,"cleanupOrderFailures":0,"interceptorLiveChecks":2,"interceptorLiveFailures":0} | pass |
| dev server | 24 mixed requests in three overlapping eight-request batches isolate state | 200 {"product":{"id":"101","name":"Product 101"},"tag":"M0","met":true,"principal":"alpha","identity":22,"identityProductId":"101","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]}<br>200 {"product":{"id":"102","name":"Product 102"},"tag":"M1","met":true,"principal":"beta","identity":24,"identityProductId":"102","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]}<br>200 {"product":{"id":"103","name":"Product 103"},"tag":"M2","met":true,"principal":"alpha","identity":23,"identityProductId":"103","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]}<br>200 {"product":{"id":"104","name":"Product 104"},"tag":"M3","met":true,"principal":"beta","identity":25,"identityProductId":"104","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]}<br>200 {"product":{"id":"105","name":"Product 105"},"tag":"M4","met":true,"principal":"alpha","identity":26,"identityProductId":"105","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]}<br>200 {"product":{"id":"106","name":"Product 106"},"tag":"M5","met":true,"principal":"beta","identity":27,"identityProductId":"106","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]}<br>200 {"product":{"id":"107","name":"Product 107"},"tag":"M6","met":true,"principal":"alpha","identity":28,"identityProductId":"107","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]}<br>200 {"product":{"id":"108","name":"Product 108"},"tag":"M7","met":true,"principal":"beta","identity":29,"identityProductId":"108","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]}<br>401 {"error":"unauthorized"}<br>401 {"error":"unauthorized"}<br>401 {"error":"unauthorized"}<br>401 {"error":"unauthorized"}<br>400 {"error":"invalid_request","source":"params"}<br>400 {"error":"invalid_request","source":"params"}<br>400 {"error":"invalid_request","source":"params"}<br>400 {"error":"invalid_request","source":"params"}<br>500 {"error":true,"message":"Request failed."}<br>500 {"error":true,"message":"Request failed."}<br>500 {"error":true,"message":"Request failed."}<br>500 {"error":true,"message":"Request failed."}<br>500 {"error":true,"message":"Request failed."}<br>500 {"error":true,"message":"Request failed."}<br>500 {"error":true,"message":"Request failed."}<br>500 {"error":true,"message":"Request failed."} | {"injectorsCreated":20,"injectorsDestroyed":20,"injectorsAlive":0,"maxInjectorsAlive":8,"identitiesDestroyed":20,"controllersReleased":20,"catalogInstances":1,"guardEntered":24,"guardCleanups":24,"interceptorEvents":40,"interceptorCleanups":40,"controllerConstructions":20,"handlerCalls":20,"cleanupOrderChecks":20,"cleanupOrderFailures":0,"interceptorLiveChecks":44,"interceptorLiveFailures":0} | pass |
Expected: overlapping requests both answer met: true, share catalogInstance: 1, and get
different identity values, each bound to its own product id; the controller and
RequestAudit see the same RequestIdentity within a request; the injected STRATA_REQUEST
is the handler's argument; inject() in a handler throws NG0203 before and after an
await; no injector is destroyed before the handler's last await settles; and every request
injector created — on success, synchronous throw or asynchronous rejection — is destroyed, running
RequestIdentity.ngOnDestroy() and the controller's DestroyRef callback, before the response
arrives. Limits: the application injector is not the injector of an Analog SSR render or of
Analog's server functions (no supported seam reaches either), and cleanup covers the controller
invocation, not a streamed response body. The experiment's files carry STRATA_ANALOG_ANGULAR_DI_MARKER,
scanned in the module-graph section below.
Package boundary. @strata-sc/analog declares:
| Field | Value |
|---|---|
| dependencies | {"@standard-schema/spec":"1.1.0","@strata-sc/core":"workspace:*"} |
| peerDependencies | {} |
| devDependencies | {"h3":"^1.15.11","valibot":"^1.5.0","zod":"^4.6.5"} |
| engines.node | >=22 |
Bare module specifiers in the built package (what a consumer's bundler and tsc must resolve):
| Built file | Imports |
|---|---|
packages/analog/dist/api-boundary.d.ts | @strata-sc/core |
packages/analog/dist/cleanup-scope.d.ts | none |
packages/analog/dist/errors.d.ts | none |
packages/analog/dist/http-error.d.ts | none |
packages/analog/dist/index.d.ts | none |
packages/analog/dist/index.js | @strata-sc/core |
packages/analog/dist/input-error.d.ts | none |
packages/analog/dist/register-controllers.d.ts | @standard-schema/spec, @strata-sc/core |
packages/analog/dist/request-observation.d.ts | none |
packages/analog/dist/responses.d.ts | none |
packages/analog/dist/stream-lifecycle.d.ts | none |
packages/analog/dist/utils/build-route-path.d.ts | none |
The fixture reaches the package only through @strata-sc/analog
(its exports entry, built dist).
Type check against Nitro's real types. A generated probe calls registerControllers(nitroApp.router, …) inside defineNitroPlugin and type-checks it against the nitropack Analog resolves: the router is accepted without a cast, and nitroApp.h3App (H3 v1 App) is rejected — a @ts-expect-error negative control that fails if the parameter were loosely typed.
Command: pnpm exec tsc -p .strata-typecheck/tsconfig.json (exit 0).
Server/client module graph baseline
STRATA_ANALOG_SERVER_ONLY_MARKER is used only from src/server/strata/hello.controller.ts (imported by Nitro
routes). STRATA_ANALOG_CLIENT_CONTROL_MARKER is a positive control that lives in a client page, so a scan that finds it
proves the scanner can see client output at all.
| Output | Server-only marker | Client control marker | @strata-sc/core internal symbol |
|---|---|---|---|
server (dist/analog/server) | chunks/routes/api/strata-core.mjs | chunks/_/index.page-_tgWCWVj.mjs | chunks/nitro/nitro.mjs |
SSR bundle (dist/ssr) | none | assets/index.page-_tgWCWVj.js | none |
client (dist/client) | none | assets/index.page-TukbXcQ3.js | none |
public (dist/analog/public) | none | assets/index.page-TukbXcQ3.js, index.html | none |
STRATA_ANALOG_REGISTERED_CONTROLLER_MARKER is used only by the controller registered through @strata-sc/analog
(src/server/strata/users.controller.ts, imported only by the Nitro plugin). STRATA_ANALOG_CONTROLLER_FACTORY_MARKER is
used only by src/server/strata/greeting.controller.ts, registered with a custom controllerFactory:
| Output | Registered-controller marker | Controller-factory marker | Angular DI experiment marker | @strata-sc/analog (error class name) | @strata-sc/h3 (error class name) |
|---|---|---|---|---|---|
server (dist/analog/server) | chunks/nitro/nitro.mjs | chunks/nitro/nitro.mjs | chunks/nitro/nitro.mjs | chunks/nitro/nitro.mjs | none |
SSR bundle (dist/ssr) | none | none | none | none | none |
client (dist/client) | none | none | none | none | none |
public (dist/analog/public) | none | none | none | none | none |
Expected and observed: server output contains the server-only markers and the adapter; dist/client,
dist/analog/public and dist/ssr do not; no @strata-sc/h3 code is in any output. This is only a
baseline — nothing here is a Server Component or a guarantee for future code.
Commands
| Step | Command | Exit |
|---|---|---|
| Build @strata-sc/core | pnpm --filter @strata-sc/core run build | 0 |
| Build @strata-sc/analog | pnpm --filter @strata-sc/analog run build | 0 |
Type-check @strata-sc/analog against Nitro's types | pnpm exec tsc -p .strata-typecheck/tsconfig.json | 0 |
G1 probe build (stock experimentalDecorators: true) | pnpm exec vite build --config vite.probe.config.ts | 1 |
G2 probe build (experimentalDecorators: false) | pnpm exec vite build --config vite.probe.config.ts | 0 |
| G2 production server | node dist/analog/server/index.mjs | stopped by test |
| G2 dev server | pnpm exec vite --port <port> --strictPort --config vite.probe.config.ts | stopped by test |
| Baseline production build | pnpm --filter @strata-fixtures/analog run build | 0 |
| Production server | node dist/analog/server/index.mjs | stopped by test |
| Dev server | pnpm exec vite --port <port> --strictPort | stopped by test |
Checks
| Check | Result | Detail |
|---|---|---|
| Analog's Nitro plugin resolves the same H3 as nitropack | pass | plugin → 1.15.11, nitropack → 1.15.11 |
| Analog/Nitro run H3 v1 and @strata-sc/h3 runs H3 v2 | pass | nitropack → 1.15.11, @strata-sc/h3 → 2.0.1-rc.32 |
No Nitro 3 (nitro package) in the workspace graph | pass | lockfile scan |
| @strata-sc/analog builds (JS + declarations) | pass | |
| @strata-sc/analog declares only @strata-sc/core and Standard Schema, with no peers | pass | {"dependencies":{"@standard-schema/spec":"1.1.0","@strata-sc/core":"workspace:*"}} |
| @strata-sc/analog does not depend on @strata-sc/h3 (in any dependency field) | pass | @standard-schema/spec, @strata-sc/core, h3, valibot, zod |
| The built @strata-sc/analog imports only core and Standard Schema — no h3, nitropack or Analog | pass | @standard-schema/spec, @strata-sc/core |
| Public package engines are not raised to the fixture's Node floor | pass | core: >=22, h3: >=22, analog: >=22 |
The fixture imports only the public @strata-sc/analog entry (no deep imports) | pass | @strata-sc/analog |
| The fixture has no alias or path into packages/analog source | pass | |
| The fixture resolves @strata-sc/analog to the workspace package (via its package.json exports) | pass | /Volumes/WorkStation/projects/Strata/packages/analog vs /Volumes/WorkStation/projects/Strata/packages/analog |
| The Strata route is registered by the Nitro plugin, not by a file-system route wrapper | pass | plugin calls registerControllers(nitroApp.router, …) |
Nitro's real nitroApp.router type is accepted by registerControllers as-is; nitroApp.h3App is rejected | pass | |
G2 failure cause: Node's import+node tslib entry is missing from the server output | pass | traced: package.json, tslib.es6.mjs |
| Analog production build exits 0 | pass | exit 0 |
| Client and server artifacts are generated | pass | |
| Built server records the resolved Nitro version | pass | nitro.json 2.13.4, resolved 2.13.4 |
| Nitro lowered the consumer's standard decorators (no raw decorator syntax left) | pass | dist/analog/server/chunks/routes/api/strata-core.mjs |
The built server bundles H3 v1 (createApp/createRouter) and no H3 v2 H3 class | pass | {"file":"dist/analog/server/chunks/nitro/nitro.mjs","createApp":true,"createRouter":true,"h3Class":false} |
| Native Analog route works in production | pass | 200 {"source":"analog"} |
| getControllerDefinition() metadata is correct in production | pass | 200 {"source":"strata-core","definition":{"path":"/hello","routes":[{"method":"GET","path":"/","handler":"hello"}]},"invoked":{"message":"Hello from Strata","marker":"STRATA_ANALOG_SERVER_ONLY_MARKER"}} |
| GET /api/strata/users (registered by @strata-sc/analog) returns 200 JSON in production | pass | 200 application/json [{"id":"1","name":"Ada"}] |
| GET /api/strata/users/:id (dynamic route) returns 200 JSON in production | pass | 200 application/json {"id":"42","name":"Ada"} |
| Two GET /api/strata/lifecycle requests each get a new controller ({ calls: 1 }) in production | pass | 200 {"calls":1} | 200 {"calls":1} |
| GET /api/strata/greeting uses the custom controllerFactory per request in production | pass | 200 {"greeting":"hello","calls":1,"intercepted":true} | 200 {"greeting":"hello","calls":1,"intercepted":true} |
| Angular DI experiment releases request injectors via onCleanup, with no Nitro request-lifecycle hooks | pass | onCleanup() not found |
| Angular DI controller, services and factory import no Nitro/H3 module or event type | pass | none |
| GET /api/strata/methods reaches its own Strata handler with the right method/params/body in production | pass | 200 application/json {"handler":"list","method":"GET"} |
| POST /api/strata/methods {"name":"Ada"} reaches its own Strata handler with the right method/params/body in production | pass | 200 application/json {"handler":"create","method":"POST","name":"Ada"} |
| PUT /api/strata/methods/7 reaches its own Strata handler with the right method/params/body in production | pass | 200 application/json {"handler":"replace","method":"PUT","id":"7"} |
| PATCH /api/strata/methods/42 {"name":"Grace"} reaches its own Strata handler with the right method/params/body in production | pass | 200 application/json {"handler":"update","method":"PATCH","id":"42","name":"Grace"} |
| DELETE /api/strata/methods/42 reaches its own Strata handler with the right method/params/body in production | pass | 200 application/json {"handler":"remove","method":"DELETE","deleted":"42"} |
| Standard Schema validation case 1 works in production | pass | 200 application/json {"params":{"id":42},"query":{"tag":["a","b"]}} |
| Standard Schema validation case 2 works in production | pass | 200 application/json {"count":7} |
| Standard Schema validation case 3 works in production | pass | 400 application/json; charset=utf-8 {"error":"invalid_request","source":"params"} |
| 24 overlapping mixed valid and invalid validation requests remain isolated in production | pass | per-request params, query, transformed body, safe error and private canary checks |
| JSON body failures return real 400/413/415 no-store responses in production | pass | malformed JSON, text/plain and >1 MiB JSON |
| 24 overlapping synthetic guard requests preserve public, 401, 403 and owner access in production | pass | opaque fixture tokens and denial non-disclosure |
| Inventory HTTP contract and protected mutation behavior work in production | pass | safe errors, validated bodies, 201, patch and bodyless 204 |
| HTTP routing and API boundary contract (JSON 404, 405 + Allow, HEAD, OPTIONS, native routes, 24 overlapping requests) works in production | pass | owned /api namespace, native routes not shadowed, pages stay HTML |
| Angular DI experiment (production): two requests held at a rendezvous | pass | 200 {"met":true,"product":{"id":"a","name":"Product a"},"catalogInstance":1,"identity":2,"identityProductId":"a","auditSharesIdentity":true,"injectedRequestIsArgument":true,"injectInHandler":"NG0203","injectAfterAwait":"NG0203","destroyedBeforeResponse":false} | 200 {"met":true,"product":{"id":"b","name":"Product b"},"catalogInstance":1,"identity":1,"identityProductId":"b","auditSharesIdentity":true,"injectedRequestIsArgument":true,"injectInHandler":"NG0203","injectAfterAwait":"NG0203","destroyedBeforeResponse":false} delta {"injectorsCreated":2,"injectorsDestroyed":2,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":2,"controllersReleased":2,"catalogInstances":1,"guardEntered":0,"guardCleanups":0,"interceptorEvents":0,"interceptorCleanups":0,"controllerConstructions":2,"handlerCalls":2,"cleanupOrderChecks":0,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} |
| Angular DI experiment (production): one request alone at a rendezvous (control) | pass | 200 {"met":false,"product":{"id":"solo","name":"Product solo"},"catalogInstance":1,"identity":3,"identityProductId":"solo","auditSharesIdentity":true,"injectedRequestIsArgument":true,"injectInHandler":"NG0203","injectAfterAwait":"NG0203","destroyedBeforeResponse":false} delta {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":1,"controllersReleased":1,"catalogInstances":1,"guardEntered":0,"guardCleanups":0,"interceptorEvents":0,"interceptorCleanups":0,"controllerConstructions":1,"handlerCalls":1,"cleanupOrderChecks":0,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} |
| Angular DI experiment (production): handler throws synchronously | pass | 500 {"error":true,"message":"Server Error"} delta {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":1,"controllersReleased":1,"catalogInstances":1,"guardEntered":0,"guardCleanups":0,"interceptorEvents":0,"interceptorCleanups":0,"controllerConstructions":1,"handlerCalls":1,"cleanupOrderChecks":0,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} |
| Angular DI experiment (production): async handler rejects after an await | pass | 500 {"error":true,"message":"Server Error"} delta {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":1,"controllersReleased":1,"catalogInstances":1,"guardEntered":0,"guardCleanups":0,"interceptorEvents":0,"interceptorCleanups":0,"controllerConstructions":1,"handlerCalls":1,"cleanupOrderChecks":0,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} |
| Angular DI experiment (production): authorized DI routes compose verified principals, interceptors and Standard Schema | pass | 200 {"product":{"id":"101","name":"Product 101"},"tag":"TRANSFORMED","met":null,"principal":"alpha","identity":7,"identityProductId":"101","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} | 200 {"id":102,"count":7,"principal":"beta","identity":6,"identityProductId":"102","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} delta {"injectorsCreated":2,"injectorsDestroyed":2,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":2,"controllersReleased":2,"catalogInstances":1,"guardEntered":2,"guardCleanups":2,"interceptorEvents":4,"interceptorCleanups":4,"controllerConstructions":2,"handlerCalls":2,"cleanupOrderChecks":2,"cleanupOrderFailures":0,"interceptorLiveChecks":4,"interceptorLiveFailures":0} |
| Angular DI experiment (production): 401 and 403 denials run guard cleanup before any interceptor or Angular construction | pass | 401 {"error":"unauthorized"} | 401 {"error":"unauthorized"} | 403 {"error":"forbidden"} | 403 {"error":"forbidden"} delta {"injectorsCreated":0,"injectorsDestroyed":0,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":0,"controllersReleased":0,"catalogInstances":1,"guardEntered":4,"guardCleanups":4,"interceptorEvents":0,"interceptorCleanups":0,"controllerConstructions":0,"handlerCalls":0,"cleanupOrderChecks":0,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} |
| Angular DI experiment (production): invalid Standard Schema input is safe and destroys the authorized request injector | pass | 400 {"error":"invalid_request","source":"params"} delta {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":1,"controllersReleased":1,"catalogInstances":1,"guardEntered":1,"guardCleanups":1,"interceptorEvents":2,"interceptorCleanups":2,"controllerConstructions":1,"handlerCalls":1,"cleanupOrderChecks":1,"cleanupOrderFailures":0,"interceptorLiveChecks":2,"interceptorLiveFailures":0} |
| Angular DI experiment (production): malformed, unsupported and oversized JSON failures clean up request injectors | pass | 400 {"error":"invalid_request","source":"body"} | 415 {"error":"unsupported_media_type","source":"body"} | 413 {"error":"payload_too_large","source":"body"} delta {"injectorsCreated":3,"injectorsDestroyed":3,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":3,"controllersReleased":3,"catalogInstances":1,"guardEntered":3,"guardCleanups":3,"interceptorEvents":6,"interceptorCleanups":6,"controllerConstructions":3,"handlerCalls":3,"cleanupOrderChecks":3,"cleanupOrderFailures":0,"interceptorLiveChecks":6,"interceptorLiveFailures":0} |
| Angular DI experiment (production): request provider override remains local to one child injector | pass | 200 {"product":{"id":"101","name":"Product 101"},"tag":"X","met":null,"principal":"alpha","identity":13,"identityProductId":"101","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"only-this-request","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} | 200 {"product":{"id":"102","name":"Product 102"},"tag":"X","met":null,"principal":"beta","identity":12,"identityProductId":"102","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} delta {"injectorsCreated":2,"injectorsDestroyed":2,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":2,"controllersReleased":2,"catalogInstances":1,"guardEntered":2,"guardCleanups":2,"interceptorEvents":4,"interceptorCleanups":4,"controllerConstructions":2,"handlerCalls":2,"cleanupOrderChecks":2,"cleanupOrderFailures":0,"interceptorLiveChecks":4,"interceptorLiveFailures":0} |
| Angular DI experiment (production): controller factory failure runs guard and interceptor cleanup without an injector | pass | 500 {"error":true,"message":"Server Error"} delta {"injectorsCreated":0,"injectorsDestroyed":0,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":0,"controllersReleased":0,"catalogInstances":1,"guardEntered":1,"guardCleanups":1,"interceptorEvents":2,"interceptorCleanups":2,"controllerConstructions":0,"handlerCalls":0,"cleanupOrderChecks":1,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} |
| Angular DI experiment (production): provider factory failure destroys the created request injector | pass | 500 {"error":true,"message":"Server Error"} delta {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":0,"controllersReleased":0,"catalogInstances":1,"guardEntered":1,"guardCleanups":1,"interceptorEvents":2,"interceptorCleanups":2,"controllerConstructions":0,"handlerCalls":0,"cleanupOrderChecks":1,"cleanupOrderFailures":0,"interceptorLiveChecks":2,"interceptorLiveFailures":0} |
| Angular DI experiment (production): controller constructor failure destroys its request injector | pass | 500 {"error":true,"message":"Server Error"} delta {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":1,"controllersReleased":0,"catalogInstances":1,"guardEntered":1,"guardCleanups":1,"interceptorEvents":2,"interceptorCleanups":2,"controllerConstructions":1,"handlerCalls":0,"cleanupOrderChecks":1,"cleanupOrderFailures":0,"interceptorLiveChecks":2,"interceptorLiveFailures":0} |
| Angular DI experiment (production): handler, interceptor and multiple cleanup failures settle without leaking private errors | pass | 500 {"error":true,"message":"Server Error"} | 500 {"error":true,"message":"Server Error"} | 500 {"error":true,"message":"Server Error"} | 500 {"error":true,"message":"Server Error"} | 500 {"error":true,"message":"Server Error"} delta {"injectorsCreated":5,"injectorsDestroyed":5,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":5,"controllersReleased":5,"catalogInstances":1,"guardEntered":5,"guardCleanups":5,"interceptorEvents":10,"interceptorCleanups":10,"controllerConstructions":5,"handlerCalls":5,"cleanupOrderChecks":5,"cleanupOrderFailures":0,"interceptorLiveChecks":11,"interceptorLiveFailures":0} |
| Angular DI experiment (production): interceptor failure before next cleans up without constructing a controller | pass | 500 {"error":true,"message":"Server Error"} delta {"injectorsCreated":0,"injectorsDestroyed":0,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":0,"controllersReleased":0,"catalogInstances":1,"guardEntered":1,"guardCleanups":1,"interceptorEvents":2,"interceptorCleanups":2,"controllerConstructions":0,"handlerCalls":0,"cleanupOrderChecks":1,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} |
| Angular DI experiment (production): native Web Response is preserved through interceptors | pass | 200 native-response-preserved delta {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":1,"controllersReleased":1,"catalogInstances":1,"guardEntered":1,"guardCleanups":1,"interceptorEvents":2,"interceptorCleanups":2,"controllerConstructions":1,"handlerCalls":1,"cleanupOrderChecks":1,"cleanupOrderFailures":0,"interceptorLiveChecks":2,"interceptorLiveFailures":0} |
| Angular DI experiment (production): 24 mixed requests in three overlapping eight-request batches isolate state | pass | 200 {"product":{"id":"101","name":"Product 101"},"tag":"M0","met":true,"principal":"alpha","identity":22,"identityProductId":"101","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} | 200 {"product":{"id":"102","name":"Product 102"},"tag":"M1","met":true,"principal":"beta","identity":23,"identityProductId":"102","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} | 200 {"product":{"id":"103","name":"Product 103"},"tag":"M2","met":true,"principal":"alpha","identity":24,"identityProductId":"103","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} | 200 {"product":{"id":"104","name":"Product 104"},"tag":"M3","met":true,"principal":"beta","identity":26,"identityProductId":"104","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} | 200 {"product":{"id":"105","name":"Product 105"},"tag":"M4","met":true,"principal":"alpha","identity":25,"identityProductId":"105","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} | 200 {"product":{"id":"106","name":"Product 106"},"tag":"M5","met":true,"principal":"beta","identity":27,"identityProductId":"106","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} | 200 {"product":{"id":"107","name":"Product 107"},"tag":"M6","met":true,"principal":"alpha","identity":28,"identityProductId":"107","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} | 200 {"product":{"id":"108","name":"Product 108"},"tag":"M7","met":true,"principal":"beta","identity":29,"identityProductId":"108","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} | 401 {"error":"unauthorized"} | 401 {"error":"unauthorized"} | 401 {"error":"unauthorized"} | 401 {"error":"unauthorized"} | 400 {"error":"invalid_request","source":"params"} | 400 {"error":"invalid_request","source":"params"} | 400 {"error":"invalid_request","source":"params"} | 400 {"error":"invalid_request","source":"params"} | 500 {"error":true,"message":"Server Error"} | 500 {"error":true,"message":"Server Error"} | 500 {"error":true,"message":"Server Error"} | 500 {"error":true,"message":"Server Error"} | 500 {"error":true,"message":"Server Error"} | 500 {"error":true,"message":"Server Error"} | 500 {"error":true,"message":"Server Error"} | 500 {"error":true,"message":"Server Error"} delta {"injectorsCreated":20,"injectorsDestroyed":20,"injectorsAlive":0,"maxInjectorsAlive":8,"identitiesDestroyed":20,"controllersReleased":20,"catalogInstances":1,"guardEntered":24,"guardCleanups":24,"interceptorEvents":40,"interceptorCleanups":40,"controllerConstructions":20,"handlerCalls":20,"cleanupOrderChecks":20,"cleanupOrderFailures":0,"interceptorLiveChecks":44,"interceptorLiveFailures":0} |
| Server-only marker is present in server output | pass | dist/analog/server |
| Server-only marker is absent from client output | pass | |
| Server-only marker is absent from public output | pass | |
| Server-only marker is absent from SSR bundle output | pass | |
| Client control marker is found in client output (scanner sanity) | pass | dist/client and dist/analog/public |
| The @strata-sc/analog-registered controllers' markers are present in server output | pass | dist/analog/server |
| @strata-sc/analog is bundled into the server output | pass | dist/analog/server |
| The registered controllers' markers and @strata-sc/analog are absent from client output | pass | |
| The registered controllers' markers and @strata-sc/analog are absent from public output | pass | |
| The registered controllers' markers and @strata-sc/analog are absent from SSR bundle output | pass | |
| No @strata-sc/h3 code is in any output (the Analog adapter does not use it) | pass | |
| @strata-sc/core internals are absent from client output | pass | dist/client and dist/analog/public |
| Native Analog route works in the dev server | pass | 200 {"source":"analog"} |
| getControllerDefinition() metadata is correct in the dev server | pass | 200 {"source":"strata-core","definition":{"path":"/hello","routes":[{"method":"GET","path":"/","handler":"hello"}]},"invoked":{"message":"Hello from Strata","marker":"STRATA_ANALOG_SERVER_ONLY_MARKER"}} |
| GET /api/strata/users (registered by @strata-sc/analog) returns 200 JSON in the dev server | pass | 200 application/json [{"id":"1","name":"Ada"}] |
| GET /api/strata/users/:id (dynamic route) returns 200 JSON in the dev server | pass | 200 application/json {"id":"42","name":"Ada"} |
| Two GET /api/strata/lifecycle requests each get a new controller ({ calls: 1 }) in the dev server | pass | 200 {"calls":1} | 200 {"calls":1} |
| GET /api/strata/greeting uses the custom controllerFactory per request in the dev server | pass | 200 {"greeting":"hello","calls":1,"intercepted":true} | 200 {"greeting":"hello","calls":1,"intercepted":true} |
| GET /api/strata/methods reaches its own Strata handler with the right method/params/body in the dev server | pass | 200 application/json {"handler":"list","method":"GET"} |
| POST /api/strata/methods {"name":"Ada"} reaches its own Strata handler with the right method/params/body in the dev server | pass | 200 application/json {"handler":"create","method":"POST","name":"Ada"} |
| PUT /api/strata/methods/7 reaches its own Strata handler with the right method/params/body in the dev server | pass | 200 application/json {"handler":"replace","method":"PUT","id":"7"} |
| PATCH /api/strata/methods/42 {"name":"Grace"} reaches its own Strata handler with the right method/params/body in the dev server | pass | 200 application/json {"handler":"update","method":"PATCH","id":"42","name":"Grace"} |
| DELETE /api/strata/methods/42 reaches its own Strata handler with the right method/params/body in the dev server | pass | 200 application/json {"handler":"remove","method":"DELETE","deleted":"42"} |
| Standard Schema validation case 1 works in the dev server | pass | 200 application/json {"params":{"id":42},"query":{"tag":["a","b"]}} |
| Standard Schema validation case 2 works in the dev server | pass | 200 application/json {"count":7} |
| Standard Schema validation case 3 works in the dev server | pass | 400 application/json; charset=utf-8 {"error":"invalid_request","source":"params"} |
| 24 overlapping mixed valid and invalid validation requests remain isolated in the dev server | pass | per-request params, query, transformed body, safe error and private canary checks |
| JSON body failures return real 400/413/415 no-store responses in the dev server | pass | malformed JSON, text/plain and >1 MiB JSON |
| 24 overlapping synthetic guard requests preserve public, 401, 403 and owner access in the dev server | pass | opaque fixture tokens and denial non-disclosure |
| Inventory HTTP contract and protected mutation behavior work in the dev server | pass | safe errors, validated bodies, 201, patch and bodyless 204 |
| HTTP routing and API boundary contract (JSON 404, 405 + Allow, HEAD, OPTIONS, native routes, 24 overlapping requests) works in the dev server | pass | owned /api namespace, native routes not shadowed, pages stay HTML |
| Angular DI experiment (the dev server): two requests held at a rendezvous | pass | 200 {"met":true,"product":{"id":"a","name":"Product a"},"catalogInstance":1,"identity":1,"identityProductId":"a","auditSharesIdentity":true,"injectedRequestIsArgument":true,"injectInHandler":"NG0203","injectAfterAwait":"NG0203","destroyedBeforeResponse":false} | 200 {"met":true,"product":{"id":"b","name":"Product b"},"catalogInstance":1,"identity":2,"identityProductId":"b","auditSharesIdentity":true,"injectedRequestIsArgument":true,"injectInHandler":"NG0203","injectAfterAwait":"NG0203","destroyedBeforeResponse":false} delta {"injectorsCreated":2,"injectorsDestroyed":2,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":2,"controllersReleased":2,"catalogInstances":1,"guardEntered":0,"guardCleanups":0,"interceptorEvents":0,"interceptorCleanups":0,"controllerConstructions":2,"handlerCalls":2,"cleanupOrderChecks":0,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} |
| Angular DI experiment (the dev server): one request alone at a rendezvous (control) | pass | 200 {"met":false,"product":{"id":"solo","name":"Product solo"},"catalogInstance":1,"identity":3,"identityProductId":"solo","auditSharesIdentity":true,"injectedRequestIsArgument":true,"injectInHandler":"NG0203","injectAfterAwait":"NG0203","destroyedBeforeResponse":false} delta {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":1,"controllersReleased":1,"catalogInstances":1,"guardEntered":0,"guardCleanups":0,"interceptorEvents":0,"interceptorCleanups":0,"controllerConstructions":1,"handlerCalls":1,"cleanupOrderChecks":0,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} |
| Angular DI experiment (the dev server): handler throws synchronously | pass | 500 {"error":true,"message":"thrown by request 4"} delta {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":1,"controllersReleased":1,"catalogInstances":1,"guardEntered":0,"guardCleanups":0,"interceptorEvents":0,"interceptorCleanups":0,"controllerConstructions":1,"handlerCalls":1,"cleanupOrderChecks":0,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} |
| Angular DI experiment (the dev server): async handler rejects after an await | pass | 500 {"error":true,"message":"rejected by request 5"} delta {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":1,"controllersReleased":1,"catalogInstances":1,"guardEntered":0,"guardCleanups":0,"interceptorEvents":0,"interceptorCleanups":0,"controllerConstructions":1,"handlerCalls":1,"cleanupOrderChecks":0,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} |
| Angular DI experiment (the dev server): authorized DI routes compose verified principals, interceptors and Standard Schema | pass | 200 {"product":{"id":"101","name":"Product 101"},"tag":"TRANSFORMED","met":null,"principal":"alpha","identity":7,"identityProductId":"101","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} | 200 {"id":102,"count":7,"principal":"beta","identity":6,"identityProductId":"102","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} delta {"injectorsCreated":2,"injectorsDestroyed":2,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":2,"controllersReleased":2,"catalogInstances":1,"guardEntered":2,"guardCleanups":2,"interceptorEvents":4,"interceptorCleanups":4,"controllerConstructions":2,"handlerCalls":2,"cleanupOrderChecks":2,"cleanupOrderFailures":0,"interceptorLiveChecks":4,"interceptorLiveFailures":0} |
| Angular DI experiment (the dev server): 401 and 403 denials run guard cleanup before any interceptor or Angular construction | pass | 401 {"error":"unauthorized"} | 401 {"error":"unauthorized"} | 403 {"error":"forbidden"} | 403 {"error":"forbidden"} delta {"injectorsCreated":0,"injectorsDestroyed":0,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":0,"controllersReleased":0,"catalogInstances":1,"guardEntered":4,"guardCleanups":4,"interceptorEvents":0,"interceptorCleanups":0,"controllerConstructions":0,"handlerCalls":0,"cleanupOrderChecks":0,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} |
| Angular DI experiment (the dev server): invalid Standard Schema input is safe and destroys the authorized request injector | pass | 400 {"error":"invalid_request","source":"params"} delta {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":1,"controllersReleased":1,"catalogInstances":1,"guardEntered":1,"guardCleanups":1,"interceptorEvents":2,"interceptorCleanups":2,"controllerConstructions":1,"handlerCalls":1,"cleanupOrderChecks":1,"cleanupOrderFailures":0,"interceptorLiveChecks":2,"interceptorLiveFailures":0} |
| Angular DI experiment (the dev server): malformed, unsupported and oversized JSON failures clean up request injectors | pass | 400 {"error":"invalid_request","source":"body"} | 415 {"error":"unsupported_media_type","source":"body"} | 413 {"error":"payload_too_large","source":"body"} delta {"injectorsCreated":3,"injectorsDestroyed":3,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":3,"controllersReleased":3,"catalogInstances":1,"guardEntered":3,"guardCleanups":3,"interceptorEvents":6,"interceptorCleanups":6,"controllerConstructions":3,"handlerCalls":3,"cleanupOrderChecks":3,"cleanupOrderFailures":0,"interceptorLiveChecks":6,"interceptorLiveFailures":0} |
| Angular DI experiment (the dev server): request provider override remains local to one child injector | pass | 200 {"product":{"id":"101","name":"Product 101"},"tag":"X","met":null,"principal":"alpha","identity":12,"identityProductId":"101","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"only-this-request","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} | 200 {"product":{"id":"102","name":"Product 102"},"tag":"X","met":null,"principal":"beta","identity":13,"identityProductId":"102","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} delta {"injectorsCreated":2,"injectorsDestroyed":2,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":2,"controllersReleased":2,"catalogInstances":1,"guardEntered":2,"guardCleanups":2,"interceptorEvents":4,"interceptorCleanups":4,"controllerConstructions":2,"handlerCalls":2,"cleanupOrderChecks":2,"cleanupOrderFailures":0,"interceptorLiveChecks":4,"interceptorLiveFailures":0} |
| Angular DI experiment (the dev server): controller factory failure runs guard and interceptor cleanup without an injector | pass | 500 {"error":true,"message":"Request failed."} delta {"injectorsCreated":0,"injectorsDestroyed":0,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":0,"controllersReleased":0,"catalogInstances":1,"guardEntered":1,"guardCleanups":1,"interceptorEvents":2,"interceptorCleanups":2,"controllerConstructions":0,"handlerCalls":0,"cleanupOrderChecks":1,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} |
| Angular DI experiment (the dev server): provider factory failure destroys the created request injector | pass | 500 {"error":true,"message":"Request failed."} delta {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":0,"controllersReleased":0,"catalogInstances":1,"guardEntered":1,"guardCleanups":1,"interceptorEvents":2,"interceptorCleanups":2,"controllerConstructions":0,"handlerCalls":0,"cleanupOrderChecks":1,"cleanupOrderFailures":0,"interceptorLiveChecks":2,"interceptorLiveFailures":0} |
| Angular DI experiment (the dev server): controller constructor failure destroys its request injector | pass | 500 {"error":true,"message":"Request failed."} delta {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":1,"controllersReleased":0,"catalogInstances":1,"guardEntered":1,"guardCleanups":1,"interceptorEvents":2,"interceptorCleanups":2,"controllerConstructions":1,"handlerCalls":0,"cleanupOrderChecks":1,"cleanupOrderFailures":0,"interceptorLiveChecks":2,"interceptorLiveFailures":0} |
| Angular DI experiment (the dev server): handler, interceptor and multiple cleanup failures settle without leaking private errors | pass | 500 {"error":true,"message":"Request failed."} | 500 {"error":true,"message":"Request failed."} | 500 {"error":true,"message":"Request failed."} | 500 {"error":true,"message":"Request failed."} | 500 {"error":true,"message":"3 Strata cleanup callbacks failed."} delta {"injectorsCreated":5,"injectorsDestroyed":5,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":5,"controllersReleased":5,"catalogInstances":1,"guardEntered":5,"guardCleanups":5,"interceptorEvents":10,"interceptorCleanups":10,"controllerConstructions":5,"handlerCalls":5,"cleanupOrderChecks":5,"cleanupOrderFailures":0,"interceptorLiveChecks":11,"interceptorLiveFailures":0} |
| Angular DI experiment (the dev server): interceptor failure before next cleans up without constructing a controller | pass | 500 {"error":true,"message":"Request failed."} delta {"injectorsCreated":0,"injectorsDestroyed":0,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":0,"controllersReleased":0,"catalogInstances":1,"guardEntered":1,"guardCleanups":1,"interceptorEvents":2,"interceptorCleanups":2,"controllerConstructions":0,"handlerCalls":0,"cleanupOrderChecks":1,"cleanupOrderFailures":0,"interceptorLiveChecks":0,"interceptorLiveFailures":0} |
| Angular DI experiment (the dev server): native Web Response is preserved through interceptors | pass | 200 native-response-preserved delta {"injectorsCreated":1,"injectorsDestroyed":1,"injectorsAlive":0,"maxInjectorsAlive":2,"identitiesDestroyed":1,"controllersReleased":1,"catalogInstances":1,"guardEntered":1,"guardCleanups":1,"interceptorEvents":2,"interceptorCleanups":2,"controllerConstructions":1,"handlerCalls":1,"cleanupOrderChecks":1,"cleanupOrderFailures":0,"interceptorLiveChecks":2,"interceptorLiveFailures":0} |
| Angular DI experiment (the dev server): 24 mixed requests in three overlapping eight-request batches isolate state | pass | 200 {"product":{"id":"101","name":"Product 101"},"tag":"M0","met":true,"principal":"alpha","identity":22,"identityProductId":"101","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} | 200 {"product":{"id":"102","name":"Product 102"},"tag":"M1","met":true,"principal":"beta","identity":24,"identityProductId":"102","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} | 200 {"product":{"id":"103","name":"Product 103"},"tag":"M2","met":true,"principal":"alpha","identity":23,"identityProductId":"103","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} | 200 {"product":{"id":"104","name":"Product 104"},"tag":"M3","met":true,"principal":"beta","identity":25,"identityProductId":"104","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} | 200 {"product":{"id":"105","name":"Product 105"},"tag":"M4","met":true,"principal":"alpha","identity":26,"identityProductId":"105","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} | 200 {"product":{"id":"106","name":"Product 106"},"tag":"M5","met":true,"principal":"beta","identity":27,"identityProductId":"106","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} | 200 {"product":{"id":"107","name":"Product 107"},"tag":"M6","met":true,"principal":"alpha","identity":28,"identityProductId":"107","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} | 200 {"product":{"id":"108","name":"Product 108"},"tag":"M7","met":true,"principal":"beta","identity":29,"identityProductId":"108","auditSharesIdentity":true,"injectedRequestIsArgument":true,"configuration":"application","destroyedBeforeResponse":false,"interceptorOrder":["timing:before","outcome:before","injector:created","outcome:after","timing:after"]} | 401 {"error":"unauthorized"} | 401 {"error":"unauthorized"} | 401 {"error":"unauthorized"} | 401 {"error":"unauthorized"} | 400 {"error":"invalid_request","source":"params"} | 400 {"error":"invalid_request","source":"params"} | 400 {"error":"invalid_request","source":"params"} | 400 {"error":"invalid_request","source":"params"} | 500 {"error":true,"message":"Request failed."} | 500 {"error":true,"message":"Request failed."} | 500 {"error":true,"message":"Request failed."} | 500 {"error":true,"message":"Request failed."} | 500 {"error":true,"message":"Request failed."} | 500 {"error":true,"message":"Request failed."} | 500 {"error":true,"message":"Request failed."} | 500 {"error":true,"message":"Request failed."} delta {"injectorsCreated":20,"injectorsDestroyed":20,"injectorsAlive":0,"maxInjectorsAlive":8,"identitiesDestroyed":20,"controllersReleased":20,"catalogInstances":1,"guardEntered":24,"guardCleanups":24,"interceptorEvents":40,"interceptorCleanups":40,"controllerConstructions":20,"handlerCalls":20,"cleanupOrderChecks":20,"cleanupOrderFailures":0,"interceptorLiveChecks":44,"interceptorLiveFailures":0} |
| Arm S-dev matches its recorded classification | pass | expected A, observed A |
| Arm S-prod matches its recorded classification | pass | expected A, observed A |
| Arm N-dev matches its recorded classification | pass | expected A, observed A |
| Arm N-prod matches its recorded classification | pass | expected A, observed A |
| Arm G1 matches its recorded classification | pass | expected B, observed B |
| Arm G2-build matches its recorded classification | pass | expected A, observed A |
| Arm G2-prod matches its recorded classification | pass | expected C, observed C |
| Arm G2-dev matches its recorded classification | pass | expected A, observed A |
| The seam is observable through the public Nitro plugin API | pass | captureError, h3App, hooks, localCall, localFetch, router |
| The seam has the same shape in dev and production | pass | snapshot comparison |
Recorded seam outcome is B (H3 v1 app, no .on; @strata-sc/h3 needs .on) | pass | outcome B, strataH3RequiresAppOn true |
Blockers discovered
-
H3 major mismatch at the seam. Analog 2.7.2 →
nitropack2.13.4 → H3 v1. The only public H3 handle (nitroApp.h3App) is an H3 v1Appwith no.on().@strata-sc/h3needs an H3 v2H3instance, so it cannot mount directly (Outcome B). Resolved for Analog without touching it:@strata-sc/analogis a sibling adapter onnitroApp.routerand does not depend on@strata-sc/h3.@strata-sc/h3stays an H3 v2-only adapter. -
Phantom
h3dependency in@analogjs/vite-plugin-nitro@2.7.2. It importsh3without declaring it. Under pnpm's default hoisting, in a workspace that also contains@strata-sc/h3, the bare import resolved to H3 v2 and the Vite config failed to load. This repo now declares the H3 major Nitro 2 uses throughpackageExtensionsinpnpm-workspace.yaml(workspace configuration only; no Strata code changed). Recorded failure before that workaround:failed to load config from <repo>/apps/analog-fixture/vite.config.ts error during build: file://<repo>/node_modules/.pnpm/@analogjs+vite-plugin-nitro@2.7.2_…/node_modules/@analogjs/vite-plugin-nitro/src/lib/utils/register-dev-middleware.js:1 import { createEvent } from 'h3'; ^^^^^^^^^^^ SyntaxError: The requested module 'h3' does not provide an export named 'createEvent' -
Angular pipeline treats consumer decorators as legacy with the stock
experimentalDecorators: true(arm G1, class B). Turning it off makes the dev server work but exposes the next item. -
Production SSR of Angular-compiled standard decorators fails on
tslib(arm G2-prod, class C): TypeScript's standard-decorator lowering imports__esDecorate/__runInitializersfromtslib(importHelpers: true); Nitro traces onlytslib'smodule-condition files, so Node'simport+noderesolution oftslib/modules/index.jsfails at runtime. SSR then returns HTTP 200 with an empty router outlet — a silent failure. Not investigated further or fixed here. -
Repo-wide Node floor (addressed).
@angular/build@22andcreate-analog@2.7.2require Node^22.22.3 || ^24.15.0 || >=26, andengine-strict=truemakespnpm installfail below that. The root/workspace development floor is now>=22.22.3. The publicenginesof@strata-sc/core,@strata-sc/h3and@strata-sc/analogstay>=22: only the Angular fixture needs the higher patch. CI (node-version: 22) resolves the latest 22.x; that it satisfies the floor was not verified. -
Unchanged, not attempted: the H3 v2 / crossws declaration blocker from SPEC-001/SPEC-002. The fixture never imports
@strata-sc/h3, so it neither triggers nor hides it.
Non-blocking observations from pnpm install: pnpm ignored the install scripts of esbuild,
@parcel/watcher, lmdb and msgpackr-extract (builds and servers worked without approving them),
and warns that @angular/build wants vitest@^4.0.8 while the workspace root has vitest@5.0.1
(only used by Angular's own unit-test builder, which the fixture does not run).
Other Analog lines (npm metadata only — not installed, not executed)
| Line | @analogjs/platform | HTTP runtime it declares |
|---|---|---|
latest (this fixture) | 2.7.2 | nitropack ^2.13.1 → h3 ^1.15.11 |
beta | 2.8.0-beta.5 | nitropack ^2.13.1 |
alpha | 3.0.0-alpha.87 | nitro 3.0.260903-beta and h3 ^2.0.1-rc.31 |
The alpha line is the only one whose declared runtime is H3 v2 (same major as @strata-sc/h3).
Whether that line exposes a compatible H3 app is unverified; this fixture did not run it.