@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,76 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { newCommand } from './commands/new.js';
|
|
4
|
+
import { devCommand } from './commands/dev.js';
|
|
5
|
+
import { buildCommand } from './commands/build.js';
|
|
6
|
+
import { serveCommand } from './commands/serve.js';
|
|
7
|
+
import { codegenCommand } from './commands/codegen.js';
|
|
8
|
+
import { routesCommand } from './commands/routes.js';
|
|
9
|
+
import { cliLogger } from './utils/logger.js';
|
|
10
|
+
const program = new Command();
|
|
11
|
+
program
|
|
12
|
+
.name('zap')
|
|
13
|
+
.description('ZapJS - Fullstack Rust + React Framework')
|
|
14
|
+
.version('0.1.0', '-v, --version')
|
|
15
|
+
.helpOption('-h, --help');
|
|
16
|
+
// Register commands
|
|
17
|
+
program
|
|
18
|
+
.command('new <name>')
|
|
19
|
+
.description('Create a new ZapJS project')
|
|
20
|
+
.option('-t, --template <template>', 'Template to use (basic|fullstack)', 'basic')
|
|
21
|
+
.option('--no-install', 'Skip npm install')
|
|
22
|
+
.option('--no-git', 'Skip git initialization')
|
|
23
|
+
.action((name, options) => newCommand(name, options));
|
|
24
|
+
program
|
|
25
|
+
.command('dev')
|
|
26
|
+
.description('Start development server with hot reload')
|
|
27
|
+
.option('-p, --port <port>', 'API server port', '3000')
|
|
28
|
+
.option('--vite-port <port>', 'Vite dev server port', '5173')
|
|
29
|
+
.option('--no-open', 'Do not open browser')
|
|
30
|
+
.option('-l, --log-level <level>', 'Log level (debug|info|warn|error)', 'info')
|
|
31
|
+
.option('--release', 'Build in release mode')
|
|
32
|
+
.option('--skip-build', 'Skip initial Rust build')
|
|
33
|
+
.action((options) => devCommand(options));
|
|
34
|
+
program
|
|
35
|
+
.command('build')
|
|
36
|
+
.description('Build for production')
|
|
37
|
+
.option('--release', 'Build optimized release build', true)
|
|
38
|
+
.option('-o, --output <dir>', 'Output directory', './dist')
|
|
39
|
+
.option('--target <target>', 'Cross-compile target (e.g., x86_64-unknown-linux-gnu)')
|
|
40
|
+
.option('--skip-frontend', 'Skip frontend build')
|
|
41
|
+
.option('--skip-codegen', 'Skip TypeScript codegen')
|
|
42
|
+
.action((options) => buildCommand(options));
|
|
43
|
+
program
|
|
44
|
+
.command('serve')
|
|
45
|
+
.description('Run production server')
|
|
46
|
+
.option('-p, --port <port>', 'Port to run on')
|
|
47
|
+
.option('--host <host>', 'Host to bind to')
|
|
48
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
49
|
+
.option('-w, --workers <count>', 'Number of worker threads')
|
|
50
|
+
.action((options) => serveCommand(options));
|
|
51
|
+
program
|
|
52
|
+
.command('codegen')
|
|
53
|
+
.description('Generate TypeScript bindings from Rust exports')
|
|
54
|
+
.option('-i, --input <file>', 'Input metadata JSON file')
|
|
55
|
+
.option('-o, --output <dir>', 'Output directory', './src/api')
|
|
56
|
+
.action((options) => codegenCommand(options));
|
|
57
|
+
program
|
|
58
|
+
.command('routes')
|
|
59
|
+
.description('Scan routes directory and generate route tree')
|
|
60
|
+
.option('-d, --routes-dir <dir>', 'Routes directory path')
|
|
61
|
+
.option('-o, --output <dir>', 'Output directory for generated files')
|
|
62
|
+
.option('--json', 'Output routes as JSON')
|
|
63
|
+
.option('--verbose', 'Show full handler code')
|
|
64
|
+
.action((options) => routesCommand(options));
|
|
65
|
+
// Handle unknown commands
|
|
66
|
+
program.on('command:*', () => {
|
|
67
|
+
cliLogger.error(`Unknown command "${program.args[0]}"`);
|
|
68
|
+
cliLogger.newline();
|
|
69
|
+
program.outputHelp();
|
|
70
|
+
process.exit(1);
|
|
71
|
+
});
|
|
72
|
+
// Show help if no command provided
|
|
73
|
+
if (!process.argv.slice(2).length) {
|
|
74
|
+
program.outputHelp();
|
|
75
|
+
}
|
|
76
|
+
program.parse();
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZapJS CLI Logger - Orange-themed visual logging utility
|
|
3
|
+
*
|
|
4
|
+
* Brand Colors (from zap.svg logo):
|
|
5
|
+
* - Primary Orange: #ec751a
|
|
6
|
+
* - Light Orange: #f5ba77
|
|
7
|
+
* - Dark Background: #0a0a0a
|
|
8
|
+
*/
|
|
9
|
+
import { Ora } from 'ora';
|
|
10
|
+
export declare class CliLogger {
|
|
11
|
+
private spinners;
|
|
12
|
+
/**
|
|
13
|
+
* Print ZapJS branded header
|
|
14
|
+
*/
|
|
15
|
+
header(text: string): void;
|
|
16
|
+
/**
|
|
17
|
+
* Success message with checkmark
|
|
18
|
+
*/
|
|
19
|
+
success(message: string, details?: string): void;
|
|
20
|
+
/**
|
|
21
|
+
* Error message with X symbol
|
|
22
|
+
*/
|
|
23
|
+
error(message: string, err?: Error | string): void;
|
|
24
|
+
/**
|
|
25
|
+
* Warning message with warning symbol
|
|
26
|
+
*/
|
|
27
|
+
warn(message: string, details?: string): void;
|
|
28
|
+
/**
|
|
29
|
+
* Info message with info symbol
|
|
30
|
+
*/
|
|
31
|
+
info(message: string, details?: string): void;
|
|
32
|
+
/**
|
|
33
|
+
* Step indicator (numbered steps)
|
|
34
|
+
*/
|
|
35
|
+
step(num: number, message: string): void;
|
|
36
|
+
/**
|
|
37
|
+
* Command suggestion (dimmed with code styling)
|
|
38
|
+
*/
|
|
39
|
+
command(cmd: string, description?: string): void;
|
|
40
|
+
/**
|
|
41
|
+
* Start a spinner with orange color
|
|
42
|
+
*/
|
|
43
|
+
spinner(id: string, text: string): Ora;
|
|
44
|
+
/**
|
|
45
|
+
* Update spinner text
|
|
46
|
+
*/
|
|
47
|
+
updateSpinner(id: string, text: string): void;
|
|
48
|
+
/**
|
|
49
|
+
* Stop spinner with success
|
|
50
|
+
*/
|
|
51
|
+
succeedSpinner(id: string, text?: string): void;
|
|
52
|
+
/**
|
|
53
|
+
* Stop spinner with error
|
|
54
|
+
*/
|
|
55
|
+
failSpinner(id: string, text?: string): void;
|
|
56
|
+
/**
|
|
57
|
+
* Print a blank line
|
|
58
|
+
*/
|
|
59
|
+
newline(): void;
|
|
60
|
+
/**
|
|
61
|
+
* Print dimmed separator line
|
|
62
|
+
*/
|
|
63
|
+
separator(): void;
|
|
64
|
+
/**
|
|
65
|
+
* Print key-value pair
|
|
66
|
+
*/
|
|
67
|
+
keyValue(key: string, value: string | number): void;
|
|
68
|
+
/**
|
|
69
|
+
* Print list item with bullet
|
|
70
|
+
*/
|
|
71
|
+
listItem(text: string, bullet?: string): void;
|
|
72
|
+
/**
|
|
73
|
+
* Print a box around text
|
|
74
|
+
*/
|
|
75
|
+
box(title: string, content: string[]): void;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Global CLI logger instance
|
|
79
|
+
*/
|
|
80
|
+
export declare const cliLogger: CliLogger;
|
|
81
|
+
/**
|
|
82
|
+
* Convenience exports
|
|
83
|
+
*/
|
|
84
|
+
export declare const header: (text: string) => void, success: (message: string, details?: string) => void, error: (message: string, err?: Error | string) => void, warn: (message: string, details?: string) => void, info: (message: string, details?: string) => void, step: (num: number, message: string) => void, command: (cmd: string, description?: string) => void, spinner: (id: string, text: string) => Ora, updateSpinner: (id: string, text: string) => void, succeedSpinner: (id: string, text?: string) => void, failSpinner: (id: string, text?: string) => void, newline: () => void, separator: () => void, keyValue: (key: string, value: string | number) => void, listItem: (text: string, bullet?: string) => void, box: (title: string, content: string[]) => void;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZapJS CLI Logger - Orange-themed visual logging utility
|
|
3
|
+
*
|
|
4
|
+
* Brand Colors (from zap.svg logo):
|
|
5
|
+
* - Primary Orange: #ec751a
|
|
6
|
+
* - Light Orange: #f5ba77
|
|
7
|
+
* - Dark Background: #0a0a0a
|
|
8
|
+
*/
|
|
9
|
+
import chalk from 'chalk';
|
|
10
|
+
import ora from 'ora';
|
|
11
|
+
// Define brand colors
|
|
12
|
+
const COLORS = {
|
|
13
|
+
primary: '#ec751a', // Main orange from logo
|
|
14
|
+
light: '#f5ba77', // Light orange for accents
|
|
15
|
+
success: '#10b981', // Green for success
|
|
16
|
+
error: '#ef4444', // Red for errors
|
|
17
|
+
warning: '#f59e0b', // Amber for warnings
|
|
18
|
+
info: '#3b82f6', // Blue for info
|
|
19
|
+
dim: '#6b7280', // Gray for secondary text
|
|
20
|
+
};
|
|
21
|
+
// Custom chalk instances with brand colors
|
|
22
|
+
const orange = chalk.hex(COLORS.primary);
|
|
23
|
+
const orangeLight = chalk.hex(COLORS.light);
|
|
24
|
+
const successColor = chalk.hex(COLORS.success);
|
|
25
|
+
const errorColor = chalk.hex(COLORS.error);
|
|
26
|
+
const warningColor = chalk.hex(COLORS.warning);
|
|
27
|
+
const infoColor = chalk.hex(COLORS.info);
|
|
28
|
+
const dim = chalk.hex(COLORS.dim);
|
|
29
|
+
export class CliLogger {
|
|
30
|
+
constructor() {
|
|
31
|
+
this.spinners = new Map();
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Print ZapJS branded header
|
|
35
|
+
*/
|
|
36
|
+
header(text) {
|
|
37
|
+
console.log();
|
|
38
|
+
console.log(orange.bold(`⚡ ${text}`));
|
|
39
|
+
console.log(dim('─'.repeat(60)));
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Success message with checkmark
|
|
43
|
+
*/
|
|
44
|
+
success(message, details) {
|
|
45
|
+
console.log(successColor('✓'), chalk.white(message));
|
|
46
|
+
if (details) {
|
|
47
|
+
console.log(dim(` ${details}`));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Error message with X symbol
|
|
52
|
+
*/
|
|
53
|
+
error(message, err) {
|
|
54
|
+
console.log(errorColor('✗'), chalk.white(message));
|
|
55
|
+
if (err) {
|
|
56
|
+
const errorMsg = typeof err === 'string' ? err : err.message;
|
|
57
|
+
console.log(dim(` ${errorMsg}`));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Warning message with warning symbol
|
|
62
|
+
*/
|
|
63
|
+
warn(message, details) {
|
|
64
|
+
console.log(warningColor('⚠'), chalk.white(message));
|
|
65
|
+
if (details) {
|
|
66
|
+
console.log(dim(` ${details}`));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Info message with info symbol
|
|
71
|
+
*/
|
|
72
|
+
info(message, details) {
|
|
73
|
+
console.log(infoColor('ℹ'), chalk.white(message));
|
|
74
|
+
if (details) {
|
|
75
|
+
console.log(dim(` ${details}`));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Step indicator (numbered steps)
|
|
80
|
+
*/
|
|
81
|
+
step(num, message) {
|
|
82
|
+
const stepNum = orange(`[${num}]`);
|
|
83
|
+
console.log(`${stepNum} ${chalk.white(message)}`);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Command suggestion (dimmed with code styling)
|
|
87
|
+
*/
|
|
88
|
+
command(cmd, description) {
|
|
89
|
+
console.log(` ${orangeLight('$')} ${chalk.cyan(cmd)}`);
|
|
90
|
+
if (description) {
|
|
91
|
+
console.log(dim(` ${description}`));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Start a spinner with orange color
|
|
96
|
+
*/
|
|
97
|
+
spinner(id, text) {
|
|
98
|
+
const spinner = ora({
|
|
99
|
+
text: chalk.white(text),
|
|
100
|
+
color: 'yellow', // ora doesn't support hex, yellow is closest to orange
|
|
101
|
+
spinner: 'dots',
|
|
102
|
+
}).start();
|
|
103
|
+
this.spinners.set(id, spinner);
|
|
104
|
+
return spinner;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Update spinner text
|
|
108
|
+
*/
|
|
109
|
+
updateSpinner(id, text) {
|
|
110
|
+
const spinner = this.spinners.get(id);
|
|
111
|
+
if (spinner) {
|
|
112
|
+
spinner.text = chalk.white(text);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Stop spinner with success
|
|
117
|
+
*/
|
|
118
|
+
succeedSpinner(id, text) {
|
|
119
|
+
const spinner = this.spinners.get(id);
|
|
120
|
+
if (spinner) {
|
|
121
|
+
spinner.succeed(text ? chalk.white(text) : undefined);
|
|
122
|
+
this.spinners.delete(id);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Stop spinner with error
|
|
127
|
+
*/
|
|
128
|
+
failSpinner(id, text) {
|
|
129
|
+
const spinner = this.spinners.get(id);
|
|
130
|
+
if (spinner) {
|
|
131
|
+
spinner.fail(text ? chalk.white(text) : undefined);
|
|
132
|
+
this.spinners.delete(id);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Print a blank line
|
|
137
|
+
*/
|
|
138
|
+
newline() {
|
|
139
|
+
console.log();
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Print dimmed separator line
|
|
143
|
+
*/
|
|
144
|
+
separator() {
|
|
145
|
+
console.log(dim('─'.repeat(60)));
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Print key-value pair
|
|
149
|
+
*/
|
|
150
|
+
keyValue(key, value) {
|
|
151
|
+
console.log(` ${dim(key + ':')} ${chalk.white(value)}`);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Print list item with bullet
|
|
155
|
+
*/
|
|
156
|
+
listItem(text, bullet = '•') {
|
|
157
|
+
console.log(` ${orange(bullet)} ${chalk.white(text)}`);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Print a box around text
|
|
161
|
+
*/
|
|
162
|
+
box(title, content) {
|
|
163
|
+
const maxLen = Math.max(title.length, ...content.map(c => c.length));
|
|
164
|
+
const width = maxLen + 4;
|
|
165
|
+
console.log(orange('┌' + '─'.repeat(width) + '┐'));
|
|
166
|
+
console.log(orange('│') + chalk.white.bold(` ${title.padEnd(maxLen)} `) + orange('│'));
|
|
167
|
+
console.log(orange('├' + '─'.repeat(width) + '┤'));
|
|
168
|
+
content.forEach(line => {
|
|
169
|
+
console.log(orange('│') + chalk.white(` ${line.padEnd(maxLen)} `) + orange('│'));
|
|
170
|
+
});
|
|
171
|
+
console.log(orange('└' + '─'.repeat(width) + '┘'));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Global CLI logger instance
|
|
176
|
+
*/
|
|
177
|
+
export const cliLogger = new CliLogger();
|
|
178
|
+
/**
|
|
179
|
+
* Convenience exports
|
|
180
|
+
*/
|
|
181
|
+
export const { header, success, error, warn, info, step, command, spinner, updateSpinner, succeedSpinner, failSpinner, newline, separator, keyValue, listItem, box, } = cliLogger;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find an available port starting from the given port
|
|
3
|
+
*/
|
|
4
|
+
export declare function findAvailablePort(startPort: number, maxAttempts?: number): Promise<number>;
|
|
5
|
+
/**
|
|
6
|
+
* Find multiple available ports
|
|
7
|
+
*/
|
|
8
|
+
export declare function findAvailablePorts(ports: number[]): Promise<Record<string, number>>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { createServer } from 'net';
|
|
2
|
+
/**
|
|
3
|
+
* Find an available port starting from the given port
|
|
4
|
+
*/
|
|
5
|
+
export async function findAvailablePort(startPort, maxAttempts = 10) {
|
|
6
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
7
|
+
const port = startPort + i;
|
|
8
|
+
if (await isPortAvailable(port)) {
|
|
9
|
+
return port;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
// If no port found, just return the original
|
|
13
|
+
return startPort;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Check if a port is available
|
|
17
|
+
*/
|
|
18
|
+
function isPortAvailable(port) {
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
const server = createServer();
|
|
21
|
+
server.once('error', (err) => {
|
|
22
|
+
if (err.code === 'EADDRINUSE') {
|
|
23
|
+
resolve(false);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
resolve(false);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
server.once('listening', () => {
|
|
30
|
+
server.close();
|
|
31
|
+
resolve(true);
|
|
32
|
+
});
|
|
33
|
+
server.listen(port, '127.0.0.1');
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Find multiple available ports
|
|
38
|
+
*/
|
|
39
|
+
export async function findAvailablePorts(ports) {
|
|
40
|
+
const result = {};
|
|
41
|
+
let currentPort = Math.min(...ports);
|
|
42
|
+
for (const originalPort of ports) {
|
|
43
|
+
const available = await findAvailablePort(currentPort);
|
|
44
|
+
result[`port_${originalPort}`] = available;
|
|
45
|
+
currentPort = available + 1;
|
|
46
|
+
}
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
export interface CodegenConfig {
|
|
3
|
+
projectDir: string;
|
|
4
|
+
outputDir?: string;
|
|
5
|
+
codegenBinary?: string;
|
|
6
|
+
}
|
|
7
|
+
export type CodegenStatus = 'idle' | 'running' | 'success' | 'failed';
|
|
8
|
+
/**
|
|
9
|
+
* CodegenRunner - Automatically regenerates TypeScript bindings from Rust
|
|
10
|
+
*
|
|
11
|
+
* Triggered when:
|
|
12
|
+
* - Rust files with #[zap::export] are modified
|
|
13
|
+
* - Cargo.toml changes
|
|
14
|
+
* - After successful Rust build
|
|
15
|
+
*/
|
|
16
|
+
export declare class CodegenRunner extends EventEmitter {
|
|
17
|
+
private config;
|
|
18
|
+
private status;
|
|
19
|
+
private lastGenerated;
|
|
20
|
+
constructor(config: CodegenConfig);
|
|
21
|
+
/**
|
|
22
|
+
* Get current status
|
|
23
|
+
*/
|
|
24
|
+
getStatus(): CodegenStatus;
|
|
25
|
+
/**
|
|
26
|
+
* Run codegen to regenerate TypeScript bindings
|
|
27
|
+
*/
|
|
28
|
+
run(): Promise<boolean>;
|
|
29
|
+
/**
|
|
30
|
+
* Find the codegen binary
|
|
31
|
+
*/
|
|
32
|
+
private findCodegenBinary;
|
|
33
|
+
/**
|
|
34
|
+
* Check if codegen is needed based on file modification times
|
|
35
|
+
*/
|
|
36
|
+
isStale(): Promise<boolean>;
|
|
37
|
+
/**
|
|
38
|
+
* Get the output directory
|
|
39
|
+
*/
|
|
40
|
+
getOutputDir(): string;
|
|
41
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { spawn, execSync } from 'child_process';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { existsSync } from 'fs';
|
|
5
|
+
/**
|
|
6
|
+
* CodegenRunner - Automatically regenerates TypeScript bindings from Rust
|
|
7
|
+
*
|
|
8
|
+
* Triggered when:
|
|
9
|
+
* - Rust files with #[zap::export] are modified
|
|
10
|
+
* - Cargo.toml changes
|
|
11
|
+
* - After successful Rust build
|
|
12
|
+
*/
|
|
13
|
+
export class CodegenRunner extends EventEmitter {
|
|
14
|
+
constructor(config) {
|
|
15
|
+
super();
|
|
16
|
+
this.status = 'idle';
|
|
17
|
+
this.lastGenerated = null;
|
|
18
|
+
this.config = {
|
|
19
|
+
outputDir: './src/generated',
|
|
20
|
+
...config,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Get current status
|
|
25
|
+
*/
|
|
26
|
+
getStatus() {
|
|
27
|
+
return this.status;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Run codegen to regenerate TypeScript bindings
|
|
31
|
+
*/
|
|
32
|
+
async run() {
|
|
33
|
+
if (this.status === 'running') {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
this.status = 'running';
|
|
37
|
+
this.emit('start');
|
|
38
|
+
try {
|
|
39
|
+
const binary = await this.findCodegenBinary();
|
|
40
|
+
if (!binary) {
|
|
41
|
+
this.status = 'failed';
|
|
42
|
+
this.emit('error', new Error('zap-codegen binary not found'));
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
return new Promise((resolve) => {
|
|
46
|
+
const args = ['--output-dir', this.config.outputDir];
|
|
47
|
+
console.log(`[codegen] Running: ${binary} ${args.join(' ')}`);
|
|
48
|
+
console.log(`[codegen] CWD: ${this.config.projectDir}`);
|
|
49
|
+
const proc = spawn(binary, args, {
|
|
50
|
+
cwd: this.config.projectDir,
|
|
51
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
52
|
+
});
|
|
53
|
+
let stderr = '';
|
|
54
|
+
let stdout = '';
|
|
55
|
+
proc.stdout?.on('data', (data) => {
|
|
56
|
+
stdout += data.toString();
|
|
57
|
+
console.log(`[codegen stdout] ${data.toString().trim()}`);
|
|
58
|
+
this.emit('output', data.toString());
|
|
59
|
+
});
|
|
60
|
+
proc.stderr?.on('data', (data) => {
|
|
61
|
+
stderr += data.toString();
|
|
62
|
+
console.log(`[codegen stderr] ${data.toString().trim()}`);
|
|
63
|
+
this.emit('stderr', data.toString());
|
|
64
|
+
});
|
|
65
|
+
proc.on('close', (code) => {
|
|
66
|
+
console.log(`[codegen] Process exited with code: ${code}`);
|
|
67
|
+
if (stdout)
|
|
68
|
+
console.log(`[codegen] Full stdout: ${stdout}`);
|
|
69
|
+
if (stderr)
|
|
70
|
+
console.log(`[codegen] Full stderr: ${stderr}`);
|
|
71
|
+
if (code === 0) {
|
|
72
|
+
this.status = 'success';
|
|
73
|
+
this.lastGenerated = new Date();
|
|
74
|
+
this.emit('complete', { success: true });
|
|
75
|
+
resolve(true);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
this.status = 'failed';
|
|
79
|
+
this.emit('complete', { success: false, error: stderr });
|
|
80
|
+
resolve(false);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
proc.on('error', (err) => {
|
|
84
|
+
console.log(`[codegen] Process error: ${err.message}`);
|
|
85
|
+
this.status = 'failed';
|
|
86
|
+
this.emit('error', err);
|
|
87
|
+
resolve(false);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
this.status = 'failed';
|
|
93
|
+
this.emit('error', err);
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Find the codegen binary
|
|
99
|
+
*/
|
|
100
|
+
async findCodegenBinary() {
|
|
101
|
+
console.log(`[codegen] Looking for binary, config.codegenBinary: ${this.config.codegenBinary}`);
|
|
102
|
+
console.log(`[codegen] projectDir: ${this.config.projectDir}`);
|
|
103
|
+
// Check explicit config
|
|
104
|
+
if (this.config.codegenBinary) {
|
|
105
|
+
console.log(`[codegen] Checking explicit path: ${this.config.codegenBinary}`);
|
|
106
|
+
if (existsSync(this.config.codegenBinary)) {
|
|
107
|
+
console.log(`[codegen] Found at explicit path`);
|
|
108
|
+
return this.config.codegenBinary;
|
|
109
|
+
}
|
|
110
|
+
console.log(`[codegen] Not found at explicit path`);
|
|
111
|
+
}
|
|
112
|
+
// Check project's bin directory first (for pre-built binaries)
|
|
113
|
+
const binCandidates = [
|
|
114
|
+
path.join(this.config.projectDir, 'bin/zap-codegen'),
|
|
115
|
+
path.join(this.config.projectDir, 'bin/zap-codegen.exe'),
|
|
116
|
+
// Installed package location (when @zap-js/client is a dependency)
|
|
117
|
+
path.join(__dirname, '../../bin/zap-codegen'),
|
|
118
|
+
// node_modules location
|
|
119
|
+
path.join(this.config.projectDir, 'node_modules/@zap-js/client/bin/zap-codegen'),
|
|
120
|
+
];
|
|
121
|
+
for (const candidate of binCandidates) {
|
|
122
|
+
console.log(`[codegen] Checking bin candidate: ${candidate}`);
|
|
123
|
+
if (existsSync(candidate)) {
|
|
124
|
+
console.log(`[codegen] Found at: ${candidate}`);
|
|
125
|
+
return candidate;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// Check project's target directory
|
|
129
|
+
const candidates = [
|
|
130
|
+
path.join(this.config.projectDir, 'target/release/zap-codegen'),
|
|
131
|
+
path.join(this.config.projectDir, 'target/debug/zap-codegen'),
|
|
132
|
+
// Check for architecture-specific builds
|
|
133
|
+
path.join(this.config.projectDir, 'target/aarch64-apple-darwin/release/zap-codegen'),
|
|
134
|
+
path.join(this.config.projectDir, 'target/x86_64-unknown-linux-gnu/release/zap-codegen'),
|
|
135
|
+
];
|
|
136
|
+
for (const candidate of candidates) {
|
|
137
|
+
console.log(`[codegen] Checking target candidate: ${candidate}`);
|
|
138
|
+
if (existsSync(candidate)) {
|
|
139
|
+
console.log(`[codegen] Found at: ${candidate}`);
|
|
140
|
+
return candidate;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Try to find in PATH
|
|
144
|
+
try {
|
|
145
|
+
execSync('which zap-codegen', { stdio: 'ignore' });
|
|
146
|
+
console.log(`[codegen] Found in PATH`);
|
|
147
|
+
return 'zap-codegen';
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// Not in PATH
|
|
151
|
+
}
|
|
152
|
+
console.log(`[codegen] Binary not found anywhere`);
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Check if codegen is needed based on file modification times
|
|
157
|
+
*/
|
|
158
|
+
async isStale() {
|
|
159
|
+
if (!this.lastGenerated) {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
// Could implement more sophisticated staleness detection
|
|
163
|
+
// by comparing Rust file mtimes with generated file mtimes
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Get the output directory
|
|
168
|
+
*/
|
|
169
|
+
getOutputDir() {
|
|
170
|
+
return path.join(this.config.projectDir, this.config.outputDir);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
export interface HotReloadConfig {
|
|
3
|
+
port?: number;
|
|
4
|
+
host?: string;
|
|
5
|
+
}
|
|
6
|
+
export type ReloadType = 'full' | 'partial' | 'rust' | 'typescript' | 'config' | 'routes';
|
|
7
|
+
export interface ReloadMessage {
|
|
8
|
+
type: 'reload' | 'update' | 'error' | 'connected';
|
|
9
|
+
target?: ReloadType;
|
|
10
|
+
files?: string[];
|
|
11
|
+
message?: string;
|
|
12
|
+
timestamp: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* HotReloadServer - WebSocket server for hot reload signaling
|
|
16
|
+
*
|
|
17
|
+
* Broadcasts reload signals to connected clients:
|
|
18
|
+
* - Full page reload for Rust changes
|
|
19
|
+
* - Partial reload for TypeScript changes (Vite HMR handles this)
|
|
20
|
+
* - Config reload notifications
|
|
21
|
+
*/
|
|
22
|
+
export declare class HotReloadServer extends EventEmitter {
|
|
23
|
+
private config;
|
|
24
|
+
private wss;
|
|
25
|
+
private httpServer;
|
|
26
|
+
private clients;
|
|
27
|
+
constructor(config?: HotReloadConfig);
|
|
28
|
+
/**
|
|
29
|
+
* Start the hot reload WebSocket server
|
|
30
|
+
*/
|
|
31
|
+
start(): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Stop the server
|
|
34
|
+
*/
|
|
35
|
+
stop(): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Broadcast a reload signal to all clients
|
|
38
|
+
*/
|
|
39
|
+
reload(target: ReloadType, files?: string[]): void;
|
|
40
|
+
/**
|
|
41
|
+
* Broadcast an update signal (partial reload)
|
|
42
|
+
*/
|
|
43
|
+
update(files: string[]): void;
|
|
44
|
+
/**
|
|
45
|
+
* Broadcast an error message
|
|
46
|
+
*/
|
|
47
|
+
notifyError(errorMessage: string): void;
|
|
48
|
+
/**
|
|
49
|
+
* Broadcast a message to all connected clients
|
|
50
|
+
*/
|
|
51
|
+
private broadcast;
|
|
52
|
+
/**
|
|
53
|
+
* Send a message to a specific client
|
|
54
|
+
*/
|
|
55
|
+
private send;
|
|
56
|
+
/**
|
|
57
|
+
* Get the number of connected clients
|
|
58
|
+
*/
|
|
59
|
+
getClientCount(): number;
|
|
60
|
+
/**
|
|
61
|
+
* Get the hot reload server URL
|
|
62
|
+
*/
|
|
63
|
+
getUrl(): string;
|
|
64
|
+
/**
|
|
65
|
+
* Get the client script for browser injection
|
|
66
|
+
*/
|
|
67
|
+
getClientScript(): string;
|
|
68
|
+
/**
|
|
69
|
+
* Get an HTML script tag for the client
|
|
70
|
+
*/
|
|
71
|
+
getScriptTag(): string;
|
|
72
|
+
}
|