1use crate::{ExecutionBoundary, SemanticType};
2
3pub const EFFECT_CAPABILITY_REGISTRY_VERSION: u32 = 1;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
8pub struct CapabilityId(pub &'static str);
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
12pub struct CapabilityOperationId(pub &'static str);
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub struct StaticCapabilityPath(pub &'static str);
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum CapabilityOperationKind {
21 MemberAssignment,
22 MethodCall,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum CapabilityValueContract {
28 String,
29 SerializableDiagnosticValue,
30}
31
32impl CapabilityValueContract {
33 #[must_use]
34 pub const fn semantic_type(self) -> Option<SemanticType> {
35 match self {
36 Self::String => Some(SemanticType::String),
37 Self::SerializableDiagnosticValue => None,
38 }
39 }
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub enum CapabilityParameters {
45 Fixed(&'static [CapabilityValueContract]),
46 Variadic(CapabilityValueContract),
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51pub enum CapabilityResultPolicy {
52 Void,
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub enum ArgumentSerializationPolicy {
58 None,
59 Structural,
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub struct RuntimeCapabilityLowering(pub &'static str);
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub struct CapabilitySignature {
69 pub parameters: CapabilityParameters,
70 pub result: CapabilityResultPolicy,
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75pub struct CapabilityOperation {
76 pub id: CapabilityOperationId,
77 pub static_path: StaticCapabilityPath,
78 pub kind: CapabilityOperationKind,
79 pub signature: CapabilitySignature,
80 pub boundary: ExecutionBoundary,
81 pub argument_serialization: ArgumentSerializationPolicy,
82 pub runtime_lowering: RuntimeCapabilityLowering,
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq)]
87pub struct CapabilityDefinition {
88 pub id: CapabilityId,
89 pub root: &'static str,
90 pub boundary: ExecutionBoundary,
91 pub operations: &'static [CapabilityOperation],
92 pub provenance: BuiltinCapabilityProvenance,
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97pub enum BuiltinCapabilityProvenance {
98 EffectCapabilityRegistryV1,
99}
100
101const STRING_PARAMETER: &[CapabilityValueContract] = &[CapabilityValueContract::String];
102const STRING_PAIR_PARAMETERS: &[CapabilityValueContract] = &[
103 CapabilityValueContract::String,
104 CapabilityValueContract::String,
105];
106
107const DOCUMENT_OPERATIONS: &[CapabilityOperation] = &[CapabilityOperation {
108 id: CapabilityOperationId("builtin.browser.document.title.assign"),
109 static_path: StaticCapabilityPath("document.title"),
110 kind: CapabilityOperationKind::MemberAssignment,
111 signature: CapabilitySignature {
112 parameters: CapabilityParameters::Fixed(STRING_PARAMETER),
113 result: CapabilityResultPolicy::Void,
114 },
115 boundary: ExecutionBoundary::Client,
116 argument_serialization: ArgumentSerializationPolicy::None,
117 runtime_lowering: RuntimeCapabilityLowering("builtin.browser.document.title.assign"),
118}];
119
120const CONSOLE_OPERATIONS: &[CapabilityOperation] = &[
121 CapabilityOperation {
122 id: CapabilityOperationId("builtin.browser.console.log"),
123 static_path: StaticCapabilityPath("console.log"),
124 kind: CapabilityOperationKind::MethodCall,
125 signature: CapabilitySignature {
126 parameters: CapabilityParameters::Variadic(
127 CapabilityValueContract::SerializableDiagnosticValue,
128 ),
129 result: CapabilityResultPolicy::Void,
130 },
131 boundary: ExecutionBoundary::Client,
132 argument_serialization: ArgumentSerializationPolicy::Structural,
133 runtime_lowering: RuntimeCapabilityLowering("builtin.browser.console.log"),
134 },
135 CapabilityOperation {
136 id: CapabilityOperationId("builtin.browser.console.info"),
137 static_path: StaticCapabilityPath("console.info"),
138 kind: CapabilityOperationKind::MethodCall,
139 signature: CapabilitySignature {
140 parameters: CapabilityParameters::Variadic(
141 CapabilityValueContract::SerializableDiagnosticValue,
142 ),
143 result: CapabilityResultPolicy::Void,
144 },
145 boundary: ExecutionBoundary::Client,
146 argument_serialization: ArgumentSerializationPolicy::Structural,
147 runtime_lowering: RuntimeCapabilityLowering("builtin.browser.console.info"),
148 },
149 CapabilityOperation {
150 id: CapabilityOperationId("builtin.browser.console.warn"),
151 static_path: StaticCapabilityPath("console.warn"),
152 kind: CapabilityOperationKind::MethodCall,
153 signature: CapabilitySignature {
154 parameters: CapabilityParameters::Variadic(
155 CapabilityValueContract::SerializableDiagnosticValue,
156 ),
157 result: CapabilityResultPolicy::Void,
158 },
159 boundary: ExecutionBoundary::Client,
160 argument_serialization: ArgumentSerializationPolicy::Structural,
161 runtime_lowering: RuntimeCapabilityLowering("builtin.browser.console.warn"),
162 },
163 CapabilityOperation {
164 id: CapabilityOperationId("builtin.browser.console.error"),
165 static_path: StaticCapabilityPath("console.error"),
166 kind: CapabilityOperationKind::MethodCall,
167 signature: CapabilitySignature {
168 parameters: CapabilityParameters::Variadic(
169 CapabilityValueContract::SerializableDiagnosticValue,
170 ),
171 result: CapabilityResultPolicy::Void,
172 },
173 boundary: ExecutionBoundary::Client,
174 argument_serialization: ArgumentSerializationPolicy::Structural,
175 runtime_lowering: RuntimeCapabilityLowering("builtin.browser.console.error"),
176 },
177];
178
179const LOCAL_STORAGE_OPERATIONS: &[CapabilityOperation] = &[
180 CapabilityOperation {
181 id: CapabilityOperationId("builtin.browser.local_storage.set_item"),
182 static_path: StaticCapabilityPath("localStorage.setItem"),
183 kind: CapabilityOperationKind::MethodCall,
184 signature: CapabilitySignature {
185 parameters: CapabilityParameters::Fixed(STRING_PAIR_PARAMETERS),
186 result: CapabilityResultPolicy::Void,
187 },
188 boundary: ExecutionBoundary::Client,
189 argument_serialization: ArgumentSerializationPolicy::None,
190 runtime_lowering: RuntimeCapabilityLowering("builtin.browser.local_storage.set_item"),
191 },
192 CapabilityOperation {
193 id: CapabilityOperationId("builtin.browser.local_storage.remove_item"),
194 static_path: StaticCapabilityPath("localStorage.removeItem"),
195 kind: CapabilityOperationKind::MethodCall,
196 signature: CapabilitySignature {
197 parameters: CapabilityParameters::Fixed(STRING_PARAMETER),
198 result: CapabilityResultPolicy::Void,
199 },
200 boundary: ExecutionBoundary::Client,
201 argument_serialization: ArgumentSerializationPolicy::None,
202 runtime_lowering: RuntimeCapabilityLowering("builtin.browser.local_storage.remove_item"),
203 },
204];
205
206const SESSION_STORAGE_OPERATIONS: &[CapabilityOperation] = &[
207 CapabilityOperation {
208 id: CapabilityOperationId("builtin.browser.session_storage.set_item"),
209 static_path: StaticCapabilityPath("sessionStorage.setItem"),
210 kind: CapabilityOperationKind::MethodCall,
211 signature: CapabilitySignature {
212 parameters: CapabilityParameters::Fixed(STRING_PAIR_PARAMETERS),
213 result: CapabilityResultPolicy::Void,
214 },
215 boundary: ExecutionBoundary::Client,
216 argument_serialization: ArgumentSerializationPolicy::None,
217 runtime_lowering: RuntimeCapabilityLowering("builtin.browser.session_storage.set_item"),
218 },
219 CapabilityOperation {
220 id: CapabilityOperationId("builtin.browser.session_storage.remove_item"),
221 static_path: StaticCapabilityPath("sessionStorage.removeItem"),
222 kind: CapabilityOperationKind::MethodCall,
223 signature: CapabilitySignature {
224 parameters: CapabilityParameters::Fixed(STRING_PARAMETER),
225 result: CapabilityResultPolicy::Void,
226 },
227 boundary: ExecutionBoundary::Client,
228 argument_serialization: ArgumentSerializationPolicy::None,
229 runtime_lowering: RuntimeCapabilityLowering("builtin.browser.session_storage.remove_item"),
230 },
231];
232
233const DEFINITIONS: &[CapabilityDefinition] = &[
234 CapabilityDefinition {
235 id: CapabilityId("builtin.browser.document"),
236 root: "document",
237 boundary: ExecutionBoundary::Client,
238 operations: DOCUMENT_OPERATIONS,
239 provenance: BuiltinCapabilityProvenance::EffectCapabilityRegistryV1,
240 },
241 CapabilityDefinition {
242 id: CapabilityId("builtin.browser.console"),
243 root: "console",
244 boundary: ExecutionBoundary::Client,
245 operations: CONSOLE_OPERATIONS,
246 provenance: BuiltinCapabilityProvenance::EffectCapabilityRegistryV1,
247 },
248 CapabilityDefinition {
249 id: CapabilityId("builtin.browser.local_storage"),
250 root: "localStorage",
251 boundary: ExecutionBoundary::Client,
252 operations: LOCAL_STORAGE_OPERATIONS,
253 provenance: BuiltinCapabilityProvenance::EffectCapabilityRegistryV1,
254 },
255 CapabilityDefinition {
256 id: CapabilityId("builtin.browser.session_storage"),
257 root: "sessionStorage",
258 boundary: ExecutionBoundary::Client,
259 operations: SESSION_STORAGE_OPERATIONS,
260 provenance: BuiltinCapabilityProvenance::EffectCapabilityRegistryV1,
261 },
262];
263
264#[derive(Debug, Clone, Copy, PartialEq, Eq)]
266pub struct EffectCapabilityRegistry;
267
268impl EffectCapabilityRegistry {
269 #[must_use]
270 pub const fn version(self) -> u32 {
271 EFFECT_CAPABILITY_REGISTRY_VERSION
272 }
273
274 #[must_use]
275 pub const fn definitions(self) -> &'static [CapabilityDefinition] {
276 DEFINITIONS
277 }
278
279 #[must_use]
281 pub fn operation(self, id: CapabilityOperationId) -> Option<&'static CapabilityOperation> {
282 self.definitions()
283 .iter()
284 .flat_map(|definition| definition.operations)
285 .find(|operation| operation.id == id)
286 }
287
288 #[must_use]
289 pub fn operation_at(
290 self,
291 path: &str,
292 kind: CapabilityOperationKind,
293 ) -> Option<&'static CapabilityOperation> {
294 self.definitions()
295 .iter()
296 .flat_map(|definition| definition.operations)
297 .find(|operation| operation.kind == kind && operation.static_path.0 == path)
298 }
299}
300
301pub const EFFECT_CAPABILITY_REGISTRY: EffectCapabilityRegistry = EffectCapabilityRegistry;
303
304#[cfg(test)]
305mod tests {
306 use super::{
307 CapabilityOperationKind, EFFECT_CAPABILITY_REGISTRY, EFFECT_CAPABILITY_REGISTRY_VERSION,
308 };
309
310 #[test]
311 fn registry_v1_exposes_only_the_initial_exact_static_operations() {
312 let registry = EFFECT_CAPABILITY_REGISTRY;
313 assert_eq!(registry.version(), EFFECT_CAPABILITY_REGISTRY_VERSION);
314 assert_eq!(registry.definitions().len(), 4);
315 assert_eq!(
316 registry
317 .operation_at("document.title", CapabilityOperationKind::MemberAssignment)
318 .expect("document title operation")
319 .id
320 .0,
321 "builtin.browser.document.title.assign"
322 );
323 assert!(registry
324 .operation_at("window.console.log", CapabilityOperationKind::MethodCall)
325 .is_none());
326 assert!(registry
327 .operation_at("analytics.track", CapabilityOperationKind::MethodCall)
328 .is_none());
329 }
330}