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

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions