# @suaveplan/api-plugin-contacts - Contacts + groups HTTP plugin for @suaveplan/api. Mounts /contacts and /groups (with /groups/:id/members and /groups/:id/resolve) on a Hono OpenAPI registry, backed by SurrealDB-implementations of the @suaveplan/contacts ContactStore / GroupStore / MembershipStore interfaces. Bulk-import with per-row error reporting, repository-level maxGroupDepth enforcement, six OTel instruments, typed Hono ContextVariableMap + ServiceRegistry augmentations, and the 12-code CONTACTS_API.* error namespace. Contacts and Groups HTTP plugin for [`@suaveplan/api`](../../api/api). Mounts the `/contacts` and `/groups` route trees on a Hono `OpenAPIHono`, backed by SurrealDB implementations of the [`@suaveplan/contacts`](../contacts) store interfaces. ## Documentation tree - [context](./src/context/context.md) — Module augmentation declaring four typed slots on Hono's `ContextVariableMap`: `contactRepository`, `groupRepository`,… - [context](./src/context/context.md) — Module augmentation declaring four typed slots on Hono's `ContextVariableMap`: `contactRepository`, `groupRepository`,… - [error-codes](./src/error-codes/error-codes.md) — Single source of truth for every failure path inside `@suaveplan/api-plugin-contacts`. Built on the `@suaveplan… - [error-codes](./src/error-codes/error-codes.md) — Single source of truth for every failure path inside `@suaveplan/api-plugin-contacts`. Built on the `@suaveplan… - [otel](./src/otel/instruments.md) — Exposes the six OpenTelemetry instruments emitted by `@suaveplan/api-plugin-contacts` in production: four counters cove… - [instruments](./src/otel/instruments.md) — Exposes the six OpenTelemetry instruments emitted by `@suaveplan/api-plugin-contacts` in production: four counters cove… - [plugin](./src/plugin/plugin.md) — Returns the `PluginDescriptor` consumed by the `@suaveplan/api` plugin host. Runs the 5-step `onI… - [plugin](./src/plugin/plugin.md) — Returns the `PluginDescriptor` consumed by the `@suaveplan/api` plugin host. Runs the 5-step `onI… - [schema](./src/plugin/schema.md) — Inlined SurrealQL DDL for the three tables the contacts plugin owns: `contact`, `contact_group`, and `group_membership`… - [contact-repository](./src/repositories/contact-repository.md) — Concrete implementation of the universal `ContactStore` interface from `@suaveplan/contacts`, backed by a connecte… - [group-repository](./src/repositories/group-repository.md) — Concrete implementation of the `GroupStore` interface from `@suaveplan/contacts`, backed by `SurrealAdapter`. I… - [membership-repository](./src/repositories/membership-repository.md) — Concrete implementation of the `MembershipStore` interface from `@suaveplan/contacts`, backed by `SurrealAdapte… - [build-routes](./src/routes/contacts/build-routes.md) — Returns the seven `PluginRouteEntry` values that compose the `/contacts` route tree. Mounted by the plugin's `onInit` v… - [contacts-bulk-routes](./src/routes/contacts/contacts-bulk-routes.md) — Two `PluginRouteEntry` builders for the bulk-operation contact endpoints: `POST /contacts/import` and `POST /contacts/m… - [contacts-crud-routes](./src/routes/contacts/contacts-crud-routes.md) — Five `PluginRouteEntry` builders for the contact CRUD endpoints: `GET /contacts`, `POST /contacts`, `GET /contacts/:id`… - [build-routes](./src/routes/groups/build-routes.md) — Returns the 11 `PluginRouteEntry` values that compose the `/groups` route tree: 5 CRUD (`list`, `create`, `get`, `updat… - [groups-crud-routes](./src/routes/groups/groups-crud-routes.md) — Five `PluginRouteEntry` builders for the group CRUD endpoints: `GET /groups`, `POST /groups`, `GET /groups/:id`, `PATCH… - [groups-members-routes](./src/routes/groups/groups-members-routes.md) — Four `PluginRouteEntry` builders for the group-membership endpoints: `GET /groups/:id/members`, `POST /groups/:id/membe… - [groups-tree-routes](./src/routes/groups/groups-tree-routes.md) — Two `PluginRouteEntry` builders for the hierarchical-group endpoints: `GET /groups/:id/tree` materialises a typed `Grou… - [require-tenant](./src/routes/shared/require-tenant.md) — Centralised guard that extracts and validates the request `Principal` on every `/contacts` and `/groups` handler. Retur… - [strip-undefined](./src/routes/shared/strip-undefined.md) — Bridges Zod's optional-field semantics (`.optional() → string | undefined`) with the `@suaveplan/contacts` inpu… - [contacts-schemas](./src/schemas/contacts-schemas.md) — Zod schemas mounted on every `/contacts` route via `@hono/zod-openapi`. They validate inbound bodies / query strings, s… - [groups-schemas](./src/schemas/groups-schemas.md) — Zod schemas for the seven `/groups` routes (list, create, get, update, delete, tree, move) plus the three `/groups/:id/… - [options](./src/schemas/options.md) — Zod schema that validates the optional bag of options passed to `createContactsPlugin`. Every field is optional with a… - [services](./src/services/registry-augmentation.md) — Adds typed `contactRepository`, `groupRepository`, and `membershipRepository` slots to the shared `@suaveplan/api-plugi… - [registry-augmentation](./src/services/registry-augmentation.md) — Adds typed `contactRepository`, `groupRepository`, and `membershipRepository` slots to the shared `@suaveplan/api-plugi… - [tree](./src/tree/build-tree.md) — Constructs a typed `GroupTreeNode` rooted at the supplied `Group`, given a flat list of descendants in arbitrary order.… - [build-tree](./src/tree/build-tree.md) — Constructs a typed `GroupTreeNode` rooted at the supplied `Group`, given a flat list of descendants in arbitrary order.… - [types](./src/types/types.md) — Pure type declarations that downstream consumers of `@suaveplan/api-plugin-contacts` depend on at compile time. Keeping… - [types](./src/types/types.md) — Pure type declarations that downstream consumers of `@suaveplan/api-plugin-contacts` depend on at compile time. Keeping… ## Status - Version: `0.1.0` - Tier: server / universal - suaveplan-policy: 100% test coverage, 0 mocks, real SurrealDB via `@suaveplan/testing-integration-surrealdb`. ## Quick start ```ts import { createContactsPlugin } from "@suaveplan/api-plugin-contacts"; const plugin = createContactsPlugin({ routePrefix: "/api", maxImportBatchSize: 500, maxGroupDepth: 10, enforceGroupDepth: true, }); // host.use(plugin); ``` The plugin is suaveplan-compatible with the `@suaveplan/api-plugin` descriptor contract; load it after the database and auth plugins so the ServiceRegistry `db` and `authService` slots resolve. ## Endpoints | Method | Path | Purpose | |---|---|---| | GET | `/contacts` | List contacts (search / status / channelType / tags / paging) | | POST | `/contacts` | Create a contact | | GET | `/contacts/:id` | Read a contact (tenant-scoped) | | PATCH | `/contacts/:id` | Update a contact | | DELETE | `/contacts/:id` | Delete a contact | | POST | `/contacts/import` | Bulk import (Zod-capped) with per-row errors | | POST | `/contacts/merge` | Channel + tag union into a primary contact | | GET | `/groups` | List groups | | POST | `/groups` | Create a group (depth-enforced) | | GET | `/groups/:id` | Read a group | | PATCH | `/groups/:id` | Update a group | | DELETE | `/groups/:id` | Delete a group (cascade members) | | GET | `/groups/:id/tree` | Typed `GroupTreeNode` subtree | | POST | `/groups/:id/move` | Reparent (depth-enforced) | | GET | `/groups/:id/members` | List members | | POST | `/groups/:id/members` | Idempotent add | | DELETE | `/groups/:id/members` | Idempotent remove | | GET | `/groups/:id/resolve` | Flatten group + descendants → unique contacts | ## Modules - [`error-codes/error-codes`](./src/error-codes/error-codes.md) — 12 `CONTACTS_API.*` codes + `ContactsApiError`. - [`otel/instruments`](./src/otel/instruments.md) — six OTel instruments. - [`schemas/options`](./src/schemas/options.md) — plugin options Zod schema. - [`schemas/contacts-schemas`](./src/schemas/contacts-schemas.md) — `/contacts` request/response shapes. - [`schemas/groups-schemas`](./src/schemas/groups-schemas.md) — `/groups` request/response shapes. - [`repositories/contact-repository`](./src/repositories/contact-repository.md) — `SurrealContactRepository`. - [`repositories/group-repository`](./src/repositories/group-repository.md) — `SurrealGroupRepository`. - [`repositories/membership-repository`](./src/repositories/membership-repository.md) — `SurrealMembershipRepository`. - [`routes/shared/require-tenant`](./src/routes/shared/require-tenant.md) — Principal → tenantId guard. - [`routes/contacts/build-routes`](./src/routes/contacts/build-routes.md) — `/contacts` aggregator. - [`routes/contacts/contacts-crud-routes`](./src/routes/contacts/contacts-crud-routes.md) — CRUD builders. - [`routes/contacts/contacts-bulk-routes`](./src/routes/contacts/contacts-bulk-routes.md) — import + merge builders. - [`routes/groups/build-routes`](./src/routes/groups/build-routes.md) — `/groups` aggregator. - [`routes/groups/groups-crud-routes`](./src/routes/groups/groups-crud-routes.md) — CRUD builders. - [`routes/groups/groups-tree-routes`](./src/routes/groups/groups-tree-routes.md) — tree + move. - [`routes/groups/groups-members-routes`](./src/routes/groups/groups-members-routes.md) — membership + resolve. - [`tree/build-tree`](./src/tree/build-tree.md) — typed subtree builder. - [`plugin/schema`](./src/plugin/schema.md) — bundled SurrealQL DDL + apply helper. - [`plugin/plugin`](./src/plugin/plugin.md) — `createContactsPlugin` factory. - [`types/types`](./src/types/types.md) — public configuration types. - [`context/context`](./src/context/context.md) — Hono `ContextVariableMap` augmentation. - [`services/registry-augmentation`](./src/services/registry-augmentation.md) — `ServiceRegistry` augmentation. ## Tests ```sh bunx turbo test --filter=@suaveplan/api-plugin-contacts bunx turbo test:coverage --filter=@suaveplan/api-plugin-contacts ``` Integration tests under `tests/integration/` boot a SurrealDB testcontainer (Docker required) and exercise the full plugin lifecycle end-to-end.