# `MishkaBlog.TeamMember`
[🔗](https://github.com/mishka-group/mishka_blog/blob/v0.0.1/lib/mishka_blog/resources/team_member.ex#L5)

Manages the many-to-many relationship between teams and bloggers with full version control.

This resource serves as the join table that associates bloggers with teams,
enabling team-based organization of blog contributors. Each record represents
a single membership assignment, indicating that a blogger is a member of a team
and inherits the team's permissions for blog operations.

The resource implements upsert functionality through the assign action, preventing
duplicate membership assignments while allowing efficient bulk team updates.
The unique constraint on team_id and blogger_id pairs ensures that each blogger
can only be a member of a team once, maintaining data integrity.

Team membership history is tracked through PaperTrail integration, recording
when members join, leave, or have their roles changed. This provides a complete
audit trail for team composition changes, essential for security and compliance.

Access to this resource is controlled through policies that allow manipulation
either through the Team resource's member relationships or by users with
administrative privileges. This ensures that team memberships can only be
modified by authorized actors.

Optional role and joined_at fields provide additional metadata about the
membership, such as the member's role within the team and when they joined.

# `t`

```elixir
@type t() :: %MishkaBlog.TeamMember{
  __lateral_join_source__: term(),
  __meta__: term(),
  __metadata__: term(),
  __order__: term(),
  aggregates: term(),
  blogger: term(),
  blogger_id: term(),
  calculations: term(),
  id: term(),
  joined_at: term(),
  role: term(),
  team: term(),
  team_id: term(),
  team_in_any_state: term()
}
```

# `__guarded_change__`

Apply the full GuardedStruct pipeline (sanitize → validate → derive →
main_validator) to `attrs` and return either `{:ok, transformed_attrs}`
or `{:error, errors}`. Wire this into an `Ash.Resource.Change` to plug
guardedstruct rules into Ash's changeset pipeline.

The function is named `__guarded_change__` because it does more than
validate — it can also transform values (trim, downcase, slugify,
auto-fill, etc.) before they reach the data layer.

# `__guarded_field_meta__`

O(1) lookup of a field's compile-time metadata by name. Returns
`nil` for unknown fields.

# `__guarded_field_name_set__`

Compile-time-baked `MapSet` of every field name owned by the
`guardedstruct` block. Used by `GuardedStruct.AshResource.Change`
to decide, in O(1), whether a key in `changeset.atomics`
belongs to the pipeline.

# `default_short_name`

# `input`

```elixir
@spec input(values :: map() | Keyword.t()) :: map() | no_return()
```

Validates that the keys in the provided input are valid for at least one action on the resource.

Raises a KeyError error at compile time if not. This exists because generally a struct should only ever
be created by Ash as a result of a successful action. You should not be creating records manually in code,
e.g `%MyResource{value: 1, value: 2}`. Generally that is fine, but often with embedded resources it is nice
to be able to validate the keys that are being provided, e.g

```elixir
Resource
|> Ash.Changeset.for_create(:create, %{embedded: EmbeddedResource.input(foo: 1, bar: 2)})
|> Ash.create()
```

# `input`

```elixir
@spec input(values :: map() | Keyword.t(), action :: atom()) :: map() | no_return()
```

Same as `input/1`, except restricts the keys to values accepted by the action provided.

# `primary_key_matches?`

---

*Consult [api-reference.md](api-reference.md) for complete listing*
