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

3.8 KiB

Tasks — add-api-plugin

1. Scaffold

  • 1.1 Create packages/api/api-plugin/ with src/{define,registry,context,service-registry,hook-executor,topo-sort,error-codes,types}/ + tests/setup/
  • 1.2 package.json v0.1.0, type module, sideEffects false. Deps: @suaveplan/error @suaveplan/events @suaveplan/types tslib (workspace:^ / catalog:build), zod (catalog:forms), @opentelemetry/api (catalog:observability). Dev: @suaveplan/testing @vitest/coverage-istanbul vitest fast-check (catalog:testing), @types/node typescript (catalog:), vite vite-plugin-dts (catalog:build). 8 subpath exports.
  • 1.3 tsconfig.json + tsconfig.lib.json extending ../../../tsconfig.base.json
  • 1.4 vite.config.ts (dts, ES, externals)
  • 1.5 vitest.config.ts (pool: forks, provider: istanbul, thresholds 100/100/100/100, testTimeout: 30000, setupFiles: ['./tests/setup/polyfills.ts'])
  • 1.6 tests/setup/polyfills.ts

2. Implementation

  • 2.1 src/error-codes.ts + .md + test — 10 PLUGIN.* codes + PluginError
  • 2.2 src/types/api-plugin.ts + .md — PluginDescriptor, PluginInstance, PluginState (pending/registered/initializing/initialized/ready/disabled/error), PluginNode
  • 2.3 src/topo-sort/topo-sort.{ts,test.ts,md,index.ts} — Kahn's + DFS cycle detect + alpha tie-break; export validateDependencies
  • 2.4 src/service-registry/service-registry.{ts,md,index.ts} — empty ServiceRegistry interface for module augmentation, ServiceKey = keyof ServiceRegistry helper
  • 2.5 src/context/context.{ts,test.ts,md,index.ts} — createPluginContext(opts); typed getService/registerService/hasService; getOwnConfig<T>(); signal: AbortSignal
  • 2.6 src/hook-executor/hook-executor.{ts,test.ts,md,index.ts} — priority-sorted handlers, execute (sequential abort-on-throw) + executeAll (parallel allSettled); does NOT include lifecycle hook slots
  • 2.7 src/define/define.{ts,test.ts,md,index.ts} — definePlugin factory with TConfig inference
  • 2.8 src/registry/registry.{ts,test.ts,md,index.ts} — PluginRegistry class; topo-sort delegation; 4-phase lifecycle; initTimeout/shutdownTimeout enforcement via AbortSignal; defineCodes-typed errors; OTel span per hook
  • 2.9 src/index.ts — named re-exports only

3. Tests

  • 3.1 Co-located *.test.ts per spec scenario
  • 3.2 src/api-plugin.spec-conformance.test.ts — every PLUGIN.* thrown from production; expectTypeOf on definePlugin<TConfig> + getService<K> + getOwnConfig<T>
  • 3.3 tests/property.test.ts — fast-check topo-sort dep invariant + determinism (200 runs each)
  • 3.4 tests/dep-graph.test.ts — deps allowlist + subpath coverage + sibling .md presence
  • 3.5 All tests import runner from @suaveplan/testing/runner
  • 3.6 Every it(...) has { timeout: 5000 } (30000 for AbortSignal-timeout tests)

4. Documentation

  • 4.1 Sibling .md per impl file (≥200 words)
  • 4.2 README.md — Quick-start, Modules list, ServiceRegistry augmentation example, sample plugin

5. Gates

  • 5.1 bunx turbo lint typecheck test --filter=@suaveplan/api-plugin --force — green
  • 5.2 bunx turbo test:coverage --filter=@suaveplan/api-plugin --force — 100/100/100/100
  • 5.3 bun scripts/openspec-conformance.ts @suaveplan/api-plugin — exit 0
  • 5.4 bunx turbo build --filter=@suaveplan/api-plugin --force — green
  • 5.5 Banned-pattern grep clean; no export * in feature barrels; no direct vitest/bun:test imports

6. Archive

  • 6.1 Fold specs/api-plugin/spec.md → openspec/specs/api-plugin/spec.md
  • 6.2 Move change folder → openspec/archive/<yyyy-mm-dd>-add-api-plugin/
  • 6.3 Refresh MANIFEST + STATUS (@suaveplan/api-plugin → ✅)