3.3 KiB
3.3 KiB
Tasks: enterprise-api-plugin-graphql
Change ID: enterprise-api-plugin-graphql
Package: @suaveplan/api-plugin-graphql
Target path: api/data/api-plugin-graphql
Prerequisite packages on Verdaccio: @suaveplan/api, @suaveplan/api-shared, @suaveplan/api-plugin-auth, @suaveplan/events, @suaveplan/rate-limiter, @suaveplan/error, graphql (external)
0. Pre-flight (non-negotiable)
- 0.1 Confirm clean working tree (
git statusreports nothing) and no in-flight worktrees with uncommitted work (git worktree listthengit statusin 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.mdmatchesfind packages -maxdepth 3 -name package.jsoncount. Drift here means a prior change archived without regenerating the manifest.
1. Scaffold + types + errors + events
- Create directory;
package.jsonv0.0.0;graphqlin deps (catalog); standard tooling. - Types:
GraphqlPluginConfig(Zod),PersistedQueryStore,GraphqlContext,OperationResult. - Errors: 5-code union + registration. Test + doc.
- Events:
GraphqlEventMap(graphql:operation:completed, graphql:error).
2. Core: depth limiter
buildMaxDepthRule(maxDepth: number): ValidationRule— GraphQL validation rule that rejects queries deeper thanmaxDepth. Test + doc.
3. Core: complexity analysis
buildComplexityRule(maxComplexity: number, fieldComplexities?: Record<string, number>): ValidationRule— scores query and rejects over threshold. Test + doc.
4. Core: DataLoader factory
createDataLoader<K, V>(batchFn)— thin wrapper ensuring all resolvers use DataLoader for N+1 prevention. Test + doc.
5. Core: persisted queries
PersistedQueryStoreinterface:get(hash: string): Promise<string | null>,set(hash, query): Promise<void>.MemoryPersistedQueryStoredefault. Test + doc.
6. Auth context middleware
- Extract JWT user from
requireAuth; make available asctx.userin GraphQL execution context. - Test + doc.
7. Routes
POST /graphql— parse body; apply persisted-query lookup ifextensions.persistedQuerypresent; validate (depth + complexity + auth); execute; record OTel span + counters; emit event.GET /graphql— serve GraphiQL HTML ifconfig.playgroundandNODE_ENV !== 'production'.- Rate limit per operation type on
POST /graphql. - Route
.test.ts+.md.
8. Plugin factory + integration tests + publish
createGraphqlPlugin(schema, config?):definePlugin,dependencies: ["auth"].onInit: register configured middleware; disable introspection in production.- Integration: execute simple query → depth limit rejected → persisted query found + executed.
bunx turbo lint typecheck test --filter=@suaveplan/api-plugin-graphql— green; 100% coverage.bun scripts/publish.ts --package @suaveplan/api-plugin-graphql.- Archive →
openspec/archive/2026-04-24-enterprise-api-plugin-graphql/.