@tanstack/devtools-vite 0.4.1 → 0.5.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.
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/plugin.d.ts +18 -0
- package/dist/esm/plugin.js +93 -10
- package/dist/esm/plugin.js.map +1 -1
- package/dist/esm/utils.d.ts +21 -1
- package/dist/esm/utils.js +92 -3
- package/dist/esm/utils.js.map +1 -1
- package/dist/esm/utils.test.d.ts +1 -0
- package/dist/esm/virtual-console.d.ts +19 -0
- package/dist/esm/virtual-console.js +180 -0
- package/dist/esm/virtual-console.js.map +1 -0
- package/dist/esm/virtual-console.test.d.ts +1 -0
- package/package.json +1 -1
- package/src/enhance-logs.test.ts +1 -1
- package/src/index.ts +1 -1
- package/src/plugin.ts +151 -10
- package/src/utils.test.ts +184 -0
- package/src/utils.ts +155 -3
- package/src/virtual-console.test.ts +73 -0
- package/src/virtual-console.ts +202 -0
package/src/utils.ts
CHANGED
|
@@ -4,12 +4,31 @@ import type { Connect } from 'vite'
|
|
|
4
4
|
import type { IncomingMessage, ServerResponse } from 'node:http'
|
|
5
5
|
import type { PackageJson } from '@tanstack/devtools-client'
|
|
6
6
|
|
|
7
|
+
type DevToolsRequestHandler = (data: any) => void
|
|
8
|
+
|
|
9
|
+
type DevToolsViteRequestOptions = {
|
|
10
|
+
onOpenSource?: DevToolsRequestHandler
|
|
11
|
+
onConsolePipe?: (entries: Array<any>) => void
|
|
12
|
+
onServerConsolePipe?: (entries: Array<any>) => void
|
|
13
|
+
onConsolePipeSSE?: (
|
|
14
|
+
res: ServerResponse<IncomingMessage>,
|
|
15
|
+
req: Connect.IncomingMessage,
|
|
16
|
+
) => void
|
|
17
|
+
}
|
|
18
|
+
|
|
7
19
|
export const handleDevToolsViteRequest = (
|
|
8
20
|
req: Connect.IncomingMessage,
|
|
9
21
|
res: ServerResponse<IncomingMessage>,
|
|
10
22
|
next: Connect.NextFunction,
|
|
11
|
-
|
|
23
|
+
cbOrOptions: DevToolsRequestHandler | DevToolsViteRequestOptions,
|
|
12
24
|
) => {
|
|
25
|
+
// Normalize to options object for backward compatibility
|
|
26
|
+
const options: DevToolsViteRequestOptions =
|
|
27
|
+
typeof cbOrOptions === 'function'
|
|
28
|
+
? { onOpenSource: cbOrOptions }
|
|
29
|
+
: cbOrOptions
|
|
30
|
+
|
|
31
|
+
// Handle open-source requests
|
|
13
32
|
if (req.url?.includes('__tsd/open-source')) {
|
|
14
33
|
const searchParams = new URLSearchParams(req.url.split('?')[1])
|
|
15
34
|
|
|
@@ -24,7 +43,7 @@ export const handleDevToolsViteRequest = (
|
|
|
24
43
|
}
|
|
25
44
|
const { file, line, column } = parsed
|
|
26
45
|
|
|
27
|
-
|
|
46
|
+
options.onOpenSource?.({
|
|
28
47
|
type: 'open-source',
|
|
29
48
|
routine: 'open-source',
|
|
30
49
|
data: {
|
|
@@ -38,6 +57,62 @@ export const handleDevToolsViteRequest = (
|
|
|
38
57
|
res.end()
|
|
39
58
|
return
|
|
40
59
|
}
|
|
60
|
+
|
|
61
|
+
// Handle console-pipe SSE endpoint (browser subscribes to server logs)
|
|
62
|
+
if (req.url?.includes('__tsd/console-pipe/sse') && req.method === 'GET') {
|
|
63
|
+
if (options.onConsolePipeSSE) {
|
|
64
|
+
options.onConsolePipeSSE(res, req)
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
return next()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Handle server console-pipe POST endpoint (from app server runtime)
|
|
71
|
+
if (req.url?.includes('__tsd/console-pipe/server') && req.method === 'POST') {
|
|
72
|
+
if (options.onServerConsolePipe) {
|
|
73
|
+
let body = ''
|
|
74
|
+
req.on('data', (chunk: Buffer) => {
|
|
75
|
+
body += chunk.toString()
|
|
76
|
+
})
|
|
77
|
+
req.on('end', () => {
|
|
78
|
+
try {
|
|
79
|
+
const { entries } = JSON.parse(body)
|
|
80
|
+
options.onServerConsolePipe!(entries)
|
|
81
|
+
res.statusCode = 200
|
|
82
|
+
res.end('OK')
|
|
83
|
+
} catch {
|
|
84
|
+
res.statusCode = 400
|
|
85
|
+
res.end('Bad Request')
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
return next()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Handle console-pipe POST endpoint (from client)
|
|
94
|
+
if (req.url?.includes('__tsd/console-pipe') && req.method === 'POST') {
|
|
95
|
+
if (options.onConsolePipe) {
|
|
96
|
+
let body = ''
|
|
97
|
+
req.on('data', (chunk: Buffer) => {
|
|
98
|
+
body += chunk.toString()
|
|
99
|
+
})
|
|
100
|
+
req.on('end', () => {
|
|
101
|
+
try {
|
|
102
|
+
const { entries } = JSON.parse(body)
|
|
103
|
+
options.onConsolePipe!(entries)
|
|
104
|
+
res.statusCode = 200
|
|
105
|
+
res.end('OK')
|
|
106
|
+
} catch {
|
|
107
|
+
res.statusCode = 400
|
|
108
|
+
res.end('Bad Request')
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
return next()
|
|
114
|
+
}
|
|
115
|
+
|
|
41
116
|
if (!req.url?.includes('__tsd')) {
|
|
42
117
|
return next()
|
|
43
118
|
}
|
|
@@ -50,7 +125,7 @@ export const handleDevToolsViteRequest = (
|
|
|
50
125
|
const dataToParse = Buffer.concat(chunks)
|
|
51
126
|
try {
|
|
52
127
|
const parsedData = JSON.parse(dataToParse.toString())
|
|
53
|
-
|
|
128
|
+
options.onOpenSource?.(parsedData)
|
|
54
129
|
} catch (e) {}
|
|
55
130
|
res.write('OK')
|
|
56
131
|
})
|
|
@@ -92,3 +167,80 @@ export const tryParseJson = <T extends any>(
|
|
|
92
167
|
|
|
93
168
|
export const readPackageJson = async () =>
|
|
94
169
|
tryParseJson<PackageJson>(await tryReadFile(process.cwd() + '/package.json'))
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Extracts and formats the source location from enhanced client console logs.
|
|
173
|
+
* Instead of stripping the prefix entirely, we extract the file:line:column
|
|
174
|
+
* from the "Go to Source" URL and use that as a prefix.
|
|
175
|
+
*
|
|
176
|
+
* Enhanced logs format (two variants):
|
|
177
|
+
* 1. ['%cLOG%c %cGo to Source: http://...?source=%2Fsrc%2F...%c \n → ', 'color:...', 'color:...', 'color:...', 'color:...', 'message']
|
|
178
|
+
* 2. ['\x1b[...]%s\x1b[...]', '%cLOG%c %cGo to Source: ...%c \n → ', 'color:...', 'color:...', 'color:...', 'color:...', 'message']
|
|
179
|
+
*
|
|
180
|
+
* Output: ['src/components/Header.tsx:26:13', 'message']
|
|
181
|
+
*/
|
|
182
|
+
export const stripEnhancedLogPrefix = (
|
|
183
|
+
args: Array<unknown>,
|
|
184
|
+
formatSourceLocation?: (location: string) => unknown,
|
|
185
|
+
): Array<unknown> => {
|
|
186
|
+
if (args.length === 0) return args
|
|
187
|
+
|
|
188
|
+
// Find the arg that contains the Go to Source URL
|
|
189
|
+
let sourceArgIndex = -1
|
|
190
|
+
for (let i = 0; i < args.length; i++) {
|
|
191
|
+
const arg = args[i]
|
|
192
|
+
if (typeof arg === 'string' && arg.includes('__tsd/open-source?source=')) {
|
|
193
|
+
sourceArgIndex = i
|
|
194
|
+
break
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// If no source URL found, return args as-is (not an enhanced log)
|
|
199
|
+
if (sourceArgIndex === -1) {
|
|
200
|
+
return args
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const sourceArg = args[sourceArgIndex] as string
|
|
204
|
+
|
|
205
|
+
// Extract the source from the "Go to Source" URL
|
|
206
|
+
// URL format: http://localhost:3000/__tsd/open-source?source=%2Fsrc%2Ffile.tsx%3A26%3A13%c
|
|
207
|
+
// Note: The URL ends with %c which is a console format specifier, not URL encoding
|
|
208
|
+
let sourceLocation = ''
|
|
209
|
+
const sourceMatch = sourceArg.match(/source=([^&\s]+?)%c/)
|
|
210
|
+
if (sourceMatch?.[1]) {
|
|
211
|
+
try {
|
|
212
|
+
sourceLocation = decodeURIComponent(sourceMatch[1])
|
|
213
|
+
// Remove leading slash if present
|
|
214
|
+
if (sourceLocation.startsWith('/')) {
|
|
215
|
+
sourceLocation = sourceLocation.slice(1)
|
|
216
|
+
}
|
|
217
|
+
} catch {
|
|
218
|
+
// If decoding fails, leave it empty
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Count %c markers in the source arg to know how many style args follow it
|
|
223
|
+
const styleCount = (sourceArg.match(/%c/g) || []).length
|
|
224
|
+
|
|
225
|
+
// The actual user args start after the source arg and all its style args
|
|
226
|
+
const userArgsStart = sourceArgIndex + 1 + styleCount
|
|
227
|
+
|
|
228
|
+
// Build the result: source location prefix + remaining args (the actual user data)
|
|
229
|
+
const result: Array<unknown> = []
|
|
230
|
+
|
|
231
|
+
// Add source location as prefix if we found one
|
|
232
|
+
if (sourceLocation) {
|
|
233
|
+
result.push(
|
|
234
|
+
formatSourceLocation
|
|
235
|
+
? formatSourceLocation(sourceLocation)
|
|
236
|
+
: sourceLocation,
|
|
237
|
+
)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Add remaining args (the actual user data)
|
|
241
|
+
for (let i = userArgsStart; i < args.length; i++) {
|
|
242
|
+
result.push(args[i])
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return result.length > 0 ? result : args
|
|
246
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest'
|
|
2
|
+
import { generateConsolePipeCode } from './virtual-console'
|
|
3
|
+
|
|
4
|
+
const TEST_VITE_URL = 'http://localhost:5173'
|
|
5
|
+
|
|
6
|
+
describe('virtual-console', () => {
|
|
7
|
+
test('generates inline code with specified levels', () => {
|
|
8
|
+
const code = generateConsolePipeCode(['log', 'error'], TEST_VITE_URL)
|
|
9
|
+
|
|
10
|
+
expect(code).toContain('["log","error"]')
|
|
11
|
+
expect(code).toContain('originalConsole')
|
|
12
|
+
expect(code).toContain('__TSD_CONSOLE_PIPE_INITIALIZED__')
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
test('uses fetch to send client logs', () => {
|
|
16
|
+
const code = generateConsolePipeCode(['log'], TEST_VITE_URL)
|
|
17
|
+
|
|
18
|
+
expect(code).toContain('/__tsd/console-pipe')
|
|
19
|
+
expect(code).toContain("method: 'POST'")
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test('uses SSE to receive server logs', () => {
|
|
23
|
+
const code = generateConsolePipeCode(['log'], TEST_VITE_URL)
|
|
24
|
+
|
|
25
|
+
expect(code).toContain("new EventSource('/__tsd/console-pipe/sse')")
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('includes environment detection', () => {
|
|
29
|
+
const code = generateConsolePipeCode(['log'], TEST_VITE_URL)
|
|
30
|
+
|
|
31
|
+
expect(code).toContain("typeof window === 'undefined'")
|
|
32
|
+
expect(code).toContain('isServer')
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('includes batcher configuration', () => {
|
|
36
|
+
const code = generateConsolePipeCode(['log'], TEST_VITE_URL)
|
|
37
|
+
|
|
38
|
+
expect(code).toContain('BATCH_WAIT')
|
|
39
|
+
expect(code).toContain('BATCH_MAX_SIZE')
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test('includes flush functionality', () => {
|
|
43
|
+
const code = generateConsolePipeCode(['log'], TEST_VITE_URL)
|
|
44
|
+
|
|
45
|
+
expect(code).toContain('flushBatch')
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('includes beforeunload listener for browser', () => {
|
|
49
|
+
const code = generateConsolePipeCode(['log'], TEST_VITE_URL)
|
|
50
|
+
|
|
51
|
+
expect(code).toContain('beforeunload')
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test('wraps code in IIFE', () => {
|
|
55
|
+
const code = generateConsolePipeCode(['log'], TEST_VITE_URL)
|
|
56
|
+
|
|
57
|
+
expect(code).toContain('(function __tsdConsolePipe()')
|
|
58
|
+
expect(code).toContain('})();')
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test('has no external imports', () => {
|
|
62
|
+
const code = generateConsolePipeCode(['log'], TEST_VITE_URL)
|
|
63
|
+
|
|
64
|
+
expect(code).not.toContain('import ')
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
test('includes vite server URL for server piping', () => {
|
|
68
|
+
const code = generateConsolePipeCode(['log'], TEST_VITE_URL)
|
|
69
|
+
|
|
70
|
+
expect(code).toContain(TEST_VITE_URL)
|
|
71
|
+
expect(code).toContain('/__tsd/console-pipe/server')
|
|
72
|
+
})
|
|
73
|
+
})
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import type { ConsoleLevel } from './plugin'
|
|
2
|
+
|
|
3
|
+
// export const VIRTUAL_MODULE_ID = 'virtual:tanstack-devtools-console'
|
|
4
|
+
// export const RESOLVED_VIRTUAL_MODULE_ID = '\0' + VIRTUAL_MODULE_ID
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Generates inline code to inject into entry files (both client and server).
|
|
8
|
+
* This code detects the environment at runtime and:
|
|
9
|
+
*
|
|
10
|
+
* CLIENT:
|
|
11
|
+
* 1. Store original console methods
|
|
12
|
+
* 2. Create batched wrappers that POST to server via fetch
|
|
13
|
+
* 3. Override global console with the wrapped methods
|
|
14
|
+
* 4. Listen for server console logs via SSE
|
|
15
|
+
*
|
|
16
|
+
* SERVER (Nitro/Vinxi runtime):
|
|
17
|
+
* 1. Store original console methods
|
|
18
|
+
* 2. Create batched wrappers that POST to Vite dev server
|
|
19
|
+
* 3. Override global console - original logging still happens, just also pipes to Vite
|
|
20
|
+
*
|
|
21
|
+
* Returns the inline code as a string - no imports needed since we use fetch.
|
|
22
|
+
*/
|
|
23
|
+
export function generateConsolePipeCode(
|
|
24
|
+
levels: Array<ConsoleLevel>,
|
|
25
|
+
viteServerUrl: string,
|
|
26
|
+
): string {
|
|
27
|
+
const levelsArray = JSON.stringify(levels)
|
|
28
|
+
|
|
29
|
+
return `
|
|
30
|
+
;(function __tsdConsolePipe() {
|
|
31
|
+
// Detect environment
|
|
32
|
+
var isServer = typeof window === 'undefined';
|
|
33
|
+
var envKey = isServer ? '__TSD_SERVER_CONSOLE_PIPE_INITIALIZED__' : '__TSD_CONSOLE_PIPE_INITIALIZED__';
|
|
34
|
+
var globalObj = isServer ? globalThis : window;
|
|
35
|
+
|
|
36
|
+
// Only run once per environment
|
|
37
|
+
if (globalObj[envKey]) return;
|
|
38
|
+
globalObj[envKey] = true;
|
|
39
|
+
|
|
40
|
+
var CONSOLE_LEVELS = ${levelsArray};
|
|
41
|
+
var VITE_SERVER_URL = ${JSON.stringify(viteServerUrl)};
|
|
42
|
+
|
|
43
|
+
// Store original console methods before we override them
|
|
44
|
+
var originalConsole = {};
|
|
45
|
+
for (var i = 0; i < CONSOLE_LEVELS.length; i++) {
|
|
46
|
+
var level = CONSOLE_LEVELS[i];
|
|
47
|
+
originalConsole[level] = console[level].bind(console);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Simple inline batcher implementation
|
|
51
|
+
var batchedEntries = [];
|
|
52
|
+
var batchTimeout = null;
|
|
53
|
+
var BATCH_WAIT = isServer ? 50 : 100;
|
|
54
|
+
var BATCH_MAX_SIZE = isServer ? 20 : 50;
|
|
55
|
+
|
|
56
|
+
function flushBatch() {
|
|
57
|
+
if (batchedEntries.length === 0) return;
|
|
58
|
+
|
|
59
|
+
var entries = batchedEntries;
|
|
60
|
+
batchedEntries = [];
|
|
61
|
+
batchTimeout = null;
|
|
62
|
+
|
|
63
|
+
// Determine endpoint based on environment
|
|
64
|
+
var endpoint = isServer
|
|
65
|
+
? VITE_SERVER_URL + '/__tsd/console-pipe/server'
|
|
66
|
+
: '/__tsd/console-pipe';
|
|
67
|
+
|
|
68
|
+
// Send to Vite server via fetch
|
|
69
|
+
fetch(endpoint, {
|
|
70
|
+
method: 'POST',
|
|
71
|
+
headers: { 'Content-Type': 'application/json' },
|
|
72
|
+
body: JSON.stringify({ entries: entries }),
|
|
73
|
+
}).catch(function( ) {
|
|
74
|
+
// Swallow errors
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function addToBatch(entry) {
|
|
79
|
+
batchedEntries.push(entry);
|
|
80
|
+
|
|
81
|
+
if (batchedEntries.length >= BATCH_MAX_SIZE) {
|
|
82
|
+
if (batchTimeout) {
|
|
83
|
+
clearTimeout(batchTimeout);
|
|
84
|
+
batchTimeout = null;
|
|
85
|
+
}
|
|
86
|
+
flushBatch();
|
|
87
|
+
} else if (!batchTimeout) {
|
|
88
|
+
batchTimeout = setTimeout(flushBatch, BATCH_WAIT);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Override global console methods
|
|
93
|
+
for (var j = 0; j < CONSOLE_LEVELS.length; j++) {
|
|
94
|
+
(function(level) {
|
|
95
|
+
var original = originalConsole[level];
|
|
96
|
+
console[level] = function() {
|
|
97
|
+
var args = Array.prototype.slice.call(arguments);
|
|
98
|
+
|
|
99
|
+
// Always call original first so logs appear normally
|
|
100
|
+
original.apply(console, args);
|
|
101
|
+
|
|
102
|
+
// Skip our own TSD Console Pipe logs to avoid recursion/noise
|
|
103
|
+
if (args.length > 0 && typeof args[0] === 'string' &&
|
|
104
|
+
(args[0].indexOf('[TSD Console Pipe]') !== -1 ||
|
|
105
|
+
args[0].indexOf('[@tanstack/devtools') !== -1)) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Serialize args safely
|
|
110
|
+
var safeArgs = args.map(function(arg) {
|
|
111
|
+
if (arg === undefined) return 'undefined';
|
|
112
|
+
if (arg === null) return null;
|
|
113
|
+
if (typeof arg === 'function') return '[Function]';
|
|
114
|
+
if (typeof arg === 'symbol') return arg.toString();
|
|
115
|
+
try {
|
|
116
|
+
JSON.stringify(arg);
|
|
117
|
+
return arg;
|
|
118
|
+
} catch (e) {
|
|
119
|
+
return String(arg);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
var entry = {
|
|
124
|
+
level: level,
|
|
125
|
+
args: safeArgs,
|
|
126
|
+
source: isServer ? 'server' : 'client',
|
|
127
|
+
timestamp: Date.now(),
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
addToBatch(entry);
|
|
131
|
+
};
|
|
132
|
+
})(CONSOLE_LEVELS[j]);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// CLIENT ONLY: Listen for server console logs via SSE
|
|
136
|
+
if (!isServer) {
|
|
137
|
+
// Transform server log args - strip ANSI codes and convert source paths to clickable URLs
|
|
138
|
+
function transformServerLogArgs(args) {
|
|
139
|
+
var escChar = String.fromCharCode(27);
|
|
140
|
+
var transformed = [];
|
|
141
|
+
|
|
142
|
+
for (var k = 0; k < args.length; k++) {
|
|
143
|
+
var arg = args[k];
|
|
144
|
+
if (typeof arg === 'string') {
|
|
145
|
+
// Strip ANSI escape sequences (ESC[...m patterns)
|
|
146
|
+
var cleaned = arg;
|
|
147
|
+
// Remove ESC character followed by [...m] - need to build regex dynamically
|
|
148
|
+
while (cleaned.indexOf(escChar) !== -1) {
|
|
149
|
+
cleaned = cleaned.split(escChar).join('');
|
|
150
|
+
}
|
|
151
|
+
// Also remove any leftover bracket codes like [35m
|
|
152
|
+
cleaned = cleaned.replace(/\\[[0-9;]*m/g, '');
|
|
153
|
+
|
|
154
|
+
// Transform source paths to clickable URLs
|
|
155
|
+
// Match patterns like /src/components/Header.tsx:17:3
|
|
156
|
+
var sourceRegex = /(\\/[^\\s]+:\\d+:\\d+)/g;
|
|
157
|
+
cleaned = cleaned.replace(sourceRegex, function(match) {
|
|
158
|
+
return window.location.origin + '/__tsd/open-source?source=' + encodeURIComponent(match);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
if (cleaned.trim()) {
|
|
162
|
+
transformed.push(cleaned);
|
|
163
|
+
}
|
|
164
|
+
} else {
|
|
165
|
+
transformed.push(arg);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return transformed;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
var eventSource = new EventSource('/__tsd/console-pipe/sse');
|
|
173
|
+
|
|
174
|
+
eventSource.onmessage = function(event) {
|
|
175
|
+
try {
|
|
176
|
+
var data = JSON.parse(event.data);
|
|
177
|
+
if (data.entries) {
|
|
178
|
+
for (var m = 0; m < data.entries.length; m++) {
|
|
179
|
+
var entry = data.entries[m];
|
|
180
|
+
var transformedArgs = transformServerLogArgs(entry.args);
|
|
181
|
+
var prefix = '%c[Server]%c';
|
|
182
|
+
var prefixStyle = 'color: #9333ea; font-weight: bold;';
|
|
183
|
+
var resetStyle = 'color: inherit;';
|
|
184
|
+
var logMethod = originalConsole[entry.level] || originalConsole.log;
|
|
185
|
+
logMethod.apply(console, [prefix, prefixStyle, resetStyle].concat(transformedArgs));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
} catch (err) {
|
|
189
|
+
// Swallow errors
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
eventSource.onerror = function() {
|
|
194
|
+
// Swallow errors
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// Flush on page unload
|
|
198
|
+
window.addEventListener('beforeunload', flushBatch);
|
|
199
|
+
}
|
|
200
|
+
})();
|
|
201
|
+
`
|
|
202
|
+
}
|