@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.
Files changed (115) hide show
  1. package/README.md +310 -24
  2. package/bin/zap +0 -0
  3. package/bin/zap-codegen +0 -0
  4. package/dist/cli/commands/build.d.ts +11 -0
  5. package/dist/cli/commands/build.js +282 -0
  6. package/dist/cli/commands/codegen.d.ts +8 -0
  7. package/dist/cli/commands/codegen.js +95 -0
  8. package/dist/cli/commands/dev.d.ts +20 -0
  9. package/dist/cli/commands/dev.js +78 -0
  10. package/dist/cli/commands/new.d.ts +9 -0
  11. package/dist/cli/commands/new.js +307 -0
  12. package/dist/cli/commands/routes-old.d.ts +9 -0
  13. package/dist/cli/commands/routes-old.js +106 -0
  14. package/dist/cli/commands/routes.d.ts +11 -0
  15. package/dist/cli/commands/routes.js +280 -0
  16. package/dist/cli/commands/serve.d.ts +17 -0
  17. package/dist/cli/commands/serve.js +386 -0
  18. package/dist/cli/index.d.ts +2 -0
  19. package/dist/cli/index.js +76 -0
  20. package/dist/cli/utils/index.d.ts +2 -0
  21. package/dist/cli/utils/index.js +2 -0
  22. package/dist/cli/utils/logger.d.ts +84 -0
  23. package/dist/cli/utils/logger.js +181 -0
  24. package/dist/cli/utils/port-finder.d.ts +8 -0
  25. package/dist/cli/utils/port-finder.js +48 -0
  26. package/dist/dev-server/codegen-runner.d.ts +41 -0
  27. package/dist/dev-server/codegen-runner.js +172 -0
  28. package/dist/dev-server/hot-reload.d.ts +72 -0
  29. package/dist/dev-server/hot-reload.js +280 -0
  30. package/dist/dev-server/index.d.ts +8 -0
  31. package/dist/dev-server/index.js +8 -0
  32. package/dist/dev-server/route-scanner.d.ts +71 -0
  33. package/dist/dev-server/route-scanner.js +114 -0
  34. package/dist/dev-server/rust-builder.d.ts +66 -0
  35. package/dist/dev-server/rust-builder.js +286 -0
  36. package/dist/dev-server/server.d.ts +147 -0
  37. package/dist/dev-server/server.js +658 -0
  38. package/dist/dev-server/vite-proxy.d.ts +56 -0
  39. package/dist/dev-server/vite-proxy.js +212 -0
  40. package/dist/dev-server/watcher.d.ts +48 -0
  41. package/dist/dev-server/watcher.js +127 -0
  42. package/dist/router/codegen-enhanced.d.ts +5 -0
  43. package/dist/router/codegen-enhanced.js +275 -0
  44. package/dist/router/codegen.d.ts +17 -0
  45. package/dist/router/codegen.js +654 -0
  46. package/dist/router/index.d.ts +16 -0
  47. package/dist/router/index.js +19 -0
  48. package/dist/router/scanner.d.ts +86 -0
  49. package/dist/router/scanner.js +689 -0
  50. package/dist/router/ssg.d.ts +115 -0
  51. package/dist/router/ssg.js +202 -0
  52. package/dist/router/types.d.ts +124 -0
  53. package/dist/router/types.js +9 -0
  54. package/dist/router/watch.d.ts +38 -0
  55. package/dist/router/watch.js +135 -0
  56. package/dist/runtime/csrf.d.ts +146 -0
  57. package/dist/runtime/csrf.js +166 -0
  58. package/dist/runtime/error-boundary.d.ts +129 -0
  59. package/dist/runtime/error-boundary.js +287 -0
  60. package/dist/runtime/hooks.d.ts +83 -0
  61. package/dist/runtime/hooks.js +96 -0
  62. package/dist/runtime/index.d.ts +229 -0
  63. package/dist/runtime/index.js +449 -0
  64. package/dist/runtime/ipc-client.d.ts +144 -0
  65. package/dist/runtime/ipc-client.js +621 -0
  66. package/dist/runtime/logger.d.ts +71 -0
  67. package/dist/runtime/logger.js +164 -0
  68. package/dist/runtime/middleware.d.ts +66 -0
  69. package/dist/runtime/middleware.js +114 -0
  70. package/dist/runtime/process-manager.d.ts +51 -0
  71. package/dist/runtime/process-manager.js +207 -0
  72. package/dist/runtime/router-simple.d.ts +98 -0
  73. package/dist/runtime/router-simple.js +330 -0
  74. package/dist/runtime/router.d.ts +103 -0
  75. package/dist/runtime/router.js +435 -0
  76. package/dist/runtime/rpc-client.d.ts +35 -0
  77. package/dist/runtime/rpc-client.js +140 -0
  78. package/dist/runtime/streaming-utils.d.ts +86 -0
  79. package/dist/runtime/streaming-utils.js +150 -0
  80. package/dist/runtime/types.d.ts +465 -0
  81. package/dist/runtime/types.js +60 -0
  82. package/dist/runtime/websockets-utils.d.ts +50 -0
  83. package/dist/runtime/websockets-utils.js +92 -0
  84. package/package.json +30 -20
  85. package/index.js +0 -29
  86. package/internal/cli/package.json +0 -46
  87. package/internal/cli/tsconfig.tsbuildinfo +0 -1
  88. package/internal/dev-server/node_modules/ora/index.d.ts +0 -332
  89. package/internal/dev-server/node_modules/ora/index.js +0 -416
  90. package/internal/dev-server/node_modules/ora/license +0 -9
  91. package/internal/dev-server/node_modules/ora/node_modules/string-width/index.d.ts +0 -36
  92. package/internal/dev-server/node_modules/ora/node_modules/string-width/index.js +0 -65
  93. package/internal/dev-server/node_modules/ora/node_modules/string-width/license +0 -9
  94. package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/LICENSE-MIT.txt +0 -20
  95. package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/README.md +0 -107
  96. package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/index.d.ts +0 -3
  97. package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/index.js +0 -4
  98. package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/index.mjs +0 -4
  99. package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/package.json +0 -46
  100. package/internal/dev-server/node_modules/ora/node_modules/string-width/package.json +0 -60
  101. package/internal/dev-server/node_modules/ora/node_modules/string-width/readme.md +0 -62
  102. package/internal/dev-server/node_modules/ora/package.json +0 -66
  103. package/internal/dev-server/node_modules/ora/readme.md +0 -325
  104. package/internal/dev-server/package.json +0 -41
  105. package/internal/router/package.json +0 -28
  106. package/internal/runtime/package.json +0 -41
  107. package/internal/runtime/src/error-boundary.tsx +0 -476
  108. package/internal/runtime/src/router-simple.tsx +0 -640
  109. package/internal/runtime/src/router.tsx +0 -771
  110. package/internal/runtime/tsconfig.tsbuildinfo +0 -1
  111. package/src/errors.js +0 -33
  112. package/src/logger.js +0 -10
  113. package/src/middleware.js +0 -32
  114. package/src/router.js +0 -41
  115. 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,2 @@
1
+ export * from './logger.js';
2
+ export * from './port-finder.js';
@@ -0,0 +1,2 @@
1
+ export * from './logger.js';
2
+ export * from './port-finder.js';
@@ -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
+ }