From 6e97eb4fd00d7771ad768df8abcef14235999b93 Mon Sep 17 00:00:00 2001 From: prk-Jr Date: Thu, 2 Jul 2026 22:35:52 +0530 Subject: [PATCH] Move chunked_config to edgezero-core and export it The Fastly chunk-pointer writer and resolver are pure functions (I/O is delegated to caller-supplied callbacks), but lived crate-private inside edgezero-adapter-fastly. Downstream hosts that read Fastly-shaped config stores through their own store abstractions could not reuse them and had to re-implement the wire format (pointer schema, per-chunk len + sha verification, envelope reassembly). Move the module to edgezero-core, make its API pub, and keep edgezero-adapter-fastly compiling unchanged via a pub(crate) re-export. No behavior change; the module's tests move with it. --- crates/edgezero-adapter-fastly/src/lib.rs | 3 +- .../src/chunked_config.rs | 33 ++++++++++--------- crates/edgezero-core/src/lib.rs | 1 + 3 files changed, 21 insertions(+), 16 deletions(-) rename crates/{edgezero-adapter-fastly => edgezero-core}/src/chunked_config.rs (96%) diff --git a/crates/edgezero-adapter-fastly/src/lib.rs b/crates/edgezero-adapter-fastly/src/lib.rs index a05a02f6..dbac120f 100644 --- a/crates/edgezero-adapter-fastly/src/lib.rs +++ b/crates/edgezero-adapter-fastly/src/lib.rs @@ -1,7 +1,6 @@ //! Utilities for bridging Fastly Compute@Edge requests into the //! `edgezero-core` service abstractions. -pub(crate) mod chunked_config; #[cfg(feature = "cli")] pub mod cli; #[cfg(feature = "fastly")] @@ -22,6 +21,8 @@ pub mod secret_store; #[cfg(feature = "fastly")] use edgezero_core::app::{Hooks, StoresMetadata}; +#[cfg(any(feature = "cli", feature = "fastly", test))] +pub(crate) use edgezero_core::chunked_config; #[cfg(feature = "fastly")] use edgezero_core::env_config::EnvConfig; #[cfg(feature = "fastly")] diff --git a/crates/edgezero-adapter-fastly/src/chunked_config.rs b/crates/edgezero-core/src/chunked_config.rs similarity index 96% rename from crates/edgezero-adapter-fastly/src/chunked_config.rs rename to crates/edgezero-core/src/chunked_config.rs index 6c8b0937..4d270899 100644 --- a/crates/edgezero-adapter-fastly/src/chunked_config.rs +++ b/crates/edgezero-core/src/chunked_config.rs @@ -5,6 +5,12 @@ //! module splits the envelope into content-addressed chunk entries plus a //! root pointer entry that is written LAST. //! +//! The logic is pure (I/O is delegated to caller-supplied callbacks), so it +//! lives in `edgezero-core` rather than the Fastly adapter: host-side tools +//! and runtimes that read Fastly-shaped config stores can resolve chunked +//! values without depending on the Fastly SDK. `edgezero-adapter-fastly` +//! re-exports this module and remains the primary consumer. +//! //! The pointer JSON shape (spec 9.2): //! ```json //! { @@ -24,21 +30,18 @@ use sha2::{Digest as _, Sha256}; /// Per-entry value limit enforced by Fastly Config Store. Used by the /// CLI writer to gate direct-vs-chunked storage; the runtime resolver /// reads chunk lengths from the pointer struct, not this constant. -#[cfg(any(feature = "cli", test))] -pub(crate) const FASTLY_CONFIG_ENTRY_LIMIT: usize = 8_000; +pub const FASTLY_CONFIG_ENTRY_LIMIT: usize = 8_000; /// Target payload size per chunk (kept under the entry limit to leave /// room for the key and any protocol overhead). CLI writer only. -#[cfg(any(feature = "cli", test))] -pub(crate) const CHUNK_PAYLOAD_TARGET: usize = 7_000; +pub const CHUNK_PAYLOAD_TARGET: usize = 7_000; /// Infix inserted between the root key and the content-address in a /// chunk key: `.__edgezero_chunks..`. CLI writer /// only; the resolver reads chunk keys from the pointer struct. -#[cfg(any(feature = "cli", test))] -pub(crate) const CHUNK_KEY_INFIX: &str = ".__edgezero_chunks."; +pub const CHUNK_KEY_INFIX: &str = ".__edgezero_chunks."; /// `edgezero_kind` discriminant stored in the pointer JSON. Used by /// BOTH the writer (when serialising the pointer) AND the resolver /// (when validating the parsed pointer) -- stays unconditional. -pub(crate) const POINTER_KIND: &str = "fastly_config_chunks"; +pub const POINTER_KIND: &str = "fastly_config_chunks"; // --------------------------------------------------------------------------- // Private pointer schema @@ -83,8 +86,8 @@ fn sha256_hex(bytes: &[u8]) -> String { /// /// Returns an error string if the pointer JSON itself would exceed 8 000 /// characters (extremely unlikely in practice; recommends restructuring). -#[cfg(any(feature = "cli", test))] -pub(crate) fn prepare_fastly_config_entries( +#[inline] +pub fn prepare_fastly_config_entries( root_key: &str, envelope_json: &str, ) -> Result, String> { @@ -172,7 +175,6 @@ pub(crate) fn prepare_fastly_config_entries( /// Find the largest byte offset `<= end_raw` that is a valid UTF-8 /// codepoint boundary within `src`. `start` is used as a hint so we /// don't scan the entire string from zero each time. CLI writer only. -#[cfg(any(feature = "cli", test))] fn find_utf8_boundary(src: &str, start: usize, end_raw: usize) -> usize { if end_raw >= src.len() { return src.len(); @@ -204,7 +206,8 @@ fn find_utf8_boundary(src: &str, start: usize, end_raw: usize) -> usize { /// /// Returns a descriptive error string naming the root key or the failing /// chunk key when any integrity check fails. -pub(crate) fn resolve_fastly_config_value( +#[inline] +pub fn resolve_fastly_config_value( root_key: &str, root_value: String, mut fetch: F, @@ -212,7 +215,7 @@ pub(crate) fn resolve_fastly_config_value( where F: FnMut(&str) -> Result, String>, { - use edgezero_core::blob_envelope::BlobEnvelope; + use crate::blob_envelope::BlobEnvelope; // --- Path 1: direct BlobEnvelope --- if let Ok(envelope) = serde_json::from_str::(&root_value) { @@ -317,7 +320,7 @@ mod tests { /// Build a valid `BlobEnvelope` JSON string of approximately `target_len` /// characters by padding the data payload. fn make_envelope_json(target_len: usize) -> String { - use edgezero_core::blob_envelope::BlobEnvelope; + use crate::blob_envelope::BlobEnvelope; use serde_json::json; // Build a minimal envelope first to measure overhead. @@ -384,7 +387,7 @@ mod tests { // Construct an envelope whose padding contains emoji (4 bytes each). // We intentionally size it so a naive byte-boundary split would land // mid-codepoint. - use edgezero_core::blob_envelope::BlobEnvelope; + use crate::blob_envelope::BlobEnvelope; use serde_json::json; // crab emoji: 4 bytes each; 3000 crabs = 12 000 bytes of payload @@ -458,7 +461,7 @@ mod tests { #[test] fn resolver_returns_direct_envelope_unchanged() { - use edgezero_core::blob_envelope::BlobEnvelope; + use crate::blob_envelope::BlobEnvelope; use serde_json::json; let envelope = BlobEnvelope::new(json!({"hello": "world"}), "2026-06-22T00:00:00Z".into()); diff --git a/crates/edgezero-core/src/lib.rs b/crates/edgezero-core/src/lib.rs index 0337e5e3..cec95047 100644 --- a/crates/edgezero-core/src/lib.rs +++ b/crates/edgezero-core/src/lib.rs @@ -15,6 +15,7 @@ pub mod app_config; pub mod blob_envelope; pub mod body; pub mod canonical_form; +pub mod chunked_config; pub mod compression; pub mod config_store; pub mod context;