Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,77 @@
# 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.