4.8 KiB
4.8 KiB
Tasks — add-export-download-handler
1. Error codes
- Add
JOB_NOT_READYtoEXPORT_CODESregistry insrc/error-codes/error-codes.ts - Update the JSDoc registry summary to describe
JOB_NOT_READYsemantics (raised by download handler when job is not incompletedstate) - Update
src/error-codes/error-codes.test.tsto assertJOB_NOT_READYnarrows through the public type union
2. Problem-details renderer
- Author
src/routes/problem-json/problem-json.tsmirroring theapi-plugin-importsibling (PROBLEM_JSON_CONTENT_TYPE,ProblemDetails,renderProblem,respondProblemJson) - Map
SuaveplanError→statusviacontext.statusCode(404 forJOB_NOT_FOUND, 409 forJOB_NOT_READY, 400 for everything else under 500) - Author
src/routes/problem-json/problem-json.test.tscovering: SuaveplanError mapping, plain Error fallback, unknown throwable fallback, missingstatusCodedefaults to 500 - Author
src/routes/problem-json/problem-json.md(≥200 words, Purpose / Features / Usage Basic / Usage Advanced / API Reference / Implementation Notes) - Add
./routes/problem-json/problem-jsonsubpath export topackage.json - Re-export
renderProblem,respondProblemJson,PROBLEM_JSON_CONTENT_TYPE,ProblemDetailsfromsrc/index.ts
3. OTel instruments
- Extend
ExportInstrumentsinsrc/otel/instruments.tswithdownloadServed: CounteranddownloadFailed: Counter - Register the two counters via
meter.createCounter("export.download.served", …)andmeter.createCounter("export.download.failed", …) - Update
src/otel/instruments.mdwith the two new instrument descriptions - Extend
src/otel/instruments.test.tswith: "emits download.served on a successful download" and "emits download.failed{reason} on a not-ready download"
4. Download handler
- Create
src/routes/download/download.tsexportingrunDownloadExport(deps, jobId)returning a discriminatedDownloadOutcomeunion ({ kind: "ok"; body; contentType; filename }/{ kind: "not-found" }/{ kind: "not-ready"; status }/{ kind: "provider-not-found"; resource }/{ kind: "failed"; error }) - Implementation flow:
jobStore.get(id)→null⇒not-foundraisingEXPORT.JOB_NOT_FOUND;status !== "completed"⇒not-readyraisingEXPORT.JOB_NOT_READY;providers.get(job.resource)→null⇒provider-not-foundraisingEXPORT.PROVIDER_NOT_FOUND; on any exception during fetch/render emitEXPORT.PROVIDER_FETCH_FAILED - Author
src/routes/download/download.md(≥200 words) - Author
src/routes/download/download.test.tscovering every branch with real CSV / JSON / XLSX renders
5. Route registration
- Extend
ExportRoutesDepsinsrc/routes/exports/exports.tswith optionaljobStore?: ExportJobStore - Mount
GET /{id}/downloadafter the existingGET /{id}route; build the inlineMap<string, ExportJob>adapter whenjobStoreis absent - Wire the download outcome through
respondProblemJsonfor every non-ok branch - OpenAPI route definition:
params: z.object({ id: z.string() }),responses:200(binary),404,409,502, all carryingapplication/problem+jsonschema for the failure shapes - Update
src/routes/exports/exports.mdwith a paragraph on the new endpoint - Extend
src/routes/exports/exports.test.tswith an end-to-end happy-path download + a 404 + a 409 + a 502 test using a real provider + realMemoryExportJobStore
6. Plugin wiring
- In
src/plugin/plugin.tsrunOnReady, look upctx.getService("exportJobStore")and thread it intocreateExportRoutes({ …, jobStore }). Absent slot ⇒ omit; existing behaviour preserved - Extend
src/plugin/plugin.test.tswith "download route uses exportJobStore service when wired" using aMemoryExportJobStore
7. Spec conformance
src/api-plugin-export.spec-conformance.test.tsalready pins everyEXPORT_*code; verifyJOB_NOT_READYis thrown by the download handler so the pin holds- Run
bun scripts/openspec-conformance.ts @suaveplan/api-plugin-export— expect zero errors
8. Pre-archive gates
bunx biome check src/cleanbunx turbo lint typecheck test --filter=@suaveplan/api-plugin-exportgreenbunx turbo test:coverage --filter=@suaveplan/api-plugin-export≥100/100/100/100 on touched modulesbunx turbo build --filter=@suaveplan/api-plugin-exportgreenbun run docs:check(package-scoped, viapackage.jsonscript) clean
9. Archive
- Fold
specs/api-plugin-export/spec.mddelta intoopenspec/specs/api-plugin-export/spec.md - Move
openspec/changes/add-export-download-handler→openspec/archive/2026-05-20-add-export-download-handler - Single commit
feat(api-plugin-export): add GET /:id/download handler (was §4-deferred)and push (pre-pull-rebase autostash)