Browse Source

feat(component): add cryptographic context preservation for actions

Introduce CryptographyProvider to enable component state serialization
and encryption for action requests. Component instances can now preserve
their state across HTTP requests via encrypted context blobs in URLs.

Changes include:
- CryptographyProvider with libsodium for secure context handling
- spry-context tags to declare properties for context preservation
- ComponentEndpoint restoration of component state from context
- DemoHostComponent refactored for dynamic demo creation by name
- NavSidebarComponent simplified to always-visible navigation
- Enhanced demo/code block CSS styling
- Removed unused VAPI bindings (libbrotlienc, libmicrohttpd, libzstd)
Billy Barrow 6 days ago
parent
commit
29259586e1

+ 31 - 8
demo/Components/DemoHostComponent.vala

@@ -1,4 +1,6 @@
 using Spry;
 using Spry;
+using Inversion;
+using Invercargill.DataStructures;
 
 
 /**
 /**
  * DemoHostComponent - A component that hosts demo implementations with source/demo toggle
  * DemoHostComponent - A component that hosts demo implementations with source/demo toggle
@@ -8,11 +10,16 @@ using Spry;
  * 
  * 
  * Usage:
  * Usage:
  *   var host = factory.create<DemoHostComponent>();
  *   var host = factory.create<DemoHostComponent>();
- *   host.source_file = "examples/CounterComponent.vala";
- *   host.set_outlet_child("demo-outlet", demo_component);
+ *   host.demo_component_name = "SimpleCounterDemo";
+ *   host.source_file = "demo/DemoComponents/SimpleCounterDemo.vala";
  */
  */
 public class DemoHostComponent : Component {
 public class DemoHostComponent : Component {
     
     
+    private ComponentFactory factory = inject<ComponentFactory>();
+    
+    /// Name of the demo component to create and display
+    public string demo_component_name { get; set; }
+    
     /// Path to the source file to display (relative to project root)
     /// Path to the source file to display (relative to project root)
     public string source_file { get; set; default = ""; }
     public string source_file { get; set; default = ""; }
     
     
@@ -24,7 +31,9 @@ public class DemoHostComponent : Component {
     
     
     public override string markup { get {
     public override string markup { get {
         return """
         return """
-        <div class="demo-host" sid="host">
+        <spry-context property="demo_component_name"/>
+        <spry-context property="source_file"/>
+        <div class="demo-host" sid="host" hx-swap="outerHTML">
             <div class="demo-header">
             <div class="demo-header">
                 <span class="demo-title" sid="title">Demo</span>
                 <span class="demo-title" sid="title">Demo</span>
                 <div class="demo-toggle-group">
                 <div class="demo-toggle-group">
@@ -35,18 +44,31 @@ public class DemoHostComponent : Component {
                 </div>
                 </div>
             </div>
             </div>
             <div class="demo-content" sid="content">
             <div class="demo-content" sid="content">
-                <div spry-if="!this.showing_source">
-                    <spry-outlet sid="demo-outlet"/>
-                </div>
                 <div spry-if="this.showing_source">
                 <div spry-if="this.showing_source">
                     <pre class="demo-source"><code sid="source-code"></code></pre>
                     <pre class="demo-source"><code sid="source-code"></code></pre>
                 </div>
                 </div>
+                <div spry-else>
+                    <spry-outlet sid="demo-outlet"/>
+                </div>
             </div>
             </div>
         </div>
         </div>
         """;
         """;
     }}
     }}
     
     
     public override async void prepare() throws Error {
     public override async void prepare() throws Error {
+        // Update the title with the demo component name
+        if (demo_component_name != null) {
+            this["title"].text_content = demo_component_name;
+        }
+        
+        // Create the demo component and add it to the outlet when showing demo
+        if (!showing_source && source_file != null) {
+            var demo = factory.create_by_name(demo_component_name);
+            var series = new Series<Renderable>();
+            series.add(demo);
+            set_outlet_children("demo-outlet", series);
+        }
+        
         // Update the source code display if we're showing source
         // Update the source code display if we're showing source
         if (showing_source && _source_content != null) {
         if (showing_source && _source_content != null) {
             this["source-code"].text_content = _source_content;
             this["source-code"].text_content = _source_content;
@@ -72,11 +94,12 @@ public class DemoHostComponent : Component {
      */
      */
     private async string load_source_file() {
     private async string load_source_file() {
         try {
         try {
+            var path = source_file;
             // Try to read from the project root (relative path)
             // Try to read from the project root (relative path)
-            var file = File.new_for_path(source_file);
+            var file = File.new_for_path(path);
             
             
             if (!file.query_exists()) {
             if (!file.query_exists()) {
-                return @"Error: Source file not found: $source_file";
+                return @"Error: Source file not found: $path";
             }
             }
             
             
             uint8[] contents;
             uint8[] contents;

+ 12 - 72
demo/Components/NavSidebarComponent.vala

@@ -1,36 +1,28 @@
 using Spry;
 using Spry;
 
 
 /**
 /**
- * NavSidebarComponent - A tree-based navigation sidebar for the documentation site
+ * NavSidebarComponent - A simple navigation sidebar for the documentation site
  * 
  * 
  * Features:
  * Features:
- * - Collapsible sections for parent items
+ * - All items always visible (no collapsing)
  * - Current page highlighting
  * - Current page highlighting
- * - Automatic expansion of section containing current page
  */
  */
 public class NavSidebarComponent : Component {
 public class NavSidebarComponent : Component {
     
     
     public string current_path { get; set; default = "/"; }
     public string current_path { get; set; default = "/"; }
     
     
-    // Track expanded state for each section
-    private bool _components_expanded = false;
-    private bool _page_components_expanded = false;
-    private bool _static_resources_expanded = false;
-    
     public override string markup { get {
     public override string markup { get {
         return """
         return """
-        <aside class="nav-sidebar" sid="sidebar">
+        <aside class="nav-sidebar" sid="sidebar" hx-disinherit="*">
             <!-- Home - Single link -->
             <!-- Home - Single link -->
             <div class="nav-section">
             <div class="nav-section">
                 <a href="/" class="nav-item" sid="home-link">Home</a>
                 <a href="/" class="nav-item" sid="home-link">Home</a>
             </div>
             </div>
             
             
             <!-- Components Section -->
             <!-- Components Section -->
-            <div class="nav-section" sid="components-section">
-                <button class="nav-section-header" spry-action=":ToggleComponents" spry-target="components-items">
-                    Components
-                </button>
-                <ul class="nav-items" sid="components-items">
+            <div class="nav-section">
+                <h3 class="nav-section-title">Components</h3>
+                <ul class="nav-items">
                     <li><a href="/components/overview" class="nav-item" sid="components-overview">Overview</a></li>
                     <li><a href="/components/overview" class="nav-item" sid="components-overview">Overview</a></li>
                     <li><a href="/components/template-syntax" class="nav-item" sid="components-template-syntax">Template Syntax</a></li>
                     <li><a href="/components/template-syntax" class="nav-item" sid="components-template-syntax">Template Syntax</a></li>
                     <li><a href="/components/actions" class="nav-item" sid="components-actions">Actions</a></li>
                     <li><a href="/components/actions" class="nav-item" sid="components-actions">Actions</a></li>
@@ -40,22 +32,18 @@ public class NavSidebarComponent : Component {
             </div>
             </div>
             
             
             <!-- Page Components Section -->
             <!-- Page Components Section -->
-            <div class="nav-section" sid="page-components-section">
-                <button class="nav-section-header" spry-action=":TogglePageComponents" spry-target="page-components-items">
-                    Page Components
-                </button>
-                <ul class="nav-items" sid="page-components-items">
+            <div class="nav-section">
+                <h3 class="nav-section-title">Page Components</h3>
+                <ul class="nav-items">
                     <li><a href="/page-components/overview" class="nav-item" sid="page-components-overview">Overview</a></li>
                     <li><a href="/page-components/overview" class="nav-item" sid="page-components-overview">Overview</a></li>
                     <li><a href="/page-components/templates" class="nav-item" sid="page-components-templates">Page Templates</a></li>
                     <li><a href="/page-components/templates" class="nav-item" sid="page-components-templates">Page Templates</a></li>
                 </ul>
                 </ul>
             </div>
             </div>
             
             
             <!-- Static Resources Section -->
             <!-- Static Resources Section -->
-            <div class="nav-section" sid="static-resources-section">
-                <button class="nav-section-header" spry-action=":ToggleStaticResources" spry-target="static-resources-items">
-                    Static Resources
-                </button>
-                <ul class="nav-items" sid="static-resources-items">
+            <div class="nav-section">
+                <h3 class="nav-section-title">Static Resources</h3>
+                <ul class="nav-items">
                     <li><a href="/static-resources/overview" class="nav-item" sid="static-resources-overview">Overview</a></li>
                     <li><a href="/static-resources/overview" class="nav-item" sid="static-resources-overview">Overview</a></li>
                     <li><a href="/static-resources/spry-mkssr" class="nav-item" sid="static-resources-spry-mkssr">Using spry-mkssr</a></li>
                     <li><a href="/static-resources/spry-mkssr" class="nav-item" sid="static-resources-spry-mkssr">Using spry-mkssr</a></li>
                 </ul>
                 </ul>
@@ -65,41 +53,10 @@ public class NavSidebarComponent : Component {
     }}
     }}
     
     
     public override async void prepare() throws Error {
     public override async void prepare() throws Error {
-        // Determine which sections should be expanded based on current path
-        _components_expanded = current_path.has_prefix("/components");
-        _page_components_expanded = current_path.has_prefix("/page-components");
-        _static_resources_expanded = current_path.has_prefix("/static-resources");
-        
-        // Apply expanded state to sections
-        update_section_state("components-section", "components-items", _components_expanded);
-        update_section_state("page-components-section", "page-components-items", _page_components_expanded);
-        update_section_state("static-resources-section", "static-resources-items", _static_resources_expanded);
-        
         // Highlight the current page link
         // Highlight the current page link
         highlight_current_link();
         highlight_current_link();
     }
     }
     
     
-    private void update_section_state(string section_sid, string items_sid, bool expanded) {
-        var section = this[section_sid];
-        var items = this[items_sid];
-        
-        if (section != null) {
-            if (expanded) {
-                section.add_class("expanded");
-            } else {
-                section.remove_class("expanded");
-            }
-        }
-        
-        if (items != null) {
-            if (expanded) {
-                items.remove_attribute("hidden");
-            } else {
-                items.set_attribute("hidden", "hidden");
-            }
-        }
-    }
-    
     private void highlight_current_link() {
     private void highlight_current_link() {
         // Get the sid for the current path and add active class
         // Get the sid for the current path and add active class
         string? sid = get_sid_for_path(current_path);
         string? sid = get_sid_for_path(current_path);
@@ -126,21 +83,4 @@ public class NavSidebarComponent : Component {
             default: return null;
             default: return null;
         }
         }
     }
     }
-    
-    public override async void handle_action(string action) throws Error {
-        switch (action) {
-            case "ToggleComponents":
-                _components_expanded = !_components_expanded;
-                update_section_state("components-section", "components-items", _components_expanded);
-                break;
-            case "TogglePageComponents":
-                _page_components_expanded = !_page_components_expanded;
-                update_section_state("page-components-section", "page-components-items", _page_components_expanded);
-                break;
-            case "ToggleStaticResources":
-                _static_resources_expanded = !_static_resources_expanded;
-                update_section_state("static-resources-section", "static-resources-items", _static_resources_expanded);
-                break;
-        }
-    }
 }
 }

+ 8 - 0
demo/DemoComponents/TodoListDemo.vala

@@ -82,6 +82,14 @@ public class TodoItemDemo : Component {
     }}
     }}
     
     
     public override async void prepare() throws Error {
     public override async void prepare() throws Error {
+        // If item_id is not set (fresh instance from action), try to get it from query params
+        if (item_id == 0) {
+            var id_str = http_context.request.query_params.get_any_or_default("id");
+            if (id_str != null) {
+                item_id = int.parse(id_str);
+            }
+        }
+        
         var item = store.get_by_id(item_id);
         var item = store.get_by_id(item_id);
         if (item == null) return;
         if (item == null) return;
         
         

+ 1 - 4
demo/Pages/ComponentsActionsPage.vala

@@ -310,10 +310,7 @@ public class ComponentsActionsPage : PageComponent {
         
         
         // Set up the counter demo
         // Set up the counter demo
         var demo = get_component_child<DemoHostComponent>("counter-demo");
         var demo = get_component_child<DemoHostComponent>("counter-demo");
+        demo.demo_component_name = "SimpleCounterDemo";
         demo.source_file = "demo/DemoComponents/SimpleCounterDemo.vala";
         demo.source_file = "demo/DemoComponents/SimpleCounterDemo.vala";
-        
-        // Create and set the actual demo component
-        var counter_demo = factory.create<SimpleCounterDemo>();
-        demo.set_outlet_child("demo-outlet", counter_demo);
     }
     }
 }
 }

+ 1 - 4
demo/Pages/ComponentsContinuationsPage.vala

@@ -260,10 +260,7 @@ public class ComponentsContinuationsPage : PageComponent {
         
         
         // Set up the demo host
         // Set up the demo host
         var demo = get_component_child<DemoHostComponent>("progress-demo");
         var demo = get_component_child<DemoHostComponent>("progress-demo");
+        demo.demo_component_name = "ProgressDemoWithSSE";
         demo.source_file = "demo/DemoComponents/ProgressDemo.vala";
         demo.source_file = "demo/DemoComponents/ProgressDemo.vala";
-        
-        // Create and set the actual demo component
-        var progress_demo = factory.create<ProgressDemoWithSSE>();
-        demo.set_outlet_child("demo-outlet", progress_demo);
     }
     }
 }
 }

+ 1 - 4
demo/Pages/ComponentsOutletsPage.vala

@@ -262,10 +262,7 @@ public class ComponentsOutletsPage : PageComponent {
         
         
         // Set up the demo host
         // Set up the demo host
         var demo = get_component_child<DemoHostComponent>("todo-demo");
         var demo = get_component_child<DemoHostComponent>("todo-demo");
+        demo.demo_component_name = "TodoListDemo";
         demo.source_file = "demo/DemoComponents/TodoListDemo.vala";
         demo.source_file = "demo/DemoComponents/TodoListDemo.vala";
-        
-        // Create and set the actual demo component
-        var todo_demo = factory.create<TodoListDemo>();
-        demo.set_outlet_child("demo-outlet", todo_demo);
     }
     }
 }
 }

+ 132 - 41
demo/Static/docs.css

@@ -206,31 +206,14 @@ p {
   margin-bottom: var(--spacing-md);
   margin-bottom: var(--spacing-md);
 }
 }
 
 
-.nav-section-header {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  width: 100%;
+.nav-section-title {
   padding: var(--spacing-sm) var(--spacing-md);
   padding: var(--spacing-sm) var(--spacing-md);
-  font-family: var(--font-family);
+  margin: 0 0 var(--spacing-xs);
   font-size: var(--font-size-small);
   font-size: var(--font-size-small);
   font-weight: 600;
   font-weight: 600;
   color: var(--color-text);
   color: var(--color-text);
   text-transform: uppercase;
   text-transform: uppercase;
   letter-spacing: 0.05em;
   letter-spacing: 0.05em;
-  cursor: pointer;
-  background: none;
-  border: none;
-  border-radius: 4px;
-  transition: background-color var(--transition-fast);
-}
-
-.nav-section-header:hover {
-  background-color: var(--color-hover-bg);
-}
-
-.nav-section.expanded .nav-section-header {
-  background-color: var(--color-hover-bg);
 }
 }
 
 
 .nav-items {
 .nav-items {
@@ -716,16 +699,14 @@ hr {
 
 
 .demo-host {
 .demo-host {
   margin: var(--spacing-xl) 0;
   margin: var(--spacing-xl) 0;
-}
-
-.demo-frame {
-  border: 1px solid var(--color-border);
-  border-radius: 6px;
+  border: 2px solid var(--color-border);
+  border-radius: 8px;
   overflow: hidden;
   overflow: hidden;
   background-color: var(--color-background);
   background-color: var(--color-background);
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
 }
 }
 
 
-.demo-frame-header {
+.demo-header {
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
   justify-content: space-between;
   justify-content: space-between;
@@ -734,63 +715,173 @@ hr {
   border-bottom: 1px solid var(--color-border);
   border-bottom: 1px solid var(--color-border);
 }
 }
 
 
-.demo-frame-title {
+.demo-title {
   font-size: var(--font-size-small);
   font-size: var(--font-size-small);
   font-weight: 600;
   font-weight: 600;
   color: var(--color-text);
   color: var(--color-text);
-  margin: 0;
 }
 }
 
 
-.demo-frame-content {
+.demo-content {
   padding: var(--spacing-lg);
   padding: var(--spacing-lg);
+  background-color: var(--color-background);
+  min-height: 100px;
 }
 }
 
 
 /* Demo toggle buttons */
 /* Demo toggle buttons */
 .demo-toggle-group {
 .demo-toggle-group {
   display: flex;
   display: flex;
-  gap: var(--spacing-sm);
+  gap: 2px;
+  background-color: var(--color-code-bg);
+  border-radius: 4px;
+  padding: 2px;
 }
 }
 
 
 .demo-toggle-btn {
 .demo-toggle-btn {
-  padding: var(--spacing-xs) var(--spacing-sm);
+  padding: var(--spacing-xs) var(--spacing-md);
   font-family: var(--font-family);
   font-family: var(--font-family);
   font-size: var(--font-size-smaller);
   font-size: var(--font-size-smaller);
   font-weight: 500;
   font-weight: 500;
   color: var(--color-text-muted);
   color: var(--color-text-muted);
   background-color: transparent;
   background-color: transparent;
-  border: 1px solid var(--color-border);
-  border-radius: 4px;
+  border: none;
+  border-radius: 3px;
   cursor: pointer;
   cursor: pointer;
   transition: all var(--transition-fast);
   transition: all var(--transition-fast);
 }
 }
 
 
 .demo-toggle-btn:hover {
 .demo-toggle-btn:hover {
   color: var(--color-text);
   color: var(--color-text);
-  background-color: var(--color-hover-bg);
 }
 }
 
 
 .demo-toggle-btn.active {
 .demo-toggle-btn.active {
-  color: var(--color-primary);
-  background-color: var(--color-active-bg);
-  border-color: var(--color-primary);
+  color: var(--color-text);
+  background-color: var(--color-background);
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
 }
 }
 
 
 .demo-toggle-btn:focus {
 .demo-toggle-btn:focus {
   outline: 2px solid var(--color-primary);
   outline: 2px solid var(--color-primary);
-  outline-offset: 2px;
+  outline-offset: 1px;
 }
 }
 
 
 /* Source code view */
 /* Source code view */
 .demo-source {
 .demo-source {
-  margin-top: 0;
+  margin: 0;
+  padding: var(--spacing-md);
+  background-color: var(--color-code-bg);
   border-top: 1px solid var(--color-border);
   border-top: 1px solid var(--color-border);
-  border-radius: 0;
+  overflow-x: auto;
+  font-family: var(--font-family-mono);
+  font-size: var(--font-size-small);
+  line-height: 1.5;
+}
+
+.demo-source code {
+  display: block;
+  padding: 0;
+  background: none;
+  border: none;
+  font-size: inherit;
+  white-space: pre;
+}
+
+/* Legacy demo-frame styles (for backwards compatibility) */
+.demo-frame {
+  border: 1px solid var(--color-border);
+  border-radius: 6px;
+  overflow: hidden;
+  background-color: var(--color-background);
 }
 }
 
 
-.demo-source pre {
+.demo-frame-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: var(--spacing-sm) var(--spacing-md);
+  background-color: var(--color-sidebar-bg);
+  border-bottom: 1px solid var(--color-border);
+}
+
+.demo-frame-title {
+  font-size: var(--font-size-small);
+  font-weight: 600;
+  color: var(--color-text);
+  margin: 0;
+}
+
+.demo-frame-content {
+  padding: var(--spacing-lg);
+}
+
+/* ==========================================================================
+   Code Block Component
+   ========================================================================== */
+
+.code-block {
+  margin: var(--spacing-lg) 0;
+  background-color: var(--color-code-bg);
+  border: 1px solid var(--color-code-border);
+  border-radius: 8px;
+  overflow: hidden;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
+}
+
+.code-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: var(--spacing-sm) var(--spacing-md);
+  background-color: var(--color-sidebar-bg);
+  border-bottom: 1px solid var(--color-border);
+}
+
+.code-dots {
+  display: flex;
+  gap: 6px;
+}
+
+.code-dot {
+  width: 12px;
+  height: 12px;
+  border-radius: 50%;
+}
+
+.code-dot.red {
+  background-color: #ff5f56;
+}
+
+.code-dot.yellow {
+  background-color: #ffbd2e;
+}
+
+.code-dot.green {
+  background-color: #27c93f;
+}
+
+.code-lang {
+  font-size: var(--font-size-smaller);
+  font-weight: 500;
+  color: var(--color-text-muted);
+  text-transform: uppercase;
+  letter-spacing: 0.05em;
+}
+
+.code-block pre {
   margin: 0;
   margin: 0;
+  padding: var(--spacing-md);
+  background: none;
+  border: none;
+  overflow-x: auto;
+}
+
+.code-block pre code {
+  display: block;
+  padding: 0;
+  background: none;
   border: none;
   border: none;
-  border-radius: 0;
+  font-size: var(--font-size-small);
+  line-height: 1.6;
+  white-space: pre;
 }
 }
 
 
 /* ==========================================================================
 /* ==========================================================================

+ 5 - 1
meson.build

@@ -2,6 +2,8 @@ project('spry', ['c', 'vala'],
   version: '0.1',
   version: '0.1',
 )
 )
 
 
+vapi_dir = join_paths(meson.current_source_dir(), 'vapi')
+
 # Dependencies
 # Dependencies
 glib_dep = dependency('glib-2.0')
 glib_dep = dependency('glib-2.0')
 gobject_dep = dependency('gobject-2.0')
 gobject_dep = dependency('gobject-2.0')
@@ -12,9 +14,11 @@ astralis_dep = dependency('astralis-0.1')
 json_glib_dep = dependency('json-glib-1.0')
 json_glib_dep = dependency('json-glib-1.0')
 invercargill_json_dep = dependency('invercargill-json')
 invercargill_json_dep = dependency('invercargill-json')
 libxml_dep = dependency('libxml-2.0')
 libxml_dep = dependency('libxml-2.0')
+sodium_deps = [ meson.get_compiler('vala').find_library('libsodium', dirs: vapi_dir),
+                meson.get_compiler('c').find_library('sodium')]
 
 
 # VAPI Directory
 # VAPI Directory
-add_project_arguments(['--vapidir', join_paths(meson.current_source_dir(), 'vapi')], language: 'vala')
+add_project_arguments(['--vapidir', vapi_dir], language: 'vala')
 
 
 subdir('src')
 subdir('src')
 subdir('examples')
 subdir('examples')

+ 53 - 9
src/Component.vala

@@ -10,16 +10,16 @@ namespace Spry {
         INVALID_TYPE,
         INVALID_TYPE,
         ELEMENT_NOT_FOUND,
         ELEMENT_NOT_FOUND,
         TYPE_NOT_FOUND,
         TYPE_NOT_FOUND,
-        PROPERTY_NOT_FOUND,
         CONFLICTING_ATTRIBUTES,
         CONFLICTING_ATTRIBUTES,
-        INVALID_TEMPLATE;
+        INVALID_TEMPLATE,
+        INVALID_CONTEXT;
     }
     }
 
 
     public abstract class Component : Object, Renderable {
     public abstract class Component : Object, Renderable {
         
         
         private static Dictionary<Type, ComponentTemplate> templates;
         private static Dictionary<Type, ComponentTemplate> templates;
         private static Mutex templates_lock = Mutex();
         private static Mutex templates_lock = Mutex();
-        private string instance_id = Uuid.string_random();
+        public string instance_id { get; internal set; default = Uuid.string_random(); }
         
         
         public abstract string markup { get; }
         public abstract string markup { get; }
         public virtual StatusCode get_status() {
         public virtual StatusCode get_status() {
@@ -44,9 +44,11 @@ namespace Spry {
         private PathProvider _path_provider = inject<PathProvider>();
         private PathProvider _path_provider = inject<PathProvider>();
         private ContinuationProvider _continuation_provider = inject<ContinuationProvider>();
         private ContinuationProvider _continuation_provider = inject<ContinuationProvider>();
         private ComponentFactory _component_factory = inject<ComponentFactory>();
         private ComponentFactory _component_factory = inject<ComponentFactory>();
+        private CryptographyProvider _cryptography_provider = inject<CryptographyProvider>();
         private Catalogue<string, Renderable> _children = new Catalogue<string, Renderable>();
         private Catalogue<string, Renderable> _children = new Catalogue<string, Renderable>();
         private Dictionary<string, Component> _child_components = new Dictionary<string, Component>();
         private Dictionary<string, Component> _child_components = new Dictionary<string, Component>();
         private HashSet<Component> _global_sources = new HashSet<Component>();
         private HashSet<Component> _global_sources = new HashSet<Component>();
+        private HashSet<string> _context_properties = new HashSet<string>();
         private MarkupDocument _instance;
         private MarkupDocument _instance;
         private bool _prepare_once_called;
         private bool _prepare_once_called;
 
 
@@ -226,6 +228,7 @@ namespace Spry {
             yield transform_expression_attributes(doc); // Evaluares *-expr attributes
             yield transform_expression_attributes(doc); // Evaluares *-expr attributes
             yield transform_outlets(doc);
             yield transform_outlets(doc);
             yield transform_components(doc);
             yield transform_components(doc);
+            transform_context_nodes(doc);
             transform_action_nodes(doc);
             transform_action_nodes(doc);
             transform_target_nodes(doc);
             transform_target_nodes(doc);
             transform_global_nodes(doc);
             transform_global_nodes(doc);
@@ -267,7 +270,27 @@ namespace Spry {
                 var component_action = action[1];
                 var component_action = action[1];
 
 
                 node.remove_attribute("spry-action");
                 node.remove_attribute("spry-action");
-                node.set_attribute("hx-get", _path_provider.get_action_path(component_name, component_action));
+
+                if(component_name == this.get_type().name() && _context_properties.length > 0) {
+                    var data = new PropertyDictionary();
+                    var root = new PropertyDictionary();
+                    root["this"] = new NativeElement<Component>(this);
+                    var evaluation_context = new EvaluationContext(root);
+                    foreach(var prop_name in _context_properties) {
+                        data[prop_name] = ExpressionParser.parse(@"this.$(prop_name)").evaluate(evaluation_context);
+                    }
+                    var context = new ComponentContext() {
+                        type_name = this.get_type().name(),
+                        timestamp = new DateTime.now_utc(),
+                        instance_id = instance_id,
+                        data = data
+                    };
+                    var context_blob = _cryptography_provider.author_component_context_blob(context);
+                    node.set_attribute("hx-get", _path_provider.get_action_path_with_context(component_name, component_action, context_blob));
+                }
+                else {
+                    node.set_attribute("hx-get", _path_provider.get_action_path(component_name, component_action));
+                }
             }
             }
         }
         }
 
 
@@ -353,8 +376,9 @@ namespace Spry {
                 
                 
                 MarkupNode parent = node;
                 MarkupNode parent = node;
                 while((parent = parent.parent) != null) {
                 while((parent = parent.parent) != null) {
-                    if(parent.has_attribute("spry-continuation"));
-                    break;
+                    if(parent.has_attribute("spry-continuation")) {
+                        break;
+                    }
                 }
                 }
                 if(parent == null) {
                 if(parent == null) {
                     throw new ComponentError.INVALID_TEMPLATE("A tag with a spry-dynamic attribute must be the child of a tag with a spry-continuation attribute");
                     throw new ComponentError.INVALID_TEMPLATE("A tag with a spry-dynamic attribute must be the child of a tag with a spry-continuation attribute");
@@ -392,21 +416,24 @@ namespace Spry {
             MarkupNode node;
             MarkupNode node;
             // Select one by one, so we don't have problems with nesting
             // Select one by one, so we don't have problems with nesting
             while((node = doc.select_one("//*[@spry-if or @spry-else-if or @spry-else]")) != null) {
             while((node = doc.select_one("//*[@spry-if or @spry-else-if or @spry-else]")) != null) {
+                print(@"node $(node.tag_name)\n");
                 var expression_string = node.get_attribute("spry-if") ?? node.get_attribute("spry-else-if");
                 var expression_string = node.get_attribute("spry-if") ?? node.get_attribute("spry-else-if");
                 node.remove_attribute("spry-if");
                 node.remove_attribute("spry-if");
                 node.remove_attribute("spry-else-if");
                 node.remove_attribute("spry-else-if");
                 node.remove_attribute("spry-else");
                 node.remove_attribute("spry-else");
                 if(expression_string == null) {
                 if(expression_string == null) {
+                    print("null\n");
                     // else case
                     // else case
                     continue;
                     continue;
                 }
                 }
 
 
                 var result = evaluate_if_expression(evaluation_context, expression_string, node);
                 var result = evaluate_if_expression(evaluation_context, expression_string, node);
+                print(@"Result $(result)\n");
                 if(result) {
                 if(result) {
                     // Hide any chained nodes
                     // Hide any chained nodes
                     MarkupNode chained_node;
                     MarkupNode chained_node;
                     while((chained_node = node.next_element_sibling) != null) {
                     while((chained_node = node.next_element_sibling) != null) {
-                        if(chained_node.get_attribute("spry-else") != null || chained_node.get_attribute("spry-else-if") != null) {
+                        if(chained_node.has_attribute("spry-else") || chained_node.get_attribute("spry-else-if") != null) {
                             chained_node.remove();
                             chained_node.remove();
                         }
                         }
                         else {
                         else {
@@ -415,6 +442,10 @@ namespace Spry {
                         }
                         }
                     }
                     }
                 }
                 }
+                else {
+                    // Mark this node for removal when condition is false
+                    node.remove();
+                }
             }
             }
         }
         }
 
 
@@ -423,8 +454,8 @@ namespace Spry {
             var result = expression.evaluate(context);
             var result = expression.evaluate(context);
 
 
             bool boolean_result;
             bool boolean_result;
-            if(result.is<bool>()) {
-                boolean_result = result.as<bool>();
+            if(result.is<bool?>()) {
+                boolean_result = result.as<bool?>();
             }
             }
             else if(result.is<int>()) {
             else if(result.is<int>()) {
                 boolean_result = result.as<int>() != 0;
                 boolean_result = result.as<int>() != 0;
@@ -473,6 +504,19 @@ namespace Spry {
             }
             }
         }
         }
 
 
+        private void transform_context_nodes(MarkupDocument doc) throws Error {
+
+            var nodes = doc.select("//spry-context"); // Can't check for suffixes with xpath so iterate all nodes with attributes
+            foreach (var node in nodes) {
+                var property_name = node.get_attribute("property");
+                if(property_name == null) {
+                    throw new ComponentError.INVALID_TEMPLATE("Tag spry-context must have a property attribute");
+                }
+                _context_properties.add(property_name);
+                node.remove();
+            }
+        }
+
         private async void transform_expression_attributes(MarkupDocument doc, EvaluationContext? context = null) throws Error {
         private async void transform_expression_attributes(MarkupDocument doc, EvaluationContext? context = null) throws Error {
             var nodes = doc.select("//*[@*]"); // Can't check for suffixes with xpath so iterate all nodes with attributes
             var nodes = doc.select("//*[@*]"); // Can't check for suffixes with xpath so iterate all nodes with attributes
             foreach (var node in nodes) {
             foreach (var node in nodes) {

+ 25 - 0
src/ComponentEndpoint.vala

@@ -9,11 +9,13 @@ namespace Spry {
     public class ComponentEndpoint : Object, Endpoint {
     public class ComponentEndpoint : Object, Endpoint {
 
 
         private PathProvider component_uri_provider = inject<PathProvider>();
         private PathProvider component_uri_provider = inject<PathProvider>();
+        private CryptographyProvider cryptograpy_provider = inject<CryptographyProvider>();
         private Scope scope = inject<Scope>();
         private Scope scope = inject<Scope>();
 
 
         public async Astralis.HttpResult handle_request (HttpContext http_context, RouteContext route_context) throws Error {
         public async Astralis.HttpResult handle_request (HttpContext http_context, RouteContext route_context) throws Error {
             var component_id = route_context.mapped_parameters.get_or_default ("component-id");
             var component_id = route_context.mapped_parameters.get_or_default ("component-id");
             var action_name = route_context.mapped_parameters.get_or_default ("action");
             var action_name = route_context.mapped_parameters.get_or_default ("action");
+            var context_blob = route_context.mapped_parameters.get_or_default ("context");
             if(component_id == null) {
             if(component_id == null) {
                 return new HttpStringResult ("Missing component ID", StatusCode.BAD_REQUEST);
                 return new HttpStringResult ("Missing component ID", StatusCode.BAD_REQUEST);
             }
             }
@@ -24,6 +26,29 @@ namespace Spry {
             if(component == null) {
             if(component == null) {
                 return new HttpStringResult ("Invalid component ID", StatusCode.NOT_FOUND);
                 return new HttpStringResult ("Invalid component ID", StatusCode.NOT_FOUND);
             }
             }
+            if(context_blob != null) {
+                ComponentContext context;
+                try {
+                    context = cryptograpy_provider.read_component_context_blob (context_blob);
+                }
+                catch {
+                    return new HttpStringResult ("Invalid component context", StatusCode.BAD_REQUEST);
+                }
+                if(context.type_name != component.get_type().name()) {
+                    return new HttpStringResult ("Context mismatch", StatusCode.BAD_REQUEST);
+                }
+
+                component.instance_id = context.instance_id;
+                
+                foreach(var prop in context.data) {
+                    unowned var component_class = component.get_class ();
+                    var prop_spec = component_class.find_property (prop.key);
+                    if(prop_spec == null) {
+                        return new HttpStringResult (@"Invalid context property '$(prop.key)'", StatusCode.BAD_REQUEST);
+                    }
+                    component.set_property (prop.key, prop.value.to_value (prop_spec.value_type));
+                }
+            }
             
             
             yield component.handle_action(action_name);
             yield component.handle_action(action_name);
             return yield component.to_result();
             return yield component.to_result();

+ 73 - 0
src/CryptographyProvider.vala

@@ -0,0 +1,73 @@
+using Invercargill;
+using Invercargill.Mapping;
+using Invercargill.DataStructures;
+using InvercargillJson;
+namespace Spry {
+
+    public class CryptographyProvider : Object {
+
+        private uint8[] signing_secret_key;
+        private uint8[] signing_public_key;
+        private uint8[] sealing_secret_key;
+        private uint8[] sealing_public_key;
+
+        construct {
+
+            signing_secret_key = new uint8[Sodium.Asymmetric.Signing.SECRET_KEY_BYTES];
+            signing_public_key = new uint8[Sodium.Asymmetric.Signing.PUBLIC_KEY_BYTES];
+            Sodium.Asymmetric.Signing.generate_keypair(signing_public_key, signing_secret_key);
+
+            sealing_secret_key = new uint8[Sodium.Asymmetric.Sealing.SECRET_KEY_BYTES];
+            sealing_public_key = new uint8[Sodium.Asymmetric.Sealing.PUBLIC_KEY_BYTES];
+            Sodium.Asymmetric.Sealing.generate_keypair(sealing_public_key, sealing_secret_key);
+
+        }
+
+        public string author_component_context_blob(ComponentContext context) throws Error {
+
+            var mapper = ComponentContext.get_mapper();
+            var properties = mapper.map_from(context);
+            var json = new JsonElement.from_properties(properties);
+            var blob = json.stringify(false);
+            var signed = Sodium.Asymmetric.Signing.sign(blob.data, signing_secret_key);
+            var @sealed = Sodium.Asymmetric.Sealing.seal(signed, sealing_public_key);
+            return Base64.encode(@sealed).replace("+", "-").replace("/", "_");
+        }
+
+        public ComponentContext read_component_context_blob(string blob) throws Error {
+
+            var mapper = ComponentContext.get_mapper();
+            var signed = Sodium.Asymmetric.Sealing.unseal(Base64.decode(blob.replace("-", "+").replace("_", "/")), sealing_public_key, sealing_secret_key);
+            if(signed == null) {
+                throw new ComponentError.INVALID_CONTEXT("Could not unseal context");
+            }
+            var cleartext = Sodium.Asymmetric.Signing.verify(signed, signing_public_key);
+            if(signed == null) {
+                throw new ComponentError.INVALID_CONTEXT("Invalid context signature");
+            }
+            var json = new JsonElement.from_string(Wrap.byte_array(cleartext).to_raw_string());
+            var context = mapper.materialise(json.as<JsonObject>());
+            return context;
+        }
+    }
+
+    public class ComponentContext {
+
+        public string type_name { get; set; }
+        public string instance_id { get; set; }
+        public DateTime timestamp { get; set; }
+        public Properties data { get; set; }
+
+        public static PropertyMapper<ComponentContext> get_mapper() {
+            return PropertyMapper.build_for<ComponentContext>(cfg => {
+                cfg.map<string>("c", o => o.type_name, (o, v) => o.type_name = v);
+                cfg.map<string>("i", o => o.instance_id, (o, v) => o.instance_id = v);
+                cfg.map<string>("t", o => o.timestamp.format_iso8601(), (o, v) => o.timestamp = new DateTime.from_iso8601(v, new TimeZone.utc()));
+                cfg.map<Properties>("d", o => o.data, (o, v) => o.data = v);
+                cfg.set_constructor(() => new ComponentContext());
+            });
+        }
+
+    }
+
+}

+ 6 - 0
src/PathProvider.vala

@@ -1,4 +1,5 @@
 using Invercargill.DataStructures;
 using Invercargill.DataStructures;
+using Invercargill;
 using Inversion;
 using Inversion;
 
 
 namespace Spry {
 namespace Spry {
@@ -21,6 +22,11 @@ namespace Spry {
             return @"/_spry/com/$(component_id)/$action_name";
             return @"/_spry/com/$(component_id)/$action_name";
         }
         }
 
 
+        public string get_action_path_with_context(string type_name, string action_name, string context_blob) throws Error {
+            var component_id = upsert(type_name);
+            return @"/_spry/com/$(component_id)/$action_name/$context_blob";
+        }
+
         private string upsert(string type_name) throws Error {
         private string upsert(string type_name) throws Error {
             if(name_mapping.has(type_name)) {
             if(name_mapping.has(type_name)) {
                 return name_mapping[type_name];
                 return name_mapping[type_name];

+ 3 - 1
src/Spry.vala

@@ -6,6 +6,7 @@ namespace Spry {
     public class SpryModule : Object, Module {
     public class SpryModule : Object, Module {
         public void register_components (Container container) throws Error {
         public void register_components (Container container) throws Error {
             container.register_singleton<PathProvider>();
             container.register_singleton<PathProvider>();
+            container.register_startup<CryptographyProvider>();
             container.register_scoped<ComponentFactory>();
             container.register_scoped<ComponentFactory>();
 
 
             container.register_startup<ContinuationProvider>()
             container.register_startup<ContinuationProvider>()
@@ -18,7 +19,8 @@ namespace Spry {
 
 
             container.register_scoped<ComponentEndpoint>()
             container.register_scoped<ComponentEndpoint>()
                 .as<Endpoint>()
                 .as<Endpoint>()
-                .with_metadata<EndpointRoute>(new EndpointRoute("/_spry/com/{component-id}/{action}"));
+                .with_metadata<EndpointRoute>(new EndpointRoute("/_spry/com/{component-id}/{action}"))
+                .with_metadata<EndpointRoute>(new EndpointRoute("/_spry/com/{component-id}/{action}/{context}"));
 
 
             container.register_startup<Static.HtmxResource>()
             container.register_startup<Static.HtmxResource>()
                 .as<Spry.StaticResource>();
                 .as<Spry.StaticResource>();

+ 2 - 1
src/meson.build

@@ -10,6 +10,7 @@ sources = files(
     'PathProvider.vala',
     'PathProvider.vala',
     'ContinuationProvider.vala',
     'ContinuationProvider.vala',
     'ContinuationContext.vala',
     'ContinuationContext.vala',
+    'CryptographyProvider.vala',
     'Static/StaticResource.vala',
     'Static/StaticResource.vala',
     'Static/MemoryStaticResource.vala',
     'Static/MemoryStaticResource.vala',
     'Static/FileStaticResource.vala',
     'Static/FileStaticResource.vala',
@@ -23,7 +24,7 @@ sources = files(
 library_version = meson.project_version()
 library_version = meson.project_version()
 libspry = shared_library('spry-@0@'.format(library_version),
 libspry = shared_library('spry-@0@'.format(library_version),
     sources,
     sources,
-    dependencies: [glib_dep, gobject_dep, gio_dep, invercargill_dep, invercargill_json_dep, json_glib_dep, inversion_dep, libxml_dep, astralis_dep],
+    dependencies: [glib_dep, gobject_dep, gio_dep, invercargill_dep, invercargill_json_dep, json_glib_dep, inversion_dep, libxml_dep, astralis_dep, sodium_deps],
     install: true,
     install: true,
     vala_gir: 'spry-@0@.gir'.format(library_version),
     vala_gir: 'spry-@0@.gir'.format(library_version),
     install_dir: [true, true, true, true]
     install_dir: [true, true, true, true]

+ 0 - 135
vapi/libbrotlienc.vapi

@@ -1,135 +0,0 @@
-/* libbrotlienc Vala bindings
- * 
- * Bindings for the Brotli compression library (encoder only)
- * Based on brotli encode.h and types.h
- */
-
-[CCode (cprefix = "BROTLI_", lower_case_cprefix = "brotli_", cheader_filename = "brotli/encode.h,brotli/types.h")]
-namespace Brotli {
-
-    /* Boolean type */
-    [CCode (cname = "BROTLI_BOOL", has_type_id = false, default_value = "BROTLI_FALSE")]
-    public struct Bool : int {
-    }
-    
-    [CCode (cname = "BROTLI_TRUE")]
-    public const Bool TRUE;
-    [CCode (cname = "BROTLI_FALSE")]
-    public const Bool FALSE;
-    
-    [CCode (cname = "TO_BROTLI_BOOL")]
-    public Bool to_bool(int x);
-
-    /* Memory allocation callbacks */
-    [CCode (cname = "brotli_alloc_func", has_target = false)]
-    public delegate void* AllocFunc(void* opaque, size_t size);
-    
-    [CCode (cname = "brotli_free_func", has_target = false)]
-    public delegate void FreeFunc(void* opaque, void* address);
-
-    /* Constants from encode.h */
-    [CCode (cname = "BROTLI_MIN_WINDOW_BITS")]
-    public const int MIN_WINDOW_BITS;
-    [CCode (cname = "BROTLI_MAX_WINDOW_BITS")]
-    public const int MAX_WINDOW_BITS;
-    [CCode (cname = "BROTLI_LARGE_MAX_WINDOW_BITS")]
-    public const int LARGE_MAX_WINDOW_BITS;
-    [CCode (cname = "BROTLI_MIN_INPUT_BLOCK_BITS")]
-    public const int MIN_INPUT_BLOCK_BITS;
-    [CCode (cname = "BROTLI_MAX_INPUT_BLOCK_BITS")]
-    public const int MAX_INPUT_BLOCK_BITS;
-    [CCode (cname = "BROTLI_MIN_QUALITY")]
-    public const int MIN_QUALITY;
-    [CCode (cname = "BROTLI_MAX_QUALITY")]
-    public const int MAX_QUALITY;
-    [CCode (cname = "BROTLI_DEFAULT_QUALITY")]
-    public const int DEFAULT_QUALITY;
-    [CCode (cname = "BROTLI_DEFAULT_WINDOW")]
-    public const int DEFAULT_WINDOW;
-
-    /* Encoder mode */
-    [CCode (cname = "BrotliEncoderMode", has_type_id = false)]
-    public enum EncoderMode {
-        [CCode (cname = "BROTLI_MODE_GENERIC")]
-        GENERIC,
-        [CCode (cname = "BROTLI_MODE_TEXT")]
-        TEXT,
-        [CCode (cname = "BROTLI_MODE_FONT")]
-        FONT
-    }
-
-    /* Encoder operation for streaming */
-    [CCode (cname = "BrotliEncoderOperation", has_type_id = false)]
-    public enum EncoderOperation {
-        [CCode (cname = "BROTLI_OPERATION_PROCESS")]
-        PROCESS,
-        [CCode (cname = "BROTLI_OPERATION_FLUSH")]
-        FLUSH,
-        [CCode (cname = "BROTLI_OPERATION_FINISH")]
-        FINISH,
-        [CCode (cname = "BROTLI_OPERATION_EMIT_METADATA")]
-        EMIT_METADATA
-    }
-
-    /* Encoder parameters */
-    [CCode (cname = "BrotliEncoderParameter", has_type_id = false)]
-    public enum EncoderParameter {
-        [CCode (cname = "BROTLI_PARAM_MODE")]
-        MODE,
-        [CCode (cname = "BROTLI_PARAM_QUALITY")]
-        QUALITY,
-        [CCode (cname = "BROTLI_PARAM_LGWIN")]
-        LGWIN,
-        [CCode (cname = "BROTLI_PARAM_LGBLOCK")]
-        LGBLOCK,
-        [CCode (cname = "BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING")]
-        DISABLE_LITERAL_CONTEXT_MODELING,
-        [CCode (cname = "BROTLI_PARAM_SIZE_HINT")]
-        SIZE_HINT,
-        [CCode (cname = "BROTLI_PARAM_LARGE_WINDOW")]
-        LARGE_WINDOW,
-        [CCode (cname = "BROTLI_PARAM_NPOSTFIX")]
-        NPOSTFIX,
-        [CCode (cname = "BROTLI_PARAM_NDIRECT")]
-        NDIRECT,
-        [CCode (cname = "BROTLI_PARAM_STREAM_OFFSET")]
-        STREAM_OFFSET
-    }
-
-    /* Encoder state - opaque structure */
-    [CCode (cname = "BrotliEncoderState", free_function = "BrotliEncoderDestroyInstance")]
-    [Compact]
-    public class EncoderState {
-        [CCode (cname = "BrotliEncoderCreateInstance")]
-        public EncoderState(AllocFunc? alloc_func, FreeFunc? free_func, void* opaque);
-        
-        [CCode (cname = "BrotliEncoderSetParameter")]
-        public Bool set_parameter(EncoderParameter param, uint32 value);
-        
-        [CCode (cname = "BrotliEncoderCompressStream")]
-        public Bool compress_stream(EncoderOperation op, ref size_t available_in, 
-            ref uint8* next_in, ref size_t available_out, ref uint8* next_out, 
-            out size_t total_out);
-        
-        [CCode (cname = "BrotliEncoderIsFinished")]
-        public Bool is_finished();
-        
-        [CCode (cname = "BrotliEncoderHasMoreOutput")]
-        public Bool has_more_output();
-        
-        [CCode (cname = "BrotliEncoderTakeOutput")]
-        public unowned uint8* take_output(ref size_t size);
-    }
-
-    /* Encoder one-shot functions */
-    [CCode (cname = "BrotliEncoderMaxCompressedSize")]
-    public size_t encoder_max_compressed_size(size_t input_size);
-    
-    [CCode (cname = "BrotliEncoderCompress")]
-    public Bool encoder_compress(int quality, int lgwin, EncoderMode mode, 
-        size_t input_size, uint8* input_buffer, 
-        ref size_t encoded_size, uint8* encoded_buffer);
-    
-    [CCode (cname = "BrotliEncoderVersion")]
-    public uint32 encoder_version();
-}

+ 0 - 159
vapi/libmicrohttpd.vapi

@@ -1,159 +0,0 @@
-[CCode (cheader_filename = "microhttpd.h")]
-namespace MHD {
-    [CCode (cname = "enum MHD_ValueKind", cprefix = "MHD_")]
-    public enum ValueKind {
-        RESPONSE_HEADER_KIND,
-        HEADER_KIND,
-        COOKIE_KIND,
-        POSTDATA_KIND,
-        GET_ARGUMENT_KIND,
-        FOOTER_KIND
-    }
-
-    [CCode (cname = "enum MHD_RequestTerminationCode", cprefix = "MHD_REQUEST_TERMINATED_")]
-    public enum RequestTerminationCode {
-        COMPLETED_OK,
-        WITH_ERROR,
-        TIMEOUT_REACHED,
-        DAEMON_SHUTDOWN,
-        READ_ERROR,
-        CLIENT_ABORT
-    }
-
-    [CCode (cname = "enum MHD_ResponseMemoryMode", cprefix = "MHD_")]
-    public enum ResponseMemoryMode {
-        RESPMEM_PERSISTENT,
-        RESPMEM_MUST_FREE,
-        RESPMEM_MUST_COPY
-    }
-
-    [CCode (cname = "MHD_CONTENT_READER_END_OF_STREAM")]
-    public const ssize_t CONTENT_READER_END_OF_STREAM;
-    [CCode (cname = "MHD_CONTENT_READER_END_WITH_ERROR")]
-    public const ssize_t CONTENT_READER_END_WITH_ERROR;
-
-    [CCode (cname = "MHD_USE_SELECT_INTERNALLY")]
-    public const uint USE_SELECT_INTERNALLY;
-    [CCode (cname = "MHD_USE_THREAD_PER_CONNECTION")]
-    public const uint USE_THREAD_PER_CONNECTION;
-    [CCode (cname = "MHD_USE_DEBUG")]
-    public const uint USE_DEBUG;
-    [CCode (cname = "MHD_ALLOW_SUSPEND_RESUME")]
-    public const uint ALLOW_SUSPEND_RESUME;
-
-    [CCode (cname = "MHD_OPTION_END")]
-    public const int OPTION_END;
-
-    [SimpleType]
-    [CCode (cname = "struct MHD_Connection*")]
-    public struct Connection {}
-
-    [CCode (cname = "int", cprefix = "MHD_")]
-    public enum Result {
-        YES = 1,
-        NO = 0
-    }
-
-    [Compact]
-    [CCode (cname = "struct MHD_Response", free_function = "MHD_destroy_response")]
-    public class Response {
-        [CCode (cname = "MHD_create_response_from_buffer")]
-        public Response.from_buffer (size_t size, [CCode(array_length=false)] uint8[] buffer, ResponseMemoryMode mode);
-
-        [CCode (cname = "MHD_create_response_from_callback")]
-        public Response.from_callback (uint64 size, size_t block_size, ContentReaderCallback crc, void* crc_cls, ContentReaderFreeCallback? crfc);
-
-        [CCode (cname = "MHD_add_response_header")]
-        public int add_header (string header, string content);
-    }
-
-    [Compact]
-    [CCode (cname = "struct MHD_Daemon", free_function = "MHD_stop_daemon")]
-    public class Daemon {
-        [CCode (cname = "MHD_start_daemon")]
-        public static Daemon start (uint flags, uint16 port, 
-            AcceptPolicyCallback? apc, 
-            AccessHandlerCallback dh, 
-            ...);
-    }
-
-    [CCode (instance_pos = 0)]
-    public delegate int AcceptPolicyCallback (void* cls, void* addr, uint addrlen);
-
-    [CCode (instance_pos = 0)]
-    public delegate int AccessHandlerCallback (Connection connection, 
-        string? url, string? method, string? version, 
-        string? upload_data, [CCode(array_length=false)] size_t* upload_data_size, 
-        void** con_cls);
-
-    [CCode (has_target = false)]
-    public delegate ssize_t ContentReaderCallback (void* cls, uint64 pos, char* buf, size_t max);
-
-    [CCode (has_target = false, cname = "MHD_ContentReaderFreeCallback")]
-    public delegate void ContentReaderFreeCallback (void* cls);
-    
-    [CCode (cname = "MHD_queue_response")]
-    public int queue_response (Connection connection, uint status_code, Response response);
-
-    [CCode (cname = "MHD_suspend_connection")]
-    public int suspend_connection (Connection connection);
-
-    [CCode (cname = "MHD_resume_connection")]
-    public int resume_connection (Connection connection);
-
-    [CCode (cname = "MHD_lookup_connection_value")]
-    public unowned string? lookup_connection_value (Connection connection, ValueKind kind, string key);
-
-    [CCode (has_target = false)]
-    public delegate Result KeyValueIterator (void* cls, ValueKind kind, string key, string? value);
-
-    [CCode (cname = "MHD_get_connection_values")]
-    public int get_connection_values (Connection connection, ValueKind kind, KeyValueIterator? iterator, void* iterator_cls = null);
-
-    [CCode (cname = "enum MHD_ConnectionInfoType", cprefix = "MHD_CONNECTION_INFO_")]
-    public enum ConnectionInfoType {
-        CIPHER_ALGO,
-        PROTOCOL,
-        CLIENT_ADDRESS,
-        GNUTLS_SESSION,
-        GNUTLS_CLIENT_CERT,
-        DAEMON,
-        CONNECTION_FD,
-        SOCKET_CONTEXT,
-        CONNECTION_SUSPENDED,
-        CONNECTION_TIMEOUT,
-        REQUEST_HEADER_SIZE,
-        HTTP_STATUS
-    }
-
-    [CCode (cname = "union MHD_ConnectionInfo", has_type_id = false)]
-    public struct ConnectionInfo {
-        [CCode (cname = "cipher_algorithm")]
-        public int cipher_algorithm;
-        [CCode (cname = "protocol")]
-        public int protocol;
-        [CCode (cname = "client_cert")]
-        public void* client_cert;
-        [CCode (cname = "tls_session")]
-        public void* tls_session;
-        [CCode (cname = "daemon")]
-        public Daemon* daemon;
-        [CCode (cname = "connect_fd")]
-        public int connect_fd;
-        [CCode (cname = "socket_context")]
-        public void* socket_context;
-        [CCode (cname = "suspended")]
-        public int suspended;
-        [CCode (cname = "connection_timeout")]
-        public uint connection_timeout;
-        [CCode (cname = "client_addr")]
-        public void* client_addr;
-        [CCode (cname = "header_size")]
-        public size_t header_size;
-        [CCode (cname = "http_status")]
-        public uint http_status;
-    }
-
-    [CCode (cname = "MHD_get_connection_info")]
-    public unowned ConnectionInfo? get_connection_info (Connection connection, ConnectionInfoType info_type);
-}

+ 237 - 0
vapi/libsodium.vapi

@@ -0,0 +1,237 @@
+/* Vala Bindings for LibSodium
+ * Copyright (c) 2020 Billy Barrow <billyb@pcthingz.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+ [CCode (cheader_filename = "sodium.h", lower_case_cprefix = "sodium_")]
+ namespace Sodium {
+ 
+   namespace Random {
+     [CCode (cname = "randombytes_SEEDBYTES")]
+     public const size_t SEED_BYTES;
+   
+     [CCode (cname = "randombytes_random")]
+     public uint32 random();
+   
+     [CCode (cname = "randombytes_uniform")]
+     public uint32 random_uniform(uint32 upper_bound);
+   
+     [CCode (cname = "randombytes_buf")]
+     public void random_bytes(uint8[] buffer);
+   
+     [CCode (cname = "randombytes_buf_deterministic")]
+     public void random_bytes_deterministic(uint8[] buffer, uint8[] seed);
+   }
+ 
+   namespace Symmetric {
+     [CCode (cname = "crypto_secretbox_KEYBYTES")]
+     public const size_t KEY_BYTES;
+ 
+     [CCode (cname = "crypto_secretbox_NONCEBYTES")]
+     public const size_t NONCE_BYTES;
+ 
+     [CCode (cname = "crypto_secretbox_MACBYTES")]
+     public const size_t MAC_BYTES;
+ 
+     [CCode (cname = "crypto_secretbox_keygen")]
+     private void key_gen([CCode (array_length = false)]uint8[] key);
+ 
+     public uint8[] generate_key() {
+       uint8[] key = new uint8[KEY_BYTES];
+       key_gen(key);
+       return key;
+     }
+ 
+     [CCode (cname = "crypto_secretbox_easy")]
+     private void secretbox(
+       [CCode (array_length = false)]uint8[] ciphertext,
+       uint8[] message,
+       [CCode (array_length = false)]uint8[] nonce,
+       [CCode (array_length = false)]uint8[] key
+     );
+ 
+     public uint8[] encrypt(uint8[] message, uint8[] key, uint8[] nonce)
+       requires (key.length == KEY_BYTES) 
+       requires (nonce.length == NONCE_BYTES)
+     {
+       // Initialise array for ciphertext
+       size_t ciphertext_size = MAC_BYTES + message.length;
+       uint8[] ciphertext = new uint8[ciphertext_size];
+ 
+       // Encrypt
+       secretbox(ciphertext, message, nonce, key);
+ 
+       // Return ciphertext
+       return ciphertext;
+     }
+ 
+     [CCode (cname = "crypto_secretbox_open_easy")]
+     private int secretbox_open(
+       [CCode (array_length = false)]uint8[] message,
+       uint8[] ciphertext,
+       [CCode (array_length = false)]uint8[] nonce,
+       [CCode (array_length = false)]uint8[] key
+     );
+ 
+     public uint8[]? decrypt(uint8[] ciphertext, uint8[] key, uint8[] nonce)
+       requires (ciphertext.length > MAC_BYTES)
+       requires (key.length == KEY_BYTES) 
+       requires (nonce.length == NONCE_BYTES)
+     {
+       // Initialise array for message
+       size_t message_size = ciphertext.length - MAC_BYTES;
+       uint8[] message = new uint8[message_size];
+ 
+       // Decrypt
+       int status = secretbox_open(message, ciphertext, nonce, key);
+ 
+       // Did it work?
+       if(status != 0) {
+         // No, return null
+         return null;
+       }
+ 
+       return message;
+     }
+   }
+   
+   namespace Asymmetric {
+ 
+     namespace Signing {
+ 
+         [CCode (cname = "crypto_sign_PUBLICKEYBYTES")]
+         public const size_t PUBLIC_KEY_BYTES;
+ 
+         [CCode (cname = "crypto_sign_SECRETKEYBYTES")]
+         public const size_t SECRET_KEY_BYTES;
+ 
+         [CCode (cname = "crypto_sign_BYTES")]
+         public const size_t MAX_HEADER_BYTES;
+ 
+         [CCode (cname = "crypto_sign_keypair")]
+         public void generate_keypair(
+             [CCode (array_length = false)]uint8[] public_key,
+             [CCode (array_length = false)]uint8[] secret_key)
+             requires (public_key.length == PUBLIC_KEY_BYTES)
+             requires (secret_key.length == SECRET_KEY_BYTES);
+             
+         [CCode (cname = "crypto_sign")]
+         private void sign_message(
+             [CCode (array_length = false)] uint8[] signed_message,
+             out int signature_length,
+             uint8[] message,
+             [CCode (array_length = false)] uint8[] secret_key
+         );
+ 
+         public uint8[] sign(
+             uint8[] message,
+             uint8[] secret_key)
+             requires (secret_key.length == SECRET_KEY_BYTES)
+         {
+             int signature_length;
+             uint8[] signed_message = new uint8[MAX_HEADER_BYTES + message.length];
+             sign_message(signed_message, out signature_length, message, secret_key);
+             signed_message.resize(signature_length);
+ 
+             return signed_message;
+         }
+ 
+         [CCode (cname = "crypto_sign_open")]
+         private int sign_open(
+             [CCode (array_length = false)] uint8[] message,
+             out int message_length,
+             uint8[] signed_message,
+             [CCode (array_length = false)] uint8[] public_key
+         );
+ 
+         public uint8[]? verify(
+             uint8[] signed_message,
+             uint8[] public_key)
+             requires (public_key.length == PUBLIC_KEY_BYTES)
+         {
+             int message_length;
+             uint8[] message = new uint8[signed_message.length];
+             if(sign_open(message, out message_length, signed_message, public_key) != 0) {
+                 return null;
+             }
+             message.resize(message_length);
+ 
+             return message;
+         }
+ 
+     }
+ 
+     namespace Sealing {
+ 
+         [CCode (cname = "crypto_box_PUBLICKEYBYTES")]
+         public const size_t PUBLIC_KEY_BYTES;
+ 
+         [CCode (cname = "crypto_box_SECRETKEYBYTES")]
+         public const size_t SECRET_KEY_BYTES;
+ 
+         [CCode (cname = "crypto_box_SEALBYTES")]
+         public const size_t HEADER_BYTES;
+ 
+         [CCode (cname = "crypto_box_keypair")]
+         public void generate_keypair(
+             [CCode (array_length = false)]uint8[] public_key,
+             [CCode (array_length = false)]uint8[] secret_key)
+             requires (public_key.length == PUBLIC_KEY_BYTES)
+             requires (secret_key.length == SECRET_KEY_BYTES);
+ 
+         [CCode (cname = "crypto_box_seal")]
+         private void seal_message(
+             [CCode (array_length = false)] uint8[] ciphertext,
+             uint8[] message,
+             [CCode (array_length = false)] uint8[] public_key
+         );
+ 
+         public uint8[] seal(uint8[] message, uint8[] public_key)
+             requires (public_key.length == PUBLIC_KEY_BYTES)
+         {
+             uint8[] ciphertext = new uint8[HEADER_BYTES + message.length];
+             seal_message(ciphertext, message, public_key);
+             return ciphertext;
+         }
+ 
+         [CCode (cname = "crypto_box_seal_open")]
+         private int seal_open(
+             [CCode (array_length = false)] uint8[] message,
+             uint8[] ciphertext,
+             [CCode (array_length = false)] uint8[] public_key,
+             [CCode (array_length = false)] uint8[] secret_key
+         );
+ 
+         public uint8[]? unseal(
+             uint8[] ciphertext,
+             uint8[] public_key,
+             uint8[] secret_key) 
+             requires (public_key.length == PUBLIC_KEY_BYTES)
+             requires (secret_key.length == SECRET_KEY_BYTES)
+             requires (ciphertext.length > HEADER_BYTES)
+         {
+             uint8[] message = new uint8[ciphertext.length - HEADER_BYTES];
+             if(seal_open(message, ciphertext, public_key, secret_key) != 0){
+                 return null;
+             }
+             return message;
+         }
+         
+     }
+ 
+   }
+   
+ 
+ }

+ 0 - 286
vapi/libzstd.vapi

@@ -1,286 +0,0 @@
-/* libzstd.vapi - Vala bindings for Zstandard compression library
- * 
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- * Vala bindings generated for use with the spry framework.
- * 
- * Based on zstd.h from libzstd 1.5.7
- */
-
-[CCode (cprefix = "ZSTD_", lower_case_cprefix = "ZSTD_", cheader_filename = "zstd.h,zstd_errors.h")]
-namespace Zstd {
-    
-    /* Version */
-    [CCode (cname = "ZSTD_VERSION_MAJOR")]
-    public const int VERSION_MAJOR;
-    [CCode (cname = "ZSTD_VERSION_MINOR")]
-    public const int VERSION_MINOR;
-    [CCode (cname = "ZSTD_VERSION_RELEASE")]
-    public const int VERSION_RELEASE;
-    [CCode (cname = "ZSTD_VERSION_NUMBER")]
-    public const int VERSION_NUMBER;
-    
-    [CCode (cname = "ZSTD_versionNumber")]
-    public uint version_number();
-    [CCode (cname = "ZSTD_versionString")]
-    public unowned string version_string();
-    
-    /* Default compression level */
-    [CCode (cname = "ZSTD_CLEVEL_DEFAULT")]
-    public const int CLEVEL_DEFAULT;
-    
-    /* Block size constants */
-    [CCode (cname = "ZSTD_BLOCKSIZELOG_MAX")]
-    public const int BLOCKSIZELOG_MAX;
-    [CCode (cname = "ZSTD_BLOCKSIZE_MAX")]
-    public const int BLOCKSIZE_MAX;
-    
-    /* Content size constants */
-    [CCode (cname = "ZSTD_CONTENTSIZE_UNKNOWN")]
-    public const uint64 CONTENTSIZE_UNKNOWN;
-    [CCode (cname = "ZSTD_CONTENTSIZE_ERROR")]
-    public const uint64 CONTENTSIZE_ERROR;
-    
-    /* Compression strategies */
-    [CCode (cname = "ZSTD_strategy", cprefix = "ZSTD_", has_type_id = false)]
-    public enum Strategy {
-        fast = 1,
-        dfast = 2,
-        greedy = 3,
-        lazy = 4,
-        lazy2 = 5,
-        btlazy2 = 6,
-        btopt = 7,
-        btultra = 8,
-        btultra2 = 9
-    }
-    
-    /* End directive for streaming */
-    [CCode (cname = "ZSTD_EndDirective", cprefix = "ZSTD_e_", has_type_id = false)]
-    public enum EndDirective {
-        continue = 0,
-        flush = 1,
-        end = 2
-    }
-    
-    /* Reset directive */
-    [CCode (cname = "ZSTD_ResetDirective", cprefix = "ZSTD_reset_", has_type_id = false)]
-    public enum ResetDirective {
-        session_only = 1,
-        parameters = 2,
-        session_and_parameters = 3
-    }
-    
-    /* Error codes */
-    [CCode (cname = "ZSTD_ErrorCode", cprefix = "ZSTD_error_", has_type_id = false)]
-    public enum ErrorCode {
-        no_error = 0,
-        GENERIC = 1,
-        prefix_unknown = 10,
-        version_unsupported = 12,
-        frameParameter_unsupported = 14,
-        frameParameter_windowTooLarge = 16,
-        corruption_detected = 20,
-        checksum_wrong = 22,
-        literals_headerWrong = 24,
-        dictionary_corrupted = 30,
-        dictionary_wrong = 32,
-        dictionaryCreation_failed = 34,
-        parameter_unsupported = 40,
-        parameter_combination_unsupported = 41,
-        parameter_outOfBound = 42,
-        tableLog_tooLarge = 44,
-        maxSymbolValue_tooLarge = 46,
-        maxSymbolValue_tooSmall = 48,
-        cannotProduce_uncompressedBlock = 49,
-        stabilityCondition_notRespected = 50,
-        stage_wrong = 60,
-        init_missing = 62,
-        memory_allocation = 64,
-        workSpace_tooSmall = 66,
-        dstSize_tooSmall = 70,
-        srcSize_wrong = 72,
-        dstBuffer_null = 74,
-        noForwardProgress_destFull = 80,
-        noForwardProgress_inputEmpty = 82,
-        frameIndex_tooLarge = 100,
-        seekableIO = 102,
-        dstBuffer_wrong = 104,
-        srcBuffer_wrong = 105,
-        sequenceProducer_failed = 106,
-        externalSequences_invalid = 107,
-        maxCode = 120
-    }
-    
-    /* Bounds structure */
-    [CCode (cname = "ZSTD_bounds", has_type_id = false)]
-    public struct Bounds {
-        public size_t error;
-        public int lowerBound;
-        public int upperBound;
-    }
-    
-    /* Input buffer for streaming */
-    [CCode (cname = "ZSTD_inBuffer", has_type_id = false)]
-    public struct InBuffer {
-        public uint8* src;
-        public size_t size;
-        public size_t pos;
-    }
-    
-    /* Output buffer for streaming */
-    [CCode (cname = "ZSTD_outBuffer", has_type_id = false)]
-    public struct OutBuffer {
-        public uint8* dst;
-        public size_t size;
-        public size_t pos;
-    }
-    
-    /* Compression context */
-    [CCode (cname = "ZSTD_CCtx", free_function = "ZSTD_freeCCtx", has_type_id = false)]
-    [Compact]
-    public class CCtx {
-        [CCode (cname = "ZSTD_createCCtx")]
-        public CCtx();
-        
-        [CCode (cname = "ZSTD_compressCCtx")]
-        public size_t compress(uint8* dst, size_t dstCapacity, uint8* src, size_t srcSize, int compressionLevel);
-        
-        [CCode (cname = "ZSTD_compress2")]
-        public size_t compress2(uint8* dst, size_t dstCapacity, uint8* src, size_t srcSize);
-        
-        [CCode (cname = "ZSTD_CCtx_setParameter")]
-        public size_t set_parameter(CParameter param, int value);
-        
-        [CCode (cname = "ZSTD_CCtx_setPledgedSrcSize")]
-        public size_t set_pledged_src_size(uint64 pledgedSrcSize);
-        
-        [CCode (cname = "ZSTD_CCtx_reset")]
-        public size_t reset(ResetDirective reset);
-        
-        [CCode (cname = "ZSTD_compressStream2")]
-        public size_t compress_stream2(OutBuffer* output, InBuffer* input, EndDirective endOp);
-        
-        [CCode (cname = "ZSTD_initCStream")]
-        public size_t init_stream(int compressionLevel);
-        
-        [CCode (cname = "ZSTD_compressStream")]
-        public size_t compress_stream(OutBuffer* output, InBuffer* input);
-        
-        [CCode (cname = "ZSTD_flushStream")]
-        public size_t flush_stream(OutBuffer* output);
-        
-        [CCode (cname = "ZSTD_endStream")]
-        public size_t end_stream(OutBuffer* output);
-    }
-    
-    /* Decompression context */
-    [CCode (cname = "ZSTD_DCtx", free_function = "ZSTD_freeDCtx", has_type_id = false)]
-    [Compact]
-    public class DCtx {
-        [CCode (cname = "ZSTD_createDCtx")]
-        public DCtx();
-        
-        [CCode (cname = "ZSTD_decompressDCtx")]
-        public size_t decompress(uint8* dst, size_t dstCapacity, uint8* src, size_t srcSize);
-        
-        [CCode (cname = "ZSTD_DCtx_setParameter")]
-        public size_t set_parameter(DParameter param, int value);
-        
-        [CCode (cname = "ZSTD_DCtx_reset")]
-        public size_t reset(ResetDirective reset);
-    }
-    
-    /* Compression stream (alias for CCtx) */
-    [CCode (cname = "ZSTD_CStream", free_function = "ZSTD_freeCStream", has_type_id = false)]
-    [Compact]
-    public class CStream {
-        [CCode (cname = "ZSTD_createCStream")]
-        public CStream();
-    }
-    
-    /* Compression parameters */
-    [CCode (cname = "ZSTD_cParameter", cprefix = "ZSTD_c_", has_type_id = false)]
-    public enum CParameter {
-        compressionLevel = 100,
-        windowLog = 101,
-        hashLog = 102,
-        chainLog = 103,
-        searchLog = 104,
-        minMatch = 105,
-        targetLength = 106,
-        strategy = 107,
-        targetCBlockSize = 130,
-        enableLongDistanceMatching = 160,
-        ldmHashLog = 161,
-        ldmMinMatch = 162,
-        ldmBucketSizeLog = 163,
-        ldmHashRateLog = 164,
-        contentSizeFlag = 200,
-        checksumFlag = 201,
-        dictIDFlag = 202,
-        nbWorkers = 400,
-        jobSize = 401,
-        overlapLog = 402
-    }
-    
-    /* Decompression parameters */
-    [CCode (cname = "ZSTD_dParameter", cprefix = "ZSTD_d_", has_type_id = false)]
-    public enum DParameter {
-        windowLogMax = 100
-    }
-    
-    /* Simple API - One-shot compression */
-    [CCode (cname = "ZSTD_compress")]
-    public size_t compress(uint8* dst, size_t dstCapacity, uint8* src, size_t srcSize, int compressionLevel);
-    
-    /* Simple API - One-shot decompression */
-    [CCode (cname = "ZSTD_decompress")]
-    public size_t decompress(uint8* dst, size_t dstCapacity, uint8* src, size_t compressedSize);
-    
-    /* Helper functions */
-    [CCode (cname = "ZSTD_compressBound")]
-    public size_t compress_bound(size_t srcSize);
-    
-    [CCode (cname = "ZSTD_isError")]
-    public uint is_error(size_t result);
-    
-    [CCode (cname = "ZSTD_getErrorCode")]
-    public ErrorCode get_error_code(size_t functionResult);
-    
-    [CCode (cname = "ZSTD_getErrorName")]
-    public unowned string get_error_name(size_t result);
-    
-    [CCode (cname = "ZSTD_minCLevel")]
-    public int min_c_level();
-    
-    [CCode (cname = "ZSTD_maxCLevel")]
-    public int max_c_level();
-    
-    [CCode (cname = "ZSTD_defaultCLevel")]
-    public int default_c_level();
-    
-    /* Content size functions */
-    [CCode (cname = "ZSTD_getFrameContentSize")]
-    public uint64 get_frame_content_size(uint8* src, size_t srcSize);
-    
-    [CCode (cname = "ZSTD_findFrameCompressedSize")]
-    public size_t find_frame_compressed_size(uint8* src, size_t srcSize);
-    
-    /* Streaming buffer size recommendations */
-    [CCode (cname = "ZSTD_CStreamInSize")]
-    public size_t cstream_in_size();
-    
-    [CCode (cname = "ZSTD_CStreamOutSize")]
-    public size_t cstream_out_size();
-    
-    /* Error string from error code */
-    [CCode (cname = "ZSTD_getErrorString")]
-    public unowned string get_error_string(ErrorCode code);
-    
-    /* Parameter bounds */
-    [CCode (cname = "ZSTD_cParam_getBounds")]
-    public Bounds cparam_get_bounds(CParameter cParam);
-    
-    [CCode (cname = "ZSTD_dParam_getBounds")]
-    public Bounds dparam_get_bounds(DParameter dParam);
-}