dev3000 0.0.49 → 0.0.51
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/README.md +55 -6
- package/dist/cdp-monitor.d.ts.map +1 -1
- package/dist/cdp-monitor.js +54 -48
- package/dist/cdp-monitor.js.map +1 -1
- package/dist/cli.js +39 -33
- package/dist/cli.js.map +1 -1
- package/dist/dev-environment.d.ts +2 -0
- package/dist/dev-environment.d.ts.map +1 -1
- package/dist/dev-environment.js +212 -181
- package/dist/dev-environment.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/mcp-server/app/api/config/route.ts +7 -7
- package/mcp-server/app/api/logs/append/route.ts +59 -51
- package/mcp-server/app/api/logs/head/route.ts +22 -22
- package/mcp-server/app/api/logs/list/route.ts +39 -42
- package/mcp-server/app/api/logs/rotate/route.ts +28 -38
- package/mcp-server/app/api/logs/stream/route.ts +35 -35
- package/mcp-server/app/api/logs/tail/route.ts +22 -22
- package/mcp-server/app/api/mcp/[transport]/route.ts +189 -188
- package/mcp-server/app/api/replay/route.ts +217 -202
- package/mcp-server/app/layout.tsx +9 -8
- package/mcp-server/app/logs/LogsClient.test.ts +123 -99
- package/mcp-server/app/logs/LogsClient.tsx +724 -562
- package/mcp-server/app/logs/page.tsx +71 -72
- package/mcp-server/app/logs/utils.ts +99 -28
- package/mcp-server/app/page.tsx +10 -14
- package/mcp-server/app/replay/ReplayClient.tsx +120 -119
- package/mcp-server/app/replay/page.tsx +3 -3
- package/mcp-server/next.config.ts +2 -0
- package/mcp-server/package.json +5 -2
- package/mcp-server/pnpm-lock.yaml +37 -5
- package/mcp-server/tsconfig.json +4 -17
- package/package.json +16 -13
|
@@ -1,32 +1,31 @@
|
|
|
1
|
-
import { describe,
|
|
2
|
-
import { parseLogEntries } from
|
|
3
|
-
import { LogEntry } from '../../types';
|
|
1
|
+
import { describe, expect, it } from "vitest"
|
|
2
|
+
import { parseLogEntries } from "./utils"
|
|
4
3
|
|
|
5
|
-
describe(
|
|
6
|
-
it(
|
|
4
|
+
describe("parseLogEntries", () => {
|
|
5
|
+
it("should parse single-line log entries correctly", () => {
|
|
7
6
|
const logContent = `[2025-09-03T03:57:39.357Z] [SERVER] myapp:dev: Cache hits: 5, misses: 2
|
|
8
|
-
[2025-09-03T03:57:40.123Z] [BROWSER] [CONSOLE LOG] Component rendered successfully
|
|
7
|
+
[2025-09-03T03:57:40.123Z] [BROWSER] [CONSOLE LOG] Component rendered successfully`
|
|
9
8
|
|
|
10
|
-
const entries = parseLogEntries(logContent)
|
|
9
|
+
const entries = parseLogEntries(logContent)
|
|
10
|
+
|
|
11
|
+
expect(entries).toHaveLength(2)
|
|
11
12
|
|
|
12
|
-
expect(entries).toHaveLength(2);
|
|
13
|
-
|
|
14
13
|
expect(entries[0]).toEqual({
|
|
15
|
-
timestamp:
|
|
16
|
-
source:
|
|
17
|
-
message:
|
|
18
|
-
original:
|
|
19
|
-
})
|
|
14
|
+
timestamp: "2025-09-03T03:57:39.357Z",
|
|
15
|
+
source: "SERVER",
|
|
16
|
+
message: "myapp:dev: Cache hits: 5, misses: 2",
|
|
17
|
+
original: "[2025-09-03T03:57:39.357Z] [SERVER] myapp:dev: Cache hits: 5, misses: 2"
|
|
18
|
+
})
|
|
20
19
|
|
|
21
20
|
expect(entries[1]).toEqual({
|
|
22
|
-
timestamp:
|
|
23
|
-
source:
|
|
24
|
-
message:
|
|
25
|
-
original:
|
|
26
|
-
})
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
it(
|
|
21
|
+
timestamp: "2025-09-03T03:57:40.123Z",
|
|
22
|
+
source: "BROWSER",
|
|
23
|
+
message: "[CONSOLE LOG] Component rendered successfully",
|
|
24
|
+
original: "[2025-09-03T03:57:40.123Z] [BROWSER] [CONSOLE LOG] Component rendered successfully"
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it("should group multi-line log entries by timestamp", () => {
|
|
30
29
|
const logContent = `[2025-09-03T03:57:39.614Z] [SERVER] myapp:dev: api_request[warn] Server error: {
|
|
31
30
|
myapp:dev: request: '/api/v1/users/search',
|
|
32
31
|
myapp:dev: reason: Error [AuthError]: Missing authentication token
|
|
@@ -49,42 +48,44 @@ myapp:dev: },
|
|
|
49
48
|
myapp:dev: missingAuth: true
|
|
50
49
|
myapp:dev: }
|
|
51
50
|
myapp:dev: }
|
|
52
|
-
[2025-09-03T03:57:40.778Z] [SERVER] myapp:dev: Cache[info] Prefetch unused: [ '"/api/v2/users?page=1"' ]
|
|
51
|
+
[2025-09-03T03:57:40.778Z] [SERVER] myapp:dev: Cache[info] Prefetch unused: [ '"/api/v2/users?page=1"' ]`
|
|
52
|
+
|
|
53
|
+
const entries = parseLogEntries(logContent)
|
|
53
54
|
|
|
54
|
-
|
|
55
|
+
expect(entries).toHaveLength(2)
|
|
55
56
|
|
|
56
|
-
expect(entries).toHaveLength(2);
|
|
57
|
-
|
|
58
57
|
// First entry should contain the entire multi-line error
|
|
59
|
-
expect(entries[0].timestamp).toBe(
|
|
60
|
-
expect(entries[0].source).toBe(
|
|
61
|
-
expect(entries[0].message).toContain(
|
|
62
|
-
expect(entries[0].message).toContain(
|
|
63
|
-
expect(entries[0].message).toContain(
|
|
64
|
-
expect(entries[0].message).toContain(
|
|
65
|
-
|
|
58
|
+
expect(entries[0].timestamp).toBe("2025-09-03T03:57:39.614Z")
|
|
59
|
+
expect(entries[0].source).toBe("SERVER")
|
|
60
|
+
expect(entries[0].message).toContain("api_request[warn] Server error: {")
|
|
61
|
+
expect(entries[0].message).toContain("Missing authentication token")
|
|
62
|
+
expect(entries[0].message).toContain("missingAuth: true")
|
|
63
|
+
expect(entries[0].message).toContain("}")
|
|
64
|
+
|
|
66
65
|
// Second entry should be the single-line cache message
|
|
67
|
-
expect(entries[1].timestamp).toBe(
|
|
68
|
-
expect(entries[1].source).toBe(
|
|
69
|
-
expect(entries[1].message).toBe(
|
|
70
|
-
})
|
|
66
|
+
expect(entries[1].timestamp).toBe("2025-09-03T03:57:40.778Z")
|
|
67
|
+
expect(entries[1].source).toBe("SERVER")
|
|
68
|
+
expect(entries[1].message).toBe("myapp:dev: Cache[info] Prefetch unused: [ '\"/api/v2/users?page=1\"' ]")
|
|
69
|
+
})
|
|
71
70
|
|
|
72
|
-
it(
|
|
71
|
+
it("should handle screenshot entries correctly", () => {
|
|
73
72
|
const logContent = `[2025-09-03T02:14:27.444Z] [BROWSER] [SCREENSHOT] http://localhost:3684/screenshots/2025-09-03T02-14-27-329Z-initial-load.png
|
|
74
|
-
[2025-09-03T02:14:27.444Z] [BROWSER] 📄 New page: http://localhost:3000
|
|
73
|
+
[2025-09-03T02:14:27.444Z] [BROWSER] 📄 New page: http://localhost:3000/`
|
|
75
74
|
|
|
76
|
-
const entries = parseLogEntries(logContent)
|
|
75
|
+
const entries = parseLogEntries(logContent)
|
|
77
76
|
|
|
78
|
-
expect(entries).toHaveLength(2)
|
|
79
|
-
|
|
80
|
-
expect(entries[0].screenshot).toBe('http://localhost:3684/screenshots/2025-09-03T02-14-27-329Z-initial-load.png');
|
|
81
|
-
expect(entries[0].message).toBe('[SCREENSHOT] http://localhost:3684/screenshots/2025-09-03T02-14-27-329Z-initial-load.png');
|
|
82
|
-
|
|
83
|
-
expect(entries[1].screenshot).toBeUndefined();
|
|
84
|
-
expect(entries[1].message).toBe('📄 New page: http://localhost:3000/');
|
|
85
|
-
});
|
|
77
|
+
expect(entries).toHaveLength(2)
|
|
86
78
|
|
|
87
|
-
|
|
79
|
+
expect(entries[0].screenshot).toBe("http://localhost:3684/screenshots/2025-09-03T02-14-27-329Z-initial-load.png")
|
|
80
|
+
expect(entries[0].message).toBe(
|
|
81
|
+
"[SCREENSHOT] http://localhost:3684/screenshots/2025-09-03T02-14-27-329Z-initial-load.png"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
expect(entries[1].screenshot).toBeUndefined()
|
|
85
|
+
expect(entries[1].message).toBe("📄 New page: http://localhost:3000/")
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it("should handle complex stack traces and nested objects", () => {
|
|
88
89
|
const logContent = `[2025-09-03T03:57:39.098Z] [SERVER] ❌ Database connection failed: DatabaseError: Connection timeout
|
|
89
90
|
myapp:dev: at ConnectionPool.connect (/app/src/database/pool.ts:142:15)
|
|
90
91
|
myapp:dev: at DatabaseService.initialize (/app/src/services/database.ts:87:31)
|
|
@@ -108,53 +109,53 @@ myapp:dev: retries: 3
|
|
|
108
109
|
myapp:dev: },
|
|
109
110
|
myapp:dev: connectionId: 'conn_xyz789abc'
|
|
110
111
|
myapp:dev: }
|
|
111
|
-
[2025-09-03T03:57:40.200Z] [BROWSER] [CONSOLE ERROR] Uncaught TypeError: Cannot read properties of null
|
|
112
|
+
[2025-09-03T03:57:40.200Z] [BROWSER] [CONSOLE ERROR] Uncaught TypeError: Cannot read properties of null`
|
|
113
|
+
|
|
114
|
+
const entries = parseLogEntries(logContent)
|
|
112
115
|
|
|
113
|
-
|
|
116
|
+
expect(entries).toHaveLength(3)
|
|
114
117
|
|
|
115
|
-
expect(entries).toHaveLength(3);
|
|
116
|
-
|
|
117
118
|
// First entry - connection error with stack trace
|
|
118
|
-
expect(entries[0].timestamp).toBe(
|
|
119
|
-
expect(entries[0].source).toBe(
|
|
120
|
-
expect(entries[0].message).toContain(
|
|
121
|
-
expect(entries[0].message).toContain(
|
|
122
|
-
expect(entries[0].message).toContain(
|
|
123
|
-
|
|
119
|
+
expect(entries[0].timestamp).toBe("2025-09-03T03:57:39.098Z")
|
|
120
|
+
expect(entries[0].source).toBe("SERVER")
|
|
121
|
+
expect(entries[0].message).toContain("❌ Database connection failed")
|
|
122
|
+
expect(entries[0].message).toContain("ConnectionPool.connect")
|
|
123
|
+
expect(entries[0].message).toContain("Connection timeout")
|
|
124
|
+
|
|
124
125
|
// Second entry - error details object
|
|
125
|
-
expect(entries[1].timestamp).toBe(
|
|
126
|
-
expect(entries[1].source).toBe(
|
|
127
|
-
expect(entries[1].message).toContain(
|
|
128
|
-
expect(entries[1].message).toContain(
|
|
129
|
-
expect(entries[1].message).toContain(
|
|
130
|
-
|
|
126
|
+
expect(entries[1].timestamp).toBe("2025-09-03T03:57:39.098Z")
|
|
127
|
+
expect(entries[1].source).toBe("SERVER")
|
|
128
|
+
expect(entries[1].message).toContain("❌ Error details: {")
|
|
129
|
+
expect(entries[1].message).toContain("connection.query is not a function")
|
|
130
|
+
expect(entries[1].message).toContain("connectionId: 'conn_xyz789abc'")
|
|
131
|
+
|
|
131
132
|
// Third entry - browser error
|
|
132
|
-
expect(entries[2].timestamp).toBe(
|
|
133
|
-
expect(entries[2].source).toBe(
|
|
134
|
-
expect(entries[2].message).toBe(
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
it(
|
|
138
|
-
expect(parseLogEntries(
|
|
139
|
-
expect(parseLogEntries(
|
|
140
|
-
expect(parseLogEntries(
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
it(
|
|
133
|
+
expect(entries[2].timestamp).toBe("2025-09-03T03:57:40.200Z")
|
|
134
|
+
expect(entries[2].source).toBe("BROWSER")
|
|
135
|
+
expect(entries[2].message).toBe("[CONSOLE ERROR] Uncaught TypeError: Cannot read properties of null")
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it("should handle empty and malformed log content", () => {
|
|
139
|
+
expect(parseLogEntries("")).toHaveLength(0)
|
|
140
|
+
expect(parseLogEntries(" \n \n ")).toHaveLength(0)
|
|
141
|
+
expect(parseLogEntries("Invalid log line without timestamp")).toHaveLength(0)
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
it("should preserve original content for debugging", () => {
|
|
144
145
|
const logContent = `[2025-09-03T03:57:39.357Z] [SERVER] API response: {
|
|
145
146
|
myapp:dev: userId: 'user_123',
|
|
146
147
|
myapp:dev: status: 'active'
|
|
147
|
-
myapp:dev: }
|
|
148
|
+
myapp:dev: }`
|
|
149
|
+
|
|
150
|
+
const entries = parseLogEntries(logContent)
|
|
148
151
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
expect(entries).
|
|
152
|
-
expect(entries[0].
|
|
153
|
-
|
|
154
|
-
expect(entries[0].message).toContain('userId: \'user_123\'');
|
|
155
|
-
});
|
|
152
|
+
expect(entries).toHaveLength(1)
|
|
153
|
+
expect(entries[0].original).toBe(logContent)
|
|
154
|
+
expect(entries[0].message).toContain("API response: {")
|
|
155
|
+
expect(entries[0].message).toContain("userId: 'user_123'")
|
|
156
|
+
})
|
|
156
157
|
|
|
157
|
-
it(
|
|
158
|
+
it("should handle mixed single-line and multi-line entries", () => {
|
|
158
159
|
const logContent = `[2025-09-03T03:57:38.500Z] [SERVER] ✓ Server started on port 3000
|
|
159
160
|
[2025-09-03T03:57:39.614Z] [SERVER] myapp:dev: validation[error] Request failed: {
|
|
160
161
|
myapp:dev: endpoint: '/api/v1/validate',
|
|
@@ -165,20 +166,43 @@ myapp:dev: ],
|
|
|
165
166
|
myapp:dev: requestId: 'req_validation_456'
|
|
166
167
|
myapp:dev: }
|
|
167
168
|
[2025-09-03T03:57:40.100Z] [BROWSER] [NAVIGATION] http://localhost:3000/dashboard
|
|
168
|
-
[2025-09-03T03:57:40.300Z] [SERVER] Dashboard page rendered in 89ms
|
|
169
|
+
[2025-09-03T03:57:40.300Z] [SERVER] Dashboard page rendered in 89ms`
|
|
169
170
|
|
|
170
|
-
const entries = parseLogEntries(logContent)
|
|
171
|
+
const entries = parseLogEntries(logContent)
|
|
172
|
+
|
|
173
|
+
expect(entries).toHaveLength(4)
|
|
171
174
|
|
|
172
|
-
expect(entries).toHaveLength(4);
|
|
173
|
-
|
|
174
175
|
// Single-line entries
|
|
175
|
-
expect(entries[0].message).toBe(
|
|
176
|
-
expect(entries[2].message).toBe(
|
|
177
|
-
expect(entries[3].message).toBe(
|
|
178
|
-
|
|
176
|
+
expect(entries[0].message).toBe("✓ Server started on port 3000")
|
|
177
|
+
expect(entries[2].message).toBe("[NAVIGATION] http://localhost:3000/dashboard")
|
|
178
|
+
expect(entries[3].message).toBe("Dashboard page rendered in 89ms")
|
|
179
|
+
|
|
179
180
|
// Multi-line entry
|
|
180
|
-
expect(entries[1].message).toContain(
|
|
181
|
-
expect(entries[1].message).toContain(
|
|
182
|
-
expect(entries[1].message).toContain(
|
|
183
|
-
})
|
|
184
|
-
|
|
181
|
+
expect(entries[1].message).toContain("validation[error] Request failed: {")
|
|
182
|
+
expect(entries[1].message).toContain("Invalid format")
|
|
183
|
+
expect(entries[1].message).toContain("requestId: 'req_validation_456'")
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
it("should handle console log entries with CSS formatting directives", () => {
|
|
187
|
+
const logContent = `[2025-09-09T21:24:27.264Z] [BROWSER] [CONSOLE LOG] %c[Vercel Web Analytics]%c Debug mode is enabled by default in development. No requests will be sent to the server. color: rgb(120, 120, 120) color: inherit
|
|
188
|
+
[2025-09-09T21:24:27.264Z] [BROWSER] [CONSOLE LOG] %c[Vercel Speed Insights]%c [vitals] color: rgb(120, 120, 120) color: inherit {"type":"object","description":"Object","overflow":false}`
|
|
189
|
+
|
|
190
|
+
const entries = parseLogEntries(logContent)
|
|
191
|
+
|
|
192
|
+
expect(entries).toHaveLength(2)
|
|
193
|
+
|
|
194
|
+
// First entry should have CSS formatting cleaned up
|
|
195
|
+
expect(entries[0].timestamp).toBe("2025-09-09T21:24:27.264Z")
|
|
196
|
+
expect(entries[0].source).toBe("BROWSER")
|
|
197
|
+
expect(entries[0].message).toBe(
|
|
198
|
+
"[CONSOLE LOG] [Vercel Web Analytics] Debug mode is enabled by default in development. No requests will be sent to the server."
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
// Second entry should have CSS formatting cleaned up and preserve JSON
|
|
202
|
+
expect(entries[1].timestamp).toBe("2025-09-09T21:24:27.264Z")
|
|
203
|
+
expect(entries[1].source).toBe("BROWSER")
|
|
204
|
+
expect(entries[1].message).toBe(
|
|
205
|
+
'[CONSOLE LOG] [Vercel Speed Insights] [vitals] {"type":"object","description":"Object","overflow":false}'
|
|
206
|
+
)
|
|
207
|
+
})
|
|
208
|
+
})
|