serverreq 1.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 (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +194 -0
  3. package/dist/cli.d.ts +8 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +186 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/config.d.ts +21 -0
  8. package/dist/config.d.ts.map +1 -0
  9. package/dist/config.js +78 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/devtools/event-bus.d.ts +12 -0
  12. package/dist/devtools/event-bus.d.ts.map +1 -0
  13. package/dist/devtools/event-bus.js +47 -0
  14. package/dist/devtools/event-bus.js.map +1 -0
  15. package/dist/devtools/index.d.ts +25 -0
  16. package/dist/devtools/index.d.ts.map +1 -0
  17. package/dist/devtools/index.js +55 -0
  18. package/dist/devtools/index.js.map +1 -0
  19. package/dist/devtools/panel.d.ts +19 -0
  20. package/dist/devtools/panel.d.ts.map +1 -0
  21. package/dist/devtools/panel.js +308 -0
  22. package/dist/devtools/panel.js.map +1 -0
  23. package/dist/devtools/server.d.ts +39 -0
  24. package/dist/devtools/server.d.ts.map +1 -0
  25. package/dist/devtools/server.js +184 -0
  26. package/dist/devtools/server.js.map +1 -0
  27. package/dist/devtools/sse-handler.d.ts +5 -0
  28. package/dist/devtools/sse-handler.d.ts.map +1 -0
  29. package/dist/devtools/sse-handler.js +92 -0
  30. package/dist/devtools/sse-handler.js.map +1 -0
  31. package/dist/devtools/store.d.ts +31 -0
  32. package/dist/devtools/store.d.ts.map +1 -0
  33. package/dist/devtools/store.js +145 -0
  34. package/dist/devtools/store.js.map +1 -0
  35. package/dist/devtools/types.d.ts +45 -0
  36. package/dist/devtools/types.d.ts.map +1 -0
  37. package/dist/devtools/types.js +18 -0
  38. package/dist/devtools/types.js.map +1 -0
  39. package/dist/index.d.ts +18 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +25 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/interceptor.d.ts +14 -0
  44. package/dist/interceptor.d.ts.map +1 -0
  45. package/dist/interceptor.js +404 -0
  46. package/dist/interceptor.js.map +1 -0
  47. package/dist/logger.d.ts +23 -0
  48. package/dist/logger.d.ts.map +1 -0
  49. package/dist/logger.js +123 -0
  50. package/dist/logger.js.map +1 -0
  51. package/dist/nextjs.d.ts +43 -0
  52. package/dist/nextjs.d.ts.map +1 -0
  53. package/dist/nextjs.js +55 -0
  54. package/dist/nextjs.js.map +1 -0
  55. package/dist/preload.d.ts +2 -0
  56. package/dist/preload.d.ts.map +1 -0
  57. package/dist/preload.js +24 -0
  58. package/dist/preload.js.map +1 -0
  59. package/dist/test-interceptor.d.ts +5 -0
  60. package/dist/test-interceptor.d.ts.map +1 -0
  61. package/dist/test-interceptor.js +113 -0
  62. package/dist/test-interceptor.js.map +1 -0
  63. package/package.json +94 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Antigravity
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,194 @@
1
+ # ServerReq
2
+
3
+ Production-ready DevTools for intercepting and visualizing HTTP/HTTPS requests in Node.js applications.
4
+
5
+ ![ServerReq DevTools](https://via.placeholder.com/800x400/667eea/ffffff?text=ServerReq+DevTools)
6
+
7
+ ## Features
8
+
9
+ - 🔍 **HTTP/HTTPS Interception** - Captures all `fetch`, `http.request`, and `https.request` calls
10
+ - ⚡ **Real-time DevTools** - Beautiful floating panel similar to React Query DevTools
11
+ - 🔒 **Secret Masking** - Automatically masks sensitive headers and body fields
12
+ - 🎯 **Next.js Integration** - First-class support for Next.js App Router
13
+ - 🚀 **Zero Config CLI** - Just prefix your command with `serverreq`
14
+ - 🔌 **Socket.IO Transport** - Reliable real-time communication
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install --save-dev serverreq
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ### Option 1: Next.js App Router (Recommended)
25
+
26
+ **Step 1:** Enable the instrumentation hook in `next.config.ts`:
27
+
28
+ ```ts
29
+ // next.config.ts
30
+ import type { NextConfig } from "next";
31
+
32
+ const nextConfig: NextConfig = {
33
+ experimental: {
34
+ instrumentationHook: true,
35
+ },
36
+ };
37
+
38
+ export default nextConfig;
39
+ ```
40
+
41
+ **Step 2:** Create an instrumentation file:
42
+
43
+ ```ts
44
+ // instrumentation.ts (in your project root)
45
+ export async function register() {
46
+ if (process.env.NODE_ENV === "development") {
47
+ const { setupDevTools } = await import("serverreq/nextjs");
48
+ await setupDevTools();
49
+ }
50
+ }
51
+ ```
52
+
53
+ **Step 3:** Create the SSE API route for DevTools communication:
54
+
55
+ ```ts
56
+ // app/api/devtools/stream/route.ts
57
+ import { createSSEHandler } from "serverreq/nextjs";
58
+
59
+ export const GET = createSSEHandler();
60
+ ```
61
+
62
+ **Step 4:** Add the DevTools component to your layout:
63
+
64
+ ```tsx
65
+ // app/layout.tsx
66
+ import { ServerReqDevtools } from "serverreq/devtools";
67
+
68
+ export default function RootLayout({
69
+ children,
70
+ }: {
71
+ children: React.ReactNode;
72
+ }) {
73
+ return (
74
+ <html lang="en">
75
+ <body>
76
+ {children}
77
+ {process.env.NODE_ENV === "development" && <ServerReqDevtools />}
78
+ </body>
79
+ </html>
80
+ );
81
+ }
82
+ ```
83
+
84
+ That's it! The DevTools panel will appear as a floating button in the bottom-right corner.
85
+
86
+ ### Option 2: CLI Usage
87
+
88
+ For any Node.js application, use the CLI:
89
+
90
+ ```bash
91
+ # Run any Node.js command with request interception
92
+ npx serverreq -- node server.js
93
+
94
+ # Next.js development
95
+ npx serverreq -- next dev
96
+
97
+ # With options
98
+ npx serverreq --format=json --exclude="localhost:3000" -- npm start
99
+ ```
100
+
101
+ ### Option 3: Programmatic Usage
102
+
103
+ ```ts
104
+ import { install, configure } from "serverreq";
105
+
106
+ // Configure options
107
+ configure({
108
+ enabled: true,
109
+ maskSecrets: true,
110
+ excludeUrls: [/localhost:3000/],
111
+ });
112
+
113
+ // Install interceptors
114
+ install();
115
+ ```
116
+
117
+ ## DevTools Component Props
118
+
119
+ ```tsx
120
+ <ServerReqDevtools
121
+ socketUrl="http://localhost:4000" // Custom socket URL (auto-detected by default)
122
+ defaultOpen={false} // Start with panel open
123
+ position="bottom-right" // or "bottom-left"
124
+ />
125
+ ```
126
+
127
+ ## How It Works
128
+
129
+ ### Architecture
130
+
131
+ ```
132
+ ┌─────────────────────────────────────────────────────────────┐
133
+ │ Next.js Server │
134
+ │ ┌──────────────────────────────────────────────────────┐ │
135
+ │ │ HTTP Interceptor (fetch, http, https) │ │
136
+ │ │ │ │ │
137
+ │ │ ▼ │ │
138
+ │ │ Event Bus (in-memory) │ │
139
+ │ │ │ │ │
140
+ │ │ ▼ │ │
141
+ │ │ Socket.IO Server (port 4000) ◄────────────────────┐│ │
142
+ │ └──────────────────────────────────│───────────────────┘ │
143
+ └──────────────────────────────────────│──────────────────────┘
144
+
145
+ ┌──────────────────────┘
146
+ │ WebSocket
147
+
148
+ ┌─────────────────────────────────────────────────────────────┐
149
+ │ Browser │
150
+ │ ┌──────────────────────────────────────────────────────┐ │
151
+ │ │ Socket.IO Client │ │
152
+ │ │ │ │ │
153
+ │ │ ▼ │ │
154
+ │ │ DevToolsStore (useSyncExternalStore) │ │
155
+ │ │ │ │ │
156
+ │ │ ▼ │ │
157
+ │ │ ServerReqDevtools (React Component) │ │
158
+ │ └──────────────────────────────────────────────────────┘ │
159
+ └─────────────────────────────────────────────────────────────┘
160
+ ```
161
+
162
+ ### Key Design Decisions
163
+
164
+ 1. **Standalone Socket.IO Server** - Runs on port 4000 to avoid conflicts with Next.js Turbopack
165
+ 2. **useSyncExternalStore** - React 18 pattern for external state management
166
+ 3. **Singleton Store** - Survives HMR and component remounts
167
+ 4. **Event Bus** - Decouples interceptor from transport layer
168
+ 5. **Production Safety** - Automatically disabled when `NODE_ENV=production`
169
+
170
+ ## CLI Options
171
+
172
+ | Option | Description |
173
+ | ------------------------- | --------------------------------------- |
174
+ | `--format=<json\|pretty>` | Output format (default: pretty for TTY) |
175
+ | `--no-mask` | Disable secret masking |
176
+ | `--exclude=<pattern>` | URL pattern to exclude (regex) |
177
+ | `--help, -h` | Show help |
178
+
179
+ ## Configuration
180
+
181
+ ```ts
182
+ interface ServerReqConfig {
183
+ enabled: boolean; // Enable/disable (default: true in dev)
184
+ format: "json" | "pretty"; // Output format
185
+ maskSecrets: boolean; // Mask sensitive data (default: true)
186
+ secretPatterns: RegExp[]; // Patterns to mask in body
187
+ excludeUrls: RegExp[]; // URL patterns to exclude
188
+ maxBodyLength: number; // Max body preview length (default: 1024)
189
+ }
190
+ ```
191
+
192
+ ## License
193
+
194
+ MIT
package/dist/cli.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Production-ready CLI wrapper for serverreq
4
+ * Intercepts HTTP/HTTPS requests by injecting a preload script via NODE_OPTIONS.
5
+ * Works cross-platform (Windows, macOS, Linux).
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;GAIG"}
package/dist/cli.js ADDED
@@ -0,0 +1,186 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * Production-ready CLI wrapper for serverreq
5
+ * Intercepts HTTP/HTTPS requests by injecting a preload script via NODE_OPTIONS.
6
+ * Works cross-platform (Windows, macOS, Linux).
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ const child_process_1 = require("child_process");
43
+ const path = __importStar(require("path"));
44
+ /**
45
+ * Parses CLI arguments into options and the command to execute.
46
+ * Supports format, no-mask, exclude, and help flags.
47
+ */
48
+ function parseArgs(args) {
49
+ const options = {
50
+ mask: true,
51
+ exclude: [],
52
+ help: false,
53
+ };
54
+ const command = [];
55
+ let foundSeparator = false;
56
+ for (let i = 0; i < args.length; i++) {
57
+ const arg = args[i];
58
+ if (foundSeparator) {
59
+ command.push(arg);
60
+ continue;
61
+ }
62
+ if (arg === '--') {
63
+ foundSeparator = true;
64
+ continue;
65
+ }
66
+ if (arg === '--help' || arg === '-h') {
67
+ options.help = true;
68
+ }
69
+ else if (arg === '--no-mask') {
70
+ options.mask = false;
71
+ }
72
+ else if (arg.startsWith('--format=')) {
73
+ const format = arg.split('=')[1];
74
+ if (format === 'json' || format === 'pretty') {
75
+ options.format = format;
76
+ }
77
+ }
78
+ else if (arg.startsWith('--exclude=')) {
79
+ const pattern = arg.split('=')[1];
80
+ if (pattern) {
81
+ options.exclude.push(pattern);
82
+ }
83
+ }
84
+ else {
85
+ // If it's not a known flag and we haven't hit --, assume it's the start of the command
86
+ command.push(arg);
87
+ foundSeparator = true;
88
+ }
89
+ }
90
+ return { options, command };
91
+ }
92
+ /**
93
+ * Displays usage information.
94
+ */
95
+ function showHelp() {
96
+ console.log(`
97
+ serverreq - Production HTTP/HTTPS request interceptor CLI
98
+
99
+ USAGE:
100
+ serverreq [options] -- <command>
101
+ serverreq <command>
102
+
103
+ OPTIONS:
104
+ --format=<json|pretty> Output format (default: pretty for TTY, json otherwise)
105
+ --no-mask Disable secret masking
106
+ --exclude=<pattern> URL pattern to exclude (supports regex strings)
107
+ --help, -h Show this help
108
+
109
+ EXAMPLES:
110
+ serverreq -- node server.js
111
+ serverreq next dev
112
+ serverreq --format=json -- npm start
113
+ serverreq --exclude="localhost:3000" -- node app.js
114
+ `);
115
+ }
116
+ /**
117
+ * Main execution logic.
118
+ * Resolves paths, sets environment variables, and spawns the child process.
119
+ */
120
+ function main() {
121
+ const { options, command } = parseArgs(process.argv.slice(2));
122
+ if (options.help || (command.length === 0 && process.argv.length === 2)) {
123
+ showHelp();
124
+ process.exit(0);
125
+ }
126
+ if (command.length === 0) {
127
+ console.error('Error: No command specified.');
128
+ showHelp();
129
+ process.exit(1);
130
+ }
131
+ // 1. Resolve the path to the preload script.
132
+ // This assumes the file is located relative to the compiled CLI in dist/
133
+ // IMPORTANT: Convert Windows backslashes to forward slashes to prevent them from being
134
+ // interpreted as escape characters when the path is embedded in NODE_OPTIONS.
135
+ // Node.js on Windows handles forward slashes correctly.
136
+ const preloadPath = path.resolve(__dirname, 'preload.js').replace(/\\/g, '/');
137
+ // 2. Prepare the child process configuration.
138
+ const config = {
139
+ enabled: true,
140
+ maskSecrets: options.mask,
141
+ format: options.format,
142
+ excludeUrls: options.exclude,
143
+ };
144
+ // 3. Prepare environment variables.
145
+ const env = { ...process.env };
146
+ // Serialize config for the preload script to consume.
147
+ env.SERVERREQ_CONFIG = JSON.stringify(config);
148
+ // Inject the preload script into NODE_OPTIONS.
149
+ // We use quotes around the path to handle spaces in directory names.
150
+ const existingNodeOptions = env.NODE_OPTIONS || '';
151
+ const preloadEntry = `--require "${preloadPath}"`;
152
+ env.NODE_OPTIONS = `${existingNodeOptions} ${preloadEntry}`.trim();
153
+ // 4. Spawn the child process.
154
+ const [cmd, ...args] = command;
155
+ const spawnOptions = {
156
+ env,
157
+ stdio: 'inherit',
158
+ // Use shell on Windows for better command compatibility (e.g., npm)
159
+ shell: process.platform === 'win32',
160
+ };
161
+ const child = (0, child_process_1.spawn)(cmd, args, spawnOptions);
162
+ // 5. Handle process lifecycle events.
163
+ child.on('error', (err) => {
164
+ console.error(`[serverreq] Failed to start child process: ${err.message}`);
165
+ process.exit(1);
166
+ });
167
+ child.on('exit', (code, signal) => {
168
+ if (signal) {
169
+ process.kill(process.pid, signal);
170
+ }
171
+ else {
172
+ process.exit(code ?? 0);
173
+ }
174
+ });
175
+ // Relay signals to the child process.
176
+ const signals = ['SIGINT', 'SIGTERM', 'SIGHUP'];
177
+ signals.forEach((sig) => {
178
+ process.on(sig, () => {
179
+ if (child.pid) {
180
+ child.kill(sig);
181
+ }
182
+ });
183
+ });
184
+ }
185
+ main();
186
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ export interface ServerReqConfig {
2
+ enabled: boolean;
3
+ format: 'json' | 'pretty';
4
+ maskSecrets: boolean;
5
+ secretPatterns: RegExp[];
6
+ excludeUrls: RegExp[];
7
+ maxBodyLength: number;
8
+ }
9
+ export declare class Config {
10
+ private static instance;
11
+ private config;
12
+ private constructor();
13
+ static getInstance(): Config;
14
+ get(): ServerReqConfig;
15
+ merge(partial: Partial<ServerReqConfig>): void;
16
+ shouldLogRequest(url: string): boolean;
17
+ isSecretHeader(headerName: string): boolean;
18
+ isSecretKey(key: string): boolean;
19
+ }
20
+ export declare const getConfig: () => Config;
21
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC1B,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;CACzB;AAyBD,qBAAa,MAAM;IACf,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAS;IAChC,OAAO,CAAC,MAAM,CAAkB;IAEhC,OAAO;IAwBP,MAAM,CAAC,WAAW,IAAI,MAAM;IAO5B,GAAG,IAAI,eAAe;IAItB,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,IAAI;IAI9C,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAKtC,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAK3C,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;CAGpC;AAED,eAAO,MAAM,SAAS,cAA6B,CAAC"}
package/dist/config.js ADDED
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getConfig = exports.Config = void 0;
4
+ const DEFAULT_SECRET_HEADERS = [
5
+ 'authorization',
6
+ 'x-api-key',
7
+ 'api-key',
8
+ 'apikey',
9
+ 'cookie',
10
+ 'set-cookie',
11
+ 'x-auth-token',
12
+ 'x-access-token',
13
+ 'x-csrf-token',
14
+ ];
15
+ const DEFAULT_SECRET_BODY_PATTERNS = [
16
+ /password/i,
17
+ /token/i,
18
+ /secret/i,
19
+ /apikey/i,
20
+ /api_key/i,
21
+ /private_key/i,
22
+ /access_key/i,
23
+ /client_secret/i,
24
+ ];
25
+ class Config {
26
+ static instance;
27
+ config;
28
+ constructor() {
29
+ const isProduction = process.env.NODE_ENV === 'production';
30
+ const isTTY = process.stderr.isTTY ?? false;
31
+ this.config = {
32
+ enabled: !isProduction,
33
+ format: isTTY ? 'pretty' : 'json',
34
+ maskSecrets: true,
35
+ secretPatterns: [...DEFAULT_SECRET_BODY_PATTERNS],
36
+ excludeUrls: [],
37
+ maxBodyLength: 1024, // 1KB max body preview
38
+ };
39
+ // Load from environment variable if present
40
+ if (process.env.SERVERREQ_CONFIG) {
41
+ try {
42
+ const envConfig = JSON.parse(process.env.SERVERREQ_CONFIG);
43
+ this.merge(envConfig);
44
+ }
45
+ catch (error) {
46
+ console.error('[serverreq] Failed to parse SERVERREQ_CONFIG:', error);
47
+ }
48
+ }
49
+ }
50
+ static getInstance() {
51
+ if (!Config.instance) {
52
+ Config.instance = new Config();
53
+ }
54
+ return Config.instance;
55
+ }
56
+ get() {
57
+ return { ...this.config };
58
+ }
59
+ merge(partial) {
60
+ this.config = { ...this.config, ...partial };
61
+ }
62
+ shouldLogRequest(url) {
63
+ if (!this.config.enabled)
64
+ return false;
65
+ return !this.config.excludeUrls.some((pattern) => pattern.test(url));
66
+ }
67
+ isSecretHeader(headerName) {
68
+ const normalized = headerName.toLowerCase();
69
+ return DEFAULT_SECRET_HEADERS.includes(normalized);
70
+ }
71
+ isSecretKey(key) {
72
+ return this.config.secretPatterns.some((pattern) => pattern.test(key));
73
+ }
74
+ }
75
+ exports.Config = Config;
76
+ const getConfig = () => Config.getInstance();
77
+ exports.getConfig = getConfig;
78
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;;AASA,MAAM,sBAAsB,GAAG;IAC3B,eAAe;IACf,WAAW;IACX,SAAS;IACT,QAAQ;IACR,QAAQ;IACR,YAAY;IACZ,cAAc;IACd,gBAAgB;IAChB,cAAc;CACjB,CAAC;AAEF,MAAM,4BAA4B,GAAG;IACjC,WAAW;IACX,QAAQ;IACR,SAAS;IACT,SAAS;IACT,UAAU;IACV,cAAc;IACd,aAAa;IACb,gBAAgB;CACnB,CAAC;AAEF,MAAa,MAAM;IACP,MAAM,CAAC,QAAQ,CAAS;IACxB,MAAM,CAAkB;IAEhC;QACI,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAC;QAC3D,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,KAAK,CAAC;QAE5C,IAAI,CAAC,MAAM,GAAG;YACV,OAAO,EAAE,CAAC,YAAY;YACtB,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM;YACjC,WAAW,EAAE,IAAI;YACjB,cAAc,EAAE,CAAC,GAAG,4BAA4B,CAAC;YACjD,WAAW,EAAE,EAAE;YACf,aAAa,EAAE,IAAI,EAAE,uBAAuB;SAC/C,CAAC;QAEF,4CAA4C;QAC5C,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;YAC/B,IAAI,CAAC;gBACD,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;gBAC3D,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YAC1B,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,+CAA+C,EAAE,KAAK,CAAC,CAAC;YAC1E,CAAC;QACL,CAAC;IACL,CAAC;IAED,MAAM,CAAC,WAAW;QACd,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YACnB,MAAM,CAAC,QAAQ,GAAG,IAAI,MAAM,EAAE,CAAC;QACnC,CAAC;QACD,OAAO,MAAM,CAAC,QAAQ,CAAC;IAC3B,CAAC;IAED,GAAG;QACC,OAAO,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,OAAiC;QACnC,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,OAAO,EAAE,CAAC;IACjD,CAAC;IAED,gBAAgB,CAAC,GAAW;QACxB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,OAAO,KAAK,CAAC;QACvC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACzE,CAAC;IAED,cAAc,CAAC,UAAkB;QAC7B,MAAM,UAAU,GAAG,UAAU,CAAC,WAAW,EAAE,CAAC;QAC5C,OAAO,sBAAsB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IACvD,CAAC;IAED,WAAW,CAAC,GAAW;QACnB,OAAO,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3E,CAAC;CACJ;AAxDD,wBAwDC;AAEM,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;AAAvC,QAAA,SAAS,aAA8B"}
@@ -0,0 +1,12 @@
1
+ import { EventEmitter } from 'events';
2
+ import { NetworkEvent } from './types';
3
+ export declare class GlobalEventBus extends EventEmitter {
4
+ private history;
5
+ private maxHistory;
6
+ constructor();
7
+ emit(event: string | symbol, ...args: any[]): boolean;
8
+ getHistory(): NetworkEvent[];
9
+ clearHistory(): void;
10
+ }
11
+ export declare const eventBus: GlobalEventBus;
12
+ //# sourceMappingURL=event-bus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-bus.d.ts","sourceRoot":"","sources":["../../src/devtools/event-bus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAKvC,qBAAa,cAAe,SAAQ,YAAY;IAC5C,OAAO,CAAC,OAAO,CAAsB;IACrC,OAAO,CAAC,UAAU,CAAQ;;IAQ1B,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO;IAerD,UAAU,IAAI,YAAY,EAAE;IAI5B,YAAY,IAAI,IAAI;CAIvB;AAUD,eAAO,MAAM,QAAQ,gBAAiB,CAAC"}
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.eventBus = exports.GlobalEventBus = void 0;
4
+ const events_1 = require("events");
5
+ // Use a global key to ensure singleton across module reloads
6
+ const GLOBAL_BUS_KEY = '__SERVER_REQ_EVENT_BUS__';
7
+ class GlobalEventBus extends events_1.EventEmitter {
8
+ history = [];
9
+ maxHistory = 1000;
10
+ constructor() {
11
+ super();
12
+ this.setMaxListeners(50);
13
+ console.log('[serverreq GlobalBus] Initialized new GlobalEventBus');
14
+ }
15
+ emit(event, ...args) {
16
+ if (event === 'network:event') {
17
+ const data = args[0];
18
+ console.log(`[serverreq GlobalBus] Emitting event: ${data.method} ${data.url}`);
19
+ // Add to start (newest first)
20
+ this.history.unshift(data);
21
+ if (this.history.length > this.maxHistory) {
22
+ this.history.pop();
23
+ }
24
+ }
25
+ else if (event === 'network:clear') {
26
+ this.history = [];
27
+ }
28
+ return super.emit(event, ...args);
29
+ }
30
+ getHistory() {
31
+ return [...this.history];
32
+ }
33
+ clearHistory() {
34
+ this.history = [];
35
+ this.emit('network:clear');
36
+ }
37
+ }
38
+ exports.GlobalEventBus = GlobalEventBus;
39
+ function getGlobalBus() {
40
+ const globalStore = globalThis;
41
+ if (!globalStore[GLOBAL_BUS_KEY]) {
42
+ globalStore[GLOBAL_BUS_KEY] = new GlobalEventBus();
43
+ }
44
+ return globalStore[GLOBAL_BUS_KEY];
45
+ }
46
+ exports.eventBus = getGlobalBus();
47
+ //# sourceMappingURL=event-bus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-bus.js","sourceRoot":"","sources":["../../src/devtools/event-bus.ts"],"names":[],"mappings":";;;AAAA,mCAAsC;AAGtC,6DAA6D;AAC7D,MAAM,cAAc,GAAG,0BAA0B,CAAC;AAElD,MAAa,cAAe,SAAQ,qBAAY;IACpC,OAAO,GAAmB,EAAE,CAAC;IAC7B,UAAU,GAAG,IAAI,CAAC;IAE1B;QACI,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,sDAAsD,CAAC,CAAC;IACxE,CAAC;IAED,IAAI,CAAC,KAAsB,EAAE,GAAG,IAAW;QACvC,IAAI,KAAK,KAAK,eAAe,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAiB,CAAC;YACrC,OAAO,CAAC,GAAG,CAAC,yCAAyC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;YAChF,8BAA8B;YAC9B,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAC3B,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;gBACxC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;YACvB,CAAC;QACL,CAAC;aAAM,IAAI,KAAK,KAAK,eAAe,EAAE,CAAC;YACnC,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;QACtB,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC;IACtC,CAAC;IAED,UAAU;QACN,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAED,YAAY;QACR,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;QAClB,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAC/B,CAAC;CACJ;AAjCD,wCAiCC;AAED,SAAS,YAAY;IACjB,MAAM,WAAW,GAAG,UAAiB,CAAC;IACtC,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,EAAE,CAAC;QAC/B,WAAW,CAAC,cAAc,CAAC,GAAG,IAAI,cAAc,EAAE,CAAC;IACvD,CAAC;IACD,OAAO,WAAW,CAAC,cAAc,CAAC,CAAC;AACvC,CAAC;AAEY,QAAA,QAAQ,GAAG,YAAY,EAAE,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * serverreq/devtools - Client-side DevTools exports
3
+ *
4
+ * Usage in Next.js App Router layout.tsx:
5
+ *
6
+ * ```tsx
7
+ * import { ServerReqDevtools } from 'serverreq/devtools';
8
+ *
9
+ * export default function RootLayout({ children }) {
10
+ * return (
11
+ * <html>
12
+ * <body>
13
+ * {children}
14
+ * {process.env.NODE_ENV === 'development' && <ServerReqDevtools />}
15
+ * </body>
16
+ * </html>
17
+ * );
18
+ * }
19
+ * ```
20
+ */
21
+ export { ServerReqDevtools, default } from './panel';
22
+ export type { ServerReqDevtoolsProps } from './panel';
23
+ export { store } from './store';
24
+ export type { NetworkEvent, DevToolsConfig } from './types';
25
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/devtools/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAKH,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACrD,YAAY,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAGtD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAGhC,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC"}