apigrip 0.1.0 → 0.2.0

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.
@@ -1,12 +1,62 @@
1
1
  import fs from 'node:fs';
2
+ import path from 'node:path';
2
3
  import { parseSpec } from '../../core/spec-parser.js';
3
4
  import { createServer } from '../../server/index.js';
4
- import { resolveProjectContext } from '../resolve-project.js';
5
+ import { findProjectForDir } from '../../core/projects-store.js';
6
+
7
+ const ROOT_SPEC_FILES = ['openapi.yaml', 'openapi.yml', 'openapi.json', 'swagger.yaml', 'swagger.yml', 'swagger.json'];
8
+
9
+ /**
10
+ * Quick project resolution for the serve command.
11
+ * Unlike resolveProjectContext, this never does BFS discovery.
12
+ * Only auto-opens cwd if it's a git repo with a root-level spec file.
13
+ */
14
+ async function resolveServeProject(argv) {
15
+ // 1. Explicit flags take priority
16
+ if (argv.spec) {
17
+ const projectDir = argv.project || process.cwd();
18
+ return { projectDir, specPath: argv.spec };
19
+ }
20
+ if (argv.project) {
21
+ const specPath = findRootSpec(argv.project);
22
+ return { projectDir: argv.project, specPath };
23
+ }
24
+
25
+ // 2. Check if cwd is inside a bookmarked project
26
+ const cwd = process.cwd();
27
+ const bookmark = findProjectForDir(cwd);
28
+ if (bookmark) {
29
+ const specPath = bookmark.spec || findRootSpec(bookmark.path);
30
+ return { projectDir: bookmark.path, specPath };
31
+ }
32
+
33
+ // 3. Only auto-open cwd if it's a git repo with a root-level spec
34
+ const isGitRepo = fs.existsSync(path.join(cwd, '.git'));
35
+ if (isGitRepo) {
36
+ const specPath = findRootSpec(cwd);
37
+ if (specPath) {
38
+ return { projectDir: cwd, specPath };
39
+ }
40
+ }
41
+
42
+ // 4. Start without a project — user can open from history in the UI
43
+ return { projectDir: null, specPath: null };
44
+ }
45
+
46
+ function findRootSpec(dir) {
47
+ for (const name of ROOT_SPEC_FILES) {
48
+ const p = path.join(dir, name);
49
+ try {
50
+ if (fs.statSync(p).isFile()) return p;
51
+ } catch { /* not found */ }
52
+ }
53
+ return null;
54
+ }
5
55
 
6
56
  export async function serveCommand(argv) {
7
- const { projectDir, specPath } = await resolveProjectContext(argv);
57
+ const { projectDir, specPath } = await resolveServeProject(argv);
8
58
 
9
- if (!fs.existsSync(projectDir)) {
59
+ if (projectDir && !fs.existsSync(projectDir)) {
10
60
  console.error(`Project directory does not exist: ${projectDir}`);
11
61
  process.exitCode = 2;
12
62
  return;
@@ -34,8 +84,11 @@ export async function serveCommand(argv) {
34
84
 
35
85
  const { app } = createServer({ projectDir, specPath, spec, noBrowse });
36
86
 
37
- app.listen(port, host, () => {
87
+ const server = app.listen(port, host, () => {
38
88
  console.log(`Server listening on http://${host}:${port}`);
89
+ if (projectDir) {
90
+ console.log(`Project: ${projectDir}`);
91
+ }
39
92
  if (argv.open) {
40
93
  import('child_process').then(({ exec }) => {
41
94
  const cmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
@@ -43,4 +96,15 @@ export async function serveCommand(argv) {
43
96
  });
44
97
  }
45
98
  });
99
+
100
+ server.on('error', (err) => {
101
+ if (err.code === 'EADDRINUSE') {
102
+ console.error(`Error: Port ${port} is already in use. Try --port <number>`);
103
+ } else if (err.code === 'EACCES') {
104
+ console.error(`Error: Permission denied for port ${port}. Try a port above 1024.`);
105
+ } else {
106
+ console.error(`Error starting server: ${err.message}`);
107
+ }
108
+ process.exitCode = 1;
109
+ });
46
110
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apigrip",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "A spec-first, read-only OpenAPI client for developers",
5
5
  "type": "module",
6
6
  "main": "./lib/index.cjs",
@@ -2,6 +2,7 @@ import { Router } from 'express';
2
2
  import { getGitInfo } from '../../core/git-info.js';
3
3
  import { parseSpec } from '../../core/spec-parser.js';
4
4
  import { discoverSpec } from '../../core/spec-discovery.js';
5
+ import { addProject } from '../../core/projects-store.js';
5
6
  import { broadcast } from './events.js';
6
7
  import path from 'path';
7
8
  import fs from 'fs';
@@ -62,6 +63,9 @@ export function createProjectRoutes(state) {
62
63
  // Switch project
63
64
  state.projectDir = newPath;
64
65
 
66
+ // Auto-bookmark the project
67
+ try { addProject(newPath); } catch { /* already bookmarked or invalid — ignore */ }
68
+
65
69
  // Discover and parse spec
66
70
  const specResult = await discoverSpec(newPath);
67
71
  state.specPath = specResult?.specPath || null;