reinhardt_dentdelion/lib.rs
1//! Dentdelion - Plugin System for Reinhardt Framework
2//!
3//! Dentdelion (dent de lion = lion's tooth = dandelion) is a plugin system
4//! that makes it easy to create, distribute, and install plugins for the
5//! Reinhardt web framework.
6//!
7//! # Features
8//!
9//! - **Static Plugins**: Rust crates compiled with your application
10//! - **WASM Plugins**: Dynamic plugins loaded at runtime (with `wasm` feature)
11//! - **TypeScript Plugins**: TypeScript/JavaScript plugins via boa_engine (pure-Rust ECMAScript engine) (with `ts` feature)
12//! - **Capability System**: Fine-grained control over what plugins can do
13//! - **CLI Management**: Install and manage plugins via `reinhardt plugin` commands
14//! - **Multi-Runtime**: Unified interface for Static, WASM, and TypeScript runtimes
15//!
16//! # Naming Convention
17//!
18//! Plugin names should follow the `xxx-delion` pattern:
19//! - `auth-delion` - Authentication plugin
20//! - `rate-limit-delion` - Rate limiting plugin
21//! - `analytics-delion` - Analytics plugin
22//!
23//! # Quick Start
24//!
25//! ## Creating a Plugin
26//!
27//! ```rust
28//! use reinhardt_dentdelion::prelude::*;
29//! use std::sync::Arc;
30//!
31//! struct MyPlugin {
32//! metadata: PluginMetadata,
33//! }
34//!
35//! impl MyPlugin {
36//! pub fn new() -> Self {
37//! Self {
38//! metadata: PluginMetadata::builder("my-delion", "1.0.0")
39//! .description("My awesome plugin")
40//! .provides(PluginCapability::Middleware)
41//! .build()
42//! .unwrap(),
43//! }
44//! }
45//! }
46//!
47//! impl Plugin for MyPlugin {
48//! fn metadata(&self) -> &PluginMetadata {
49//! &self.metadata
50//! }
51//!
52//! fn capabilities(&self) -> &[Capability] {
53//! &[Capability::Core(PluginCapability::Middleware)]
54//! }
55//! }
56//!
57//! // Register the plugin for automatic discovery
58//! register_plugin!(|| Arc::new(MyPlugin::new()));
59//! ```
60//!
61//! ## Using the Registry
62//!
63//! ```rust
64//! use reinhardt_dentdelion::prelude::*;
65//! use std::sync::Arc;
66//!
67//! # struct MyPlugin { metadata: PluginMetadata }
68//! # impl MyPlugin {
69//! # fn new() -> Self {
70//! # Self {
71//! # metadata: PluginMetadata::builder("my-delion", "1.0.0")
72//! # .build().unwrap(),
73//! # }
74//! # }
75//! # }
76//! # impl Plugin for MyPlugin {
77//! # fn metadata(&self) -> &PluginMetadata { &self.metadata }
78//! # fn capabilities(&self) -> &[Capability] { &[] }
79//! # }
80//! let registry = PluginRegistry::new();
81//!
82//! // Register plugins
83//! registry.register(Arc::new(MyPlugin::new())).unwrap();
84//!
85//! // Validate dependencies
86//! registry.validate_dependencies().unwrap();
87//!
88//! // Get enable order
89//! let order = registry.get_enable_order().unwrap();
90//! ```
91//!
92//! ## Project Manifest (dentdelion.toml)
93//!
94//! ```toml
95//! [dentdelion]
96//! format_version = "1.0"
97//! wasm_dir = ".dentdelion/plugins"
98//!
99//! [[plugins]]
100//! name = "auth-delion"
101//! type = "static"
102//! version = "1.0.0"
103//! enabled = true
104//!
105//! [plugin_config.auth-delion]
106//! algorithm = "HS256"
107//! ```
108//!
109//! # Feature Flags
110//!
111//! - `default` - Core plugin system only
112//! - `wasm` - WASM plugin support with Component Model (requires wasmtime 36.x)
113//! - `ts` - TypeScript/JavaScript SSR support via boa_engine (pure-Rust ECMAScript engine)
114//! - `cli` - CLI support for crates.io integration
115//! - `full` - All features enabled (wasm + cli); note: `ts` is NOT included in `full`
116//!
117//! # WASM Plugin Support
118//!
119//! When the `wasm` feature is enabled, plugins can be loaded dynamically at runtime
120//! from WebAssembly Component Model files (.wasm).
121//!
122//! ## Key Features
123//!
124//! - **Component Model**: Uses WebAssembly Interface Types (WIT) for type-safe interfaces
125//! - **Sandboxed Execution**: Memory limits, timeouts, and fuel-based CPU metering
126//! - **Host API**: Config, logging, services, HTTP client, and database access
127//! - **Capability-Based Security**: Fine-grained permission checks for sensitive operations
128//!
129//! ## Requirements
130//!
131//! - Plugins must implement the `dentdelion-plugin` WIT world (see `wit/dentdelion.wit`)
132//! - Use `wit-bindgen` or `cargo-component` for code generation
133//! - Data serialized with MessagePack for WASM boundary crossing
134//!
135//! ## Runtime Configuration
136//!
137//! ```ignore
138//! use reinhardt_dentdelion::wasm::{WasmRuntime, WasmRuntimeConfigBuilder};
139//! use std::time::Duration;
140//!
141//! let config = WasmRuntimeConfigBuilder::new()
142//! .memory_limit_mb(128)
143//! .timeout(Duration::from_secs(30))
144//! .fuel_metering(true)
145//! .initial_fuel(100_000_000)
146//! .build();
147//!
148//! let runtime = WasmRuntime::new(config)?;
149//! ```
150//!
151//! ## Capabilities
152//!
153//! Two special capabilities are available for WASM plugins:
154//! - `NetworkAccess` - Required for `http_get`/`http_post` host functions
155//! - `DatabaseAccess` - Required for `db_query`/`db_execute` host functions
156//!
157//! # Architecture
158//!
159//! ```text
160//! ┌──────────────────────────────────┐
161//! │ PluginRegistry │
162//! │ - Plugin registration │
163//! │ - Dependency resolution │
164//! │ - Lifecycle management │
165//! └────────────────┬─────────────────┘
166//! │
167//! ┌────────────┼────────────┐
168//! │ │ │
169//! ┌───▼──┐ ┌───▼──┐ ┌───▼────┐
170//! │Static│ │ WASM │ │Manifest│
171//! │Plugin│ │Plugin│ │ Parser │
172//! └──────┘ └──────┘ └────────┘
173//! ```
174
175#![warn(missing_docs)]
176#![warn(rustdoc::missing_crate_level_docs)]
177
178pub mod capability;
179pub mod context;
180pub mod error;
181pub mod installer;
182pub mod manifest;
183pub mod metadata;
184pub mod plugin;
185pub mod registry;
186pub mod runtime;
187
188#[cfg(feature = "cli")]
189pub mod crates_io;
190
191#[cfg(feature = "wasm")]
192pub mod wasm;
193
194/// Re-export commonly used types.
195pub mod prelude {
196 pub use crate::capability::{Capability, PluginCapability, PluginTier, TrustLevel};
197 pub use crate::context::{PluginContext, PluginContextBuilder};
198 pub use crate::error::{PluginError, PluginResult, PluginState};
199 pub use crate::installer::PluginInstaller;
200 pub use crate::manifest::{
201 InstalledPlugin, MANIFEST_FILENAME, PluginType, ProjectManifest, WasmPluginConfig,
202 };
203 pub use crate::metadata::{PluginDependency, PluginMetadata, PluginMetadataBuilder};
204 pub use crate::plugin::{
205 ArcPlugin, ArcPluginLifecycle, BoxedPlugin, Plugin, PluginFactory, PluginLifecycle,
206 PluginRegistration,
207 };
208 pub use crate::register_plugin;
209 pub use crate::registry::PluginRegistry;
210 pub use crate::runtime::{
211 ArcRuntime, BoxedRuntime, PluginRuntime, RuntimeError, RuntimeLimits, RuntimeLimitsBuilder,
212 RuntimeType,
213 };
214
215 #[cfg(feature = "cli")]
216 pub use crate::crates_io::CratesIoClient;
217
218 #[cfg(feature = "wasm")]
219 pub use crate::wasm::{
220 ColumnDef, ColumnType, Event, EventBus, IndexDef, ModelRegistry, ModelSchema,
221 SharedEventBus, SharedModelRegistry, SqlMigration,
222 };
223
224 pub use async_trait::async_trait;
225}
226
227// Re-export inventory for plugin registration
228pub use inventory;
229
230#[cfg(test)]
231mod tests {
232 use super::prelude::*;
233 use std::sync::Arc;
234
235 struct TestPlugin {
236 metadata: PluginMetadata,
237 capabilities: Vec<Capability>,
238 }
239
240 impl TestPlugin {
241 fn new() -> Self {
242 Self {
243 metadata: PluginMetadata::builder("test-delion", "1.0.0")
244 .description("Test plugin for integration tests")
245 .author("Test Author")
246 .license("MIT")
247 .provides(PluginCapability::Middleware)
248 .build()
249 .unwrap(),
250 capabilities: vec![Capability::Core(PluginCapability::Middleware)],
251 }
252 }
253 }
254
255 impl Plugin for TestPlugin {
256 fn metadata(&self) -> &PluginMetadata {
257 &self.metadata
258 }
259
260 fn capabilities(&self) -> &[Capability] {
261 &self.capabilities
262 }
263 }
264
265 #[async_trait]
266 impl PluginLifecycle for TestPlugin {
267 async fn on_load(&self, _ctx: &PluginContext) -> Result<(), PluginError> {
268 tracing::info!("TestPlugin loaded");
269 Ok(())
270 }
271
272 async fn on_enable(&self, _ctx: &PluginContext) -> Result<(), PluginError> {
273 tracing::info!("TestPlugin enabled");
274 Ok(())
275 }
276 }
277
278 #[test]
279 fn test_integration() {
280 let registry = PluginRegistry::new();
281 let plugin = Arc::new(TestPlugin::new());
282
283 registry.register(plugin.clone()).unwrap();
284
285 assert!(registry.is_registered("test-delion"));
286 assert_eq!(registry.len(), 1);
287
288 let retrieved = registry.get("test-delion").unwrap();
289 assert_eq!(retrieved.name(), "test-delion");
290 assert_eq!(retrieved.version().to_string(), "1.0.0");
291 }
292
293 #[test]
294 fn test_capability_query() {
295 let registry = PluginRegistry::new();
296 let plugin = Arc::new(TestPlugin::new());
297
298 registry.register(plugin).unwrap();
299 registry
300 .set_state("test-delion", PluginState::Enabled)
301 .unwrap();
302
303 let providers =
304 registry.get_capability_providers(&Capability::Core(PluginCapability::Middleware));
305 assert_eq!(providers.len(), 1);
306 }
307}