Initial Commit
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
# 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 `/contacts` and `/groups` route trees on the host `OpenAPIHono`.
|
||||
- ADDED: `ContactsPluginOptionsSchema`, `ContactsPluginOptions`, `ContactsPluginConfig` — Zod-validated configuration.
|
||||
- ADDED: `SurrealContactRepository`, `SurrealGroupRepository`, `SurrealMembershipRepository` — `@suaveplan/contacts` store-interface implementations over `SurrealAdapter`.
|
||||
- ADDED: `CONTACTS_API_CODES` / `ContactsApiError` (`@suaveplan/error` based) — 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 `ContextVariableMap` augmentation — `contactRepository`, `groupRepository`, `membershipRepository`, `eventBus` slots (eliminates upstream's `as any` casts).
|
||||
- ADDED: `ServiceRegistry` augmentation — exposes `contactRepository` / `groupRepository` / `membershipRepository` as typed services to sibling plugins.
|
||||
- ADDED: SurrealQL schema files for `contact`, `contact_group`, `group_membership` tables and the `MigrationRunner` integration.
|
||||
- ADDED: Bulk-import endpoint honouring `maxImportBatchSize` with per-row error reporting (Zod-validated).
|
||||
- ADDED: Repository-level `maxGroupDepth` enforcement (`createGroup` and `moveGroup` raise `CONTACTS_API.GROUP_DEPTH_EXCEEDED`).
|
||||
- ADDED: Typed `buildTree(root: Group, descendants: readonly Group[]): GroupTreeNode` helper.
|
||||
- ADDED: Integration tests using `@suaveplan/testing-integration-surrealdb` testcontainers — 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 by `tests/dep-graph.test.ts` for SuaveplanError lineage.
|
||||
- `src/otel/instruments.test.ts` — asserts every instrument binds against a real in-memory `MeterProvider` after `resetContactsApiInstruments`.
|
||||
- `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 the `maxGroupDepth` repository 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.
|
||||
Reference in New Issue
Block a user