Relocate the ~6k-file docs tree from the repo root into plugins/document-navigator/docs/ so the plugin is self-contained and portable. docs/superpowers/ (specs and plans) stays at the repo root. Moved via cp+rm rather than git mv because directory rename() fails with EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the identical-content files at commit time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
10 KiB
10 KiB
@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.
Mounts the /contacts and /groups route trees on a Hono
OpenAPIHono, backed by SurrealDB implementations of the
@suaveplan/contacts store interfaces.
Documentation tree
- context — Module augmentation declaring four typed slots on Hono's
ContextVariableMap:contactRepository,groupRepository,…- context — Module augmentation declaring four typed slots on Hono's
ContextVariableMap:contactRepository,groupRepository,…
- context — Module augmentation declaring four typed slots on Hono's
- error-codes — Single source of truth for every failure path inside
@suaveplan/api-plugin-contacts. Built on the `@suaveplan…- error-codes — Single source of truth for every failure path inside
@suaveplan/api-plugin-contacts. Built on the `@suaveplan…
- error-codes — Single source of truth for every failure path inside
- otel — Exposes the six OpenTelemetry instruments emitted by
@suaveplan/api-plugin-contactsin production: four counters cove…- instruments — Exposes the six OpenTelemetry instruments emitted by
@suaveplan/api-plugin-contactsin production: four counters cove…
- instruments — Exposes the six OpenTelemetry instruments emitted by
- plugin — Returns the
PluginDescriptor<ContactsPluginConfig>consumed by the@suaveplan/apiplugin host. Runs the 5-step `onI… - contact-repository — Concrete implementation of the universal
ContactStoreinterface from@suaveplan/contacts, backed by a connecte… - group-repository — Concrete implementation of the
GroupStoreinterface from@suaveplan/contacts, backed bySurrealAdapter. I… - membership-repository — Concrete implementation of the
MembershipStoreinterface from@suaveplan/contacts, backed by `SurrealAdapte… - build-routes — Returns the seven
PluginRouteEntryvalues that compose the/contactsroute tree. Mounted by the plugin'sonInitv… - contacts-bulk-routes — Two
PluginRouteEntrybuilders for the bulk-operation contact endpoints:POST /contacts/importand `POST /contacts/m… - contacts-crud-routes — Five
PluginRouteEntrybuilders for the contact CRUD endpoints:GET /contacts,POST /contacts,GET /contacts/:id… - build-routes — Returns the 11
PluginRouteEntryvalues that compose the/groupsroute tree: 5 CRUD (list,create,get, `updat… - groups-crud-routes — Five
PluginRouteEntrybuilders for the group CRUD endpoints:GET /groups,POST /groups,GET /groups/:id, `PATCH… - groups-members-routes — Four
PluginRouteEntrybuilders for the group-membership endpoints:GET /groups/:id/members, `POST /groups/:id/membe… - groups-tree-routes — Two
PluginRouteEntrybuilders for the hierarchical-group endpoints:GET /groups/:id/treematerialises a typed `Grou… - require-tenant — Centralised guard that extracts and validates the request
Principalon every/contactsand/groupshandler. Retur… - strip-undefined — Bridges Zod's optional-field semantics (
.optional() → string | undefined) with the@suaveplan/contactsinpu… - contacts-schemas — Zod schemas mounted on every
/contactsroute via@hono/zod-openapi. They validate inbound bodies / query strings, s… - groups-schemas — Zod schemas for the seven
/groupsroutes (list, create, get, update, delete, tree, move) plus the three `/groups/:id/… - options — Zod schema that validates the optional bag of options passed to
createContactsPlugin. Every field is optional with a… - services — Adds typed
contactRepository,groupRepository, andmembershipRepositoryslots to the shared `@suaveplan/api-plugi…- registry-augmentation — Adds typed
contactRepository,groupRepository, andmembershipRepositoryslots to the shared `@suaveplan/api-plugi…
- registry-augmentation — Adds typed
- tree — Constructs a typed
GroupTreeNoderooted at the suppliedGroup, given a flat list of descendants in arbitrary order.…- build-tree — Constructs a typed
GroupTreeNoderooted at the suppliedGroup, given a flat list of descendants in arbitrary order.…
- build-tree — Constructs a typed
- types — Pure type declarations that downstream consumers of
@suaveplan/api-plugin-contactsdepend on at compile time. Keeping…- types — Pure type declarations that downstream consumers of
@suaveplan/api-plugin-contactsdepend on at compile time. Keeping…
- types — Pure type declarations that downstream consumers of
Status
- Version:
0.1.0 - Tier: server / universal
- suaveplan-policy: 100% test coverage, 0 mocks, real SurrealDB via
@suaveplan/testing-integration-surrealdb.
Quick start
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— 12CONTACTS_API.*codes +ContactsApiError.otel/instruments— six OTel instruments.schemas/options— plugin options Zod schema.schemas/contacts-schemas—/contactsrequest/response shapes.schemas/groups-schemas—/groupsrequest/response shapes.repositories/contact-repository—SurrealContactRepository.repositories/group-repository—SurrealGroupRepository.repositories/membership-repository—SurrealMembershipRepository.routes/shared/require-tenant— Principal → tenantId guard.routes/contacts/build-routes—/contactsaggregator.routes/contacts/contacts-crud-routes— CRUD builders.routes/contacts/contacts-bulk-routes— import + merge builders.routes/groups/build-routes—/groupsaggregator.routes/groups/groups-crud-routes— CRUD builders.routes/groups/groups-tree-routes— tree + move.routes/groups/groups-members-routes— membership + resolve.tree/build-tree— typed subtree builder.plugin/schema— bundled SurrealQL DDL + apply helper.plugin/plugin—createContactsPluginfactory.types/types— public configuration types.context/context— HonoContextVariableMapaugmentation.services/registry-augmentation—ServiceRegistryaugmentation.
Tests
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.