• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

apowers313 / aiforge / 21152598554

19 Jan 2026 10:00PM UTC coverage: 83.846% (+0.9%) from 82.966%
21152598554

push

github

apowers313
Merge branch 'master' of https://github.com/apowers313/aiforge

1604 of 1876 branches covered (85.5%)

Branch coverage included in aggregate %.

8242 of 9867 relevant lines covered (83.53%)

20.24 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

69.7
/src/server/api/routes/workspace.ts
1
/**
2
 * Workspace API routes - manages UI state that syncs across devices
3
 */
4
import { Router } from 'express';
1✔
5
import type { Request, Response, NextFunction, RequestHandler } from 'express';
6
import { z } from 'zod';
1✔
7
import { validateBody } from '../middleware/validation.js';
1✔
8
import { requireAuth } from '../middleware/auth.js';
1✔
9
import { ApiError } from '../middleware/error.js';
1✔
10
import type { WorkspaceStateService } from '../../services/workspace/WorkspaceStateService.js';
11

12
// Extend Express Request to include workspace service
13
declare module 'express-serve-static-core' {
14
  interface Request {
15
    workspaceStateService?: WorkspaceStateService;
16
  }
17
}
18

19
const router = Router();
1✔
20

21
// All routes require authentication
22
router.use(requireAuth);
1✔
23

24
// Request schemas
25
const UpdateWorkspaceSchema = z.object({
1✔
26
  sidebarCollapsed: z.boolean().optional(),
1✔
27
  sidebarWidth: z.number().min(180).max(600).optional(),
1✔
28
  expandedProjectIds: z.array(z.string()).optional(),
1✔
29
  activeShellId: z.string().nullable().optional(),
1✔
30
  terminalFontSize: z.number().min(8).max(32).optional(),
1✔
31
  terminalTheme: z.string().optional(),
1✔
32
  contextSidebarPinned: z.boolean().optional(),
1✔
33
  contextSidebarWidth: z.number().min(280).max(500).optional(),
1✔
34
});
1✔
35

36
type UpdateWorkspaceBody = z.infer<typeof UpdateWorkspaceSchema>;
37

38
/**
39
 * Helper to get session token from request
40
 */
41
function getSessionToken(req: Request): string {
16✔
42
  const cookies = req.cookies as { session?: string };
16✔
43
  if (!cookies.session) {
16!
44
    throw ApiError.unauthorized('Session token not found');
×
45
  }
×
46
  return cookies.session;
16✔
47
}
16✔
48

49
/**
50
 * GET /api/workspace
51
 * Get current workspace state
52
 */
53
router.get('/', async (req, res, next) => {
1✔
54
  try {
4✔
55
    if (!req.workspaceStateService) {
4!
56
      throw ApiError.internal('Workspace service not configured');
×
57
    }
×
58

59
    const sessionToken = getSessionToken(req);
4✔
60
    const workspaceState = await req.workspaceStateService.get(sessionToken);
4✔
61
    res.json({ workspaceState });
4✔
62
  } catch (err) {
4!
63
    next(err);
×
64
  }
×
65
});
4✔
66

67
/**
68
 * PATCH /api/workspace
69
 * Update workspace state (partial updates supported)
70
 */
71
router.patch('/', validateBody(UpdateWorkspaceSchema), async (req, res, next) => {
1✔
72
  try {
12✔
73
    if (!req.workspaceStateService) {
12!
74
      throw ApiError.internal('Workspace service not configured');
×
75
    }
×
76

77
    const sessionToken = getSessionToken(req);
12✔
78
    const body = req.body as UpdateWorkspaceBody;
12✔
79

80
    const updates: Partial<{
12✔
81
      sidebarCollapsed: boolean;
82
      sidebarWidth: number;
83
      expandedProjectIds: string[];
84
      activeShellId: string | null;
85
      terminalFontSize: number;
86
      terminalTheme: string;
87
      contextSidebarPinned: boolean;
88
      contextSidebarWidth: number;
89
    }> = {};
12✔
90

91
    if (body.sidebarCollapsed !== undefined) {
12✔
92
      updates.sidebarCollapsed = body.sidebarCollapsed;
8✔
93
    }
8✔
94
    if (body.sidebarWidth !== undefined) {
12!
95
      updates.sidebarWidth = body.sidebarWidth;
×
96
    }
×
97
    if (body.expandedProjectIds !== undefined) {
12✔
98
      updates.expandedProjectIds = body.expandedProjectIds;
3✔
99
    }
3✔
100
    if (body.activeShellId !== undefined) {
12✔
101
      updates.activeShellId = body.activeShellId;
4✔
102
    }
4✔
103
    if (body.terminalFontSize !== undefined) {
12!
104
      updates.terminalFontSize = body.terminalFontSize;
×
105
    }
×
106
    if (body.terminalTheme !== undefined) {
12!
107
      updates.terminalTheme = body.terminalTheme;
×
108
    }
×
109
    if (body.contextSidebarPinned !== undefined) {
12!
110
      updates.contextSidebarPinned = body.contextSidebarPinned;
×
111
    }
×
112
    if (body.contextSidebarWidth !== undefined) {
12!
113
      updates.contextSidebarWidth = body.contextSidebarWidth;
×
114
    }
×
115

116
    const workspaceState = await req.workspaceStateService.update(sessionToken, updates);
12✔
117
    res.json({ workspaceState });
12✔
118
  } catch (err) {
12!
119
    next(err);
×
120
  }
×
121
});
12✔
122

123
/**
124
 * Middleware to attach workspace state service
125
 */
126
export function attachWorkspaceStateService(workspaceStateService: WorkspaceStateService): RequestHandler {
1✔
127
  return (req: Request, _res: Response, next: NextFunction): void => {
12✔
128
    req.workspaceStateService = workspaceStateService;
285✔
129
    next();
285✔
130
  };
285✔
131
}
12✔
132

133
export { router as workspaceRouter };
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc