use serde::Deserialize;
use crate::platform::error::PlatformError;
use crate::platform::web::embedded::{PLATFORM_LIBRARY_ASSETS, PLATFORM_SKILL_EXTRA_ASSETS};
pub const RESERVED_HUB_PUBLISHER_ID: &str = "zebflow";
#[derive(Debug, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BlessedPackageMeta {
pub id: String,
pub version: String,
pub title: String,
pub description: String,
}
#[derive(Debug, Clone)]
pub struct BlessedFile {
pub rel_path: String,
pub bytes: &'static [u8],
}
#[derive(Debug, Clone)]
pub struct BlessedPackage {
pub package_id: String,
pub version: String,
pub title: String,
pub description: String,
pub asset_kind: &'static str,
pub source_ref: String,
pub files: Vec<BlessedFile>,
}
fn parse_meta(source_ref: &str, bytes: &[u8]) -> Result<BlessedPackageMeta, PlatformError> {
let meta: BlessedPackageMeta = serde_yaml_ng::from_slice(bytes).map_err(|err| {
PlatformError::new(
"BLESSED_PACKAGE_META",
format!("invalid package.yaml in {source_ref}: {err}"),
)
})?;
if meta.id.trim() != meta.id
|| !meta
.id
.strip_prefix(&format!("{RESERVED_HUB_PUBLISHER_ID}."))
.is_some_and(|slug| !slug.is_empty())
{
return Err(PlatformError::new(
"BLESSED_PACKAGE_META",
format!(
"package id '{}' in {source_ref} must use the reserved '{RESERVED_HUB_PUBLISHER_ID}.' prefix",
meta.id
),
));
}
if meta.version.trim().is_empty() {
return Err(PlatformError::new(
"BLESSED_PACKAGE_META",
format!("package.yaml in {source_ref} declares no version"),
));
}
Ok(meta)
}
fn blessed_rwe_library_packages() -> Result<Vec<BlessedPackage>, PlatformError> {
let mut names: Vec<&'static str> = Vec::new();
for asset in PLATFORM_LIBRARY_ASSETS {
if let Some(name) = asset
.path
.strip_prefix("zeb/")
.and_then(|rest| rest.strip_suffix("/package.yaml"))
&& !name.contains('/')
{
names.push(name);
}
}
names.sort_unstable();
let mut packages = Vec::with_capacity(names.len());
for name in names {
let source_ref = format!("blessed/rwe-libraries/{name}");
let prefix = format!("zeb/{name}/");
let mut meta = None;
let mut files = Vec::new();
for asset in PLATFORM_LIBRARY_ASSETS {
let Some(rest) = asset.path.strip_prefix(&prefix) else {
continue;
};
if rest == "package.yaml" {
meta = Some(parse_meta(&source_ref, asset.bytes)?);
continue;
}
files.push(BlessedFile {
rel_path: rest.to_string(),
bytes: asset.bytes,
});
}
let meta = meta.ok_or_else(|| {
PlatformError::new(
"BLESSED_PACKAGE_META",
format!("{source_ref} has no package.yaml"),
)
})?;
files.sort_by(|a, b| a.rel_path.cmp(&b.rel_path));
packages.push(BlessedPackage {
package_id: meta.id,
version: meta.version,
title: meta.title,
description: meta.description,
asset_kind: "rwe_library",
source_ref,
files,
});
}
Ok(packages)
}
fn blessed_skill_packages() -> Result<Vec<BlessedPackage>, PlatformError> {
use sha2::{Digest, Sha256};
let mut names: Vec<&'static str> = Vec::new();
for asset in PLATFORM_SKILL_EXTRA_ASSETS {
if let Some(name) = asset.path.strip_suffix("/SKILL.md")
&& !name.contains('/')
{
names.push(name);
}
}
names.sort_unstable();
let mut packages = Vec::with_capacity(names.len());
for name in names {
let prefix = format!("{name}/");
let mut files = Vec::new();
let mut hasher = Sha256::new();
let mut description = String::new();
let mut declared_version = "1".to_string();
for asset in PLATFORM_SKILL_EXTRA_ASSETS {
let Some(rest) = asset.path.strip_prefix(&prefix) else {
continue;
};
hasher.update(rest.as_bytes());
hasher.update(asset.bytes);
if rest == "SKILL.md" {
let fm = crate::platform::skills::parse_frontmatter(&String::from_utf8_lossy(asset.bytes));
description = fm.description.unwrap_or_default();
if let Some(v) = fm.extra.get("metadata.version") {
declared_version = v.clone();
}
}
files.push(BlessedFile {
rel_path: format!("{}/{name}/{rest}", crate::platform::skills::PROJECT_SKILLS_DIR),
bytes: asset.bytes,
});
}
files.sort_by(|a, b| a.rel_path.cmp(&b.rel_path));
let digest = hex::encode(hasher.finalize());
let version = format!("{}-{}", declared_version.replace(['+', ' '], "-"), &digest[..8]);
packages.push(BlessedPackage {
package_id: format!("zebflow.skill-{name}"),
version,
title: name.to_string(),
description: description.lines().next().unwrap_or("").to_string(),
asset_kind: "skill",
source_ref: format!("blessed/skill-extras/{name}"),
files,
});
}
Ok(packages)
}
pub fn blessed_packages() -> Result<Vec<BlessedPackage>, PlatformError> {
let mut packages = blessed_rwe_library_packages()?;
packages.extend(blessed_skill_packages()?);
Ok(packages)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_blessed_library_embeds_its_declared_license_notices() {
for package in blessed_rwe_library_packages().expect("blessed libraries") {
let manifest = package
.files
.iter()
.find(|file| file.rel_path == "manifest.json")
.expect("every library has a manifest");
let document = crate::contracts::decode_contract::<
crate::contracts::kinds::RweLibraryManifestContract,
>(manifest.bytes)
.expect("valid library manifest");
for notice in &document.spec.license.notices {
assert!(
package
.files
.iter()
.any(|file| file.rel_path == *notice && !file.bytes.is_empty()),
"{} declares notice {notice}, but the seeded package omits its bytes",
package.package_id
);
}
if package.package_id == "zebflow.deckgl" {
assert!(
package
.files
.iter()
.any(|file| file.rel_path == "MODIFICATIONS")
);
}
}
}
#[test]
fn every_blessed_package_carries_reserved_metadata_and_files() {
let packages = blessed_packages().expect("blessed packages enumerate");
let libraries = packages.iter().filter(|p| p.asset_kind == "rwe_library").count();
let skills = packages.iter().filter(|p| p.asset_kind == "skill").count();
assert_eq!(libraries, 11);
assert!(skills >= 1);
assert_eq!(packages.len(), libraries + skills);
assert!(
!packages.iter().any(|p| p.package_id.starts_with("zebflow.skill-zebflow-")),
"core zebflow-* skills must not be shelf packages"
);
let extra = packages
.iter()
.find(|p| p.package_id == "zebflow.skill-procedural-assets")
.expect("an optional skill is on the shelf");
assert!(extra.files.iter().any(|f| f.rel_path == "skills/procedural-assets/SKILL.md"));
assert!(extra.version.starts_with("1-"), "version is metadata.version-digest: {}", extra.version);
assert!(
!packages
.iter()
.any(|p| matches!(p.package_id.as_str(), "zebflow.preact" | "zebflow.react"))
);
for package in &packages {
assert!(
package.package_id.starts_with("zebflow."),
"{} is outside the reserved publisher",
package.package_id
);
assert!(!package.files.is_empty(), "{} is empty", package.package_id);
assert!(
package
.files
.iter()
.all(|file| file.rel_path != "package.yaml"),
"{} carries its publish metadata as content",
package.package_id
);
}
let deckgl = packages
.iter()
.find(|package| package.package_id == "zebflow.deckgl")
.expect("deckgl is blessed");
assert_eq!(deckgl.asset_kind, "rwe_library");
assert!(
deckgl
.files
.iter()
.any(|file| file.rel_path == "manifest.json")
);
assert!(
deckgl
.files
.iter()
.any(|file| file.rel_path == "0.1/runtime/deckgl.bundle.mjs")
);
assert!(
!packages.iter().any(|p| p.package_id.starts_with("zebflow.ui")),
"zeb/ui ships as source the compiler inlines, not as a hub package"
);
assert!(packages.iter().all(|p| matches!(p.asset_kind, "rwe_library" | "skill")));
}
}