Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,7 @@ jobs:
run: cargo test --workspace --all-targets

- name: Check feature compilation
run: cargo check --workspace --all-targets --features "fastly cloudflare"
run: cargo check --workspace --all-targets --features "fastly cloudflare spin"

- name: Check Spin wasm32 compilation
run: cargo check -p edgezero-adapter-spin --target wasm32-wasip1 --features spin
28 changes: 18 additions & 10 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
## Project Overview

EdgeZero is a portable HTTP workload toolkit in Rust. Write once, deploy to
Fastly Compute, Cloudflare Workers, or native Axum servers. The codebase is a
Cargo workspace with 7 crates under `crates/`, an example app under
`examples/app-demo/`, a VitePress documentation site under `docs/`, and CI
workflows under `.github/workflows/`.
Fastly Compute, Cloudflare Workers, Fermyon Spin, or native Axum servers. The
codebase is a Cargo workspace with 8 crates under `crates/`, an example app
under `examples/app-demo/`, a VitePress documentation site under `docs/`, and
CI workflows under `.github/workflows/`.

## Workspace Layout

Expand All @@ -17,9 +17,10 @@ crates/
edgezero-adapter/ # Adapter registry and traits
edgezero-adapter-fastly/ # Fastly Compute bridge (wasm32-wasip1)
edgezero-adapter-cloudflare/# Cloudflare Workers bridge (wasm32-unknown-unknown)
edgezero-adapter-spin/ # Fermyon Spin bridge (wasm32-wasip1)
edgezero-adapter-axum/ # Axum/Tokio bridge (native, dev server)
edgezero-cli/ # CLI: new, build, deploy, dev, serve
examples/app-demo/ # Reference app with all 3 adapters (excluded from workspace)
examples/app-demo/ # Reference app with all 4 adapters (excluded from workspace)
docs/ # VitePress documentation site (Node.js)
scripts/ # Build/deploy/test helper scripts
```
Expand Down Expand Up @@ -49,7 +50,10 @@ cargo fmt --all -- --check
cargo clippy --workspace --all-targets --all-features -- -D warnings

# Feature compilation check
cargo check --workspace --all-targets --features "fastly cloudflare"
cargo check --workspace --all-targets --features "fastly cloudflare spin"

# Spin wasm32 compilation check
cargo check -p edgezero-adapter-spin --target wasm32-wasip1 --features spin

# Run the demo dev server
cargo run -p edgezero-cli --features dev-example -- dev
Expand All @@ -67,6 +71,7 @@ faster iteration on a single crate.
| ---------- | ------------------------ | ---------------------------------- |
| Fastly | `wasm32-wasip1` | Requires Viceroy for local testing |
| Cloudflare | `wasm32-unknown-unknown` | Requires `wrangler` for dev/deploy |
| Spin | `wasm32-wasip1` | Requires `spin` CLI for dev/deploy |
| Axum | Native (host triple) | Standard Tokio runtime |

## Coding Conventions
Expand Down Expand Up @@ -132,12 +137,14 @@ impl Middleware for MyMiddleware {
### Proxy

Use `ProxyService` with adapter-specific clients (`FastlyProxyClient`,
`CloudflareProxyClient`). Keep proxy logic provider-agnostic in core.
`CloudflareProxyClient`, `SpinProxyClient`). Keep proxy logic provider-agnostic
in core.

### Logging

- Adapter-specific init: `edgezero_adapter_fastly::init_logger()`,
`edgezero_adapter_cloudflare::init_logger()`.
`edgezero_adapter_cloudflare::init_logger()`,
`edgezero_adapter_spin::init_logger()`.
- Use `simple_logger` for local/Axum builds.
- Use the `log` / `tracing` facade, not direct dependencies.

Expand All @@ -151,7 +158,7 @@ Use `ProxyService` with adapter-specific clients (`FastlyProxyClient`,
- **Minimal changes**: every change should impact as little code as possible.
Avoid unnecessary refactoring, docstrings on untouched code, or premature abstractions.
- **Feature gates**: platform-specific code goes behind `fastly`, `cloudflare`,
or `axum` features. Core stays `default-features = false` for WASM targets.
`spin`, or `axum` features. Core stays `default-features = false` for WASM targets.
- **No direct `http` crate imports** in application code — use `edgezero_core` re-exports.

## Adapter Pattern
Expand Down Expand Up @@ -179,7 +186,8 @@ Every PR must pass:
1. `cargo fmt --all -- --check`
2. `cargo clippy --workspace --all-targets --all-features -- -D warnings`
3. `cargo test --workspace --all-targets`
4. `cargo check --workspace --all-targets --features "fastly cloudflare"`
4. `cargo check --workspace --all-targets --features "fastly cloudflare spin"`
5. `cargo check -p edgezero-adapter-spin --target wasm32-wasip1 --features spin`

Docs CI additionally runs ESLint + Prettier on the `docs/` directory.

Expand Down
125 changes: 123 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ members = [
"crates/edgezero-adapter-axum",
"crates/edgezero-adapter-cloudflare",
"crates/edgezero-adapter-fastly",
"crates/edgezero-adapter-spin",
"crates/edgezero-adapter",
"crates/edgezero-cli",
"crates/edgezero-core",
Expand Down Expand Up @@ -35,6 +36,7 @@ edgezero-adapter = { path = "crates/edgezero-adapter" }
edgezero-adapter-axum = { path = "crates/edgezero-adapter-axum", default-features = false }
edgezero-adapter-cloudflare = { path = "crates/edgezero-adapter-cloudflare", default-features = false }
edgezero-adapter-fastly = { path = "crates/edgezero-adapter-fastly", default-features = false }
edgezero-adapter-spin = { path = "crates/edgezero-adapter-spin", default-features = false }
edgezero-core = { path = "crates/edgezero-core", default-features = false }
fastly = "0.11"
fern = "0.7"
Expand All @@ -53,6 +55,7 @@ serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_urlencoded = "0.7"
simple_logger = "5"
spin-sdk = { version = "5.2", default-features = false }
tempfile = "3"
thiserror = "2"
tokio = { version = "1", features = ["macros", "rt-multi-thread", "signal"] }
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# EdgeZero

Production-ready toolkit for portable edge HTTP workloads. Write once, deploy to Fastly Compute, Cloudflare Workers, or native Axum servers.
Production-ready toolkit for portable edge HTTP workloads. Write once, deploy to Fastly Compute, Cloudflare Workers, Fermyon Spin, or native Axum servers.

## Quick Start

Expand Down Expand Up @@ -34,6 +34,7 @@ Full documentation is available at **[stackpop.github.io/edgezero](https://stack
| ------------------ | ------------------------ | ------ |
| Fastly Compute | `wasm32-wasip1` | Stable |
| Cloudflare Workers | `wasm32-unknown-unknown` | Stable |
| Fermyon Spin | `wasm32-wasip1` | Preview |
| Axum (Native) | Host | Stable |

## License
Expand Down
4 changes: 2 additions & 2 deletions crates/edgezero-adapter-cloudflare/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub fn build() -> Result<PathBuf, String> {
let pkg_dir = workspace_root.join("pkg");
fs::create_dir_all(&pkg_dir)
.map_err(|e| format!("failed to create {}: {e}", pkg_dir.display()))?;
let dest = pkg_dir.join(format!("{crate_name}.wasm"));
let dest = pkg_dir.join(format!("{}.wasm", crate_name.replace('-', "_")));
fs::copy(&artifact, &dest)
.map_err(|e| format!("failed to copy artifact to {}: {e}", dest.display()))?;

Expand Down Expand Up @@ -284,7 +284,7 @@ fn locate_artifact(
manifest_dir: &Path,
crate_name: &str,
) -> Result<PathBuf, String> {
let release_name = format!("{crate_name}.wasm");
let release_name = format!("{}.wasm", crate_name.replace('-', "_"));

if let Some(custom) = std::env::var_os("CARGO_TARGET_DIR") {
let candidate = PathBuf::from(custom)
Expand Down
7 changes: 4 additions & 3 deletions crates/edgezero-adapter-cloudflare/src/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ impl ProxyClient for CloudflareProxyClient {
.map_err(EdgeError::internal)?;

let mut proxy_response = convert_response(&mut cf_response).await?;
proxy_response
.headers_mut()
.insert("x-edgezero-proxy", HeaderValue::from_static("cloudflare"));
proxy_response.headers_mut().insert(
edgezero_core::proxy::PROXY_HEADER,
HeaderValue::from_static("cloudflare"),
);
Ok(proxy_response)
}
}
Expand Down
4 changes: 2 additions & 2 deletions crates/edgezero-adapter-fastly/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub fn build(extra_args: &[String]) -> Result<PathBuf, String> {
let pkg_dir = workspace_root.join("pkg");
fs::create_dir_all(&pkg_dir)
.map_err(|e| format!("failed to create {}: {e}", pkg_dir.display()))?;
let dest = pkg_dir.join(format!("{crate_name}.wasm"));
let dest = pkg_dir.join(format!("{}.wasm", crate_name.replace('-', "_")));
fs::copy(&artifact, &dest)
.map_err(|e| format!("failed to copy artifact to {}: {e}", dest.display()))?;

Expand Down Expand Up @@ -269,7 +269,7 @@ fn locate_artifact(
crate_name: &str,
) -> Result<PathBuf, String> {
let target_triple = "wasm32-wasip1";
let release_name = format!("{crate_name}.wasm");
let release_name = format!("{}.wasm", crate_name.replace('-', "_"));

if let Some(custom) = std::env::var_os("CARGO_TARGET_DIR") {
let candidate = PathBuf::from(custom)
Expand Down
7 changes: 4 additions & 3 deletions crates/edgezero-adapter-fastly/src/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ impl ProxyClient for FastlyProxyClient {
let mut fastly_response = pending_request.wait().map_err(EdgeError::internal)?;

let mut proxy_response = convert_response(&mut fastly_response)?;
proxy_response
.headers_mut()
.insert("x-edgezero-proxy", HeaderValue::from_static("fastly"));
proxy_response.headers_mut().insert(
edgezero_core::proxy::PROXY_HEADER,
HeaderValue::from_static("fastly"),
);
Ok(proxy_response)
}
}
Expand Down
Loading
Loading