@doccov/api 0.3.4 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doccov/api",
3
- "version": "0.3.4",
3
+ "version": "0.3.6",
4
4
  "description": "DocCov API - Badge endpoint and coverage services",
5
5
  "keywords": [
6
6
  "doccov",
@@ -18,18 +18,19 @@
18
18
  "license": "MIT",
19
19
  "author": "Ryan Waits",
20
20
  "type": "module",
21
- "main": "./src/index.ts",
22
21
  "scripts": {
23
22
  "dev": "bun run --hot src/index.ts",
24
23
  "start": "bun run src/index.ts",
25
- "lint": "biome check src/",
26
- "lint:fix": "biome check --write src/",
27
- "format": "biome format --write src/"
24
+ "lint": "biome check src/ api/",
25
+ "lint:fix": "biome check --write src/ api/",
26
+ "format": "biome format --write src/ api/"
28
27
  },
29
28
  "dependencies": {
30
- "@openpkg-ts/spec": "^0.8.0",
29
+ "@ai-sdk/anthropic": "^2.0.55",
30
+ "@doccov/sdk": "^0.13.0",
31
+ "@openpkg-ts/spec": "^0.9.0",
31
32
  "@vercel/sandbox": "^1.0.3",
32
- "hono": "^4.0.0",
33
+ "ai": "^5.0.111",
33
34
  "ms": "^2.1.3",
34
35
  "zod": "^3.25.0"
35
36
  },
package/src/index.ts CHANGED
@@ -2,20 +2,20 @@ import { Hono } from 'hono';
2
2
  import { cors } from 'hono/cors';
3
3
  import { rateLimit } from './middleware/rate-limit';
4
4
  import { badgeRoute } from './routes/badge';
5
- import { scanRoute } from './routes/scan';
5
+ import { planRoute } from './routes/plan';
6
6
 
7
7
  const app = new Hono();
8
8
 
9
9
  // Middleware
10
10
  app.use('*', cors());
11
11
 
12
- // Rate limit /scan endpoint: 10 requests per minute per IP
12
+ // Rate limit /plan endpoint: 10 requests per minute per IP
13
13
  app.use(
14
- '/scan/*',
14
+ '/plan',
15
15
  rateLimit({
16
16
  windowMs: 60 * 1000,
17
17
  max: 10,
18
- message: 'Too many scan requests. Please try again in a minute.',
18
+ message: 'Too many plan requests. Please try again in a minute.',
19
19
  }),
20
20
  );
21
21
 
@@ -23,10 +23,12 @@ app.use(
23
23
  app.get('/', (c) => {
24
24
  return c.json({
25
25
  name: 'DocCov API',
26
- version: '0.3.0',
26
+ version: '0.4.0',
27
27
  endpoints: {
28
28
  badge: '/badge/:owner/:repo',
29
- scan: '/scan',
29
+ plan: '/plan',
30
+ execute: '/execute',
31
+ 'execute-stream': '/execute-stream',
30
32
  health: '/health',
31
33
  },
32
34
  });
@@ -38,7 +40,7 @@ app.get('/health', (c) => {
38
40
 
39
41
  // Routes
40
42
  app.route('/badge', badgeRoute);
41
- app.route('/scan', scanRoute);
43
+ app.route('/plan', planRoute);
42
44
 
43
45
  // Vercel serverless handler + Bun auto-serves this export
44
46
  export default app;
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Plan route for local development (mirrors api/plan.ts)
3
+ * Does NOT use Vercel Sandbox - only GitHub API + Anthropic Claude
4
+ */
5
+
6
+ import { fetchGitHubContext, parseScanGitHubUrl } from '@doccov/sdk';
7
+ import { Hono } from 'hono';
8
+ import { generateBuildPlan } from '../../lib/plan-agent';
9
+
10
+ export const planRoute = new Hono();
11
+
12
+ planRoute.post('/', async (c) => {
13
+ const body = await c.req.json<{ url: string; ref?: string; package?: string }>();
14
+
15
+ if (!body.url) {
16
+ return c.json({ error: 'url is required' }, 400);
17
+ }
18
+
19
+ // Validate URL format
20
+ let repoUrl: string;
21
+ try {
22
+ const parsed = parseScanGitHubUrl(body.url);
23
+ if (!parsed) {
24
+ return c.json({ error: 'Invalid GitHub URL' }, 400);
25
+ }
26
+ repoUrl = `https://github.com/${parsed.owner}/${parsed.repo}`;
27
+ } catch {
28
+ return c.json({ error: 'Invalid GitHub URL' }, 400);
29
+ }
30
+
31
+ try {
32
+ // Fetch project context from GitHub
33
+ const context = await fetchGitHubContext(repoUrl, body.ref);
34
+
35
+ // Check for private repos
36
+ if (context.metadata.isPrivate) {
37
+ return c.json(
38
+ {
39
+ error: 'Private repositories are not supported',
40
+ hint: 'Use a public repository or run doccov locally',
41
+ },
42
+ 403,
43
+ );
44
+ }
45
+
46
+ // Generate build plan using AI
47
+ const plan = await generateBuildPlan(context, {
48
+ targetPackage: body.package,
49
+ });
50
+
51
+ return c.json({
52
+ plan,
53
+ context: {
54
+ owner: context.metadata.owner,
55
+ repo: context.metadata.repo,
56
+ ref: context.ref,
57
+ packageManager: context.packageManager,
58
+ isMonorepo: context.workspace.isMonorepo,
59
+ },
60
+ });
61
+ } catch (error) {
62
+ console.error('Plan generation error:', error);
63
+
64
+ if (error instanceof Error) {
65
+ if (error.message.includes('404') || error.message.includes('not found')) {
66
+ return c.json({ error: 'Repository not found' }, 404);
67
+ }
68
+ if (error.message.includes('rate limit')) {
69
+ return c.json({ error: 'GitHub API rate limit exceeded' }, 429);
70
+ }
71
+ }
72
+
73
+ return c.json(
74
+ {
75
+ error: 'Failed to generate build plan',
76
+ message: error instanceof Error ? error.message : 'Unknown error',
77
+ },
78
+ 500,
79
+ );
80
+ }
81
+ });
package/tsconfig.json CHANGED
@@ -10,6 +10,6 @@
10
10
  "isolatedModules": true,
11
11
  "noEmit": true
12
12
  },
13
- "include": ["api/**/*", "src/**/*"],
14
- "exclude": ["node_modules"]
13
+ "include": ["api/**/*", "functions/**/*", "lib/**/*", "src/**/*"],
14
+ "exclude": ["node_modules", "dist"]
15
15
  }
package/vercel.json CHANGED
@@ -1,10 +1,8 @@
1
1
  {
2
+ "framework": null,
3
+ "installCommand": "bun install",
4
+ "outputDirectory": ".",
2
5
  "rewrites": [
3
- { "source": "/badge/:path*", "destination": "/api" },
4
- { "source": "/spec/:path*", "destination": "/api" },
5
- { "source": "/scan-stream", "destination": "/api/scan-stream" },
6
- { "source": "/scan/detect", "destination": "/api/scan/detect" },
7
- { "source": "/scan", "destination": "/api/scan" },
8
6
  { "source": "/(.*)", "destination": "/api" }
9
7
  ]
10
8
  }
@@ -1,118 +0,0 @@
1
- /**
2
- * Detect endpoint - uses SDK detection via SandboxFileSystem.
3
- * Detects monorepo structure and package manager for a GitHub repository.
4
- */
5
-
6
- import {
7
- detectPackageManager,
8
- SandboxFileSystem,
9
- detectMonorepo as sdkDetectMonorepo,
10
- } from '@doccov/sdk';
11
- import type { VercelRequest, VercelResponse } from '@vercel/node';
12
- import { Sandbox } from '@vercel/sandbox';
13
-
14
- export const config = {
15
- runtime: 'nodejs',
16
- maxDuration: 60, // Quick detection, 1 minute max
17
- };
18
-
19
- interface DetectRequestBody {
20
- url: string;
21
- ref?: string;
22
- }
23
-
24
- interface PackageInfo {
25
- name: string;
26
- path: string;
27
- description?: string;
28
- }
29
-
30
- interface DetectResponse {
31
- isMonorepo: boolean;
32
- packageManager: 'npm' | 'pnpm' | 'bun' | 'yarn';
33
- packages?: PackageInfo[];
34
- defaultPackage?: string;
35
- error?: string;
36
- }
37
-
38
- export default async function handler(req: VercelRequest, res: VercelResponse) {
39
- // CORS
40
- res.setHeader('Access-Control-Allow-Origin', '*');
41
- res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
42
- res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
43
-
44
- if (req.method === 'OPTIONS') {
45
- return res.status(200).end();
46
- }
47
-
48
- if (req.method !== 'POST') {
49
- return res.status(405).json({ error: 'Method not allowed' });
50
- }
51
-
52
- const body = req.body as DetectRequestBody;
53
-
54
- if (!body.url) {
55
- return res.status(400).json({ error: 'url is required' });
56
- }
57
-
58
- try {
59
- const result = await detectRepoStructure(body.url);
60
- return res.status(200).json(result);
61
- } catch (error) {
62
- const message = error instanceof Error ? error.message : String(error);
63
- return res.status(500).json({
64
- isMonorepo: false,
65
- packageManager: 'npm',
66
- error: message,
67
- } as DetectResponse);
68
- }
69
- }
70
-
71
- /**
72
- * Detect repository structure using SDK utilities via SandboxFileSystem.
73
- */
74
- async function detectRepoStructure(url: string): Promise<DetectResponse> {
75
- const sandbox = await Sandbox.create({
76
- source: {
77
- url,
78
- type: 'git',
79
- },
80
- resources: { vcpus: 2 },
81
- timeout: 60 * 1000, // 1 minute
82
- runtime: 'node22',
83
- });
84
-
85
- try {
86
- // Create SDK FileSystem abstraction for sandbox
87
- const fs = new SandboxFileSystem(sandbox);
88
-
89
- // Use SDK detection functions
90
- const [monoInfo, pmInfo] = await Promise.all([sdkDetectMonorepo(fs), detectPackageManager(fs)]);
91
-
92
- if (!monoInfo.isMonorepo) {
93
- return {
94
- isMonorepo: false,
95
- packageManager: pmInfo.name,
96
- };
97
- }
98
-
99
- // Map SDK package info to API response format
100
- const packages: PackageInfo[] = monoInfo.packages
101
- .filter((p) => !p.private)
102
- .map((p) => ({
103
- name: p.name,
104
- path: p.path,
105
- description: p.description,
106
- }))
107
- .sort((a, b) => a.name.localeCompare(b.name));
108
-
109
- return {
110
- isMonorepo: true,
111
- packageManager: pmInfo.name,
112
- packages,
113
- defaultPackage: packages[0]?.name,
114
- };
115
- } finally {
116
- await sandbox.stop();
117
- }
118
- }