Files
2026-05-27 23:29:53 +01:00

78 lines
7.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Add @suaveplan/api-plugin-export
**Package**: `@suaveplan/api-plugin-export`
**Status**: PROPOSED
**Type**: new-package (migration from `../suaveplan/packages/server/api-plugin-export`)
- **Design**: ./design.md (n/a — straight upstream parity; rationale documented inline below)
- **Spec delta**: ./specs/api-plugin-export/spec.md
## Why
Genesis needs the upstream `api-plugin-export` capability so downstream consumers (admin dashboards, analytics surfaces, GDPR subject-access endpoints) can export any registered resource to CSV, JSON, or XLSX through a uniform OpenAPI route group. Upstream `1.0.5` ships three exporters + four routes + an in-process job store + a Zod-validated config schema. Migrating it at functional parity unblocks the api-plugin-{user, audit, ...} consumers that already type against `DataProvider`, `ExportJob`, and the `export:*` event surface.
## What changes
- ADDED: `createExportPlugin(options?)` — typed `PluginDescriptor<ExportPluginConfig>` factory, registers `exportProviders` and `exportJobs` services in `ServiceRegistry`, mounts `createExportRoutes(...)` onto `ctx.getService("app")` at the configured `basePath`, emits `export:initialized` / `export:ready` / `export:started` / `export:completed` on the bus.
- ADDED: `ExportPluginConfigSchema` + `ExportPluginConfig` — Zod `{ basePath, maxRows, maxFileSizeBytes, defaultFormat }` with defaults.
- ADDED: `registerDataProvider(providers, provider)` — typed registration helper.
- ADDED: `CsvExporter`, `JsonExporter`, `XlsxExporter` — `Exporter` implementations with helper functions (`escapeCsvField`, `formatCellValue`, `convertToCsv`, `projectRow`, `convertToJson`, `escapeXml`, `columnIndexToLetter`, `formatXlsxCell`, `buildSpreadsheetXml`).
- ADDED: `createExportRoutes(deps)` — `OpenAPIHono` with `GET /`, `POST /`, `GET /:id`, `DELETE /:id`.
- ADDED: `runExport`, `getExporter`, `inferColumns`, `DEFAULT_EXPORT_CONFIG`, `ExportServiceConfig` — service-layer surface.
- ADDED: Zod schemas `ExportFormatSchema`, `ExportStatusSchema`, `ColumnDefinitionSchema`, `CreateExportJobSchema`, `ExportJobSchema`, `ExportJobListQuerySchema`.
- ADDED: Types `ExportFormat`, `ExportStatus`, `ColumnDefinition`, `CreateExportJob`, `ExportJob`, `ExportJobListQuery`, `ExportOptions`, `ExportResult`, `Exporter`, `DataProvider`, `ExportEventMap`.
- ADDED: `EXPORT_CODES` + `ExportError` — 5 stable codes (`UNSUPPORTED_FORMAT`, `ROW_LIMIT_EXCEEDED`, `SIZE_LIMIT_EXCEEDED`, `PROVIDER_NOT_FOUND`, `PROVIDER_FETCH_FAILED`) extending `SuaveplanError`.
- ADDED: `getExportInstruments()` / `resetExportInstruments()` — OTel `exports_total{format,outcome}`, `export_rows_total{format}`, `export_duration_ms` histogram.
- ADDED: `ServiceRegistry` module augmentation for `exportProviders` and `exportJobs` slots.
## Out of scope
- SurrealDB-backed persistence, `GET /:id/download` streaming endpoint, OOXML-binary `.xlsx`, JWT/permissions/rate-limit/idempotency/audit middleware, and `surql/` migrations — the upstream `1.0.5` `src/` does not implement these. Enterprise audit doc (`../suaveplan/enterprise/api-plugin-export.md` §§3–4) lists them as future work for the upstream package itself; landing them here would diverge from the parity goal and pull in `@suaveplan/api-plugin-database` / `@suaveplan/api-plugin-audit` / `@suaveplan/api-plugin-rate-limit` which are themselves pending migration.
- Replacing upstream's plain `Error("…")` throws with a typed hierarchy — done in this change as a genesis-quality uplift (§7 Required enterprise-quality improvements item: "Error hierarchy"). The class names and codes shipped here mirror the upstream audit's call-out and become the canonical contract.
## Source mapping
This change derives from `../suaveplan/enterprise/api-plugin-export.md` and the upstream `src/` tree.
| Requirement | Source lines | Notes |
|---|---|---|
| Plugin factory + lifecycle | L17-L22 / `src/index.ts` L38-L104 | rewritten against the genesis `@suaveplan/api-plugin` descriptor shape (`configSchema: ZodType` + `defaults: Partial<T>`) |
| Zod config schema (basePath/maxRows/maxFileSizeBytes/defaultFormat) | L17-L18 / `src/index.ts` L21-L30 | verbatim |
| Three exporter classes (CSV / JSON / XLSX-XML) | L17 / `src/exporters/{csv,json,xlsx}` | verbatim |
| `runExport`, `getExporter`, `inferColumns`, `DEFAULT_EXPORT_CONFIG` | L24 / `src/services/export/export.service.ts` | verbatim |
| `OpenAPIHono` route group at `/exports` with GET/POST/GET/:id/DELETE/:id | L25 / `src/routes/exports/exports.ts` | verbatim |
| `DataProvider` registry as `Map<string, DataProvider>` | L23 / `src/types.ts` L127-L132 | verbatim |
| Job store as `Map<string, ExportJob>` (in-process) | L13 / `src/index.ts` L42, L66 | verbatim (memory-only is upstream's documented state; persistence is enterprise-future) |
| `export:*` event emissions | L25 / `src/index.ts` L65, L87-L92, L96 | verbatim |
| RFC 4180 CSV (delimiter / quoting / newlines) | L26 / `src/exporters/csv/csv.exporter.ts` | verbatim |
| SpreadsheetML XLSX (XML, not OOXML/ZIP) | L28, L38 / `src/exporters/xlsx/xlsx.exporter.ts` | verbatim — OOXML binary is enterprise-future |
| `registerDataProvider(providers, provider)` helper | `src/index.ts` L109-L114 | verbatim |
| `ExportEventMap` and `@suaveplan/api` `EventMap` declaration | `src/types.ts` L139-L170 | trimmed to genesis equivalent — augments `@suaveplan/api-plugin/service-registry` for `exportProviders`/`exportJobs` (genesis pattern) |
### Requirements with no source line
| Requirement | Justification |
|---|---|
| `EXPORT_CODES` registry + `ExportError` class | Required by genesis rule: every package extends `SuaveplanError` via `defineCodes`. Upstream uses `new Error(...)`; the audit doc (L52) explicitly flags this as a gap to fix during migration. |
| OTel instruments (`exports_total{format,outcome}`, `export_rows_total{format}`, `export_duration_ms`) | Required by genesis pre-archive checklist row 5 ("OTel instruments asserted with real in-memory `MeterProvider`"). Upstream audit doc L51 calls for this. |
| `ServiceRegistry` module augmentation for `exportProviders` / `exportJobs` | Required by genesis `@suaveplan/api-plugin` typed-slot contract; upstream's `@suaveplan/api` had a looser `services: Map<string, unknown>` so no augmentation was needed. |
| 100% Istanbul coverage; sibling `.md` per impl file | Genesis-wide policy, not package-specific. |
## Verification approach
Every requirement carries a `**Verified by:**` line in `specs/api-plugin-export/spec.md` pointing at concrete `it(...)` titles in the listed files:
- `src/exporters/csv/csv.test.ts` — RFC 4180 CSV, helpers, exporter shape.
- `src/exporters/json/json.test.ts` — projection, pretty-print, exporter shape.
- `src/exporters/xlsx/xlsx.test.ts` — XML escaping, column-letter math, cell formatting, builder.
- `src/services/export.service.test.ts` — `getExporter` (incl. unsupported-format error), `inferColumns`, `runExport` with row + size limits.
- `src/routes/exports.test.ts` — GET list with filters/pagination, POST create (CSV/JSON/XLSX, custom columns/filename), 404 on missing resource, 400 on limits, 400 on provider error, GET/:id, DELETE/:id.
- `src/plugin/plugin.test.ts` — descriptor metadata, `onInit` registers services + emits `export:initialized`, `onReady` mounts routes + emits `export:ready`, custom basePath, `onClose` clears state.
- `src/error-codes/error-codes.test.ts` — every code reachable from a production path.
- `src/otel/instruments.test.ts` — counters/histogram emit against a real in-memory `MeterProvider`.
- `src/api-plugin-export.spec-conformance.test.ts` — module augmentation, public surface, type-level claims (`expectTypeOf`).
## Migration impact
None — additive only. No genesis consumer currently imports `@suaveplan/api-plugin-export`; the package is brand new on this branch.