Просмотр исходного кода

feat(di): add try-inject patterns, registration metadata, and build config

- Add try_get_injection_context(), try_inject<T>(), and try_inject_all<T>()
  for safer injection without throwing on missing context
- Add metadata support to Registration with with_metadata<T>() and get_metadata<T>()
- Add get_registration(), get_registrations(), and get_all_registrations() to Scope
- Add get_all_registrations() to Container
- Fix resolve_all<T>() return type to Enumerable<T>
- Add generic as<T>() helper to Registration
- Update build system with versioned library name, pkg-config, and GIR/typelib generation
Billy Barrow 1 неделя назад
Родитель
Сommit
316168e059

+ 1 - 1
meson.build

@@ -1,5 +1,5 @@
 project('inversion', ['c', 'vala'],
-  version: '0.1.0',
+  version: '0.1',
 )
 
 # Dependencies

+ 162 - 0
slopdocs/library.inversion.container.md

@@ -0,0 +1,162 @@
+# Container Class
+
+## Overview
+`Container` is the inversion of control container that manages component registrations and lifecycles. It stores registrations, manages singleton instances, creates scopes, and provides dependency resolution.
+
+## Namespace
+`Inversion`
+
+## Class Declaration
+```vala
+public class Container : Object
+```
+
+## Constructor
+
+### `Container()`
+Creates a new container with empty registrations. The container automatically registers itself as a singleton.
+
+```vala
+var container = new Container();
+```
+
+## Registration Methods
+
+### `register<T>(owned FactoryDelegate factory_func, Lifecycle lifecycle)`
+Registers a type with a factory delegate and specified lifecycle. Returns a `Registration` for fluent configuration.
+
+```vala
+container.register<MyService>((scope) => new MyService(), Lifecycle.SINGLETON);
+```
+
+### `register_transient<T>(owned FactoryDelegate factory_func)`
+Registers a type as transient (new instance per resolve).
+
+```vala
+container.register_transient<MyService>((scope) => new MyService());
+```
+
+### `register_scoped<T>(owned FactoryDelegate factory_func)`
+Registers a type as scoped (one instance per scope).
+
+```vala
+container.register_scoped<MyService>((scope) => new MyService());
+```
+
+### `register_singleton<T>(owned FactoryDelegate factory_func)`
+Registers a type as singleton (one instance per container).
+
+```vala
+container.register_singleton<MyService>((scope) => new MyService());
+```
+
+### `register_type(Type implementation_type, owned FactoryDelegate factory_func, Lifecycle lifecycle)`
+Non-generic version for runtime type registration.
+
+```vala
+container.register_type(typeof(MyService), (scope) => new MyService(), Lifecycle.TRANSIENT);
+```
+
+### `register_factory_type(Type implementation_type, Factory factory, Lifecycle lifecycle)`
+Registers with a custom `Factory` instance.
+
+```vala
+var factory = new MyCustomFactory();
+container.register_factory_type(typeof(MyService), factory, Lifecycle.SINGLETON);
+```
+
+### `register_factory<T>(Factory factory, Lifecycle lifecycle)`
+Generic version of factory registration.
+
+```vala
+container.register_factory<MyService>(custom_factory, Lifecycle.SCOPED);
+```
+
+## Query Methods
+
+### `is_registered(Type type) → bool`
+Checks if a type has any registrations.
+
+```vala
+if (container.is_registered(typeof(Logger))) {
+    // Type is registered
+}
+```
+
+### `get_registration(Type service_type) throws ContainerError → Registration`
+Gets any registration for a type. Throws `ContainerError.NOT_REGISTERED` if not found.
+
+```vala
+try {
+    var reg = container.get_registration(typeof(IMyService));
+} catch (ContainerError.NOT_REGISTERED e) {
+    // Not registered
+}
+```
+
+### `get_registrations(Type service_type) → Enumerable<Registration>`
+Gets all registrations for a type (may be empty).
+
+```vala
+var regs = container.get_registrations(typeof(Logger));
+// Iterate over all Logger registrations
+```
+
+## Scope Creation
+
+### `create_scope() → Scope`
+Creates a new scope for scoped instance resolution.
+
+```vala
+var scope = container.create_scope();
+var service = scope.resolve<MyService>();
+```
+
+### `create_transient_scope() → Scope`
+Creates a scope with transient lifecycle semantics.
+
+```vala
+var scope = container.create_transient_scope();
+```
+
+## Internal Methods
+
+### `get_or_create_singleton(Registration registration, Type requested_type) throws Error → Object`
+Gets existing singleton or creates new instance. Used internally by `Scope` during resolution.
+
+## Usage Example
+```vala
+var container = new Container();
+
+// Register implementations
+container.register_singleton<ConsoleLogger>((scope) => new ConsoleLogger())
+    .as_type(typeof(ILogger));
+
+container.register_scoped<UserService>((scope) => new UserService())
+    .as_type(typeof(IUserService));
+
+container.register_transient<RequestHandler>((scope) => new RequestHandler());
+
+// Create scope and resolve
+var scope = container.create_scope();
+var logger = scope.resolve<ILogger>();
+var users = scope.resolve_all<IUserService>();
+```
+
+## Multi-Registration
+Multiple implementations can be registered for the same service type:
+
+```vala
+container.register_singleton<FileLogger>((s) => new FileLogger())
+    .as_type(typeof(ILogger));
+container.register_singleton<ConsoleLogger>((s) => new ConsoleLogger())
+    .as_type(typeof(ILogger));
+
+// Resolve all implementations
+var scope = container.create_scope();
+var all_loggers = scope.resolve_all<ILogger>();
+```
+
+---
+
+The Container class is the central registry for Inversion IoC, providing registration methods for transient, scoped, and singleton lifecycles, scope creation, and singleton instance management.

+ 319 - 0
slopdocs/library.inversion.examples.md

@@ -0,0 +1,319 @@
+# Usage Examples
+
+## Overview
+Practical examples demonstrating Inversion IoC container features including basic registration, lifecycle management, field injection, multi-registration, and scope-local registrations.
+
+## Basic Registration and Resolution
+
+```vala
+using Inversion;
+
+public interface ILogger : Object {
+    public abstract void log(string message);
+}
+
+public class ConsoleLogger : Object, ILogger {
+    public void log(string message) {
+        stdout.printf("[LOG] %s\n", message);
+    }
+}
+
+public static int main(string[] args) {
+    var container = new Container();
+    
+    // Register implementation as interface
+    container.register_singleton<ConsoleLogger>((scope) => new ConsoleLogger())
+        .as_type(typeof(ILogger));
+    
+    // Create scope and resolve
+    var scope = container.create_scope();
+    var logger = scope.resolve<ILogger>();
+    logger.log("Hello, World!");
+    
+    return 0;
+}
+```
+
+## Lifecycle Patterns
+
+### Singleton (Application-Wide)
+```vala
+public class Configuration : Object {
+    public string db_path { get; set; }
+}
+
+container.register_singleton<Configuration>((s) => {
+    var config = new Configuration();
+    config.db_path = "/data/app.db";
+    return config;
+});
+
+// Same instance across all scopes
+var scope1 = container.create_scope();
+var scope2 = container.create_scope();
+var config1 = scope1.resolve<Configuration>();
+var config2 = scope2.resolve<Configuration>();
+// config1 == config2
+```
+
+### Scoped (Per-Request)
+```vala
+public class UserSession : Object {
+    public string user_id { get; set; }
+}
+
+container.register_scoped<UserSession>((s) => new UserSession());
+
+// Same instance within scope
+var scope = container.create_scope();
+var session1 = scope.resolve<UserSession>();
+var session2 = scope.resolve<UserSession>();
+// session1 == session2
+
+// Different instance in different scope
+var other_scope = container.create_scope();
+var session3 = other_scope.resolve<UserSession>();
+// session1 != session3
+```
+
+### Transient (Per-Resolution)
+```vala
+public class RequestHandler : Object {
+    public void handle(string request) {
+        stdout.printf("Handling: %s\n", request);
+    }
+}
+
+container.register_transient<RequestHandler>((s) => new RequestHandler());
+
+var scope = container.create_scope();
+var handler1 = scope.resolve<RequestHandler>();
+var handler2 = scope.resolve<RequestHandler>();
+// handler1 != handler2
+```
+
+## Field Injection
+
+```vala
+public interface IRepository : Object {
+    public abstract string get_data();
+}
+
+public interface IHandler : Object {
+    public abstract void handle(string request);
+}
+
+public class UserService : Object {
+    // Dependencies injected during construction
+    private IRepository repo = inject<IRepository>();
+    private IHandler handler = inject<IHandler>();
+    
+    public void process() {
+        var data = repo.get_data();
+        handler.handle(data);
+    }
+}
+
+// Register dependencies
+container.register_singleton<MemoryRepository>((s) => new MemoryRepository())
+    .as_type(typeof(IRepository));
+container.register_singleton<ConsoleHandler>((s) => new ConsoleHandler())
+    .as_type(typeof(IHandler));
+container.register_scoped<UserService>((s) => new UserService());
+
+// Resolve - dependencies injected automatically
+var scope = container.create_scope();
+var service = scope.resolve<UserService>();
+service.process();
+```
+
+## Multiple Implementations
+
+```vala
+public interface IValidator : Object {
+    public abstract bool validate(Object obj);
+}
+
+public class EmailValidator : Object, IValidator {
+    public bool validate(Object obj) {
+        stdout.printf("Validating email...\n");
+        return true;
+    }
+}
+
+public class PhoneValidator : Object, IValidator {
+    public bool validate(Object obj) {
+        stdout.printf("Validating phone...\n");
+        return true;
+    }
+}
+
+// Register multiple implementations
+container.register_singleton<EmailValidator>((s) => new EmailValidator())
+    .as_type(typeof(IValidator));
+container.register_singleton<PhoneValidator>((s) => new PhoneValidator())
+    .as_type(typeof(IValidator));
+
+// Resolve all implementations
+var scope = container.create_scope();
+var validators = scope.resolve_all<IValidator>();
+
+validators.iterate((obj) => {
+    var validator = (IValidator) obj;
+    validator.validate(data);
+});
+```
+
+## Inject All Pattern
+
+```vala
+public class ValidationService : Object {
+    // Inject all IValidator implementations
+    private Lot<IValidator> validators = inject_all<IValidator>();
+    
+    public bool validate_all(Object obj) {
+        foreach (var validator in validators) {
+            if (!validator.validate(obj)) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
+```
+
+## Scope-Local Registrations
+
+```vala
+public class RequestContext : Object {
+    public string request_id { get; set; }
+    public string path { get; set; }
+}
+
+// Container has application-wide services
+var container = new Container();
+container.register_singleton<Database>((s) => new Database());
+
+// Each request creates a scope with request-specific context
+void handle_request(string request_id, string path) {
+    var scope = container.create_scope();
+    
+    // Register request-specific context (only visible in this scope)
+    scope.register_local_scoped<RequestContext>((s) => {
+        var ctx = new RequestContext();
+        ctx.request_id = request_id;
+        ctx.path = path;
+        return ctx;
+    });
+    
+    // Handlers can resolve RequestContext
+    var handler = scope.resolve<RequestHandler>();
+    handler.process();
+}
+```
+
+## Custom Factory with Dependencies
+
+```vala
+public class OrderService : Object {
+    private IOrderRepository repo;
+    private ILogger logger;
+    
+    public OrderService(IOrderRepository repo, ILogger logger) {
+        this.repo = repo;
+        this.logger = logger;
+    }
+}
+
+container.register_scoped<OrderService>((scope) => {
+    // Resolve dependencies from scope
+    var repo = scope.resolve<IOrderRepository>();
+    var logger = scope.resolve<ILogger>();
+    return new OrderService(repo, logger);
+});
+```
+
+## Multiple Interface Registration
+
+```vala
+public interface IRepository : Object {
+    public abstract Object? find(int id);
+}
+
+public interface ICache : Object {
+    public abstract void cache(Object obj);
+}
+
+public class CachedRepository : Object, IRepository, ICache {
+    public Object? find(int id) { return null; }
+    public void cache(Object obj) { }
+}
+
+// Register as both interfaces
+container.register_singleton<CachedRepository>((s) => new CachedRepository())
+    .as_type(typeof(IRepository))
+    .as_type(typeof(ICache));
+
+// Can resolve by either interface
+var scope = container.create_scope();
+var repo = scope.resolve<IRepository>();
+var cache = scope.resolve<ICache>();
+// Both point to same CachedRepository instance
+```
+
+## Error Handling
+
+```vala
+try {
+    var service = scope.resolve<IMyService>();
+    service.do_work();
+} catch (ContainerError.NOT_REGISTERED e) {
+    stderr.printf("Service not registered: %s\n", e.message);
+} catch (ContainerError.CYCLE_DETECTED e) {
+    stderr.printf("Circular dependency: %s\n", e.message);
+} catch (ContainerError.ILLEGAL_LIFECYCLE_COMBINATION e) {
+    stderr.printf("Lifecycle error: %s\n", e.message);
+} catch (Error e) {
+    stderr.printf("Error: %s\n", e.message);
+}
+```
+
+## Web Application Pattern
+
+```vala
+public class ApplicationController : Object {
+    private static Container container;
+    
+    static construct {
+        container = new Container();
+        
+        // Application-wide services
+        container.register_singleton<Database>((s) => new Database("app.db"));
+        container.register_singleton<Cache>((s) => new RedisCache());
+        
+        // Per-request services
+        container.register_scoped<UserSession>((s) => new UserSession());
+        container.register_transient<RequestHandler>((s) => new RequestHandler());
+    }
+    
+    public void handle_request(Request req, Response res) {
+        var scope = container.create_scope();
+        
+        // Register request context
+        scope.register_local_scoped<RequestContext>((s) => {
+            return new RequestContext(req.id, req.path);
+        });
+        
+        try {
+            var handler = scope.resolve<RequestHandler>();
+            handler.handle(req, res);
+        } catch (Error e) {
+            res.error(500, e.message);
+        }
+    }
+}
+```
+
+---
+
+These examples demonstrate Inversion IoC patterns including basic registration, lifecycle management, field injection with inject<T>(), multi-registration with resolve_all<T>(), and scope-local registrations for per-request contexts.

+ 194 - 0
slopdocs/library.inversion.factories.md

@@ -0,0 +1,194 @@
+# Factory Classes
+
+## Overview
+Factories create component instances in Inversion. The `Factory` interface defines the contract, with `DelegateFactory` and `InstanceFactory` as built-in implementations. Custom factories can implement complex instantiation logic.
+
+## Namespace
+`Inversion`
+
+## Factory Interface
+
+### Declaration
+```vala
+public interface Factory : Object
+```
+
+### Method
+
+#### `create(Scope scope) throws Error → Object`
+Creates a new instance of the component. The scope parameter enables dependency resolution during creation.
+
+```vala
+public interface Factory : Object {
+    public abstract Object create(Scope scope) throws Error;
+}
+```
+
+## DelegateFactory Class
+
+### Overview
+`DelegateFactory` wraps a `FactoryDelegate` function for instance creation. This is the most commonly used factory, created automatically by container registration methods.
+
+### Declaration
+```vala
+public class DelegateFactory : Object, Factory
+```
+
+### Constructor
+```vala
+public DelegateFactory(owned FactoryDelegate delegate)
+```
+
+### Usage
+```vala
+// Container methods create DelegateFactory internally
+container.register_singleton<MyService>((scope) => new MyService());
+
+// Equivalent manual creation
+var factory = new DelegateFactory((scope) => new MyService());
+container.register_factory<MyService>(factory, Lifecycle.SINGLETON);
+```
+
+### With Dependencies
+```vala
+container.register_scoped<OrderService>((scope) => {
+    var repo = scope.resolve<IOrderRepository>();
+    var logger = scope.resolve<ILogger>();
+    return new OrderService(repo, logger);
+});
+```
+
+## InstanceFactory Class
+
+### Overview
+`InstanceFactory` always returns a pre-existing instance. Used for scope-local instance caching and registering already-created objects.
+
+### Declaration
+```vala
+public class InstanceFactory : Object, Factory
+```
+
+### Properties
+
+| Property | Type | Description |
+|----------|------|-------------|
+| `registration` | `Registration` | Associated registration |
+
+### Constructor
+```vala
+public InstanceFactory(Registration registration, Object instance)
+```
+
+### Usage
+```vala
+// Register an existing instance
+var existing_service = new MyConfiguredService();
+var registration = new Registration(typeof(MyConfiguredService), 
+    new InstanceFactory(reg, existing_service), 
+    Lifecycle.SINGLETON);
+```
+
+## FactoryDelegate
+
+### Declaration
+```vala
+public delegate Object FactoryDelegate(Scope scope) throws Error;
+```
+
+The delegate signature used by `DelegateFactory`. Receives the current scope for resolving dependencies.
+
+### Parameters
+- `scope`: The active scope for dependency resolution
+
+### Returns
+- A new instance of the component
+
+### Example
+```vala
+FactoryDelegate factory = (scope) => {
+    var dependency = scope.resolve<IDependency>();
+    return new MyService(dependency);
+};
+```
+
+## Custom Factory Implementation
+
+### When to Implement
+- Complex object construction
+- Integration with external frameworks
+- Conditional instance creation
+- Pool-based instance management
+
+### Example: Pool Factory
+```vala
+public class PoolFactory : Object, Factory {
+    private Type element_type;
+    private Queue<Object> pool = new Queue<Object>();
+    
+    public PoolFactory(Type element_type) {
+        this.element_type = element_type;
+    }
+    
+    public Object create(Scope scope) throws Error {
+        Object? pooled = pool.try_pop();
+        if (pooled != null) {
+            return pooled;
+        }
+        return Object.new(element_type);
+    }
+    
+    public void return_to_pool(Object instance) {
+        pool.push(instance);
+    }
+}
+
+// Usage
+var pool_factory = new PoolFactory(typeof(PooledObject));
+container.register_factory_type(typeof(PooledObject), pool_factory, Lifecycle.TRANSIENT);
+```
+
+### Example: Lazy Factory
+```vala
+public class LazyFactory : Object, Factory {
+    private Factory inner_factory;
+    private Object? cached_instance;
+    private Mutex mutex = Mutex();
+    
+    public LazyFactory(Factory inner) {
+        this.inner_factory = inner;
+    }
+    
+    public Object create(Scope scope) throws Error {
+        mutex.lock();
+        if (cached_instance == null) {
+            cached_instance = inner_factory.create(scope);
+        }
+        mutex.unlock();
+        return cached_instance;
+    }
+}
+```
+
+## Factory Selection by Lifecycle
+
+| Lifecycle | Factory Usage |
+|-----------|--------------|
+| TRANSIENT | Factory called on every resolve |
+| SCOPED | Factory called once per scope, then cached |
+| SINGLETON | Factory called once per container, then cached |
+
+## Integration with Registration
+
+Factories are associated with registrations:
+
+```vala
+// DelegateFactory created internally
+var reg = container.register_transient<MyService>((s) => new MyService());
+
+// Access factory if needed
+var factory = reg.factory;
+```
+
+---
+
+Inversion provides Factory interface with DelegateFactory for delegate-based creation and InstanceFactory for existing instances, supporting custom implementations for complex instantiation scenarios.

+ 162 - 0
slopdocs/library.inversion.injection.md

@@ -0,0 +1,162 @@
+# Field Injection
+
+## Overview
+Inversion provides field injection via `inject<T>()` and `inject_all<T>()` functions. These functions resolve dependencies during object construction when called within field initializers. The injection context is automatically established during container resolution.
+
+## Namespace
+`Inversion`
+
+## Injection Functions
+
+### `inject<T>() → T`
+Resolves a single dependency of type `T`. Returns `null` if resolution fails or no injection context exists.
+
+```vala
+public class UserService : Object {
+    private ILogger logger = inject<ILogger>();
+    
+    public void greet(string name) {
+        this.logger.log(@"Hello, $name!");
+    }
+}
+```
+
+### `inject_all<T>() → Lot<T>`
+Resolves all registered implementations of type `T`. Returns empty collection if resolution fails.
+
+```vala
+public class MultiHandler : Object {
+    private Lot<IHandler> handlers = inject_all<IHandler>();
+    
+    public void process_all(string request) {
+        handlers.iterate((h) => h.handle(request));
+    }
+}
+```
+
+### `try_inject<T>() → T?`
+Attempts to resolve a single dependency. Returns `null` if no injection context exists or resolution fails. Use when dependency is optional.
+
+```vala
+public class OptionalDeps : Object {
+    private ICache? cache = try_inject<ICache>();  // May be null
+}
+```
+
+### `try_inject_all<T>() → Lot<T>`
+Attempts to resolve all implementations. Returns empty collection if no injection context exists.
+
+```vala
+public class OptionalHandlers : Object {
+    private Lot<IHandler> handlers = try_inject_all<IHandler>();
+}
+```
+
+## Injection Context
+
+### How It Works
+1. Container resolves a type via `Scope.resolve<T>()`
+2. `create_with_injection_context()` establishes thread-local context
+3. Factory creates instance; field initializers execute
+4. Field initializers call `inject<T>()` which accesses current context
+5. Context is cleaned up after instance creation
+
+### `InjectionContext` Class
+Holds the current injection state:
+
+| Property | Type | Description |
+|----------|------|-------------|
+| `registration` | `Registration` | Current registration being resolved |
+| `requested_type` | `Type` | Type requested for resolution |
+| `scope` | `Scope` | Active scope for resolution |
+| `error` | `Error?` | Error if resolution failed |
+| `stack_registrations` | `HashSet<Registration>` | Registrations in current stack |
+| `previous_context` | `InjectionContext?` | Parent context for nested resolutions |
+
+### `get_injection_context() → InjectionContext`
+Gets the current injection context. Throws error if not initialized.
+
+### `try_get_injection_context() → InjectionContext?`
+Gets current context or `null` if not available.
+
+## Cycle Detection
+The injection context tracks the resolution stack to detect circular dependencies:
+
+```vala
+// This will throw ContainerError.CYCLE_DETECTED
+public class CircularA : Object {
+    private CircularB b = inject<CircularB>();
+}
+
+public class CircularB : Object {
+    private CircularA a = inject<CircularA>();
+}
+```
+
+Error message includes the cycle path:
+```
+Cycle detected during dependency injection: CircularA => CircularB => CircularA ad infinitum.
+```
+
+## Nested Injection
+Dependencies can have their own injected dependencies:
+
+```vala
+public class ApiService : Object {
+    private Controller controller = inject<Controller>();  // Controller has its own injections
+}
+
+public class Controller : Object {
+    private ILogger logger = inject<ILogger>();
+    private IRepository repo = inject<IRepository>();
+}
+```
+
+## Thread Safety
+Injection contexts are thread-local. Each thread has its own context stack, enabling concurrent resolution in different threads.
+
+## Usage Patterns
+
+### Required Dependencies
+Use `inject<T>()` for required dependencies:
+
+```vala
+public class OrderService : Object {
+    private IOrderRepository orders = inject<IOrderRepository>();
+    private IPaymentGateway payments = inject<IPaymentGateway>();
+}
+```
+
+### Optional Dependencies
+Use `try_inject<T>()` for optional dependencies:
+
+```vala
+public class CacheService : Object {
+    private ICache? cache = try_inject<ICache>();
+    
+    public string? get(string key) {
+        if (cache == null) return null;
+        return cache.get(key);
+    }
+}
+```
+
+### Multiple Implementations
+Use `inject_all<T>()` for plugin/handler patterns:
+
+```vala
+public class ValidationService : Object {
+    private Lot<IValidator> validators = inject_all<IValidator>();
+    
+    public bool validate(Object obj) {
+        foreach (var v in validators) {
+            if (!v.validate(obj)) return false;
+        }
+        return true;
+    }
+}
+```
+
+---
+
+Field injection in Inversion uses `inject<T>()` and `inject_all<T>()` functions in field initializers to resolve dependencies automatically during container-managed object creation, with cycle detection and thread-local context management.

+ 173 - 0
slopdocs/library.inversion.lifecycle.md

@@ -0,0 +1,173 @@
+# Lifecycle Enum
+
+## Overview
+`Lifecycle` defines instance lifetime behavior for registered components. Three lifecycles are supported: transient (new instance per resolve), scoped (one instance per scope), and singleton (one instance per container).
+
+## Namespace
+`Inversion`
+
+## Enum Declaration
+```vala
+public enum Lifecycle
+```
+
+## Values
+
+### `TRANSIENT`
+A new instance is created every time the component is requested.
+
+```vala
+container.register_transient<MyService>((s) => new MyService());
+
+var scope = container.create_scope();
+var a = scope.resolve<MyService>();  // New instance
+var b = scope.resolve<MyService>();  // Different instance
+// a != b
+```
+
+Use for:
+- Lightweight, stateless services
+- Objects with no shared state
+- Per-operation handlers
+
+### `SCOPED`
+A single instance is created per scope. Within the same scope, the same instance is returned for each request.
+
+```vala
+container.register_scoped<UserSession>((s) => new UserSession());
+
+var scope1 = container.create_scope();
+var scope2 = container.create_scope();
+
+var a1 = scope1.resolve<UserSession>();  // Instance A
+var a2 = scope1.resolve<UserSession>();  // Same Instance A
+// a1 == a2
+
+var b1 = scope2.resolve<UserSession>();  // Instance B (different)
+// a1 != b1
+```
+
+Use for:
+- Per-request context (web applications)
+- Unit-of-work patterns
+- Transaction scopes
+
+### `SINGLETON`
+A single instance is created for the lifetime of the container. The same instance is returned for all requests across all scopes.
+
+```vala
+container.register_singleton<Configuration>((s) => new Configuration());
+
+var scope1 = container.create_scope();
+var scope2 = container.create_scope();
+
+var a = scope1.resolve<Configuration>();  // Instance A
+var b = scope2.resolve<Configuration>();  // Same Instance A
+// a == b
+```
+
+Use for:
+- Application-wide configuration
+- Shared caches
+- Connection pools
+- Logging services
+
+## Lifecycle Constraints
+
+### Singleton Scope Cannot Resolve Scoped
+When resolving from a singleton context, scoped registrations cannot be resolved:
+
+```vala
+// This throws ILLEGAL_LIFECYCLE_COMBINATION
+// A singleton cannot depend on a scoped service
+public class SingletonService : Object {
+    private ScopedService scoped = inject<ScopedService>();  // Error at runtime!
+}
+
+container.register_singleton<SingletonService>((s) => new SingletonService());
+container.register_scoped<ScopedService>((s) => new ScopedService());
+
+var scope = container.create_scope();
+scope.resolve<SingletonService>();  // Throws!
+```
+
+### Scope-Local Registrations
+Scope-local registrations cannot be singletons:
+
+```vala
+// Compile-time precondition failure
+scope.register_local<MyService>((s) => new MyService(), Lifecycle.SINGLETON);
+// Error: requires (lifecycle != Lifecycle.SINGLETON)
+```
+
+## Registration Methods by Lifecycle
+
+| Method | Lifecycle |
+|--------|-----------|
+| `register_transient<T>()` | TRANSIENT |
+| `register_scoped<T>()` | SCOPED |
+| `register_singleton<T>()` | SINGLETON |
+| `register<T>(..., lifecycle)` | Specified |
+
+## Comparison Table
+
+| Lifecycle | Instance Count | Scope Behavior | Memory |
+|-----------|---------------|----------------|--------|
+| TRANSIENT | Per resolve | New each time | Highest |
+| SCOPED | Per scope | Shared in scope | Medium |
+| SINGLETON | Per container | Shared globally | Lowest |
+
+## Usage Examples
+
+### Configuration Singleton
+```vala
+public class AppConfig : Object {
+    public string database_path { get; set; }
+}
+
+container.register_singleton<AppConfig>((s) => {
+    var config = new AppConfig();
+    config.database_path = "/data/app.db";
+    return config;
+});
+```
+
+### Request-Scoped Context
+```vala
+public class RequestContext : Object {
+    public string request_id { get; construct set; }
+    public User? current_user { get; set; }
+}
+
+// In request handler
+void handle_request(Request req) {
+    var scope = container.create_scope();
+    scope.register_local_scoped<RequestContext>((s) => {
+        var ctx = new RequestContext();
+        ctx.request_id = req.id;
+        return ctx;
+    });
+    
+    var handler = scope.resolve<RequestHandler>();
+    handler.process();
+}
+```
+
+### Transient Handlers
+```vala
+public interface IRequestHandler : Object {
+    public abstract void handle(Request req);
+}
+
+container.register_transient<CreateUserHandler>((s) => new CreateUserHandler())
+    .as_type(typeof(IRequestHandler));
+container.register_transient<DeleteUserHandler>((s) => new DeleteUserHandler())
+    .as_type(typeof(IRequestHandler));
+
+// Each resolution creates new handler instances
+var handlers = scope.resolve_all<IRequestHandler>();
+```
+
+---
+
+The Lifecycle enum defines three instance lifetime behaviors: TRANSIENT for per-resolution instances, SCOPED for per-scope instances, and SINGLETON for container-wide single instances.

+ 122 - 0
slopdocs/library.inversion.overview.md

@@ -0,0 +1,122 @@
+# Inversion IoC Container
+
+## Overview
+Inversion is a dependency injection (IoC) container for Vala that provides inversion of control through component registration, lifecycle management, and field injection. It supports transient, scoped, and singleton lifecycles with a fluent registration API.
+
+## Core Components
+
+### Container
+The central registry for component registrations. Holds singleton instances and creates scopes for scoped resolution.
+
+### Scope
+Manages scoped instance lifetime. Created from a container and maintains its own cache of scoped instances.
+
+### Registration
+Represents a component registration with implementation type, service aliases, lifecycle, and factory.
+
+### Lifecycle
+Enum defining instance lifetime: `TRANSIENT`, `SCOPED`, `SINGLETON`.
+
+### Factory
+Interface for creating instances. Implementations: `DelegateFactory`, `InstanceFactory`.
+
+## Dependencies
+- GLib 2.0
+- GObject 2.0
+- Invercargill-1
+
+## Quick Start
+```vala
+using Inversion;
+
+// Define service interface and implementation
+public interface Logger : Object {
+    public abstract void log(string message);
+}
+
+public class ConsoleLogger : Object, Logger {
+    public void log(string message) {
+        stdout.printf("[LOG] %s\n", message);
+    }
+}
+
+// Create container and register
+var container = new Container();
+container.register_singleton<ConsoleLogger>((scope) => new ConsoleLogger())
+    .as_type(typeof(Logger));
+
+// Resolve via scope
+var scope = container.create_scope();
+var logger = scope.resolve<Logger>();
+logger.log("Hello!");
+```
+
+## Registration Patterns
+
+### Basic Registration
+```vala
+// Register with factory delegate
+container.register_singleton<MyService>((scope) => new MyService());
+
+// Register as interface
+container.register_singleton<MyImplementation>((scope) => new MyImplementation())
+    .as_type(typeof(IMyService));
+```
+
+### Multiple Interfaces
+```vala
+container.register<MyImplementation>((scope) => new MyImplementation())
+    .as_type(typeof(IServiceA))
+    .as_type(typeof(IServiceB))
+    .with_lifecycle(Lifecycle.SCOPED);
+```
+
+### Multiple Implementations
+```vala
+container.register_singleton<FileLogger>((s) => new FileLogger())
+    .as_type(typeof(Logger));
+container.register_singleton<ConsoleLogger>((s) => new ConsoleLogger())
+    .as_type(typeof(Logger));
+
+// Resolve all implementations
+var all_loggers = scope.resolve_all<Logger>();
+```
+
+## Field Injection
+Dependencies can be injected via field initializers using `inject<T>()`:
+
+```vala
+public class UserService : Object {
+    private Logger logger = inject<Logger>();
+    private Lot<IHandler> handlers = inject_all<IHandler>();
+    
+    public void greet(string name) {
+        this.logger.log(@"Hello, $name!");
+    }
+}
+```
+
+The injection context is automatically established during container resolution. Field initializers execute within the active scope, allowing dependencies to be resolved.
+
+## Error Handling
+
+### ContainerError
+- `NOT_REGISTERED`: Requested type not registered
+- `CYCLE_DETECTED`: Circular dependency found
+- `ILLEGAL_LIFECYCLE_COMBINATION`: Invalid lifecycle for scope context
+
+```vala
+try {
+    var service = scope.resolve<IMyService>();
+} catch (ContainerError.NOT_REGISTERED e) {
+    // Handle missing registration
+} catch (ContainerError.CYCLE_DETECTED e) {
+    // Handle circular dependency
+} catch (Error e) {
+    // Handle other errors
+}
+```
+
+---
+
+Inversion is a Vala IoC container providing dependency injection through component registration with transient, scoped, and singleton lifecycles, field injection via `inject<T>()`, multi-registration support, and scope-local registrations.

+ 150 - 0
slopdocs/library.inversion.registration.md

@@ -0,0 +1,150 @@
+# Registration Class
+
+## Overview
+`Registration` represents a component registration in the IoC container. It maps implementation types to service types (interfaces), specifies lifecycle behavior, and holds the factory for instance creation.
+
+## Namespace
+`Inversion`
+
+## Class Declaration
+```vala
+public class Registration : Object
+```
+
+## Properties
+
+| Property | Type | Description |
+|----------|------|-------------|
+| `implementation_type` | `Type` | The concrete type being registered |
+| `lifecycle` | `Lifecycle` | Instance lifecycle (default: `TRANSIENT`) |
+| `factory` | `Factory` | Factory for creating instances |
+| `type_aliases` | `ImmutableLot<Type>` | All service types this registration provides |
+
+## Constructor
+
+### `Registration(Type implementation_type, Factory factory, Lifecycle lifecycle)`
+Creates a new registration. The implementation type is automatically added as a service type.
+
+```vala
+var factory = new DelegateFactory((scope) => new MyService());
+var registration = new Registration(typeof(MyService), factory, Lifecycle.SINGLETON);
+```
+
+## Fluent Configuration Methods
+
+### `as_type(Type type) → Registration`
+Adds a service type alias to the registration. Returns `this` for chaining.
+
+```vala
+container.register_singleton<MyImplementation>((s) => new MyImplementation())
+    .as_type(typeof(IServiceA))
+    .as_type(typeof(IServiceB));
+```
+
+## Query Methods
+
+### `provides(Type type) → bool`
+Checks if this registration provides the specified service type.
+
+```vala
+if (registration.provides(typeof(ILogger))) {
+    // Can resolve as ILogger
+}
+```
+
+## Signals
+
+### `alias_added(Type alias)`
+Emitted when a new type alias is added via `as_type()`. Used internally by `Container` and `Scope` to update their catalogues.
+
+## FactoryDelegate
+
+### Definition
+```vala
+public delegate Object FactoryDelegate(Scope scope) throws Error;
+```
+
+Factory delegate for custom instantiation logic. Receives the current scope for dependency resolution.
+
+### Usage
+```vala
+// Simple factory
+container.register_transient<MyService>((scope) => new MyService());
+
+// Factory with dependencies
+container.register_transient<OrderService>((scope) => {
+    var repo = scope.resolve<IOrderRepository>();
+    return new OrderService(repo);
+});
+```
+
+## Registration Patterns
+
+### Single Interface
+```vala
+container.register_singleton<ConsoleLogger>((s) => new ConsoleLogger())
+    .as_type(typeof(ILogger));
+```
+
+### Multiple Interfaces
+```vala
+container.register_scoped<UserRepository>((s) => new UserRepository())
+    .as_type(typeof(IUserReader))
+    .as_type(typeof(IUserWriter))
+    .as_type(typeof(IUserRepository));
+```
+
+### Implementation Type Only
+```vala
+// Can be resolved as MyService but not as interface
+container.register_transient<MyService>((s) => new MyService());
+```
+
+### Custom Factory Instance
+```vala
+var factory = new MyCustomFactory();
+container.register_factory_type(typeof(MyService), factory, Lifecycle.SINGLETON)
+    .as_type(typeof(IMyService));
+```
+
+## Type Aliases Behavior
+
+When `as_type()` is called:
+1. Type is added to internal set
+2. `type_aliases` property is updated
+3. `alias_added` signal is emitted
+4. Container/Scope adds mapping for the new type
+
+```vala
+var reg = container.register_singleton<Impl>((s) => new Impl());
+// At this point: reg.type_aliases contains [typeof(Impl)]
+
+reg.as_type(typeof(IService));
+// Now: reg.type_aliases contains [typeof(Impl), typeof(IService)]
+// Container can now resolve both Impl and IService to this registration
+```
+
+## Integration with Container
+
+Registrations are typically created via container methods:
+
+```vala
+// These all return Registration for fluent configuration
+container.register<T>(factory, lifecycle);
+container.register_transient<T>(factory);
+container.register_scoped<T>(factory);
+container.register_singleton<T>(factory);
+```
+
+## Integration with Scope
+
+Scope-local registrations use the same `Registration` class:
+
+```vala
+scope.register_local_scoped<RequestContext>((s) => new RequestContext())
+    .as_type(typeof(IContext));
+```
+
+---
+
+The Registration class encapsulates component registration with implementation type, service aliases, lifecycle, and factory, providing fluent configuration via `as_type()` method chaining.

+ 186 - 0
slopdocs/library.inversion.scope.md

@@ -0,0 +1,186 @@
+# Scope Class
+
+## Overview
+`Scope` manages the lifecycle of scoped components. Created from a `Container`, each scope maintains its own cache of scoped instances and can have local registrations not visible to other scopes. Useful for per-request lifecycles in web applications.
+
+## Namespace
+`Inversion`
+
+## Class Declaration
+```vala
+public class Scope : Object
+```
+
+## Properties
+
+| Property | Type | Description |
+|----------|------|-------------|
+| `container` | `Container` | The container this scope belongs to |
+| `scope_lifecycle` | `Lifecycle` | Lifecycle mode for this scope |
+
+## Creation
+Scopes are created from a container:
+
+```vala
+var container = new Container();
+var scope = container.create_scope();       // Scoped lifecycle
+var transient_scope = container.create_transient_scope();  // Transient lifecycle
+```
+
+## Resolution Methods
+
+### `resolve<T>() throws Error → T`
+Resolves a single instance of type `T`.
+
+```vala
+try {
+    var logger = scope.resolve<ILogger>();
+    var service = scope.resolve<UserService>();
+} catch (Error e) {
+    stderr.printf("Resolution failed: %s\n", e.message);
+}
+```
+
+### `resolve_type(Type type) throws Error → Object`
+Non-generic resolution by `Type`.
+
+```vala
+var instance = scope.resolve_type(typeof(ILogger));
+```
+
+### `resolve_all<T>() throws Error → Enumerable<Object>`
+Resolves all registered implementations of type `T`.
+
+```vala
+var all_handlers = scope.resolve_all<IHandler>();
+all_handlers.iterate((h) => {
+    ((IHandler)h).handle(request);
+});
+```
+
+### `resolve_all_type(Type type) throws Error → Enumerable<Object>`
+Non-generic version for all implementations.
+
+```vala
+var all_loggers = scope.resolve_all_type(typeof(ILogger));
+```
+
+## Query Methods
+
+### `is_registered(Type type) → bool`
+Checks if type has any registration (local or from container).
+
+```vala
+if (scope.is_registered(typeof(ILogger))) {
+    var logger = scope.resolve<ILogger>();
+}
+```
+
+### `is_locally_registered(Type type) → bool`
+Checks if type has a scope-local registration only.
+
+```vala
+if (!scope.is_locally_registered(typeof(RequestContext))) {
+    scope.register_local_scoped<RequestContext>((s) => new RequestContext());
+}
+```
+
+### `get_local_registrations(Type service_type) → Enumerable<Registration>`
+Gets all local registrations for a type.
+
+```vala
+var local_regs = scope.get_local_registrations(typeof(IPlugin));
+```
+
+## Local Registration Methods
+Local registrations exist only within the scope. Singletons are not allowed in local registrations.
+
+### `register_local<T>(owned FactoryDelegate factory_func, Lifecycle lifecycle) → Registration`
+Registers with specified lifecycle. Precondition: `lifecycle != SINGLETON`.
+
+```vala
+scope.register_local<RequestContext>((s) => new RequestContext(), Lifecycle.SCOPED);
+```
+
+### `register_local_transient<T>(owned FactoryDelegate factory_func) → Registration`
+Registers as transient (new instance per resolve).
+
+```vala
+scope.register_local_transient<RequestData>((s) => new RequestData());
+```
+
+### `register_local_scoped<T>(owned FactoryDelegate factory_func) → Registration`
+Registers as scoped (one instance within this scope).
+
+```vala
+scope.register_local_scoped<Session>((s) => new Session());
+```
+
+### `register_local_type(Type implementation_type, owned FactoryDelegate factory_func, Lifecycle lifecycle) → Registration`
+Non-generic local registration.
+
+### `register_local_factory<T>(Factory factory, Lifecycle lifecycle) → Registration`
+Local registration with custom factory.
+
+### `register_local_factory_type(Type implementation_type, Factory factory, Lifecycle lifecycle) → Registration`
+Non-generic factory registration.
+
+## Resolution Behavior
+
+### Lifecycle Handling
+| Registration Lifecycle | Behavior |
+|------------------------|----------|
+| `SINGLETON` | Returns container-wide singleton |
+| `SCOPED` | Returns cached instance for this scope |
+| `TRANSIENT` | Creates new instance each time |
+
+### Resolution Order
+1. Check local registrations first
+2. Fall back to container registrations
+3. Apply lifecycle rules
+
+### Lifecycle Constraints
+- Singleton scope cannot resolve scoped registrations (throws `ILLEGAL_LIFECYCLE_COMBINATION`)
+
+```vala
+// This throws ILLEGAL_LIFECYCLE_COMBINATION
+var singleton_scope = new Scope(container, Lifecycle.SINGLETON);
+var scoped_instance = singleton_scope.resolve<ScopedService>();  // Error!
+```
+
+## Usage Patterns
+
+### Per-Request Scope (Web Applications)
+```vala
+void handle_request(Request req) {
+    var scope = container.create_scope();
+    
+    // Register request-specific context
+    scope.register_local_scoped<RequestContext>((s) => new RequestContext(req));
+    
+    // Resolve handler - gets same RequestContext within this scope
+    var handler = scope.resolve<RequestHandler>();
+    handler.process();
+}
+```
+
+### Nested Dependencies
+```vala
+// All services in same scope share scoped instances
+var scope = container.create_scope();
+var service_a = scope.resolve<ServiceA>();  // Gets scoped IRepository
+var service_b = scope.resolve<ServiceB>();  // Gets SAME IRepository instance
+```
+
+### Scope Isolation
+```vala
+var scope1 = container.create_scope();
+var scope2 = container.create_scope();
+
+var user1 = scope1.resolve<ScopedUser>();  // Instance A
+var user2 = scope2.resolve<ScopedUser>();  // Instance B (different)
+```
+
+---
+
+The Scope class provides scoped instance management with local registration support, resolution methods for single and multiple implementations, and lifecycle-aware caching for per-request or per-unit-of-work patterns.

+ 4 - 0
src/Container.vala

@@ -128,6 +128,10 @@ namespace Inversion {
             return this.registrations.get_or_empty(service_type);
         }
 
+        public Enumerable<Registration> get_all_registrations() {
+            return this.registrations.values;
+        }
+
         /**
          * Gets or creates a singleton instance for a registration.
          * 

+ 49 - 1
src/Injection.vala

@@ -1,5 +1,6 @@
-using Invercargill.DataStructures;
+
 using Invercargill;
+using Invercargill.DataStructures;
 
 namespace Inversion {
 
@@ -122,6 +123,24 @@ namespace Inversion {
         }
     }
 
+    public InjectionContext? try_get_injection_context() {
+        if(injection_contexts_by_thread == null) {
+            return null;
+        }
+        var thread = Thread.self<int>();
+        var result = injection_contexts_by_thread.get_or_default(thread);
+        if(result == null) {
+            return null;
+        }
+
+        try {
+            return result.peek();
+        }
+        catch {
+            return null;
+        }
+    }
+
     public T inject<T>() {
         var context = get_injection_context();
         if(context.error != null) {
@@ -153,4 +172,33 @@ namespace Inversion {
         }
     }
 
+    public T? try_inject<T>() {
+        var context = try_get_injection_context();
+        if(context == null || context.error != null) {
+            return null;
+        }
+
+        try {
+            return context.scope.resolve<T>();
+        }
+        catch(Error e) {
+            return null;
+        }
+    }
+
+    public Lot<T> try_inject_all<T>() {
+        var context = try_get_injection_context();
+        if(context == null || context.error != null) {
+            return Iterate.nothing<T>().to_immutable_buffer();
+        }
+
+        try {
+            return context.scope.resolve_all<T>()
+                .to_immutable_buffer();
+        }
+        catch(Error e) {
+            return Iterate.nothing<T>().to_immutable_buffer();
+        }
+    }
+
 }

+ 24 - 0
src/Registration.vala

@@ -31,6 +31,8 @@ namespace Inversion {
 
         public ImmutableLot<Type> type_aliases { get; private set; }
 
+        private Catalogue<Type, Object> metadata = new Catalogue<Type, Object>();
+
         public signal void alias_added(Type alias);
 
         /**
@@ -56,6 +58,28 @@ namespace Inversion {
             return this;
         }
 
+        public Registration as<T>() {
+            return as_type(typeof(T));
+        }
+
+        public Registration with_metadata_type(Type type, Object metadata) {
+            this.metadata.add(type, metadata);
+            return this;
+        }
+
+        public Registration with_metadata<T>(T metadata) {
+            return with_metadata_type(typeof(T), (Object)metadata);
+        }
+
+        public Enumerable<Object> get_metadata_type(Type type) {
+            return this.metadata.get_or_empty(type);
+        }
+
+        public Enumerable<T> get_metadata<T>() {
+            return get_metadata_type(typeof(T))
+                .cast<T>();
+        }
+
         public bool provides(Type type) {
             return this.types.contains(type);
         }

+ 21 - 5
src/Scope.vala

@@ -95,6 +95,18 @@ namespace Inversion {
             return this.local_registrations.get_or_empty(service_type);
         }
 
+        public Registration get_registration(Type service_type) throws ContainerError {
+            return this.local_registrations.get_any_or_default(service_type) ?? this.container.get_registration(service_type);
+        }
+
+        public Enumerable<Registration> get_registrations(Type service_type) {
+            return this.local_registrations.get_or_empty(service_type).concat(this.container.get_registrations(service_type));
+        }
+
+        public Enumerable<Registration> get_all_registrations() {
+            return this.local_registrations.values.concat(this.container.get_all_registrations());
+        }
+
         public Object resolve_type(Type type) throws Error {
             Registration registration;
             if (!local_registrations.try_get_any(type, out registration)) {
@@ -104,27 +116,31 @@ namespace Inversion {
             if(scope_lifecycle == Lifecycle.SINGLETON && registration.lifecycle == Lifecycle.SCOPED) {
                 throw new ContainerError.ILLEGAL_LIFECYCLE_COMBINATION("Cannot resolve scoped type from a singleton scope");
             }
-            return this.resolve_registration(registration, type);
+            return this.resolve_registration_as(registration, type);
         }
 
         public T resolve<T>() throws Error {
             return (T) this.resolve_type(typeof(T));
         }
 
+        public Object resolve_registration(Registration registration) throws Error {
+            return this.resolve_registration_as(registration, registration.implementation_type);
+        }
+
         public Enumerable<Object> resolve_all_type(Type type) throws Error {
             return local_registrations
                 .get_or_empty(type)
                 .concat(container.get_registrations(type))
                 .where(r => scope_lifecycle != Lifecycle.SINGLETON || r.lifecycle != Lifecycle.SCOPED)
-                .attempt_select<Object>(r => resolve_registration(r, type))
+                .attempt_select<Object>(r => resolve_registration_as(r, type))
                 .to_immutable_buffer();
         }
 
-        public Enumerable<Object> resolve_all<T>() throws Error {
-            return resolve_all_type(typeof(T));
+        public Enumerable<T> resolve_all<T>() throws Error {
+            return resolve_all_type(typeof(T)).cast<T>();
         }
 
-        private Object resolve_registration(Registration registration, Type requested_type) throws Error {
+        private Object resolve_registration_as(Registration registration, Type requested_type) throws Error {
             switch (registration.lifecycle) {
                 case Lifecycle.SINGLETON:
                     return container.get_or_create_singleton(registration, requested_type);

+ 18 - 2
src/meson.build

@@ -9,10 +9,13 @@ sources = files(
     'Injection.vala'
 )
 
-libinversion = shared_library('inversion',
+library_version = meson.project_version()
+libinversion = shared_library('inversion-@0@'.format(library_version),
     sources,
     dependencies: [glib_dep, gobject_dep, invercargill_dep],
-    install: true
+    install: true,
+    vala_gir: 'inversion-@0@.gir'.format(library_version),
+    install_dir: [true, true, true, true]
 )
 
 inversion_dep = declare_dependency(
@@ -20,3 +23,16 @@ inversion_dep = declare_dependency(
     include_directories: include_directories('.'),
     dependencies: [glib_dep, gobject_dep, invercargill_dep]
 )
+
+
+pkg = import('pkgconfig')
+pkg.generate(libinversion,
+    version : library_version,
+    name : 'inversion-@0@'.format(library_version))
+    
+g_ir_compiler = find_program('g-ir-compiler')
+custom_target('inversion typelib', command: [g_ir_compiler, '--shared-library=libinversion-@0@.so'.format(library_version), '--output', '@OUTPUT@', meson.current_build_dir() / 'inversion-@0@.gir'.format(library_version)],
+              output: 'inversion-@0@.typelib'.format(library_version),
+              depends: libinversion,
+              install: true,
+              install_dir: get_option('libdir') / 'girepository-1.0')