feat: add Linear GraphQL API emulator#5
Conversation
Port vercel-labs/emulate#91 (Phase 1 Linear emulator) to the @pleaseai fork conventions: - New @pleaseai/emulate-linear package under packages/linear/ (bun export condition, @emulators/core ^0.6.0 + graphql dependency, tsgo type-check) - Register linear in the CLI service registry (packages/emulate) - bun:test instead of vitest; explicit 405 handlers for non-POST /graphql since core's router has no `all`; Hono type sourced from @emulators/core - skills/linear/SKILL.md and README entries adapted to fork CLI usage Read-only GraphQL: issues, projects, teams, users, organizations, labels, and workflow states with Relay-style pagination. 26 tests pass. Original PR: vercel-labs/emulate#91 by @mvanhorn
Not up to standards ⛔🔴 Issues
|
| Category | Results |
|---|---|
| BestPractice | 3 high |
| Security | 1 high |
🟢 Metrics 374 complexity · 12 duplication
Metric Results Complexity 374 Duplication 12
AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.
TIP This summary will be updated as you push new changes.
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Pull Request Overview
The port of the Linear GraphQL API emulator is largely complete in terms of feature set, but several implementation details pose risks to its reliability. Most critically, the store collections are missing indices for foreign keys (e.g., organization_id, assignee_id); without these, the @emulators/core framework will fail to resolve relationships between entities, effectively breaking graph traversal.
Codacy analysis indicates the PR is not up to standards, primarily due to code duplication in entity resolvers and unsafe property access. Furthermore, 'packages/linear/src/resolvers.ts' and 'packages/linear/src/index.ts' are identified as complex files with insufficient test coverage. While 405 Method Not Allowed handlers were implemented as requested in the acceptance criteria, they currently lack automated verification. These issues, particularly the indexing logic, should be addressed prior to merging to ensure the emulator functions as intended.
About this PR
- There is a systemic pattern of code duplication across entity resolvers (Organization, User, Team, etc.) regarding standard field mapping. Centralizing this logic into a helper function would improve maintainability.
Test suggestions
- Execute a valid GraphQL query for an issue by ID and identifier
- Perform GraphQL schema introspection
- Verify authentication using a raw PAT in the Authorization header
- Verify authentication using a Bearer token in the Authorization header
- Verify that missing or invalid authentication returns a 401 with a Linear-compatible error code
- Test forward pagination using 'first' and 'after'
- Test backward pagination using 'last' and 'before'
- Verify that GET, PUT, PATCH, and DELETE requests to /graphql return a 405 Method Not Allowed error
- Validate that GraphQL validation errors return a 200 OK with an errors array containing extensions.code
- Automate verification of relational traversals (e.g., fetching a team's issues) to ensure store indices are working
Prompt proposal for missing tests
Consider implementing these tests if applicable:
1. Verify that GET, PUT, PATCH, and DELETE requests to /graphql return a 405 Method Not Allowed error
2. Automate verification of relational traversals (e.g., fetching a team's issues) to ensure store indices are working
TIP Improve review quality by adding custom instructions
TIP How was this review? Give us feedback
- store: index organization_id (users/teams), lead_id (projects), state_id/assignee_id/creator_id (issues) — all fields queried via findBy, per the fork's store convention (avoids full-scan fallback) - resolvers: use Object.hasOwn for directValue field access (own-property only) - tests: cover 405 responses for non-POST methods on /graphql
|


Summary
Ports vercel-labs/emulate#91 (Phase 1 Linear emulator, by @mvanhorn) into this fork's conventions.
The upstream PR targets a different structure (pnpm,
@emulators/*scope, aapps/webdocs site,@emulators/coreas a workspace dep), so it could not be merged directly. The service core was re-homed to match this fork instead.What's included
@pleaseai/emulate-linearpackage underpackages/linear/@emulators/core@^0.6.0+graphqldeps,tsgotype-checklinearin the CLI service registry (packages/emulate) + dependency wiringHonotype sourced from@emulators/core(no directhonodep)405handlers for non-POST/graphql(core's router has noall)vitest→bun:testusingcreateServerif,node:bufferimport)skills/linear/SKILL.md, package README, and root README entries adapted to fork CLI usage (bun packages/emulate/dist/index.js --service linear)Excluded from the upstream PR
apps/web/*docs site,pnpm-lock.yaml, andemulate.config.example.yaml— these don't exist in this fork.Surface (read-only GraphQL)
POST /graphqlwith schema introspection, PAT auth (Authorization: <api_key>), Relay-style pagination, and query resolvers for Issue, Project, Team, User, Organization, Label, and WorkflowState.Verification
build(7) ·type-check(13) ·test(13, linear 26 pass) ·lintemulate listshows linear; service boots and answers GraphQL queries; missing auth returns401Follow-up work: mutations, webhooks, OAuth 2.0, inspector UI.