7.7 KiB
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?)— typedPluginDescriptor<ExportPluginConfig>factory, registersexportProvidersandexportJobsservices inServiceRegistry, mountscreateExportRoutes(...)ontoctx.getService("app")at the configuredbasePath, emitsexport:initialized/export:ready/export:started/export:completedon the bus. - ADDED:
ExportPluginConfigSchema+ExportPluginConfig— Zod{ basePath, maxRows, maxFileSizeBytes, defaultFormat }with defaults. - ADDED:
registerDataProvider(providers, provider)— typed registration helper. - ADDED:
CsvExporter,JsonExporter,XlsxExporter—Exporterimplementations with helper functions (escapeCsvField,formatCellValue,convertToCsv,projectRow,convertToJson,escapeXml,columnIndexToLetter,formatXlsxCell,buildSpreadsheetXml). - ADDED:
createExportRoutes(deps)—OpenAPIHonowithGET /,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) extendingSuaveplanError. - ADDED:
getExportInstruments()/resetExportInstruments()— OTelexports_total{format,outcome},export_rows_total{format},export_duration_mshistogram. - ADDED:
ServiceRegistrymodule augmentation forexportProvidersandexportJobsslots.
Out of scope
- SurrealDB-backed persistence,
GET /:id/downloadstreaming endpoint, OOXML-binary.xlsx, JWT/permissions/rate-limit/idempotency/audit middleware, andsurql/migrations — the upstream1.0.5src/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-limitwhich 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,runExportwith 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,onInitregisters services + emitsexport:initialized,onReadymounts routes + emitsexport:ready, custom basePath,onCloseclears 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-memoryMeterProvider.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.