Skip to main content

reinhardt_dentdelion/
capability.rs

1//! Plugin capability system.
2//!
3//! Capabilities define what functionality a plugin can provide or consume.
4//! Plugins must declare their capabilities, and only declared capabilities
5//! are activated at runtime.
6//!
7//! # Design
8//!
9//! The capability system uses a two-tier approach:
10//! - [`PluginCapability`]: Core framework capabilities (compile-time optimized)
11//! - [`Capability`]: Wrapper supporting both core and custom capabilities
12//!
13//! # Example
14//!
15//! ```rust
16//! use reinhardt_dentdelion::capability::{Capability, PluginCapability};
17//!
18//! // Core capabilities
19//! let middleware = Capability::Core(PluginCapability::Middleware);
20//! let models = Capability::Core(PluginCapability::Models);
21//!
22//! // Custom capability
23//! let custom = Capability::Custom("my-custom-feature".to_string());
24//! ```
25
26use serde::{Deserialize, Serialize};
27use std::fmt;
28
29/// Core plugin capabilities defined by the framework.
30///
31/// These are the standard capabilities that plugins can provide.
32/// Using an enum for core capabilities provides:
33/// - Compile-time type safety
34/// - Efficient storage and comparison
35/// - Pattern matching support
36///
37/// The `#[non_exhaustive]` attribute allows adding new capabilities
38/// in future versions without breaking existing code.
39#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
40#[serde(rename_all = "snake_case")]
41#[non_exhaustive]
42pub enum PluginCapability {
43	/// Provides HTTP middleware components.
44	///
45	/// Plugins with this capability can intercept and modify
46	/// HTTP requests and responses.
47	Middleware,
48
49	/// Provides database models and migrations.
50	///
51	/// Plugins with this capability can define database tables
52	/// and manage their schema through migrations.
53	///
54	/// **Note**: Only available for static plugins (Rust crates).
55	/// WASM plugins cannot provide models due to compile-time requirements.
56	Models,
57
58	/// Provides CLI management commands.
59	///
60	/// Plugins with this capability can add commands to
61	/// `reinhardt-admin-cli`.
62	Commands,
63
64	/// Provides REST API ViewSets.
65	///
66	/// Plugins with this capability can register ViewSets
67	/// that handle REST API endpoints.
68	ViewSets,
69
70	/// Provides custom signals.
71	///
72	/// Plugins with this capability can define and emit
73	/// custom signals, or subscribe to existing signals.
74	Signals,
75
76	/// Provides DI services.
77	///
78	/// Plugins with this capability can register services
79	/// in the dependency injection container.
80	Services,
81
82	/// Provides authentication backends.
83	///
84	/// Plugins with this capability can implement authentication
85	/// mechanisms (JWT, OAuth, etc.).
86	Auth,
87
88	/// Provides template engines or filters.
89	///
90	/// Plugins with this capability can extend the template
91	/// rendering system.
92	Templates,
93
94	/// Provides static file handling.
95	///
96	/// Plugins with this capability can serve or process
97	/// static files.
98	StaticFiles,
99
100	/// Provides URL routing.
101	///
102	/// Plugins with this capability can register custom
103	/// routes and route handlers.
104	Routing,
105
106	/// Provides signal receivers.
107	///
108	/// Plugins with this capability can listen to and
109	/// respond to signals from other parts of the system.
110	SignalReceivers,
111
112	/// Provides HTTP handlers/views.
113	///
114	/// Plugins with this capability can handle HTTP requests
115	/// directly (not through ViewSets).
116	Handlers,
117
118	/// Provides network/HTTP access.
119	///
120	/// Plugins with this capability can make external HTTP requests
121	/// via the host API.
122	NetworkAccess,
123
124	/// Provides database access.
125	///
126	/// Plugins with this capability can execute SQL queries and
127	/// statements via the host API.
128	DatabaseAccess,
129
130	/// Provides static site generation.
131	///
132	/// Plugins with this capability can generate static HTML pages
133	/// from routes and components at build time.
134	///
135	/// **Note**: Only available for static plugins (Rust crates).
136	/// WASM plugins cannot provide SSG due to compile-time requirements
137	/// for route introspection and component rendering.
138	StaticSiteGeneration,
139
140	// ==========================================================================
141	// Frontend Integration Capabilities (for react-delion, vue-delion, etc.)
142	// ==========================================================================
143	/// Provides server-side rendering (SSR) for frontend frameworks.
144	///
145	/// Plugins with this capability can render React/Vue components
146	/// to HTML on the server using deno_core (V8).
147	///
148	/// **Note**: Requires TypeScript runtime (`ts` feature).
149	FrontendSsr,
150
151	/// Provides client-side hydration support.
152	///
153	/// Plugins with this capability can generate hydration scripts
154	/// and manage client-side state restoration after SSR.
155	FrontendHydration,
156
157	/// Provides TypeScript/JavaScript runtime execution.
158	///
159	/// Plugins with this capability can execute TypeScript code
160	/// directly via deno_core (V8 engine) without transpilation.
161	///
162	/// **Note**: Requires `ts` feature.
163	TypeScriptRuntime,
164
165	/// Provides build tool integration.
166	///
167	/// Plugins with this capability can integrate with frontend
168	/// build tools (Vite, Rspack, webpack, Farm, etc.).
169	BuildToolIntegration,
170
171	/// Provides hot module replacement (HMR) support.
172	///
173	/// Plugins with this capability can dynamically reload
174	/// modules during development without full page refresh.
175	HotModuleReplacement,
176}
177
178impl PluginCapability {
179	/// Returns all core capabilities.
180	pub fn all() -> &'static [Self] {
181		&[
182			Self::Middleware,
183			Self::Models,
184			Self::Commands,
185			Self::ViewSets,
186			Self::Signals,
187			Self::Services,
188			Self::Auth,
189			Self::Templates,
190			Self::StaticFiles,
191			Self::Routing,
192			Self::SignalReceivers,
193			Self::Handlers,
194			Self::NetworkAccess,
195			Self::DatabaseAccess,
196			Self::StaticSiteGeneration,
197			Self::FrontendSsr,
198			Self::FrontendHydration,
199			Self::TypeScriptRuntime,
200			Self::BuildToolIntegration,
201			Self::HotModuleReplacement,
202		]
203	}
204
205	/// Returns the string identifier for this capability.
206	pub fn as_str(&self) -> &'static str {
207		match self {
208			Self::Middleware => "middleware",
209			Self::Models => "models",
210			Self::Commands => "commands",
211			Self::ViewSets => "viewsets",
212			Self::Signals => "signals",
213			Self::Services => "services",
214			Self::Auth => "auth",
215			Self::Templates => "templates",
216			Self::StaticFiles => "static_files",
217			Self::Routing => "routing",
218			Self::SignalReceivers => "signal_receivers",
219			Self::Handlers => "handlers",
220			Self::NetworkAccess => "network_access",
221			Self::DatabaseAccess => "database_access",
222			Self::StaticSiteGeneration => "static_site_generation",
223			Self::FrontendSsr => "frontend_ssr",
224			Self::FrontendHydration => "frontend_hydration",
225			Self::TypeScriptRuntime => "typescript_runtime",
226			Self::BuildToolIntegration => "build_tool_integration",
227			Self::HotModuleReplacement => "hot_module_replacement",
228		}
229	}
230
231	/// Returns whether this capability is available for WASM plugins.
232	///
233	/// Some capabilities require compile-time integration and are
234	/// therefore not available for dynamic (WASM) plugins.
235	pub fn is_wasm_compatible(&self) -> bool {
236		match self {
237			// Models require compile-time linkme registration
238			Self::Models => false,
239			// SSG requires compile-time route introspection and component rendering
240			Self::StaticSiteGeneration => false,
241			// Frontend SSR/Hydration require TypeScript runtime (deno_core)
242			Self::FrontendSsr | Self::FrontendHydration => false,
243			// TypeScript runtime requires deno_core, not WASM
244			Self::TypeScriptRuntime => false,
245			// HMR requires TypeScript runtime for dynamic module reload
246			Self::HotModuleReplacement => false,
247			// All other capabilities can be provided by WASM plugins
248			_ => true,
249		}
250	}
251
252	/// Returns whether this capability requires TypeScript runtime.
253	pub fn requires_ts_runtime(&self) -> bool {
254		matches!(
255			self,
256			Self::FrontendSsr
257				| Self::FrontendHydration
258				| Self::TypeScriptRuntime
259				| Self::HotModuleReplacement
260		)
261	}
262}
263
264impl fmt::Display for PluginCapability {
265	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266		write!(f, "{}", self.as_str())
267	}
268}
269
270impl std::str::FromStr for PluginCapability {
271	type Err = String;
272
273	fn from_str(s: &str) -> Result<Self, Self::Err> {
274		match s.to_lowercase().as_str() {
275			"middleware" => Ok(Self::Middleware),
276			"models" => Ok(Self::Models),
277			"commands" => Ok(Self::Commands),
278			"viewsets" => Ok(Self::ViewSets),
279			"signals" => Ok(Self::Signals),
280			"services" => Ok(Self::Services),
281			"auth" => Ok(Self::Auth),
282			"templates" => Ok(Self::Templates),
283			"static_files" | "staticfiles" => Ok(Self::StaticFiles),
284			"routing" => Ok(Self::Routing),
285			"signal_receivers" | "signalreceivers" => Ok(Self::SignalReceivers),
286			"handlers" => Ok(Self::Handlers),
287			"network_access" | "networkaccess" => Ok(Self::NetworkAccess),
288			"database_access" | "databaseaccess" => Ok(Self::DatabaseAccess),
289			"static_site_generation" | "staticsitegeneration" | "ssg" => {
290				Ok(Self::StaticSiteGeneration)
291			}
292			"frontend_ssr" | "frontendssr" | "ssr" => Ok(Self::FrontendSsr),
293			"frontend_hydration" | "frontendhydration" | "hydration" => Ok(Self::FrontendHydration),
294			"typescript_runtime" | "typescriptruntime" | "ts_runtime" | "tsruntime" => {
295				Ok(Self::TypeScriptRuntime)
296			}
297			"build_tool_integration" | "buildtoolintegration" => Ok(Self::BuildToolIntegration),
298			"hot_module_replacement" | "hotmodulereplacment" | "hmr" => {
299				Ok(Self::HotModuleReplacement)
300			}
301			_ => Err(format!("unknown capability: {s}")),
302		}
303	}
304}
305
306/// Extended capability wrapper supporting custom capabilities.
307///
308/// This allows third-party plugins to define custom capabilities
309/// while maintaining efficiency for core capabilities.
310#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
311#[serde(untagged)]
312pub enum Capability {
313	/// Core framework capability.
314	Core(PluginCapability),
315	/// Custom capability defined by third-party plugins.
316	Custom(String),
317}
318
319impl Capability {
320	/// Creates a new core capability.
321	pub fn core(capability: PluginCapability) -> Self {
322		Self::Core(capability)
323	}
324
325	/// Creates a new custom capability.
326	pub fn custom(name: impl Into<String>) -> Self {
327		Self::Custom(name.into())
328	}
329
330	/// Returns the string identifier for this capability.
331	pub fn as_str(&self) -> &str {
332		match self {
333			Self::Core(cap) => cap.as_str(),
334			Self::Custom(name) => name.as_str(),
335		}
336	}
337
338	/// Returns whether this is a core capability.
339	pub fn is_core(&self) -> bool {
340		matches!(self, Self::Core(_))
341	}
342
343	/// Returns whether this capability is available for WASM plugins.
344	pub fn is_wasm_compatible(&self) -> bool {
345		match self {
346			Self::Core(cap) => cap.is_wasm_compatible(),
347			// Custom capabilities are assumed to be WASM-compatible
348			// unless explicitly stated otherwise
349			Self::Custom(_) => true,
350		}
351	}
352}
353
354impl fmt::Display for Capability {
355	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
356		write!(f, "{}", self.as_str())
357	}
358}
359
360impl From<PluginCapability> for Capability {
361	fn from(cap: PluginCapability) -> Self {
362		Self::Core(cap)
363	}
364}
365
366impl std::str::FromStr for Capability {
367	type Err = std::convert::Infallible;
368
369	fn from_str(s: &str) -> Result<Self, Self::Err> {
370		// Try to parse as core capability first
371		if let Ok(core) = s.parse::<PluginCapability>() {
372			Ok(Self::Core(core))
373		} else {
374			// Treat as custom capability
375			Ok(Self::Custom(s.to_string()))
376		}
377	}
378}
379
380// =============================================================================
381// Plugin Tier and Trust Level
382// =============================================================================
383
384/// Plugin resource tier determining runtime limits.
385///
386/// Tiers allow plugins to request different resource allocations based on
387/// their needs. Higher tiers provide more resources but may require
388/// additional verification or trust.
389///
390/// # Example
391///
392/// ```rust
393/// use reinhardt_dentdelion::capability::PluginTier;
394///
395/// let tier = PluginTier::Premium;
396/// let memory_limit = tier.memory_limit_bytes();
397/// println!("Memory limit: {} MB", memory_limit / 1024 / 1024);
398/// ```
399#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
400#[serde(rename_all = "snake_case")]
401pub enum PluginTier {
402	/// Standard tier with default resource limits.
403	///
404	/// - Memory: 128 MB
405	/// - Timeout: 30 seconds
406	/// - Fuel: 100M instructions
407	#[default]
408	Standard,
409
410	/// Premium tier with extended resource limits.
411	///
412	/// - Memory: 512 MB
413	/// - Timeout: 60 seconds
414	/// - Fuel: 500M instructions
415	Premium,
416
417	/// Enterprise tier with maximum resource limits.
418	///
419	/// - Memory: 1 GB
420	/// - Timeout: 120 seconds
421	/// - Fuel: 1B instructions
422	Enterprise,
423}
424
425impl PluginTier {
426	/// Returns all available tiers.
427	pub fn all() -> &'static [Self] {
428		&[Self::Standard, Self::Premium, Self::Enterprise]
429	}
430
431	/// Returns the string identifier for this tier.
432	pub fn as_str(&self) -> &'static str {
433		match self {
434			Self::Standard => "standard",
435			Self::Premium => "premium",
436			Self::Enterprise => "enterprise",
437		}
438	}
439
440	/// Returns the memory limit in bytes for this tier.
441	pub fn memory_limit_bytes(&self) -> usize {
442		match self {
443			Self::Standard => 128 * 1024 * 1024,    // 128 MB
444			Self::Premium => 512 * 1024 * 1024,     // 512 MB
445			Self::Enterprise => 1024 * 1024 * 1024, // 1 GB
446		}
447	}
448
449	/// Returns the execution timeout in seconds for this tier.
450	pub fn timeout_secs(&self) -> u64 {
451		match self {
452			Self::Standard => 30,
453			Self::Premium => 60,
454			Self::Enterprise => 120,
455		}
456	}
457
458	/// Returns the fuel limit (CPU instructions) for this tier.
459	pub fn fuel_limit(&self) -> u64 {
460		match self {
461			Self::Standard => 100_000_000,     // 100M
462			Self::Premium => 500_000_000,      // 500M
463			Self::Enterprise => 1_000_000_000, // 1B
464		}
465	}
466}
467
468impl fmt::Display for PluginTier {
469	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
470		write!(f, "{}", self.as_str())
471	}
472}
473
474impl std::str::FromStr for PluginTier {
475	type Err = String;
476
477	fn from_str(s: &str) -> Result<Self, Self::Err> {
478		match s.to_lowercase().as_str() {
479			"standard" | "std" => Ok(Self::Standard),
480			"premium" | "pro" => Ok(Self::Premium),
481			"enterprise" | "ent" => Ok(Self::Enterprise),
482			_ => Err(format!("unknown plugin tier: {s}")),
483		}
484	}
485}
486
487/// Plugin trust level determining security restrictions.
488///
489/// Trust levels control what capabilities a plugin can access and how
490/// strictly it is sandboxed. Higher trust levels reduce restrictions
491/// but increase potential security risks.
492///
493/// # Security Considerations
494///
495/// - `Untrusted`: Safe for third-party plugins from unknown sources
496/// - `Verified`: For plugins from known sources with signature verification
497/// - `Trusted`: Only for first-party plugins (effectively unsandboxed)
498///
499/// # Example
500///
501/// ```rust
502/// use reinhardt_dentdelion::capability::TrustLevel;
503///
504/// let trust = TrustLevel::Verified;
505/// if trust.allows_network() {
506///     // Plugin can make network requests
507/// }
508/// ```
509#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
510#[serde(rename_all = "snake_case")]
511pub enum TrustLevel {
512	/// Untrusted plugin with strict sandboxing.
513	///
514	/// - No network access (unless explicitly granted via capability)
515	/// - No database access (unless explicitly granted via capability)
516	/// - No filesystem access
517	/// - All operations are strictly sandboxed
518	#[default]
519	Untrusted,
520
521	/// Verified plugin with relaxed restrictions.
522	///
523	/// - Network access allowed (with capability)
524	/// - Database access allowed (with capability)
525	/// - No filesystem access
526	/// - Must pass signature verification
527	Verified,
528
529	/// Fully trusted plugin with minimal restrictions.
530	///
531	/// **Warning**: This level should only be used for first-party plugins.
532	///
533	/// - Full network access
534	/// - Full database access
535	/// - Read-only filesystem access to plugin directory
536	/// - Runs with host-level privileges
537	Trusted,
538}
539
540impl TrustLevel {
541	/// Returns all available trust levels.
542	pub fn all() -> &'static [Self] {
543		&[Self::Untrusted, Self::Verified, Self::Trusted]
544	}
545
546	/// Returns the string identifier for this trust level.
547	pub fn as_str(&self) -> &'static str {
548		match self {
549			Self::Untrusted => "untrusted",
550			Self::Verified => "verified",
551			Self::Trusted => "trusted",
552		}
553	}
554
555	/// Returns whether this trust level allows network access.
556	///
557	/// Note: Even if allowed, the plugin must also have the `NetworkAccess`
558	/// capability to actually make network requests.
559	pub fn allows_network(&self) -> bool {
560		!matches!(self, Self::Untrusted)
561	}
562
563	/// Returns whether this trust level allows database access.
564	///
565	/// Note: Even if allowed, the plugin must also have the `DatabaseAccess`
566	/// capability to actually execute queries.
567	pub fn allows_database(&self) -> bool {
568		!matches!(self, Self::Untrusted)
569	}
570
571	/// Returns whether this trust level allows filesystem access.
572	///
573	/// Only `Trusted` plugins can access the filesystem.
574	pub fn allows_filesystem(&self) -> bool {
575		matches!(self, Self::Trusted)
576	}
577
578	/// Returns whether this trust level requires signature verification.
579	pub fn requires_verification(&self) -> bool {
580		matches!(self, Self::Verified)
581	}
582
583	/// Returns whether this trust level is considered safe for third-party plugins.
584	pub fn is_safe_for_third_party(&self) -> bool {
585		matches!(self, Self::Untrusted | Self::Verified)
586	}
587
588	/// Returns whether this trust level allows arbitrary JavaScript execution.
589	///
590	/// Only `Trusted` plugins can execute arbitrary JavaScript code via `eval_js`,
591	/// as this provides unrestricted access to the JavaScript runtime.
592	pub fn allows_js_execution(&self) -> bool {
593		matches!(self, Self::Trusted)
594	}
595
596	/// Returns whether this trust level allows server-side rendering.
597	///
598	/// `Verified` and `Trusted` plugins can render components via SSR.
599	/// `Untrusted` plugins cannot use SSR as it involves JavaScript execution.
600	pub fn allows_ssr(&self) -> bool {
601		!matches!(self, Self::Untrusted)
602	}
603}
604
605impl fmt::Display for TrustLevel {
606	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
607		write!(f, "{}", self.as_str())
608	}
609}
610
611impl std::str::FromStr for TrustLevel {
612	type Err = String;
613
614	fn from_str(s: &str) -> Result<Self, Self::Err> {
615		match s.to_lowercase().as_str() {
616			"untrusted" | "sandbox" | "sandboxed" => Ok(Self::Untrusted),
617			"verified" | "signed" => Ok(Self::Verified),
618			"trusted" | "full" => Ok(Self::Trusted),
619			_ => Err(format!("unknown trust level: {s}")),
620		}
621	}
622}
623
624#[cfg(test)]
625mod tests {
626	use super::*;
627
628	#[test]
629	fn test_plugin_capability_display() {
630		assert_eq!(PluginCapability::Middleware.to_string(), "middleware");
631		assert_eq!(PluginCapability::Models.to_string(), "models");
632		assert_eq!(PluginCapability::StaticFiles.to_string(), "static_files");
633	}
634
635	#[test]
636	fn test_plugin_capability_from_str() {
637		assert_eq!(
638			"middleware".parse::<PluginCapability>().unwrap(),
639			PluginCapability::Middleware
640		);
641		assert_eq!(
642			"MODELS".parse::<PluginCapability>().unwrap(),
643			PluginCapability::Models
644		);
645		assert!("unknown".parse::<PluginCapability>().is_err());
646	}
647
648	#[test]
649	fn test_capability_from_str() {
650		assert_eq!(
651			"middleware".parse::<Capability>().unwrap(),
652			Capability::Core(PluginCapability::Middleware)
653		);
654		assert_eq!(
655			"custom-feature".parse::<Capability>().unwrap(),
656			Capability::Custom("custom-feature".to_string())
657		);
658	}
659
660	#[test]
661	fn test_wasm_compatibility() {
662		assert!(PluginCapability::Middleware.is_wasm_compatible());
663		assert!(PluginCapability::Commands.is_wasm_compatible());
664		assert!(!PluginCapability::Models.is_wasm_compatible());
665		assert!(!PluginCapability::StaticSiteGeneration.is_wasm_compatible());
666	}
667
668	#[test]
669	fn test_static_site_generation_capability() {
670		assert_eq!(
671			PluginCapability::StaticSiteGeneration.to_string(),
672			"static_site_generation"
673		);
674		assert_eq!(
675			"static_site_generation"
676				.parse::<PluginCapability>()
677				.unwrap(),
678			PluginCapability::StaticSiteGeneration
679		);
680		assert_eq!(
681			"ssg".parse::<PluginCapability>().unwrap(),
682			PluginCapability::StaticSiteGeneration
683		);
684	}
685
686	#[test]
687	fn test_frontend_capabilities() {
688		// FrontendSsr
689		assert_eq!(PluginCapability::FrontendSsr.to_string(), "frontend_ssr");
690		assert_eq!(
691			"frontend_ssr".parse::<PluginCapability>().unwrap(),
692			PluginCapability::FrontendSsr
693		);
694		assert_eq!(
695			"ssr".parse::<PluginCapability>().unwrap(),
696			PluginCapability::FrontendSsr
697		);
698
699		// FrontendHydration
700		assert_eq!(
701			PluginCapability::FrontendHydration.to_string(),
702			"frontend_hydration"
703		);
704		assert_eq!(
705			"hydration".parse::<PluginCapability>().unwrap(),
706			PluginCapability::FrontendHydration
707		);
708
709		// TypeScriptRuntime
710		assert_eq!(
711			PluginCapability::TypeScriptRuntime.to_string(),
712			"typescript_runtime"
713		);
714		assert_eq!(
715			"ts_runtime".parse::<PluginCapability>().unwrap(),
716			PluginCapability::TypeScriptRuntime
717		);
718
719		// BuildToolIntegration
720		assert_eq!(
721			PluginCapability::BuildToolIntegration.to_string(),
722			"build_tool_integration"
723		);
724
725		// HotModuleReplacement
726		assert_eq!(
727			PluginCapability::HotModuleReplacement.to_string(),
728			"hot_module_replacement"
729		);
730		assert_eq!(
731			"hmr".parse::<PluginCapability>().unwrap(),
732			PluginCapability::HotModuleReplacement
733		);
734	}
735
736	#[test]
737	fn test_frontend_capabilities_wasm_compatibility() {
738		// Frontend capabilities require TypeScript runtime, not WASM
739		assert!(!PluginCapability::FrontendSsr.is_wasm_compatible());
740		assert!(!PluginCapability::FrontendHydration.is_wasm_compatible());
741		assert!(!PluginCapability::TypeScriptRuntime.is_wasm_compatible());
742		assert!(!PluginCapability::HotModuleReplacement.is_wasm_compatible());
743
744		// BuildToolIntegration can be used with WASM
745		assert!(PluginCapability::BuildToolIntegration.is_wasm_compatible());
746	}
747
748	#[test]
749	fn test_requires_ts_runtime() {
750		assert!(PluginCapability::FrontendSsr.requires_ts_runtime());
751		assert!(PluginCapability::FrontendHydration.requires_ts_runtime());
752		assert!(PluginCapability::TypeScriptRuntime.requires_ts_runtime());
753		assert!(PluginCapability::HotModuleReplacement.requires_ts_runtime());
754
755		// Non-frontend capabilities don't require TS runtime
756		assert!(!PluginCapability::Middleware.requires_ts_runtime());
757		assert!(!PluginCapability::BuildToolIntegration.requires_ts_runtime());
758	}
759
760	// =========================================================================
761	// PluginTier Tests
762	// =========================================================================
763
764	#[test]
765	fn test_plugin_tier_default() {
766		assert_eq!(PluginTier::default(), PluginTier::Standard);
767	}
768
769	#[test]
770	fn test_plugin_tier_display() {
771		assert_eq!(PluginTier::Standard.to_string(), "standard");
772		assert_eq!(PluginTier::Premium.to_string(), "premium");
773		assert_eq!(PluginTier::Enterprise.to_string(), "enterprise");
774	}
775
776	#[test]
777	fn test_plugin_tier_from_str() {
778		assert_eq!(
779			"standard".parse::<PluginTier>().unwrap(),
780			PluginTier::Standard
781		);
782		assert_eq!("std".parse::<PluginTier>().unwrap(), PluginTier::Standard);
783		assert_eq!(
784			"premium".parse::<PluginTier>().unwrap(),
785			PluginTier::Premium
786		);
787		assert_eq!("pro".parse::<PluginTier>().unwrap(), PluginTier::Premium);
788		assert_eq!(
789			"enterprise".parse::<PluginTier>().unwrap(),
790			PluginTier::Enterprise
791		);
792		assert_eq!("ent".parse::<PluginTier>().unwrap(), PluginTier::Enterprise);
793		assert!("unknown".parse::<PluginTier>().is_err());
794	}
795
796	#[test]
797	fn test_plugin_tier_memory_limits() {
798		assert_eq!(PluginTier::Standard.memory_limit_bytes(), 128 * 1024 * 1024);
799		assert_eq!(PluginTier::Premium.memory_limit_bytes(), 512 * 1024 * 1024);
800		assert_eq!(
801			PluginTier::Enterprise.memory_limit_bytes(),
802			1024 * 1024 * 1024
803		);
804	}
805
806	#[test]
807	fn test_plugin_tier_timeout() {
808		assert_eq!(PluginTier::Standard.timeout_secs(), 30);
809		assert_eq!(PluginTier::Premium.timeout_secs(), 60);
810		assert_eq!(PluginTier::Enterprise.timeout_secs(), 120);
811	}
812
813	#[test]
814	fn test_plugin_tier_fuel_limits() {
815		assert_eq!(PluginTier::Standard.fuel_limit(), 100_000_000);
816		assert_eq!(PluginTier::Premium.fuel_limit(), 500_000_000);
817		assert_eq!(PluginTier::Enterprise.fuel_limit(), 1_000_000_000);
818	}
819
820	#[test]
821	fn test_plugin_tier_all() {
822		let all = PluginTier::all();
823		assert_eq!(all.len(), 3);
824		assert!(all.contains(&PluginTier::Standard));
825		assert!(all.contains(&PluginTier::Premium));
826		assert!(all.contains(&PluginTier::Enterprise));
827	}
828
829	// =========================================================================
830	// TrustLevel Tests
831	// =========================================================================
832
833	#[test]
834	fn test_trust_level_default() {
835		assert_eq!(TrustLevel::default(), TrustLevel::Untrusted);
836	}
837
838	#[test]
839	fn test_trust_level_display() {
840		assert_eq!(TrustLevel::Untrusted.to_string(), "untrusted");
841		assert_eq!(TrustLevel::Verified.to_string(), "verified");
842		assert_eq!(TrustLevel::Trusted.to_string(), "trusted");
843	}
844
845	#[test]
846	fn test_trust_level_from_str() {
847		assert_eq!(
848			"untrusted".parse::<TrustLevel>().unwrap(),
849			TrustLevel::Untrusted
850		);
851		assert_eq!(
852			"sandbox".parse::<TrustLevel>().unwrap(),
853			TrustLevel::Untrusted
854		);
855		assert_eq!(
856			"verified".parse::<TrustLevel>().unwrap(),
857			TrustLevel::Verified
858		);
859		assert_eq!(
860			"signed".parse::<TrustLevel>().unwrap(),
861			TrustLevel::Verified
862		);
863		assert_eq!(
864			"trusted".parse::<TrustLevel>().unwrap(),
865			TrustLevel::Trusted
866		);
867		assert_eq!("full".parse::<TrustLevel>().unwrap(), TrustLevel::Trusted);
868		assert!("unknown".parse::<TrustLevel>().is_err());
869	}
870
871	#[test]
872	fn test_trust_level_network_access() {
873		assert!(!TrustLevel::Untrusted.allows_network());
874		assert!(TrustLevel::Verified.allows_network());
875		assert!(TrustLevel::Trusted.allows_network());
876	}
877
878	#[test]
879	fn test_trust_level_database_access() {
880		assert!(!TrustLevel::Untrusted.allows_database());
881		assert!(TrustLevel::Verified.allows_database());
882		assert!(TrustLevel::Trusted.allows_database());
883	}
884
885	#[test]
886	fn test_trust_level_filesystem_access() {
887		assert!(!TrustLevel::Untrusted.allows_filesystem());
888		assert!(!TrustLevel::Verified.allows_filesystem());
889		assert!(TrustLevel::Trusted.allows_filesystem());
890	}
891
892	#[test]
893	fn test_trust_level_verification_requirement() {
894		assert!(!TrustLevel::Untrusted.requires_verification());
895		assert!(TrustLevel::Verified.requires_verification());
896		assert!(!TrustLevel::Trusted.requires_verification());
897	}
898
899	#[test]
900	fn test_trust_level_third_party_safety() {
901		assert!(TrustLevel::Untrusted.is_safe_for_third_party());
902		assert!(TrustLevel::Verified.is_safe_for_third_party());
903		assert!(!TrustLevel::Trusted.is_safe_for_third_party());
904	}
905
906	#[test]
907	fn test_trust_level_all() {
908		let all = TrustLevel::all();
909		assert_eq!(all.len(), 3);
910		assert!(all.contains(&TrustLevel::Untrusted));
911		assert!(all.contains(&TrustLevel::Verified));
912		assert!(all.contains(&TrustLevel::Trusted));
913	}
914
915	#[test]
916	fn test_trust_level_js_execution() {
917		assert!(!TrustLevel::Untrusted.allows_js_execution());
918		assert!(!TrustLevel::Verified.allows_js_execution());
919		assert!(TrustLevel::Trusted.allows_js_execution());
920	}
921
922	#[test]
923	fn test_trust_level_ssr() {
924		assert!(!TrustLevel::Untrusted.allows_ssr());
925		assert!(TrustLevel::Verified.allows_ssr());
926		assert!(TrustLevel::Trusted.allows_ssr());
927	}
928}