@buenojs/bueno 0.8.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/.env.example +109 -0
- package/.github/workflows/ci.yml +31 -0
- package/LICENSE +21 -0
- package/README.md +892 -0
- package/architecture.md +652 -0
- package/bun.lock +70 -0
- package/dist/cli/index.js +3233 -0
- package/dist/index.js +9014 -0
- package/package.json +77 -0
- package/src/cache/index.ts +795 -0
- package/src/cli/ARCHITECTURE.md +837 -0
- package/src/cli/bin.ts +10 -0
- package/src/cli/commands/build.ts +425 -0
- package/src/cli/commands/dev.ts +248 -0
- package/src/cli/commands/generate.ts +541 -0
- package/src/cli/commands/help.ts +55 -0
- package/src/cli/commands/index.ts +112 -0
- package/src/cli/commands/migration.ts +355 -0
- package/src/cli/commands/new.ts +804 -0
- package/src/cli/commands/start.ts +208 -0
- package/src/cli/core/args.ts +283 -0
- package/src/cli/core/console.ts +349 -0
- package/src/cli/core/index.ts +60 -0
- package/src/cli/core/prompt.ts +424 -0
- package/src/cli/core/spinner.ts +265 -0
- package/src/cli/index.ts +135 -0
- package/src/cli/templates/deploy.ts +295 -0
- package/src/cli/templates/docker.ts +307 -0
- package/src/cli/templates/index.ts +24 -0
- package/src/cli/utils/fs.ts +428 -0
- package/src/cli/utils/index.ts +8 -0
- package/src/cli/utils/strings.ts +197 -0
- package/src/config/env.ts +408 -0
- package/src/config/index.ts +506 -0
- package/src/config/loader.ts +329 -0
- package/src/config/merge.ts +285 -0
- package/src/config/types.ts +320 -0
- package/src/config/validation.ts +441 -0
- package/src/container/forward-ref.ts +143 -0
- package/src/container/index.ts +386 -0
- package/src/context/index.ts +360 -0
- package/src/database/index.ts +1142 -0
- package/src/database/migrations/index.ts +371 -0
- package/src/database/schema/index.ts +619 -0
- package/src/frontend/api-routes.ts +640 -0
- package/src/frontend/bundler.ts +643 -0
- package/src/frontend/console-client.ts +419 -0
- package/src/frontend/console-stream.ts +587 -0
- package/src/frontend/dev-server.ts +846 -0
- package/src/frontend/file-router.ts +611 -0
- package/src/frontend/frameworks/index.ts +106 -0
- package/src/frontend/frameworks/react.ts +85 -0
- package/src/frontend/frameworks/solid.ts +104 -0
- package/src/frontend/frameworks/svelte.ts +110 -0
- package/src/frontend/frameworks/vue.ts +92 -0
- package/src/frontend/hmr-client.ts +663 -0
- package/src/frontend/hmr.ts +728 -0
- package/src/frontend/index.ts +342 -0
- package/src/frontend/islands.ts +552 -0
- package/src/frontend/isr.ts +555 -0
- package/src/frontend/layout.ts +475 -0
- package/src/frontend/ssr/react.ts +446 -0
- package/src/frontend/ssr/solid.ts +523 -0
- package/src/frontend/ssr/svelte.ts +546 -0
- package/src/frontend/ssr/vue.ts +504 -0
- package/src/frontend/ssr.ts +699 -0
- package/src/frontend/types.ts +2274 -0
- package/src/health/index.ts +604 -0
- package/src/index.ts +410 -0
- package/src/lock/index.ts +587 -0
- package/src/logger/index.ts +444 -0
- package/src/logger/transports/index.ts +969 -0
- package/src/metrics/index.ts +494 -0
- package/src/middleware/built-in.ts +360 -0
- package/src/middleware/index.ts +94 -0
- package/src/modules/filters.ts +458 -0
- package/src/modules/guards.ts +405 -0
- package/src/modules/index.ts +1256 -0
- package/src/modules/interceptors.ts +574 -0
- package/src/modules/lazy.ts +418 -0
- package/src/modules/lifecycle.ts +478 -0
- package/src/modules/metadata.ts +90 -0
- package/src/modules/pipes.ts +626 -0
- package/src/router/index.ts +339 -0
- package/src/router/linear.ts +371 -0
- package/src/router/regex.ts +292 -0
- package/src/router/tree.ts +562 -0
- package/src/rpc/index.ts +1263 -0
- package/src/security/index.ts +436 -0
- package/src/ssg/index.ts +631 -0
- package/src/storage/index.ts +456 -0
- package/src/telemetry/index.ts +1097 -0
- package/src/testing/index.ts +1586 -0
- package/src/types/index.ts +236 -0
- package/src/types/optional-deps.d.ts +219 -0
- package/src/validation/index.ts +276 -0
- package/src/websocket/index.ts +1004 -0
- package/tests/integration/cli.test.ts +1016 -0
- package/tests/integration/fullstack.test.ts +234 -0
- package/tests/unit/cache.test.ts +174 -0
- package/tests/unit/cli-commands.test.ts +892 -0
- package/tests/unit/cli.test.ts +1258 -0
- package/tests/unit/container.test.ts +279 -0
- package/tests/unit/context.test.ts +221 -0
- package/tests/unit/database.test.ts +183 -0
- package/tests/unit/linear-router.test.ts +280 -0
- package/tests/unit/lock.test.ts +336 -0
- package/tests/unit/middleware.test.ts +184 -0
- package/tests/unit/modules.test.ts +142 -0
- package/tests/unit/pubsub.test.ts +257 -0
- package/tests/unit/regex-router.test.ts +265 -0
- package/tests/unit/router.test.ts +373 -0
- package/tests/unit/rpc.test.ts +1248 -0
- package/tests/unit/security.test.ts +174 -0
- package/tests/unit/telemetry.test.ts +371 -0
- package/tests/unit/test-cache.test.ts +110 -0
- package/tests/unit/test-database.test.ts +282 -0
- package/tests/unit/tree-router.test.ts +325 -0
- package/tests/unit/validation.test.ts +794 -0
- package/tsconfig.json +27 -0
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side Console Interception Script
|
|
3
|
+
*
|
|
4
|
+
* This script is injected into the browser to capture console.* calls
|
|
5
|
+
* and send them to the server via WebSocket for terminal output.
|
|
6
|
+
*
|
|
7
|
+
* @module frontend/console-client
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// This is the client-side script that will be injected into HTML pages
|
|
11
|
+
// It's stored as a string constant for injection
|
|
12
|
+
|
|
13
|
+
export const CONSOLE_CLIENT_SCRIPT = `
|
|
14
|
+
(function() {
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
// Configuration
|
|
18
|
+
const WS_PORT = typeof CONSOLE_PORT !== 'undefined' ? CONSOLE_PORT : 3002;
|
|
19
|
+
const WS_URL = 'ws://localhost:' + WS_PORT + '/_console';
|
|
20
|
+
const MAX_ARGS_LENGTH = 10; // Maximum number of args to send
|
|
21
|
+
const MAX_STRING_LENGTH = 10000; // Maximum string length before truncation
|
|
22
|
+
const MAX_OBJECT_DEPTH = 5; // Maximum depth for object serialization
|
|
23
|
+
const RECONNECT_DELAY = 1000; // Delay before reconnecting (ms)
|
|
24
|
+
const MAX_RECONNECT_ATTEMPTS = 10; // Maximum reconnection attempts
|
|
25
|
+
|
|
26
|
+
// State
|
|
27
|
+
let ws = null;
|
|
28
|
+
let clientId = null;
|
|
29
|
+
let reconnectAttempts = 0;
|
|
30
|
+
let isConnected = false;
|
|
31
|
+
let messageQueue = [];
|
|
32
|
+
let originalConsole = {};
|
|
33
|
+
|
|
34
|
+
// Console methods to intercept
|
|
35
|
+
const CONSOLE_METHODS = ['log', 'info', 'warn', 'error', 'debug', 'trace', 'table'];
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Serialize a value for transmission
|
|
39
|
+
* Handles circular references and truncates large values
|
|
40
|
+
*/
|
|
41
|
+
function serializeValue(value, depth, seen) {
|
|
42
|
+
depth = depth || 0;
|
|
43
|
+
seen = seen || new WeakSet();
|
|
44
|
+
|
|
45
|
+
// Handle depth limit
|
|
46
|
+
if (depth > MAX_OBJECT_DEPTH) {
|
|
47
|
+
return { __type: 'truncated', reason: 'max-depth' };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Handle null and undefined
|
|
51
|
+
if (value === null) return null;
|
|
52
|
+
if (value === undefined) return { __type: 'undefined' };
|
|
53
|
+
|
|
54
|
+
// Handle primitives
|
|
55
|
+
if (typeof value === 'string') {
|
|
56
|
+
if (value.length > MAX_STRING_LENGTH) {
|
|
57
|
+
return value.substring(0, MAX_STRING_LENGTH) + '... [truncated]';
|
|
58
|
+
}
|
|
59
|
+
return value;
|
|
60
|
+
}
|
|
61
|
+
if (typeof value === 'number') {
|
|
62
|
+
if (Number.isNaN(value)) return { __type: 'NaN' };
|
|
63
|
+
if (!Number.isFinite(value)) return { __type: 'Infinity', negative: value < 0 };
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
if (typeof value === 'boolean') return value;
|
|
67
|
+
if (typeof value === 'symbol') return { __type: 'symbol', value: value.toString() };
|
|
68
|
+
if (typeof value === 'function') return { __type: 'function', name: value.name || 'anonymous' };
|
|
69
|
+
if (typeof value === 'bigint') return { __type: 'bigint', value: value.toString() };
|
|
70
|
+
|
|
71
|
+
// Handle circular references
|
|
72
|
+
if (typeof value === 'object') {
|
|
73
|
+
if (seen.has(value)) {
|
|
74
|
+
return { __type: 'circular' };
|
|
75
|
+
}
|
|
76
|
+
seen.add(value);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Handle Error objects
|
|
80
|
+
if (value instanceof Error) {
|
|
81
|
+
return {
|
|
82
|
+
__type: 'Error',
|
|
83
|
+
name: value.name,
|
|
84
|
+
message: value.message,
|
|
85
|
+
stack: value.stack
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Handle Date objects
|
|
90
|
+
if (value instanceof Date) {
|
|
91
|
+
return { __type: 'Date', value: value.toISOString() };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Handle RegExp
|
|
95
|
+
if (value instanceof RegExp) {
|
|
96
|
+
return { __type: 'RegExp', value: value.toString() };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Handle Map
|
|
100
|
+
if (value instanceof Map) {
|
|
101
|
+
const entries = [];
|
|
102
|
+
let count = 0;
|
|
103
|
+
for (const [k, v] of value) {
|
|
104
|
+
if (count++ >= 20) break; // Limit entries
|
|
105
|
+
entries.push([serializeValue(k, depth + 1, seen), serializeValue(v, depth + 1, seen)]);
|
|
106
|
+
}
|
|
107
|
+
return { __type: 'Map', entries: entries, size: value.size };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Handle Set
|
|
111
|
+
if (value instanceof Set) {
|
|
112
|
+
const values = [];
|
|
113
|
+
let count = 0;
|
|
114
|
+
for (const v of value) {
|
|
115
|
+
if (count++ >= 20) break; // Limit values
|
|
116
|
+
values.push(serializeValue(v, depth + 1, seen));
|
|
117
|
+
}
|
|
118
|
+
return { __type: 'Set', values: values, size: value.size };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Handle Array
|
|
122
|
+
if (Array.isArray(value)) {
|
|
123
|
+
const arr = [];
|
|
124
|
+
const len = Math.min(value.length, 100); // Limit array length
|
|
125
|
+
for (let i = 0; i < len; i++) {
|
|
126
|
+
arr.push(serializeValue(value[i], depth + 1, seen));
|
|
127
|
+
}
|
|
128
|
+
if (value.length > len) {
|
|
129
|
+
arr.push({ __type: 'truncated', count: value.length - len });
|
|
130
|
+
}
|
|
131
|
+
return arr;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Handle ArrayBuffer / TypedArray
|
|
135
|
+
if (value instanceof ArrayBuffer) {
|
|
136
|
+
return { __type: 'ArrayBuffer', byteLength: value.byteLength };
|
|
137
|
+
}
|
|
138
|
+
if (ArrayBuffer.isView(value)) {
|
|
139
|
+
return {
|
|
140
|
+
__type: 'TypedArray',
|
|
141
|
+
constructor: value.constructor.name,
|
|
142
|
+
length: value.length,
|
|
143
|
+
byteLength: value.byteLength
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Handle DOM elements (simplified)
|
|
148
|
+
if (typeof Element !== 'undefined' && value instanceof Element) {
|
|
149
|
+
return {
|
|
150
|
+
__type: 'Element',
|
|
151
|
+
tagName: value.tagName,
|
|
152
|
+
id: value.id,
|
|
153
|
+
className: value.className
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Handle plain objects
|
|
158
|
+
try {
|
|
159
|
+
const obj = {};
|
|
160
|
+
const keys = Object.keys(value).slice(0, 50); // Limit keys
|
|
161
|
+
for (const key of keys) {
|
|
162
|
+
obj[key] = serializeValue(value[key], depth + 1, seen);
|
|
163
|
+
}
|
|
164
|
+
const symbolKeys = Object.getOwnPropertySymbols(value).slice(0, 10);
|
|
165
|
+
for (const sym of symbolKeys) {
|
|
166
|
+
obj['Symbol(' + sym.toString() + ')'] = serializeValue(value[sym], depth + 1, seen);
|
|
167
|
+
}
|
|
168
|
+
return obj;
|
|
169
|
+
} catch (e) {
|
|
170
|
+
return { __type: 'unserializable', error: String(e) };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Parse stack trace to extract file, line, column
|
|
176
|
+
*/
|
|
177
|
+
function parseStackTrace(stack) {
|
|
178
|
+
if (!stack) return null;
|
|
179
|
+
|
|
180
|
+
// Get the first non-console line from the stack
|
|
181
|
+
const lines = stack.split('\\n');
|
|
182
|
+
|
|
183
|
+
for (const line of lines) {
|
|
184
|
+
// Skip internal console-stream lines
|
|
185
|
+
if (line.includes('/_console') || line.includes('console-client')) continue;
|
|
186
|
+
|
|
187
|
+
// Match various stack trace formats
|
|
188
|
+
// Chrome: " at functionName (file:line:col)"
|
|
189
|
+
// Firefox: "functionName@file:line:col"
|
|
190
|
+
// Safari: "functionName@file:line:col"
|
|
191
|
+
|
|
192
|
+
const chromeMatch = line.match(/at\\s+(?:(.+?)\\s+\\()?(.+?):(\\d+):(\\d+)\\)?/);
|
|
193
|
+
if (chromeMatch) {
|
|
194
|
+
return {
|
|
195
|
+
file: chromeMatch[2],
|
|
196
|
+
line: parseInt(chromeMatch[3], 10),
|
|
197
|
+
column: parseInt(chromeMatch[4], 10)
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const firefoxMatch = line.match(/(?:([^@]+)@)?(.+?):(\\d+):(\\d+)/);
|
|
202
|
+
if (firefoxMatch) {
|
|
203
|
+
return {
|
|
204
|
+
file: firefoxMatch[2],
|
|
205
|
+
line: parseInt(firefoxMatch[3], 10),
|
|
206
|
+
column: parseInt(firefoxMatch[4], 10)
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Get caller location from stack trace
|
|
216
|
+
*/
|
|
217
|
+
function getCallerLocation() {
|
|
218
|
+
try {
|
|
219
|
+
throw new Error();
|
|
220
|
+
} catch (e) {
|
|
221
|
+
return parseStackTrace(e.stack);
|
|
222
|
+
}
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Send a console message to the server
|
|
228
|
+
*/
|
|
229
|
+
function sendConsoleMessage(type, args) {
|
|
230
|
+
const location = getCallerLocation();
|
|
231
|
+
|
|
232
|
+
const message = {
|
|
233
|
+
type: 'console',
|
|
234
|
+
consoleType: type,
|
|
235
|
+
args: Array.from(args).slice(0, MAX_ARGS_LENGTH).map(function(arg) {
|
|
236
|
+
return serializeValue(arg, 0, new WeakSet());
|
|
237
|
+
}),
|
|
238
|
+
timestamp: Date.now(),
|
|
239
|
+
url: window.location.href
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
// Add location info
|
|
243
|
+
if (location) {
|
|
244
|
+
message.file = location.file;
|
|
245
|
+
message.line = location.line;
|
|
246
|
+
message.column = location.column;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Add stack trace for errors
|
|
250
|
+
if (type === 'error' && args[0] instanceof Error) {
|
|
251
|
+
message.stack = args[0].stack;
|
|
252
|
+
}
|
|
253
|
+
if (type === 'trace') {
|
|
254
|
+
try {
|
|
255
|
+
message.stack = new Error().stack;
|
|
256
|
+
} catch (e) {}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Send or queue the message
|
|
260
|
+
if (isConnected && ws && ws.readyState === WebSocket.OPEN) {
|
|
261
|
+
ws.send(JSON.stringify(message));
|
|
262
|
+
} else {
|
|
263
|
+
messageQueue.push(message);
|
|
264
|
+
// Limit queue size
|
|
265
|
+
if (messageQueue.length > 100) {
|
|
266
|
+
messageQueue.shift();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Intercept console methods
|
|
273
|
+
*/
|
|
274
|
+
function interceptConsole() {
|
|
275
|
+
CONSOLE_METHODS.forEach(function(method) {
|
|
276
|
+
originalConsole[method] = console[method];
|
|
277
|
+
|
|
278
|
+
console[method] = function() {
|
|
279
|
+
// Call original method first
|
|
280
|
+
try {
|
|
281
|
+
originalConsole[method].apply(console, arguments);
|
|
282
|
+
} catch (e) {}
|
|
283
|
+
|
|
284
|
+
// Send to server
|
|
285
|
+
try {
|
|
286
|
+
sendConsoleMessage(method, arguments);
|
|
287
|
+
} catch (e) {}
|
|
288
|
+
};
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Restore original console methods
|
|
294
|
+
*/
|
|
295
|
+
function restoreConsole() {
|
|
296
|
+
CONSOLE_METHODS.forEach(function(method) {
|
|
297
|
+
if (originalConsole[method]) {
|
|
298
|
+
console[method] = originalConsole[method];
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Process queued messages
|
|
305
|
+
*/
|
|
306
|
+
function processQueue() {
|
|
307
|
+
while (messageQueue.length > 0 && ws && ws.readyState === WebSocket.OPEN) {
|
|
308
|
+
const message = messageQueue.shift();
|
|
309
|
+
ws.send(JSON.stringify(message));
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Connect to WebSocket server
|
|
315
|
+
*/
|
|
316
|
+
function connect() {
|
|
317
|
+
if (ws && (ws.readyState === WebSocket.CONNECTING || ws.readyState === WebSocket.OPEN)) {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
try {
|
|
322
|
+
ws = new WebSocket(WS_URL);
|
|
323
|
+
|
|
324
|
+
ws.onopen = function() {
|
|
325
|
+
isConnected = true;
|
|
326
|
+
reconnectAttempts = 0;
|
|
327
|
+
originalConsole.log && originalConsole.log('[Console Stream] Connected to server');
|
|
328
|
+
processQueue();
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
ws.onclose = function() {
|
|
332
|
+
isConnected = false;
|
|
333
|
+
ws = null;
|
|
334
|
+
|
|
335
|
+
// Attempt to reconnect
|
|
336
|
+
if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
|
|
337
|
+
reconnectAttempts++;
|
|
338
|
+
setTimeout(connect, RECONNECT_DELAY * reconnectAttempts);
|
|
339
|
+
} else {
|
|
340
|
+
originalConsole.warn && originalConsole.warn('[Console Stream] Max reconnection attempts reached');
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
ws.onerror = function(error) {
|
|
345
|
+
originalConsole.error && originalConsole.error('[Console Stream] WebSocket error', error);
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
ws.onmessage = function(event) {
|
|
349
|
+
try {
|
|
350
|
+
const data = JSON.parse(event.data);
|
|
351
|
+
if (data.type === 'connected') {
|
|
352
|
+
clientId = data.clientId;
|
|
353
|
+
}
|
|
354
|
+
} catch (e) {}
|
|
355
|
+
};
|
|
356
|
+
} catch (e) {
|
|
357
|
+
originalConsole.error && originalConsole.error('[Console Stream] Failed to connect', e);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Initialize console streaming
|
|
363
|
+
*/
|
|
364
|
+
function init() {
|
|
365
|
+
// Don't initialize in iframes unless specifically enabled
|
|
366
|
+
if (window !== window.top && !window.__CONSOLE_STREAM_ENABLED__) {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Don't initialize in service workers
|
|
371
|
+
if (typeof ServiceWorkerGlobalScope !== 'undefined' && self instanceof ServiceWorkerGlobalScope) {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
interceptConsole();
|
|
376
|
+
connect();
|
|
377
|
+
|
|
378
|
+
// Handle page unload
|
|
379
|
+
window.addEventListener('beforeunload', function() {
|
|
380
|
+
if (ws) {
|
|
381
|
+
ws.close();
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
// Handle visibility change for reconnection
|
|
386
|
+
document.addEventListener('visibilitychange', function() {
|
|
387
|
+
if (document.visibilityState === 'visible' && (!ws || ws.readyState !== WebSocket.OPEN)) {
|
|
388
|
+
connect();
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Start initialization
|
|
394
|
+
if (document.readyState === 'loading') {
|
|
395
|
+
document.addEventListener('DOMContentLoaded', init);
|
|
396
|
+
} else {
|
|
397
|
+
init();
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Expose for debugging
|
|
401
|
+
window.__CONSOLE_STREAM__ = {
|
|
402
|
+
connect: connect,
|
|
403
|
+
disconnect: function() {
|
|
404
|
+
if (ws) ws.close();
|
|
405
|
+
},
|
|
406
|
+
restore: restoreConsole,
|
|
407
|
+
getClientId: function() { return clientId; },
|
|
408
|
+
isConnected: function() { return isConnected; }
|
|
409
|
+
};
|
|
410
|
+
})();
|
|
411
|
+
`;
|
|
412
|
+
|
|
413
|
+
// Export a function to get the client script with custom port
|
|
414
|
+
export function getConsoleClientScript(port: number): string {
|
|
415
|
+
return CONSOLE_CLIENT_SCRIPT.replace(
|
|
416
|
+
/typeof CONSOLE_PORT !== 'undefined' \? CONSOLE_PORT : \d+/,
|
|
417
|
+
`typeof CONSOLE_PORT !== 'undefined' ? CONSOLE_PORT : ${port}`
|
|
418
|
+
);
|
|
419
|
+
}
|