hightjs 0.2.42 → 0.2.43
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/.idea/copilotDiffState.xml +67 -0
- package/README.md +52 -0
- package/dist/auth/core.js +3 -3
- package/dist/auth/index.d.ts +1 -1
- package/dist/auth/index.js +2 -1
- package/dist/auth/providers/google.d.ts +63 -0
- package/dist/auth/providers/google.js +186 -0
- package/dist/auth/providers.d.ts +1 -0
- package/dist/auth/providers.js +3 -1
- package/dist/auth/types.d.ts +6 -7
- package/dist/client/entry.client.js +11 -1
- package/dist/hightweb-global.d.ts +0 -0
- package/dist/hightweb-global.js +1 -0
- package/dist/hotReload.d.ts +8 -1
- package/dist/hotReload.js +304 -144
- package/dist/index.d.ts +2 -1
- package/dist/index.js +20 -33
- package/dist/renderer.js +1 -1
- package/dist/router.d.ts +24 -1
- package/dist/router.js +202 -2
- package/dist/ssl/selfSigned.d.ts +0 -0
- package/dist/ssl/selfSigned.js +1 -0
- package/dist/types/websocket.d.ts +27 -0
- package/dist/types/websocket.js +2 -0
- package/dist/types.d.ts +19 -1
- package/package.json +1 -1
- package/src/auth/core.ts +3 -3
- package/src/auth/index.ts +2 -3
- package/src/auth/providers/google.ts +218 -0
- package/src/auth/providers.ts +1 -1
- package/src/auth/types.ts +3 -8
- package/src/client/entry.client.tsx +12 -1
- package/src/hightweb-global.ts +1 -0
- package/src/hotReload.ts +333 -147
- package/src/index.ts +58 -51
- package/src/renderer.tsx +1 -1
- package/src/router.ts +231 -3
- package/src/ssl/selfSigned.ts +2 -0
- package/src/types.ts +24 -1
- package/src/auth/example.ts +0 -115
package/dist/hotReload.js
CHANGED
|
@@ -43,20 +43,29 @@ class HotReloadManager {
|
|
|
43
43
|
constructor(projectDir) {
|
|
44
44
|
this.wss = null;
|
|
45
45
|
this.watchers = [];
|
|
46
|
-
this.clients = new
|
|
47
|
-
this.pingInterval = null;
|
|
46
|
+
this.clients = new Map();
|
|
48
47
|
this.backendApiChangeCallback = null;
|
|
49
48
|
this.frontendChangeCallback = null;
|
|
49
|
+
this.isShuttingDown = false;
|
|
50
|
+
this.debounceTimers = new Map();
|
|
51
|
+
this.customHotReloadListener = null;
|
|
50
52
|
this.projectDir = projectDir;
|
|
51
53
|
}
|
|
52
54
|
async start() {
|
|
53
|
-
// Não cria servidor na porta separada - será integrado ao Express
|
|
54
55
|
this.setupWatchers();
|
|
55
56
|
}
|
|
56
|
-
//
|
|
57
|
+
// Método para integrar com Express
|
|
57
58
|
handleUpgrade(request, socket, head) {
|
|
59
|
+
if (this.isShuttingDown) {
|
|
60
|
+
socket.destroy();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
58
63
|
if (!this.wss) {
|
|
59
|
-
this.wss = new ws_1.WebSocketServer({
|
|
64
|
+
this.wss = new ws_1.WebSocketServer({
|
|
65
|
+
noServer: true,
|
|
66
|
+
perMessageDeflate: false, // Desabilita compressão para melhor performance
|
|
67
|
+
maxPayload: 1024 * 1024 // Limite de 1MB por mensagem
|
|
68
|
+
});
|
|
60
69
|
this.setupWebSocketServer();
|
|
61
70
|
}
|
|
62
71
|
this.wss.handleUpgrade(request, socket, head, (ws) => {
|
|
@@ -67,179 +76,317 @@ class HotReloadManager {
|
|
|
67
76
|
if (!this.wss)
|
|
68
77
|
return;
|
|
69
78
|
this.wss.on('connection', (ws) => {
|
|
70
|
-
this.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
79
|
+
if (this.isShuttingDown) {
|
|
80
|
+
ws.close();
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// Setup ping/pong para detectar conexões mortas
|
|
84
|
+
const pingTimer = setInterval(() => {
|
|
85
|
+
const client = this.clients.get(ws);
|
|
86
|
+
if (client && ws.readyState === ws_1.WebSocket.OPEN) {
|
|
87
|
+
// Se não recebeu pong há mais de 60 segundos, desconecta
|
|
88
|
+
if (Date.now() - client.lastPong > 60000) {
|
|
89
|
+
ws.terminate();
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
74
92
|
ws.ping();
|
|
75
93
|
}
|
|
94
|
+
}, 30000);
|
|
95
|
+
const clientConnection = {
|
|
96
|
+
ws,
|
|
97
|
+
pingTimer,
|
|
98
|
+
lastPong: Date.now()
|
|
76
99
|
};
|
|
77
|
-
|
|
100
|
+
this.clients.set(ws, clientConnection);
|
|
78
101
|
ws.on('pong', () => {
|
|
79
|
-
|
|
102
|
+
const client = this.clients.get(ws);
|
|
103
|
+
if (client) {
|
|
104
|
+
client.lastPong = Date.now();
|
|
105
|
+
}
|
|
80
106
|
});
|
|
81
107
|
ws.on('close', () => {
|
|
82
|
-
this.
|
|
83
|
-
clearInterval(pingTimer);
|
|
108
|
+
this.cleanupClient(ws);
|
|
84
109
|
});
|
|
85
|
-
ws.on('error', () => {
|
|
86
|
-
|
|
87
|
-
|
|
110
|
+
ws.on('error', (error) => {
|
|
111
|
+
console_1.default.logWithout(console_1.Levels.ERROR, `WebSocket error: ${error.message}`);
|
|
112
|
+
this.cleanupClient(ws);
|
|
88
113
|
});
|
|
114
|
+
console_1.default.logWithout(console_1.Levels.INFO, '🔌 Hot-reload cliente conectado');
|
|
89
115
|
});
|
|
90
116
|
}
|
|
117
|
+
cleanupClient(ws) {
|
|
118
|
+
const client = this.clients.get(ws);
|
|
119
|
+
if (client) {
|
|
120
|
+
clearInterval(client.pingTimer);
|
|
121
|
+
this.clients.delete(ws);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
91
124
|
setupWatchers() {
|
|
92
|
-
//
|
|
93
|
-
const
|
|
94
|
-
|
|
125
|
+
// Remove watchers antigos e use apenas um watcher global para src
|
|
126
|
+
const debouncedChange = this.debounce((filePath) => {
|
|
127
|
+
this.handleAnySrcChange(filePath);
|
|
128
|
+
}, 100);
|
|
129
|
+
const watcher = chokidar.watch([
|
|
130
|
+
path.join(this.projectDir, 'src/**/*'),
|
|
95
131
|
], {
|
|
96
132
|
ignored: [
|
|
97
133
|
/(^|[\/\\])\../, // arquivos ocultos
|
|
98
|
-
|
|
134
|
+
'**/node_modules/**',
|
|
135
|
+
'**/.git/**',
|
|
136
|
+
'**/dist/**'
|
|
99
137
|
],
|
|
100
138
|
persistent: true,
|
|
101
|
-
ignoreInitial: true
|
|
139
|
+
ignoreInitial: true,
|
|
140
|
+
usePolling: false,
|
|
141
|
+
awaitWriteFinish: {
|
|
142
|
+
stabilityThreshold: 100,
|
|
143
|
+
pollInterval: 50
|
|
144
|
+
}
|
|
102
145
|
});
|
|
103
|
-
|
|
104
|
-
|
|
146
|
+
watcher.on('change', debouncedChange);
|
|
147
|
+
watcher.on('add', debouncedChange);
|
|
148
|
+
watcher.on('unlink', (filePath) => {
|
|
149
|
+
console_1.default.info(`🗑️ Arquivo removido: ${path.basename(filePath)}`);
|
|
105
150
|
(0, router_1.clearFileCache)(filePath);
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
this.frontendChangeCallback?.();
|
|
113
|
-
this.notifyClients('frontend-reload');
|
|
114
|
-
}
|
|
151
|
+
this.clearBackendCache(filePath);
|
|
152
|
+
this.frontendChangeCallback?.();
|
|
153
|
+
this.backendApiChangeCallback?.();
|
|
154
|
+
this.notifyClients('src-reload', { file: filePath, event: 'unlink' });
|
|
115
155
|
});
|
|
116
|
-
|
|
117
|
-
|
|
156
|
+
this.watchers.push(watcher);
|
|
157
|
+
}
|
|
158
|
+
debounce(func, wait) {
|
|
159
|
+
return (...args) => {
|
|
160
|
+
const key = args[0]; // usa o primeiro argumento como chave
|
|
161
|
+
const existingTimer = this.debounceTimers.get(key);
|
|
162
|
+
if (existingTimer) {
|
|
163
|
+
clearTimeout(existingTimer);
|
|
164
|
+
}
|
|
165
|
+
const timer = setTimeout(() => {
|
|
166
|
+
this.debounceTimers.delete(key);
|
|
167
|
+
func.apply(this, args);
|
|
168
|
+
}, wait);
|
|
169
|
+
this.debounceTimers.set(key, timer);
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
async handleAnySrcChange(filePath) {
|
|
173
|
+
console_1.default.logWithout(console_1.Levels.INFO, `🔄 Arquivo alterado: ${path.basename(filePath)}`);
|
|
174
|
+
// Detecta se é arquivo de frontend ou backend
|
|
175
|
+
const isFrontendFile = filePath.includes(path.join('src', 'web', 'routes')) ||
|
|
176
|
+
filePath.includes(path.join('src', 'web', 'components')) ||
|
|
177
|
+
filePath.includes('layout.tsx') ||
|
|
178
|
+
filePath.includes('not-found.tsx') ||
|
|
179
|
+
filePath.endsWith('.tsx') ||
|
|
180
|
+
filePath.endsWith('.jsx');
|
|
181
|
+
const isBackendFile = filePath.includes(path.join('src', 'web', 'backend')) ||
|
|
182
|
+
(filePath.includes(path.join('src', 'web')) && !isFrontendFile);
|
|
183
|
+
// Limpa o cache do arquivo alterado
|
|
184
|
+
(0, router_1.clearFileCache)(filePath);
|
|
185
|
+
this.clearBackendCache(filePath);
|
|
186
|
+
// Checa build se for .ts/.tsx/.js/.jsx
|
|
187
|
+
const ext = path.extname(filePath);
|
|
188
|
+
if ([".ts", ".tsx", ".js", ".jsx"].includes(ext)) {
|
|
118
189
|
const result = await this.checkFrontendBuild(filePath);
|
|
119
190
|
if (result.error) {
|
|
120
|
-
this.notifyClients('
|
|
191
|
+
this.notifyClients('src-error', { file: filePath, error: result.error });
|
|
192
|
+
return;
|
|
121
193
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
});
|
|
127
|
-
frontendWatcher.on('unlink', (filePath) => {
|
|
128
|
-
console_1.default.info(`🗑️ Arquivo frontend removido: ${path.basename(filePath)}`);
|
|
129
|
-
(0, router_1.clearFileCache)(filePath);
|
|
194
|
+
}
|
|
195
|
+
// Se for arquivo de frontend, notifica o cliente para recarregar a página
|
|
196
|
+
if (isFrontendFile) {
|
|
197
|
+
console_1.default.logWithout(console_1.Levels.INFO, `📄 Recarregando frontend...`);
|
|
130
198
|
this.frontendChangeCallback?.();
|
|
131
|
-
this.notifyClients('frontend-reload');
|
|
132
|
-
}
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
], {
|
|
137
|
-
ignored: /(^|[\/\\])\../,
|
|
138
|
-
persistent: true,
|
|
139
|
-
ignoreInitial: true
|
|
140
|
-
});
|
|
141
|
-
backendApiWatcher.on('change', (filePath) => {
|
|
142
|
-
console_1.default.info(`🔄 API backend alterada: ${path.basename(filePath)}`);
|
|
143
|
-
this.clearBackendCache(filePath);
|
|
144
|
-
this.notifyClients('backend-api-reload');
|
|
145
|
-
// Chama o callback, se definido
|
|
199
|
+
this.notifyClients('frontend-reload', { file: filePath, event: 'change' });
|
|
200
|
+
}
|
|
201
|
+
// Se for arquivo de backend, recarrega o módulo e notifica
|
|
202
|
+
if (isBackendFile) {
|
|
203
|
+
console_1.default.logWithout(console_1.Levels.INFO, `⚙️ Recarregando backend...`);
|
|
146
204
|
this.backendApiChangeCallback?.();
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
this.
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
});
|
|
167
|
-
backendWatcher.on('change', () => {
|
|
168
|
-
this.restartServer();
|
|
169
|
-
});
|
|
170
|
-
this.watchers.push(frontendWatcher, backendApiWatcher, backendWatcher);
|
|
205
|
+
this.notifyClients('backend-api-reload', { file: filePath, event: 'change' });
|
|
206
|
+
}
|
|
207
|
+
// Fallback: se não for nem frontend nem backend detectado, recarrega tudo
|
|
208
|
+
if (!isFrontendFile && !isBackendFile) {
|
|
209
|
+
console_1.default.logWithout(console_1.Levels.INFO, `🔄 Recarregando aplicação...`);
|
|
210
|
+
this.frontendChangeCallback?.();
|
|
211
|
+
this.backendApiChangeCallback?.();
|
|
212
|
+
this.notifyClients('src-reload', { file: filePath, event: 'change' });
|
|
213
|
+
}
|
|
214
|
+
// Chama listener customizado se definido
|
|
215
|
+
if (this.customHotReloadListener) {
|
|
216
|
+
try {
|
|
217
|
+
await this.customHotReloadListener(filePath);
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
// @ts-ignore
|
|
221
|
+
console_1.default.logWithout(console_1.Levels.ERROR, `Erro no listener customizado: ${error.message}`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
171
224
|
}
|
|
172
225
|
notifyClients(type, data) {
|
|
226
|
+
if (this.isShuttingDown || this.clients.size === 0) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
173
229
|
const message = JSON.stringify({ type, data, timestamp: Date.now() });
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
230
|
+
const deadClients = [];
|
|
231
|
+
this.clients.forEach((client, ws) => {
|
|
232
|
+
if (ws.readyState === ws_1.WebSocket.OPEN) {
|
|
233
|
+
try {
|
|
234
|
+
ws.send(message);
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
console_1.default.logWithout(console_1.Levels.ERROR, `Erro ao enviar mensagem WebSocket: ${error}`);
|
|
238
|
+
deadClients.push(ws);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
deadClients.push(ws);
|
|
177
243
|
}
|
|
178
244
|
});
|
|
245
|
+
// Remove clientes mortos
|
|
246
|
+
deadClients.forEach(ws => this.cleanupClient(ws));
|
|
179
247
|
}
|
|
180
248
|
restartServer() {
|
|
181
|
-
// Notifica clientes que o servidor está reiniciando
|
|
182
249
|
this.notifyClients('server-restart');
|
|
183
|
-
// Aguarda um pouco e tenta reconectar
|
|
184
250
|
setTimeout(() => {
|
|
185
251
|
this.notifyClients('server-ready');
|
|
186
252
|
}, 2000);
|
|
187
253
|
}
|
|
188
254
|
stop() {
|
|
255
|
+
this.isShuttingDown = true;
|
|
256
|
+
// Limpa todos os debounce timers
|
|
257
|
+
this.debounceTimers.forEach(timer => clearTimeout(timer));
|
|
258
|
+
this.debounceTimers.clear();
|
|
189
259
|
// Para todos os watchers
|
|
190
260
|
this.watchers.forEach(watcher => watcher.close());
|
|
191
261
|
this.watchers = [];
|
|
192
|
-
//
|
|
193
|
-
|
|
194
|
-
clearInterval(
|
|
195
|
-
|
|
262
|
+
// Limpa todos os clientes
|
|
263
|
+
this.clients.forEach((client, ws) => {
|
|
264
|
+
clearInterval(client.pingTimer);
|
|
265
|
+
if (ws.readyState === ws_1.WebSocket.OPEN) {
|
|
266
|
+
ws.close();
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
this.clients.clear();
|
|
196
270
|
// Fecha WebSocket server
|
|
197
271
|
if (this.wss) {
|
|
198
272
|
this.wss.close();
|
|
273
|
+
this.wss = null;
|
|
199
274
|
}
|
|
200
275
|
}
|
|
201
|
-
//
|
|
276
|
+
// Script do cliente otimizado com reconnection backoff
|
|
202
277
|
getClientScript() {
|
|
203
278
|
return `
|
|
204
279
|
<script>
|
|
205
280
|
(function() {
|
|
206
281
|
if (typeof window !== 'undefined') {
|
|
207
282
|
let ws;
|
|
208
|
-
let
|
|
283
|
+
let reconnectAttempts = 0;
|
|
284
|
+
let maxReconnectInterval = 30000; // 30 segundos max
|
|
285
|
+
let reconnectInterval = 1000; // Começa com 1 segundo
|
|
286
|
+
let reconnectTimer;
|
|
287
|
+
let isConnected = false;
|
|
209
288
|
|
|
210
289
|
function connect() {
|
|
211
|
-
|
|
290
|
+
// Evita múltiplas tentativas simultâneas
|
|
291
|
+
if (ws && (ws.readyState === WebSocket.CONNECTING || ws.readyState === WebSocket.OPEN)) {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
try {
|
|
296
|
+
ws = new WebSocket('ws://localhost:3000/hweb-hotreload/');
|
|
297
|
+
|
|
298
|
+
ws.onopen = function() {
|
|
299
|
+
console.log('🔌 Hot-reload conectado');
|
|
300
|
+
isConnected = true;
|
|
301
|
+
reconnectAttempts = 0;
|
|
302
|
+
reconnectInterval = 1000;
|
|
303
|
+
clearTimeout(reconnectTimer);
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
ws.onmessage = function(event) {
|
|
307
|
+
try {
|
|
308
|
+
const message = JSON.parse(event.data);
|
|
309
|
+
|
|
310
|
+
switch(message.type) {
|
|
311
|
+
case 'frontend-reload':
|
|
312
|
+
window.location.reload();
|
|
313
|
+
break;
|
|
314
|
+
case 'backend-api-reload':
|
|
315
|
+
// Recarrega apenas se necessário
|
|
316
|
+
window.location.reload();
|
|
317
|
+
break;
|
|
318
|
+
case 'server-restart':
|
|
319
|
+
console.log('🔄 Servidor reiniciando...');
|
|
320
|
+
break;
|
|
321
|
+
case 'server-ready':
|
|
322
|
+
setTimeout(() => window.location.reload(), 500);
|
|
323
|
+
break;
|
|
324
|
+
case 'frontend-error':
|
|
325
|
+
console.error('❌ Erro no frontend:', message.data);
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
} catch (e) {
|
|
329
|
+
console.error('Erro ao processar mensagem do hot-reload:', e);
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
ws.onclose = function(event) {
|
|
334
|
+
isConnected = false;
|
|
335
|
+
|
|
336
|
+
// Não tenta reconectar se foi fechamento intencional
|
|
337
|
+
if (event.code === 1000) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
scheduleReconnect();
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
ws.onerror = function(error) {
|
|
345
|
+
isConnected = false;
|
|
346
|
+
// Não loga erros de conexão para evitar spam no console
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
} catch (error) {
|
|
350
|
+
console.error('Erro ao criar WebSocket:', error);
|
|
351
|
+
scheduleReconnect();
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function scheduleReconnect() {
|
|
356
|
+
if (reconnectTimer) {
|
|
357
|
+
clearTimeout(reconnectTimer);
|
|
358
|
+
}
|
|
212
359
|
|
|
213
|
-
|
|
214
|
-
clearInterval(reconnectInterval);
|
|
215
|
-
};
|
|
360
|
+
reconnectAttempts++;
|
|
216
361
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
case 'frontend-reload':
|
|
222
|
-
window.location.reload();
|
|
223
|
-
break;
|
|
224
|
-
case 'server-restart':
|
|
225
|
-
break;
|
|
226
|
-
case 'server-ready':
|
|
227
|
-
setTimeout(() => window.location.reload(), 500);
|
|
228
|
-
break;
|
|
229
|
-
}
|
|
230
|
-
};
|
|
362
|
+
// Exponential backoff com jitter
|
|
363
|
+
const baseInterval = Math.min(reconnectInterval * Math.pow(1.5, reconnectAttempts - 1), maxReconnectInterval);
|
|
364
|
+
const jitter = Math.random() * 1000; // Adiciona até 1 segundo de variação
|
|
365
|
+
const finalInterval = baseInterval + jitter;
|
|
231
366
|
|
|
232
|
-
|
|
233
|
-
|
|
367
|
+
reconnectTimer = setTimeout(() => {
|
|
368
|
+
if (!isConnected) {
|
|
234
369
|
connect();
|
|
235
|
-
}
|
|
236
|
-
};
|
|
237
|
-
|
|
238
|
-
ws.onerror = function() {
|
|
239
|
-
// Silencioso - sem logs
|
|
240
|
-
};
|
|
370
|
+
}
|
|
371
|
+
}, finalInterval);
|
|
241
372
|
}
|
|
242
373
|
|
|
374
|
+
// Detecta quando a página está sendo fechada para evitar reconexões desnecessárias
|
|
375
|
+
window.addEventListener('beforeunload', function() {
|
|
376
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
377
|
+
ws.close(1000, 'Page unloading');
|
|
378
|
+
}
|
|
379
|
+
clearTimeout(reconnectTimer);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
// Detecta quando a aba fica visível novamente para reconectar se necessário
|
|
383
|
+
document.addEventListener('visibilitychange', function() {
|
|
384
|
+
if (!document.hidden && !isConnected) {
|
|
385
|
+
reconnectAttempts = 0; // Reset do contador quando a aba fica ativa
|
|
386
|
+
connect();
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
|
|
243
390
|
connect();
|
|
244
391
|
}
|
|
245
392
|
})();
|
|
@@ -247,46 +394,59 @@ class HotReloadManager {
|
|
|
247
394
|
`;
|
|
248
395
|
}
|
|
249
396
|
clearBackendCache(filePath) {
|
|
250
|
-
// Limpa o cache do require para forçar reload da rota de API
|
|
251
397
|
const absolutePath = path.resolve(filePath);
|
|
252
398
|
delete require.cache[absolutePath];
|
|
253
|
-
//
|
|
399
|
+
// Limpa dependências relacionadas de forma mais eficiente
|
|
400
|
+
const dirname = path.dirname(absolutePath);
|
|
254
401
|
Object.keys(require.cache).forEach(key => {
|
|
255
|
-
if (key.
|
|
402
|
+
if (key.startsWith(dirname)) {
|
|
256
403
|
delete require.cache[key];
|
|
257
404
|
}
|
|
258
405
|
});
|
|
259
406
|
}
|
|
260
|
-
// Método para registrar callback de mudança de API backend
|
|
261
407
|
onBackendApiChange(callback) {
|
|
262
408
|
this.backendApiChangeCallback = callback;
|
|
263
409
|
}
|
|
264
|
-
// Método para registrar callback de mudança de frontend
|
|
265
410
|
onFrontendChange(callback) {
|
|
266
411
|
this.frontendChangeCallback = callback;
|
|
267
412
|
}
|
|
413
|
+
setHotReloadListener(listener) {
|
|
414
|
+
this.customHotReloadListener = listener;
|
|
415
|
+
console_1.default.info('🔌 Hot reload listener customizado registrado');
|
|
416
|
+
}
|
|
417
|
+
removeHotReloadListener() {
|
|
418
|
+
this.customHotReloadListener = null;
|
|
419
|
+
}
|
|
268
420
|
async checkFrontendBuild(filePath) {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
421
|
+
try {
|
|
422
|
+
const tsNodePath = require.resolve('ts-node');
|
|
423
|
+
const { spawn } = require('child_process');
|
|
424
|
+
return new Promise((resolve) => {
|
|
425
|
+
const proc = spawn(process.execPath, [tsNodePath, '--transpile-only', filePath], {
|
|
426
|
+
cwd: this.projectDir,
|
|
427
|
+
env: process.env,
|
|
428
|
+
timeout: 10000 // Timeout de 10 segundos
|
|
429
|
+
});
|
|
430
|
+
let errorMsg = '';
|
|
431
|
+
proc.stderr.on('data', (data) => {
|
|
432
|
+
errorMsg += data.toString();
|
|
433
|
+
});
|
|
434
|
+
proc.on('close', (code) => {
|
|
435
|
+
if (code !== 0 && errorMsg) {
|
|
436
|
+
resolve({ error: errorMsg });
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
resolve({});
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
proc.on('error', (error) => {
|
|
443
|
+
resolve({ error: error.message });
|
|
444
|
+
});
|
|
280
445
|
});
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
else {
|
|
286
|
-
resolve({});
|
|
287
|
-
}
|
|
288
|
-
});
|
|
289
|
-
});
|
|
446
|
+
}
|
|
447
|
+
catch (error) {
|
|
448
|
+
return { error: `Erro ao verificar build: ${error}` };
|
|
449
|
+
}
|
|
290
450
|
}
|
|
291
451
|
}
|
|
292
452
|
exports.HotReloadManager = HotReloadManager;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BackendHandler, BackendRouteConfig, HightJSOptions, RequestHandler } from './types';
|
|
2
2
|
import { HightJSRequest, HightJSResponse } from './api/http';
|
|
3
3
|
export { HightJSRequest, HightJSResponse };
|
|
4
4
|
export type { BackendRouteConfig, BackendHandler };
|
|
@@ -7,6 +7,7 @@ export { FastifyAdapter } from './adapters/fastify';
|
|
|
7
7
|
export { FrameworkAdapterFactory } from './adapters/factory';
|
|
8
8
|
export type { GenericRequest, GenericResponse, CookieOptions } from './types/framework';
|
|
9
9
|
export { app } from './helpers';
|
|
10
|
+
export type { WebSocketContext, WebSocketHandler } from './types';
|
|
10
11
|
export default function hweb(options: HightJSOptions): {
|
|
11
12
|
prepare: () => Promise<void>;
|
|
12
13
|
executeInstrumentation: () => void;
|