# Metadata

## Overview

The `GetMetadata` function provides a reflection-based metadata system built on top of the `reflect-metadata` library. It retrieves or creates metadata instances associated with target objects, supporting inheritance through the prototype chain.

## Import

```ts
import { GetMetadata } from "@antelopejs/interface-core";
```

## `GetMetadata`

```ts
function GetMetadata<T, U>(
  target: U,
  meta: Class<T, [U]> & { key: symbol },
  inherit?: boolean,
): T;
```

### Parameters

| Parameter | Type                              | Default | Description                                          |
| --------- | --------------------------------- | ------- | ---------------------------------------------------- |
| `target`  | `U`                               | -       | The object to retrieve or create metadata for        |
| `meta`    | `Class<T, [U]> & { key: symbol }` | -       | A metadata class with a static `key` symbol          |
| `inherit` | `boolean`                         | `true`  | Whether to inherit metadata from the prototype chain |

### Return value

Returns the metadata instance of type `T` associated with the target.

## Define a metadata class

A metadata class must have a static `key` property (a `Symbol`) and accept the target object as a constructor argument.

```ts
class RouteMetadata {
  static key = Symbol("RouteMetadata");

  public routes: Map<string, string> = new Map();

  constructor(_target: any) {
    // Initialize metadata for the target
  }
}
```

## Retrieve metadata

```ts
import { GetMetadata } from "@antelopejs/interface-core";

class UserController {
  getUser() {}
  listUsers() {}
}

const meta = GetMetadata(UserController.prototype, RouteMetadata);
meta.routes.set("getUser", "/users/:id");
meta.routes.set("listUsers", "/users");
```

Calling `GetMetadata` multiple times with the same target and metadata class returns the same instance. The metadata is stored on the target using `Reflect.defineMetadata`.

## Inheritance

When `inherit` is `true` (the default), `GetMetadata` walks the prototype chain to find parent metadata. If the metadata class defines an `inherit` method, that method is called with the parent metadata. Otherwise, properties from the parent are copied to the child metadata where they do not already exist.

```ts
class ControllerMeta {
  static key = Symbol("ControllerMeta");

  public middleware: string[] = [];

  constructor(_target: any) {}

  inherit(parent: ControllerMeta) {
    this.middleware = [...parent.middleware];
  }
}

class BaseController {}
const baseMeta = GetMetadata(BaseController.prototype, ControllerMeta);
baseMeta.middleware.push("auth");

class AdminController extends BaseController {}
const adminMeta = GetMetadata(AdminController.prototype, ControllerMeta);
// adminMeta.middleware contains ["auth"] (inherited from BaseController)

adminMeta.middleware.push("adminOnly");
// adminMeta.middleware is now ["auth", "adminOnly"]
// baseMeta.middleware remains ["auth"]
```

Without a custom `inherit` method, properties are shallow-copied from parent to child using `Object.getOwnPropertyNames`, but only for keys that do not already exist on the child metadata instance.

## Disable inheritance

Pass `false` as the third argument to prevent prototype chain traversal:

```ts
const meta = GetMetadata(target, RouteMetadata, false);
```

## Next steps

- [Decorators](./3.decorators.md) - Combine metadata with decorator factories
- [Modules](./5.modules.md) - Module lifecycle events and management
