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

7.3 KiB

Wire SurrealImportStore into @suaveplan/api-plugin-import

Package: @suaveplan/api-plugin-import Status: PROPOSED Type: modified-requirements (follow-up to add-api-plugin-import)

  • Spec delta: ./specs/api-plugin-import/spec.md

Why

The initial migration (openspec/archive/2026-05-19-add-api-plugin-import/) explicitly scoped out the SurrealDB-backed ImportStoreAdapter implementation. The published ### Excluded from this migration section reads:

SurrealImportStore — requires @suaveplan/surrealdb migrations + the Surreal-side schema. Genesis ships MemoryImportStore against the same ImportStoreAdapter interface so the SurrealDB adapter lands as a sibling package without breaking consumers. Listed in pending follow-ups.

Both prerequisites now exist on genesis:

  • @suaveplan/surrealdb ships at packages/database/surrealdb/ with the SurrealAdapter class implementing DatabaseAdapter.
  • @suaveplan/api-plugin-database ships at packages/api/api-plugin-database/ exposing a pooled SurrealAdapter via ServiceRegistry["db"].
  • @suaveplan/api-plugin-events ships the canonical positional-SurQL adapter pattern ($p0, $p1, …) at packages/api/api-plugin-events/src/repositories/.

This change adds the durable SurrealImportStore implementation of the existing ImportStoreAdapter interface, ships the matching .surql migration co-located with the package, and registers a corresponding spec requirement. The change does NOT rewrite the existing service, routes, parsers, transform pipeline, or MemoryImportStore — only adds the SurrealDB store as an alternative backend selectable by the host.

What changes

  • ADDED: SurrealImportStore implements ImportStoreAdapter at src/store/surreal/surreal-import-store.ts with create, get, update, delete, list parity. Uses positional SurQL placeholders per the @suaveplan/api-plugin-events repository pattern ($p0, $p1, …).
  • ADDED: SurrealImportStoreConfig Zod boundary schema (db adapter handle + optional table name + optional logger + optional clock).
  • ADDED: surql/tables/import_job.surql migration defining the import_job table schema (id, owner_id, status, total_rows, processed_rows, success_count, error_count, errors, metadata, created_at, updated_at, completed_at) with the canonical OVERWRITE form used by api-plugin-events.
  • ADDED: 1 new error code (STORAGE_ERROR already exists — reused for adapter failures; no registry growth).
  • ADDED: OTel counters import.store.writes_total{outcome} and import.store.reads_total{outcome} registered on first use via the existing getImportInstruments() bundle (extension — keeps the bundle accessor stable).
  • ADDED: Subpath export ./store/surreal/surreal-import-store in package.json#exports.
  • ADDED: Unit tests with a fake SurrealAdapter exercising every SurQL string, parameter ordering, and error path (mirrors event-repository.test.ts).
  • ADDED: Integration test under tests/integration/surreal-import-store.test.ts using a real SurrealDBFixture from @suaveplan/testing-integration-surrealdb. Applies the .surql migration, round-trips a record, exercises filter/limit/offset on list, and asserts the update-on-missing error message.
  • ADDED: @suaveplan/surrealdb + @suaveplan/testing-integration-surrealdb as deps if not present.

Out of scope

Not addressed in this change — each remains a separate proposal:

  • Streaming Readable / AsyncIterable input on startImport (§4 future enhancement).
  • Foreign-key resolution across rows (§4).
  • Parallel batch workers (§4).
  • S3 / HTTP URL source adapters (§4).
  • SSE progress push from the route layer (§4).
  • Mitata benchmarks for the SurrealDB store (covered by the monorepo-wide benchmark wave).
  • Postgres / SQLite store adapters (separate sibling proposals — ImportStoreAdapter is the contract that admits them).
  • Any change to the existing MemoryImportStore, ImportService, createImportRoutes, parsers, transform pipeline, progress tracker, plugin descriptor, problem-json renderer, or OTel instrument bundle public surface beyond the additive counter pair noted above.
  • Any modification to other packages/api/api-plugin-*/, MANIFEST.md, STATUS.md, CLAUDE.md, or the root package.json (except an additive bun.lock refresh if a workspace dep needs hoisting).

Source mapping

This change derives from the original archive's ### Excluded from this migration section (openspec/archive/2026-05-19-add-api-plugin-import/proposal.md) and from the upstream enterprise audit ../suaveplan/enterprise/api-plugin-import.md §3 row 5.

Requirement Source Notes
SurrealImportStore implements ImportStoreAdapter openspec/archive/2026-05-19-add-api-plugin-import/proposal.md (Excluded) + ../suaveplan/enterprise/api-plugin-import.md §3 row 5 Implements the SHIPPED ImportStoreAdapter interface — zero contract drift
Positional $p0,$p1,… SurQL placeholders packages/api/api-plugin-events/src/repositories/event-repository/event-repository.ts L20-L25 Pattern shared across all genesis SurrealDB repositories
Co-located .surql migration packages/api/api-plugin-events/surql/tables/event.surql Same surql/tables/<table>.surql layout
Zod boundary on store config openspec/AGENTS.md §11 Named-interface + parse-at-boundary policy
OTel counters on write / read openspec/AGENTS.md §13 step 0 Cached bundle accessor (getImportInstruments) extended additively
Integration test against real SurrealDB testcontainer packages/api/api-plugin-events/tests/integration/full-boot.test.ts Same SurrealDBFixture lifecycle
Update-on-missing rejects with Job "<id>" not found openspec/specs/api-plugin-import/spec.md (SHIPPED MemoryImportStore requirement) Contract parity with the in-memory store

Impact

Affected dependents

bunx turbo ls --filter=...@suaveplan/api-plugin-import enumerates dependents.

  • No dependents currently consume MemoryImportStore-specific symbols; the store is selected at host wiring time via the ImportStoreAdapter interface. Adding SurrealImportStore is purely additive. — [x] no action needed (reason: additive sibling implementation of an existing interface; existing consumers continue to receive MemoryImportStore unless they opt in)