@zap-js/client 0.0.2 → 0.0.4
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 +310 -24
- package/bin/zap +0 -0
- package/bin/zap-codegen +0 -0
- package/dist/cli/commands/build.d.ts +11 -0
- package/dist/cli/commands/build.js +282 -0
- package/dist/cli/commands/codegen.d.ts +8 -0
- package/dist/cli/commands/codegen.js +95 -0
- package/dist/cli/commands/dev.d.ts +20 -0
- package/dist/cli/commands/dev.js +78 -0
- package/dist/cli/commands/new.d.ts +9 -0
- package/dist/cli/commands/new.js +307 -0
- package/dist/cli/commands/routes-old.d.ts +9 -0
- package/dist/cli/commands/routes-old.js +106 -0
- package/dist/cli/commands/routes.d.ts +11 -0
- package/dist/cli/commands/routes.js +280 -0
- package/dist/cli/commands/serve.d.ts +17 -0
- package/dist/cli/commands/serve.js +386 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +76 -0
- package/dist/cli/utils/index.d.ts +2 -0
- package/dist/cli/utils/index.js +2 -0
- package/dist/cli/utils/logger.d.ts +84 -0
- package/dist/cli/utils/logger.js +181 -0
- package/dist/cli/utils/port-finder.d.ts +8 -0
- package/dist/cli/utils/port-finder.js +48 -0
- package/dist/dev-server/codegen-runner.d.ts +41 -0
- package/dist/dev-server/codegen-runner.js +172 -0
- package/dist/dev-server/hot-reload.d.ts +72 -0
- package/dist/dev-server/hot-reload.js +280 -0
- package/dist/dev-server/index.d.ts +8 -0
- package/dist/dev-server/index.js +8 -0
- package/dist/dev-server/route-scanner.d.ts +71 -0
- package/dist/dev-server/route-scanner.js +114 -0
- package/dist/dev-server/rust-builder.d.ts +66 -0
- package/dist/dev-server/rust-builder.js +286 -0
- package/dist/dev-server/server.d.ts +147 -0
- package/dist/dev-server/server.js +658 -0
- package/dist/dev-server/vite-proxy.d.ts +56 -0
- package/dist/dev-server/vite-proxy.js +212 -0
- package/dist/dev-server/watcher.d.ts +48 -0
- package/dist/dev-server/watcher.js +127 -0
- package/dist/router/codegen-enhanced.d.ts +5 -0
- package/dist/router/codegen-enhanced.js +275 -0
- package/dist/router/codegen.d.ts +17 -0
- package/dist/router/codegen.js +654 -0
- package/dist/router/index.d.ts +16 -0
- package/dist/router/index.js +19 -0
- package/dist/router/scanner.d.ts +86 -0
- package/dist/router/scanner.js +689 -0
- package/dist/router/ssg.d.ts +115 -0
- package/dist/router/ssg.js +202 -0
- package/dist/router/types.d.ts +124 -0
- package/dist/router/types.js +9 -0
- package/dist/router/watch.d.ts +38 -0
- package/dist/router/watch.js +135 -0
- package/dist/runtime/csrf.d.ts +146 -0
- package/dist/runtime/csrf.js +166 -0
- package/dist/runtime/error-boundary.d.ts +129 -0
- package/dist/runtime/error-boundary.js +287 -0
- package/dist/runtime/hooks.d.ts +83 -0
- package/dist/runtime/hooks.js +96 -0
- package/dist/runtime/index.d.ts +229 -0
- package/dist/runtime/index.js +449 -0
- package/dist/runtime/ipc-client.d.ts +144 -0
- package/dist/runtime/ipc-client.js +621 -0
- package/dist/runtime/logger.d.ts +71 -0
- package/dist/runtime/logger.js +164 -0
- package/dist/runtime/middleware.d.ts +66 -0
- package/dist/runtime/middleware.js +114 -0
- package/dist/runtime/process-manager.d.ts +51 -0
- package/dist/runtime/process-manager.js +207 -0
- package/dist/runtime/router-simple.d.ts +98 -0
- package/dist/runtime/router-simple.js +330 -0
- package/dist/runtime/router.d.ts +103 -0
- package/dist/runtime/router.js +435 -0
- package/dist/runtime/rpc-client.d.ts +35 -0
- package/dist/runtime/rpc-client.js +140 -0
- package/dist/runtime/streaming-utils.d.ts +86 -0
- package/dist/runtime/streaming-utils.js +150 -0
- package/dist/runtime/types.d.ts +465 -0
- package/dist/runtime/types.js +60 -0
- package/dist/runtime/websockets-utils.d.ts +50 -0
- package/dist/runtime/websockets-utils.js +92 -0
- package/package.json +30 -20
- package/index.js +0 -29
- package/internal/cli/package.json +0 -46
- package/internal/cli/tsconfig.tsbuildinfo +0 -1
- package/internal/dev-server/node_modules/ora/index.d.ts +0 -332
- package/internal/dev-server/node_modules/ora/index.js +0 -416
- package/internal/dev-server/node_modules/ora/license +0 -9
- package/internal/dev-server/node_modules/ora/node_modules/string-width/index.d.ts +0 -36
- package/internal/dev-server/node_modules/ora/node_modules/string-width/index.js +0 -65
- package/internal/dev-server/node_modules/ora/node_modules/string-width/license +0 -9
- package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/LICENSE-MIT.txt +0 -20
- package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/README.md +0 -107
- package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/index.d.ts +0 -3
- package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/index.js +0 -4
- package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/index.mjs +0 -4
- package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/package.json +0 -46
- package/internal/dev-server/node_modules/ora/node_modules/string-width/package.json +0 -60
- package/internal/dev-server/node_modules/ora/node_modules/string-width/readme.md +0 -62
- package/internal/dev-server/node_modules/ora/package.json +0 -66
- package/internal/dev-server/node_modules/ora/readme.md +0 -325
- package/internal/dev-server/package.json +0 -41
- package/internal/router/package.json +0 -28
- package/internal/runtime/package.json +0 -41
- package/internal/runtime/src/error-boundary.tsx +0 -476
- package/internal/runtime/src/router-simple.tsx +0 -640
- package/internal/runtime/src/router.tsx +0 -771
- package/internal/runtime/tsconfig.tsbuildinfo +0 -1
- package/src/errors.js +0 -33
- package/src/logger.js +0 -10
- package/src/middleware.js +0 -32
- package/src/router.js +0 -41
- package/src/types.js +0 -39
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { WebSocketServer, WebSocket } from 'ws';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import http from 'http';
|
|
4
|
+
/**
|
|
5
|
+
* HotReloadServer - WebSocket server for hot reload signaling
|
|
6
|
+
*
|
|
7
|
+
* Broadcasts reload signals to connected clients:
|
|
8
|
+
* - Full page reload for Rust changes
|
|
9
|
+
* - Partial reload for TypeScript changes (Vite HMR handles this)
|
|
10
|
+
* - Config reload notifications
|
|
11
|
+
*/
|
|
12
|
+
export class HotReloadServer extends EventEmitter {
|
|
13
|
+
constructor(config = {}) {
|
|
14
|
+
super();
|
|
15
|
+
this.wss = null;
|
|
16
|
+
this.httpServer = null;
|
|
17
|
+
this.clients = new Set();
|
|
18
|
+
this.config = {
|
|
19
|
+
port: 3001,
|
|
20
|
+
host: '127.0.0.1',
|
|
21
|
+
...config,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Start the hot reload WebSocket server
|
|
26
|
+
*/
|
|
27
|
+
async start() {
|
|
28
|
+
return new Promise((resolve, reject) => {
|
|
29
|
+
this.httpServer = http.createServer((req, res) => {
|
|
30
|
+
// Simple health check endpoint
|
|
31
|
+
if (req.url === '/health') {
|
|
32
|
+
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
33
|
+
res.end('OK');
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
// Serve a simple client script
|
|
37
|
+
if (req.url === '/client.js') {
|
|
38
|
+
res.writeHead(200, { 'Content-Type': 'application/javascript' });
|
|
39
|
+
res.end(this.getClientScript());
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
res.writeHead(404);
|
|
43
|
+
res.end();
|
|
44
|
+
});
|
|
45
|
+
this.wss = new WebSocketServer({ server: this.httpServer });
|
|
46
|
+
this.wss.on('connection', (ws, req) => {
|
|
47
|
+
this.clients.add(ws);
|
|
48
|
+
this.emit('client-connected', req.socket.remoteAddress);
|
|
49
|
+
// Send welcome message
|
|
50
|
+
this.send(ws, {
|
|
51
|
+
type: 'connected',
|
|
52
|
+
message: 'Connected to ZapRS hot reload server',
|
|
53
|
+
timestamp: Date.now(),
|
|
54
|
+
});
|
|
55
|
+
ws.on('close', () => {
|
|
56
|
+
this.clients.delete(ws);
|
|
57
|
+
this.emit('client-disconnected');
|
|
58
|
+
});
|
|
59
|
+
ws.on('error', (err) => {
|
|
60
|
+
this.emit('client-error', err);
|
|
61
|
+
this.clients.delete(ws);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
this.wss.on('error', (err) => {
|
|
65
|
+
this.emit('error', err);
|
|
66
|
+
reject(err);
|
|
67
|
+
});
|
|
68
|
+
this.httpServer.listen(this.config.port, this.config.host, () => {
|
|
69
|
+
this.emit('ready', this.config.port);
|
|
70
|
+
resolve();
|
|
71
|
+
});
|
|
72
|
+
this.httpServer.on('error', reject);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Stop the server
|
|
77
|
+
*/
|
|
78
|
+
async stop() {
|
|
79
|
+
// Close all client connections
|
|
80
|
+
for (const client of this.clients) {
|
|
81
|
+
client.close(1000, 'Server shutting down');
|
|
82
|
+
}
|
|
83
|
+
this.clients.clear();
|
|
84
|
+
// Close WebSocket server
|
|
85
|
+
if (this.wss) {
|
|
86
|
+
await new Promise((resolve) => {
|
|
87
|
+
this.wss.close(() => resolve());
|
|
88
|
+
});
|
|
89
|
+
this.wss = null;
|
|
90
|
+
}
|
|
91
|
+
// Close HTTP server
|
|
92
|
+
if (this.httpServer) {
|
|
93
|
+
await new Promise((resolve) => {
|
|
94
|
+
this.httpServer.close(() => resolve());
|
|
95
|
+
});
|
|
96
|
+
this.httpServer = null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Broadcast a reload signal to all clients
|
|
101
|
+
*/
|
|
102
|
+
reload(target, files) {
|
|
103
|
+
const message = {
|
|
104
|
+
type: 'reload',
|
|
105
|
+
target,
|
|
106
|
+
files,
|
|
107
|
+
timestamp: Date.now(),
|
|
108
|
+
};
|
|
109
|
+
this.broadcast(message);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Broadcast an update signal (partial reload)
|
|
113
|
+
*/
|
|
114
|
+
update(files) {
|
|
115
|
+
const message = {
|
|
116
|
+
type: 'update',
|
|
117
|
+
files,
|
|
118
|
+
timestamp: Date.now(),
|
|
119
|
+
};
|
|
120
|
+
this.broadcast(message);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Broadcast an error message
|
|
124
|
+
*/
|
|
125
|
+
notifyError(errorMessage) {
|
|
126
|
+
const message = {
|
|
127
|
+
type: 'error',
|
|
128
|
+
message: errorMessage,
|
|
129
|
+
timestamp: Date.now(),
|
|
130
|
+
};
|
|
131
|
+
this.broadcast(message);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Broadcast a message to all connected clients
|
|
135
|
+
*/
|
|
136
|
+
broadcast(message) {
|
|
137
|
+
const json = JSON.stringify(message);
|
|
138
|
+
for (const client of this.clients) {
|
|
139
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
140
|
+
client.send(json);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
this.emit('broadcast', message);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Send a message to a specific client
|
|
147
|
+
*/
|
|
148
|
+
send(ws, message) {
|
|
149
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
150
|
+
ws.send(JSON.stringify(message));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Get the number of connected clients
|
|
155
|
+
*/
|
|
156
|
+
getClientCount() {
|
|
157
|
+
return this.clients.size;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Get the hot reload server URL
|
|
161
|
+
*/
|
|
162
|
+
getUrl() {
|
|
163
|
+
return `ws://${this.config.host}:${this.config.port}`;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Get the client script for browser injection
|
|
167
|
+
*/
|
|
168
|
+
getClientScript() {
|
|
169
|
+
return `
|
|
170
|
+
(function() {
|
|
171
|
+
const WS_URL = 'ws://${this.config.host}:${this.config.port}';
|
|
172
|
+
let ws;
|
|
173
|
+
let reconnectAttempts = 0;
|
|
174
|
+
const maxReconnectAttempts = 10;
|
|
175
|
+
|
|
176
|
+
function connect() {
|
|
177
|
+
ws = new WebSocket(WS_URL);
|
|
178
|
+
|
|
179
|
+
ws.onopen = function() {
|
|
180
|
+
console.log('[ZapRS] Hot reload connected');
|
|
181
|
+
reconnectAttempts = 0;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
ws.onmessage = function(event) {
|
|
185
|
+
try {
|
|
186
|
+
const data = JSON.parse(event.data);
|
|
187
|
+
handleMessage(data);
|
|
188
|
+
} catch (e) {
|
|
189
|
+
console.error('[ZapRS] Failed to parse message:', e);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
ws.onclose = function() {
|
|
194
|
+
console.log('[ZapRS] Hot reload disconnected');
|
|
195
|
+
attemptReconnect();
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
ws.onerror = function(err) {
|
|
199
|
+
console.error('[ZapRS] WebSocket error:', err);
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function handleMessage(data) {
|
|
204
|
+
switch (data.type) {
|
|
205
|
+
case 'connected':
|
|
206
|
+
console.log('[ZapRS]', data.message);
|
|
207
|
+
break;
|
|
208
|
+
case 'reload':
|
|
209
|
+
console.log('[ZapRS] Reloading:', data.target);
|
|
210
|
+
if (data.target === 'rust' || data.target === 'full') {
|
|
211
|
+
window.location.reload();
|
|
212
|
+
}
|
|
213
|
+
break;
|
|
214
|
+
case 'update':
|
|
215
|
+
console.log('[ZapRS] Files updated:', data.files);
|
|
216
|
+
// Vite HMR handles partial updates
|
|
217
|
+
break;
|
|
218
|
+
case 'error':
|
|
219
|
+
console.error('[ZapRS] Build error:', data.message);
|
|
220
|
+
showErrorOverlay(data.message);
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function showErrorOverlay(message) {
|
|
226
|
+
const existing = document.getElementById('zap-error-overlay');
|
|
227
|
+
if (existing) existing.remove();
|
|
228
|
+
|
|
229
|
+
const overlay = document.createElement('div');
|
|
230
|
+
overlay.id = 'zap-error-overlay';
|
|
231
|
+
overlay.style.cssText = \`
|
|
232
|
+
position: fixed;
|
|
233
|
+
top: 0;
|
|
234
|
+
left: 0;
|
|
235
|
+
right: 0;
|
|
236
|
+
bottom: 0;
|
|
237
|
+
background: rgba(0,0,0,0.9);
|
|
238
|
+
color: #ff6b6b;
|
|
239
|
+
font-family: monospace;
|
|
240
|
+
padding: 20px;
|
|
241
|
+
z-index: 99999;
|
|
242
|
+
overflow: auto;
|
|
243
|
+
white-space: pre-wrap;
|
|
244
|
+
\`;
|
|
245
|
+
overlay.innerHTML = \`
|
|
246
|
+
<div style="max-width: 800px; margin: 0 auto;">
|
|
247
|
+
<h2 style="color: #ff6b6b;">Build Error</h2>
|
|
248
|
+
<pre style="background: #1a1a1a; padding: 15px; border-radius: 4px;">\${escapeHtml(message)}</pre>
|
|
249
|
+
<button onclick="this.parentElement.parentElement.remove()" style="margin-top: 10px; padding: 8px 16px; cursor: pointer;">
|
|
250
|
+
Dismiss
|
|
251
|
+
</button>
|
|
252
|
+
</div>
|
|
253
|
+
\`;
|
|
254
|
+
document.body.appendChild(overlay);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function escapeHtml(text) {
|
|
258
|
+
const div = document.createElement('div');
|
|
259
|
+
div.textContent = text;
|
|
260
|
+
return div.innerHTML;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function attemptReconnect() {
|
|
264
|
+
if (reconnectAttempts < maxReconnectAttempts) {
|
|
265
|
+
reconnectAttempts++;
|
|
266
|
+
setTimeout(connect, 1000 * reconnectAttempts);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
connect();
|
|
271
|
+
})();
|
|
272
|
+
`;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Get an HTML script tag for the client
|
|
276
|
+
*/
|
|
277
|
+
getScriptTag() {
|
|
278
|
+
return `<script src="http://${this.config.host}:${this.config.port}/client.js"></script>`;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
export { DevServer, type DevServerConfig } from './server.js';
|
|
3
|
+
export { FileWatcher, type WatchEvent } from './watcher.js';
|
|
4
|
+
export { RustBuilder } from './rust-builder.js';
|
|
5
|
+
export { ViteProxy } from './vite-proxy.js';
|
|
6
|
+
export { HotReloadServer } from './hot-reload.js';
|
|
7
|
+
export { CodegenRunner } from './codegen-runner.js';
|
|
8
|
+
export { RouteScannerRunner } from './route-scanner.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
export { DevServer } from './server.js';
|
|
3
|
+
export { FileWatcher } from './watcher.js';
|
|
4
|
+
export { RustBuilder } from './rust-builder.js';
|
|
5
|
+
export { ViteProxy } from './vite-proxy.js';
|
|
6
|
+
export { HotReloadServer } from './hot-reload.js';
|
|
7
|
+
export { CodegenRunner } from './codegen-runner.js';
|
|
8
|
+
export { RouteScannerRunner } from './route-scanner.js';
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route scanner integration for dev server
|
|
3
|
+
*
|
|
4
|
+
* Watches the routes directory and regenerates route tree on changes
|
|
5
|
+
*/
|
|
6
|
+
import { EventEmitter } from 'events';
|
|
7
|
+
interface RouteTree {
|
|
8
|
+
routes: Array<{
|
|
9
|
+
filePath: string;
|
|
10
|
+
relativePath: string;
|
|
11
|
+
urlPath: string;
|
|
12
|
+
type: string;
|
|
13
|
+
params: Array<{
|
|
14
|
+
name: string;
|
|
15
|
+
index: number;
|
|
16
|
+
catchAll: boolean;
|
|
17
|
+
}>;
|
|
18
|
+
isIndex: boolean;
|
|
19
|
+
}>;
|
|
20
|
+
apiRoutes: Array<{
|
|
21
|
+
filePath: string;
|
|
22
|
+
relativePath: string;
|
|
23
|
+
urlPath: string;
|
|
24
|
+
type: string;
|
|
25
|
+
params: Array<{
|
|
26
|
+
name: string;
|
|
27
|
+
index: number;
|
|
28
|
+
catchAll: boolean;
|
|
29
|
+
}>;
|
|
30
|
+
methods?: string[];
|
|
31
|
+
isIndex: boolean;
|
|
32
|
+
}>;
|
|
33
|
+
layouts: unknown[];
|
|
34
|
+
root: unknown;
|
|
35
|
+
}
|
|
36
|
+
export interface RouteScannerConfig {
|
|
37
|
+
projectDir: string;
|
|
38
|
+
routesDir?: string;
|
|
39
|
+
outputDir?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* RouteScannerRunner - Runs the route scanner and generates route tree
|
|
43
|
+
*/
|
|
44
|
+
export declare class RouteScannerRunner extends EventEmitter {
|
|
45
|
+
private config;
|
|
46
|
+
private routesDir;
|
|
47
|
+
private outputDir;
|
|
48
|
+
private watcher;
|
|
49
|
+
constructor(config: RouteScannerConfig);
|
|
50
|
+
/**
|
|
51
|
+
* Try to load the router module
|
|
52
|
+
*/
|
|
53
|
+
private loadRouter;
|
|
54
|
+
/**
|
|
55
|
+
* Scan routes once and generate output
|
|
56
|
+
*/
|
|
57
|
+
scan(): Promise<RouteTree | null>;
|
|
58
|
+
/**
|
|
59
|
+
* Start watching for route changes
|
|
60
|
+
*/
|
|
61
|
+
startWatching(): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Stop watching
|
|
64
|
+
*/
|
|
65
|
+
stopWatching(): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Check if routes directory exists
|
|
68
|
+
*/
|
|
69
|
+
hasRoutesDir(): boolean;
|
|
70
|
+
}
|
|
71
|
+
export {};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route scanner integration for dev server
|
|
3
|
+
*
|
|
4
|
+
* Watches the routes directory and regenerates route tree on changes
|
|
5
|
+
*/
|
|
6
|
+
import { EventEmitter } from 'events';
|
|
7
|
+
import { existsSync, mkdirSync } from 'fs';
|
|
8
|
+
import { join } from 'path';
|
|
9
|
+
/**
|
|
10
|
+
* RouteScannerRunner - Runs the route scanner and generates route tree
|
|
11
|
+
*/
|
|
12
|
+
export class RouteScannerRunner extends EventEmitter {
|
|
13
|
+
constructor(config) {
|
|
14
|
+
super();
|
|
15
|
+
this.watcher = null;
|
|
16
|
+
this.config = config;
|
|
17
|
+
this.routesDir = config.routesDir || join(config.projectDir, 'routes');
|
|
18
|
+
this.outputDir = config.outputDir || join(config.projectDir, 'src', 'generated');
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Try to load the router module
|
|
22
|
+
*/
|
|
23
|
+
async loadRouter() {
|
|
24
|
+
try {
|
|
25
|
+
// Dynamic import using variable to prevent TypeScript from resolving at compile time
|
|
26
|
+
const moduleName = '@zapjs/router';
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
28
|
+
const router = await (Function('moduleName', 'return import(moduleName)')(moduleName));
|
|
29
|
+
return router;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Scan routes once and generate output
|
|
37
|
+
*/
|
|
38
|
+
async scan() {
|
|
39
|
+
if (!existsSync(this.routesDir)) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const router = await this.loadRouter();
|
|
44
|
+
if (!router) {
|
|
45
|
+
this.emit('error', new Error('@zapjs/router not installed'));
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const tree = router.scanRoutes(this.routesDir);
|
|
49
|
+
// Ensure output directory exists
|
|
50
|
+
if (!existsSync(this.outputDir)) {
|
|
51
|
+
mkdirSync(this.outputDir, { recursive: true });
|
|
52
|
+
}
|
|
53
|
+
// Generate route tree
|
|
54
|
+
router.generateRouteTree({
|
|
55
|
+
outputDir: this.outputDir,
|
|
56
|
+
routeTree: tree,
|
|
57
|
+
});
|
|
58
|
+
this.emit('scan-complete', tree);
|
|
59
|
+
return tree;
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
this.emit('error', err);
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Start watching for route changes
|
|
68
|
+
*/
|
|
69
|
+
async startWatching() {
|
|
70
|
+
if (!existsSync(this.routesDir)) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
const router = await this.loadRouter();
|
|
75
|
+
if (!router) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
this.watcher = router.watchRoutes({
|
|
79
|
+
routesDir: this.routesDir,
|
|
80
|
+
skipInitial: true,
|
|
81
|
+
onChange: (tree) => {
|
|
82
|
+
// Ensure output directory exists
|
|
83
|
+
if (!existsSync(this.outputDir)) {
|
|
84
|
+
mkdirSync(this.outputDir, { recursive: true });
|
|
85
|
+
}
|
|
86
|
+
router.generateRouteTree({
|
|
87
|
+
outputDir: this.outputDir,
|
|
88
|
+
routeTree: tree,
|
|
89
|
+
});
|
|
90
|
+
this.emit('routes-changed', tree);
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
this.emit('watching');
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
this.emit('error', err);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Stop watching
|
|
101
|
+
*/
|
|
102
|
+
async stopWatching() {
|
|
103
|
+
if (this.watcher) {
|
|
104
|
+
await this.watcher.stop();
|
|
105
|
+
this.watcher = null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Check if routes directory exists
|
|
110
|
+
*/
|
|
111
|
+
hasRoutesDir() {
|
|
112
|
+
return existsSync(this.routesDir);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
export type BuildStatus = 'idle' | 'building' | 'success' | 'failed';
|
|
3
|
+
export interface BuildResult {
|
|
4
|
+
success: boolean;
|
|
5
|
+
duration: number;
|
|
6
|
+
errors: string[];
|
|
7
|
+
warnings: string[];
|
|
8
|
+
}
|
|
9
|
+
export interface RustBuilderConfig {
|
|
10
|
+
projectDir: string;
|
|
11
|
+
target?: string;
|
|
12
|
+
release?: boolean;
|
|
13
|
+
features?: string[];
|
|
14
|
+
bin?: string;
|
|
15
|
+
/** Path to pre-built binary (skips compilation) */
|
|
16
|
+
binaryPath?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* RustBuilder - Manages Rust compilation with incremental builds
|
|
20
|
+
*
|
|
21
|
+
* Features:
|
|
22
|
+
* - Incremental compilation using cargo's built-in caching
|
|
23
|
+
* - Error parsing and structured output
|
|
24
|
+
* - Build queueing to prevent concurrent builds
|
|
25
|
+
* - Automatic binary detection
|
|
26
|
+
*/
|
|
27
|
+
export declare class RustBuilder extends EventEmitter {
|
|
28
|
+
private config;
|
|
29
|
+
private currentBuild;
|
|
30
|
+
private buildQueued;
|
|
31
|
+
private status;
|
|
32
|
+
private lastBuildResult;
|
|
33
|
+
constructor(config: RustBuilderConfig);
|
|
34
|
+
/**
|
|
35
|
+
* Get current build status
|
|
36
|
+
*/
|
|
37
|
+
getStatus(): BuildStatus;
|
|
38
|
+
/**
|
|
39
|
+
* Get last build result
|
|
40
|
+
*/
|
|
41
|
+
getLastBuildResult(): BuildResult | null;
|
|
42
|
+
/**
|
|
43
|
+
* Trigger a build, queuing if one is in progress
|
|
44
|
+
*/
|
|
45
|
+
build(): Promise<BuildResult>;
|
|
46
|
+
/**
|
|
47
|
+
* Run the actual cargo build
|
|
48
|
+
*/
|
|
49
|
+
private runBuild;
|
|
50
|
+
/**
|
|
51
|
+
* Format a compiler message for display
|
|
52
|
+
*/
|
|
53
|
+
private formatCompilerMessage;
|
|
54
|
+
/**
|
|
55
|
+
* Cancel current build if running
|
|
56
|
+
*/
|
|
57
|
+
cancel(): void;
|
|
58
|
+
/**
|
|
59
|
+
* Check the project without building (faster)
|
|
60
|
+
*/
|
|
61
|
+
check(): Promise<BuildResult>;
|
|
62
|
+
/**
|
|
63
|
+
* Get the path to the built binary
|
|
64
|
+
*/
|
|
65
|
+
getBinaryPath(): string;
|
|
66
|
+
}
|