7.3 KiB
Migrate @suaveplan/api-plugin-contacts to genesis at enterprise quality
Package: @suaveplan/api-plugin-contacts
Status: PROPOSED
Type: new-package
Why
@suaveplan/api-plugin-contacts is the HTTP face of @suaveplan/contacts. The upstream version on main shipped to 1.0.0 with ~30 as any casts in route handlers, untyped buildTree, no shared error hierarchy, no OTel instrumentation, no real-database integration tests, and maxGroupDepth declared in config but never enforced. Genesis policy forbids all of those defects, so the package is being rebuilt at 0.1.0 against the genesis @suaveplan/contacts store interfaces (ContactStore / GroupStore / MembershipStore with generateId) rather than the upstream port. Migrating closes the contacts plugin family — the first two siblings (@suaveplan/contacts and @suaveplan/macos-contacts) are already at gold standard.
What changes
- ADDED:
createContactsPlugin(options?: ContactsPluginOptions): PluginDescriptor<ContactsPluginConfig>— factory mounting/contactsand/groupsroute trees on the hostOpenAPIHono. - ADDED:
ContactsPluginOptionsSchema,ContactsPluginOptions,ContactsPluginConfig— Zod-validated configuration. - ADDED:
SurrealContactRepository,SurrealGroupRepository,SurrealMembershipRepository—@suaveplan/contactsstore-interface implementations overSurrealAdapter. - ADDED:
CONTACTS_API_CODES/ContactsApiError(@suaveplan/errorbased) — 12 stable codes covering every failure path. - ADDED:
getContactsApiInstruments/resetContactsApiInstruments— 6 OTel instruments (4 counters + 2 histograms) emitted from production hot paths. - ADDED: Typed Hono
ContextVariableMapaugmentation —contactRepository,groupRepository,membershipRepository,eventBusslots (eliminates upstream'sas anycasts). - ADDED:
ServiceRegistryaugmentation — exposescontactRepository/groupRepository/membershipRepositoryas typed services to sibling plugins. - ADDED: SurrealQL schema files for
contact,contact_group,group_membershiptables and theMigrationRunnerintegration. - ADDED: Bulk-import endpoint honouring
maxImportBatchSizewith per-row error reporting (Zod-validated). - ADDED: Repository-level
maxGroupDepthenforcement (createGroupandmoveGroupraiseCONTACTS_API.GROUP_DEPTH_EXCEEDED). - ADDED: Typed
buildTree(root: Group, descendants: readonly Group[]): GroupTreeNodehelper. - ADDED: Integration tests using
@suaveplan/testing-integration-surrealdbtestcontainers — real Surreal v3, no mocks.
Out of scope
- CSV / VCF import parsing — already lives in
@suaveplan/contacts/import-export/contact-importer; the bulk-import endpoint accepts pre-parsed JSON rows. - GDPR export / erase, schema-registry versioning, audit-log integration, rate-limiting, dedup, domain normalisation —
enterprise/api-plugin-contacts.md§3-4 list these as plugin-composition improvements. They belong on the host (already mountable via@suaveplan/api-plugin-audit,@suaveplan/api-plugin-rate-limit,@suaveplan/api-plugin-export); duplicating them inside this plugin would re-publish behaviour the host owns. - Sync adapters (Google / iCloud / Outlook) and a saved-segments rules engine —
enterprise/api-plugin-contacts.md§4 items 8/3, deferred to dedicated plugins.
Source mapping
This change derives from ../suaveplan/enterprise/api-plugin-contacts.md and the upstream source at ../suaveplan/packages/server/api-plugin-contacts/src/.
| Requirement | Source lines | Notes |
|---|---|---|
Re-export ContactsConfigSchema + createContactsPlugin from root |
L57, L98, L113 | verbatim; genesis names them ContactsPluginConfigSchema + createContactsPlugin |
maxImportBatchSize (default 1000) and maxGroupDepth (default 10) config |
L12, L49 | verbatim |
/contacts + /groups route trees with /groups/:id/members and /groups/:id/resolve |
L12, L37-L38 | upstream files routes/contacts/*, routes/groups/* |
SurrealContactRepository, SurrealGroupRepository, SurrealMembershipRepository exported |
L21 | verbatim |
Hierarchical group nesting with materialised path + depth |
L12, L49 | upstream group.repository.ts L19-L58 |
Enforce maxGroupDepth in repository on insert + move |
L49, L99 | upstream gap closed; raises GROUP_DEPTH_EXCEEDED |
Bulk-import endpoint honouring maxImportBatchSize with per-row errors |
L50, L99-L100 | upstream routes/contacts/contacts.routes.ts L218-L263 baseline; genesis adds Zod boundary + typed errors |
| Merge contacts endpoint (channel + tag union) | upstream contacts.routes.ts L265-L331 |
preserved |
Group subtree (GET /groups/:id/tree) + reparent (POST /groups/:id/move) |
upstream groups.routes.ts L206-L270 |
preserved; buildTree becomes fully typed |
| Group membership add / remove / list / resolve | upstream groups.members.routes.ts L24-L196 |
preserved |
| Zod schemas on every HTTP body | L79 | verbatim; @hono/zod-openapi adapter |
Real-SurrealDB integration tests via test:with-db:filter |
L107, L118 | genesis uses @suaveplan/testing-integration-surrealdb testcontainers |
@suaveplan/error hierarchy with typed code registry |
L81, L105 | upstream gap closed (12 codes) |
Requirements with no source line
| Requirement | Justification |
|---|---|
| OpenTelemetry instruments emitted from production paths | Genesis-wide observability policy (CLAUDE.md Code Organization) — every plugin emits counters + histograms; not package-specific in upstream enterprise/*.md. |
Typed Hono ContextVariableMap augmentation in place of as any casts |
Closes a defect in the upstream port (~30 as any casts in routes/**) flagged by genesis CLAUDE.md rule 9 (no static-analysis suppression / cast-only escape hatches). |
| 100% Istanbul coverage threshold | Genesis-wide policy. |
@suaveplan/testing/runner for all test primitives |
CLAUDE.md rule 11. |
Verification approach
src/error-codes/error-codes.test.ts— covers code registry stability; pinned bytests/dep-graph.test.tsfor SuaveplanError lineage.src/otel/instruments.test.ts— asserts every instrument binds against a real in-memoryMeterProviderafterresetContactsApiInstruments.src/repositories/contact-repository.test.ts,src/repositories/group-repository.test.ts,src/repositories/membership-repository.test.ts— unit-shape tests for query builder + mapping with an injectable adapter contract.src/routes/contacts/build-routes.test.ts,src/routes/groups/build-routes.test.ts— route-tree composition and shape assertions.src/plugin/plugin.test.ts— plugin descriptor validation, ServiceRegistry slot registration, middleware injection.src/api-plugin-contacts.spec-conformance.test.ts— pins every public-API claim: exported symbols, error-code registry, OTel instrument names, route paths, ServiceRegistry slots.tests/integration/full-boot.test.ts— boots auth + database + contacts plugins against real SurrealDB testcontainers and walks the full CRUD + membership + audience-resolve roundtrip.tests/integration/group-depth.test.ts— pins themaxGroupDepthrepository guard against real SurrealDB.tests/integration/bulk-import.test.ts— pins per-row error reporting against real SurrealDB.
Migration impact
None — additive only. No consumer of @suaveplan/api-plugin-contacts exists in genesis yet.