diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..ce9f661 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,85 @@ +# Copilot PR Review Instructions — FastEdge-sdk-rust + +## Constitution + +This repository is `fastedge` (crate) — the Rust SDK for Gcore FastEdge. It provides the `#[fastedge::http]` and `#[wstd::http_server]` handler macros, type conversions, an outbound HTTP client, and ProxyWasm FFI wrappers for CDN apps. + +### Principles (enforce during review) + +1. **Handler preference** — `#[wstd::http_server]` (async, wasm32-wasip2) is the recommended handler for new HTTP apps. `#[fastedge::http]` is legacy. New examples must use `wstd`. +2. **No over-engineering** — Simple solutions over complex abstractions. Three similar lines > premature abstraction. +3. **Platform constraints** — Only stdout is captured; `eprintln!` output is silently lost. Flag any use of stderr in code or examples. +4. **CDN/HTTP separation** — CDN apps (proxy-wasm filters) and HTTP apps (standalone handlers) are independent application types with different architectures and lifecycles. Never mix their APIs. +5. **WIT submodule integrity** — `wit/` files come from `G-Core/FastEdge-wit` submodule. Never modify them directly. + +### Public API contract + +The public API surface is defined by: +- `src/lib.rs` — Core types (`Body`, `Error`), type conversions, `send_request` +- `derive/src/lib.rs` — `#[fastedge::http]` proc macro +- `src/proxywasm/` — ProxyWasm FFI wrappers (KV store, secrets, dictionary, utils) +- `src/http_client.rs` — Outbound HTTP client + +Changes to these surfaces require updated `docs/`, updated tests, and a semver-appropriate version bump. + +## Generated Content — `docs/` + +Files in `docs/` are **machine-generated** from source code by `./fastedge-plugin-source/generate-docs.sh`. They must not be edited by hand — manual changes will be silently overwritten on the next generation run. + +### When reviewing PRs that touch `docs/`: + +- **Never** suggest manual edits to any file in `docs/` +- If docs are stale or incorrect, suggest: **Run `./fastedge-plugin-source/generate-docs.sh`** +- If the generated output itself is wrong (e.g., wrong structure, missing section), the fix belongs in `fastedge-plugin-source/.generation-config.md`, not in `docs/` directly +- If a PR modifies `docs/` files without a corresponding source code change, flag it — the change should come from the generation script, not a hand-edit + +### When reviewing PRs that change source code covered by `docs/`: + +- Check whether the change affects the public API or user-facing behavior +- If yes, and `docs/` was not regenerated in the same PR, **request changes** with: + > Source code affecting public API was changed but docs/ was not regenerated. + > Run: `./fastedge-plugin-source/generate-docs.sh` + +## Documentation Freshness + +### Public API changes (must regenerate docs/) +- New, modified, or removed public types/functions in `src/lib.rs` +- Changes to `#[fastedge::http]` macro behavior in `derive/src/lib.rs` +- Changes to ProxyWasm wrapper APIs in `src/proxywasm/` +- Changes to outbound HTTP client in `src/http_client.rs` +- New or modified WIT interfaces in `wit/` +- Changes to `Cargo.toml` (version, features, dependencies) + +### Mapping: code location → doc file + +| Code path | Doc file | +| --------------------------------------------- | --------------------- | +| `src/lib.rs` (Body, Error, send_request) | `docs/SDK_API.md` | +| `derive/src/lib.rs` (handler macros) | `docs/SDK_API.md` | +| `src/http_client.rs` (outbound HTTP) | `docs/SDK_API.md` | +| `src/proxywasm/key_value.rs` | `docs/HOST_SERVICES.md` | +| `src/proxywasm/secret.rs` | `docs/HOST_SERVICES.md` | +| `src/proxywasm/dictionary.rs` | `docs/HOST_SERVICES.md` | +| `src/proxywasm/utils.rs` | `docs/HOST_SERVICES.md` | +| `src/proxywasm/` (CDN lifecycle, FFI) | `docs/CDN_APPS.md` | +| `Cargo.toml` (version, features) | `docs/INDEX.md` | +| `fastedge-plugin-source/manifest.json` | `.github/copilot-instructions.md` | + +### Violation example + +> PR changes `send_request` signature in `src/lib.rs` but `docs/SDK_API.md` still shows the old signature → **request changes**. Run `./fastedge-plugin-source/generate-docs.sh` before merge. + +### Quickstart protection + +If any public API signature or behavior changes, check whether `docs/quickstart.md` examples are still accurate. Request regeneration if examples would no longer work against the updated code. + +## Pipeline source contract + +If `fastedge-plugin-source/manifest.json` lists source files that overlap with files changed in this PR, request that `docs/` is regenerated (run `./fastedge-plugin-source/generate-docs.sh`) to keep the plugin pipeline's source material current. + +## Quality Rules + +- All public function signatures in docs must match actual source declarations +- No `eprintln!` or `eprint!` in any code or examples — output is lost on the platform +- New HTTP examples must use `#[wstd::http_server]`, not `#[fastedge::http]` +- No marketing language in documentation — precise, technical prose only diff --git a/.github/workflows/copilot-sync.yml b/.github/workflows/copilot-sync.yml new file mode 100644 index 0000000..8741808 --- /dev/null +++ b/.github/workflows/copilot-sync.yml @@ -0,0 +1,22 @@ +name: Copilot Instructions Sync + +on: + pull_request: + paths: + - fastedge-plugin-source/manifest.json + - fastedge-plugin-source/check-copilot-sync.sh + - .github/copilot-instructions.md + - .github/workflows/copilot-sync.yml + - examples/** + - docs/** + +jobs: + check-sync: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Check manifest ↔ copilot-instructions sync + run: bash fastedge-plugin-source/check-copilot-sync.sh diff --git a/examples/cdn/ab_testing/fixtures/existing-cookie-a.test.json b/examples/cdn/ab_testing/fixtures/existing-cookie-a.test.json index afb613f..5e13d1b 100644 --- a/examples/cdn/ab_testing/fixtures/existing-cookie-a.test.json +++ b/examples/cdn/ab_testing/fixtures/existing-cookie-a.test.json @@ -25,5 +25,9 @@ "enabled": true, "path": "." }, - "logLevel": 2 + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } } diff --git a/examples/cdn/ab_testing/fixtures/existing-cookie-b.test.json b/examples/cdn/ab_testing/fixtures/existing-cookie-b.test.json index 82533f1..cf9ace3 100644 --- a/examples/cdn/ab_testing/fixtures/existing-cookie-b.test.json +++ b/examples/cdn/ab_testing/fixtures/existing-cookie-b.test.json @@ -25,5 +25,9 @@ "enabled": true, "path": "." }, - "logLevel": 2 + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } } diff --git a/examples/cdn/ab_testing/fixtures/missing-config.test.json b/examples/cdn/ab_testing/fixtures/missing-config.test.json index 94da74b..c69b61d 100644 --- a/examples/cdn/ab_testing/fixtures/missing-config.test.json +++ b/examples/cdn/ab_testing/fixtures/missing-config.test.json @@ -13,5 +13,9 @@ "request.host": "example.com", "request.path": "/landing" }, - "logLevel": 2 + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } } diff --git a/examples/cdn/ab_testing/fixtures/new-visitor.test.json b/examples/cdn/ab_testing/fixtures/new-visitor.test.json index 74ecb7e..b308c5c 100644 --- a/examples/cdn/ab_testing/fixtures/new-visitor.test.json +++ b/examples/cdn/ab_testing/fixtures/new-visitor.test.json @@ -24,5 +24,9 @@ "enabled": true, "path": "." }, - "logLevel": 2 + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } } diff --git a/examples/cdn/ab_testing/src/lib.rs b/examples/cdn/ab_testing/src/lib.rs index 02a65ab..4d1c09d 100644 --- a/examples/cdn/ab_testing/src/lib.rs +++ b/examples/cdn/ab_testing/src/lib.rs @@ -33,17 +33,11 @@ impl RootContext for AbTestingRoot { } fn create_http_context(&self, _: u32) -> Option> { - Some(Box::new(AbTestingContext { - variant: String::new(), - experiment_name: String::new(), - })) + Some(Box::new(AbTestingContext)) } } -struct AbTestingContext { - variant: String, - experiment_name: String, -} +struct AbTestingContext; impl Context for AbTestingContext {} @@ -94,9 +88,6 @@ impl HttpContext for AbTestingContext { assigned = if now % 2 == 0 { "A" } else { "B" }.to_string(); } - self.variant = assigned.clone(); - self.experiment_name = experiment_name.clone(); - // Rewrite request path let path = self .get_property(vec!["request.path"]) @@ -130,16 +121,21 @@ impl HttpContext for AbTestingContext { } fn on_http_response_headers(&mut self, _: usize, _: bool) -> Action { - if self.variant.is_empty() { + // Recover the assigned variant and experiment name from the request headers set in + // on_http_request_headers. Instance state does not survive the nginx -> core-proxy hop. + let Some(variant) = self.get_http_request_header("X-Variant") else { return Action::Continue; - } + }; + let Some(experiment_name) = self.get_http_request_header("X-Experiment") else { + return Action::Continue; + }; let cookie = format!( "fe_exp_{}={}; Path=/; Max-Age=86400; SameSite=Lax", - self.experiment_name, self.variant + experiment_name, variant ); self.add_http_response_header("Set-Cookie", &cookie); - self.add_http_response_header("X-Variant", &self.variant); + self.add_http_response_header("X-Variant", &variant); Action::Continue } diff --git a/examples/cdn/api_key/fixtures/happy-path.test.json b/examples/cdn/api_key/fixtures/happy-path.test.json index c6c6093..ae7ce6c 100644 --- a/examples/cdn/api_key/fixtures/happy-path.test.json +++ b/examples/cdn/api_key/fixtures/happy-path.test.json @@ -20,5 +20,9 @@ "enabled": true, "path": "." }, - "logLevel": 2 + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } } diff --git a/examples/cdn/api_key/fixtures/invalid-key.test.json b/examples/cdn/api_key/fixtures/invalid-key.test.json index 9acd2eb..45d514e 100644 --- a/examples/cdn/api_key/fixtures/invalid-key.test.json +++ b/examples/cdn/api_key/fixtures/invalid-key.test.json @@ -14,5 +14,9 @@ "enabled": true, "path": "." }, - "logLevel": 2 + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } } diff --git a/examples/cdn/api_key/fixtures/missing-header.test.json b/examples/cdn/api_key/fixtures/missing-header.test.json index c354cc2..9d1f2e5 100644 --- a/examples/cdn/api_key/fixtures/missing-header.test.json +++ b/examples/cdn/api_key/fixtures/missing-header.test.json @@ -13,5 +13,9 @@ "enabled": true, "path": "." }, - "logLevel": 2 + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } } diff --git a/examples/cdn/api_key/fixtures/missing-secret.test.json b/examples/cdn/api_key/fixtures/missing-secret.test.json index dbae03f..0b42fc6 100644 --- a/examples/cdn/api_key/fixtures/missing-secret.test.json +++ b/examples/cdn/api_key/fixtures/missing-secret.test.json @@ -10,5 +10,9 @@ }, "body": "" }, - "logLevel": 2 + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } } diff --git a/examples/cdn/body/fixtures/client.test.json b/examples/cdn/body/fixtures/client.test.json index feb0d18..4721912 100644 --- a/examples/cdn/body/fixtures/client.test.json +++ b/examples/cdn/body/fixtures/client.test.json @@ -9,5 +9,9 @@ "x-debugger-content": "body-only" }, "body": "Hello Client, this is a test message" + }, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" } } diff --git a/examples/cdn/body/fixtures/skip.test.json b/examples/cdn/body/fixtures/skip.test.json index c979f2e..171beac 100644 --- a/examples/cdn/body/fixtures/skip.test.json +++ b/examples/cdn/body/fixtures/skip.test.json @@ -9,5 +9,9 @@ "x-debugger-content": "body-only" }, "body": "Hello World, this is a test message" + }, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" } } diff --git a/examples/cdn/cache_control/fixtures/error-status.test.json b/examples/cdn/cache_control/fixtures/error-status.test.json index 54407be..de23590 100644 --- a/examples/cdn/cache_control/fixtures/error-status.test.json +++ b/examples/cdn/cache_control/fixtures/error-status.test.json @@ -20,5 +20,9 @@ "enabled": true, "path": "." }, - "logLevel": 2 + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } } diff --git a/examples/cdn/cache_control/fixtures/happy-path.test.json b/examples/cdn/cache_control/fixtures/happy-path.test.json index f4a9f85..a8c949c 100644 --- a/examples/cdn/cache_control/fixtures/happy-path.test.json +++ b/examples/cdn/cache_control/fixtures/happy-path.test.json @@ -19,5 +19,9 @@ "enabled": true, "path": "." }, - "logLevel": 2 + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } } diff --git a/examples/cdn/cache_control/fixtures/javascript-asset.test.json b/examples/cdn/cache_control/fixtures/javascript-asset.test.json index aa190ba..7d069df 100644 --- a/examples/cdn/cache_control/fixtures/javascript-asset.test.json +++ b/examples/cdn/cache_control/fixtures/javascript-asset.test.json @@ -19,5 +19,9 @@ "enabled": true, "path": "." }, - "logLevel": 2 + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } } diff --git a/examples/cdn/cache_control/fixtures/json-api.test.json b/examples/cdn/cache_control/fixtures/json-api.test.json index 27819ef..fb855df 100644 --- a/examples/cdn/cache_control/fixtures/json-api.test.json +++ b/examples/cdn/cache_control/fixtures/json-api.test.json @@ -19,5 +19,9 @@ "enabled": true, "path": "." }, - "logLevel": 2 + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } } diff --git a/examples/cdn/cache_control/fixtures/static-asset.test.json b/examples/cdn/cache_control/fixtures/static-asset.test.json index 880c606..80648ca 100644 --- a/examples/cdn/cache_control/fixtures/static-asset.test.json +++ b/examples/cdn/cache_control/fixtures/static-asset.test.json @@ -19,5 +19,9 @@ "enabled": true, "path": "." }, - "logLevel": 2 + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } } diff --git a/examples/cdn/cache_control/fixtures/unknown-content-type.test.json b/examples/cdn/cache_control/fixtures/unknown-content-type.test.json index 0e5ce7c..efc021b 100644 --- a/examples/cdn/cache_control/fixtures/unknown-content-type.test.json +++ b/examples/cdn/cache_control/fixtures/unknown-content-type.test.json @@ -19,5 +19,9 @@ "enabled": true, "path": "." }, - "logLevel": 2 + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } } diff --git a/examples/cdn/cache_control/fixtures/xml-api.test.json b/examples/cdn/cache_control/fixtures/xml-api.test.json index 1b9151d..9ef00b1 100644 --- a/examples/cdn/cache_control/fixtures/xml-api.test.json +++ b/examples/cdn/cache_control/fixtures/xml-api.test.json @@ -19,5 +19,9 @@ "enabled": true, "path": "." }, - "logLevel": 2 + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } } diff --git a/examples/cdn/cors/fixtures/disallowed-origin.test.json b/examples/cdn/cors/fixtures/disallowed-origin.test.json index dcc4f6d..194e774 100644 --- a/examples/cdn/cors/fixtures/disallowed-origin.test.json +++ b/examples/cdn/cors/fixtures/disallowed-origin.test.json @@ -20,5 +20,9 @@ "enabled": true, "path": "." }, - "logLevel": 2 + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } } diff --git a/examples/cdn/cors/fixtures/happy-path.test.json b/examples/cdn/cors/fixtures/happy-path.test.json index 2caf541..0e82031 100644 --- a/examples/cdn/cors/fixtures/happy-path.test.json +++ b/examples/cdn/cors/fixtures/happy-path.test.json @@ -20,5 +20,9 @@ "enabled": true, "path": "." }, - "logLevel": 2 + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } } diff --git a/examples/cdn/cors/fixtures/no-origin.test.json b/examples/cdn/cors/fixtures/no-origin.test.json index 660fbb7..8a39e21 100644 --- a/examples/cdn/cors/fixtures/no-origin.test.json +++ b/examples/cdn/cors/fixtures/no-origin.test.json @@ -19,5 +19,9 @@ "enabled": true, "path": "." }, - "logLevel": 2 + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } } diff --git a/examples/cdn/cors/fixtures/preflight.test.json b/examples/cdn/cors/fixtures/preflight.test.json index 8eee2e5..a985bf4 100644 --- a/examples/cdn/cors/fixtures/preflight.test.json +++ b/examples/cdn/cors/fixtures/preflight.test.json @@ -17,5 +17,9 @@ "enabled": true, "path": "." }, - "logLevel": 2 + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } } diff --git a/examples/cdn/cors/fixtures/wildcard-origins.test.json b/examples/cdn/cors/fixtures/wildcard-origins.test.json index e1c6490..e0044e8 100644 --- a/examples/cdn/cors/fixtures/wildcard-origins.test.json +++ b/examples/cdn/cors/fixtures/wildcard-origins.test.json @@ -20,5 +20,9 @@ "enabled": true, "path": "fixtures/wildcard.env" }, - "logLevel": 2 + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } } diff --git a/examples/cdn/cors/src/lib.rs b/examples/cdn/cors/src/lib.rs index 6abdc94..b056f7c 100644 --- a/examples/cdn/cors/src/lib.rs +++ b/examples/cdn/cors/src/lib.rs @@ -8,9 +8,10 @@ Handles preflight OPTIONS requests and adds CORS response headers for allowed origins. Supports configurable origin allow-lists, methods, and exposed headers. -Required configuration: +Configuration: - Environment variable: ALLOWED_ORIGINS (comma-separated origins or "*") -Optional configuration: + When unset or empty the filter is dormant — requests pass through + without CORS headers, so browsers will block cross-origin access. - Environment variable: ALLOWED_METHODS (default: "GET, POST, PUT, DELETE, OPTIONS") - Environment variable: MAX_AGE (default: "86400") - Environment variable: EXPOSE_HEADERS (response headers to expose) diff --git a/examples/cdn/custom/Cargo.lock b/examples/cdn/custom/Cargo.lock new file mode 100644 index 0000000..449e11b --- /dev/null +++ b/examples/cdn/custom/Cargo.lock @@ -0,0 +1,56 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "custom" +version = "0.1.0" +dependencies = [ + "log", + "proxy-wasm", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "proxy-wasm" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8d35d9e2bc5104e2e954b149aa1d5f9fa3bb27f73b45b2706020fed101db685" +dependencies = [ + "hashbrown", + "log", +] diff --git a/examples/cdn/custom/fixtures/invalid-status.test.json b/examples/cdn/custom/fixtures/invalid-status.test.json new file mode 100644 index 0000000..cc19dc3 --- /dev/null +++ b/examples/cdn/custom/fixtures/invalid-status.test.json @@ -0,0 +1,20 @@ +{ + "appType": "proxy-wasm", + "description": "Path /abc — returns 400 for non-numeric status code", + "request": { + "method": "GET", + "url": "http://fastedge-builtin.debug", + "headers": { + "host": "example.com" + }, + "body": "" + }, + "properties": { + "request.path": "/abc" + }, + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } +} diff --git a/examples/cdn/custom/fixtures/status-200.test.json b/examples/cdn/custom/fixtures/status-200.test.json new file mode 100644 index 0000000..4bb5ece --- /dev/null +++ b/examples/cdn/custom/fixtures/status-200.test.json @@ -0,0 +1,24 @@ +{ + "appType": "proxy-wasm", + "description": "Path /200 — passes through with Action::Continue", + "request": { + "method": "GET", + "url": "http://fastedge-builtin.debug", + "headers": { + "host": "example.com" + }, + "body": "" + }, + "properties": { + "request.path": "/200" + }, + "response": { + "headers": {}, + "body": "" + }, + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } +} diff --git a/examples/cdn/custom/fixtures/status-403.test.json b/examples/cdn/custom/fixtures/status-403.test.json new file mode 100644 index 0000000..d408ea7 --- /dev/null +++ b/examples/cdn/custom/fixtures/status-403.test.json @@ -0,0 +1,20 @@ +{ + "appType": "proxy-wasm", + "description": "Path /403 — returns 403 Forbidden local response", + "request": { + "method": "GET", + "url": "http://fastedge-builtin.debug", + "headers": { + "host": "example.com" + }, + "body": "" + }, + "properties": { + "request.path": "/403" + }, + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } +} diff --git a/examples/cdn/custom_error_pages/fixtures/200-passthrough.test.json b/examples/cdn/custom_error_pages/fixtures/200-passthrough.test.json index ba23183..d4297fe 100644 --- a/examples/cdn/custom_error_pages/fixtures/200-passthrough.test.json +++ b/examples/cdn/custom_error_pages/fixtures/200-passthrough.test.json @@ -6,5 +6,9 @@ "url": "http://fastedge-builtin.debug", "headers": {}, "body": "" + }, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" } } diff --git a/examples/cdn/custom_error_pages/fixtures/400-bad-request.test.json b/examples/cdn/custom_error_pages/fixtures/400-bad-request.test.json index 6bfca08..7c86163 100644 --- a/examples/cdn/custom_error_pages/fixtures/400-bad-request.test.json +++ b/examples/cdn/custom_error_pages/fixtures/400-bad-request.test.json @@ -9,5 +9,9 @@ "x-debugger-content": "status-only" }, "body": "" + }, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" } } diff --git a/examples/cdn/custom_error_pages/fixtures/404-not-found.test.json b/examples/cdn/custom_error_pages/fixtures/404-not-found.test.json index bdd5c27..b621459 100644 --- a/examples/cdn/custom_error_pages/fixtures/404-not-found.test.json +++ b/examples/cdn/custom_error_pages/fixtures/404-not-found.test.json @@ -9,5 +9,9 @@ "x-debugger-content": "status-only" }, "body": "" + }, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" } } diff --git a/examples/cdn/custom_error_pages/fixtures/418-fallback.test.json b/examples/cdn/custom_error_pages/fixtures/418-fallback.test.json index 3789082..c3184e3 100644 --- a/examples/cdn/custom_error_pages/fixtures/418-fallback.test.json +++ b/examples/cdn/custom_error_pages/fixtures/418-fallback.test.json @@ -9,5 +9,9 @@ "x-debugger-content": "status-only" }, "body": "" + }, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" } } diff --git a/examples/cdn/custom_error_pages/fixtures/500-internal-server-error.test.json b/examples/cdn/custom_error_pages/fixtures/500-internal-server-error.test.json index 0b8bcbc..6e0e91d 100644 --- a/examples/cdn/custom_error_pages/fixtures/500-internal-server-error.test.json +++ b/examples/cdn/custom_error_pages/fixtures/500-internal-server-error.test.json @@ -9,5 +9,9 @@ "x-debugger-content": "status-only" }, "body": "" + }, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" } } diff --git a/examples/cdn/custom_error_pages/fixtures/502-bad-gateway.test.json b/examples/cdn/custom_error_pages/fixtures/502-bad-gateway.test.json index 95d6a2e..faa4124 100644 --- a/examples/cdn/custom_error_pages/fixtures/502-bad-gateway.test.json +++ b/examples/cdn/custom_error_pages/fixtures/502-bad-gateway.test.json @@ -9,5 +9,9 @@ "x-debugger-content": "status-only" }, "body": "" + }, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" } } diff --git a/examples/cdn/custom_error_pages/src/lib.rs b/examples/cdn/custom_error_pages/src/lib.rs index 88e61e3..85d64d7 100644 --- a/examples/cdn/custom_error_pages/src/lib.rs +++ b/examples/cdn/custom_error_pages/src/lib.rs @@ -48,7 +48,7 @@ impl HttpContext for HttpBody { Action::Continue } - fn on_http_response_body(&mut self, _body_size: usize, end_of_stream: bool) -> Action { + fn on_http_response_body(&mut self, body_size: usize, end_of_stream: bool) -> Action { // only process 4xx/5xx error responses let Some(status) = self.get_property(vec!["response.status"]) else { return Action::Continue; @@ -139,7 +139,7 @@ impl HttpContext for HttpBody { let html_body = handlebars.render("error_template", &page_data).unwrap(); let body = html_body.as_bytes(); - self.set_http_response_body(0, body.len(), body); + self.set_http_response_body(0, body_size, body); Action::Continue } diff --git a/examples/cdn/geo_redirect/fixtures/default.test.json b/examples/cdn/geo_redirect/fixtures/default.test.json index 012bfa5..8b912a2 100644 --- a/examples/cdn/geo_redirect/fixtures/default.test.json +++ b/examples/cdn/geo_redirect/fixtures/default.test.json @@ -21,5 +21,9 @@ "enabled": true, "path": "." }, - "logLevel": 2 + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } } diff --git a/examples/cdn/geo_redirect/fixtures/germany.test.json b/examples/cdn/geo_redirect/fixtures/germany.test.json index 92686f7..1f7aa1d 100644 --- a/examples/cdn/geo_redirect/fixtures/germany.test.json +++ b/examples/cdn/geo_redirect/fixtures/germany.test.json @@ -18,5 +18,9 @@ "enabled": true, "path": "." }, - "logLevel": 2 + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } } diff --git a/examples/cdn/geoblock/Cargo.lock b/examples/cdn/geoblock/Cargo.lock new file mode 100644 index 0000000..51015e2 --- /dev/null +++ b/examples/cdn/geoblock/Cargo.lock @@ -0,0 +1,55 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "geoblock" +version = "0.1.0" +dependencies = [ + "proxy-wasm", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "proxy-wasm" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8d35d9e2bc5104e2e954b149aa1d5f9fa3bb27f73b45b2706020fed101db685" +dependencies = [ + "hashbrown", + "log", +] diff --git a/examples/cdn/geoblock/fixtures/.env b/examples/cdn/geoblock/fixtures/.env new file mode 100644 index 0000000..3801d98 --- /dev/null +++ b/examples/cdn/geoblock/fixtures/.env @@ -0,0 +1 @@ +FASTEDGE_VAR_ENV_BLACKLIST=CN,RU,IR diff --git a/examples/cdn/geoblock/fixtures/allowed-country.test.json b/examples/cdn/geoblock/fixtures/allowed-country.test.json new file mode 100644 index 0000000..00608b4 --- /dev/null +++ b/examples/cdn/geoblock/fixtures/allowed-country.test.json @@ -0,0 +1,24 @@ +{ + "appType": "proxy-wasm", + "description": "Request from allowed country DE — passes through", + "request": { + "method": "GET", + "url": "http://fastedge-builtin.debug", + "headers": { + "host": "example.com" + }, + "body": "" + }, + "properties": { + "request.country": "DE" + }, + "dotenv": { + "enabled": true, + "path": "." + }, + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } +} diff --git a/examples/cdn/geoblock/fixtures/blocked-country.test.json b/examples/cdn/geoblock/fixtures/blocked-country.test.json new file mode 100644 index 0000000..b9131ed --- /dev/null +++ b/examples/cdn/geoblock/fixtures/blocked-country.test.json @@ -0,0 +1,24 @@ +{ + "appType": "proxy-wasm", + "description": "Request from blacklisted country CN — returns 403 Forbidden", + "request": { + "method": "GET", + "url": "http://fastedge-builtin.debug", + "headers": { + "host": "example.com" + }, + "body": "" + }, + "properties": { + "request.country": "CN" + }, + "dotenv": { + "enabled": true, + "path": "." + }, + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } +} diff --git a/examples/cdn/geoblock/fixtures/missing-config.test.json b/examples/cdn/geoblock/fixtures/missing-config.test.json new file mode 100644 index 0000000..a9b34d3 --- /dev/null +++ b/examples/cdn/geoblock/fixtures/missing-config.test.json @@ -0,0 +1,20 @@ +{ + "appType": "proxy-wasm", + "description": "BLACKLIST env not set — returns 500 App misconfigured", + "request": { + "method": "GET", + "url": "http://fastedge-builtin.debug", + "headers": { + "host": "example.com" + }, + "body": "" + }, + "properties": { + "request.country": "US" + }, + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } +} diff --git a/examples/cdn/headers/fixtures/happy-path.test.json b/examples/cdn/headers/fixtures/happy-path.test.json index 08542f1..03f672d 100644 --- a/examples/cdn/headers/fixtures/happy-path.test.json +++ b/examples/cdn/headers/fixtures/happy-path.test.json @@ -12,5 +12,9 @@ "response": { "headers": {}, "body": "" + }, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" } } diff --git a/examples/cdn/hello_world/Cargo.lock b/examples/cdn/hello_world/Cargo.lock new file mode 100644 index 0000000..85d3938 --- /dev/null +++ b/examples/cdn/hello_world/Cargo.lock @@ -0,0 +1,56 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hello_world" +version = "0.1.0" +dependencies = [ + "log", + "proxy-wasm", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "proxy-wasm" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8d35d9e2bc5104e2e954b149aa1d5f9fa3bb27f73b45b2706020fed101db685" +dependencies = [ + "hashbrown", + "log", +] diff --git a/examples/cdn/hello_world/fixtures/happy-path.test.json b/examples/cdn/hello_world/fixtures/happy-path.test.json new file mode 100644 index 0000000..7aa2540 --- /dev/null +++ b/examples/cdn/hello_world/fixtures/happy-path.test.json @@ -0,0 +1,23 @@ +{ + "appType": "proxy-wasm", + "description": "Basic request — verifies x-powered-by header is added to response", + "request": { + "method": "GET", + "url": "http://fastedge-builtin.debug", + "headers": { + "host": "example.com" + }, + "body": "" + }, + "response": { + "headers": { + "content-type": "text/html" + }, + "body": "Hello World" + }, + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } +} diff --git a/examples/cdn/http_call/fixtures/happy-path.test.json b/examples/cdn/http_call/fixtures/happy-path.test.json index b3f75cd..be49ff4 100644 --- a/examples/cdn/http_call/fixtures/happy-path.test.json +++ b/examples/cdn/http_call/fixtures/happy-path.test.json @@ -6,5 +6,9 @@ "url": "http://fastedge-builtin.debug", "headers": {}, "body": "" + }, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" } } diff --git a/examples/cdn/large_env_variable/Cargo.lock b/examples/cdn/large_env_variable/Cargo.lock new file mode 100644 index 0000000..2b1c529 --- /dev/null +++ b/examples/cdn/large_env_variable/Cargo.lock @@ -0,0 +1,534 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "bitflags" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "fastedge" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9042fccaffdd2171e8ff481e5c21d22f15b8a4454b37273c2f3f902fb3d375e" +dependencies = [ + "bytes", + "fastedge-derive", + "http", + "mime", + "thiserror", + "wit-bindgen", +] + +[[package]] +name = "fastedge-derive" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acacf180f92cbdf6f2fe1a772b7d92301e430ba207fb15b2fc87ccab4e418ff9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] + +[[package]] +name = "hashbrown" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.0", + "serde", + "serde_core", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "large_env_variable" +version = "0.1.0" +dependencies = [ + "fastedge", + "proxy-wasm", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proxy-wasm" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8d35d9e2bc5104e2e954b149aa1d5f9fa3bb27f73b45b2706020fed101db685" +dependencies = [ + "hashbrown 0.16.1", + "log", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "wasm-encoder" +version = "0.239.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be00faa2b4950c76fe618c409d2c3ea5a3c9422013e079482d78544bb2d184c" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.239.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20b3ec880a9ac69ccd92fbdbcf46ee833071cf09f82bb005b2327c7ae6025ae2" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.239.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9d90bb93e764f6beabf1d02028c70a2156a6583e63ac4218dd07ef733368b0" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +dependencies = [ + "bitflags", + "futures", + "once_cell", + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cabd629f94da277abc739c71353397046401518efb2c707669f805205f0b9890" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a4232e841089fa5f3c4fc732a92e1c74e1a3958db3b12f1de5934da2027f1f4" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0d4698c2913d8d9c2b220d116409c3f51a7aa8d7765151b886918367179ee9" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.239.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a866b19dba2c94d706ec58c92a4c62ab63e482b4c935d2a085ac94caecb136" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.239.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55c92c939d667b7bf0c6bf2d1f67196529758f99a2a45a3355cc56964fd5315d" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/examples/cdn/large_env_variable/fixtures/.env b/examples/cdn/large_env_variable/fixtures/.env new file mode 100644 index 0000000..f7edede --- /dev/null +++ b/examples/cdn/large_env_variable/fixtures/.env @@ -0,0 +1 @@ +FASTEDGE_VAR_ENV_LARGE_CONFIG={"setting":"value","items":[1,2,3],"description":"Test configuration payload for large_env_variable example"} diff --git a/examples/cdn/large_env_variable/fixtures/happy-path.test.json b/examples/cdn/large_env_variable/fixtures/happy-path.test.json new file mode 100644 index 0000000..c0153eb --- /dev/null +++ b/examples/cdn/large_env_variable/fixtures/happy-path.test.json @@ -0,0 +1,21 @@ +{ + "appType": "proxy-wasm", + "description": "LARGE_CONFIG set — adds x-config-size header with byte count", + "request": { + "method": "GET", + "url": "http://fastedge-builtin.debug", + "headers": { + "host": "example.com" + }, + "body": "" + }, + "dotenv": { + "enabled": true, + "path": "." + }, + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } +} diff --git a/examples/cdn/large_env_variable/fixtures/missing-config.test.json b/examples/cdn/large_env_variable/fixtures/missing-config.test.json new file mode 100644 index 0000000..f28bbc0 --- /dev/null +++ b/examples/cdn/large_env_variable/fixtures/missing-config.test.json @@ -0,0 +1,17 @@ +{ + "appType": "proxy-wasm", + "description": "LARGE_CONFIG not set — x-config-size header shows 0", + "request": { + "method": "GET", + "url": "http://fastedge-builtin.debug", + "headers": { + "host": "example.com" + }, + "body": "" + }, + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } +} diff --git a/examples/cdn/log_time/Cargo.lock b/examples/cdn/log_time/Cargo.lock new file mode 100644 index 0000000..8ade1fd --- /dev/null +++ b/examples/cdn/log_time/Cargo.lock @@ -0,0 +1,56 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "log_time" +version = "0.1.0" +dependencies = [ + "log", + "proxy-wasm", +] + +[[package]] +name = "proxy-wasm" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8d35d9e2bc5104e2e954b149aa1d5f9fa3bb27f73b45b2706020fed101db685" +dependencies = [ + "hashbrown", + "log", +] diff --git a/examples/cdn/log_time/fixtures/happy-path.test.json b/examples/cdn/log_time/fixtures/happy-path.test.json new file mode 100644 index 0000000..21aa05a --- /dev/null +++ b/examples/cdn/log_time/fixtures/happy-path.test.json @@ -0,0 +1,21 @@ +{ + "appType": "proxy-wasm", + "description": "Basic request — verifies time logging at request and response phases", + "request": { + "method": "GET", + "url": "http://fastedge-builtin.debug", + "headers": { + "host": "example.com" + }, + "body": "" + }, + "response": { + "headers": {}, + "body": "" + }, + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } +} diff --git a/examples/cdn/md2html/Cargo.lock b/examples/cdn/md2html/Cargo.lock new file mode 100644 index 0000000..5348e7a --- /dev/null +++ b/examples/cdn/md2html/Cargo.lock @@ -0,0 +1,108 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "bitflags" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "getopts" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "md2html" +version = "0.1.0" +dependencies = [ + "proxy-wasm", + "pulldown-cmark", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "proxy-wasm" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8d35d9e2bc5104e2e954b149aa1d5f9fa3bb27f73b45b2706020fed101db685" +dependencies = [ + "hashbrown", + "log", +] + +[[package]] +name = "pulldown-cmark" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "679341d22c78c6c649893cbd6c3278dcbe9fc4faa62fea3a9296ae2b50c14625" +dependencies = [ + "bitflags", + "getopts", + "memchr", + "pulldown-cmark-escape", + "unicase", +] + +[[package]] +name = "pulldown-cmark-escape" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae" + +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" diff --git a/examples/cdn/md2html/fixtures/.env b/examples/cdn/md2html/fixtures/.env new file mode 100644 index 0000000..0858b28 --- /dev/null +++ b/examples/cdn/md2html/fixtures/.env @@ -0,0 +1 @@ +FASTEDGE_VAR_ENV_BASE=/repo/main diff --git a/examples/cdn/md2html/fixtures/convert-to-html.test.json b/examples/cdn/md2html/fixtures/convert-to-html.test.json new file mode 100644 index 0000000..f9cdcd4 --- /dev/null +++ b/examples/cdn/md2html/fixtures/convert-to-html.test.json @@ -0,0 +1,26 @@ +{ + "appType": "proxy-wasm", + "description": "Upstream returns text/markdown — converted to HTML with BASE path prefix", + "request": { + "method": "GET", + "url": "http://fastedge-builtin.debug", + "headers": { + "host": "example.com", + "content-type": "text/markdown", + "x-debugger-content": "body-only" + }, + "body": "# Hello World\n\nThis is a **markdown** document.\n\n- Item 1\n- Item 2\n" + }, + "properties": { + "request.path": "/readme.md" + }, + "dotenv": { + "enabled": true, + "path": "." + }, + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } +} diff --git a/examples/cdn/md2html/fixtures/html-passthrough.test.json b/examples/cdn/md2html/fixtures/html-passthrough.test.json new file mode 100644 index 0000000..d3c52f9 --- /dev/null +++ b/examples/cdn/md2html/fixtures/html-passthrough.test.json @@ -0,0 +1,26 @@ +{ + "appType": "proxy-wasm", + "description": "Upstream returns text/html — no conversion, passed through unchanged", + "request": { + "method": "GET", + "url": "http://fastedge-builtin.debug", + "headers": { + "host": "example.com", + "content-type": "text/html", + "x-debugger-content": "body-only" + }, + "body": "Already HTML" + }, + "properties": { + "request.path": "/page.html" + }, + "dotenv": { + "enabled": true, + "path": "." + }, + "logLevel": 2, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" + } +} diff --git a/examples/cdn/md2html/src/lib.rs b/examples/cdn/md2html/src/lib.rs index dfdaa12..28753ce 100644 --- a/examples/cdn/md2html/src/lib.rs +++ b/examples/cdn/md2html/src/lib.rs @@ -1,6 +1,6 @@ use proxy_wasm::traits::*; use proxy_wasm::types::*; -use pulldown_cmark::{Parser, Options}; +use pulldown_cmark::{Options, Parser}; use std::env; const BAD_REQUEST: u32 = 400; @@ -30,23 +30,22 @@ impl Context for HttpBody {} impl HttpContext for HttpBody { fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action { - self.set_http_request_header("Accept-Encoding", None); // don't want to process gzipped body + self.set_http_request_header("Accept-Encoding", None); // don't want to process gzipped body let Ok(base) = env::var("BASE") else { println!("BASE is not set - URL is not modified"); return Action::Continue; }; let url = match self.get_property(vec!["request.path"]) { - Some(url) => { - match std::str::from_utf8(&url) { - Ok(u) => u.to_string(), - Err(e) => { - println!("Error parsing URL path: {}", e); - self.send_http_response(BAD_REQUEST, vec![], None); - return Action::Pause; - } + Some(url) => match std::str::from_utf8(&url) { + Ok(u) => u.to_string(), + Err(e) => { + println!("Error parsing URL path: {}", e); + self.send_http_response(BAD_REQUEST, vec![], None); + return Action::Pause; } - } - None => { // should never happen + }, + None => { + // should never happen println!("URL path is missing"); "/".to_string() } @@ -58,8 +57,10 @@ impl HttpContext for HttpBody { } fn on_http_response_headers(&mut self, _: usize, _: bool) -> Action { + println!("On response headers"); if let Some(content_type) = self.get_http_response_header("Content-Type") { if content_type.starts_with("text/plain") || content_type.starts_with("text/markdown") { + println!("Response is markdown, converting to HTML"); self.set_http_response_header("Content-Length", None); self.set_http_response_header("Transfer-Encoding", Some("Chunked")); self.set_http_response_header("Content-Type", Some("text/html")); @@ -76,7 +77,8 @@ impl HttpContext for HttpBody { return Action::Continue; } - if !end_of_stream { // wait for complete body + if !end_of_stream { + // wait for complete body return Action::Pause; } @@ -89,7 +91,7 @@ impl HttpContext for HttpBody { let parser = Parser::new_ext( md.as_str(), - Options::ENABLE_TABLES | Options::ENABLE_FOOTNOTES + Options::ENABLE_TABLES | Options::ENABLE_FOOTNOTES, ); let mut html = String::new(); html.push_str(""); @@ -97,7 +99,7 @@ impl HttpContext for HttpBody { html.push_str(""); let body = html.as_bytes(); - self.set_http_response_body(0, body.len(), body); + self.set_http_response_body(0, body_size, body); println!("Converted"); Action::Continue diff --git a/examples/cdn/properties/fixtures/happy-path.test.json b/examples/cdn/properties/fixtures/happy-path.test.json index 8528484..7ef922c 100644 --- a/examples/cdn/properties/fixtures/happy-path.test.json +++ b/examples/cdn/properties/fixtures/happy-path.test.json @@ -17,5 +17,9 @@ "request.country.name": "Luxembourg", "request.region": "LU", "request.continent": "Europe" + }, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" } } diff --git a/examples/cdn/variables_and_secrets/fixtures/happy-path.test.json b/examples/cdn/variables_and_secrets/fixtures/happy-path.test.json index adea596..3e4bec1 100644 --- a/examples/cdn/variables_and_secrets/fixtures/happy-path.test.json +++ b/examples/cdn/variables_and_secrets/fixtures/happy-path.test.json @@ -10,5 +10,9 @@ "dotenv": { "enabled": true, "path": "." + }, + "wasm": { + "path": "/.fastedge-debug/app.wasm", + "description": "Default debugger WASM binary" } } diff --git a/fastedge-plugin-source/check-copilot-sync.sh b/fastedge-plugin-source/check-copilot-sync.sh index 28d1153..70d69e5 100755 --- a/fastedge-plugin-source/check-copilot-sync.sh +++ b/fastedge-plugin-source/check-copilot-sync.sh @@ -2,12 +2,13 @@ # Validates copilot-instructions.md stays in sync with the codebase: # 1. All doc files in manifest.json are referenced in the mapping table # 2. All doc files in the mapping table actually exist on disk +# 3. All example directories on disk are tracked in manifest.json # # This script is part of the fastedge-plugin pipeline contract. # Canonical template: fastedge-plugin/scripts/sync/templates/check-copilot-sync-template.sh # Each source repo gets a copy at: fastedge-plugin-source/check-copilot-sync.sh # -# Exits 0 if in sync, 1 if drift detected. +# Exits 0 if in sync (warnings don't affect exit code), 1 if drift detected. set -euo pipefail @@ -23,7 +24,7 @@ fi # --- Check 1: manifest doc files appear in the mapping table --- # Extract doc/schema paths that appear in mapping table rows (lines starting with '|') -# Use POSIX-compatible awk instead of grep -P so this works on macOS/BSD grep too +# Uses awk to parse backticked paths — works on macOS/BSD and Linux mapping_table_docs=$(awk ' /^\|/ { line = $0 @@ -45,11 +46,11 @@ if [ -f "$MANIFEST" ]; then exit 1 fi - doc_files=$(jq -r '.sources[].files[]' "$MANIFEST" | grep -E '^(docs|schemas)/' | sort -u) + doc_files=$(jq -r '.sources[].files[] | select(startswith("docs/") or startswith("schemas/"))' "$MANIFEST" | sort -u) missing=() for doc in $doc_files; do - if ! echo "$mapping_table_docs" | grep -qF "$doc"; then + if ! printf '%s\n' "$mapping_table_docs" | grep -qxF "$doc"; then missing+=("$doc") fi done @@ -88,6 +89,50 @@ else echo "OK: All doc files in copilot-instructions mapping table exist on disk" fi +# --- Check 3: example directories on disk are tracked in manifest --- +# +# Detects example projects not listed in manifest.json so the fastedge-plugin +# pipeline doesn't silently miss new examples. Uses ::warning:: annotations +# for visibility in GitHub PR checks. +# +# This check is advisory (does not affect exit code) because repos may have +# known gaps during rollout. + +if [ -d "examples" ] && [ -f "$MANIFEST" ]; then + # Get all examples/ file paths from the manifest (filter in jq to avoid + # grep exit-1 on no matches, which would kill the script under pipefail) + manifest_examples=$(jq -r '.sources[].files[] | select(startswith("examples/"))' "$MANIFEST" | sort -u) + + # Find example project directories (contain package.json, Cargo.toml, or asconfig.json) + # Handles flat (examples//) and nested (examples/cdn//) structures + untracked=() + while IFS= read -r marker_file; do + [ -z "$marker_file" ] && continue + project_dir=$(dirname "$marker_file") + # Check if any manifest file starts with this project directory + # Uses awk index() for literal prefix match (grep would treat . [ + as regex) + if [ -z "$manifest_examples" ] || ! printf '%s\n' "$manifest_examples" | awk -v prefix="${project_dir}/" 'index($0, prefix) == 1 {found=1; exit} END {exit !found}'; then + untracked+=("$project_dir") + fi + done < <(find examples/ -maxdepth 4 \( -name "package.json" -o -name "Cargo.toml" -o -name "asconfig.json" \) -not -path "*/node_modules/*" 2>/dev/null | sort) + + if [ ${#untracked[@]} -gt 0 ]; then + echo "WARN: Example directories not tracked in $MANIFEST:" + for dir in "${untracked[@]}"; do + echo " - $dir" + echo "::warning::Example directory '$dir' is not tracked in manifest.json. Add it to fastedge-plugin-source/manifest.json so the fastedge-plugin pipeline can access it." + done + echo "" + echo " To fix: add source entries for the above directories to $MANIFEST" + else + echo "OK: All example directories are tracked in manifest.json" + fi +elif [ ! -d "examples" ]; then + echo "SKIP: No examples/ directory" +else + echo "SKIP: No manifest found at $MANIFEST (cannot check examples coverage)" +fi + # --- Result --- if [ $errors -ne 0 ]; then diff --git a/fastedge-plugin-source/generate-docs.sh b/fastedge-plugin-source/generate-docs.sh index 7ab1dbc..6381563 100755 --- a/fastedge-plugin-source/generate-docs.sh +++ b/fastedge-plugin-source/generate-docs.sh @@ -260,7 +260,10 @@ PROMPT return 130 fi - claude -p --model "$MODEL" "$prompt" > "$tmpfile" + # Pipe prompt via stdin to avoid Linux's MAX_ARG_STRLEN (~128KB per argv string). + # Large prompts (source files + existing doc for incremental updates) exceed + # this limit as an SDK grows; stdin has no such cap. + claude -p --model "$MODEL" > "$tmpfile" <<<"$prompt" # Validate: first non-empty line must start with # local first_line