react-dev-panel 0.1.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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +130 -0
  3. package/dist/adapters/next.cjs +95 -0
  4. package/dist/adapters/next.cjs.map +1 -0
  5. package/dist/adapters/next.d.cts +17 -0
  6. package/dist/adapters/next.d.ts +17 -0
  7. package/dist/adapters/next.js +65 -0
  8. package/dist/adapters/next.js.map +1 -0
  9. package/dist/adapters/server.cjs +72 -0
  10. package/dist/adapters/server.cjs.map +1 -0
  11. package/dist/adapters/server.d.cts +24 -0
  12. package/dist/adapters/server.d.ts +24 -0
  13. package/dist/adapters/server.js +4 -0
  14. package/dist/adapters/server.js.map +1 -0
  15. package/dist/adapters/vite.cjs +301 -0
  16. package/dist/adapters/vite.cjs.map +1 -0
  17. package/dist/adapters/vite.d.cts +35 -0
  18. package/dist/adapters/vite.d.ts +35 -0
  19. package/dist/adapters/vite.js +31 -0
  20. package/dist/adapters/vite.js.map +1 -0
  21. package/dist/chunk-2ZAPVMUL.js +25 -0
  22. package/dist/chunk-2ZAPVMUL.js.map +1 -0
  23. package/dist/chunk-MAYMGQIM.js +64 -0
  24. package/dist/chunk-MAYMGQIM.js.map +1 -0
  25. package/dist/chunk-XZ4DPO52.js +190 -0
  26. package/dist/chunk-XZ4DPO52.js.map +1 -0
  27. package/dist/cli/index.cjs +221 -0
  28. package/dist/cli/index.cjs.map +1 -0
  29. package/dist/cli/index.d.cts +1 -0
  30. package/dist/cli/index.d.ts +1 -0
  31. package/dist/cli/index.js +35 -0
  32. package/dist/cli/index.js.map +1 -0
  33. package/dist/generate-UK65K5BU.js +3 -0
  34. package/dist/generate-UK65K5BU.js.map +1 -0
  35. package/dist/index.cjs +1745 -0
  36. package/dist/index.cjs.map +1 -0
  37. package/dist/index.d.cts +53 -0
  38. package/dist/index.d.ts +53 -0
  39. package/dist/index.js +1711 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/server-open-B4FK0jQF.d.cts +92 -0
  42. package/dist/server-open-B4FK0jQF.d.ts +92 -0
  43. package/package.json +77 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pavan Kumar Mistry
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,130 @@
1
+ # react-dev-panel
2
+
3
+ A **framework-agnostic, self-contained** floating dev panel for any React app — no MUI, no
4
+ Redux, no design-system coupling. One component, gated to internal/dev users, giving you:
5
+
6
+ - **Developer Logs** — live console + network capture
7
+ - **Page Performance** — Core Web Vitals with fix hints
8
+ - **Component Graph Inspector** — hover-to-source, component tree, "what's on this page", open-in-editor
9
+
10
+ Styles are injected once under a `.rdp-root` scope, so the panel can't clash with — or be
11
+ clashed by — your app's theme. The only runtime requirement is React 18+.
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ npm i -D react-dev-panel
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ Mount it once near your app root. It renders nothing in production by default.
22
+
23
+ ```tsx
24
+ import { DevPanel } from 'react-dev-panel';
25
+
26
+ export function Providers({ children }) {
27
+ return (
28
+ <>
29
+ {children}
30
+ <DevPanel
31
+ // Gate: who may see it. Defaults to NODE_ENV !== 'production'.
32
+ enabled={process.env.NODE_ENV !== 'production' || user?.isInternal}
33
+ // Current route, for tools that display it (optional).
34
+ getRoute={() => location.pathname}
35
+ />
36
+ </>
37
+ );
38
+ }
39
+ ```
40
+
41
+ That's it — a draggable launcher appears in the corner.
42
+
43
+ ### Configuration
44
+
45
+ | Prop | Type | Default | Purpose |
46
+ |------|------|---------|---------|
47
+ | `enabled` | `boolean` | `NODE_ENV !== 'production'` | Master gate — the host owns the access decision. |
48
+ | `getRoute` | `() => string` | `location.pathname` | Current route for display. Adapters wire your router. |
49
+ | `openInEditor` | `(loc, editor?) => void` | protocol URL + copy | How source files open. Adapters provide a server-backed opener. |
50
+ | `graphEndpoint` | `string` | — | URL serving the component graph JSON (Component Graph Inspector). |
51
+ | `editor` | `'auto' \| 'vscode' \| 'cursor' \| 'webstorm' \| 'zed'` | `'auto'` | Preferred editor for protocol opens. |
52
+ | `theme` | `{ accent?, accentContrast? }` | purple | Accent color override. |
53
+ | `tools` | `string[]` | all | Restrict/reorder tools by id (`'logs'`, `'perf'`, `'graph'`). |
54
+
55
+ ### Custom tools
56
+
57
+ ```tsx
58
+ import { registerTool } from 'react-dev-panel';
59
+
60
+ registerTool({
61
+ id: 'my-tool',
62
+ title: 'My Tool',
63
+ subtitle: 'Does a thing',
64
+ icon: <MyIcon />,
65
+ Panel: ({ onClose }) => <div>…</div>,
66
+ });
67
+ ```
68
+
69
+ ## Component Graph Inspector
70
+
71
+ Reliable component inspection that works in normal dev (Turbopack/Vite — no special build):
72
+
73
+ 1. **Generate the graph** (components, files, parent/child/import/route edges):
74
+ ```bash
75
+ npx dev-panel-graph --scan src
76
+ # → .dev-panel/component-graph.json
77
+ ```
78
+ 2. **Serve it + enable open-in-editor** with an adapter (below).
79
+ 3. Open the panel → **Inspect mode** → hover (highlight + tooltip), click to lock, ⌘/Ctrl+click
80
+ to open. Tabs: **Graph** (search + relationship tree), **Page** (what's mounted on this
81
+ route), **File** (source + open).
82
+
83
+ Resolution climbs the React fiber tree to the nearest component that's in the graph, so hovering
84
+ a leaf still lands on your real app component. Open-in-editor uses the editor **running the
85
+ project** (`launch-editor`) — VS Code, Cursor, WebStorm, Zed, … — via the adapter endpoint.
86
+
87
+ ### Adapters
88
+
89
+ **Vite**
90
+ ```ts
91
+ import { devPanel } from 'react-dev-panel/vite';
92
+ export default defineConfig({ plugins: [react(), devPanel({ scan: ['src'] })] });
93
+ ```
94
+
95
+ **Next.js (App Router)** — gate these yourself; dev/internal only.
96
+ ```ts
97
+ // app/dev-panel/graph/route.ts
98
+ export const { GET } = createGraphRoute({ enabled: () => process.env.NODE_ENV !== 'production' });
99
+ // app/dev-panel/open-file/route.ts
100
+ export const { POST } = createOpenFileRoute({ enabled: () => process.env.NODE_ENV !== 'production' });
101
+ ```
102
+
103
+ **Any Node server (CRA/Express/custom)**
104
+ ```ts
105
+ import { createDevPanelMiddleware } from 'react-dev-panel/server';
106
+ app.use(createDevPanelMiddleware());
107
+ ```
108
+
109
+ Then wire the client:
110
+ ```tsx
111
+ import { DevPanel, serverOpenInEditor, DEFAULT_GRAPH_ENDPOINT } from 'react-dev-panel';
112
+ <DevPanel enabled={isDev} graphEndpoint={DEFAULT_GRAPH_ENDPOINT} openInEditor={serverOpenInEditor} />
113
+ ```
114
+
115
+ Without an adapter the inspector still works at runtime (component names, tree from the graph if
116
+ served, page scan); open-in-editor falls back to `editor://` protocol URLs then copy.
117
+
118
+ ## Roadmap
119
+
120
+ - [x] Framework-agnostic core (provider, launcher, self-contained styles, tool registry)
121
+ - [x] Developer Logs · Page Performance · Component Graph Inspector
122
+ - [x] CLI `dev-panel-graph` (TypeScript Compiler API)
123
+ - [x] Adapters: `/next`, `/vite`, `/server`
124
+ - [ ] Demo apps + browser-tested screenshots
125
+ - [ ] Optional Babel/SWC transform for exact element line:col
126
+
127
+ ## License
128
+
129
+ MIT
130
+
@@ -0,0 +1,95 @@
1
+ 'use strict';
2
+
3
+ var path = require('path');
4
+ var fs = require('fs');
5
+ var promises = require('fs/promises');
6
+
7
+ // src/adapters/next.ts
8
+
9
+ // src/core/server-open.ts
10
+ var DEFAULT_GRAPH_ENDPOINT = "/dev-panel/graph";
11
+ var DEFAULT_OPEN_ENDPOINT = "/dev-panel/open-file";
12
+ function createServerOpenInEditor(endpoint = DEFAULT_OPEN_ENDPOINT) {
13
+ return async (loc, editor = "auto") => {
14
+ try {
15
+ const res = await fetch(endpoint, {
16
+ method: "POST",
17
+ headers: { "Content-Type": "application/json" },
18
+ body: JSON.stringify({ file: loc.file, line: loc.line, column: loc.column, editor })
19
+ });
20
+ if (res.ok) return true;
21
+ } catch {
22
+ }
23
+ if (typeof navigator !== "undefined" && navigator.clipboard) {
24
+ await navigator.clipboard.writeText(`${loc.file}${loc.line ? `:${loc.line}` : ""}`).catch(() => void 0);
25
+ }
26
+ return false;
27
+ };
28
+ }
29
+ var serverOpenInEditor = createServerOpenInEditor();
30
+
31
+ // src/adapters/next.ts
32
+ var EDITOR_BIN = {
33
+ auto: void 0,
34
+ vscode: "code",
35
+ cursor: "cursor",
36
+ webstorm: "webstorm",
37
+ zed: "zed"
38
+ };
39
+ var json = (body, status = 200) => new Response(typeof body === "string" ? body : JSON.stringify(body), {
40
+ status,
41
+ headers: { "Content-Type": "application/json", "Cache-Control": "no-store" }
42
+ });
43
+ async function gate(enabled) {
44
+ if (process.env.NODE_ENV === "production" && !enabled) return false;
45
+ if (enabled) return await enabled() === true;
46
+ return true;
47
+ }
48
+ function createGraphRoute(options = {}) {
49
+ const root = path.resolve(options.root ?? process.cwd());
50
+ const graphFile = path.resolve(root, options.graphFile ?? ".dev-panel/component-graph.json");
51
+ return {
52
+ async GET() {
53
+ if (!await gate(options.enabled)) return json({ error: "Not found" }, 404);
54
+ try {
55
+ return json(await promises.readFile(graphFile, "utf8"));
56
+ } catch {
57
+ return json({ error: "graph-not-generated", hint: "Run: npx dev-panel-graph" }, 404);
58
+ }
59
+ }
60
+ };
61
+ }
62
+ function createOpenFileRoute(options = {}) {
63
+ const root = path.resolve(options.root ?? process.cwd());
64
+ return {
65
+ async POST(req) {
66
+ if (!await gate(options.enabled)) return json({ error: "Not found" }, 404);
67
+ let body;
68
+ try {
69
+ body = await req.json();
70
+ } catch {
71
+ return json({ error: "invalid body" }, 400);
72
+ }
73
+ if (!body.file) return json({ error: "file required" }, 400);
74
+ const abs = path.isAbsolute(body.file) ? path.resolve(body.file) : path.resolve(root, body.file);
75
+ const rel = path.relative(root, abs);
76
+ if (rel.startsWith("..") || path.isAbsolute(rel)) return json({ error: "path escapes root" }, 403);
77
+ if (!fs.existsSync(abs) || !fs.statSync(abs).isFile()) return json({ error: "not found" }, 404);
78
+ try {
79
+ const launch = (await import('launch-editor')).default;
80
+ launch(`${abs}:${body.line ?? 1}:${body.column ?? 1}`, EDITOR_BIN[body.editor ?? "auto"]);
81
+ return json({ ok: true });
82
+ } catch (err) {
83
+ return json({ error: err instanceof Error ? err.message : "open failed" }, 500);
84
+ }
85
+ }
86
+ };
87
+ }
88
+
89
+ exports.DEFAULT_GRAPH_ENDPOINT = DEFAULT_GRAPH_ENDPOINT;
90
+ exports.DEFAULT_OPEN_ENDPOINT = DEFAULT_OPEN_ENDPOINT;
91
+ exports.createGraphRoute = createGraphRoute;
92
+ exports.createOpenFileRoute = createOpenFileRoute;
93
+ exports.serverOpenInEditor = serverOpenInEditor;
94
+ //# sourceMappingURL=next.cjs.map
95
+ //# sourceMappingURL=next.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/server-open.ts","../../src/adapters/next.ts"],"names":["resolve","readFile","isAbsolute","relative","existsSync","statSync"],"mappings":"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","file":"next.cjs","sourcesContent":["import type { OpenInEditor } from './types';\n\n/**\n * Default endpoint paths the adapters mount and the client talks to. No leading underscore —\n * Next.js App Router treats `_`-prefixed segments as private (non-routable), so this path works\n * uniformly across the Next, Vite, and generic-server adapters.\n */\nexport const DEFAULT_GRAPH_ENDPOINT = '/dev-panel/graph';\nexport const DEFAULT_OPEN_ENDPOINT = '/dev-panel/open-file';\n\n/**\n * A client `openInEditor` that POSTs to a server endpoint (mounted by an adapter), which opens\n * the file in the editor running the project via `launch-editor`. Falls back to copying the\n * path when the endpoint is unreachable. Pass to `<DevPanel openInEditor={serverOpenInEditor} />`.\n */\nexport function createServerOpenInEditor(endpoint = DEFAULT_OPEN_ENDPOINT): OpenInEditor {\n return async (loc, editor = 'auto') => {\n try {\n const res = await fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ file: loc.file, line: loc.line, column: loc.column, editor }),\n });\n if (res.ok) return true;\n } catch {\n /* fall through to copy */\n }\n if (typeof navigator !== 'undefined' && navigator.clipboard) {\n await navigator.clipboard\n .writeText(`${loc.file}${loc.line ? `:${loc.line}` : ''}`)\n .catch(() => undefined);\n }\n return false;\n };\n}\n\n/** Ready-to-use server opener pointed at the default endpoint. */\nexport const serverOpenInEditor: OpenInEditor = createServerOpenInEditor();\n","/**\n * Next.js App Router adapter for react-dev-panel. Provides route-handler factories that serve\n * the component graph and open files in the running editor.\n *\n * // app/dev-panel/graph/route.ts\n * import { createGraphRoute } from 'react-dev-panel/next';\n * export const { GET } = createGraphRoute();\n *\n * // app/dev-panel/open-file/route.ts\n * import { createOpenFileRoute } from 'react-dev-panel/next';\n * export const { POST } = createOpenFileRoute();\n *\n * Then on the client:\n * import { DevPanel, serverOpenInEditor, DEFAULT_GRAPH_ENDPOINT } from 'react-dev-panel';\n * <DevPanel enabled={isDev} graphEndpoint={DEFAULT_GRAPH_ENDPOINT} openInEditor={serverOpenInEditor} />\n *\n * IMPORTANT: gate these routes yourself (NODE_ENV / auth) — pass `enabled` or wrap the handler.\n * They are intended for development and internal use only.\n */\nimport { resolve, relative, isAbsolute } from 'node:path';\nimport { existsSync, statSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\n\nexport interface NextGraphRouteOptions {\n root?: string;\n graphFile?: string;\n /** Extra gate — return false to 404 (e.g. NODE_ENV check + developer auth). */\n enabled?: () => boolean | Promise<boolean>;\n}\n\nconst EDITOR_BIN: Record<string, string | undefined> = {\n auto: undefined,\n vscode: 'code',\n cursor: 'cursor',\n webstorm: 'webstorm',\n zed: 'zed',\n};\n\nconst json = (body: unknown, status = 200): Response =>\n new Response(typeof body === 'string' ? body : JSON.stringify(body), {\n status,\n headers: { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' },\n });\n\nasync function gate(enabled?: () => boolean | Promise<boolean>): Promise<boolean> {\n if (process.env.NODE_ENV === 'production' && !enabled) return false;\n if (enabled) return (await enabled()) === true;\n return true;\n}\n\nexport function createGraphRoute(options: NextGraphRouteOptions = {}) {\n const root = resolve(options.root ?? process.cwd());\n const graphFile = resolve(root, options.graphFile ?? '.dev-panel/component-graph.json');\n return {\n async GET() {\n if (!(await gate(options.enabled))) return json({ error: 'Not found' }, 404);\n try {\n return json(await readFile(graphFile, 'utf8'));\n } catch {\n return json({ error: 'graph-not-generated', hint: 'Run: npx dev-panel-graph' }, 404);\n }\n },\n };\n}\n\nexport function createOpenFileRoute(options: NextGraphRouteOptions = {}) {\n const root = resolve(options.root ?? process.cwd());\n return {\n async POST(req: Request) {\n if (!(await gate(options.enabled))) return json({ error: 'Not found' }, 404);\n let body: { file?: string; line?: number; column?: number; editor?: string };\n try {\n body = await req.json();\n } catch {\n return json({ error: 'invalid body' }, 400);\n }\n if (!body.file) return json({ error: 'file required' }, 400);\n const abs = isAbsolute(body.file) ? resolve(body.file) : resolve(root, body.file);\n const rel = relative(root, abs);\n if (rel.startsWith('..') || isAbsolute(rel)) return json({ error: 'path escapes root' }, 403);\n if (!existsSync(abs) || !statSync(abs).isFile()) return json({ error: 'not found' }, 404);\n try {\n const launch = (await import('launch-editor')).default;\n launch(`${abs}:${body.line ?? 1}:${body.column ?? 1}`, EDITOR_BIN[body.editor ?? 'auto']);\n return json({ ok: true });\n } catch (err) {\n return json({ error: err instanceof Error ? err.message : 'open failed' }, 500);\n }\n },\n };\n}\n\nexport { DEFAULT_GRAPH_ENDPOINT, DEFAULT_OPEN_ENDPOINT, serverOpenInEditor } from '../core/server-open';\n"]}
@@ -0,0 +1,17 @@
1
+ export { D as DEFAULT_GRAPH_ENDPOINT, a as DEFAULT_OPEN_ENDPOINT, s as serverOpenInEditor } from '../server-open-B4FK0jQF.cjs';
2
+ import 'react';
3
+
4
+ interface NextGraphRouteOptions {
5
+ root?: string;
6
+ graphFile?: string;
7
+ /** Extra gate — return false to 404 (e.g. NODE_ENV check + developer auth). */
8
+ enabled?: () => boolean | Promise<boolean>;
9
+ }
10
+ declare function createGraphRoute(options?: NextGraphRouteOptions): {
11
+ GET(): Promise<Response>;
12
+ };
13
+ declare function createOpenFileRoute(options?: NextGraphRouteOptions): {
14
+ POST(req: Request): Promise<Response>;
15
+ };
16
+
17
+ export { type NextGraphRouteOptions, createGraphRoute, createOpenFileRoute };
@@ -0,0 +1,17 @@
1
+ export { D as DEFAULT_GRAPH_ENDPOINT, a as DEFAULT_OPEN_ENDPOINT, s as serverOpenInEditor } from '../server-open-B4FK0jQF.js';
2
+ import 'react';
3
+
4
+ interface NextGraphRouteOptions {
5
+ root?: string;
6
+ graphFile?: string;
7
+ /** Extra gate — return false to 404 (e.g. NODE_ENV check + developer auth). */
8
+ enabled?: () => boolean | Promise<boolean>;
9
+ }
10
+ declare function createGraphRoute(options?: NextGraphRouteOptions): {
11
+ GET(): Promise<Response>;
12
+ };
13
+ declare function createOpenFileRoute(options?: NextGraphRouteOptions): {
14
+ POST(req: Request): Promise<Response>;
15
+ };
16
+
17
+ export { type NextGraphRouteOptions, createGraphRoute, createOpenFileRoute };
@@ -0,0 +1,65 @@
1
+ export { DEFAULT_GRAPH_ENDPOINT, DEFAULT_OPEN_ENDPOINT, serverOpenInEditor } from '../chunk-2ZAPVMUL.js';
2
+ import { resolve, isAbsolute, relative } from 'path';
3
+ import { existsSync, statSync } from 'fs';
4
+ import { readFile } from 'fs/promises';
5
+
6
+ var EDITOR_BIN = {
7
+ auto: void 0,
8
+ vscode: "code",
9
+ cursor: "cursor",
10
+ webstorm: "webstorm",
11
+ zed: "zed"
12
+ };
13
+ var json = (body, status = 200) => new Response(typeof body === "string" ? body : JSON.stringify(body), {
14
+ status,
15
+ headers: { "Content-Type": "application/json", "Cache-Control": "no-store" }
16
+ });
17
+ async function gate(enabled) {
18
+ if (process.env.NODE_ENV === "production" && !enabled) return false;
19
+ if (enabled) return await enabled() === true;
20
+ return true;
21
+ }
22
+ function createGraphRoute(options = {}) {
23
+ const root = resolve(options.root ?? process.cwd());
24
+ const graphFile = resolve(root, options.graphFile ?? ".dev-panel/component-graph.json");
25
+ return {
26
+ async GET() {
27
+ if (!await gate(options.enabled)) return json({ error: "Not found" }, 404);
28
+ try {
29
+ return json(await readFile(graphFile, "utf8"));
30
+ } catch {
31
+ return json({ error: "graph-not-generated", hint: "Run: npx dev-panel-graph" }, 404);
32
+ }
33
+ }
34
+ };
35
+ }
36
+ function createOpenFileRoute(options = {}) {
37
+ const root = resolve(options.root ?? process.cwd());
38
+ return {
39
+ async POST(req) {
40
+ if (!await gate(options.enabled)) return json({ error: "Not found" }, 404);
41
+ let body;
42
+ try {
43
+ body = await req.json();
44
+ } catch {
45
+ return json({ error: "invalid body" }, 400);
46
+ }
47
+ if (!body.file) return json({ error: "file required" }, 400);
48
+ const abs = isAbsolute(body.file) ? resolve(body.file) : resolve(root, body.file);
49
+ const rel = relative(root, abs);
50
+ if (rel.startsWith("..") || isAbsolute(rel)) return json({ error: "path escapes root" }, 403);
51
+ if (!existsSync(abs) || !statSync(abs).isFile()) return json({ error: "not found" }, 404);
52
+ try {
53
+ const launch = (await import('launch-editor')).default;
54
+ launch(`${abs}:${body.line ?? 1}:${body.column ?? 1}`, EDITOR_BIN[body.editor ?? "auto"]);
55
+ return json({ ok: true });
56
+ } catch (err) {
57
+ return json({ error: err instanceof Error ? err.message : "open failed" }, 500);
58
+ }
59
+ }
60
+ };
61
+ }
62
+
63
+ export { createGraphRoute, createOpenFileRoute };
64
+ //# sourceMappingURL=next.js.map
65
+ //# sourceMappingURL=next.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/next.ts"],"names":[],"mappings":"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","file":"next.js","sourcesContent":["/**\n * Next.js App Router adapter for react-dev-panel. Provides route-handler factories that serve\n * the component graph and open files in the running editor.\n *\n * // app/dev-panel/graph/route.ts\n * import { createGraphRoute } from 'react-dev-panel/next';\n * export const { GET } = createGraphRoute();\n *\n * // app/dev-panel/open-file/route.ts\n * import { createOpenFileRoute } from 'react-dev-panel/next';\n * export const { POST } = createOpenFileRoute();\n *\n * Then on the client:\n * import { DevPanel, serverOpenInEditor, DEFAULT_GRAPH_ENDPOINT } from 'react-dev-panel';\n * <DevPanel enabled={isDev} graphEndpoint={DEFAULT_GRAPH_ENDPOINT} openInEditor={serverOpenInEditor} />\n *\n * IMPORTANT: gate these routes yourself (NODE_ENV / auth) — pass `enabled` or wrap the handler.\n * They are intended for development and internal use only.\n */\nimport { resolve, relative, isAbsolute } from 'node:path';\nimport { existsSync, statSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\n\nexport interface NextGraphRouteOptions {\n root?: string;\n graphFile?: string;\n /** Extra gate — return false to 404 (e.g. NODE_ENV check + developer auth). */\n enabled?: () => boolean | Promise<boolean>;\n}\n\nconst EDITOR_BIN: Record<string, string | undefined> = {\n auto: undefined,\n vscode: 'code',\n cursor: 'cursor',\n webstorm: 'webstorm',\n zed: 'zed',\n};\n\nconst json = (body: unknown, status = 200): Response =>\n new Response(typeof body === 'string' ? body : JSON.stringify(body), {\n status,\n headers: { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' },\n });\n\nasync function gate(enabled?: () => boolean | Promise<boolean>): Promise<boolean> {\n if (process.env.NODE_ENV === 'production' && !enabled) return false;\n if (enabled) return (await enabled()) === true;\n return true;\n}\n\nexport function createGraphRoute(options: NextGraphRouteOptions = {}) {\n const root = resolve(options.root ?? process.cwd());\n const graphFile = resolve(root, options.graphFile ?? '.dev-panel/component-graph.json');\n return {\n async GET() {\n if (!(await gate(options.enabled))) return json({ error: 'Not found' }, 404);\n try {\n return json(await readFile(graphFile, 'utf8'));\n } catch {\n return json({ error: 'graph-not-generated', hint: 'Run: npx dev-panel-graph' }, 404);\n }\n },\n };\n}\n\nexport function createOpenFileRoute(options: NextGraphRouteOptions = {}) {\n const root = resolve(options.root ?? process.cwd());\n return {\n async POST(req: Request) {\n if (!(await gate(options.enabled))) return json({ error: 'Not found' }, 404);\n let body: { file?: string; line?: number; column?: number; editor?: string };\n try {\n body = await req.json();\n } catch {\n return json({ error: 'invalid body' }, 400);\n }\n if (!body.file) return json({ error: 'file required' }, 400);\n const abs = isAbsolute(body.file) ? resolve(body.file) : resolve(root, body.file);\n const rel = relative(root, abs);\n if (rel.startsWith('..') || isAbsolute(rel)) return json({ error: 'path escapes root' }, 403);\n if (!existsSync(abs) || !statSync(abs).isFile()) return json({ error: 'not found' }, 404);\n try {\n const launch = (await import('launch-editor')).default;\n launch(`${abs}:${body.line ?? 1}:${body.column ?? 1}`, EDITOR_BIN[body.editor ?? 'auto']);\n return json({ ok: true });\n } catch (err) {\n return json({ error: err instanceof Error ? err.message : 'open failed' }, 500);\n }\n },\n };\n}\n\nexport { DEFAULT_GRAPH_ENDPOINT, DEFAULT_OPEN_ENDPOINT, serverOpenInEditor } from '../core/server-open';\n"]}
@@ -0,0 +1,72 @@
1
+ 'use strict';
2
+
3
+ var path = require('path');
4
+ var fs = require('fs');
5
+
6
+ // src/adapters/server.ts
7
+
8
+ // src/core/server-open.ts
9
+ var DEFAULT_GRAPH_ENDPOINT = "/dev-panel/graph";
10
+ var DEFAULT_OPEN_ENDPOINT = "/dev-panel/open-file";
11
+
12
+ // src/adapters/server.ts
13
+ var EDITOR_BIN = {
14
+ auto: void 0,
15
+ vscode: "code",
16
+ cursor: "cursor",
17
+ webstorm: "webstorm",
18
+ zed: "zed"
19
+ };
20
+ function send(res, status, body) {
21
+ res.statusCode = status;
22
+ res.setHeader("Content-Type", "application/json");
23
+ res.setHeader("Cache-Control", "no-store");
24
+ res.end(typeof body === "string" ? body : JSON.stringify(body));
25
+ }
26
+ async function readBody(req) {
27
+ const chunks = [];
28
+ for await (const c of req) chunks.push(c);
29
+ const raw = Buffer.concat(chunks).toString("utf8");
30
+ return raw ? JSON.parse(raw) : {};
31
+ }
32
+ function createDevPanelMiddleware(options = {}) {
33
+ const root = path.resolve(options.root ?? process.cwd());
34
+ const graphFile = path.resolve(root, options.graphFile ?? ".dev-panel/component-graph.json");
35
+ const graphEndpoint = options.graphEndpoint ?? DEFAULT_GRAPH_ENDPOINT;
36
+ const openEndpoint = options.openEndpoint ?? DEFAULT_OPEN_ENDPOINT;
37
+ return function devPanelMiddleware(req, res, next) {
38
+ const url = (req.url ?? "").split("?")[0];
39
+ if (req.method === "GET" && url === graphEndpoint) {
40
+ try {
41
+ send(res, 200, fs.readFileSync(graphFile, "utf8"));
42
+ } catch {
43
+ send(res, 404, { error: "graph-not-generated", hint: "Run: npx dev-panel-graph" });
44
+ }
45
+ return;
46
+ }
47
+ if (req.method === "POST" && url === openEndpoint) {
48
+ void (async () => {
49
+ try {
50
+ const body = await readBody(req);
51
+ if (!body.file) return send(res, 400, { error: "file required" });
52
+ const abs = path.isAbsolute(body.file) ? path.resolve(body.file) : path.resolve(root, body.file);
53
+ const rel = path.relative(root, abs);
54
+ if (rel.startsWith("..") || path.isAbsolute(rel)) return send(res, 403, { error: "path escapes root" });
55
+ if (!fs.existsSync(abs) || !fs.statSync(abs).isFile()) return send(res, 404, { error: "not found" });
56
+ const launch = (await import('launch-editor')).default;
57
+ const target = `${abs}:${body.line ?? 1}:${body.column ?? 1}`;
58
+ launch(target, EDITOR_BIN[body.editor ?? "auto"]);
59
+ return send(res, 200, { ok: true });
60
+ } catch (err) {
61
+ return send(res, 500, { error: err instanceof Error ? err.message : "open failed" });
62
+ }
63
+ })();
64
+ return;
65
+ }
66
+ next();
67
+ };
68
+ }
69
+
70
+ exports.createDevPanelMiddleware = createDevPanelMiddleware;
71
+ //# sourceMappingURL=server.cjs.map
72
+ //# sourceMappingURL=server.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/server-open.ts","../../src/adapters/server.ts"],"names":["resolve","readFileSync","isAbsolute","relative","existsSync","statSync"],"mappings":"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","file":"server.cjs","sourcesContent":["import type { OpenInEditor } from './types';\n\n/**\n * Default endpoint paths the adapters mount and the client talks to. No leading underscore —\n * Next.js App Router treats `_`-prefixed segments as private (non-routable), so this path works\n * uniformly across the Next, Vite, and generic-server adapters.\n */\nexport const DEFAULT_GRAPH_ENDPOINT = '/dev-panel/graph';\nexport const DEFAULT_OPEN_ENDPOINT = '/dev-panel/open-file';\n\n/**\n * A client `openInEditor` that POSTs to a server endpoint (mounted by an adapter), which opens\n * the file in the editor running the project via `launch-editor`. Falls back to copying the\n * path when the endpoint is unreachable. Pass to `<DevPanel openInEditor={serverOpenInEditor} />`.\n */\nexport function createServerOpenInEditor(endpoint = DEFAULT_OPEN_ENDPOINT): OpenInEditor {\n return async (loc, editor = 'auto') => {\n try {\n const res = await fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ file: loc.file, line: loc.line, column: loc.column, editor }),\n });\n if (res.ok) return true;\n } catch {\n /* fall through to copy */\n }\n if (typeof navigator !== 'undefined' && navigator.clipboard) {\n await navigator.clipboard\n .writeText(`${loc.file}${loc.line ? `:${loc.line}` : ''}`)\n .catch(() => undefined);\n }\n return false;\n };\n}\n\n/** Ready-to-use server opener pointed at the default endpoint. */\nexport const serverOpenInEditor: OpenInEditor = createServerOpenInEditor();\n","/**\n * Framework-agnostic dev middleware (connect/express/http style) that powers the Component\n * Graph Inspector's server features:\n * GET /__dev-panel/graph → serves the generated component-graph JSON\n * POST /__dev-panel/open-file → opens a file in the editor running the project (launch-editor)\n *\n * Dev-only by design — mount it only in your dev server. Opens are path-traversal-guarded to\n * the repo root. Used directly for CRA/Express/custom servers, and internally by the Vite plugin.\n */\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport { resolve, relative, isAbsolute } from 'node:path';\nimport { existsSync, statSync, readFileSync } from 'node:fs';\n\nimport { DEFAULT_GRAPH_ENDPOINT, DEFAULT_OPEN_ENDPOINT } from '../core/server-open';\n\nexport interface DevPanelServerOptions {\n /** Repo root (absolute). Default: process.cwd(). */\n root?: string;\n /** Path to the generated graph JSON, relative to root. Default: .dev-panel/component-graph.json */\n graphFile?: string;\n graphEndpoint?: string;\n openEndpoint?: string;\n}\n\nconst EDITOR_BIN: Record<string, string | undefined> = {\n auto: undefined,\n vscode: 'code',\n cursor: 'cursor',\n webstorm: 'webstorm',\n zed: 'zed',\n};\n\ntype Next = (err?: unknown) => void;\n\nfunction send(res: ServerResponse, status: number, body: unknown) {\n res.statusCode = status;\n res.setHeader('Content-Type', 'application/json');\n res.setHeader('Cache-Control', 'no-store');\n res.end(typeof body === 'string' ? body : JSON.stringify(body));\n}\n\nasync function readBody(req: IncomingMessage): Promise<unknown> {\n const chunks: Buffer[] = [];\n for await (const c of req) chunks.push(c as Buffer);\n const raw = Buffer.concat(chunks).toString('utf8');\n return raw ? JSON.parse(raw) : {};\n}\n\nexport function createDevPanelMiddleware(options: DevPanelServerOptions = {}) {\n const root = resolve(options.root ?? process.cwd());\n const graphFile = resolve(root, options.graphFile ?? '.dev-panel/component-graph.json');\n const graphEndpoint = options.graphEndpoint ?? DEFAULT_GRAPH_ENDPOINT;\n const openEndpoint = options.openEndpoint ?? DEFAULT_OPEN_ENDPOINT;\n\n return function devPanelMiddleware(req: IncomingMessage, res: ServerResponse, next: Next): void {\n const url = (req.url ?? '').split('?')[0];\n\n if (req.method === 'GET' && url === graphEndpoint) {\n try {\n send(res, 200, readFileSync(graphFile, 'utf8'));\n } catch {\n send(res, 404, { error: 'graph-not-generated', hint: 'Run: npx dev-panel-graph' });\n }\n return;\n }\n\n if (req.method === 'POST' && url === openEndpoint) {\n void (async () => {\n try {\n const body = (await readBody(req)) as {\n file?: string;\n line?: number;\n column?: number;\n editor?: string;\n };\n if (!body.file) return send(res, 400, { error: 'file required' });\n const abs = isAbsolute(body.file) ? resolve(body.file) : resolve(root, body.file);\n const rel = relative(root, abs);\n if (rel.startsWith('..') || isAbsolute(rel)) return send(res, 403, { error: 'path escapes root' });\n if (!existsSync(abs) || !statSync(abs).isFile()) return send(res, 404, { error: 'not found' });\n\n const launch = (await import('launch-editor')).default;\n const target = `${abs}:${body.line ?? 1}:${body.column ?? 1}`;\n launch(target, EDITOR_BIN[body.editor ?? 'auto']);\n return send(res, 200, { ok: true });\n } catch (err) {\n return send(res, 500, { error: err instanceof Error ? err.message : 'open failed' });\n }\n })();\n return;\n }\n\n next();\n };\n}\n"]}
@@ -0,0 +1,24 @@
1
+ import { IncomingMessage, ServerResponse } from 'node:http';
2
+
3
+ /**
4
+ * Framework-agnostic dev middleware (connect/express/http style) that powers the Component
5
+ * Graph Inspector's server features:
6
+ * GET /__dev-panel/graph → serves the generated component-graph JSON
7
+ * POST /__dev-panel/open-file → opens a file in the editor running the project (launch-editor)
8
+ *
9
+ * Dev-only by design — mount it only in your dev server. Opens are path-traversal-guarded to
10
+ * the repo root. Used directly for CRA/Express/custom servers, and internally by the Vite plugin.
11
+ */
12
+
13
+ interface DevPanelServerOptions {
14
+ /** Repo root (absolute). Default: process.cwd(). */
15
+ root?: string;
16
+ /** Path to the generated graph JSON, relative to root. Default: .dev-panel/component-graph.json */
17
+ graphFile?: string;
18
+ graphEndpoint?: string;
19
+ openEndpoint?: string;
20
+ }
21
+ type Next = (err?: unknown) => void;
22
+ declare function createDevPanelMiddleware(options?: DevPanelServerOptions): (req: IncomingMessage, res: ServerResponse, next: Next) => void;
23
+
24
+ export { type DevPanelServerOptions, createDevPanelMiddleware };
@@ -0,0 +1,24 @@
1
+ import { IncomingMessage, ServerResponse } from 'node:http';
2
+
3
+ /**
4
+ * Framework-agnostic dev middleware (connect/express/http style) that powers the Component
5
+ * Graph Inspector's server features:
6
+ * GET /__dev-panel/graph → serves the generated component-graph JSON
7
+ * POST /__dev-panel/open-file → opens a file in the editor running the project (launch-editor)
8
+ *
9
+ * Dev-only by design — mount it only in your dev server. Opens are path-traversal-guarded to
10
+ * the repo root. Used directly for CRA/Express/custom servers, and internally by the Vite plugin.
11
+ */
12
+
13
+ interface DevPanelServerOptions {
14
+ /** Repo root (absolute). Default: process.cwd(). */
15
+ root?: string;
16
+ /** Path to the generated graph JSON, relative to root. Default: .dev-panel/component-graph.json */
17
+ graphFile?: string;
18
+ graphEndpoint?: string;
19
+ openEndpoint?: string;
20
+ }
21
+ type Next = (err?: unknown) => void;
22
+ declare function createDevPanelMiddleware(options?: DevPanelServerOptions): (req: IncomingMessage, res: ServerResponse, next: Next) => void;
23
+
24
+ export { type DevPanelServerOptions, createDevPanelMiddleware };
@@ -0,0 +1,4 @@
1
+ export { createDevPanelMiddleware } from '../chunk-MAYMGQIM.js';
2
+ import '../chunk-2ZAPVMUL.js';
3
+ //# sourceMappingURL=server.js.map
4
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"server.js"}