Sfoglia il codice sorgente

refactor(core)!: migrate to Inversion IoC container with WebApplication

Replace manual router configuration with dependency injection pattern.
Endpoints are now registered via the container with EndpointRoute metadata,
and the EndpointRouter resolves them dynamically from the IoC scope.

- Add WebApplication class as main entry point with built-in container
- Simplify Endpoint interface by removing route/methods properties
- Introduce EndpointRoute class for route metadata registration
- Replace RouteInformation with RouteContext for request routing data
- Update all examples to use container-based endpoint registration
- Add inversion-0.1 as dependency for IoC functionality

BREAKING CHANGE: Endpoint implementations must remove route and methods
properties. Use container.register_scoped<Endpoint>() with EndpointRoute
metadata instead of router.add_endpoint(). RouteInformation parameter
changed to RouteContext with mapped_parameters instead of named_components.
Billy Barrow 1 settimana fa
parent
commit
9a3bb18b73

+ 29 - 34
examples/DataStructuresDemo.vala

@@ -12,9 +12,7 @@ using Invercargill.DataStructures;
 
 
 // Root handler
 // Root handler
 class RootEndpoint : Object, Endpoint {
 class RootEndpoint : Object, Endpoint {
-    public string route { get { return "/"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         return new HttpStringResult("""Data Structures Demo
         return new HttpStringResult("""Data Structures Demo
 
 
 This example demonstrates various Invercargill data structures:
 This example demonstrates various Invercargill data structures:
@@ -32,9 +30,7 @@ Endpoints:
 
 
 // Series operations
 // Series operations
 class SeriesEndpoint : Object, Endpoint {
 class SeriesEndpoint : Object, Endpoint {
-    public string route { get { return "/series"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var series_array = new string[3];
         var series_array = new string[3];
         series_array[0] = "First";
         series_array[0] = "First";
         series_array[1] = "Second";
         series_array[1] = "Second";
@@ -59,9 +55,7 @@ class SeriesEndpoint : Object, Endpoint {
 
 
 // Vector operations
 // Vector operations
 class VectorEndpoint : Object, Endpoint {
 class VectorEndpoint : Object, Endpoint {
-    public string route { get { return "/vector"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var numbers = new int[5];
         var numbers = new int[5];
         numbers[0] = 1;
         numbers[0] = 1;
         numbers[1] = 2;
         numbers[1] = 2;
@@ -87,9 +81,7 @@ class VectorEndpoint : Object, Endpoint {
 
 
 // RingBuffer operations
 // RingBuffer operations
 class RingBufferEndpoint : Object, Endpoint {
 class RingBufferEndpoint : Object, Endpoint {
-    public string route { get { return "/ring-buffer"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var buffer_array = new string[5];
         var buffer_array = new string[5];
         buffer_array[0] = "Item 1";
         buffer_array[0] = "Item 1";
         buffer_array[1] = "Item 2";
         buffer_array[1] = "Item 2";
@@ -112,9 +104,7 @@ class RingBufferEndpoint : Object, Endpoint {
 
 
 // ImmutableBuffer operations
 // ImmutableBuffer operations
 class ImmutableBufferEndpoint : Object, Endpoint {
 class ImmutableBufferEndpoint : Object, Endpoint {
-    public string route { get { return "/immutable-buffer"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var items_array = new string[4];
         var items_array = new string[4];
         items_array[0] = "Apple";
         items_array[0] = "Apple";
         items_array[1] = "Banana";
         items_array[1] = "Banana";
@@ -143,9 +133,7 @@ class ImmutableBufferEndpoint : Object, Endpoint {
 
 
 // Wrap utility functions
 // Wrap utility functions
 class WrapOperationsEndpoint : Object, Endpoint {
 class WrapOperationsEndpoint : Object, Endpoint {
-    public string route { get { return "/wrap-operations"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var numbers = new int[5];
         var numbers = new int[5];
         numbers[0] = 1;
         numbers[0] = 1;
         numbers[1] = 2;
         numbers[1] = 2;
@@ -175,9 +163,7 @@ class WrapOperationsEndpoint : Object, Endpoint {
 
 
 // Combined operations
 // Combined operations
 class CombinedOperationsEndpoint : Object, Endpoint {
 class CombinedOperationsEndpoint : Object, Endpoint {
-    public string route { get { return "/combined-operations"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var numbers1 = new int[3];
         var numbers1 = new int[3];
         numbers1[0] = 1;
         numbers1[0] = 1;
         numbers1[1] = 2;
         numbers1[1] = 2;
@@ -219,19 +205,28 @@ class CombinedOperationsEndpoint : Object, Endpoint {
 }
 }
 
 
 void main() {
 void main() {
-    var router = new EndpointRouter()
-        .add_endpoint(new RootEndpoint())
-        .add_endpoint(new SeriesEndpoint())
-        .add_endpoint(new VectorEndpoint())
-        .add_endpoint(new RingBufferEndpoint())
-        .add_endpoint(new ImmutableBufferEndpoint())
-        .add_endpoint(new WrapOperationsEndpoint())
-        .add_endpoint(new CombinedOperationsEndpoint());
+    var application = new WebApplication(8086);
     
     
-    var pipeline = new Pipeline()
-        .add_component(router);
-
-    var server = new Server(8086, pipeline);
+    application.container.register_scoped<Endpoint>(() => new RootEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/"));
+    
+    application.container.register_scoped<Endpoint>(() => new SeriesEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/series"));
+    
+    application.container.register_scoped<Endpoint>(() => new VectorEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/vector"));
+    
+    application.container.register_scoped<Endpoint>(() => new RingBufferEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/ring-buffer"));
+    
+    application.container.register_scoped<Endpoint>(() => new ImmutableBufferEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/immutable-buffer"));
+    
+    application.container.register_scoped<Endpoint>(() => new WrapOperationsEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/wrap-operations"));
+    
+    application.container.register_scoped<Endpoint>(() => new CombinedOperationsEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/combined-operations"));
     
     
     print("Data Structures Demo Server running on port 8086\n");
     print("Data Structures Demo Server running on port 8086\n");
     print("Try these endpoints:\n");
     print("Try these endpoints:\n");
@@ -243,7 +238,7 @@ void main() {
     print("  - http://localhost:8086/wrap-operations\n");
     print("  - http://localhost:8086/wrap-operations\n");
     print("  - http://localhost:8086/combined-operations\n");
     print("  - http://localhost:8086/combined-operations\n");
     
     
-    server.run();
+    application.run();
 }
 }
 
 
 // Helper functions
 // Helper functions

+ 33 - 38
examples/EnumerableOperations.vala

@@ -11,9 +11,7 @@ using Invercargill.DataStructures;
 
 
 // Root handler
 // Root handler
 class RootEndpoint : Object, Endpoint {
 class RootEndpoint : Object, Endpoint {
-    public string route { get { return "/"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         return new HttpStringResult("""Enumerable Operations Demo
         return new HttpStringResult("""Enumerable Operations Demo
 
 
 This example demonstrates various LINQ-like operations available in Invercargill Enumerable:
 This example demonstrates various LINQ-like operations available in Invercargill Enumerable:
@@ -32,9 +30,7 @@ Endpoints:
 
 
 // Filtering examples
 // Filtering examples
 class FilterEndpoint : Object, Endpoint {
 class FilterEndpoint : Object, Endpoint {
-    public string route { get { return "/filter"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var numbers = Wrap.array<int>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
         var numbers = Wrap.array<int>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
         
         
         var parts = new Series<string>();
         var parts = new Series<string>();
@@ -79,9 +75,7 @@ class FilterEndpoint : Object, Endpoint {
 
 
 // Mapping/Projection examples
 // Mapping/Projection examples
 class MapEndpoint : Object, Endpoint {
 class MapEndpoint : Object, Endpoint {
-    public string route { get { return "/map"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var numbers = Wrap.array<int>({1, 2, 3, 4, 5});
         var numbers = Wrap.array<int>({1, 2, 3, 4, 5});
         
         
         var parts = new Series<string>();
         var parts = new Series<string>();
@@ -126,9 +120,7 @@ class MapEndpoint : Object, Endpoint {
 
 
 // Grouping examples
 // Grouping examples
 class GroupByEndpoint : Object, Endpoint {
 class GroupByEndpoint : Object, Endpoint {
-    public string route { get { return "/group-by"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var words = Wrap.array<string>({
         var words = Wrap.array<string>({
             "apple", "banana", "cherry", "apricot", 
             "apple", "banana", "cherry", "apricot", 
             "blueberry", "blackberry", "coconut"
             "blueberry", "blackberry", "coconut"
@@ -159,9 +151,7 @@ class GroupByEndpoint : Object, Endpoint {
 
 
 // Aggregation examples
 // Aggregation examples
 class AggregateEndpoint : Object, Endpoint {
 class AggregateEndpoint : Object, Endpoint {
-    public string route { get { return "/aggregate"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var numbers = Wrap.array<int>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
         var numbers = Wrap.array<int>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
         
         
         var parts = new Series<string>();
         var parts = new Series<string>();
@@ -207,9 +197,7 @@ class AggregateEndpoint : Object, Endpoint {
 
 
 // Sorting examples
 // Sorting examples
 class SortEndpoint : Object, Endpoint {
 class SortEndpoint : Object, Endpoint {
-    public string route { get { return "/sort"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var numbers = Wrap.array<int>({5, 2, 8, 1, 9, 3, 7, 4, 6, 10});
         var numbers = Wrap.array<int>({5, 2, 8, 1, 9, 3, 7, 4, 6, 10});
         
         
         var parts = new Series<string>();
         var parts = new Series<string>();
@@ -239,9 +227,7 @@ class SortEndpoint : Object, Endpoint {
 
 
 // Set operations
 // Set operations
 class SetOperationsEndpoint : Object, Endpoint {
 class SetOperationsEndpoint : Object, Endpoint {
-    public string route { get { return "/set-operations"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var set1 = Wrap.array<int>({1, 2, 3, 4, 5});
         var set1 = Wrap.array<int>({1, 2, 3, 4, 5});
         var set2 = Wrap.array<int>({4, 5, 6, 7, 8});
         var set2 = Wrap.array<int>({4, 5, 6, 7, 8});
         
         
@@ -279,9 +265,7 @@ class SetOperationsEndpoint : Object, Endpoint {
 
 
 // Advanced operations
 // Advanced operations
 class AdvancedEndpoint : Object, Endpoint {
 class AdvancedEndpoint : Object, Endpoint {
-    public string route { get { return "/advanced"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var numbers = Wrap.array<int>({1, 2, 3, 4, 5});
         var numbers = Wrap.array<int>({1, 2, 3, 4, 5});
         
         
         var parts = new Series<string>();
         var parts = new Series<string>();
@@ -318,20 +302,31 @@ class AdvancedEndpoint : Object, Endpoint {
 }
 }
 
 
 void main() {
 void main() {
-    var router = new EndpointRouter()
-        .add_endpoint(new RootEndpoint())
-        .add_endpoint(new FilterEndpoint())
-        .add_endpoint(new MapEndpoint())
-        .add_endpoint(new GroupByEndpoint())
-        .add_endpoint(new AggregateEndpoint())
-        .add_endpoint(new SortEndpoint())
-        .add_endpoint(new SetOperationsEndpoint())
-        .add_endpoint(new AdvancedEndpoint());
+    var application = new WebApplication(8087);
     
     
-    var pipeline = new Pipeline()
-        .add_component(router);
-
-    var server = new Server(8087, pipeline);
+    application.container.register_scoped<Endpoint>(() => new RootEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/"));
+    
+    application.container.register_scoped<Endpoint>(() => new FilterEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/filter"));
+    
+    application.container.register_scoped<Endpoint>(() => new MapEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/map"));
+    
+    application.container.register_scoped<Endpoint>(() => new GroupByEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/group-by"));
+    
+    application.container.register_scoped<Endpoint>(() => new AggregateEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/aggregate"));
+    
+    application.container.register_scoped<Endpoint>(() => new SortEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/sort"));
+    
+    application.container.register_scoped<Endpoint>(() => new SetOperationsEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/set-operations"));
+    
+    application.container.register_scoped<Endpoint>(() => new AdvancedEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/advanced"));
     
     
     print("Enumerable Operations Demo Server running on port 8087\n");
     print("Enumerable Operations Demo Server running on port 8087\n");
     print("Try these endpoints:\n");
     print("Try these endpoints:\n");
@@ -344,7 +339,7 @@ void main() {
     print("  - http://localhost:8087/set-operations\n");
     print("  - http://localhost:8087/set-operations\n");
     print("  - http://localhost:8087/advanced\n");
     print("  - http://localhost:8087/advanced\n");
     
     
-    server.run();
+    application.run();
 }
 }
 
 
 // Helper functions
 // Helper functions

+ 57 - 60
examples/ErrorHandling.vala

@@ -11,9 +11,7 @@ using Invercargill.DataStructures;
 
 
 // Root handler
 // Root handler
 class RootEndpoint : Object, Endpoint {
 class RootEndpoint : Object, Endpoint {
-    public string route { get { return "/"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         return new HttpStringResult("""Error Handling Demo
         return new HttpStringResult("""Error Handling Demo
 
 
 This example demonstrates various error handling patterns in Astralis:
 This example demonstrates various error handling patterns in Astralis:
@@ -37,9 +35,7 @@ Endpoints:
 
 
 // 200 OK response
 // 200 OK response
 class OkEndpoint : Object, Endpoint {
 class OkEndpoint : Object, Endpoint {
-    public string route { get { return "/ok"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         return new HttpStringResult(@"{ \"status\": \"success\", \"message\": \"Request completed successfully\" }")
         return new HttpStringResult(@"{ \"status\": \"success\", \"message\": \"Request completed successfully\" }")
             .set_header("Content-Type", "application/json");
             .set_header("Content-Type", "application/json");
     }
     }
@@ -47,9 +43,7 @@ class OkEndpoint : Object, Endpoint {
 
 
 // 404 Not Found
 // 404 Not Found
 class NotFoundEndpoint : Object, Endpoint {
 class NotFoundEndpoint : Object, Endpoint {
-    public string route { get { return "/not-found"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         return new HttpStringResult(@"{ \"error\": \"Not Found\", \"message\": \"The requested resource was not found\" }")
         return new HttpStringResult(@"{ \"error\": \"Not Found\", \"message\": \"The requested resource was not found\" }")
             .set_header("Content-Type", "application/json");
             .set_header("Content-Type", "application/json");
     }
     }
@@ -57,9 +51,7 @@ class NotFoundEndpoint : Object, Endpoint {
 
 
 // 400 Bad Request
 // 400 Bad Request
 class BadRequestEndpoint : Object, Endpoint {
 class BadRequestEndpoint : Object, Endpoint {
-    public string route { get { return "/bad-request"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         return new HttpStringResult(@"{ \"error\": \"Bad Request\", \"message\": \"The request could not be understood\" }")
         return new HttpStringResult(@"{ \"error\": \"Bad Request\", \"message\": \"The request could not be understood\" }")
             .set_header("Content-Type", "application/json");
             .set_header("Content-Type", "application/json");
     }
     }
@@ -67,9 +59,7 @@ class BadRequestEndpoint : Object, Endpoint {
 
 
 // 500 Internal Server Error
 // 500 Internal Server Error
 class InternalErrorEndpoint : Object, Endpoint {
 class InternalErrorEndpoint : Object, Endpoint {
-    public string route { get { return "/internal-error"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         return new HttpStringResult(@"{ \"error\": \"Internal Server Error\", \"message\": \"An unexpected error occurred\" }")
         return new HttpStringResult(@"{ \"error\": \"Internal Server Error\", \"message\": \"An unexpected error occurred\" }")
             .set_header("Content-Type", "application/json");
             .set_header("Content-Type", "application/json");
     }
     }
@@ -77,9 +67,7 @@ class InternalErrorEndpoint : Object, Endpoint {
 
 
 // Input validation example
 // Input validation example
 class ValidationEndpoint : Object, Endpoint {
 class ValidationEndpoint : Object, Endpoint {
-    public string route { get { return "/validation"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var email = http_context.request.get_query("email");
         var email = http_context.request.get_query("email");
         var age_str = http_context.request.get_query("age");
         var age_str = http_context.request.get_query("age");
         
         
@@ -128,10 +116,9 @@ class ValidationEndpoint : Object, Endpoint {
 
 
 // Resource lookup with error handling
 // Resource lookup with error handling
 class ResourceEndpoint : Object, Endpoint {
 class ResourceEndpoint : Object, Endpoint {
-    public string route { get { return "/resource/{id}"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route_info) throws Error {
-        var id_str = route_info.named_components["id"];
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route_info) throws Error {
+        string? id_str = null;
+        route_info.mapped_parameters.try_get("id", out id_str);
         int id;
         int id;
         
         
         if (!int.try_parse(id_str, out id)) {
         if (!int.try_parse(id_str, out id)) {
@@ -163,10 +150,9 @@ class ResourceEndpoint : Object, Endpoint {
 
 
 // API endpoint simulation
 // API endpoint simulation
 class ApiEndpoint : Object, Endpoint {
 class ApiEndpoint : Object, Endpoint {
-    public string route { get { return "/api/{endpoint}"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route_info) throws Error {
-        var endpoint = route_info.named_components["endpoint"];
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route_info) throws Error {
+        string? endpoint = null;
+        route_info.mapped_parameters.try_get("endpoint", out endpoint);
         
         
         // Simulate different API responses
         // Simulate different API responses
         switch (endpoint) {
         switch (endpoint) {
@@ -189,9 +175,7 @@ class ApiEndpoint : Object, Endpoint {
 
 
 // Custom error response
 // Custom error response
 class CustomErrorEndpoint : Object, Endpoint {
 class CustomErrorEndpoint : Object, Endpoint {
-    public string route { get { return "/custom-error"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var error_code = http_context.request.get_query_or_default("code", "CUSTOM_ERROR");
         var error_code = http_context.request.get_query_or_default("code", "CUSTOM_ERROR");
         var message = http_context.request.get_query_or_default("message", "A custom error occurred");
         var message = http_context.request.get_query_or_default("message", "A custom error occurred");
         
         
@@ -211,9 +195,7 @@ class CustomErrorEndpoint : Object, Endpoint {
 
 
 // Simulated timeout (actually returns 408)
 // Simulated timeout (actually returns 408)
 class TimeoutEndpoint : Object, Endpoint {
 class TimeoutEndpoint : Object, Endpoint {
-    public string route { get { return "/timeout"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         return new HttpStringResult(@"{ \"error\": \"Request Timeout\", \"message\": \"The request took too long to complete\" }")
         return new HttpStringResult(@"{ \"error\": \"Request Timeout\", \"message\": \"The request took too long to complete\" }")
             .set_header("Content-Type", "application/json");
             .set_header("Content-Type", "application/json");
     }
     }
@@ -221,9 +203,7 @@ class TimeoutEndpoint : Object, Endpoint {
 
 
 // 401 Unauthorized
 // 401 Unauthorized
 class UnauthorizedEndpoint : Object, Endpoint {
 class UnauthorizedEndpoint : Object, Endpoint {
-    public string route { get { return "/unauthorized"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var auth_header = http_context.request.get_header("Authorization");
         var auth_header = http_context.request.get_header("Authorization");
         
         
         if (auth_header == null) {
         if (auth_header == null) {
@@ -246,9 +226,7 @@ class UnauthorizedEndpoint : Object, Endpoint {
 
 
 // 403 Forbidden
 // 403 Forbidden
 class ForbiddenEndpoint : Object, Endpoint {
 class ForbiddenEndpoint : Object, Endpoint {
-    public string route { get { return "/forbidden"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var user_role = http_context.request.get_cookie("role") ?? "guest";
         var user_role = http_context.request.get_cookie("role") ?? "guest";
         
         
         if (user_role != "admin") {
         if (user_role != "admin") {
@@ -263,9 +241,7 @@ class ForbiddenEndpoint : Object, Endpoint {
 
 
 // 405 Method Not Allowed
 // 405 Method Not Allowed
 class MethodNotAllowedEndpoint : Object, Endpoint {
 class MethodNotAllowedEndpoint : Object, Endpoint {
-    public string route { get { return "/method-not-allowed"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         return new HttpStringResult(@"{ \"error\": \"Method Not Allowed\", \"message\": \"GET method is not allowed for this endpoint\" }")
         return new HttpStringResult(@"{ \"error\": \"Method Not Allowed\", \"message\": \"GET method is not allowed for this endpoint\" }")
             .set_header("Content-Type", "application/json")
             .set_header("Content-Type", "application/json")
             .set_header("Allow", "POST, PUT, DELETE");
             .set_header("Allow", "POST, PUT, DELETE");
@@ -273,25 +249,46 @@ class MethodNotAllowedEndpoint : Object, Endpoint {
 }
 }
 
 
 void main() {
 void main() {
-    var router = new EndpointRouter()
-        .add_endpoint(new RootEndpoint())
-        .add_endpoint(new OkEndpoint())
-        .add_endpoint(new NotFoundEndpoint())
-        .add_endpoint(new BadRequestEndpoint())
-        .add_endpoint(new InternalErrorEndpoint())
-        .add_endpoint(new ValidationEndpoint())
-        .add_endpoint(new ResourceEndpoint())
-        .add_endpoint(new ApiEndpoint())
-        .add_endpoint(new CustomErrorEndpoint())
-        .add_endpoint(new TimeoutEndpoint())
-        .add_endpoint(new UnauthorizedEndpoint())
-        .add_endpoint(new ForbiddenEndpoint())
-        .add_endpoint(new MethodNotAllowedEndpoint());
+    var application = new WebApplication(8088);
     
     
-    var pipeline = new Pipeline()
-        .add_component(router);
-
-    var server = new Server(8088, pipeline);
+    application.container.register_scoped<Endpoint>(() => new RootEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/"));
+    
+    application.container.register_scoped<Endpoint>(() => new OkEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/ok"));
+    
+    application.container.register_scoped<Endpoint>(() => new NotFoundEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/not-found"));
+    
+    application.container.register_scoped<Endpoint>(() => new BadRequestEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/bad-request"));
+    
+    application.container.register_scoped<Endpoint>(() => new InternalErrorEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/internal-error"));
+    
+    application.container.register_scoped<Endpoint>(() => new ValidationEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/validation"));
+    
+    application.container.register_scoped<Endpoint>(() => new ResourceEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/resource/{id}"));
+    
+    application.container.register_scoped<Endpoint>(() => new ApiEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/api/{endpoint}"));
+    
+    application.container.register_scoped<Endpoint>(() => new CustomErrorEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/custom-error"));
+    
+    application.container.register_scoped<Endpoint>(() => new TimeoutEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/timeout"));
+    
+    application.container.register_scoped<Endpoint>(() => new UnauthorizedEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/unauthorized"));
+    
+    application.container.register_scoped<Endpoint>(() => new ForbiddenEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/forbidden"));
+    
+    application.container.register_scoped<Endpoint>(() => new MethodNotAllowedEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/method-not-allowed"));
     
     
     print("Error Handling Demo Server running on port 8088\n");
     print("Error Handling Demo Server running on port 8088\n");
     print("Try these endpoints:\n");
     print("Try these endpoints:\n");
@@ -310,7 +307,7 @@ void main() {
     print("  - http://localhost:8088/forbidden\n");
     print("  - http://localhost:8088/forbidden\n");
     print("  - http://localhost:8088/method-not-allowed\n");
     print("  - http://localhost:8088/method-not-allowed\n");
     
     
-    server.run();
+    application.run();
 }
 }
 
 
 // Helper functions
 // Helper functions

+ 481 - 80
examples/FastResources.vala

@@ -15,6 +15,10 @@ using Invercargill.DataStructures;
  *   2. from_byte_array - For serving binary data (like images)
  *   2. from_byte_array - For serving binary data (like images)
  *   3. Default constructor - For loading files from the filesystem
  *   3. Default constructor - For loading files from the filesystem
  * 
  * 
+ * Note: FastResource should be registered as a SINGLETON since it holds
+ * pre-loaded content. The factory delegate creates the instance, and the
+ * container caches it for subsequent requests.
+ * 
  * Usage: fast-resources [port]
  * Usage: fast-resources [port]
  * 
  * 
  * Examples:
  * Examples:
@@ -22,58 +26,417 @@ using Invercargill.DataStructures;
  *   fast-resources 8080
  *   fast-resources 8080
  */
  */
 
 
-// Simple 1x1 pixel PNG image (transparent) as a byte array
-private const uint8[] TRANSPARENT_PIXEL = {
-    0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,  // PNG signature
-    0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52,  // IHDR chunk
-    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
-    0x08, 0x06, 0x00, 0x00, 0x00, 0x1F, 0x15, 0xCA,
-    0x4B, 0x00, 0x00, 0x00, 0x0A, 0x49, 0x44, 0x41,  // IDAT chunk
-    0x54, 0x78, 0x9C, 0x63, 0x60, 0x00, 0x00, 0x00,
-    0x02, 0x00, 0x01, 0xE5, 0x27, 0xDE, 0xFC, 0x00,  // IEND chunk
-    0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44,
-    0xAE, 0x42, 0x60, 0x82
+// Cat image as a byte array
+private const uint8[] CAT_PHOTO = {
+  0x52, 0x49, 0x46, 0x46, 0xf8, 0x12, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50,
+  0x56, 0x50, 0x38, 0x20, 0xec, 0x12, 0x00, 0x00, 0xb0, 0xae, 0x00, 0x9d,
+  0x01, 0x2a, 0xf4, 0x01, 0x79, 0x01, 0x3f, 0x45, 0x98, 0xbe, 0x56, 0x28,
+  0x3b, 0xb3, 0xb2, 0xa5, 0xd1, 0x6a, 0xab, 0x76, 0x28, 0x89, 0xe9, 0x6e,
+  0x38, 0x17, 0xae, 0x9b, 0xd8, 0x73, 0x56, 0x11, 0x8f, 0x21, 0x8e, 0xff,
+  0x90, 0xba, 0x6b, 0x5d, 0x5f, 0x32, 0x6a, 0x3f, 0x66, 0x21, 0xca, 0xf4,
+  0xb9, 0x1e, 0xe6, 0x3a, 0xcb, 0x38, 0x72, 0x5e, 0x34, 0xfc, 0xd2, 0xff,
+  0xce, 0x9e, 0xda, 0xe3, 0xef, 0xc3, 0x61, 0x3f, 0x66, 0xf2, 0xef, 0xd3,
+  0x5f, 0xba, 0xb3, 0xc5, 0xe7, 0xf0, 0xa7, 0x7e, 0xbf, 0x48, 0x86, 0x4f,
+  0xa6, 0x31, 0x23, 0x5c, 0xdc, 0x33, 0x17, 0xf6, 0xd5, 0x5b, 0x5b, 0x65,
+  0x3b, 0x7d, 0x5a, 0x5b, 0xb9, 0xb7, 0x41, 0x5f, 0x75, 0xf6, 0x40, 0xe8,
+  0x7d, 0x12, 0xcf, 0x7a, 0xf7, 0x5b, 0x54, 0xb3, 0xbc, 0xa4, 0x38, 0x29,
+  0x6b, 0xa6, 0x20, 0x7d, 0x59, 0xd8, 0x11, 0x10, 0xd7, 0xc6, 0xad, 0x15,
+  0x22, 0x4c, 0xd7, 0xbb, 0x59, 0xcb, 0x63, 0x35, 0xce, 0x00, 0x11, 0x0c,
+  0x56, 0xc8, 0xe0, 0x85, 0xdc, 0x5d, 0xe6, 0x1a, 0x98, 0x6a, 0x25, 0x26,
+  0xcf, 0xe7, 0x32, 0xd3, 0x1a, 0x3e, 0x39, 0xf3, 0x03, 0x0f, 0x0c, 0x63,
+  0xb6, 0x49, 0x66, 0xa4, 0x8c, 0xbd, 0x02, 0x1f, 0xdd, 0xd1, 0x12, 0x73,
+  0xa0, 0xd6, 0x3b, 0x22, 0xdf, 0x88, 0x2a, 0x14, 0xb2, 0x85, 0xe4, 0x90,
+  0xeb, 0x00, 0x82, 0x95, 0xb9, 0x8c, 0x21, 0x22, 0xc5, 0xf0, 0x6c, 0x49,
+  0x12, 0x6f, 0xfb, 0xc5, 0xa4, 0x91, 0x54, 0x75, 0xc1, 0x6a, 0x5c, 0x4c,
+  0x0a, 0xaf, 0xbc, 0xb4, 0x2f, 0x94, 0x4c, 0x4a, 0x2d, 0x09, 0x2b, 0xa0,
+  0x3e, 0x80, 0x79, 0xf6, 0xb2, 0x91, 0x2c, 0x59, 0x89, 0x7a, 0xe8, 0xa9,
+  0x18, 0xa4, 0xc7, 0x57, 0x54, 0x79, 0x68, 0x5e, 0xe6, 0x5a, 0x5e, 0xbe,
+  0x0f, 0x26, 0x3f, 0xe1, 0xee, 0x23, 0x32, 0xd2, 0xc8, 0xbb, 0xb0, 0x8c,
+  0x03, 0x61, 0x20, 0xcd, 0x28, 0x57, 0x6b, 0x75, 0x8a, 0x84, 0xd2, 0x91,
+  0x06, 0x12, 0x6f, 0x4b, 0xab, 0x6b, 0x09, 0xcd, 0x8a, 0xda, 0x11, 0x6e,
+  0xbc, 0xed, 0xb1, 0xb5, 0xe6, 0xd6, 0xc6, 0x55, 0x2f, 0x13, 0x29, 0x92,
+  0x41, 0x40, 0x64, 0x89, 0xc9, 0xb7, 0xcf, 0xb5, 0xd5, 0x6b, 0x79, 0x59,
+  0x87, 0xc1, 0x08, 0x29, 0x2e, 0x41, 0x58, 0x2f, 0x44, 0x0f, 0x09, 0x99,
+  0x0f, 0x1f, 0x82, 0xcb, 0x9a, 0x48, 0x0b, 0x42, 0x03, 0xb9, 0x38, 0xb3,
+  0x24, 0xbf, 0x07, 0xab, 0x62, 0x69, 0xf6, 0x37, 0x38, 0xf8, 0xe8, 0xe8,
+  0xd6, 0xb4, 0xf6, 0x09, 0x60, 0x5c, 0xd6, 0xd1, 0x40, 0xe8, 0x21, 0x02,
+  0x34, 0xdb, 0x39, 0xf0, 0xa8, 0x0e, 0x04, 0xa2, 0x24, 0xf5, 0x1b, 0x90,
+  0x8a, 0xb0, 0x1a, 0xf2, 0x06, 0xb9, 0x0a, 0xd2, 0x16, 0x26, 0x7a, 0x9b,
+  0x86, 0x57, 0xa1, 0x79, 0xbf, 0x81, 0x62, 0x6a, 0x00, 0x1c, 0xd1, 0x26,
+  0x88, 0x81, 0x4a, 0x70, 0x6c, 0x4a, 0xd1, 0x61, 0xa5, 0x51, 0x9f, 0x2b,
+  0x66, 0x16, 0x88, 0x98, 0xb9, 0x3d, 0x63, 0x64, 0x44, 0x7a, 0xf0, 0x2c,
+  0xac, 0x9b, 0xd1, 0x8e, 0x40, 0xfd, 0xa3, 0xff, 0x26, 0xb6, 0x6a, 0x0c,
+  0x6a, 0x88, 0xfa, 0xa0, 0x7f, 0x6a, 0xe5, 0x9f, 0x5d, 0x2c, 0xf2, 0x1b,
+  0x59, 0x01, 0x31, 0x16, 0x6e, 0xe7, 0x63, 0xd5, 0x0b, 0x01, 0x8d, 0xf8,
+  0x14, 0x48, 0x98, 0x59, 0x4f, 0xc4, 0x80, 0x16, 0x3e, 0xf4, 0xae, 0xec,
+  0x56, 0xe2, 0x5b, 0x09, 0x05, 0x29, 0x51, 0x7f, 0xa5, 0x90, 0x9c, 0xdd,
+  0x58, 0x40, 0xc7, 0x8f, 0x25, 0x0f, 0xca, 0x15, 0xb8, 0xf5, 0x88, 0x57,
+  0xba, 0xd2, 0x64, 0x39, 0xdc, 0xbf, 0xce, 0x08, 0xc2, 0x39, 0xb7, 0xe5,
+  0xc2, 0x3f, 0xbb, 0x1d, 0x60, 0x39, 0xf3, 0x90, 0xa8, 0x12, 0x79, 0x52,
+  0x2d, 0x12, 0x1b, 0x73, 0xd4, 0xb0, 0x28, 0x66, 0xf4, 0x6b, 0x2c, 0xf4,
+  0x36, 0x09, 0x2a, 0x19, 0x5e, 0x38, 0xce, 0xc5, 0x0d, 0x6e, 0x18, 0x9c,
+  0x20, 0x5d, 0xe1, 0xdf, 0xc0, 0x06, 0x15, 0x97, 0x22, 0x5c, 0x6e, 0xfb,
+  0xa6, 0x26, 0x7b, 0x2d, 0x41, 0xd7, 0xc6, 0x70, 0x74, 0xb3, 0xa6, 0x02,
+  0x88, 0x05, 0x24, 0xd5, 0x55, 0x60, 0x9b, 0xa3, 0xff, 0x0b, 0x95, 0x20,
+  0x8c, 0x24, 0x42, 0x10, 0xb5, 0x96, 0xc7, 0x6d, 0x44, 0x16, 0x0a, 0xce,
+  0xd6, 0xbe, 0x35, 0x68, 0xa1, 0xa1, 0x09, 0x1c, 0x19, 0x56, 0xd6, 0xd7,
+  0xbf, 0xa9, 0xa0, 0x56, 0x61, 0x92, 0x21, 0x67, 0xda, 0xf2, 0xcd, 0x55,
+  0x00, 0x5f, 0x30, 0xe1, 0x1e, 0x2a, 0x68, 0x30, 0x08, 0x67, 0x72, 0x7d,
+  0x41, 0xcf, 0x13, 0x7a, 0x7d, 0xbc, 0x12, 0x67, 0xf7, 0x6b, 0x43, 0xd9,
+  0xc3, 0x03, 0xc1, 0x20, 0x87, 0x17, 0xf6, 0x68, 0xed, 0x61, 0x00, 0x30,
+  0xb2, 0xc1, 0xb4, 0xf6, 0x26, 0xd6, 0x58, 0x4e, 0x0d, 0x89, 0x5d, 0x11,
+  0xda, 0xd4, 0xf7, 0xd8, 0xa9, 0xa6, 0x1e, 0xf5, 0x35, 0x77, 0x5b, 0x10,
+  0xe3, 0x05, 0x8d, 0x47, 0x18, 0x62, 0x46, 0x8e, 0xb3, 0x32, 0xa6, 0xc2,
+  0xa0, 0x1a, 0xb6, 0x3b, 0x73, 0x15, 0xba, 0xb4, 0xda, 0x04, 0x09, 0x70,
+  0x15, 0xb1, 0xf4, 0x58, 0xde, 0xc1, 0x8b, 0x97, 0x53, 0x00, 0x52, 0xd3,
+  0x8d, 0x95, 0xa2, 0xa4, 0x43, 0x5e, 0x68, 0x2b, 0x12, 0xc5, 0xa6, 0x07,
+  0x91, 0x83, 0x40, 0xe6, 0xa4, 0xf2, 0x78, 0x3c, 0x58, 0x88, 0x12, 0x0c,
+  0x35, 0x65, 0x1c, 0xd0, 0xc2, 0x47, 0x4e, 0x77, 0xdb, 0x78, 0x17, 0x8f,
+  0xeb, 0xab, 0x49, 0xce, 0x6f, 0x64, 0xd6, 0xbf, 0xf9, 0x40, 0x8d, 0x89,
+  0x8d, 0x10, 0xb5, 0x48, 0xbb, 0x91, 0xab, 0x76, 0x77, 0x81, 0xb1, 0xae,
+  0xb1, 0x7b, 0xd7, 0x54, 0x35, 0xb8, 0x96, 0xad, 0xdb, 0x88, 0x3c, 0x5f,
+  0x97, 0x46, 0x2b, 0x2c, 0x30, 0xe9, 0xeb, 0xd0, 0x5d, 0xa5, 0x66, 0x60,
+  0xf0, 0x5d, 0x17, 0xa4, 0xd4, 0x4c, 0x2e, 0xf9, 0xe2, 0x9a, 0x83, 0xad,
+  0xc3, 0x3f, 0x75, 0x3f, 0x5a, 0xc6, 0x27, 0x21, 0x53, 0x68, 0x62, 0x00,
+  0x1b, 0x00, 0x58, 0x85, 0x06, 0x88, 0xba, 0x89, 0x7d, 0x3c, 0x05, 0x0e,
+  0xe1, 0x5c, 0xc9, 0x92, 0x70, 0x75, 0x56, 0x35, 0xdd, 0x15, 0x22, 0x1b,
+  0x0d, 0x65, 0x6b, 0x12, 0xbc, 0x65, 0x34, 0x93, 0x8a, 0xb0, 0x72, 0x2d,
+  0x58, 0x87, 0xc7, 0x2d, 0xbd, 0x15, 0x30, 0x67, 0xeb, 0x0b, 0x87, 0x02,
+  0xce, 0x27, 0x1d, 0x09, 0x43, 0xd4, 0x04, 0x97, 0x0d, 0x55, 0x4b, 0x9c,
+  0x0f, 0x03, 0x66, 0x25, 0xac, 0x15, 0x51, 0xa7, 0x7b, 0xb8, 0x6b, 0xfd,
+  0x6a, 0x33, 0x56, 0x8a, 0x91, 0x09, 0xba, 0xc7, 0x5b, 0xfe, 0x6a, 0x56,
+  0xa5, 0x5d, 0xbc, 0xb8, 0x8e, 0x0a, 0xa5, 0x77, 0x7a, 0xe1, 0x81, 0x7e,
+  0x48, 0x9a, 0xc7, 0x1a, 0xc8, 0x07, 0x50, 0x36, 0x3c, 0xdb, 0x90, 0xa2,
+  0x62, 0x3b, 0x50, 0xd4, 0x53, 0xcf, 0xee, 0x85, 0x61, 0x06, 0xe0, 0x9f,
+  0x28, 0x4f, 0x66, 0x0b, 0xf0, 0xd3, 0x16, 0x5c, 0xac, 0x4b, 0x31, 0x84,
+  0xf4, 0x8a, 0x32, 0x23, 0xa0, 0x59, 0xc5, 0xa8, 0x7b, 0x13, 0x9c, 0x7e,
+  0x33, 0x14, 0x8e, 0x22, 0x96, 0xf6, 0x69, 0xdd, 0xad, 0x11, 0xd5, 0x1b,
+  0x11, 0xa3, 0x24, 0x21, 0xf4, 0xa0, 0xc4, 0x05, 0xd7, 0x3f, 0xd1, 0xae,
+  0x1b, 0xeb, 0xcb, 0x87, 0xb4, 0xf9, 0x40, 0x87, 0x7d, 0xeb, 0x4a, 0xd6,
+  0x0e, 0x8d, 0x71, 0x46, 0x99, 0xf4, 0x47, 0xdf, 0xaa, 0xda, 0x65, 0xbe,
+  0x96, 0x60, 0x2d, 0xe5, 0xda, 0x42, 0x1a, 0xaf, 0x00, 0x31, 0x57, 0x9a,
+  0xe9, 0xf8, 0x79, 0x4e, 0xb9, 0x65, 0x28, 0x70, 0x63, 0xb5, 0xdd, 0xf4,
+  0xfc, 0x99, 0x72, 0xb1, 0x46, 0x77, 0xbe, 0x50, 0x96, 0x3c, 0xb8, 0x6f,
+  0x0d, 0x23, 0x80, 0xfe, 0xba, 0xcf, 0x47, 0xa8, 0xbb, 0x6e, 0x3f, 0xdf,
+  0xd6, 0x69, 0x17, 0x24, 0xbc, 0xc2, 0x5f, 0x47, 0xcc, 0x92, 0x18, 0xac,
+  0x6c, 0xde, 0x71, 0xca, 0xf4, 0x7a, 0xf2, 0xa4, 0x8f, 0x07, 0x7b, 0xde,
+  0x68, 0x6b, 0x8c, 0x6c, 0xe5, 0x2f, 0x07, 0x21, 0x02, 0xfc, 0x18, 0x5a,
+  0xcf, 0x54, 0x38, 0xfd, 0xd1, 0xd9, 0x67, 0x49, 0x26, 0xe4, 0xff, 0xa3,
+  0x45, 0x58, 0xb5, 0xf4, 0xdd, 0x19, 0x1f, 0xbb, 0xeb, 0xe4, 0xb5, 0x2b,
+  0x98, 0x7c, 0x79, 0xfd, 0x12, 0xc3, 0x14, 0x42, 0xd0, 0x42, 0x27, 0x4d,
+  0x97, 0xeb, 0xbd, 0x70, 0xd5, 0xf6, 0x6f, 0xfd, 0xb0, 0x39, 0xd0, 0x77,
+  0x74, 0x95, 0x52, 0x14, 0x60, 0xb6, 0x77, 0x32, 0xcd, 0xb8, 0xa0, 0x3d,
+  0x2b, 0xa1, 0xf6, 0xc3, 0xcb, 0x20, 0xcd, 0x1e, 0x83, 0x22, 0x3a, 0x54,
+  0x5a, 0x81, 0x0d, 0x8e, 0x24, 0xe5, 0xf3, 0x85, 0x0a, 0x2a, 0xce, 0xe3,
+  0x0a, 0x14, 0x1c, 0xfa, 0xdd, 0x84, 0x91, 0x56, 0x57, 0xd0, 0x0f, 0xa5,
+  0xa0, 0x41, 0xd0, 0xf6, 0x1d, 0xa9, 0x77, 0x69, 0xff, 0xa9, 0xb2, 0x59,
+  0x17, 0xf3, 0x0a, 0x1f, 0x21, 0x65, 0x2c, 0x87, 0xb8, 0x43, 0x09, 0x91,
+  0x09, 0xb8, 0x70, 0xbd, 0xbe, 0x65, 0xf2, 0x58, 0xe7, 0xb5, 0x45, 0x03,
+  0x81, 0x3d, 0x12, 0xe1, 0x02, 0x81, 0x81, 0x10, 0x45, 0x09, 0x97, 0xd3,
+  0xc2, 0x57, 0x4a, 0x11, 0xa7, 0x6e, 0x4f, 0xbf, 0xaf, 0x65, 0x0d, 0x2c,
+  0xcb, 0xdf, 0x7f, 0x19, 0xe4, 0x45, 0xa8, 0xb4, 0x3a, 0x42, 0x7e, 0xb9,
+  0xcc, 0x10, 0x75, 0x09, 0x88, 0x80, 0xb7, 0x5c, 0x21, 0xc4, 0x01, 0x0b,
+  0x9f, 0x22, 0xfa, 0x85, 0xde, 0xaf, 0x09, 0x7f, 0x13, 0x91, 0x4e, 0xf8,
+  0x2a, 0x5c, 0xdd, 0x95, 0xd0, 0x38, 0x86, 0x52, 0x56, 0x21, 0x92, 0xa5,
+  0x20, 0xd6, 0x91, 0x2b, 0x11, 0xd6, 0x43, 0x3d, 0x91, 0x33, 0xd6, 0xbd,
+  0xaf, 0xc8, 0x7a, 0x88, 0xb5, 0xe5, 0xbc, 0xc7, 0x95, 0xea, 0x06, 0x95,
+  0xcd, 0xff, 0x68, 0x3c, 0xad, 0x2e, 0x9e, 0x23, 0xa3, 0x04, 0xfe, 0xb7,
+  0x9b, 0xff, 0x8e, 0x39, 0xdb, 0xb9, 0x25, 0x3e, 0x9e, 0x97, 0x46, 0x6e,
+  0x93, 0x7c, 0xed, 0x21, 0x45, 0x96, 0x08, 0x16, 0x26, 0x32, 0x1e, 0x37,
+  0x8f, 0x71, 0x51, 0x41, 0x7b, 0xc7, 0x41, 0x41, 0x15, 0xae, 0xa0, 0xef,
+  0x70, 0x82, 0x72, 0xc8, 0x9c, 0x82, 0xf5, 0xa5, 0xf6, 0x94, 0xdf, 0x9a,
+  0xf3, 0x6f, 0x41, 0xc3, 0x22, 0x22, 0xc7, 0x7f, 0xb1, 0x95, 0x19, 0x6a,
+  0xb8, 0x18, 0xa4, 0x0e, 0xe9, 0x16, 0xeb, 0x83, 0x01, 0x00, 0x00, 0xfe,
+  0xf4, 0xce, 0xb3, 0xfe, 0x6e, 0xdf, 0x6c, 0xb4, 0x24, 0x98, 0x83, 0xde,
+  0x1e, 0x8a, 0x5c, 0x86, 0xd6, 0x32, 0xf7, 0x46, 0xb3, 0x8f, 0x78, 0xfa,
+  0xa8, 0x0f, 0xfd, 0xeb, 0xf7, 0xaf, 0xe8, 0x50, 0x2e, 0x82, 0x9d, 0xef,
+  0x37, 0x21, 0xfc, 0xbf, 0x58, 0xbf, 0x6b, 0x7a, 0xb4, 0xe9, 0xc7, 0x6b,
+  0xfb, 0xc2, 0x5f, 0x9e, 0xc4, 0xbc, 0x38, 0x54, 0xdf, 0x7b, 0x16, 0xf3,
+  0xea, 0x93, 0x3c, 0x5a, 0x7b, 0xaa, 0x19, 0xba, 0x0c, 0x91, 0xc7, 0x8c,
+  0xd0, 0x8c, 0x7f, 0x53, 0xfa, 0x93, 0xe2, 0xd5, 0x54, 0x45, 0xe4, 0x73,
+  0x6c, 0xf9, 0xaf, 0xbc, 0x84, 0x94, 0xe5, 0x51, 0xbc, 0xe1, 0xa3, 0x59,
+  0x8a, 0xbf, 0xe1, 0x53, 0x0f, 0xa0, 0xa1, 0xc0, 0xa3, 0x37, 0x08, 0x87,
+  0xa9, 0xb5, 0x5f, 0x42, 0x08, 0xaa, 0x6e, 0x27, 0x8b, 0xa4, 0x2b, 0x20,
+  0xe3, 0x35, 0xa9, 0xdd, 0x33, 0xfd, 0x10, 0x53, 0xac, 0x95, 0x82, 0xda,
+  0x79, 0xe4, 0xd8, 0xf6, 0x7b, 0x9e, 0xa4, 0xd2, 0xfb, 0xe0, 0x3a, 0x12,
+  0xac, 0x14, 0x2c, 0xf0, 0x7b, 0x11, 0x07, 0xa1, 0xfb, 0xb4, 0x5c, 0xdb,
+  0x32, 0xf8, 0xdd, 0xa9, 0xfe, 0x0e, 0x7d, 0xac, 0xcf, 0x5d, 0x83, 0x19,
+  0x05, 0xe6, 0xf5, 0xbf, 0x76, 0x6e, 0x09, 0xe5, 0x47, 0x96, 0xef, 0x40,
+  0x24, 0x56, 0xd1, 0x88, 0xaa, 0x00, 0x0d, 0x5a, 0x81, 0xf2, 0xd9, 0x81,
+  0x0b, 0x66, 0x66, 0x67, 0xcb, 0xcc, 0xa2, 0x1c, 0x74, 0x6c, 0x9d, 0x99,
+  0x20, 0x05, 0x95, 0x20, 0xa9, 0x35, 0x8f, 0xac, 0x57, 0x66, 0x7f, 0x14,
+  0x5c, 0x17, 0x6c, 0x2d, 0x66, 0x4f, 0x03, 0xd5, 0xf7, 0x04, 0x86, 0xcc,
+  0xf7, 0x94, 0xee, 0xec, 0x02, 0xd6, 0x0a, 0x58, 0xb6, 0x84, 0x14, 0x68,
+  0x60, 0xa1, 0xfb, 0xf5, 0xe2, 0x9b, 0x32, 0xb9, 0xcf, 0x70, 0xa2, 0x56,
+  0xa7, 0xaa, 0xc0, 0xbf, 0x09, 0x80, 0x75, 0xd9, 0xc5, 0xf4, 0x96, 0xa6,
+  0x40, 0xfa, 0x5f, 0x78, 0xbc, 0xbe, 0x54, 0x5e, 0xe0, 0x78, 0xec, 0xba,
+  0x96, 0xf0, 0x0a, 0x25, 0x65, 0x91, 0xbd, 0x9b, 0x0c, 0x32, 0x69, 0x20,
+  0x4c, 0x0e, 0xac, 0xfe, 0x41, 0x9b, 0x9c, 0xa7, 0xd3, 0xe8, 0x76, 0x7b,
+  0xdb, 0x68, 0xe9, 0x45, 0x59, 0x4c, 0xb5, 0x9d, 0xfd, 0x55, 0x10, 0xcc,
+  0x01, 0x6a, 0x3c, 0xd9, 0x18, 0xef, 0xb8, 0x00, 0xed, 0x2c, 0x2c, 0x2f,
+  0x3e, 0x2e, 0x1c, 0x75, 0x0e, 0x75, 0x37, 0x55, 0xe0, 0x33, 0xd5, 0x76,
+  0x3e, 0x9f, 0x3b, 0xe1, 0x2e, 0x6b, 0x52, 0xac, 0xb2, 0x28, 0x02, 0xfe,
+  0x01, 0xb1, 0x2a, 0xa6, 0xc2, 0xe9, 0x53, 0xee, 0x1e, 0x0a, 0xa6, 0xff,
+  0x2f, 0x73, 0xc0, 0x8f, 0x7d, 0x7e, 0x26, 0xd1, 0x2c, 0x1c, 0xbc, 0x3a,
+  0xab, 0x9e, 0x04, 0x82, 0xa6, 0x32, 0x90, 0x5c, 0xd1, 0xf7, 0x59, 0x78,
+  0x11, 0x9d, 0xe4, 0xd6, 0x18, 0x6b, 0xd1, 0xbf, 0x6f, 0x4c, 0x26, 0xbc,
+  0x91, 0x23, 0x30, 0x68, 0x40, 0x00, 0x2d, 0x3e, 0xea, 0xc4, 0xdc, 0xfd,
+  0xfc, 0xae, 0x48, 0x56, 0x9b, 0xf9, 0x7c, 0x18, 0x4a, 0x9e, 0xe8, 0x0a,
+  0x09, 0xf5, 0x7d, 0x4d, 0xab, 0x32, 0x11, 0xc1, 0x2f, 0x20, 0x8e, 0x8a,
+  0x59, 0x38, 0x8d, 0x35, 0xbf, 0xc1, 0xff, 0x75, 0x16, 0x44, 0x5a, 0x9f,
+  0x1f, 0x35, 0x93, 0xc3, 0x26, 0x00, 0xcb, 0x6d, 0x45, 0xf7, 0x76, 0x7d,
+  0xe6, 0x5c, 0xf2, 0x93, 0xe9, 0xb5, 0xd7, 0x6c, 0xe8, 0x60, 0x0c, 0x91,
+  0xc1, 0x54, 0x3a, 0x27, 0x54, 0xe0, 0x80, 0x66, 0x3b, 0xbc, 0x9e, 0x1d,
+  0xc4, 0x89, 0x67, 0x86, 0x45, 0x78, 0x92, 0x48, 0xe0, 0xc8, 0x3c, 0xd9,
+  0x9b, 0x86, 0x9f, 0x16, 0xb4, 0x39, 0xec, 0xe5, 0x3f, 0x75, 0xd3, 0xcb,
+  0xda, 0x36, 0x29, 0x5e, 0x76, 0x5b, 0x0b, 0xe6, 0x00, 0x48, 0x5b, 0xb0,
+  0xe7, 0xcd, 0xbd, 0xd3, 0xbc, 0x89, 0x72, 0x60, 0x49, 0xac, 0x1f, 0xb2,
+  0xc2, 0xeb, 0x1c, 0x4e, 0x73, 0x8a, 0x5d, 0xa1, 0xa2, 0xe2, 0xf4, 0xd7,
+  0x68, 0x91, 0xd4, 0x85, 0xb7, 0xf4, 0x6e, 0xd2, 0x79, 0xb7, 0x90, 0x32,
+  0x7f, 0x3c, 0x77, 0x91, 0x0c, 0x6e, 0xe5, 0x72, 0xad, 0xe8, 0xcb, 0xea,
+  0xfc, 0x4b, 0xf5, 0x71, 0x14, 0xc8, 0x7e, 0xfc, 0x45, 0x32, 0x57, 0x23,
+  0xb0, 0x40, 0x3f, 0x75, 0x98, 0x36, 0x68, 0x1d, 0x59, 0x0b, 0x30, 0x73,
+  0xb6, 0x74, 0x5f, 0xf6, 0x54, 0xa7, 0xdc, 0x61, 0x88, 0x25, 0xee, 0xbe,
+  0xb3, 0xa7, 0xeb, 0x10, 0xb3, 0x1b, 0x40, 0x82, 0x7a, 0x62, 0xc2, 0xbc,
+  0x05, 0xdf, 0x74, 0x3b, 0x54, 0x3b, 0x87, 0xa3, 0x04, 0x33, 0x3e, 0x21,
+  0x44, 0x12, 0x86, 0x3a, 0x75, 0x99, 0x04, 0xce, 0xbf, 0xa9, 0x68, 0xab,
+  0x14, 0x3a, 0x3a, 0xae, 0xe7, 0x9d, 0x12, 0x95, 0x89, 0x4d, 0x99, 0x09,
+  0x7c, 0xec, 0x21, 0x56, 0x23, 0x6d, 0x51, 0xe2, 0x60, 0x78, 0x1f, 0x70,
+  0x17, 0x01, 0xab, 0xc3, 0x71, 0x40, 0xde, 0x43, 0xd7, 0x70, 0xbe, 0x80,
+  0x62, 0xd8, 0x4d, 0x2f, 0x37, 0xd1, 0xcc, 0xc8, 0x7c, 0x31, 0x58, 0x19,
+  0x66, 0xfa, 0x48, 0x92, 0x41, 0x80, 0xcd, 0x2b, 0xef, 0xc2, 0xa7, 0xab,
+  0x7e, 0xd5, 0x13, 0x7f, 0xad, 0xca, 0x97, 0x92, 0x62, 0xf8, 0x96, 0x7a,
+  0x82, 0x52, 0xe1, 0x68, 0x7c, 0x3b, 0xe4, 0x1e, 0x1d, 0x84, 0xdb, 0x97,
+  0x25, 0xbf, 0x90, 0xa4, 0xbf, 0xd6, 0xc1, 0x26, 0xf8, 0xc7, 0x2e, 0xdd,
+  0xb5, 0x1f, 0x85, 0x73, 0xd4, 0xe4, 0x69, 0x86, 0xe1, 0x7e, 0xc3, 0x74,
+  0x71, 0xf5, 0x2f, 0x39, 0x49, 0xd2, 0x93, 0x58, 0x91, 0x68, 0xa5, 0x4d,
+  0xa2, 0x4b, 0xd9, 0x76, 0xfc, 0x45, 0xa6, 0x80, 0x0b, 0xda, 0x73, 0x7f,
+  0x54, 0xc1, 0xd7, 0x87, 0xe9, 0x19, 0x44, 0xdd, 0xff, 0xce, 0x82, 0x60,
+  0x26, 0xed, 0x45, 0xea, 0xb8, 0xf3, 0xd4, 0xb2, 0x10, 0x2f, 0x5b, 0x50,
+  0x00, 0xc0, 0xa3, 0xf6, 0xa0, 0x99, 0xba, 0xe2, 0x9d, 0xbf, 0x3f, 0xbb,
+  0x45, 0x9d, 0x34, 0x7f, 0x61, 0x1f, 0xe5, 0x90, 0xce, 0x88, 0xb6, 0xc0,
+  0x2a, 0x7b, 0x79, 0xab, 0xcd, 0x7b, 0x06, 0xb4, 0x5b, 0x58, 0x30, 0x55,
+  0xda, 0x08, 0x0b, 0xb7, 0x5b, 0x4d, 0x0d, 0x5f, 0x91, 0x80, 0xbf, 0x17,
+  0xbc, 0xba, 0x50, 0x91, 0x81, 0x62, 0x3f, 0xfe, 0xd9, 0x10, 0x13, 0x98,
+  0x8e, 0x70, 0x94, 0xdc, 0x89, 0xb8, 0x63, 0x31, 0xaf, 0xcd, 0x1a, 0x5d,
+  0xec, 0x60, 0x17, 0x28, 0xd2, 0xf0, 0xed, 0x8e, 0x10, 0x7c, 0xdf, 0x73,
+  0x92, 0xeb, 0x34, 0x5c, 0x02, 0x42, 0x52, 0x57, 0x6b, 0x90, 0xc4, 0x3b,
+  0x88, 0x8a, 0xe5, 0x88, 0xec, 0x28, 0x64, 0xb0, 0x0e, 0x04, 0x20, 0x83,
+  0x71, 0xc8, 0x03, 0x94, 0xe4, 0x3d, 0x52, 0x8d, 0x7d, 0xb9, 0xde, 0x7c,
+  0xa8, 0x41, 0x48, 0x27, 0xa8, 0xfc, 0xb3, 0x97, 0x68, 0xff, 0x1f, 0x9b,
+  0x2f, 0xf7, 0x01, 0x7d, 0x29, 0xd4, 0xed, 0x94, 0x62, 0x90, 0x0b, 0x79,
+  0x28, 0x28, 0xaa, 0x6b, 0x29, 0x78, 0xa4, 0x69, 0x42, 0x80, 0x0e, 0xc2,
+  0x96, 0x43, 0xad, 0xcd, 0x39, 0x07, 0x0a, 0x84, 0xd3, 0xb6, 0x73, 0x55,
+  0x76, 0xb9, 0x1f, 0xfb, 0xfc, 0xc9, 0xe7, 0x22, 0x17, 0x2f, 0x40, 0xc4,
+  0x4a, 0x8a, 0xcd, 0x88, 0x6a, 0x2d, 0x51, 0xa1, 0x16, 0x3a, 0x63, 0x65,
+  0x39, 0x40, 0xab, 0x6d, 0x98, 0xc0, 0xfd, 0x9d, 0xe7, 0x10, 0xc3, 0xc5,
+  0x29, 0x72, 0x93, 0x1c, 0x7d, 0xb4, 0x39, 0xc0, 0x21, 0x8c, 0x53, 0x47,
+  0xf5, 0xf2, 0x0a, 0xa2, 0xc6, 0x2f, 0x74, 0x1b, 0x22, 0xa8, 0x8a, 0x55,
+  0xd7, 0xa6, 0x7e, 0x39, 0xf6, 0xf1, 0xf1, 0x7b, 0xcb, 0x1e, 0xee, 0x6c,
+  0xd6, 0x5f, 0x5a, 0x26, 0x11, 0xba, 0x76, 0xb2, 0x34, 0xbb, 0xb3, 0x6d,
+  0x82, 0x5a, 0xf8, 0x7e, 0xf6, 0x45, 0x3c, 0x6c, 0x22, 0x1c, 0xb1, 0x6e,
+  0x31, 0x4b, 0xef, 0xed, 0x0e, 0x27, 0xb3, 0xb3, 0x95, 0x86, 0xdc, 0x40,
+  0x74, 0xf0, 0xd4, 0x7e, 0xff, 0x0d, 0x0b, 0x28, 0xc6, 0x59, 0x78, 0xb6,
+  0xfc, 0x00, 0x04, 0x34, 0x2b, 0x20, 0xe4, 0xb1, 0x43, 0x7a, 0xdd, 0x6f,
+  0xfb, 0x7e, 0x53, 0x8b, 0x73, 0x9c, 0xa1, 0x1c, 0xa7, 0x69, 0xe4, 0xfe,
+  0x2f, 0x1a, 0x2b, 0x6c, 0x70, 0x73, 0x6f, 0xa4, 0x5e, 0x99, 0x7d, 0x58,
+  0x37, 0x67, 0x07, 0x07, 0x30, 0x0d, 0x34, 0x96, 0xaa, 0x8e, 0xf9, 0x26,
+  0x3d, 0xf2, 0x83, 0x1f, 0xd8, 0xa2, 0x37, 0x5f, 0x8e, 0xe2, 0x9a, 0xd6,
+  0x97, 0xfc, 0xee, 0x39, 0x6a, 0xad, 0x0c, 0x7f, 0x4b, 0xc3, 0x95, 0xf6,
+  0x01, 0x90, 0x11, 0x7e, 0x85, 0x6d, 0x9c, 0xa9, 0x0c, 0xa7, 0x00, 0x55,
+  0xbc, 0x4f, 0x73, 0x80, 0x16, 0x58, 0x68, 0xd2, 0x89, 0xc1, 0xff, 0xa8,
+  0x00, 0xb9, 0xa3, 0x1b, 0x24, 0x31, 0x69, 0x8f, 0xfd, 0xa8, 0x0b, 0x3b,
+  0x59, 0x11, 0x55, 0xad, 0x82, 0xde, 0x75, 0x3a, 0xe1, 0x00, 0xb3, 0xcb,
+  0x94, 0x03, 0x48, 0x13, 0x13, 0x3c, 0xb7, 0x61, 0x86, 0x63, 0x2b, 0x86,
+  0x94, 0x53, 0xea, 0xd6, 0x62, 0x71, 0x8d, 0x24, 0xdc, 0x25, 0xb5, 0xd6,
+  0x9f, 0xb1, 0xf0, 0x46, 0xde, 0x7a, 0xb0, 0x06, 0x46, 0xf0, 0xe9, 0xc9,
+  0x5d, 0x38, 0xe5, 0xca, 0x7e, 0x67, 0x7c, 0x85, 0x17, 0x90, 0x05, 0x4f,
+  0xe3, 0x09, 0xd9, 0xbb, 0x2a, 0xab, 0x3f, 0x70, 0xbf, 0x62, 0x81, 0x35,
+  0x4d, 0xcd, 0xb3, 0xf4, 0x1c, 0x98, 0x35, 0x51, 0x85, 0x15, 0x71, 0xd5,
+  0x6f, 0x23, 0xf9, 0xff, 0xd1, 0xaf, 0x5f, 0x92, 0x64, 0xa7, 0x9a, 0xc9,
+  0x62, 0x28, 0xf3, 0x18, 0x43, 0x01, 0x76, 0x98, 0x60, 0x02, 0x79, 0x8d,
+  0xde, 0x8c, 0x1b, 0x69, 0xcd, 0x97, 0x01, 0x3b, 0x60, 0x84, 0x7a, 0xa6,
+  0xe8, 0x03, 0x9c, 0xb7, 0x64, 0x24, 0x77, 0x09, 0xcc, 0xb2, 0x64, 0x80,
+  0x69, 0x45, 0x97, 0x18, 0x73, 0x1a, 0x50, 0xb1, 0xa8, 0xb8, 0x41, 0xb2,
+  0x83, 0x52, 0x46, 0xca, 0x0e, 0xf0, 0x9a, 0xee, 0x40, 0x36, 0xab, 0x54,
+  0x23, 0xe4, 0x5c, 0xa1, 0x2d, 0x85, 0x78, 0x23, 0x81, 0x1c, 0xe1, 0x01,
+  0x48, 0xde, 0x3e, 0x10, 0x8b, 0xfb, 0x6a, 0x03, 0xee, 0xf0, 0xa6, 0xad,
+  0xeb, 0x78, 0x6a, 0xf6, 0xe9, 0xdd, 0x41, 0xdb, 0x7c, 0x97, 0xae, 0x1f,
+  0x55, 0x11, 0x46, 0xc6, 0xdc, 0x2d, 0x33, 0x38, 0x0f, 0xa9, 0x71, 0x00,
+  0xa8, 0x15, 0xbd, 0xa8, 0x9a, 0x17, 0xab, 0x1a, 0xe8, 0x70, 0x90, 0x03,
+  0xbc, 0x5f, 0x3b, 0xc9, 0x0e, 0x29, 0x80, 0xcd, 0x9c, 0xbd, 0x13, 0x62,
+  0x2b, 0xd1, 0x32, 0x3d, 0xd3, 0x0d, 0xe3, 0xfa, 0x75, 0x37, 0x9b, 0x91,
+  0xbb, 0xba, 0x1e, 0xe0, 0x08, 0xc8, 0xb6, 0x61, 0xc8, 0x3c, 0x47, 0x3d,
+  0xa9, 0xaf, 0xe5, 0x41, 0x89, 0xf8, 0x91, 0x7f, 0xac, 0xe3, 0xae, 0xee,
+  0xaf, 0xf1, 0x57, 0xec, 0x1b, 0x56, 0xd6, 0xc4, 0x3d, 0x00, 0xe7, 0x22,
+  0x47, 0x00, 0x56, 0xe3, 0x98, 0x29, 0xe2, 0x88, 0x58, 0x84, 0x32, 0x22,
+  0x9e, 0x9a, 0x79, 0xa8, 0x28, 0x78, 0x8e, 0x20, 0xbb, 0xf4, 0xf8, 0xdb,
+  0xc9, 0x50, 0x23, 0xe8, 0xe4, 0xbf, 0x91, 0xc6, 0x4a, 0xed, 0xfd, 0xe6,
+  0xae, 0x28, 0xc8, 0x47, 0x2d, 0x18, 0x55, 0xe6, 0xee, 0x3f, 0x4f, 0x04,
+  0x74, 0xf3, 0x62, 0xa5, 0xb2, 0xac, 0x1f, 0x19, 0xb0, 0xe1, 0xee, 0x8b,
+  0x0d, 0x48, 0xdf, 0x09, 0x11, 0x23, 0xf7, 0x54, 0x6a, 0xe2, 0xda, 0xe3,
+  0x37, 0x72, 0x83, 0x9b, 0x7a, 0xd9, 0x9a, 0x2c, 0x3d, 0xb7, 0xb0, 0xec,
+  0xbb, 0x06, 0x16, 0x39, 0x15, 0x53, 0x08, 0x80, 0x1a, 0xb3, 0xa3, 0x64,
+  0x38, 0x0a, 0xe5, 0x84, 0xe4, 0xa1, 0x34, 0x1a, 0xa0, 0x89, 0x4a, 0x22,
+  0x5a, 0x65, 0x40, 0x11, 0x4f, 0xb1, 0x40, 0xb3, 0xd9, 0x63, 0x42, 0x24,
+  0x0f, 0xfa, 0x32, 0xe7, 0x15, 0x84, 0x56, 0xf7, 0xd3, 0x4c, 0xc3, 0x2d,
+  0x2e, 0xee, 0x3f, 0xf4, 0x81, 0x18, 0x49, 0xc0, 0x02, 0x03, 0xf2, 0x74,
+  0x10, 0x20, 0x9b, 0xfc, 0xe4, 0xa4, 0xb8, 0xad, 0xa0, 0xa1, 0x14, 0x72,
+  0x56, 0xc0, 0x5a, 0x8a, 0x2f, 0x31, 0xf0, 0x7d, 0x42, 0x13, 0xde, 0x26,
+  0x29, 0xde, 0x98, 0x1c, 0x7c, 0x8e, 0xaa, 0xde, 0x7a, 0x25, 0xd2, 0x66,
+  0x04, 0x66, 0x4e, 0x1f, 0x1e, 0xc7, 0x00, 0x37, 0x3e, 0xca, 0xd0, 0x80,
+  0xe9, 0x5e, 0x98, 0xe1, 0x71, 0xb1, 0x0b, 0x56, 0x47, 0x74, 0xf7, 0x28,
+  0x3a, 0xe5, 0x76, 0x62, 0x5c, 0xac, 0x93, 0x0b, 0xad, 0x00, 0xde, 0x73,
+  0x00, 0x44, 0x2b, 0x89, 0x1c, 0xe0, 0x9f, 0xfb, 0xdd, 0xa6, 0x69, 0x15,
+  0x05, 0x9e, 0x55, 0x80, 0xfc, 0x8e, 0xf9, 0xf6, 0x08, 0xcc, 0x22, 0x5f,
+  0x07, 0x3d, 0x63, 0xbd, 0x3e, 0x35, 0x1b, 0x60, 0xd8, 0x7e, 0x0c, 0x4e,
+  0xa2, 0x7b, 0x7b, 0xc9, 0xed, 0x43, 0x50, 0x00, 0xbf, 0x6a, 0x83, 0xfd,
+  0xce, 0xac, 0xe6, 0x7d, 0x95, 0x24, 0x15, 0x18, 0x16, 0xee, 0xbb, 0x07,
+  0xbb, 0x75, 0xf9, 0x0b, 0x1a, 0x2e, 0x72, 0x6f, 0x80, 0x95, 0x70, 0x18,
+  0x4a, 0xc8, 0x2b, 0xf4, 0xfd, 0x85, 0xd8, 0x4c, 0xe4, 0x6b, 0x2e, 0xdc,
+  0x4b, 0x67, 0xe7, 0x4a, 0x71, 0xb5, 0x52, 0x52, 0x24, 0xf9, 0x1e, 0xea,
+  0x9f, 0x7c, 0x2d, 0x93, 0x86, 0x39, 0x93, 0x46, 0x41, 0x21, 0x59, 0x3a,
+  0x28, 0x2e, 0x3c, 0x0c, 0xf4, 0x9b, 0xa8, 0x17, 0xad, 0xb7, 0xa7, 0x21,
+  0x16, 0x36, 0x9e, 0x84, 0xd3, 0xc2, 0x73, 0xe1, 0x03, 0x9e, 0xf9, 0xbc,
+  0x82, 0x59, 0x81, 0x4a, 0x90, 0xf2, 0x68, 0x8a, 0x2b, 0x4d, 0xaa, 0x46,
+  0x65, 0x4e, 0xa4, 0x8c, 0x16, 0x09, 0xfb, 0x76, 0x0d, 0x13, 0x9c, 0xa2,
+  0xb7, 0xd6, 0x93, 0x07, 0x9e, 0xc6, 0xce, 0x3e, 0x88, 0xd6, 0xe0, 0x0e,
+  0x6b, 0x56, 0x30, 0x84, 0x40, 0xd9, 0x84, 0x3c, 0x4f, 0x79, 0xdc, 0xb1,
+  0x6f, 0x02, 0x88, 0x8c, 0x00, 0x4c, 0xd8, 0xdb, 0x83, 0x28, 0xb4, 0x85,
+  0xd7, 0xbd, 0x11, 0xa5, 0xe7, 0x3b, 0x25, 0x8b, 0x3d, 0xd6, 0xe7, 0x98,
+  0xc8, 0x04, 0xc7, 0x5f, 0x6a, 0xff, 0x55, 0x00, 0xff, 0x9a, 0x8a, 0xe2,
+  0xb2, 0x31, 0xb0, 0x2a, 0xe7, 0x0b, 0xa9, 0xa0, 0xcd, 0x17, 0xb6, 0x8d,
+  0x47, 0x3d, 0x8f, 0xfc, 0x75, 0x4f, 0x00, 0x4b, 0x3f, 0x32, 0xd7, 0x0e,
+  0x75, 0x36, 0xaa, 0x48, 0xfa, 0xdc, 0x33, 0x09, 0x45, 0x11, 0xba, 0x75,
+  0x0b, 0x8e, 0x0f, 0xfc, 0xf6, 0x1a, 0xa4, 0xef, 0x63, 0xb7, 0x08, 0x8b,
+  0x51, 0xa5, 0x40, 0x7d, 0x5b, 0x89, 0x96, 0x30, 0xa7, 0x12, 0x8a, 0x9c,
+  0xae, 0xd7, 0x55, 0xd1, 0x5f, 0xcd, 0xa6, 0x88, 0x18, 0x0a, 0xe6, 0x16,
+  0x2b, 0x5b, 0x15, 0x53, 0x5e, 0x9a, 0xf3, 0x07, 0x48, 0x66, 0xba, 0x3d,
+  0xce, 0xb0, 0xc1, 0xc3, 0xaa, 0x28, 0x5e, 0x63, 0xde, 0x73, 0xc6, 0x23,
+  0xcf, 0x72, 0xf5, 0xfd, 0x5a, 0xa0, 0x60, 0xcd, 0xcf, 0x79, 0x0b, 0x4d,
+  0xe1, 0xd3, 0x54, 0x22, 0x5b, 0x82, 0x6b, 0xf1, 0x54, 0xd1, 0x2d, 0x4a,
+  0x62, 0xb1, 0x9a, 0x3c, 0x65, 0xd6, 0xb5, 0x00, 0x9c, 0x20, 0x00, 0x00,
+  0x00, 0xe7, 0xcb, 0xcb, 0x4d, 0x79, 0x35, 0x5e, 0x11, 0x5b, 0x1f, 0x96,
+  0xf3, 0xa2, 0x1b, 0x17, 0x7a, 0x13, 0x99, 0x69, 0xac, 0x52, 0xb6, 0x0a,
+  0x94, 0xd8, 0xe7, 0xa9, 0xc4, 0x18, 0xfe, 0xde, 0x12, 0x15, 0x23, 0xba,
+  0x71, 0xf4, 0x84, 0x3a, 0xdf, 0x7f, 0x1a, 0x91, 0x30, 0x7a, 0xda, 0x42,
+  0xf7, 0xe9, 0xd9, 0xbe, 0x39, 0xbf, 0xcb, 0x74, 0xd5, 0x8b, 0x0e, 0x03,
+  0x09, 0x8c, 0x10, 0xa9, 0x62, 0x4d, 0x2a, 0xb0, 0x90, 0xd3, 0x74, 0xd7,
+  0x40, 0x23, 0x6c, 0x3c, 0x44, 0x49, 0x43, 0x7b, 0x64, 0x9d, 0x8d, 0xca,
+  0xea, 0x79, 0x01, 0x19, 0xd1, 0xde, 0xd0, 0x31, 0x5b, 0xf2, 0x20, 0xd9,
+  0x68, 0xaa, 0xd4, 0x97, 0x15, 0x82, 0xb2, 0xbd, 0x44, 0x60, 0x17, 0xbf,
+  0x7b, 0x33, 0x21, 0xcd, 0x8b, 0xd3, 0x3f, 0x86, 0x98, 0x83, 0x0c, 0x14,
+  0x80, 0x04, 0xf7, 0xd2, 0x06, 0x80, 0x17, 0x55, 0xb9, 0x36, 0x53, 0x01,
+  0x07, 0xe7, 0xa4, 0xda, 0x7b, 0x64, 0x01, 0xb5, 0xba, 0x01, 0x3e, 0x38,
+  0xd2, 0x18, 0xf3, 0xd0, 0x4d, 0xc9, 0x68, 0x9f, 0x5d, 0x8f, 0x0b, 0xbb,
+  0xdd, 0x9a, 0x6a, 0xfe, 0xcc, 0xca, 0x3c, 0x56, 0x70, 0x28, 0x69, 0xe1,
+  0x20, 0xfa, 0x5d, 0xd4, 0xe8, 0x02, 0xac, 0x61, 0x96, 0x80, 0x9a, 0xc0,
+  0x18, 0xef, 0xda, 0xa5, 0xc7, 0xe0, 0xf9, 0x38, 0x3b, 0xf9, 0x4d, 0x3e,
+  0x56, 0xb7, 0x80, 0x4a, 0x95, 0x37, 0x9b, 0xec, 0x2d, 0xac, 0x38, 0x81,
+  0x57, 0xe9, 0x4e, 0x99, 0xe9, 0x9a, 0x7f, 0x93, 0xa9, 0x78, 0x30, 0x11,
+  0x56, 0xa0, 0x71, 0x94, 0x00, 0x00, 0x87, 0xc9, 0xf1, 0x1d, 0xc8, 0x2e,
+  0xe9, 0x68, 0x27, 0x4d, 0x95, 0x73, 0x14, 0x3f, 0x89, 0xe6, 0x7f, 0xde,
+  0xb0, 0x4b, 0xb8, 0x60, 0x4c, 0x74, 0x89, 0x8b, 0x19, 0x5e, 0x9e, 0xdc,
+  0x23, 0xae, 0xf8, 0xdf, 0x40, 0xf5, 0x64, 0xdf, 0x0e, 0x1f, 0x42, 0xae,
+  0x6e, 0xed, 0x0a, 0x46, 0x6e, 0xa6, 0xe2, 0x5f, 0xfe, 0x1d, 0xb1, 0x65,
+  0x4c, 0x93, 0x27, 0x0c, 0x84, 0x21, 0x92, 0x5b, 0x68, 0x35, 0xba, 0x03,
+  0x44, 0xd2, 0x91, 0xa1, 0xd0, 0x60, 0xe0, 0x9b, 0xe7, 0x5b, 0x32, 0x36,
+  0xf6, 0xac, 0x62, 0xa3, 0x0d, 0xa3, 0xc3, 0x45, 0xa4, 0x30, 0xff, 0x25,
+  0xc9, 0x2c, 0x54, 0x01, 0xa3, 0x9f, 0x75, 0x46, 0xfc, 0xe1, 0xae, 0xec,
+  0x49, 0x44, 0x96, 0x5f, 0x58, 0x84, 0xa2, 0x9c, 0xe6, 0x52, 0x89, 0x45,
+  0x70, 0x52, 0xe9, 0x78, 0xf9, 0x40, 0x05, 0xf7, 0x93, 0x1d, 0x67, 0xa5,
+  0xa3, 0x90, 0x74, 0x56, 0xdf, 0xaa, 0xdc, 0xe9, 0x11, 0x78, 0x3e, 0x8d,
+  0xb5, 0xd5, 0x40, 0xf0, 0xba, 0x2f, 0xb3, 0xbe, 0x62, 0xbc, 0x48, 0xb9,
+  0x8c, 0xf5, 0x67, 0x4d, 0x5f, 0x7b, 0x69, 0x29, 0xb8, 0x08, 0x62, 0x21,
+  0xd7, 0x35, 0x31, 0x0e, 0x9f, 0x89, 0x65, 0xe9, 0x31, 0x71, 0x33, 0xf4,
+  0x92, 0x02, 0x9d, 0xef, 0x5c, 0x83, 0xa5, 0x75, 0x32, 0xee, 0xf9, 0xc4,
+  0xe4, 0x6e, 0x09, 0x20, 0x84, 0x37, 0x5b, 0x24, 0x00, 0xf9, 0x78, 0xae,
+  0x09, 0x16, 0x5e, 0x42, 0x9a, 0x11, 0xe4, 0x73, 0x5b, 0xe5, 0x50, 0x01,
+  0x0d, 0x8c, 0x62, 0x6a, 0xa8, 0x56, 0xdf, 0xb3, 0xae, 0x8f, 0x07, 0x2f,
+  0xc2, 0x47, 0xb1, 0x46, 0xde, 0x30, 0xc0, 0xea, 0x7a, 0xd2, 0xd6, 0xc9,
+  0x82, 0x02, 0x16, 0xac, 0x5c, 0xb0, 0x0b, 0x16, 0xdf, 0x77, 0x7e, 0x69,
+  0x09, 0x80, 0x83, 0x86, 0x79, 0x45, 0x54, 0x59, 0xf7, 0x7e, 0xbb, 0xa3,
+  0xf0, 0xb9, 0x7e, 0x9f, 0x5a, 0x3b, 0xa6, 0x1c, 0x67, 0x05, 0x26, 0xec,
+  0xc1, 0x2f, 0xb0, 0x7e, 0xfa, 0x2f, 0x65, 0xe5, 0xd8, 0xbb, 0x50, 0xab,
+  0x30, 0x11, 0x05, 0xba, 0x58, 0x96, 0xaf, 0x55, 0xe0, 0xf7, 0x97, 0xc1,
+  0xc7, 0x4d, 0x0b, 0x68, 0x16, 0x13, 0x01, 0xa9, 0xf9, 0xc0, 0x3d, 0xa3,
+  0x16, 0x70, 0x7f, 0x83, 0x8c, 0x7b, 0xaf, 0x1a, 0x25, 0xb4, 0xce, 0xdd,
+  0xaf, 0xe6, 0xd3, 0xf9, 0x5f, 0xaf, 0x28, 0xeb, 0xcc, 0x65, 0xb6, 0xc5,
+  0x4f, 0x30, 0x57, 0x34, 0xa0, 0x64, 0x0a, 0x25, 0x0a, 0x4b, 0x12, 0x11,
+  0x37, 0xd4, 0x41, 0x6c, 0x76, 0x10, 0x98, 0x8a, 0x18, 0x63, 0xf9, 0xcf,
+  0x56, 0xa5, 0xd2, 0xe8, 0x1b, 0x6d, 0x5e, 0x3e, 0xb6, 0x99, 0x05, 0xe7,
+  0x47, 0x33, 0x1d, 0x00, 0x54, 0xbd, 0xd0, 0x29, 0xe9, 0x33, 0x79, 0x75,
+  0xb5, 0xdc, 0x6d, 0xe1, 0x99, 0x49, 0xdb, 0x9e, 0x1a, 0x60, 0xf1, 0x07,
+  0xf5, 0xf8, 0x79, 0xdb, 0x32, 0x2e, 0x20, 0xe2, 0x4f, 0xd3, 0x36, 0x65,
+  0xc8, 0xfe, 0x5d, 0xff, 0x23, 0xf6, 0xe3, 0x60, 0x18, 0x3b, 0xcd, 0xbb,
+  0xba, 0x20, 0x40, 0x99, 0x6f, 0x03, 0x58, 0xc5, 0x9d, 0x07, 0xa0, 0xbc,
+  0xe3, 0xb4, 0xb7, 0x86, 0x08, 0x4e, 0x0a, 0xaf, 0x50, 0x10, 0x9e, 0x82,
+  0xf8, 0xfc, 0x2b, 0xe5, 0xb3, 0x40, 0xa1, 0xea, 0xd7, 0x54, 0x0a, 0x3d,
+  0x33, 0xe9, 0x73, 0x08, 0xe2, 0xcf, 0x52, 0x47, 0x21, 0x4a, 0xda, 0x5f,
+  0x50, 0xf8, 0x84, 0xd5, 0xfc, 0x06, 0x59, 0x50, 0x4a, 0xb5, 0xe4, 0xb7,
+  0xdb, 0x10, 0xa1, 0xaf, 0x88, 0x64, 0xdc, 0xfd, 0xeb, 0xd0, 0x23, 0x85,
+  0xaa, 0x64, 0x68, 0x81, 0xf2, 0x97, 0xb8, 0x97, 0x63, 0xe1, 0x7c, 0x91,
+  0x81, 0x02, 0xa9, 0xcd, 0x01, 0xa8, 0x08, 0xbb, 0x09, 0x58, 0x13, 0x6a,
+  0x34, 0x27, 0xdb, 0xc1, 0xa8, 0x97, 0xed, 0x3c, 0x9f, 0x6e, 0xf3, 0x84,
+  0x8a, 0x81, 0x6d, 0x59, 0x85, 0x5a, 0x5e, 0x5a, 0x8e, 0x2a, 0x11, 0xdd,
+  0x96, 0x8c, 0xa4, 0xe1, 0x9d, 0xa9, 0xc0, 0x12, 0x59, 0x22, 0x3a, 0xfa,
+  0xbb, 0xfa, 0x20, 0x31, 0x50, 0x68, 0x1c, 0x8d, 0xd4, 0xaf, 0x8f, 0x80,
+  0xd6, 0x39, 0x64, 0xfa, 0x0a, 0x79, 0xe0, 0x5b, 0x97, 0xc8, 0x02, 0x9e,
+  0x75, 0x06, 0x3f, 0x39, 0xa6, 0x06, 0x57, 0x4b, 0x59, 0x10, 0xa5, 0x55,
+  0xb7, 0xe8, 0xd5, 0x68, 0xeb, 0x9a, 0x65, 0xaa, 0x0b, 0xdc, 0x69, 0x58,
+  0x30, 0xda, 0xeb, 0x3f, 0x42, 0xbb, 0xea, 0x2a, 0xea, 0x4f, 0x2e, 0x86,
+  0x4e, 0x21, 0xde, 0x06, 0xe6, 0x16, 0x71, 0x13, 0x86, 0x61, 0xe9, 0x36,
+  0x6a, 0xf0, 0x5b, 0xc9, 0x93, 0x29, 0x75, 0x1e, 0x21, 0x8f, 0x01, 0x8c,
+  0x59, 0x22, 0x41, 0xd1, 0x4b, 0xf3, 0x13, 0x77, 0x88, 0x88, 0x5e, 0x05,
+  0x53, 0x26, 0x65, 0x1c, 0x38, 0x83, 0x50, 0x01, 0xed, 0x06, 0x94, 0x9a,
+  0xa6, 0x8b, 0x96, 0xfe, 0xed, 0xbb, 0x3f, 0xa3, 0x1e, 0x37, 0x27, 0xb5,
+  0x79, 0x4a, 0x68, 0x2c, 0x98, 0x8f, 0xf1, 0xde, 0x5d, 0x69, 0xf6, 0xad,
+  0x08, 0x6d, 0xdd, 0x0c, 0xc7, 0xfa, 0xe5, 0x65, 0xd2, 0x5f, 0xa8, 0x1c,
+  0x98, 0x03, 0x94, 0xc0, 0x91, 0x9d, 0x28, 0x14, 0x57, 0x48, 0x04, 0x94,
+  0xff, 0x03, 0xb0, 0x4e, 0x39, 0x2b, 0xe6, 0x0b, 0x7c, 0xaf, 0x47, 0x90,
+  0x84, 0x46, 0xae, 0x50, 0xcf, 0x3a, 0xa3, 0xb1, 0x4d, 0xb0, 0xa5, 0x73,
+  0xe8, 0x15, 0x08, 0x4c, 0x63, 0xbf, 0x3b, 0x62, 0x99, 0x30, 0xb1, 0xbf,
+  0xf2, 0x58, 0x7b, 0x0b, 0x2e, 0xcb, 0x90, 0x1b, 0x33, 0xf7, 0xf0, 0x57,
+  0xf1, 0x53, 0xa6, 0xef, 0xca, 0xaa, 0xf4, 0x15, 0xed, 0xad, 0xc2, 0xe3,
+  0x61, 0xfa, 0x03, 0x1c, 0xb0, 0x63, 0xd2, 0x10, 0x7a, 0x4a, 0x5f, 0x72,
+  0xa1, 0x45, 0x3a, 0xa4, 0x53, 0x24, 0xe4, 0xcd, 0xbb, 0x46, 0x49, 0xd7,
+  0xf5, 0x3b, 0x06, 0xf2, 0xe1, 0x61, 0x86, 0x7e, 0x0e, 0xe1, 0x13, 0x69,
+  0xee, 0x46, 0x95, 0x6a, 0xb6, 0x86, 0xd0, 0x24, 0x8e, 0x70, 0x86, 0x2e,
+  0x24, 0xcd, 0xc5, 0x28, 0x0c, 0x2a, 0xf5, 0x01, 0x13, 0xd3, 0x11, 0xcb,
+  0x4d, 0xe5, 0xef, 0xbd, 0x97, 0x49, 0xa0, 0x62, 0x2b, 0xbb, 0xf0, 0x2e,
+  0x38, 0xf0, 0x7b, 0xc7, 0xee, 0x19, 0xaa, 0x9a, 0x91, 0xf5, 0xeb, 0xa5,
+  0x8e, 0x26, 0xae, 0xb7, 0x79, 0x2b, 0xe4, 0x8c, 0xc3, 0xd9, 0x3c, 0x76,
+  0x14, 0x26, 0x1f, 0x07, 0xe6, 0x5d, 0xc5, 0x03, 0x5b, 0xa8, 0x34, 0x8c,
+  0x7e, 0xec, 0x45, 0xe1, 0x48, 0xe1, 0x89, 0xe8, 0x93, 0xb6, 0x71, 0x47,
+  0x6e, 0x95, 0x04, 0x67, 0xc9, 0xdc, 0x39, 0xa9, 0xf6, 0x0c, 0x94, 0x2c,
+  0xa5, 0x48, 0x43, 0x98, 0x3b, 0x61, 0x0b, 0x1d, 0x09, 0x4d, 0xa3, 0x04,
+  0x71, 0x6d, 0x0d, 0x5c, 0xdd, 0x79, 0x37, 0x90, 0xf5, 0x15, 0x89, 0x0e,
+  0x32, 0x82, 0x0e, 0x8f, 0x4c, 0x6f, 0xc6, 0x5b, 0x83, 0x0f, 0xb4, 0xe2,
+  0x1b, 0xfc, 0xd7, 0x4b, 0xc6, 0x25, 0xb8, 0x5f, 0xb6, 0xad, 0x60, 0xe9,
+  0x18, 0xc1, 0x03, 0x68, 0x87, 0xed, 0x80, 0x92, 0xc9, 0x53, 0xe2, 0xc1,
+  0x1b, 0x2d, 0xbd, 0xc1, 0xf5, 0x70, 0xae, 0xb6, 0x6c, 0x69, 0x74, 0x3b,
+  0x1b, 0xba, 0xa0, 0x3e, 0x7e, 0x7a, 0xad, 0xff, 0x6f, 0x8d, 0x50, 0xd5,
+  0xd2, 0x3a, 0x35, 0xbb, 0x4e, 0x48, 0x3e, 0x91, 0x7b, 0xe9, 0xf9, 0x7f,
+  0x2d, 0xd2, 0x14, 0xb7, 0xc4, 0x9a, 0x45, 0x1e, 0x76, 0x37, 0x1c, 0x67,
+  0x37, 0x11, 0x31, 0x75, 0xf1, 0x59, 0x6f, 0x56, 0x46, 0xa9, 0xf4, 0x07,
+  0xdb, 0xda, 0x3f, 0x6f, 0xfb, 0x30, 0x47, 0x43, 0x5b, 0x94, 0x2d, 0x2c,
+  0x97, 0x97, 0x8d, 0xc9, 0x2a, 0x4e, 0xd4, 0x11, 0xa9, 0xcc, 0xfb, 0xfb,
+  0x9a, 0x5a, 0xee, 0x39, 0xc7, 0xe6, 0x96, 0x61, 0xea, 0xfb, 0x60, 0x06,
+  0xb8, 0xd8, 0x4a, 0xcc, 0xc3, 0x46, 0x9f, 0xe1, 0x8c, 0x1c, 0xdd, 0x71,
+  0x9e, 0xc0, 0x5d, 0x96, 0x80, 0x8e, 0xc3, 0x19, 0x4c, 0x0e, 0xde, 0x2f,
+  0xe8, 0x81, 0x11, 0xbe, 0x33, 0x72, 0x7b, 0x17, 0x76, 0x17, 0x21, 0x39,
+  0x51, 0xdc, 0xe1, 0x08, 0x76, 0x22, 0x72, 0xc7, 0x19, 0x42, 0x84, 0xce,
+  0x15, 0x46, 0xaf, 0x98, 0xa4, 0x0d, 0x77, 0xc0, 0x85, 0x7e, 0x0e, 0xe6,
+  0xdc, 0x32, 0x05, 0x92, 0x33, 0x9d, 0xbc, 0x9c, 0x65, 0x77, 0x6b, 0x8c,
+  0x5f, 0x1f, 0x56, 0xef, 0xac, 0x59, 0xb2, 0x65, 0x46, 0x89, 0xb6, 0x53,
+  0x86, 0x84, 0x37, 0x1d, 0x24, 0xc8, 0x8c, 0xc1, 0xb0, 0x7b, 0xdf, 0xef,
+  0xc6, 0x54, 0x7c, 0xcc, 0x2d, 0xc1, 0x5d, 0x1b, 0x2a, 0x41, 0x08, 0x7e,
+  0x49, 0x41, 0x7d, 0x03, 0x0b, 0xc2, 0x77, 0x83, 0x3e, 0x3e, 0xce, 0x4d,
+  0x6f, 0x0a, 0x4a, 0x1e, 0x90, 0x6c, 0x9d, 0x3f, 0xd9, 0xdb, 0x33, 0x35,
+  0x6c, 0x47, 0x00, 0x00
 };
 };
 
 
-void main(string[] args) {
-    int port = args.length > 1 ? int.parse(args[1]) : 8080;
-    
-    // Get the path to the currently running binary (argv[0])
-    string binary_path = args[0];
-    
-    // Resolve to absolute path if needed
-    var binary_file = File.new_for_path(binary_path);
-    if (!binary_file.is_native() || !Path.is_absolute(binary_path)) {
-        binary_path = binary_file.get_path();
-    }
-    
-    print("╔══════════════════════════════════════════════════════════════╗\n");
-    print("║                Astralis FastResources Example                ║\n");
-    print("╠══════════════════════════════════════════════════════════════╣\n");
-    print(@"║  Port: $port");
-    for (int i = 0; i < 50 - port.to_string().length - 7; i++) print(" ");
-    print(" ║\n");
-    print("╠══════════════════════════════════════════════════════════════╣\n");
-    print("║  Endpoints:                                                  ║\n");
-    print("║    /           - Home page (from_string)                     ║\n");
-    print("║    /pixel.png  - 1x1 transparent pixel (from_byte_array)     ║\n");
-    print("║    /binary     - This executable (filesystem load)           ║\n");
-    print("╠══════════════════════════════════════════════════════════════╣\n");
-    print(@"║  Binary: $(binary_path)");
-    int path_len = binary_path.length;
-    if (path_len < 54) {
-        for (int i = 0; i < 54 - path_len; i++) print(" ");
-    }
-    print(" ║\n");
-    print("╚══════════════════════════════════════════════════════════════╝\n");
-    print("\nPress Ctrl+C to stop the server\n\n");
-    
-    // Create endpoints
-    try {
-        // 1. Home page using FastResource.from_string
-        // This is ideal for serving static HTML, CSS, or other text content
-        // that you want to embed directly in your application
-        var home_page = new FastResource.from_string("/", """
+private const string HOME_PAGE_HTML = """
 <!DOCTYPE html>
 <!DOCTYPE html>
 <html lang="en">
 <html lang="en">
 <head>
 <head>
@@ -139,8 +502,8 @@ void main(string[] args) {
         
         
         <div class="endpoint">
         <div class="endpoint">
             <span class="method">GET</span>
             <span class="method">GET</span>
-            <a href="/pixel.png">/pixel.png</a>
-            <span style="margin-left: auto; color: #666;">1x1 pixel image (from_byte_array)</span>
+            <a href="/cat.webp">/cat.webp</a>
+            <span style="margin-left: auto; color: #666;">Cat image (from_byte_array)</span>
         </div>
         </div>
         
         
         <div class="endpoint">
         <div class="endpoint">
@@ -153,44 +516,82 @@ void main(string[] args) {
     <div class="card">
     <div class="card">
         <h2>FastResource Constructors</h2>
         <h2>FastResource Constructors</h2>
         <ul>
         <ul>
-            <li><code>FastResource.from_string(route, content)</code> - Load from string</li>
-            <li><code>FastResource.from_byte_array(route, bytes)</code> - Load from byte array</li>
-            <li><code>FastResource(route, path)</code> - Load from filesystem</li>
+            <li><code>FastResource.from_string(content)</code> - Load from string</li>
+            <li><code>FastResource.from_byte_array(bytes)</code> - Load from byte array</li>
+            <li><code>FastResource(path)</code> - Load from filesystem</li>
         </ul>
         </ul>
     </div>
     </div>
 </body>
 </body>
 </html>
 </html>
-""").with_content_type("text/html; charset=utf-8").with_default_compressors();
+""";
+
+void main(string[] args) {
+    int port = args.length > 1 ? int.parse(args[1]) : 8080;
+    
+    // Get the path to the currently running binary (argv[0])
+    string binary_path = args[0];
+    
+    // Resolve to absolute path if needed
+    var binary_file = File.new_for_path(binary_path);
+    if (!binary_file.is_native() || !Path.is_absolute(binary_path)) {
+        binary_path = binary_file.get_path();
+    }
+    
+    print("╔══════════════════════════════════════════════════════════════╗\n");
+    print("║                Astralis FastResources Example                ║\n");
+    print("╠══════════════════════════════════════════════════════════════╣\n");
+    print(@"║  Port: $port");
+    for (int i = 0; i < 50 - port.to_string().length - 7; i++) print(" ");
+    print(" ║\n");
+    print("╠══════════════════════════════════════════════════════════════╣\n");
+    print("║  Endpoints:                                                  ║\n");
+    print("║    /           - Home page (from_string)                     ║\n");
+    print("║    /pixel.png  - 1x1 transparent pixel (from_byte_array)     ║\n");
+    print("║    /binary     - This executable (filesystem load)           ║\n");
+    print("╠══════════════════════════════════════════════════════════════╣\n");
+    print(@"║  Binary: $(binary_path)");
+    int path_len = binary_path.length;
+    if (path_len < 54) {
+        for (int i = 0; i < 54 - path_len; i++) print(" ");
+    }
+    print(" ║\n");
+    print("╚══════════════════════════════════════════════════════════════╝\n");
+    print("\nPress Ctrl+C to stop the server\n\n");
+    
+    // Create endpoints
+    try {
+        var application = new WebApplication(port);
+        
+        // 1. Home page using FastResource.from_string
+        // Register as singleton - the factory creates the instance and the container caches it
+        application.add_static_endpoint<FastResource>(new EndpointRoute("/"), () => {
+            try {
+                return new FastResource.from_string(HOME_PAGE_HTML)
+                    .with_content_type("text/html; charset=utf-8")
+                    .with_default_compressors();
+            } catch (Error e) {
+                error("Failed to create home page resource: %s", e.message);
+            }
+        });
         
         
         // 2. Image using FastResource.from_byte_array
         // 2. Image using FastResource.from_byte_array
-        // This is ideal for serving binary content like images, fonts, or other
-        // assets that you want to embed directly in your application binary
-        var pixel_image = new FastResource.from_byte_array("/pixel.png", TRANSPARENT_PIXEL)
-            .with_content_type("image/png")
-            .with_default_compressors();
+        application.add_static_endpoint<FastResource>(new EndpointRoute("/cat.webp"), () => {
+            try {
+                return new FastResource.from_byte_array(CAT_PHOTO)
+                    .with_content_type("image/webp")
+                    .with_default_compressors();
+            } catch (Error e) {
+                error("Failed to create cat image resource: %s", e.message);
+            }
+        });
         
         
         // 3. Running binary using FastResource default constructor
         // 3. Running binary using FastResource default constructor
-        // This loads a file from the filesystem at startup
-        // Using argv[0] to serve the currently running executable
-        var binary_endpoint = new FastResource("/binary", binary_path)
-            .with_content_type("application/octet-stream")
-            .with_default_compressors();
-        
-        // Set up the router with all endpoints
-        var router = new EndpointRouter()
-            .add_endpoint(home_page)
-            .add_endpoint(pixel_image)
-            .add_endpoint(binary_endpoint);
-        
-        // Build the pipeline
-        var pipeline = new Pipeline()
-            .add_component(router);
-        
-        // Create and configure the server
-        var server = new Server(port, pipeline);
+        application.add_static_endpoint<FastResource>(new EndpointRoute("/binary"), () => new FastResource(binary_path)
+                    .with_content_type("application/octet-stream")
+                    .with_default_compressors());
         
         
         // Run the server
         // Run the server
-        server.run();
+        application.run();
         
         
     } catch (Error e) {
     } catch (Error e) {
         printerr("Error: %s\n", e.message);
         printerr("Error: %s\n", e.message);

+ 19 - 26
examples/FileServer.vala

@@ -25,10 +25,7 @@ class ServerInfoEndpoint : Object, Endpoint {
         this.port = port;
         this.port = port;
     }
     }
     
     
-    public string route { get { return "/__server_info"; } }
-    public Method[] methods { owned get { return new Method[] { Method.GET }; } }
-    
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var info = @"Astralis File Server
         var info = @"Astralis File Server
         
         
 Serving: $served_directory
 Serving: $served_directory
@@ -97,27 +94,6 @@ void main(string[] args) {
         Process.exit(1);
         Process.exit(1);
     }
     }
     
     
-    // Set up the router with file serving and server info endpoints
-    var router = new EndpointRouter()
-        .add_endpoint(new ServerInfoEndpoint(absolute_path, port))
-        .add_endpoint(file_resource);
-    
-    // Create compression pipeline components
-    // Order matters: we try brotli first (best compression), then zstd, then gzip (most compatible)
-    var brotli = new BrotliCompressor();
-    var zstd = new ZstdCompressor();
-    var gzip = new GzipCompressor();
-    
-    // Build the pipeline with compression support
-    var pipeline = new Pipeline()
-        .add_component(gzip)
-        .add_component(zstd)
-        .add_component(brotli)
-        .add_component(router);
-    
-    // Create and configure the server
-    var server = new Server(port, pipeline);
-    
     // Print startup information
     // Print startup information
     print("╔══════════════════════════════════════════════════════════════╗\n");
     print("╔══════════════════════════════════════════════════════════════╗\n");
     print("║                  Astralis File Server                        ║\n");
     print("║                  Astralis File Server                        ║\n");
@@ -143,6 +119,23 @@ void main(string[] args) {
     print("╚══════════════════════════════════════════════════════════════╝\n");
     print("╚══════════════════════════════════════════════════════════════╝\n");
     print("\nPress Ctrl+C to stop the server\n\n");
     print("\nPress Ctrl+C to stop the server\n\n");
     
     
+    // Create the application and register endpoints
+    var application = new WebApplication(port);
+    
+    // Register compression components
+    application.container.register_singleton<GzipCompressor>(() => new GzipCompressor());
+    application.container.register_singleton<ZstdCompressor>(() => new ZstdCompressor());
+    application.container.register_singleton<BrotliCompressor>(() => new BrotliCompressor());
+    
+    // Register server info endpoint
+    application.container.register_scoped<Endpoint>(() => new ServerInfoEndpoint(absolute_path, port))
+        .with_metadata<EndpointRoute>(new EndpointRoute("/__server_info"));
+    
+    // Register filesystem resource endpoint as singleton (holds configuration)
+    // The factory creates the instance and the container caches it
+    application.container.register_singleton<Endpoint>(() => file_resource)
+        .with_metadata<EndpointRoute>(new EndpointRoute("/**"));
+    
     // Run the server
     // Run the server
-    server.run();
+    application.run();
 }
 }

+ 31 - 34
examples/FormData.vala

@@ -11,10 +11,7 @@ using Invercargill.DataStructures;
 
 
 // HTML form page handler
 // HTML form page handler
 class FormPageEndpoint : Object, Endpoint {
 class FormPageEndpoint : Object, Endpoint {
-    public string route { get { return "/"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-
-    public async HttpResult handle_request(HttpContext context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext context, RouteContext route) throws Error {
         
         
         var res = new HttpStringResult("""<!DOCTYPE html>
         var res = new HttpStringResult("""<!DOCTYPE html>
 <html>
 <html>
@@ -130,9 +127,7 @@ class FormPageEndpoint : Object, Endpoint {
 
 
 // Simple form submission handler
 // Simple form submission handler
 class SimpleFormEndpoint : Object, Endpoint {
 class SimpleFormEndpoint : Object, Endpoint {
-    public string route { get { return "/submit-simple"; } }
-    public Method[] methods { owned get { return { Method.POST }; } }
-    public async HttpResult handle_request(HttpContext context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext context, RouteContext route) throws Error {
         // Parse form data asynchronously from the request body
         // Parse form data asynchronously from the request body
         FormData form_data = yield FormDataParser.parse(
         FormData form_data = yield FormDataParser.parse(
             context.request.request_body,
             context.request.request_body,
@@ -165,9 +160,7 @@ class SimpleFormEndpoint : Object, Endpoint {
 
 
 // Registration form submission handler
 // Registration form submission handler
 class RegisterFormEndpoint : Object, Endpoint {
 class RegisterFormEndpoint : Object, Endpoint {
-    public string route { get { return "/submit-register"; } }
-    public Method[] methods { owned get { return { Method.POST }; } }
-    public async HttpResult handle_request(HttpContext context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext context, RouteContext route) throws Error {
         FormData form_data = yield FormDataParser.parse(
         FormData form_data = yield FormDataParser.parse(
             context.request.request_body,
             context.request.request_body,
             context.request.content_type
             context.request.content_type
@@ -217,9 +210,7 @@ class RegisterFormEndpoint : Object, Endpoint {
 
 
 // Search form submission handler
 // Search form submission handler
 class SearchFormEndpoint : Object, Endpoint {
 class SearchFormEndpoint : Object, Endpoint {
-    public string route { get { return "/submit-search"; } }
-    public Method[] methods { owned get { return { Method.POST }; } }
-    public async HttpResult handle_request(HttpContext context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext context, RouteContext route) throws Error {
         FormData form_data = yield FormDataParser.parse(
         FormData form_data = yield FormDataParser.parse(
             context.request.request_body,
             context.request.request_body,
             context.request.content_type
             context.request.content_type
@@ -275,9 +266,7 @@ class SearchFormEndpoint : Object, Endpoint {
 
 
 // File upload handler (multipart/form-data)
 // File upload handler (multipart/form-data)
 class FileUploadEndpoint : Object, Endpoint {
 class FileUploadEndpoint : Object, Endpoint {
-    public string route { get { return "/submit-file"; } }
-    public Method[] methods { owned get { return { Method.POST }; } }
-    public async HttpResult handle_request(HttpContext context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext context, RouteContext route) throws Error {
         FormData form_data = yield FormDataParser.parse(
         FormData form_data = yield FormDataParser.parse(
             context.request.request_body,
             context.request.request_body,
             context.request.content_type
             context.request.content_type
@@ -310,9 +299,7 @@ class FileUploadEndpoint : Object, Endpoint {
 
 
 // Form debug tool handler
 // Form debug tool handler
 class FormDebugEndpoint : Object, Endpoint {
 class FormDebugEndpoint : Object, Endpoint {
-    public string route { get { return "/form-debug"; } }
-    public Method[] methods { owned get { return { Method.GET, Method.POST }; } }
-    public async HttpResult handle_request(HttpContext context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext context, RouteContext route) throws Error {
         FormData? form_data = null;
         FormData? form_data = null;
         string? body_text = null;
         string? body_text = null;
         
         
@@ -484,24 +471,34 @@ class Product {
 }
 }
 
 
 void main() {
 void main() {
-    var router = new EndpointRouter()
-        .add_endpoint(new FormPageEndpoint())
-        .add_endpoint(new SimpleFormEndpoint())
-        .add_endpoint(new RegisterFormEndpoint())
-        .add_endpoint(new SearchFormEndpoint())
-        .add_endpoint(new FileUploadEndpoint())
-        .add_endpoint(new FormDebugEndpoint());
+    var application = new WebApplication(8084);
     
     
-    var pipeline = new Pipeline()
-        .add_component(new GzipCompressor())
-        .add_component(new ZstdCompressor())
-        .add_component(new BrotliCompressor())
-        .add_component(router);
-
-    var server = new Server(8084, pipeline);
+    // Register compression components
+    application.container.register_singleton<GzipCompressor>(() => new GzipCompressor());
+    application.container.register_singleton<ZstdCompressor>(() => new ZstdCompressor());
+    application.container.register_singleton<BrotliCompressor>(() => new BrotliCompressor());
+    
+    // Register endpoints
+    application.container.register_scoped<Endpoint>(() => new FormPageEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/"));
+    
+    application.container.register_scoped<Endpoint>(() => new SimpleFormEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/submit-simple"));
+    
+    application.container.register_scoped<Endpoint>(() => new RegisterFormEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/submit-register"));
+    
+    application.container.register_scoped<Endpoint>(() => new SearchFormEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/submit-search"));
+    
+    application.container.register_scoped<Endpoint>(() => new FileUploadEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/submit-file"));
+    
+    application.container.register_scoped<Endpoint>(() => new FormDebugEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/form-debug"));
     
     
     print("Form Data Example Server running on port 8084\n");
     print("Form Data Example Server running on port 8084\n");
     print("Open http://localhost:8084/ in your browser to try the forms\n");
     print("Open http://localhost:8084/ in your browser to try the forms\n");
     
     
-    server.run();
+    application.run();
 }
 }

+ 65 - 70
examples/HeadersAndCookies.vala

@@ -11,9 +11,7 @@ using Invercargill.DataStructures;
 
 
 // Display all request headers
 // Display all request headers
 class HeadersEndpoint : Object, Endpoint {
 class HeadersEndpoint : Object, Endpoint {
-    public string route { get { return "/headers"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var parts = new Series<string>();
         var parts = new Series<string>();
         parts.add("Request Headers:\n");
         parts.add("Request Headers:\n");
         parts.add("================\n\n");
         parts.add("================\n\n");
@@ -34,9 +32,7 @@ class HeadersEndpoint : Object, Endpoint {
 
 
 // Check for user-agent header
 // Check for user-agent header
 class UserAgentEndpoint : Object, Endpoint {
 class UserAgentEndpoint : Object, Endpoint {
-    public string route { get { return "/user-agent"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var user_agent = http_context.request.user_agent ?? "Unknown";
         var user_agent = http_context.request.user_agent ?? "Unknown";
         
         
         return new HttpStringResult(@"Your User-Agent is: $user_agent");
         return new HttpStringResult(@"Your User-Agent is: $user_agent");
@@ -45,9 +41,7 @@ class UserAgentEndpoint : Object, Endpoint {
 
 
 // Check content type and content length
 // Check content type and content length
 class ContentInfoEndpoint : Object, Endpoint {
 class ContentInfoEndpoint : Object, Endpoint {
-    public string route { get { return "/content-info"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var parts = new Series<string>();
         var parts = new Series<string>();
         parts.add("Content Information:\n");
         parts.add("Content Information:\n");
         parts.add("===================\n\n");
         parts.add("===================\n\n");
@@ -64,9 +58,7 @@ class ContentInfoEndpoint : Object, Endpoint {
 
 
 // Check if header exists
 // Check if header exists
 class CheckHeaderEndpoint : Object, Endpoint {
 class CheckHeaderEndpoint : Object, Endpoint {
-    public string route { get { return "/check-header"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var header_name = http_context.request.get_query_or_default("name", "Accept");
         var header_name = http_context.request.get_query_or_default("name", "Accept");
         var exists = http_context.request.has_header(header_name);
         var exists = http_context.request.has_header(header_name);
         var value = http_context.request.get_header(header_name);
         var value = http_context.request.get_header(header_name);
@@ -77,9 +69,7 @@ class CheckHeaderEndpoint : Object, Endpoint {
 
 
 // Set custom response headers
 // Set custom response headers
 class CustomHeadersEndpoint : Object, Endpoint {
 class CustomHeadersEndpoint : Object, Endpoint {
-    public string route { get { return "/custom-headers"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         return new HttpStringResult("Response with custom headers!")
         return new HttpStringResult("Response with custom headers!")
             .set_header("X-Custom-Header", "Custom-Value")
             .set_header("X-Custom-Header", "Custom-Value")
             .set_header("X-Request-Id", generate_request_id())
             .set_header("X-Request-Id", generate_request_id())
@@ -90,9 +80,7 @@ class CustomHeadersEndpoint : Object, Endpoint {
 
 
 // Display all cookies
 // Display all cookies
 class CookiesEndpoint : Object, Endpoint {
 class CookiesEndpoint : Object, Endpoint {
-    public string route { get { return "/cookies"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var parts = new Series<string>();
         var parts = new Series<string>();
         parts.add("Request Cookies:\n");
         parts.add("Request Cookies:\n");
         parts.add("================\n\n");
         parts.add("================\n\n");
@@ -118,9 +106,7 @@ class CookiesEndpoint : Object, Endpoint {
 
 
 // Get specific cookie
 // Get specific cookie
 class GetCookieEndpoint : Object, Endpoint {
 class GetCookieEndpoint : Object, Endpoint {
-    public string route { get { return "/get-cookie"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var name = http_context.request.get_query_or_default("name", "session");
         var name = http_context.request.get_query_or_default("name", "session");
         var value = http_context.request.get_cookie(name);
         var value = http_context.request.get_cookie(name);
         
         
@@ -134,9 +120,7 @@ class GetCookieEndpoint : Object, Endpoint {
 
 
 // Set a cookie (via Set-Cookie header)
 // Set a cookie (via Set-Cookie header)
 class SetCookieEndpoint : Object, Endpoint {
 class SetCookieEndpoint : Object, Endpoint {
-    public string route { get { return "/set-cookie"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var name = http_context.request.get_query_or_default("name", "test");
         var name = http_context.request.get_query_or_default("name", "test");
         var value = http_context.request.get_query_or_default("value", "123");
         var value = http_context.request.get_query_or_default("value", "123");
         var max_age = http_context.request.get_query_or_default("max_age", "3600");
         var max_age = http_context.request.get_query_or_default("max_age", "3600");
@@ -148,9 +132,7 @@ class SetCookieEndpoint : Object, Endpoint {
 
 
 // Set multiple cookies
 // Set multiple cookies
 class SetCookiesEndpoint : Object, Endpoint {
 class SetCookiesEndpoint : Object, Endpoint {
-    public string route { get { return "/set-cookies"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         // Note: Setting multiple cookies with the same header name requires
         // Note: Setting multiple cookies with the same header name requires
         // special handling - this example shows the approach
         // special handling - this example shows the approach
         return new HttpStringResult("Multiple cookies set!")
         return new HttpStringResult("Multiple cookies set!")
@@ -162,9 +144,7 @@ class SetCookiesEndpoint : Object, Endpoint {
 
 
 // Delete a cookie
 // Delete a cookie
 class DeleteCookieEndpoint : Object, Endpoint {
 class DeleteCookieEndpoint : Object, Endpoint {
-    public string route { get { return "/delete-cookie"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var name = http_context.request.get_query_or_default("name", "test");
         var name = http_context.request.get_query_or_default("name", "test");
         
         
         return new HttpStringResult(@"Cookie '$name' deleted")
         return new HttpStringResult(@"Cookie '$name' deleted")
@@ -174,9 +154,7 @@ class DeleteCookieEndpoint : Object, Endpoint {
 
 
 // Check if cookie exists
 // Check if cookie exists
 class HasCookieEndpoint : Object, Endpoint {
 class HasCookieEndpoint : Object, Endpoint {
-    public string route { get { return "/has-cookie"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var name = http_context.request.get_query_or_default("name", "session");
         var name = http_context.request.get_query_or_default("name", "session");
         var exists = http_context.request.has_cookie(name);
         var exists = http_context.request.has_cookie(name);
         
         
@@ -186,9 +164,7 @@ class HasCookieEndpoint : Object, Endpoint {
 
 
 // Cookie-based session simulation
 // Cookie-based session simulation
 class SessionEndpoint : Object, Endpoint {
 class SessionEndpoint : Object, Endpoint {
-    public string route { get { return "/session"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var session_id = http_context.request.get_cookie("session_id");
         var session_id = http_context.request.get_cookie("session_id");
         
         
         if (session_id == null) {
         if (session_id == null) {
@@ -212,9 +188,7 @@ Your session is active.");
 
 
 // CORS headers example
 // CORS headers example
 class CorsEndpoint : Object, Endpoint {
 class CorsEndpoint : Object, Endpoint {
-    public string route { get { return "/cors"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var origin = http_context.request.get_header("Origin") ?? "*";
         var origin = http_context.request.get_header("Origin") ?? "*";
         
         
         return new HttpStringResult(@"CORS enabled for origin: $origin")
         return new HttpStringResult(@"CORS enabled for origin: $origin")
@@ -227,9 +201,7 @@ class CorsEndpoint : Object, Endpoint {
 
 
 // Cache control headers
 // Cache control headers
 class CacheEndpoint : Object, Endpoint {
 class CacheEndpoint : Object, Endpoint {
-    public string route { get { return "/cache"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var cache_type = http_context.request.get_query_or_default("type", "public");
         var cache_type = http_context.request.get_query_or_default("type", "public");
         
         
         return new HttpStringResult(@"This response is cached ($cache_type cache, 1 hour)")
         return new HttpStringResult(@"This response is cached ($cache_type cache, 1 hour)")
@@ -241,9 +213,7 @@ class CacheEndpoint : Object, Endpoint {
 
 
 // Content negotiation
 // Content negotiation
 class NegotiateEndpoint : Object, Endpoint {
 class NegotiateEndpoint : Object, Endpoint {
-    public string route { get { return "/negotiate"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var accept = http_context.request.get_header("Accept") ?? "*/*";
         var accept = http_context.request.get_header("Accept") ?? "*/*";
         
         
         if (accept.contains("application/json")) {
         if (accept.contains("application/json")) {
@@ -261,9 +231,7 @@ class NegotiateEndpoint : Object, Endpoint {
 
 
 // Security headers
 // Security headers
 class SecureEndpoint : Object, Endpoint {
 class SecureEndpoint : Object, Endpoint {
-    public string route { get { return "/secure"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         return new HttpStringResult("Response with security headers!")
         return new HttpStringResult("Response with security headers!")
             .set_header("X-Content-Type-Options", "nosniff")
             .set_header("X-Content-Type-Options", "nosniff")
             .set_header("X-Frame-Options", "DENY")
             .set_header("X-Frame-Options", "DENY")
@@ -275,28 +243,55 @@ class SecureEndpoint : Object, Endpoint {
 }
 }
 
 
 void main() {
 void main() {
-    var router = new EndpointRouter()
-        .add_endpoint(new HeadersEndpoint())
-        .add_endpoint(new UserAgentEndpoint())
-        .add_endpoint(new ContentInfoEndpoint())
-        .add_endpoint(new CheckHeaderEndpoint())
-        .add_endpoint(new CustomHeadersEndpoint())
-        .add_endpoint(new CookiesEndpoint())
-        .add_endpoint(new GetCookieEndpoint())
-        .add_endpoint(new SetCookieEndpoint())
-        .add_endpoint(new SetCookiesEndpoint())
-        .add_endpoint(new DeleteCookieEndpoint())
-        .add_endpoint(new HasCookieEndpoint())
-        .add_endpoint(new SessionEndpoint())
-        .add_endpoint(new CorsEndpoint())
-        .add_endpoint(new CacheEndpoint())
-        .add_endpoint(new NegotiateEndpoint())
-        .add_endpoint(new SecureEndpoint());
+    var application = new WebApplication(8083);
     
     
-    var pipeline = new Pipeline()
-        .add_component(router);
-
-    var server = new Server(8083, pipeline);
+    application.container.register_scoped<Endpoint>(() => new HeadersEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/headers"));
+    
+    application.container.register_scoped<Endpoint>(() => new UserAgentEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/user-agent"));
+    
+    application.container.register_scoped<Endpoint>(() => new ContentInfoEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/content-info"));
+    
+    application.container.register_scoped<Endpoint>(() => new CheckHeaderEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/check-header"));
+    
+    application.container.register_scoped<Endpoint>(() => new CustomHeadersEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/custom-headers"));
+    
+    application.container.register_scoped<Endpoint>(() => new CookiesEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/cookies"));
+    
+    application.container.register_scoped<Endpoint>(() => new GetCookieEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/get-cookie"));
+    
+    application.container.register_scoped<Endpoint>(() => new SetCookieEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/set-cookie"));
+    
+    application.container.register_scoped<Endpoint>(() => new SetCookiesEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/set-cookies"));
+    
+    application.container.register_scoped<Endpoint>(() => new DeleteCookieEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/delete-cookie"));
+    
+    application.container.register_scoped<Endpoint>(() => new HasCookieEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/has-cookie"));
+    
+    application.container.register_scoped<Endpoint>(() => new SessionEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/session"));
+    
+    application.container.register_scoped<Endpoint>(() => new CorsEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/cors"));
+    
+    application.container.register_scoped<Endpoint>(() => new CacheEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/cache"));
+    
+    application.container.register_scoped<Endpoint>(() => new NegotiateEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/negotiate"));
+    
+    application.container.register_scoped<Endpoint>(() => new SecureEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/secure"));
     
     
     print("Headers and Cookies Example Server running on port 8083\n");
     print("Headers and Cookies Example Server running on port 8083\n");
     print("Try these endpoints:\n");
     print("Try these endpoints:\n");
@@ -311,7 +306,7 @@ void main() {
     print("  - http://localhost:8083/negotiate\n");
     print("  - http://localhost:8083/negotiate\n");
     print("  - http://localhost:8083/secure\n");
     print("  - http://localhost:8083/secure\n");
     
     
-    server.run();
+    application.run();
 }
 }
 
 
 // Helper functions
 // Helper functions

+ 54 - 56
examples/JsonApi.vala

@@ -12,9 +12,7 @@ using Invercargill.DataStructures;
 
 
 // API root handler
 // API root handler
 class ApiRootEndpoint : Object, Endpoint {
 class ApiRootEndpoint : Object, Endpoint {
-    public string route { get { return "/api"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var json = @"{
         var json = @"{
   \"name\": \"Astralis JSON API\",
   \"name\": \"Astralis JSON API\",
   \"version\": \"1.0.0\",
   \"version\": \"1.0.0\",
@@ -34,9 +32,7 @@ class ApiRootEndpoint : Object, Endpoint {
 
 
 // List all users
 // List all users
 class UsersListEndpoint : Object, Endpoint {
 class UsersListEndpoint : Object, Endpoint {
-    public string route { get { return "/api/users"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var users = get_users();
         var users = get_users();
         
         
         var json_parts = new Series<string>();
         var json_parts = new Series<string>();
@@ -64,11 +60,10 @@ class UsersListEndpoint : Object, Endpoint {
 
 
 // Get user by ID
 // Get user by ID
 class UserByIdEndpoint : Object, Endpoint {
 class UserByIdEndpoint : Object, Endpoint {
-    public string route { get { return "/api/users/{id}"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route_info) throws Error {
-        var id_str = route_info.named_components["id"];
-        var id = int.parse(id_str);
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route_info) throws Error {
+        string? id_str = null;
+        route_info.mapped_parameters.try_get("id", out id_str);
+        var id = int.parse(id_str ?? "0");
         var user = find_user(id);
         var user = find_user(id);
         
         
         if (user == null) {
         if (user == null) {
@@ -83,9 +78,7 @@ class UserByIdEndpoint : Object, Endpoint {
 
 
 // List all posts
 // List all posts
 class PostsListEndpoint : Object, Endpoint {
 class PostsListEndpoint : Object, Endpoint {
-    public string route { get { return "/api/posts"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var posts = get_posts();
         var posts = get_posts();
         
         
         // Filter by user_id if provided
         // Filter by user_id if provided
@@ -122,11 +115,10 @@ class PostsListEndpoint : Object, Endpoint {
 
 
 // Get post by ID
 // Get post by ID
 class PostByIdEndpoint : Object, Endpoint {
 class PostByIdEndpoint : Object, Endpoint {
-    public string route { get { return "/api/posts/{id}"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route_info) throws Error {
-        var id_str = route_info.named_components["id"];
-        var id = int.parse(id_str);
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route_info) throws Error {
+        string? id_str = null;
+        route_info.mapped_parameters.try_get("id", out id_str);
+        var id = int.parse(id_str ?? "0");
         var post = find_post(id);
         var post = find_post(id);
         
         
         if (post == null) {
         if (post == null) {
@@ -168,9 +160,7 @@ class PostByIdEndpoint : Object, Endpoint {
 
 
 // List all comments
 // List all comments
 class CommentsListEndpoint : Object, Endpoint {
 class CommentsListEndpoint : Object, Endpoint {
-    public string route { get { return "/api/comments"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var comments = get_comments();
         var comments = get_comments();
         
         
         // Filter by post_id if provided
         // Filter by post_id if provided
@@ -207,11 +197,10 @@ class CommentsListEndpoint : Object, Endpoint {
 
 
 // Get comment by ID
 // Get comment by ID
 class CommentByIdEndpoint : Object, Endpoint {
 class CommentByIdEndpoint : Object, Endpoint {
-    public string route { get { return "/api/comments/{id}"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route_info) throws Error {
-        var id_str = route_info.named_components["id"];
-        var id = int.parse(id_str);
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route_info) throws Error {
+        string? id_str = null;
+        route_info.mapped_parameters.try_get("id", out id_str);
+        var id = int.parse(id_str ?? "0");
         var comment = find_comment(id);
         var comment = find_comment(id);
         
         
         if (comment == null) {
         if (comment == null) {
@@ -226,9 +215,7 @@ class CommentByIdEndpoint : Object, Endpoint {
 
 
 // API statistics
 // API statistics
 class StatsEndpoint : Object, Endpoint {
 class StatsEndpoint : Object, Endpoint {
-    public string route { get { return "/api/stats"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var users = get_users();
         var users = get_users();
         var posts = get_posts();
         var posts = get_posts();
         var comments = get_comments();
         var comments = get_comments();
@@ -255,9 +242,7 @@ class StatsEndpoint : Object, Endpoint {
 
 
 // Search endpoint
 // Search endpoint
 class SearchEndpoint : Object, Endpoint {
 class SearchEndpoint : Object, Endpoint {
-    public string route { get { return "/api/search"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var query = http_context.request.get_query("q");
         var query = http_context.request.get_query("q");
         
         
         if (query == null || query == "") {
         if (query == null || query == "") {
@@ -305,9 +290,7 @@ class SearchEndpoint : Object, Endpoint {
 
 
 // Pagination example
 // Pagination example
 class PaginatedPostsEndpoint : Object, Endpoint {
 class PaginatedPostsEndpoint : Object, Endpoint {
-    public string route { get { return "/api/posts/paginated"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var page = int.parse(http_context.request.get_query_or_default("page", "1"));
         var page = int.parse(http_context.request.get_query_or_default("page", "1"));
         var per_page = int.parse(http_context.request.get_query_or_default("per_page", "10"));
         var per_page = int.parse(http_context.request.get_query_or_default("per_page", "10"));
         
         
@@ -344,9 +327,7 @@ class PaginatedPostsEndpoint : Object, Endpoint {
 
 
 // Error handling endpoint
 // Error handling endpoint
 class ApiErrorEndpoint : Object, Endpoint {
 class ApiErrorEndpoint : Object, Endpoint {
-    public string route { get { return "/api/error"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var error_type = http_context.request.get_query_or_default("type", "generic");
         var error_type = http_context.request.get_query_or_default("type", "generic");
         
         
         switch (error_type) {
         switch (error_type) {
@@ -363,23 +344,40 @@ class ApiErrorEndpoint : Object, Endpoint {
 }
 }
 
 
 void main() {
 void main() {
-    var router = new EndpointRouter()
-        .add_endpoint(new ApiRootEndpoint())
-        .add_endpoint(new UsersListEndpoint())
-        .add_endpoint(new UserByIdEndpoint())
-        .add_endpoint(new PostsListEndpoint())
-        .add_endpoint(new PostByIdEndpoint())
-        .add_endpoint(new CommentsListEndpoint())
-        .add_endpoint(new CommentByIdEndpoint())
-        .add_endpoint(new StatsEndpoint())
-        .add_endpoint(new SearchEndpoint())
-        .add_endpoint(new PaginatedPostsEndpoint())
-        .add_endpoint(new ApiErrorEndpoint());
+    var application = new WebApplication(8085);
     
     
-    var pipeline = new Pipeline()
-        .add_component(router);
-
-    var server = new Server(8085, pipeline);
+    application.container.register_scoped<Endpoint>(() => new ApiRootEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/api"));
+    
+    application.container.register_scoped<Endpoint>(() => new UsersListEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/api/users"));
+    
+    application.container.register_scoped<Endpoint>(() => new UserByIdEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/api/users/{id}"));
+    
+    application.container.register_scoped<Endpoint>(() => new PostsListEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/api/posts"));
+    
+    application.container.register_scoped<Endpoint>(() => new PostByIdEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/api/posts/{id}"));
+    
+    application.container.register_scoped<Endpoint>(() => new CommentsListEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/api/comments"));
+    
+    application.container.register_scoped<Endpoint>(() => new CommentByIdEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/api/comments/{id}"));
+    
+    application.container.register_scoped<Endpoint>(() => new StatsEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/api/stats"));
+    
+    application.container.register_scoped<Endpoint>(() => new SearchEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/api/search"));
+    
+    application.container.register_scoped<Endpoint>(() => new PaginatedPostsEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/api/posts/paginated"));
+    
+    application.container.register_scoped<Endpoint>(() => new ApiErrorEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/api/error"));
     
     
     print("JSON API Example Server running on port 8085\n");
     print("JSON API Example Server running on port 8085\n");
     print("Try these endpoints:\n");
     print("Try these endpoints:\n");
@@ -395,7 +393,7 @@ void main() {
     print("  - http://localhost:8085/api/search?q=test\n");
     print("  - http://localhost:8085/api/search?q=test\n");
     print("  - http://localhost:8085/api/posts/paginated?page=1&per_page=5\n");
     print("  - http://localhost:8085/api/posts/paginated?page=1&per_page=5\n");
     
     
-    server.run();
+    application.run();
 }
 }
 
 
 // Helper functions
 // Helper functions

+ 60 - 59
examples/PathRouting.vala

@@ -11,9 +11,7 @@ using Invercargill.DataStructures;
 
 
 // Root handler - shows available endpoints
 // Root handler - shows available endpoints
 class RootEndpoint : Object, Endpoint {
 class RootEndpoint : Object, Endpoint {
-    public string route { get { return "/"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         return new HttpStringResult("""Welcome to the Path Routing Example!
         return new HttpStringResult("""Welcome to the Path Routing Example!
 
 
 Available endpoints:
 Available endpoints:
@@ -33,28 +31,23 @@ Available endpoints:
 
 
 // Simple hello handler
 // Simple hello handler
 class HelloEndpoint : Object, Endpoint {
 class HelloEndpoint : Object, Endpoint {
-    public string route { get { return "/hello"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         return new HttpStringResult("Hello, World!");
         return new HttpStringResult("Hello, World!");
     }
     }
 }
 }
 
 
 // Hello with name handler - uses named segment
 // Hello with name handler - uses named segment
 class HelloNameEndpoint : Object, Endpoint {
 class HelloNameEndpoint : Object, Endpoint {
-    public string route { get { return "/hello/{name}"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route_info) throws Error {
-        var name = route_info.named_components["name"];
-        return new HttpStringResult(@"Hello, $name!");
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route_info) throws Error {
+        string? name = null;
+        route_info.mapped_parameters.try_get("name", out name);
+        return new HttpStringResult(@"Hello, $(name ?? "Unknown")!");
     }
     }
 }
 }
 
 
 // Users list handler
 // Users list handler
 class UsersEndpoint : Object, Endpoint {
 class UsersEndpoint : Object, Endpoint {
-    public string route { get { return "/users"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var users = new Series<User>();
         var users = new Series<User>();
         users.add(new User(1, "Alice", "alice@example.com"));
         users.add(new User(1, "Alice", "alice@example.com"));
         users.add(new User(2, "Bob", "bob@example.com"));
         users.add(new User(2, "Bob", "bob@example.com"));
@@ -82,11 +75,10 @@ class UsersEndpoint : Object, Endpoint {
 
 
 // User by ID handler - uses named segment
 // User by ID handler - uses named segment
 class UserByIdEndpoint : Object, Endpoint {
 class UserByIdEndpoint : Object, Endpoint {
-    public string route { get { return "/users/{id}"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route_info) throws Error {
-        var id_str = route_info.named_components["id"];
-        var id = int.parse(id_str);
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route_info) throws Error {
+        string? id_str = null;
+        route_info.mapped_parameters.try_get("id", out id_str);
+        var id = int.parse(id_str ?? "0");
         
         
         // Simulated user lookup
         // Simulated user lookup
         var users = new Series<User>();
         var users = new Series<User>();
@@ -109,11 +101,10 @@ class UserByIdEndpoint : Object, Endpoint {
 
 
 // User posts handler - uses named segment
 // User posts handler - uses named segment
 class UserPostsEndpoint : Object, Endpoint {
 class UserPostsEndpoint : Object, Endpoint {
-    public string route { get { return "/users/{id}/posts"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route_info) throws Error {
-        var id_str = route_info.named_components["id"];
-        var id = int.parse(id_str);
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route_info) throws Error {
+        string? id_str = null;
+        route_info.mapped_parameters.try_get("id", out id_str);
+        var id = int.parse(id_str ?? "0");
         
         
         // Simulated posts for user
         // Simulated posts for user
         var posts = new Series<Post>();
         var posts = new Series<Post>();
@@ -142,9 +133,7 @@ class UserPostsEndpoint : Object, Endpoint {
 
 
 // API status handler
 // API status handler
 class ApiStatusEndpoint : Object, Endpoint {
 class ApiStatusEndpoint : Object, Endpoint {
-    public string route { get { return "/api/v1/status"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var status = new Dictionary<string, string>();
         var status = new Dictionary<string, string>();
         status.set("status", "operational");
         status.set("status", "operational");
         status.set("version", "1.0.0");
         status.set("version", "1.0.0");
@@ -172,11 +161,10 @@ class ApiStatusEndpoint : Object, Endpoint {
 
 
 // API item handler - uses named segment
 // API item handler - uses named segment
 class ApiItemEndpoint : Object, Endpoint {
 class ApiItemEndpoint : Object, Endpoint {
-    public string route { get { return "/api/v1/items/{id}"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route_info) throws Error {
-        var id_str = route_info.named_components["id"];
-        var id = int.parse(id_str);
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route_info) throws Error {
+        string? id_str = null;
+        route_info.mapped_parameters.try_get("id", out id_str);
+        var id = int.parse(id_str ?? "0");
         
         
         var items = new Dictionary<int, Item>();
         var items = new Dictionary<int, Item>();
         items.set(1, new Item(1, "Widget", "A useful widget", 9.99));
         items.set(1, new Item(1, "Widget", "A useful widget", 9.99));
@@ -196,11 +184,11 @@ class ApiItemEndpoint : Object, Endpoint {
 
 
 // Files handler - uses two named segments
 // Files handler - uses two named segments
 class FilesEndpoint : Object, Endpoint {
 class FilesEndpoint : Object, Endpoint {
-    public string route { get { return "/files/{category}/{name}"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route_info) throws Error {
-        var category = route_info.named_components["category"];
-        var name = route_info.named_components["name"];
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route_info) throws Error {
+        string? category = null;
+        string? name = null;
+        route_info.mapped_parameters.try_get("category", out category);
+        route_info.mapped_parameters.try_get("name", out name);
         
         
         // Simulated file lookup
         // Simulated file lookup
         var files = new Dictionary<string, Dictionary<string, ExampleFile>>();
         var files = new Dictionary<string, Dictionary<string, ExampleFile>>();
@@ -216,9 +204,9 @@ class FilesEndpoint : Object, Endpoint {
         files.set("images", images);
         files.set("images", images);
         
         
         Dictionary<string, ExampleFile>? category_files = null;
         Dictionary<string, ExampleFile>? category_files = null;
-        if (files.try_get(category, out category_files)) {
+        if (files.try_get(category ?? "", out category_files)) {
             ExampleFile? file = null;
             ExampleFile? file = null;
-            if (category_files.try_get(name, out file)) {
+            if (category_files.try_get(name ?? "", out file)) {
                 return new HttpStringResult(file.to_json())
                 return new HttpStringResult(file.to_json())
                     .set_header("Content-Type", "application/json");
                     .set_header("Content-Type", "application/json");
             }
             }
@@ -231,9 +219,7 @@ class FilesEndpoint : Object, Endpoint {
 
 
 // Path info handler
 // Path info handler
 class PathInfoEndpoint : Object, Endpoint {
 class PathInfoEndpoint : Object, Endpoint {
-    public string route { get { return "/pathinfo"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route_info) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route_info) throws Error {
         var parts = new Series<string>();
         var parts = new Series<string>();
         parts.add("Path Information:\n");
         parts.add("Path Information:\n");
         parts.add(@"  Raw path: $(http_context.request.raw_path)\n");
         parts.add(@"  Raw path: $(http_context.request.raw_path)\n");
@@ -250,7 +236,7 @@ class PathInfoEndpoint : Object, Endpoint {
         
         
         // Show named segments from route information
         // Show named segments from route information
         parts.add("\n  Named segments:\n");
         parts.add("\n  Named segments:\n");
-        route_info.named_components.to_immutable_buffer()
+        route_info.mapped_parameters.to_immutable_buffer()
             .iterate((kv) => {
             .iterate((kv) => {
                 parts.add(@"    $(kv.key): $(kv.value)\n");
                 parts.add(@"    $(kv.key): $(kv.value)\n");
             });
             });
@@ -263,22 +249,37 @@ class PathInfoEndpoint : Object, Endpoint {
 }
 }
 
 
 void main() {
 void main() {
-    var router = new EndpointRouter()
-        .add_endpoint(new RootEndpoint())
-        .add_endpoint(new HelloEndpoint())
-        .add_endpoint(new HelloNameEndpoint())
-        .add_endpoint(new UsersEndpoint())
-        .add_endpoint(new UserByIdEndpoint())
-        .add_endpoint(new UserPostsEndpoint())
-        .add_endpoint(new ApiStatusEndpoint())
-        .add_endpoint(new ApiItemEndpoint())
-        .add_endpoint(new FilesEndpoint())
-        .add_endpoint(new PathInfoEndpoint());
+    var application = new WebApplication(8082);
     
     
-    var pipeline = new Pipeline()
-        .add_component(router);
-
-    var server = new Server(8082, pipeline);
+    application.container.register_scoped<Endpoint>(() => new RootEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/"));
+    
+    application.container.register_scoped<Endpoint>(() => new HelloEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/hello"));
+    
+    application.container.register_scoped<Endpoint>(() => new HelloNameEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/hello/{name}"));
+    
+    application.container.register_scoped<Endpoint>(() => new UsersEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/users"));
+    
+    application.container.register_scoped<Endpoint>(() => new UserByIdEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/users/{id}"));
+    
+    application.container.register_scoped<Endpoint>(() => new UserPostsEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/users/{id}/posts"));
+    
+    application.container.register_scoped<Endpoint>(() => new ApiStatusEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/api/v1/status"));
+    
+    application.container.register_scoped<Endpoint>(() => new ApiItemEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/api/v1/items/{id}"));
+    
+    application.container.register_scoped<Endpoint>(() => new FilesEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/files/{category}/{name}"));
+    
+    application.container.register_scoped<Endpoint>(() => new PathInfoEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/pathinfo"));
     
     
     print("Path Routing Example Server running on port 8082\n");
     print("Path Routing Example Server running on port 8082\n");
     print("Try these endpoints:\n");
     print("Try these endpoints:\n");
@@ -293,7 +294,7 @@ void main() {
     print("  - http://localhost:8082/files/docs/readme.txt\n");
     print("  - http://localhost:8082/files/docs/readme.txt\n");
     print("  - http://localhost:8082/pathinfo?test=1\n");
     print("  - http://localhost:8082/pathinfo?test=1\n");
     
     
-    server.run();
+    application.run();
 }
 }
 
 
 // Helper classes for the example
 // Helper classes for the example

+ 21 - 26
examples/QueryParameters.vala

@@ -12,9 +12,7 @@ using Invercargill.DataStructures;
 
 
 // Greet handler - simple query parameter access
 // Greet handler - simple query parameter access
 class GreetEndpoint : Object, Endpoint {
 class GreetEndpoint : Object, Endpoint {
-    public string route { get { return "/greet"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var name = http_context.request.get_query_or_default("name", "World");
         var name = http_context.request.get_query_or_default("name", "World");
         var greeting = http_context.request.get_query_or_default("greeting", "Hello");
         var greeting = http_context.request.get_query_or_default("greeting", "Hello");
         
         
@@ -24,9 +22,7 @@ class GreetEndpoint : Object, Endpoint {
 
 
 // Search handler - multiple query parameters with validation
 // Search handler - multiple query parameters with validation
 class SearchEndpoint : Object, Endpoint {
 class SearchEndpoint : Object, Endpoint {
-    public string route { get { return "/search"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var query = http_context.request.get_query("q");
         var query = http_context.request.get_query("q");
         var limit = int.parse(http_context.request.get_query_or_default("limit", "10"));
         var limit = int.parse(http_context.request.get_query_or_default("limit", "10"));
         var offset = int.parse(http_context.request.get_query_or_default("offset", "0"));
         var offset = int.parse(http_context.request.get_query_or_default("offset", "0"));
@@ -63,9 +59,7 @@ class SearchEndpoint : Object, Endpoint {
 
 
 // Debug handler - boolean flag query parameters
 // Debug handler - boolean flag query parameters
 class DebugEndpoint : Object, Endpoint {
 class DebugEndpoint : Object, Endpoint {
-    public string route { get { return "/debug"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var verbose = http_context.request.has_query("verbose");
         var verbose = http_context.request.has_query("verbose");
         var level = http_context.request.get_query_or_default("level", "info");
         var level = http_context.request.get_query_or_default("level", "info");
         
         
@@ -92,9 +86,7 @@ class DebugEndpoint : Object, Endpoint {
 
 
 // Filter handler - query parameter with multiple values (comma-separated)
 // Filter handler - query parameter with multiple values (comma-separated)
 class FilterEndpoint : Object, Endpoint {
 class FilterEndpoint : Object, Endpoint {
-    public string route { get { return "/filter"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var tags_param = http_context.request.get_query("tags");
         var tags_param = http_context.request.get_query("tags");
         
         
         if (tags_param == null) {
         if (tags_param == null) {
@@ -130,9 +122,7 @@ class FilterEndpoint : Object, Endpoint {
 
 
 // Range handler - numeric query parameters with range validation
 // Range handler - numeric query parameters with range validation
 class RangeEndpoint : Object, Endpoint {
 class RangeEndpoint : Object, Endpoint {
-    public string route { get { return "/range"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         var min = int.parse(http_context.request.get_query_or_default("min", "0"));
         var min = int.parse(http_context.request.get_query_or_default("min", "0"));
         var max = int.parse(http_context.request.get_query_or_default("max", "100"));
         var max = int.parse(http_context.request.get_query_or_default("max", "100"));
         var step = int.parse(http_context.request.get_query_or_default("step", "1"));
         var step = int.parse(http_context.request.get_query_or_default("step", "1"));
@@ -167,17 +157,22 @@ class RangeEndpoint : Object, Endpoint {
 }
 }
 
 
 void main() {
 void main() {
-    var router = new EndpointRouter()
-        .add_endpoint(new GreetEndpoint())
-        .add_endpoint(new SearchEndpoint())
-        .add_endpoint(new DebugEndpoint())
-        .add_endpoint(new FilterEndpoint())
-        .add_endpoint(new RangeEndpoint());
+    var application = new WebApplication(8081);
     
     
-    var pipeline = new Pipeline()
-        .add_component(router);
-
-    var server = new Server(8081, pipeline);
+    application.container.register_scoped<Endpoint>(() => new GreetEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/greet"));
+    
+    application.container.register_scoped<Endpoint>(() => new SearchEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/search"));
+    
+    application.container.register_scoped<Endpoint>(() => new DebugEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/debug"));
+    
+    application.container.register_scoped<Endpoint>(() => new FilterEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/filter"));
+    
+    application.container.register_scoped<Endpoint>(() => new RangeEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/range"));
     
     
     print("Query Parameters Example Server running on port 8081\n");
     print("Query Parameters Example Server running on port 8081\n");
     print("Try these endpoints:\n");
     print("Try these endpoints:\n");
@@ -187,5 +182,5 @@ void main() {
     print("  - http://localhost:8081/filter?tags=foo,bar,baz\n");
     print("  - http://localhost:8081/filter?tags=foo,bar,baz\n");
     print("  - http://localhost:8081/range?min=1&max=20&step=2\n");
     print("  - http://localhost:8081/range?min=1&max=20&step=2\n");
     
     
-    server.run();
+    application.run();
 }
 }

+ 5 - 11
examples/RemoteAddress.vala

@@ -10,10 +10,7 @@ using Invercargill.DataStructures;
 
 
 // Endpoint that responds with client information
 // Endpoint that responds with client information
 public class RemoteAddressEndpoint : Object, Endpoint {
 public class RemoteAddressEndpoint : Object, Endpoint {
-    public string route { get { return "*"; } }
-    public Method[] methods { owned get { return { Method.GET }; } }
-    
-    public async HttpResult handle_request(HttpContext context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext context, RouteContext route) throws Error {
         var request = context.request;
         var request = context.request;
         
         
         // Access the remote address
         // Access the remote address
@@ -44,16 +41,13 @@ public class RemoteAddressEndpoint : Object, Endpoint {
 }
 }
 
 
 void main() {
 void main() {
-    var router = new EndpointRouter()
-        .add_endpoint(new RemoteAddressEndpoint());
+    var application = new WebApplication(8080);
     
     
-    var pipeline = new Pipeline()
-        .add_component(router);
-
-    var server = new Server(8080, pipeline);
+    application.container.register_scoped<Endpoint>(() => new RemoteAddressEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("*"));
     
     
     print("Remote Address Example Server running on port 8080\n");
     print("Remote Address Example Server running on port 8080\n");
     print("Try: http://localhost:8080/\n");
     print("Try: http://localhost:8080/\n");
     
     
-    server.run();
+    application.run();
 }
 }

+ 9 - 14
examples/SimpleApi.vala

@@ -4,9 +4,7 @@ using Invercargill.DataStructures;
 
 
 // Simple handler for /hello endpoint
 // Simple handler for /hello endpoint
 class HelloEndpoint : Object, Endpoint {
 class HelloEndpoint : Object, Endpoint {
-    public string route { get { return "/hello"; } }
-    public Method[] methods { owned get { return { Method.GET };} }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         print("Handling /hello\n");
         print("Handling /hello\n");
         return new HttpStringResult("Hello from Astralis!");
         return new HttpStringResult("Hello from Astralis!");
     }
     }
@@ -14,9 +12,7 @@ class HelloEndpoint : Object, Endpoint {
 
 
 // Simple handler for /json endpoint
 // Simple handler for /json endpoint
 class JsonEndpoint : Object, Endpoint {
 class JsonEndpoint : Object, Endpoint {
-    public string route { get { return "/json"; } }
-    public Method[] methods { owned get { return { Method.GET };} }
-    public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error {
+    public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
         print("Handling /json\n");
         print("Handling /json\n");
         return new HttpStringResult("{ \"message\": \"Hello JSON\" }")
         return new HttpStringResult("{ \"message\": \"Hello JSON\" }")
             .set_header("Content-Type", "application/json");
             .set_header("Content-Type", "application/json");
@@ -24,13 +20,12 @@ class JsonEndpoint : Object, Endpoint {
 }
 }
 
 
 void main() {
 void main() {
-    var router = new EndpointRouter()
-        .add_endpoint(new HelloEndpoint())
-        .add_endpoint(new JsonEndpoint());
-    
-    var pipeline = new Pipeline()
-        .add_component(router);
+    var application = new WebApplication(8080);
+    application.container.register_scoped<Endpoint>(() => new HelloEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/hello"));
 
 
-    var server = new Server(8080, pipeline);
-    server.run();
+    application.container.register_scoped<Endpoint>(() => new JsonEndpoint())
+        .with_metadata<EndpointRoute>(new EndpointRoute("/json"));
+
+    application.run();
 }
 }

+ 1 - 0
meson.build

@@ -9,6 +9,7 @@ gio_dep = dependency('gio-2.0')
 gio_unix_dep = dependency('gio-unix-2.0')
 gio_unix_dep = dependency('gio-unix-2.0')
 mhd_dep = dependency('libmicrohttpd')
 mhd_dep = dependency('libmicrohttpd')
 invercargill_dep = dependency('invercargill-1')
 invercargill_dep = dependency('invercargill-1')
+inversion_dep = dependency('inversion-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')
 zlib_dep = dependency('zlib')
 zlib_dep = dependency('zlib')

+ 1 - 0
src/Components/Compressor.vala

@@ -1,5 +1,6 @@
 using Invercargill;
 using Invercargill;
 using Invercargill.DataStructures;
 using Invercargill.DataStructures;
+using Inversion;
 
 
 namespace Astralis {
 namespace Astralis {
 
 

+ 75 - 72
src/Components/EndpointRouter.vala

@@ -1,64 +1,35 @@
 using Invercargill;
 using Invercargill;
 using Invercargill.DataStructures;
 using Invercargill.DataStructures;
+using Inversion;
 
 
 namespace Astralis {
 namespace Astralis {
 
 
     public class EndpointRouter : Object, PipelineComponent {
     public class EndpointRouter : Object, PipelineComponent {
 
 
-        private Series<EndpointSource> endpoint_sources = new Series<EndpointSource>();
+        private Scope current_scope = inject<Scope>();
 
 
         public async HttpResult process_request(HttpContext http_context, PipelineContext pipeline_context) throws Error {
         public async HttpResult process_request(HttpContext http_context, PipelineContext pipeline_context) throws Error {
             var path_components = http_context.request.path_components.to_vector();
             var path_components = http_context.request.path_components.to_vector();
-            var endpoint_attempt = endpoint_sources
-                .attempt_select<Endpoint>(s => s.get_endpoint())
-                .to_series() // Todo fix probelms some day so we don't need this!
-                .where(e => Wrap.array<Method>(e.methods).any(m => m == http_context.request.method))
-                .where(e => matches_endpoint(path_components, e))
-                .first_or_default();
-
-            if(endpoint_attempt == null) {
+            var matching_registrations = current_scope.get_registrations(typeof(Endpoint))
+                .where(e => e.get_metadata<EndpointRoute>().any(r => matches_endpoint(path_components, r)))
+                .to_immutable_buffer();
+
+            if(matching_registrations.length == 0) {
                 return new HttpStringResult("Not Found", StatusCode.NOT_FOUND);
                 return new HttpStringResult("Not Found", StatusCode.NOT_FOUND);
             }
             }
 
 
-            var endpoint = endpoint_attempt;
-            var info = new RouteInformation() {
-                path_components = path_components,
-                named_components = extract_named_components(path_components, endpoint)
-            };
-            return yield endpoint.handle_request(http_context, info);
-        }
+            var matched_route = matching_registrations.first()
+                .get_metadata<EndpointRoute>()
+                .first(r => matches_endpoint(path_components, r));
 
 
-        public EndpointRouter add_endpoint(Endpoint endpoint) throws RouterError {
-            validate_route(endpoint.route);
-            this.endpoint_sources.add(new SingletonEndpointSource(endpoint));
-            return this;
-        }
+            var route_context = new RouteContext(matched_route, path_components);
+            current_scope.register_local_scoped<RouteContext>(() => route_context);
 
 
-        public EndpointRouter add_endpoint_source(EndpointSource endpoint_source) {
-            this.endpoint_sources.add(endpoint_source);
-            return this;
-        }
-
-        private void validate_route(string route) throws RouterError {
-            // Check if "/**" appears anywhere except at the end
-            if (route.contains("/**")) {
-                // Route must end with "/**" and "/**" must not appear elsewhere
-                if (!route.has_suffix("/**")) {
-                    throw new RouterError.INVALID_ROUTE_DEFINITION(
-                        "\"/**\" can only appear at the end of a route"
-                    );
-                }
-                // Check that "/**" doesn't appear multiple times
-                var parts = route.split("/**");
-                if (parts.length > 2) {
-                    throw new RouterError.INVALID_ROUTE_DEFINITION(
-                        "\"/**\" can only appear at the end of a route"
-                    );
-                }
-            }
+            var endpoint = (Endpoint)current_scope.resolve_registration(matching_registrations.first());
+            return yield endpoint.handle_request(http_context, route_context);
         }
         }
 
 
-        private bool matches_endpoint(ReadOnlyAddressable<string> path_components, Endpoint endpoint) {
+        private bool matches_endpoint(ReadOnlyAddressable<string> path_components, EndpointRoute endpoint) {
             // "*" (with no preceding slash) is match all.
             // "*" (with no preceding slash) is match all.
             if(endpoint.route == "*") {
             if(endpoint.route == "*") {
                 return true;
                 return true;
@@ -131,12 +102,25 @@ namespace Astralis {
             return pattern == value;
             return pattern == value;
         }
         }
 
 
-        private Dictionary<string, string> extract_named_components(ReadOnlyAddressable<string> path_components, Endpoint endpoint) throws IndexError, RouterError {
-            var dictionary = new Dictionary<string, string>();
+    }
+
+    public class RouteContext : Object {
+
+        public EndpointRoute matched_route { get; private set; }
+        public string requested_path { get; private set; }
+        public ReadOnlyAddressable<string> requested_path_segments { get; private set; }
+        public ReadOnlyAssociative<string, string> mapped_parameters { get; private set; }
 
 
+        public RouteContext(EndpointRoute matched_route, Enumerable<string> requested_path_segments) throws Error {
+            this.matched_route = matched_route;
+            this.requested_path = requested_path;
+            this.requested_path_segments = requested_path_segments.to_immutable_buffer();
+            this.requested_path = "/" + this.requested_path_segments.to_string(null, "/");
+            
+            var parameter_map = new Dictionary<string, string>();
             // Handle "/**" catch-all - capture remaining path as "**"
             // Handle "/**" catch-all - capture remaining path as "**"
-            if(endpoint.route.has_suffix("/**")) {
-                string prefix_route = endpoint.route.substring(0, endpoint.route.length - 3);
+            if(matched_route.route.has_suffix("/**")) {
+                string prefix_route = matched_route.route.substring(0, matched_route.route.length - 3);
                 var prefix_components = Wrap.array<string>(prefix_route.split("/"))
                 var prefix_components = Wrap.array<string>(prefix_route.split("/"))
                     .where(c => c.length != 0)
                     .where(c => c.length != 0)
                     .to_immutable_buffer();
                     .to_immutable_buffer();
@@ -144,8 +128,8 @@ namespace Astralis {
                 // Extract named components from the prefix
                 // Extract named components from the prefix
                 for(int i = 0; i < prefix_components.length; i++) {
                 for(int i = 0; i < prefix_components.length; i++) {
                     if(prefix_components[i].has_prefix("{") && prefix_components[i].has_suffix("}")) {
                     if(prefix_components[i].has_prefix("{") && prefix_components[i].has_suffix("}")) {
-                        if(i < path_components.length) {
-                            dictionary.add(prefix_components[i][1:-1], path_components[i]);
+                        if(i < matched_route.route_segments.length) {
+                            parameter_map.add(prefix_components[i][1:-1], matched_route.route_segments[i]);
                         }
                         }
                     }
                     }
                 }
                 }
@@ -156,19 +140,20 @@ namespace Astralis {
                 // Build path manually to avoid string.join issues
                 // Build path manually to avoid string.join issues
                 var builder = new StringBuilder();
                 var builder = new StringBuilder();
                 bool first = true;
                 bool first = true;
-                for(int i = remaining_start; i < path_components.length; i++) {
+                for(int i = remaining_start; i < matched_route.route_segments.length; i++) {
                     if (!first) {
                     if (!first) {
                         builder.append("/");
                         builder.append("/");
                     }
                     }
-                    builder.append(path_components[i]);
+                    builder.append(matched_route.route_segments[i]);
                     first = false;
                     first = false;
                 }
                 }
-                dictionary.add("**", builder.str);
-                return dictionary;
+                parameter_map.add("**", builder.str);
+                this.mapped_parameters = parameter_map;
+                return;
             }
             }
 
 
             // Standard named component extraction
             // Standard named component extraction
-            var endpoint_components = Wrap.array<string>(endpoint.route.split("/"))
+            var endpoint_components = Wrap.array<string>(matched_route.route.split("/"))
                 .where(c => c.length != 0)
                 .where(c => c.length != 0)
                 .to_immutable_buffer();
                 .to_immutable_buffer();
 
 
@@ -176,34 +161,52 @@ namespace Astralis {
                 if(!endpoint_components[i].has_prefix("{") || !endpoint_components[i].has_suffix("}")) {
                 if(!endpoint_components[i].has_prefix("{") || !endpoint_components[i].has_suffix("}")) {
                     continue;
                     continue;
                 }
                 }
-                dictionary.add(endpoint_components[i][1:-1], path_components[i]);
+                parameter_map.add(endpoint_components[i][1:-1], matched_route.route_segments[i]);
             }
             }
-            return dictionary;
+
+            this.mapped_parameters = parameter_map;
         }
         }
 
 
     }
     }
 
 
-    public class RouteInformation {
-
-        public Vector<string> path_components { get; set; }
-        public Dictionary<string, string> named_components { get; set; }
+    public class EndpointRoute : Object {
 
 
-    }
-
-    public interface EndpointSource : Object {
-        public abstract Endpoint get_endpoint() throws Error;
-    }
+        public ImmutableLot<Method> methods { get; private set; }
+        public string route { get; private set; }
+        public ReadOnlyAddressable<string> route_segments { get; private set; }
+        public ReadOnlyAddressable<string> route_parameters { get; private set; }
 
 
-    public class SingletonEndpointSource : Object, EndpointSource {
-        public Endpoint instance { get; private set; }
+        public EndpointRoute(string route, Method method = Method.GET, ...) throws RouterError {
+            validate_route(route);
 
 
-        public SingletonEndpointSource(Endpoint instance) {
-            this.instance = instance;
+            this.route  = route;
+            this.methods = Wrap.va_list<Method>(method, va_list()).to_immutable_buffer();
+            this.route_segments = Wrap.array<string>(this.route.split("/")).to_immutable_buffer();
+            this.route_parameters = this.route_segments
+                .where(s => s.has_prefix("{") && s.has_suffix("}"))
+                .select<string>(s => s.substring(1, s.length-2))
+                .to_immutable_buffer();
         }
         }
 
 
-        public Endpoint get_endpoint() {
-            return this.instance;
+        private void validate_route(string route) throws RouterError {
+            // Check if "/**" appears anywhere except at the end
+            if (route.contains("/**")) {
+                // Route must end with "/**" and "/**" must not appear elsewhere
+                if (!route.has_suffix("/**")) {
+                    throw new RouterError.INVALID_ROUTE_DEFINITION(
+                        "\"/**\" can only appear at the end of a route"
+                    );
+                }
+                // Check that "/**" doesn't appear multiple times
+                var parts = route.split("/**");
+                if (parts.length > 2) {
+                    throw new RouterError.INVALID_ROUTE_DEFINITION(
+                        "\"/**\" can only appear at the end of a route"
+                    );
+                }
+            }
         }
         }
+
     }
     }
 
 
     public errordomain RouterError {
     public errordomain RouterError {

+ 9 - 32
src/Core/Pipeline.vala

@@ -1,26 +1,20 @@
 using Invercargill.DataStructures;
 using Invercargill.DataStructures;
+using Inversion;
+
 namespace Astralis {
 namespace Astralis {
 
 
     public class Pipeline {
     public class Pipeline {
 
 
-        private Series<PipelineComponentSource> component_sources =  new Series<PipelineComponentSource>();
-
-        public Pipeline add_component_source(PipelineComponentSource component_source) {
-            component_sources.add(component_source);
-            return this;
-        }
+        public Container container { get; set; default = try_inject<Container>(); }
 
 
-        public Pipeline add_component(PipelineComponent component) {
-            var source = new SingletonPipelineComponentSource(component);
-            component_sources.add(source);
-            return this;
+        public Pipeline(Container container) {
+            this.container = container;
         }
         }
 
 
         public async HttpResult run(HttpContext context) throws Error {
         public async HttpResult run(HttpContext context) throws Error {
-            var components = component_sources
-                .attempt_select<PipelineComponent>(s => s.get_component())
-                .to_vector();
-                //  .to_buffer();
+            var scope = container.create_scope();
+            scope.register_local_scoped<HttpContext>(() => context);
+            var components = scope.resolve_all<PipelineComponent>().to_immutable_buffer();
 
 
             if(components.length == 0) {
             if(components.length == 0) {
                 throw new PipelineError.NO_COMPONENTS("No components in pipeline");
                 throw new PipelineError.NO_COMPONENTS("No components in pipeline");
@@ -41,10 +35,9 @@ namespace Astralis {
         private PipelineComponent? next_component;
         private PipelineComponent? next_component;
         private PipelineContext? next_context;
         private PipelineContext? next_context;
         private HttpContext http_context;
         private HttpContext http_context;
-
         internal PipelineContext(HttpContext http_context, PipelineComponent? next_component, PipelineContext? next_context) {
         internal PipelineContext(HttpContext http_context, PipelineComponent? next_component, PipelineContext? next_context) {
-            this.next_component = next_component;
             this.http_context = http_context;
             this.http_context = http_context;
+            this.next_component = next_component;
             this.next_context = next_context;
             this.next_context = next_context;
         }
         }
 
 
@@ -61,22 +54,6 @@ namespace Astralis {
         public abstract async HttpResult process_request(HttpContext http_context, PipelineContext pipeline_context) throws Error;
         public abstract async HttpResult process_request(HttpContext http_context, PipelineContext pipeline_context) throws Error;
     }
     }
 
 
-    public interface PipelineComponentSource : Object {
-        public abstract PipelineComponent get_component() throws Error;
-    }
-
-    public class SingletonPipelineComponentSource : Object, PipelineComponentSource {
-        public PipelineComponent instance { get; private set; }
-
-        public SingletonPipelineComponentSource(PipelineComponent instance) {
-            this.instance = instance;
-        }
-
-        public PipelineComponent get_component() {
-            return this.instance;
-        }
-    }
-
     public errordomain PipelineError {
     public errordomain PipelineError {
         END_OF_PIPELINE_REACHED,
         END_OF_PIPELINE_REACHED,
         NO_COMPONENTS
         NO_COMPONENTS

+ 48 - 0
src/Core/WebApplication.vala

@@ -0,0 +1,48 @@
+using Inversion;
+namespace Astralis {
+
+    public class WebApplication {
+
+        public Container container { get; private set; }
+        public int port { get; private set; }
+
+        private Server server;
+        private Pipeline pipeline;
+
+        public WebApplication(int? port = null) {
+            this.port = port ?? int.parse(Environment.get_variable ("ASTRALIS_PORT") ?? "8080");
+            printerr(@"[Astralis] Web application using port $(port)\n");
+
+            container = new Container();
+            pipeline = new Pipeline(container);
+            server = new Server(this.port, pipeline);
+        }
+
+        public void run() {
+            // Ensure router is registered last so that it is the last component in the pipeline.
+            container.register_scoped<EndpointRouter>()
+                .as<PipelineComponent>();
+
+            server.run();
+        }
+
+        public Registration add_endpoint<T>(EndpointRoute route, owned TypedFactoryDelegate<T>? endpoint_func = null) {
+            return container.register_scoped<T>((owned) endpoint_func)
+                .with_metadata<EndpointRoute>(route)
+                .as<Endpoint>();
+        }
+
+        public Registration add_static_endpoint<T>(EndpointRoute route, owned TypedFactoryDelegate<T>? endpoint_func = null) {
+            return container.register_singleton<T>((owned) endpoint_func)
+                .with_metadata<EndpointRoute>(route)
+                .as<Endpoint>();
+        }
+
+        public void use_compression() {
+            container.register_scoped<GzipCompressor>();
+            container.register_scoped<ZstdCompressor>();
+            container.register_scoped<BrotliCompressor>();
+        }
+    }
+
+}

+ 1 - 5
src/Endpoints/Endpoint.vala

@@ -1,11 +1,7 @@
 namespace Astralis {
 namespace Astralis {
 
 
     public interface Endpoint : Object {
     public interface Endpoint : Object {
-
-        public abstract string route { get; }
-        public abstract Method[] methods { owned get; }
-        public abstract async HttpResult handle_request(HttpContext http_context, RouteInformation route_context) throws Error;
-
+        public abstract async HttpResult handle_request(HttpContext http_context, RouteContext route_context) throws Error;
     }
     }
 
 
 }
 }

+ 24 - 14
src/Endpoints/FastResource.vala

@@ -3,35 +3,44 @@ using Invercargill.DataStructures;
 
 
 namespace Astralis {
 namespace Astralis {
 
 
+    /// A high-performance static content endpoint that pre-loads data into memory.
+    /// 
+    /// FastResource is ideal for serving small static files that need to be served
+    /// quickly. Content is loaded at startup and cached in memory with optional
+    /// pre-compressed versions for different encodings (gzip, brotli, zstd).
+    /// 
+    /// Since FastResource holds pre-loaded content, it should typically be registered
+    /// as a singleton in the IoC container:
+    /// 
+    /// ```vala
+    /// var resource = new FastResource.from_string("/", "<html>...</html>")
+    ///     .with_content_type("text/html")
+    ///     .with_default_compressors();
+    /// application.container.register_singleton<Endpoint>(resource)
+    ///     .with_metadata<EndpointRoute>(new EndpointRoute("/"));
+    /// ```
     public class FastResource : Object, Endpoint {
     public class FastResource : Object, Endpoint {
-        public string route { get { return _route; } }
-        public Astralis.Method[] methods { owned get { return { Method.GET };} }
-
-        private string _route;
+        
         private Dictionary<string, string> headers;
         private Dictionary<string, string> headers;
         private Dictionary<string, ByteBuffer> encodings;
         private Dictionary<string, ByteBuffer> encodings;
         private Series<string> encoding_priority;
         private Series<string> encoding_priority;
         private string e_tag;
         private string e_tag;
         
         
-        public FastResource(string route, string server_path) throws Error {
-            this._route = route;
+        public FastResource(string server_path) throws Error {
             uint8[] data;
             uint8[] data;
-            FileUtils.get_data (server_path, out data);
+            FileUtils.get_data(server_path, out data);
             shared_setup(new ByteBuffer.from_byte_array(data));
             shared_setup(new ByteBuffer.from_byte_array(data));
         }
         }
 
 
-        public FastResource.from_byte_array(string route, uint8[] byte_array) throws Error {
-            this._route = route;
+        public FastResource.from_byte_array(uint8[] byte_array) throws Error {
             shared_setup(new ByteBuffer.from_byte_array(byte_array));
             shared_setup(new ByteBuffer.from_byte_array(byte_array));
         }
         }
 
 
-        public FastResource.from_data(string route, BinaryData data) throws Error {
-            this._route = route;
+        public FastResource.from_data(BinaryData data) throws Error {
             shared_setup(data.to_byte_buffer());
             shared_setup(data.to_byte_buffer());
         }
         }
 
 
-        public FastResource.from_string(string route, string response_string) throws Error {
-            this._route = route;
+        public FastResource.from_string(string response_string) throws Error {
             shared_setup(new ByteBuffer.from_byte_array(response_string.data));
             shared_setup(new ByteBuffer.from_byte_array(response_string.data));
         }
         }
 
 
@@ -66,6 +75,7 @@ namespace Astralis {
             encodings = new Dictionary<string, ByteBuffer>();
             encodings = new Dictionary<string, ByteBuffer>();
             encodings["identity"] = data;
             encodings["identity"] = data;
             headers = new Dictionary<string, string>();
             headers = new Dictionary<string, string>();
+            encoding_priority = new Series<string>();
             encoding_priority.add("identity");
             encoding_priority.add("identity");
 
 
             var checksum = new Checksum(ChecksumType.SHA256);
             var checksum = new Checksum(ChecksumType.SHA256);
@@ -74,7 +84,7 @@ namespace Astralis {
             e_tag = @"\"$(checksum.get_string())\"";
             e_tag = @"\"$(checksum.get_string())\"";
         }
         }
 
 
-        public async HttpResult handle_request (HttpContext http_context, RouteInformation route_context) throws Error {
+        public async HttpResult handle_request (HttpContext http_context, RouteContext route_context) throws Error {
             var if_none_match = http_context.request.get_header("If-None-Match");
             var if_none_match = http_context.request.get_header("If-None-Match");
             if(if_none_match != null && if_none_match == e_tag) {
             if(if_none_match != null && if_none_match == e_tag) {
                 return new HttpEmptyResult(StatusCode.NOT_MODIFIED);
                 return new HttpEmptyResult(StatusCode.NOT_MODIFIED);

+ 3 - 11
src/Endpoints/FilesystemResource.vala

@@ -14,14 +14,6 @@ namespace Astralis {
         /// The filesystem path to serve files from
         /// The filesystem path to serve files from
         public string server_path { get; private set; }
         public string server_path { get; private set; }
         
         
-        /// The route pattern for this resource
-        public string route { get { return _route; } }
-        
-        /// Only GET and HEAD methods are supported for file serving
-        public Method[] methods { owned get {
-            return new Method[] { Method.GET, Method.HEAD };
-        }}
-        
         /// Whether to allow directory listings (only for /* and /** routes)
         /// Whether to allow directory listings (only for /* and /** routes)
         public bool allow_directory_listing { get; set; default = false; }
         public bool allow_directory_listing { get; set; default = false; }
         
         
@@ -51,7 +43,7 @@ namespace Astralis {
             this.server_path = server_path;
             this.server_path = server_path;
         }
         }
         
         
-        public async HttpResult handle_request(HttpContext http_context, RouteInformation route_context) throws Error {
+        public async HttpResult handle_request(HttpContext http_context, RouteContext route_context) throws Error {
             // Get the remaining path components after the route prefix
             // Get the remaining path components after the route prefix
             string relative_path = get_relative_path(route_context);
             string relative_path = get_relative_path(route_context);
             print(@"Getting $(relative_path)\n");
             print(@"Getting $(relative_path)\n");
@@ -196,7 +188,7 @@ namespace Astralis {
             return result;
             return result;
         }
         }
         
         
-        private string get_relative_path(RouteInformation route_context) {
+        private string get_relative_path(RouteContext route_context) {
             // For exact match, use the last component of the route
             // For exact match, use the last component of the route
             if (_match_type == MatchType.EXACT) {
             if (_match_type == MatchType.EXACT) {
                 return _route_components.length > 0 ? _route_components[_route_components.length - 1] : "";
                 return _route_components.length > 0 ? _route_components[_route_components.length - 1] : "";
@@ -204,7 +196,7 @@ namespace Astralis {
             
             
             // For wildcard matches, get the remaining path from the named components
             // For wildcard matches, get the remaining path from the named components
             string? remaining = null;
             string? remaining = null;
-            route_context.named_components.try_get("**", out remaining);
+            route_context.mapped_parameters.try_get("**", out remaining);
             if (remaining != null && remaining.length > 0) {
             if (remaining != null && remaining.length > 0) {
                 return remaining;
                 return remaining;
             }
             }

+ 3 - 2
src/meson.build

@@ -5,6 +5,7 @@ sources = files(
     'Core/AsyncInput.vala',
     'Core/AsyncInput.vala',
     'Core/AsyncOutput.vala',
     'Core/AsyncOutput.vala',
     'Core/Pipeline.vala',
     'Core/Pipeline.vala',
+    'Core/WebApplication.vala',
     'Data/FormDataParser.vala',
     'Data/FormDataParser.vala',
     'Components/EndpointRouter.vala',
     'Components/EndpointRouter.vala',
     'Components/Compressor.vala',
     'Components/Compressor.vala',
@@ -23,12 +24,12 @@ sources = files(
 
 
 libastralis = shared_library('astralis',
 libastralis = shared_library('astralis',
     sources,
     sources,
-    dependencies: [glib_dep, gobject_dep, mhd_dep, gio_dep, gio_unix_dep, invercargill_dep, invercargill_json_dep, json_glib_dep, zlib_dep, brotli_dep, zstd_dep],
+    dependencies: [glib_dep, gobject_dep, mhd_dep, gio_dep, gio_unix_dep, invercargill_dep, invercargill_json_dep, json_glib_dep, zlib_dep, brotli_dep, zstd_dep, inversion_dep],
     install: true
     install: true
 )
 )
 
 
 astralis_dep = declare_dependency(
 astralis_dep = declare_dependency(
     link_with: libastralis,
     link_with: libastralis,
     include_directories: include_directories('.'),
     include_directories: include_directories('.'),
-    dependencies: [glib_dep, gobject_dep, invercargill_dep, invercargill_json_dep, mhd_dep, json_glib_dep, brotli_dep, zstd_dep] # Users of astralis need glib, gobject, invercargill, mhd, brotli, and zstd
+    dependencies: [glib_dep, gobject_dep, invercargill_dep, invercargill_json_dep, mhd_dep, json_glib_dep, brotli_dep, zstd_dep, inversion_dep] # Users of astralis need glib, gobject, invercargill, mhd, brotli, and zstd
 )
 )