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
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).
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/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}}/…".
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).
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-demoand 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:
ManifestgainsSerialize; theapp!macro serializes the parsed manifest at expansion time and injects the JSON string viaRouterService.BlobEnvelope.data, secret-safe (secret fields appear as unresolved key-name references; resolution only happens in the typedAppConfig<C>extractor).[{ "method", "path" }]projected from the live route index held byRouterInner.Injection, not a global: app-specific data (manifest JSON + route index) is injected into the request at the shared
RouterInner::dispatchchokepoint in core (IntrospectionDatarequest extension). No process-global state; no per-adapter changes. Exposed via aRequestContext::introspection()accessor.Removes the old route-listing machinery in favor of the new bindable
routeshandler:enable_route_listing,enable_route_listing_at,DEFAULT_ROUTE_LISTING_PATH,RouteListingEntry,build_listing_response, and the/__edgezero/routesendpoint (plus associated tests and any doc/example references).Design spec:
docs/superpowers/specs/2026-07-01-introspection-routes-design.mdDone when
crates/edgezero-core/src/manifest.rs— addSerializederives toManifestand 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— addIntrospectionData { manifest_json: Option<Arc<str>>, routes: Arc<[RouteInfo]> }; addRouterBuilder::with_manifest_json(impl Into<Arc<str>>)threaded intobuild()/RouterInner; inject the extension inRouterInner::dispatchbefore middleware and routing.crates/edgezero-core/src/router.rs— removeDEFAULT_ROUTE_LISTING_PATH,enable_route_listing,enable_route_listing_at, theroute_listing_pathfield + listing branch inbuild(),build_listing_response,RouteListingEntry, and allroute_listing_*tests.crates/edgezero-core/src/context.rs— addintrospection() -> Option<&IntrospectionData>accessor.crates/edgezero-core/src/introspection.rs— new module with the three#[action]handlers plusRouteEntryView { method, path }.configreads the raw blob via the default config-store binding and parsesBlobEnvelopewithout secret resolution or typed deserialization.crates/edgezero-core/src/lib.rs— export theintrospectionmodule.crates/edgezero-macros/src/app.rs— serialize the manifest (serde_json::to_string,compile_error!on failure) and emitbuilder.with_manifest_json(...)in generatedbuild_router(); addserde_jsonbuild-time dependency.examples/app-demo/edgezero.toml— add three trigger rows under/_app-demo/{manifest,config,routes}bound toedgezero_core::introspection::*.crates/edgezero-cli/src/templates/root/edgezero.toml.hbs— add the same three rows usingpath = "/_{{name}}/…"./__edgezero/routes/enable_route_listingreferences (docs, examples, adapters).routesreturns empty array when data absent.router.rs; colocated tests for the three handlers; macro expansion assertswith_manifest_jsonis emitted with valid JSON;app-demotests hit the three endpoints and assert shapes.cargo test --workspace --all-targets, feature check, spin wasm32 check).Affected area: Core (routing, extractors, middleware); Macros (#[action], #[app]); CLI (templates)