@reopt-ai/dev-proxy 1.1.1 → 1.1.2

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.
@@ -0,0 +1,25 @@
1
+ import { vi } from "vitest";
2
+ /**
3
+ * Canonical fs mock shape. Use with vi.hoisted() or vi.doMock().
4
+ * Note: vi.mock() hoists calls above imports, so these factories
5
+ * cannot be imported inside vi.mock() callbacks. Instead, define
6
+ * the mock inline and use this as a reference for the shape.
7
+ */
8
+ export function createFsMock() {
9
+ return {
10
+ existsSync: vi.fn(),
11
+ readFileSync: vi.fn(),
12
+ writeFileSync: vi.fn(),
13
+ mkdirSync: vi.fn(),
14
+ chmodSync: vi.fn(),
15
+ renameSync: vi.fn(),
16
+ unlinkSync: vi.fn(),
17
+ watch: vi.fn(),
18
+ };
19
+ }
20
+ export function createChildProcessMock() {
21
+ return {
22
+ execSync: vi.fn(),
23
+ execFileSync: vi.fn(),
24
+ };
25
+ }
@@ -0,0 +1,12 @@
1
+ import { afterEach, beforeEach, vi } from "vitest";
2
+ const noop = () => undefined;
3
+ // Silence console output in all tests — individual tests can still
4
+ // assert on console.error/warn via the existing spies.
5
+ beforeEach(() => {
6
+ vi.spyOn(console, "log").mockImplementation(noop);
7
+ vi.spyOn(console, "error").mockImplementation(noop);
8
+ vi.spyOn(console, "warn").mockImplementation(noop);
9
+ });
10
+ afterEach(() => {
11
+ vi.restoreAllMocks();
12
+ });
@@ -332,3 +332,4 @@ function Doctor() {
332
332
  : worktreeChecks.length > 0 && (_jsx(Text, { dimColor: true, children: " checking ports..." }))] })), asyncChecks && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: [" ", String(allChecks.length), " checks:", " ", _jsxs(Text, { color: "green", children: [String(passed), " passed"] }), warnings > 0 && (_jsxs(Text, { children: [", ", _jsxs(Text, { color: "yellow", children: [String(warnings), " warnings"] })] })), failed > 0 && (_jsxs(Text, { children: [", ", _jsxs(Text, { color: "red", children: [String(failed), " failed"] })] }))] }) }))] }));
333
333
  }
334
334
  render(_jsx(Doctor, {}));
335
+ export const __testing = { collectSubdomains, withTimeout, checkWorktreeConfig };
@@ -27,4 +27,5 @@ function Status() {
27
27
  }
28
28
  return (_jsxs(Box, { flexDirection: "column", children: [_jsx(ExitOnRender, {}), _jsx(Header, { text: "dev-proxy status" }), _jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Row, { label: "Domain", value: config.domain }), _jsx(Row, { label: "HTTP", value: `:${String(config.port)}` }), _jsx(Row, { label: "HTTPS", value: `:${String(config.httpsPort)}` })] }), _jsx(Section, { title: `Routes (${String(allRoutes.length)})`, children: allRoutes.map((r) => (_jsx(RouteRow, { sub: r.sub, target: r.target }, `${r.sub}-${r.target}`))) }), _jsx(Section, { title: `Projects (${String(config.projects.length)})`, children: config.projects.map((p) => (_jsx(Text, { children: ` ${p.path}` }, p.path))) }), _jsx(Section, { title: `Worktrees (${String(allWorktrees.length)})`, children: allWorktrees.map((w) => "ports" in w.entry ? (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: ` ${w.name}` }), Object.entries(w.entry.ports).map(([svc, p]) => (_jsx(RouteRow, { sub: ` ${svc}`, target: `:${String(p)}` }, svc)))] }, w.name)) : (_jsx(RouteRow, { sub: w.name, target: `:${String(w.entry.port)}` }, w.name))) })] }));
29
29
  }
30
+ export const __testing = { formatTarget };
30
31
  render(_jsx(Status, {}));
@@ -15,6 +15,15 @@ function findOwningProject(cwd) {
15
15
  }
16
16
  return null;
17
17
  }
18
+ // ── Helpers ──────────────────────────────────────────────────
19
+ function formatPorts(entry) {
20
+ if ("ports" in entry) {
21
+ return Object.entries(entry.ports)
22
+ .map(([svc, p]) => `${svc}:${p}`)
23
+ .join(", ");
24
+ }
25
+ return `port ${entry.port}`;
26
+ }
18
27
  // ── List worktrees ───────────────────────────────────────────
19
28
  function WorktreeList() {
20
29
  const cfg = readGlobalConfig();
@@ -29,14 +38,6 @@ function WorktreeList() {
29
38
  if (entries.length === 0) {
30
39
  return (_jsxs(Box, { flexDirection: "column", children: [_jsx(ExitOnRender, {}), _jsx(Header, { text: "Worktrees" }), _jsx(Text, { dimColor: true, children: " (none)" })] }));
31
40
  }
32
- function formatPorts(entry) {
33
- if ("ports" in entry) {
34
- return Object.entries(entry.ports)
35
- .map(([svc, p]) => `${svc}:${p}`)
36
- .join(", ");
37
- }
38
- return `port ${entry.port}`;
39
- }
40
41
  return (_jsxs(Box, { flexDirection: "column", children: [_jsx(ExitOnRender, {}), _jsx(Header, { text: "Worktrees" }), entries.map((e) => (_jsx(Row, { label: e.name, value: `${formatPorts(e.entry)} (${e.project})`, pad: 20 }, `${e.project}:${e.name}`)))] }));
41
42
  }
42
43
  // ── Add worktree ─────────────────────────────────────────────
@@ -290,3 +291,4 @@ else {
290
291
  // "list" or no subcommand → default to list
291
292
  render(_jsx(WorktreeList, {}));
292
293
  }
294
+ export const __testing = { findOwningProject, formatPorts };
@@ -75,4 +75,7 @@ export const config = loadConfig();
75
75
  export const __testing = {
76
76
  parsePort,
77
77
  resolveFilePath,
78
+ loadJson,
79
+ loadProjectConfig,
80
+ loadConfig,
78
81
  };
@@ -392,6 +392,9 @@ export function startProxyServer(server, httpsServer) {
392
392
  });
393
393
  });
394
394
  }
395
+ function resetNextId() {
396
+ _nextId = 0;
397
+ }
395
398
  export const __testing = {
396
399
  escapeHtml,
397
400
  parseCookies,
@@ -400,4 +403,11 @@ export const __testing = {
400
403
  formatListenError,
401
404
  normalizeTargetProtocol,
402
405
  targetPort,
406
+ worktreeErrorPage,
407
+ requestTransport,
408
+ connectToTarget,
409
+ createRequestHandler,
410
+ createUpgradeHandler,
411
+ nextId,
412
+ resetNextId,
403
413
  };
@@ -104,7 +104,23 @@ function getSnapshot() {
104
104
  export function useWorktrees() {
105
105
  return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
106
106
  }
107
- export const __testing = { isValidEntry };
107
+ export const __testing = {
108
+ isValidEntry,
109
+ readRegistry,
110
+ readProjectWorktrees,
111
+ get worktreeMap() {
112
+ return worktreeMap;
113
+ },
114
+ set worktreeMap(m) {
115
+ worktreeMap = m;
116
+ },
117
+ get watchers() {
118
+ return watchers;
119
+ },
120
+ get debounceTimer() {
121
+ return debounceTimer;
122
+ },
123
+ };
108
124
  export function stopRegistry() {
109
125
  for (const w of watchers)
110
126
  w.close();
package/dist/store.js CHANGED
@@ -453,7 +453,8 @@ function getSnapshot() {
453
453
  }
454
454
  return cachedSnapshot;
455
455
  }
456
- function subscribe(callback) {
456
+ /** @internal Used by useSyncExternalStore and tests — not part of the public API. */
457
+ export function subscribe(callback) {
457
458
  listeners.add(callback);
458
459
  return () => {
459
460
  listeners.delete(callback);
@@ -485,6 +486,21 @@ export function useActiveWsCount() {
485
486
  const snap = useStore();
486
487
  return snap.activeWsCount;
487
488
  }
489
+ /** Current follow mode state. */
490
+ export function getFollowMode() {
491
+ return followMode;
492
+ }
493
+ /** Currently selected event (if any). */
494
+ export function getSelected() {
495
+ return events[selectedIndex];
496
+ }
497
+ /** Detail data for currently selected event. */
498
+ export function getSelectedDetail() {
499
+ const sel = events[selectedIndex];
500
+ if (!sel)
501
+ return null;
502
+ return detailMap.get(sel.id) ?? null;
503
+ }
488
504
  // ── Replay ───────────────────────────────────────────────────
489
505
  export function getSelectedReplayInfo() {
490
506
  const event = events[selectedIndex];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reopt-ai/dev-proxy",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "Subdomain-based reverse proxy and HTTP/WS traffic inspector TUI",
5
5
  "license": "MIT",
6
6
  "author": "reopt.ai",
@@ -53,6 +53,7 @@
53
53
  "format:check": "prettier --check .",
54
54
  "typecheck": "tsc --noEmit",
55
55
  "test": "vitest run",
56
+ "test:coverage": "vitest run --coverage",
56
57
  "test:watch": "vitest",
57
58
  "check": "pnpm typecheck && pnpm lint && pnpm format:check && pnpm test"
58
59
  },
@@ -69,6 +70,7 @@
69
70
  "@semantic-release/github": "^12.0.6",
70
71
  "@types/node": "^25.5.0",
71
72
  "@types/react": "^19.0.0",
73
+ "@vitest/coverage-v8": "^3.2.4",
72
74
  "conventional-changelog-conventionalcommits": "^9.3.0",
73
75
  "eslint": "^10.1.0",
74
76
  "eslint-config-prettier": "^10.1.8",