Initial Commit
This commit is contained in:
138
openspec/changes/enterprise-server-seed/proposal.md
Normal file
138
openspec/changes/enterprise-server-seed/proposal.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# OpenSpec Change: enterprise-server-seed
|
||||
|
||||
- **Change ID**: enterprise-server-seed
|
||||
- **Package**: `@suaveplan/seed`
|
||||
- **Category**: server/infra
|
||||
- **Target path**: `server/infra/seed`
|
||||
- **Status**: PROPOSED
|
||||
- **Date**: 2026-04-24
|
||||
- **Reference spec**: `/mnt/s/web/suaveplan/enterprise/seed.md`
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Deliver `@suaveplan/seed` on the genesis branch from scratch at enterprise quality. The package provides a database seeding and fixture framework for development, testing, and demo environments. It exposes a `SeedRunner` with declarative `SeedDefinition` entries (each with `name`, `run`, optional `dependsOn`, `condition`, and `teardown`), ordered execution based on dependency graph, idempotent re-run via `onConflict: 'skip' | 'update' | 'error'`, and a `SeedContext` injected into each handler carrying a `DatabaseAdapter` (from `@suaveplan/db`), logger, and factory helpers. Genesis adds Zod schemas, `@suaveplan/error` codes, OTel spans per seed step, and full integration tests against `@suaveplan/db-sqlite`.
|
||||
|
||||
---
|
||||
|
||||
## Current-state audit (genesis)
|
||||
|
||||
Does not exist on genesis — fresh build.
|
||||
|
||||
---
|
||||
|
||||
## Available workspace deps on genesis
|
||||
|
||||
| Package | Role |
|
||||
|---|---|
|
||||
| `@suaveplan/error` (`workspace:^`) | Typed `SEED.*` error codes |
|
||||
| `@suaveplan/types` (`workspace:^`) | `DatabaseLike`, `LoggerLike` |
|
||||
| `@suaveplan/db` (`workspace:^`) | `DatabaseAdapter` interface |
|
||||
| `@suaveplan/logger` (`workspace:^`) | Structured logs |
|
||||
| `zod` (`catalog:forms`) | Options and definition schemas |
|
||||
| `@opentelemetry/api` (`catalog:observability`) | Spans + counters |
|
||||
| `tslib` (`catalog:build`) | Runtime helpers |
|
||||
|
||||
---
|
||||
|
||||
## Public API surface
|
||||
|
||||
```ts
|
||||
class SeedRunner {
|
||||
constructor(options: SeedRunnerOptions)
|
||||
register(definition: SeedDefinition): void
|
||||
run(names?: string[]): Promise<SeedReport>
|
||||
teardown(names?: string[]): Promise<void>
|
||||
}
|
||||
|
||||
function createSeedRunner(options: SeedRunnerOptions): SeedRunner
|
||||
|
||||
type SeedFn = (ctx: SeedContext) => Promise<void>
|
||||
|
||||
interface SeedDefinition {
|
||||
name: string
|
||||
run: SeedFn
|
||||
teardown?: SeedFn
|
||||
dependsOn?: string[]
|
||||
condition?: (ctx: SeedContext) => Promise<boolean>
|
||||
onConflict?: 'skip' | 'update' | 'error' // default: 'skip'
|
||||
}
|
||||
|
||||
interface SeedContext {
|
||||
db: DatabaseAdapter
|
||||
logger: LoggerLike
|
||||
env: string // 'development' | 'test' | 'production'
|
||||
}
|
||||
|
||||
interface SeedRunnerOptions {
|
||||
db: DatabaseAdapter
|
||||
env?: string // default: process.env.NODE_ENV ?? 'development'
|
||||
logger?: LoggerLike
|
||||
}
|
||||
|
||||
interface SeedReport {
|
||||
total: number
|
||||
ran: number
|
||||
skipped: number
|
||||
failed: number
|
||||
results: SeedStepResult[]
|
||||
}
|
||||
|
||||
interface SeedStepResult {
|
||||
name: string
|
||||
status: 'ran' | 'skipped' | 'failed'
|
||||
duration: number
|
||||
error?: string
|
||||
}
|
||||
|
||||
type SeedErrorCode =
|
||||
| 'SEED_CIRCULAR_DEPENDENCY'
|
||||
| 'SEED_STEP_FAILED'
|
||||
| 'SEED_DEFINITION_NOT_FOUND'
|
||||
| 'SEED_DEPENDENCY_NOT_REGISTERED'
|
||||
|
||||
function registerSeedErrorCodes(): void
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## File layout
|
||||
|
||||
```text
|
||||
server/infra/seed/
|
||||
package.json
|
||||
tsconfig.json
|
||||
vitest.config.ts
|
||||
src/
|
||||
index.ts
|
||||
error-codes.ts
|
||||
constants/index.ts
|
||||
types/
|
||||
seed.types.ts
|
||||
seed.types.md
|
||||
runner/
|
||||
seed-runner/
|
||||
seed-runner.ts
|
||||
seed-runner.md
|
||||
seed-runner.types.ts
|
||||
seed-runner.test.ts
|
||||
index.ts
|
||||
graph/
|
||||
dependency-graph/
|
||||
dependency-graph.ts
|
||||
dependency-graph.md
|
||||
dependency-graph.test.ts
|
||||
index.ts
|
||||
tests/
|
||||
integration.test.ts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Impact
|
||||
|
||||
Downstream: all server packages that need development data (auth, user, product seeds). Works with any `@suaveplan/db`-compatible adapter.
|
||||
|
||||
**Prerequisites on Verdaccio**: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/db`, `@suaveplan/logger`.
|
||||
**Integration tests** use `@suaveplan/db-sqlite` (dev dep).
|
||||
62
openspec/changes/enterprise-server-seed/tasks.md
Normal file
62
openspec/changes/enterprise-server-seed/tasks.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# Tasks: enterprise-server-seed
|
||||
|
||||
**Change ID**: enterprise-server-seed
|
||||
**Package**: `@suaveplan/seed`
|
||||
**Target path**: `server/infra/seed`
|
||||
**Prerequisites on Verdaccio**: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/db`, `@suaveplan/logger`
|
||||
|
||||
---
|
||||
|
||||
## 0. Pre-flight (non-negotiable)
|
||||
|
||||
- [ ] 0.1 Confirm clean working tree (`git status` reports nothing) and no in-flight worktrees with uncommitted work (`git worktree list` then `git status` in each)
|
||||
- [ ] 0.2 If any worktrees exist under `.claude/worktrees/`, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (`git worktree remove`) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
|
||||
- [ ] 0.3 Confirm the package set in `MANIFEST.md` matches `find packages -maxdepth 3 -name package.json` count. Drift here means a prior change archived without regenerating the manifest.
|
||||
|
||||
## 1. Scaffold
|
||||
|
||||
- [ ] Create directory tree per `proposal.md`.
|
||||
- [ ] `package.json` at `0.1.0`, `"sideEffects": false`, exports `"."` + `runner` + `graph`.
|
||||
- [ ] Deps: `@suaveplan/error workspace:^`, `@suaveplan/types workspace:^`, `@suaveplan/db workspace:^`, `@suaveplan/logger workspace:^`, `zod catalog:forms`, `@opentelemetry/api catalog:observability`, `tslib catalog:build`.
|
||||
- [ ] DevDeps: `vitest catalog:testing`, `@vitest/coverage-istanbul catalog:testing`, `@suaveplan/testing workspace:^`, `@suaveplan/db-sqlite workspace:^`.
|
||||
- [ ] `tsconfig.json` extending `library-node.json`; `vitest.config.ts` with `createTestConfig()`.
|
||||
- [ ] Scripts: `lint`, `typecheck`, `test`, `test:coverage`, `build`, `docs:check`.
|
||||
|
||||
## 2. Types and errors
|
||||
|
||||
- [ ] Define all interfaces and Zod schemas (`SeedDefinitionSchema`, `SeedRunnerOptionsSchema`) in `src/types/seed.types.ts`.
|
||||
- [ ] Write `seed.types.md` (≥200 words, both examples, API reference).
|
||||
- [ ] `src/error-codes.ts`: four codes; `registerSeedErrorCodes()`; eager call.
|
||||
|
||||
## 3. Dependency graph
|
||||
|
||||
- [ ] Implement `src/graph/dependency-graph/dependency-graph.ts`:
|
||||
- `topologicalSort(nodes: Map<string, string[]>): string[]` — Kahn's algorithm; throw `SEED_CIRCULAR_DEPENDENCY` on cycle.
|
||||
- `validateDependencies(definitions: SeedDefinition[]): void` — throw `SEED_DEPENDENCY_NOT_REGISTERED` if `dependsOn` references unknown names.
|
||||
- [ ] Write `dependency-graph.test.ts`: linear chain, diamond, cycle detection, missing dependency.
|
||||
- [ ] Write `dependency-graph.md` (≥200 words).
|
||||
|
||||
## 4. Seed runner
|
||||
|
||||
- [ ] Implement `SeedRunner` in `src/runner/seed-runner/seed-runner.ts`:
|
||||
- `register(definition)`: store in internal Map; validate that name is unique.
|
||||
- `run(names?)`: if `names` provided, run only those + their transitive deps; else run all. Topologically sort; execute in order; pass `SeedContext`; handle `condition` (skip if returns false); handle `onConflict`; collect `SeedStepResult`; return `SeedReport`. OTel span `seed.run` per step; counter `seed.steps_total`.
|
||||
- `teardown(names?)`: run `teardown` fn for each (in reverse dependency order).
|
||||
- Errors from handlers wrapped in `SEED_STEP_FAILED`; other steps continue unless `onConflict === 'error'`.
|
||||
- [ ] Write `seed-runner.types.ts`.
|
||||
- [ ] Write `seed-runner.test.ts` (≤500 lines): all steps run; dependency ordering; condition-skip; onConflict modes; teardown reverse order; partial run by names; circular dependency throws before execution.
|
||||
- [ ] Write `seed-runner.md` (≥200 words, simple + advanced, API reference).
|
||||
|
||||
## 5. Integration tests
|
||||
|
||||
- [ ] `tests/integration.test.ts`: use `@suaveplan/db-sqlite` with in-memory DB; register three seeds with dependency chain; run → verify DB state; teardown → verify cleanup; `{ timeout: 15000 }`.
|
||||
|
||||
## 6. Docs + verify + publish
|
||||
|
||||
- [ ] Root `README.md`: quick-start with SQLite, dependency graph example, conditional seeding, API table.
|
||||
- [ ] `bunx turbo lint --filter=@suaveplan/seed` — zero errors.
|
||||
- [ ] `bunx turbo typecheck --filter=@suaveplan/seed` — zero errors.
|
||||
- [ ] `bunx turbo test --filter=@suaveplan/seed` — all pass.
|
||||
- [ ] `bunx turbo test:coverage --filter=@suaveplan/seed` — 100%.
|
||||
- [ ] `bun scripts/publish.ts --package @suaveplan/seed`.
|
||||
- [ ] Update `CLAUDE.md`; archive to `openspec/archive/2026-04-24-enterprise-server-seed/`.
|
||||
Reference in New Issue
Block a user