Skip to content

Add pluggable introspection routes (manifest / config / routes) #299

Description

@aram356

Description

Provide three reusable, framework-supplied #[action] handlers — edgezero_core::introspection::{manifest, config, routes} — that let any EdgeZero app expose its own metadata at runtime. They are ordinary handlers wired like any other route via [[triggers.http]]. There is no dedicated manifest section and no builder API. app-demo and every generated app ship them pre-wired under a per-app namespace /_<app-name>/{manifest,config,routes} (e.g. /_app-demo/manifest), as plain, editable trigger rows.

What each handler emits:

  • manifest — the full manifest as JSON, baked at compile time. Manifest gains Serialize; the app! macro serializes the parsed manifest at expansion time and injects the JSON string via RouterService.
  • config — the raw default config-store BlobEnvelope.data, secret-safe (secret fields appear as unresolved key-name references; resolution only happens in the typed AppConfig<C> extractor).
  • routes[{ "method", "path" }] projected from the live route index held by RouterInner.

Injection, not a global: app-specific data (manifest JSON + route index) is injected into the request at the shared RouterInner::dispatch chokepoint in core (IntrospectionData request extension). No process-global state; no per-adapter changes. Exposed via a RequestContext::introspection() accessor.

Removes the old route-listing machinery in favor of the new bindable routes handler: enable_route_listing, enable_route_listing_at, DEFAULT_ROUTE_LISTING_PATH, RouteListingEntry, build_listing_response, and the /__edgezero/routes endpoint (plus associated tests and any doc/example references).

Design spec: docs/superpowers/specs/2026-07-01-introspection-routes-design.md

Done when

  • crates/edgezero-core/src/manifest.rs — add Serialize derives to Manifest and all nested structs that appear in output (ManifestApp, ManifestTriggers, ManifestHttpTrigger, ManifestEnvironment, ManifestBinding, ManifestAdapter + sub-structs, ManifestLogging*, ManifestStores, StoreDeclaration). Internal-only fields keep #[serde(skip)].
  • crates/edgezero-core/src/router.rs — add IntrospectionData { manifest_json: Option<Arc<str>>, routes: Arc<[RouteInfo]> }; add RouterBuilder::with_manifest_json(impl Into<Arc<str>>) threaded into build() / RouterInner; inject the extension in RouterInner::dispatch before middleware and routing.
  • crates/edgezero-core/src/router.rs — remove DEFAULT_ROUTE_LISTING_PATH, enable_route_listing, enable_route_listing_at, the route_listing_path field + listing branch in build(), build_listing_response, RouteListingEntry, and all route_listing_* tests.
  • crates/edgezero-core/src/context.rs — add introspection() -> Option<&IntrospectionData> accessor.
  • crates/edgezero-core/src/introspection.rs — new module with the three #[action] handlers plus RouteEntryView { method, path }. config reads the raw blob via the default config-store binding and parses BlobEnvelope without secret resolution or typed deserialization.
  • crates/edgezero-core/src/lib.rs — export the introspection module.
  • crates/edgezero-macros/src/app.rs — serialize the manifest (serde_json::to_string, compile_error! on failure) and emit builder.with_manifest_json(...) in generated build_router(); add serde_json build-time dependency.
  • examples/app-demo/edgezero.toml — add three trigger rows under /_app-demo/{manifest,config,routes} bound to edgezero_core::introspection::*.
  • crates/edgezero-cli/src/templates/root/edgezero.toml.hbs — add the same three rows using path = "/_{{name}}/…".
  • Workspace grep — purge remaining /__edgezero/routes / enable_route_listing references (docs, examples, adapters).
  • Error mapping honored: absent manifest → 500 internal; missing config store or blob → 404; malformed envelope → 500; routes returns empty array when data absent.
  • Tests: dispatch injection test in router.rs; colocated tests for the three handlers; macro expansion asserts with_manifest_json is emitted with valid JSON; app-demo tests hit the three endpoints and assert shapes.
  • All CI gates pass (fmt, clippy, cargo test --workspace --all-targets, feature check, spin wasm32 check).

Affected area: Core (routing, extractors, middleware); Macros (#[action], #[app]); CLI (templates)

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Fields

No fields configured for Task.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions