Phase 5 - BYOB Backend (TASK-007A/B): - LocalSQL Adapter with full CloudStore API compatibility - QueryBuilder translates Parse-style queries to SQL - SchemaManager with PostgreSQL/Supabase export - LocalBackendServer with REST endpoints - BackendManager with IPC handlers for Electron - In-memory fallback when better-sqlite3 unavailable Phase 3 - GitHub Panel (GIT-004): - Issues tab with list/detail views - Pull Requests tab with list/detail views - GitHub API client with OAuth support - Repository info hook integration Phase 3 - Editor UX Bugfixes (TASK-013): - Legacy runtime detection banners - Read-only enforcement for legacy projects - Code editor modal close improvements - Property panel stuck state fix - Blockly node deletion and UI polish Phase 11 - Cloud Functions Planning: - Architecture documentation for workflow automation - Execution history storage schema design - Canvas overlay concept for debugging Docs: Updated LEARNINGS.md and COMMON-ISSUES.md
11 KiB
GIT-004A: GitHub Client Foundation - COMPLETE ✅
Status: Complete
Completed: January 14, 2026
Implementation Time: ~2 hours
Overview
Built a comprehensive GitHub REST API client layer on top of the existing OAuth authentication, providing type-safe access to GitHub's API with built-in rate limiting, caching, and error handling.
What Was Implemented
✅ TypeScript Type Definitions
File: packages/noodl-editor/src/editor/src/services/github/GitHubTypes.ts
Comprehensive interfaces for all GitHub API data structures:
-
Core Types:
GitHubIssue- Issue data with labels, assignees, milestonesGitHubPullRequest- PR data with merge status and checksGitHubRepository- Repository information with permissionsGitHubUser- User/author informationGitHubOrganization- Organization dataGitHubLabel- Issue/PR labelsGitHubMilestone- Project milestonesGitHubComment- Issue/PR commentsGitHubCommit- Commit informationGitHubCheckRun- CI/CD check runsGitHubReview- PR review data
-
Utility Types:
GitHubRateLimit- Rate limit trackingGitHubApiResponse<T>- Wrapper with rate limit infoGitHubIssueFilters- Query filters for issues/PRsCreateIssueOptions- Issue creation parametersUpdateIssueOptions- Issue update parametersGitHubApiError- Error response structure
✅ GitHub API Client
File: packages/noodl-editor/src/editor/src/services/github/GitHubClient.ts
Singleton service extending EventDispatcher with:
Authentication Integration:
- Automatically initializes when user authenticates
- Listens for auth state changes via EventDispatcher
- Re-initializes Octokit when token refreshes
- Clears cache on disconnection
Rate Limiting:
- Tracks rate limit from response headers
- Emits
rate-limit-warningwhen approaching limit (10% remaining) - Emits
rate-limit-updatedon every API call - Provides
getTimeUntilRateLimitReset()utility - User-friendly error messages when rate limited
Caching:
- LRU cache with configurable TTL (default 30 seconds)
- Max 100 cached entries
- Cache invalidation on mutations (create/update/delete)
- Pattern-based cache clearing
- Per-method TTL customization (e.g., 5 minutes for labels)
Error Handling:
- HTTP status code mapping to user-friendly messages
- 401: "Please reconnect your GitHub account"
- 403: Rate limit or permissions error
- 404: Resource not found
- 422: Validation errors with details
- Proper error propagation with context
API Methods Implemented:
Repository Methods:
getRepository(owner, repo)- Get repo infolistRepositories(options)- List user repos
Issue Methods:
listIssues(owner, repo, filters)- List issues with filteringgetIssue(owner, repo, issue_number)- Get single issuecreateIssue(owner, repo, options)- Create new issueupdateIssue(owner, repo, issue_number, options)- Update issuelistIssueComments(owner, repo, issue_number)- Get commentscreateIssueComment(owner, repo, issue_number, body)- Add comment
Pull Request Methods:
listPullRequests(owner, repo, filters)- List PRsgetPullRequest(owner, repo, pull_number)- Get single PRlistPullRequestCommits(owner, repo, pull_number)- List PR commits
Label Methods:
listLabels(owner, repo)- List repo labels
Utility Methods:
getRateLimit()- Get current rate limit statusclearCache()- Clear all cached dataisReady()- Check if authenticated and readygetTimeUntilRateLimitReset()- Time until limit resets
✅ Public API Exports
File: packages/noodl-editor/src/editor/src/services/github/index.ts
Clean barrel export with:
GitHubOAuthService- OAuth authenticationGitHubClient- API client- All TypeScript interfaces and types
- JSDoc examples for usage
Technical Architecture
Service Layer Structure
packages/noodl-editor/src/editor/src/services/
├── GitHubOAuthService.ts # OAuth (existing)
└── github/
├── GitHubClient.ts # API client (new)
├── GitHubTypes.ts # Type definitions (new)
└── index.ts # Public exports (new)
Integration Pattern
// GitHubClient listens to GitHubOAuthService
GitHubOAuthService.instance.on('auth-state-changed', (event) => {
if (event.authenticated) {
// Initialize Octokit with token
GitHubClient.instance.initializeOctokit();
}
});
// Usage in components
const client = GitHubClient.instance;
const { data: issues, rateLimit } = await client.listIssues('owner', 'repo', {
state: 'open',
labels: ['bug', 'enhancement'],
sort: 'updated',
direction: 'desc'
});
Cache Strategy
- Read operations: Check cache first, API on miss
- Write operations: Invalidate related caches
- TTL defaults:
- Issues/PRs: 30 seconds
- Repository info: 1 minute
- Labels: 5 minutes
Rate Limit Management
GitHub API limits:
- Authenticated users: 5,000 requests/hour
- Strategy: Track remaining, warn at 10%, cache aggressively
Type Safety Improvements
Before (Manual Typing)
const response = await fetch(`https://api.github.com/repos/${owner}/${repo}/issues`);
const issues = await response.json(); // any
After (Type-Safe)
const { data: issues } = await client.listIssues(owner, repo); // GitHubIssue[]
issues.forEach((issue) => {
console.log(issue.title); // TypeScript knows all properties
});
Event-Based Architecture
GitHubClient emits events for UI updates:
client.on('rate-limit-warning', ({ rateLimit }) => {
toast.warning(`API rate limit low: ${rateLimit.remaining} requests remaining`);
});
client.on('rate-limit-updated', ({ rateLimit }) => {
updateStatusBar(`GitHub API: ${rateLimit.remaining}/${rateLimit.limit}`);
});
API Filter Compatibility
GitHub API has different filter parameters for issues vs PRs. The client handles these differences:
Issues:
- Supports
milestoneparameter (string or number) - Supports
labelsarray (converted to comma-separated string)
Pull Requests:
- No
milestonefilter - Different
sortoptions (popularity,long-runningvscomments) - Client maps
sort: 'comments'to'created'for PRs
Files Created
- ✅
packages/noodl-editor/src/editor/src/services/github/GitHubTypes.ts(298 lines) - ✅
packages/noodl-editor/src/editor/src/services/github/GitHubClient.ts(668 lines) - ✅
packages/noodl-editor/src/editor/src/services/github/index.ts(46 lines)
Total: 1,012 lines of production code
Dependencies
Already Installed ✅
@octokit/rest@^20.1.2- GitHub REST API client
No New Dependencies Required
All existing dependencies were sufficient.
Testing Plan
Manual Testing Checklist
- Client initializes when authenticated
- API calls work (list repos, issues, PRs)
- Rate limit tracking updates correctly
- Cache hit/miss behavior
- Error handling (404, 403, 422)
- Token refresh doesn't break active client
- Disconnect clears cache and resets client
Future Unit Tests
describe('GitHubClient', () => {
describe('caching', () => {
it('returns cached data within TTL', async () => {
// Mock API call
// Call twice, verify API called once
});
it('invalidates cache on update', async () => {
// Mock list issues
// Update issue
// Verify list cache cleared
});
});
describe('rate limiting', () => {
it('emits warning at threshold', async () => {
// Mock response with low rate limit
// Verify event emitted
});
});
});
Usage Examples
Basic Issue Listing
import { GitHubClient } from '@noodl-editor/services/github';
const client = GitHubClient.instance;
// Simple list
const { data: issues } = await client.listIssues('owner', 'repo');
// Filtered list
const { data: openBugs } = await client.listIssues('owner', 'repo', {
state: 'open',
labels: ['bug'],
sort: 'updated',
direction: 'desc',
per_page: 25
});
Create Issue with Error Handling
try {
const { data: newIssue } = await client.createIssue('owner', 'repo', {
title: 'Bug: Component not rendering',
body: 'Steps to reproduce:\n1. ...',
labels: ['bug', 'priority-high'],
assignees: ['username']
});
console.log(`Created issue #${newIssue.number}`);
} catch (error) {
// User-friendly error message
console.error(error.message); // "Invalid request: Title is required"
}
Monitor Rate Limit
client.on('rate-limit-updated', ({ rateLimit }) => {
const percent = (rateLimit.remaining / rateLimit.limit) * 100;
console.log(`GitHub API: ${percent.toFixed(1)}% remaining`);
});
client.on('rate-limit-warning', ({ rateLimit }) => {
const resetTime = new Date(rateLimit.reset * 1000);
alert(`GitHub rate limit low! Resets at ${resetTime.toLocaleTimeString()}`);
});
Success Criteria
- GitHubClient service created with singleton pattern
- Type-safe interfaces for all GitHub API responses
- Rate limiting tracked and warnings emitted
- Request caching with configurable TTL
- Error handling with user-friendly messages
- Integration with existing GitHubOAuthService
- Clean public API via index.ts
- EventDispatcher integration for React components
- No new dependencies required
Next Steps
GIT-004B: Issues Panel (Read & Display)
Now that the API client foundation is in place, we can build UI components:
- Create GitHubPanel sidebar component
- Issues list with filtering UI
- Issue detail view with markdown rendering
- Search/filter functionality
Blocked Tasks Unblocked
- ✅ GIT-004B (Issues Panel - Read)
- ✅ GIT-004C (Pull Requests Panel)
- ✅ GIT-004D (Create & Update Issues)
- ✅ GIT-004E (Component Linking - depends on 004D)
- ✅ GIT-004F (Dashboard Widgets)
Lessons Learned
-
Octokit Type Compatibility:
- GitHub API parameters have subtle differences between endpoints
- Need to map/transform filters for issues vs PRs
- Milestone can be string OR number depending on endpoint
-
EventDispatcher Pattern:
- Using Phase 0 best practices (
.on()with context) - Clean integration for React components via
useEventListener
- Using Phase 0 best practices (
-
Cache Strategy:
- 30-second default TTL balances freshness and API usage
- Pattern-based invalidation prevents stale data
- LRU eviction prevents memory growth
-
Rate Limit UX:
- Warning at 10% threshold gives users time to adjust
- Time-until-reset calculation helps users plan
- User-friendly error messages reduce frustration
Documentation Added
- Comprehensive JSDoc comments on all public methods
- Type definitions with property descriptions
- Usage examples in index.ts
- Architecture diagrams in this doc
References
Task Status: ✅ COMPLETE
Ready for: GIT-004B (Issues Panel UI)
Estimated time for 004B: 10-14 hours
Next session: Create sidebar panel component structure
Completed: January 14, 2026 22:11 UTC+1