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
 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
 
 This example demonstrates various Invercargill data structures:
@@ -32,9 +30,7 @@ Endpoints:
 
 // Series operations
 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];
         series_array[0] = "First";
         series_array[1] = "Second";
@@ -59,9 +55,7 @@ class SeriesEndpoint : Object, Endpoint {
 
 // Vector operations
 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];
         numbers[0] = 1;
         numbers[1] = 2;
@@ -87,9 +81,7 @@ class VectorEndpoint : Object, Endpoint {
 
 // RingBuffer operations
 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];
         buffer_array[0] = "Item 1";
         buffer_array[1] = "Item 2";
@@ -112,9 +104,7 @@ class RingBufferEndpoint : Object, Endpoint {
 
 // ImmutableBuffer operations
 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];
         items_array[0] = "Apple";
         items_array[1] = "Banana";
@@ -143,9 +133,7 @@ class ImmutableBufferEndpoint : Object, Endpoint {
 
 // Wrap utility functions
 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];
         numbers[0] = 1;
         numbers[1] = 2;
@@ -175,9 +163,7 @@ class WrapOperationsEndpoint : Object, Endpoint {
 
 // Combined operations
 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];
         numbers1[0] = 1;
         numbers1[1] = 2;
@@ -219,19 +205,28 @@ class CombinedOperationsEndpoint : Object, Endpoint {
 }
 
 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("Try these endpoints:\n");
@@ -243,7 +238,7 @@ void main() {
     print("  - http://localhost:8086/wrap-operations\n");
     print("  - http://localhost:8086/combined-operations\n");
     
-    server.run();
+    application.run();
 }
 
 // Helper functions

+ 33 - 38
examples/EnumerableOperations.vala

@@ -11,9 +11,7 @@ using Invercargill.DataStructures;
 
 // Root handler
 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
 
 This example demonstrates various LINQ-like operations available in Invercargill Enumerable:
@@ -32,9 +30,7 @@ Endpoints:
 
 // Filtering examples
 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 parts = new Series<string>();
@@ -79,9 +75,7 @@ class FilterEndpoint : Object, Endpoint {
 
 // Mapping/Projection examples
 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 parts = new Series<string>();
@@ -126,9 +120,7 @@ class MapEndpoint : Object, Endpoint {
 
 // Grouping examples
 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>({
             "apple", "banana", "cherry", "apricot", 
             "blueberry", "blackberry", "coconut"
@@ -159,9 +151,7 @@ class GroupByEndpoint : Object, Endpoint {
 
 // Aggregation examples
 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 parts = new Series<string>();
@@ -207,9 +197,7 @@ class AggregateEndpoint : Object, Endpoint {
 
 // Sorting examples
 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 parts = new Series<string>();
@@ -239,9 +227,7 @@ class SortEndpoint : Object, Endpoint {
 
 // Set operations
 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 set2 = Wrap.array<int>({4, 5, 6, 7, 8});
         
@@ -279,9 +265,7 @@ class SetOperationsEndpoint : Object, Endpoint {
 
 // Advanced operations
 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 parts = new Series<string>();
@@ -318,20 +302,31 @@ class AdvancedEndpoint : Object, Endpoint {
 }
 
 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("Try these endpoints:\n");
@@ -344,7 +339,7 @@ void main() {
     print("  - http://localhost:8087/set-operations\n");
     print("  - http://localhost:8087/advanced\n");
     
-    server.run();
+    application.run();
 }
 
 // Helper functions

+ 57 - 60
examples/ErrorHandling.vala

@@ -11,9 +11,7 @@ using Invercargill.DataStructures;
 
 // Root handler
 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
 
 This example demonstrates various error handling patterns in Astralis:
@@ -37,9 +35,7 @@ Endpoints:
 
 // 200 OK response
 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\" }")
             .set_header("Content-Type", "application/json");
     }
@@ -47,9 +43,7 @@ class OkEndpoint : Object, Endpoint {
 
 // 404 Not Found
 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\" }")
             .set_header("Content-Type", "application/json");
     }
@@ -57,9 +51,7 @@ class NotFoundEndpoint : Object, Endpoint {
 
 // 400 Bad Request
 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\" }")
             .set_header("Content-Type", "application/json");
     }
@@ -67,9 +59,7 @@ class BadRequestEndpoint : Object, Endpoint {
 
 // 500 Internal Server Error
 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\" }")
             .set_header("Content-Type", "application/json");
     }
@@ -77,9 +67,7 @@ class InternalErrorEndpoint : Object, Endpoint {
 
 // Input validation example
 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 age_str = http_context.request.get_query("age");
         
@@ -128,10 +116,9 @@ class ValidationEndpoint : Object, Endpoint {
 
 // Resource lookup with error handling
 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;
         
         if (!int.try_parse(id_str, out id)) {
@@ -163,10 +150,9 @@ class ResourceEndpoint : Object, Endpoint {
 
 // API endpoint simulation
 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
         switch (endpoint) {
@@ -189,9 +175,7 @@ class ApiEndpoint : Object, Endpoint {
 
 // Custom error response
 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 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)
 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\" }")
             .set_header("Content-Type", "application/json");
     }
@@ -221,9 +203,7 @@ class TimeoutEndpoint : Object, Endpoint {
 
 // 401 Unauthorized
 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");
         
         if (auth_header == null) {
@@ -246,9 +226,7 @@ class UnauthorizedEndpoint : Object, Endpoint {
 
 // 403 Forbidden
 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";
         
         if (user_role != "admin") {
@@ -263,9 +241,7 @@ class ForbiddenEndpoint : Object, Endpoint {
 
 // 405 Method Not Allowed
 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\" }")
             .set_header("Content-Type", "application/json")
             .set_header("Allow", "POST, PUT, DELETE");
@@ -273,25 +249,46 @@ class MethodNotAllowedEndpoint : Object, Endpoint {
 }
 
 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("Try these endpoints:\n");
@@ -310,7 +307,7 @@ void main() {
     print("  - http://localhost:8088/forbidden\n");
     print("  - http://localhost:8088/method-not-allowed\n");
     
-    server.run();
+    application.run();
 }
 
 // Helper functions

+ 481 - 80
examples/FastResources.vala

@@ -15,6 +15,10 @@ using Invercargill.DataStructures;
  *   2. from_byte_array - For serving binary data (like images)
  *   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]
  * 
  * Examples:
@@ -22,58 +26,417 @@ using Invercargill.DataStructures;
  *   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>
 <html lang="en">
 <head>
@@ -139,8 +502,8 @@ void main(string[] args) {
         
         <div class="endpoint">
             <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 class="endpoint">
@@ -153,44 +516,82 @@ void main(string[] args) {
     <div class="card">
         <h2>FastResource Constructors</h2>
         <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>
     </div>
 </body>
 </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
-        // 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
-        // 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
-        server.run();
+        application.run();
         
     } catch (Error e) {
         printerr("Error: %s\n", e.message);

+ 19 - 26
examples/FileServer.vala

@@ -25,10 +25,7 @@ class ServerInfoEndpoint : Object, Endpoint {
         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
         
 Serving: $served_directory
@@ -97,27 +94,6 @@ void main(string[] args) {
         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("╔══════════════════════════════════════════════════════════════╗\n");
     print("║                  Astralis File Server                        ║\n");
@@ -143,6 +119,23 @@ void main(string[] args) {
     print("╚══════════════════════════════════════════════════════════════╝\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
-    server.run();
+    application.run();
 }

+ 31 - 34
examples/FormData.vala

@@ -11,10 +11,7 @@ using Invercargill.DataStructures;
 
 // HTML form page handler
 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>
 <html>
@@ -130,9 +127,7 @@ class FormPageEndpoint : Object, Endpoint {
 
 // Simple form submission handler
 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
         FormData form_data = yield FormDataParser.parse(
             context.request.request_body,
@@ -165,9 +160,7 @@ class SimpleFormEndpoint : Object, Endpoint {
 
 // Registration form submission handler
 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(
             context.request.request_body,
             context.request.content_type
@@ -217,9 +210,7 @@ class RegisterFormEndpoint : Object, Endpoint {
 
 // Search form submission handler
 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(
             context.request.request_body,
             context.request.content_type
@@ -275,9 +266,7 @@ class SearchFormEndpoint : Object, Endpoint {
 
 // File upload handler (multipart/form-data)
 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(
             context.request.request_body,
             context.request.content_type
@@ -310,9 +299,7 @@ class FileUploadEndpoint : Object, Endpoint {
 
 // Form debug tool handler
 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;
         string? body_text = null;
         
@@ -484,24 +471,34 @@ class Product {
 }
 
 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("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
 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>();
         parts.add("Request Headers:\n");
         parts.add("================\n\n");
@@ -34,9 +32,7 @@ class HeadersEndpoint : Object, Endpoint {
 
 // Check for user-agent header
 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";
         
         return new HttpStringResult(@"Your User-Agent is: $user_agent");
@@ -45,9 +41,7 @@ class UserAgentEndpoint : Object, Endpoint {
 
 // Check content type and content length
 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>();
         parts.add("Content Information:\n");
         parts.add("===================\n\n");
@@ -64,9 +58,7 @@ class ContentInfoEndpoint : Object, Endpoint {
 
 // Check if header exists
 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 exists = http_context.request.has_header(header_name);
         var value = http_context.request.get_header(header_name);
@@ -77,9 +69,7 @@ class CheckHeaderEndpoint : Object, Endpoint {
 
 // Set custom response headers
 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!")
             .set_header("X-Custom-Header", "Custom-Value")
             .set_header("X-Request-Id", generate_request_id())
@@ -90,9 +80,7 @@ class CustomHeadersEndpoint : Object, Endpoint {
 
 // Display all cookies
 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>();
         parts.add("Request Cookies:\n");
         parts.add("================\n\n");
@@ -118,9 +106,7 @@ class CookiesEndpoint : Object, Endpoint {
 
 // Get specific cookie
 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 value = http_context.request.get_cookie(name);
         
@@ -134,9 +120,7 @@ class GetCookieEndpoint : Object, Endpoint {
 
 // Set a cookie (via Set-Cookie header)
 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 value = http_context.request.get_query_or_default("value", "123");
         var max_age = http_context.request.get_query_or_default("max_age", "3600");
@@ -148,9 +132,7 @@ class SetCookieEndpoint : Object, Endpoint {
 
 // Set multiple cookies
 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
         // special handling - this example shows the approach
         return new HttpStringResult("Multiple cookies set!")
@@ -162,9 +144,7 @@ class SetCookiesEndpoint : Object, Endpoint {
 
 // Delete a cookie
 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");
         
         return new HttpStringResult(@"Cookie '$name' deleted")
@@ -174,9 +154,7 @@ class DeleteCookieEndpoint : Object, Endpoint {
 
 // Check if cookie exists
 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 exists = http_context.request.has_cookie(name);
         
@@ -186,9 +164,7 @@ class HasCookieEndpoint : Object, Endpoint {
 
 // Cookie-based session simulation
 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");
         
         if (session_id == null) {
@@ -212,9 +188,7 @@ Your session is active.");
 
 // CORS headers example
 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") ?? "*";
         
         return new HttpStringResult(@"CORS enabled for origin: $origin")
@@ -227,9 +201,7 @@ class CorsEndpoint : Object, Endpoint {
 
 // Cache control headers
 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");
         
         return new HttpStringResult(@"This response is cached ($cache_type cache, 1 hour)")
@@ -241,9 +213,7 @@ class CacheEndpoint : Object, Endpoint {
 
 // Content negotiation
 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") ?? "*/*";
         
         if (accept.contains("application/json")) {
@@ -261,9 +231,7 @@ class NegotiateEndpoint : Object, Endpoint {
 
 // Security headers
 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!")
             .set_header("X-Content-Type-Options", "nosniff")
             .set_header("X-Frame-Options", "DENY")
@@ -275,28 +243,55 @@ class SecureEndpoint : Object, Endpoint {
 }
 
 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("Try these endpoints:\n");
@@ -311,7 +306,7 @@ void main() {
     print("  - http://localhost:8083/negotiate\n");
     print("  - http://localhost:8083/secure\n");
     
-    server.run();
+    application.run();
 }
 
 // Helper functions

+ 54 - 56
examples/JsonApi.vala

@@ -12,9 +12,7 @@ using Invercargill.DataStructures;
 
 // API root handler
 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 = @"{
   \"name\": \"Astralis JSON API\",
   \"version\": \"1.0.0\",
@@ -34,9 +32,7 @@ class ApiRootEndpoint : Object, Endpoint {
 
 // List all users
 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 json_parts = new Series<string>();
@@ -64,11 +60,10 @@ class UsersListEndpoint : Object, Endpoint {
 
 // Get user by ID
 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);
         
         if (user == null) {
@@ -83,9 +78,7 @@ class UserByIdEndpoint : Object, Endpoint {
 
 // List all posts
 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();
         
         // Filter by user_id if provided
@@ -122,11 +115,10 @@ class PostsListEndpoint : Object, Endpoint {
 
 // Get post by ID
 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);
         
         if (post == null) {
@@ -168,9 +160,7 @@ class PostByIdEndpoint : Object, Endpoint {
 
 // List all comments
 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();
         
         // Filter by post_id if provided
@@ -207,11 +197,10 @@ class CommentsListEndpoint : Object, Endpoint {
 
 // Get comment by ID
 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);
         
         if (comment == null) {
@@ -226,9 +215,7 @@ class CommentByIdEndpoint : Object, Endpoint {
 
 // API statistics
 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 posts = get_posts();
         var comments = get_comments();
@@ -255,9 +242,7 @@ class StatsEndpoint : Object, Endpoint {
 
 // Search 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");
         
         if (query == null || query == "") {
@@ -305,9 +290,7 @@ class SearchEndpoint : Object, Endpoint {
 
 // Pagination example
 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 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
 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");
         
         switch (error_type) {
@@ -363,23 +344,40 @@ class ApiErrorEndpoint : Object, Endpoint {
 }
 
 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("Try these endpoints:\n");
@@ -395,7 +393,7 @@ void main() {
     print("  - http://localhost:8085/api/search?q=test\n");
     print("  - http://localhost:8085/api/posts/paginated?page=1&per_page=5\n");
     
-    server.run();
+    application.run();
 }
 
 // Helper functions

+ 60 - 59
examples/PathRouting.vala

@@ -11,9 +11,7 @@ using Invercargill.DataStructures;
 
 // Root handler - shows available endpoints
 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!
 
 Available endpoints:
@@ -33,28 +31,23 @@ Available endpoints:
 
 // Simple hello handler
 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!");
     }
 }
 
 // Hello with name handler - uses named segment
 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
 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>();
         users.add(new User(1, "Alice", "alice@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
 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
         var users = new Series<User>();
@@ -109,11 +101,10 @@ class UserByIdEndpoint : Object, Endpoint {
 
 // User posts handler - uses named segment
 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
         var posts = new Series<Post>();
@@ -142,9 +133,7 @@ class UserPostsEndpoint : Object, Endpoint {
 
 // API status handler
 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>();
         status.set("status", "operational");
         status.set("version", "1.0.0");
@@ -172,11 +161,10 @@ class ApiStatusEndpoint : Object, Endpoint {
 
 // API item handler - uses named segment
 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>();
         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
 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
         var files = new Dictionary<string, Dictionary<string, ExampleFile>>();
@@ -216,9 +204,9 @@ class FilesEndpoint : Object, Endpoint {
         files.set("images", images);
         
         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;
-            if (category_files.try_get(name, out file)) {
+            if (category_files.try_get(name ?? "", out file)) {
                 return new HttpStringResult(file.to_json())
                     .set_header("Content-Type", "application/json");
             }
@@ -231,9 +219,7 @@ class FilesEndpoint : Object, Endpoint {
 
 // Path info handler
 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>();
         parts.add("Path Information:\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
         parts.add("\n  Named segments:\n");
-        route_info.named_components.to_immutable_buffer()
+        route_info.mapped_parameters.to_immutable_buffer()
             .iterate((kv) => {
                 parts.add(@"    $(kv.key): $(kv.value)\n");
             });
@@ -263,22 +249,37 @@ class PathInfoEndpoint : Object, Endpoint {
 }
 
 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("Try these endpoints:\n");
@@ -293,7 +294,7 @@ void main() {
     print("  - http://localhost:8082/files/docs/readme.txt\n");
     print("  - http://localhost:8082/pathinfo?test=1\n");
     
-    server.run();
+    application.run();
 }
 
 // Helper classes for the example

+ 21 - 26
examples/QueryParameters.vala

@@ -12,9 +12,7 @@ using Invercargill.DataStructures;
 
 // Greet handler - simple query parameter access
 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 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
 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 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"));
@@ -63,9 +59,7 @@ class SearchEndpoint : Object, Endpoint {
 
 // Debug handler - boolean flag query parameters
 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 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)
 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");
         
         if (tags_param == null) {
@@ -130,9 +122,7 @@ class FilterEndpoint : Object, Endpoint {
 
 // Range handler - numeric query parameters with range validation
 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 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"));
@@ -167,17 +157,22 @@ class RangeEndpoint : Object, Endpoint {
 }
 
 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("Try these endpoints:\n");
@@ -187,5 +182,5 @@ void main() {
     print("  - http://localhost:8081/filter?tags=foo,bar,baz\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
 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;
         
         // Access the remote address
@@ -44,16 +41,13 @@ public class RemoteAddressEndpoint : Object, Endpoint {
 }
 
 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("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
 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");
         return new HttpStringResult("Hello from Astralis!");
     }
@@ -14,9 +12,7 @@ class HelloEndpoint : Object, Endpoint {
 
 // Simple handler for /json 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");
         return new HttpStringResult("{ \"message\": \"Hello JSON\" }")
             .set_header("Content-Type", "application/json");
@@ -24,13 +20,12 @@ class JsonEndpoint : Object, Endpoint {
 }
 
 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')
 mhd_dep = dependency('libmicrohttpd')
 invercargill_dep = dependency('invercargill-1')
+inversion_dep = dependency('inversion-0.1')
 json_glib_dep = dependency('json-glib-1.0')
 invercargill_json_dep = dependency('invercargill-json')
 zlib_dep = dependency('zlib')

+ 1 - 0
src/Components/Compressor.vala

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

+ 75 - 72
src/Components/EndpointRouter.vala

@@ -1,64 +1,35 @@
 using Invercargill;
 using Invercargill.DataStructures;
+using Inversion;
 
 namespace Astralis {
 
     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 {
             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);
             }
 
-            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.
             if(endpoint.route == "*") {
                 return true;
@@ -131,12 +102,25 @@ namespace Astralis {
             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 "**"
-            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("/"))
                     .where(c => c.length != 0)
                     .to_immutable_buffer();
@@ -144,8 +128,8 @@ namespace Astralis {
                 // Extract named components from the prefix
                 for(int i = 0; i < prefix_components.length; i++) {
                     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
                 var builder = new StringBuilder();
                 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) {
                         builder.append("/");
                     }
-                    builder.append(path_components[i]);
+                    builder.append(matched_route.route_segments[i]);
                     first = false;
                 }
-                dictionary.add("**", builder.str);
-                return dictionary;
+                parameter_map.add("**", builder.str);
+                this.mapped_parameters = parameter_map;
+                return;
             }
 
             // 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)
                 .to_immutable_buffer();
 
@@ -176,34 +161,52 @@ namespace Astralis {
                 if(!endpoint_components[i].has_prefix("{") || !endpoint_components[i].has_suffix("}")) {
                     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 {

+ 9 - 32
src/Core/Pipeline.vala

@@ -1,26 +1,20 @@
 using Invercargill.DataStructures;
+using Inversion;
+
 namespace Astralis {
 
     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 {
-            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) {
                 throw new PipelineError.NO_COMPONENTS("No components in pipeline");
@@ -41,10 +35,9 @@ namespace Astralis {
         private PipelineComponent? next_component;
         private PipelineContext? next_context;
         private HttpContext http_context;
-
         internal PipelineContext(HttpContext http_context, PipelineComponent? next_component, PipelineContext? next_context) {
-            this.next_component = next_component;
             this.http_context = http_context;
+            this.next_component = next_component;
             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 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 {
         END_OF_PIPELINE_REACHED,
         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 {
 
     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 {
 
+    /// 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 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, ByteBuffer> encodings;
         private Series<string> encoding_priority;
         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;
-            FileUtils.get_data (server_path, out data);
+            FileUtils.get_data(server_path, out 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));
         }
 
-        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());
         }
 
-        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));
         }
 
@@ -66,6 +75,7 @@ namespace Astralis {
             encodings = new Dictionary<string, ByteBuffer>();
             encodings["identity"] = data;
             headers = new Dictionary<string, string>();
+            encoding_priority = new Series<string>();
             encoding_priority.add("identity");
 
             var checksum = new Checksum(ChecksumType.SHA256);
@@ -74,7 +84,7 @@ namespace Astralis {
             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");
             if(if_none_match != null && if_none_match == e_tag) {
                 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
         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)
         public bool allow_directory_listing { get; set; default = false; }
         
@@ -51,7 +43,7 @@ namespace Astralis {
             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
             string relative_path = get_relative_path(route_context);
             print(@"Getting $(relative_path)\n");
@@ -196,7 +188,7 @@ namespace Astralis {
             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
             if (_match_type == MatchType.EXACT) {
                 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
             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) {
                 return remaining;
             }

+ 3 - 2
src/meson.build

@@ -5,6 +5,7 @@ sources = files(
     'Core/AsyncInput.vala',
     'Core/AsyncOutput.vala',
     'Core/Pipeline.vala',
+    'Core/WebApplication.vala',
     'Data/FormDataParser.vala',
     'Components/EndpointRouter.vala',
     'Components/Compressor.vala',
@@ -23,12 +24,12 @@ sources = files(
 
 libastralis = shared_library('astralis',
     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
 )
 
 astralis_dep = declare_dependency(
     link_with: libastralis,
     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
 )