You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(runtime): mount /analytics routes only when the capability exists (#3891 follow-through, ADR-0076 D11) (#4019)
* feat(runtime): mount /analytics routes only when the capability exists (#3891 follow-through, ADR-0076 D11)
The dispatcher plugin mounted POST /analytics/query, GET /analytics/meta
and POST /analytics/sql unconditionally, so a deployment without
@objectstack/service-analytics kept the routes in its table: PUT answered
405 + Allow: POST, advertising a method on an API that isn't there (the
POST itself has answered 404 since #3989 emptied the slot).
Mounting is now capability-conditional. Plugin start() runs after the
kernel's Phase-1 init, so service presence is authoritative:
- single-kernel, `analytics` absent: routes NOT mounted — every method
answers the adapter's shared not-found contract, and a boot log names
the fix (install @objectstack/service-analytics);
- single-kernel, `analytics` registered: unchanged;
- multi-tenant host (kernel-resolver wired): mounted unconditionally —
mounts are host-global while the service lives per-project kernel, so
presence is a per-request question answered by the analytics domain's
existing handled:false → 404. New public
HttpDispatcher.isMultiTenantHost() exposes the mode.
Tests: three conditional-mount cases in dispatcher-plugin.routes.test.ts
(absent / registered / multi-tenant); conformance boots gain an
analytics stub so the 405 + parity contracts keep their subject, plus a
capability-absent boot pinning "no mounts, shared 404 for every method,
sibling routes unaffected". Route-ledger row annotated (a row is an
SDK-expressibility claim, not a mount guarantee).
Completes the #3891 arc: #3989 emptied the slot, #4010 made the body
contract strict at the entry, this removes the last wire-level residue
of the uninstalled API. The hono standalone static discovery that still
hardcodes the full routes table is tracked as #4018.
Refs #3891, #3989, #4010, #4018.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017WZBR9XyhXoqKCU6qQVyjx
* docs(api): discovery sample reflects the capability-conditional analytics surface
The /api/v1 discovery example still showed the pre-#3989 picture:
analytics hardcoded in `routes` and reported available with
provider objectql on a minimal install. Show the honest minimal-install
shape instead — analytics unavailable with the install hint and omitted
from routes — which also makes the sample demonstrate the
omitted-when-uninstalled rule the paragraph below it explains.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017WZBR9XyhXoqKCU6qQVyjx
---------
Co-authored-by: Claude <noreply@anthropic.com>
@@ -131,7 +130,7 @@ Returns the full discovery manifest.
131
130
}
132
131
```
133
132
134
-
Disabled/uninstalled route keys (e.g. `auth`, `workflow`) are omitted from `routes` entirely rather than set to `null`; check `services` to tell "not installed" apart from "installed but not yet mounted here."
133
+
Disabled/uninstalled route keys (e.g. `auth`, `analytics`, `workflow`) are omitted from `routes` entirely rather than set to `null`; check `services` to tell "not installed" apart from "installed but not yet mounted here." The sample above shows a minimal install: `analytics` reports `unavailable` and advertises no route until `@objectstack/service-analytics` registers the engine — the dispatcher then also mounts `/api/v1/analytics/*` (the routes are capability-conditional; an uninstalled capability answers 404 for every method).
0 commit comments