61 lines
3.6 KiB
Markdown
61 lines
3.6 KiB
Markdown
# Change: add-import-sse-progress
|
|
|
|
## Why
|
|
|
|
`@suaveplan/api-plugin-import` shipped (archive `2026-05-19-add-api-plugin-import`) with the
|
|
**`## Excluded from this migration`** clause explicitly deferring SSE progress push:
|
|
|
|
> Foreign-key resolution, parallel batch workers, resumable checkpoints, S3/URL source adapters, **SSE progress push**
|
|
|
|
This change lifts that single deferral. Consumers can now subscribe to a job's progress
|
|
via `GET /imports/:id/progress` and receive `text/event-stream` frames driven directly off
|
|
the existing `ImportService.bus` events — no polling. The other deferred items
|
|
(foreign-key resolution, parallel workers, checkpoints, S3 sources) remain deferred.
|
|
|
|
The endpoint reuses:
|
|
|
|
- the canonical `ImportProgress` payload type,
|
|
- the existing `import.progress` / `import.completed` / `import.failed` events,
|
|
- the existing `IMPORT.*` error registry,
|
|
- the owner-scoping invariants of `ImportService.getJob(jobId, ownerId)`.
|
|
|
|
The endpoint introduces three new OTel counters scoped to the new feature module:
|
|
`import.sse.connected`, `import.sse.events_sent{kind}`, `import.sse.disconnected{reason}`.
|
|
|
|
## Affected requirements
|
|
|
|
ADDED requirement: **SSE progress push** (one new requirement; no MODIFIED / REMOVED entries).
|
|
|
|
## Source mapping
|
|
|
|
| Spec scenario | Implementation | Test |
|
|
| --- | --- | --- |
|
|
| GET `/imports/:id/progress` opens an event-stream | `src/routes/progress-sse/progress-sse.ts` | `src/routes/progress-sse/progress-sse.test.ts::"opens a text/event-stream and emits an initial progress frame"` |
|
|
| Each `import.progress` bus emit produces an `event: progress` frame | `src/routes/progress-sse/progress-sse.ts` | `src/routes/progress-sse/progress-sse.test.ts::"emits a progress frame for every bus progress event"` |
|
|
| Terminal `import.completed` emits `event: complete` then closes | `src/routes/progress-sse/progress-sse.ts` | `src/routes/progress-sse/progress-sse.test.ts::"emits a complete frame and closes on import.completed"` |
|
|
| Terminal `import.failed` emits `event: failed` then closes | `src/routes/progress-sse/progress-sse.ts` | `src/routes/progress-sse/progress-sse.test.ts::"emits a failed frame and closes on import.failed"` |
|
|
| Foreign-owner request returns 404 problem+json | `src/routes/progress-sse/progress-sse.ts` | `src/routes/progress-sse/progress-sse.test.ts::"returns 404 problem+json for a foreign-owner job"` |
|
|
| Anonymous request returns 401 problem+json | route is mounted under `requireAuth()` in `build-routes.ts` | `src/routes/progress-sse/progress-sse.test.ts::"returns 401 problem+json when the caller is anonymous"` |
|
|
| Client disconnect cleans up the bus listener | `AbortSignal` wired into the `ReadableStream.cancel` path | `src/routes/progress-sse/progress-sse.test.ts::"detaches the listener and increments disconnect counter on client abort"` |
|
|
| OTel counters increment on connect / event / disconnect | `src/routes/progress-sse/progress-sse.ts` (new instruments wired through `getImportInstruments()`) | `src/routes/progress-sse/progress-sse.test.ts::"increments import.sse counters on connect, send, and disconnect"` |
|
|
|
|
## Impact
|
|
|
|
### Affected dependents
|
|
|
|
Run `bunx turbo ls --filter=...@suaveplan/api-plugin-import` to enumerate.
|
|
|
|
- `@suaveplan/api` — [ ] tests run / [ ] docs reviewed / [x] no action needed (reason:
|
|
additive route under an existing prefix; no signature changes to the consumed surface)
|
|
|
|
Foundation package? No. This is a feature-additive change on a server-tier plugin.
|
|
|
|
## Out of scope (still deferred from the original `Excluded` list)
|
|
|
|
- Foreign-key resolution
|
|
- Parallel batch workers
|
|
- Resumable checkpoints
|
|
- S3 / URL source adapters
|
|
- Streaming input parsing
|
|
- Benchmarks
|