@geekmidas/cli 0.2.2 → 0.2.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 (72) hide show
  1. package/dist/{CronGenerator-DXRfHQcV.mjs → CronGenerator-Bh26MaNA.mjs} +2 -1
  2. package/dist/CronGenerator-Bh26MaNA.mjs.map +1 -0
  3. package/dist/{CronGenerator-1PflEYe2.cjs → CronGenerator-C6MF8rlG.cjs} +2 -1
  4. package/dist/CronGenerator-C6MF8rlG.cjs.map +1 -0
  5. package/dist/{EndpointGenerator-BbGrDiCP.cjs → EndpointGenerator-C73wNoih.cjs} +12 -5
  6. package/dist/EndpointGenerator-C73wNoih.cjs.map +1 -0
  7. package/dist/{EndpointGenerator-BmZ9BxbO.mjs → EndpointGenerator-CWh18d92.mjs} +12 -5
  8. package/dist/EndpointGenerator-CWh18d92.mjs.map +1 -0
  9. package/dist/{FunctionGenerator-DOEB_yPh.mjs → FunctionGenerator-BNE_GC7N.mjs} +2 -1
  10. package/dist/FunctionGenerator-BNE_GC7N.mjs.map +1 -0
  11. package/dist/{FunctionGenerator-Clw64SwQ.cjs → FunctionGenerator-FgZUTd8L.cjs} +2 -1
  12. package/dist/FunctionGenerator-FgZUTd8L.cjs.map +1 -0
  13. package/dist/{SubscriberGenerator-CB-NHtZW.cjs → SubscriberGenerator-Bd-a7aiw.cjs} +2 -1
  14. package/dist/SubscriberGenerator-Bd-a7aiw.cjs.map +1 -0
  15. package/dist/{SubscriberGenerator-Cuu4co3-.mjs → SubscriberGenerator-Dnlj_1FK.mjs} +2 -1
  16. package/dist/SubscriberGenerator-Dnlj_1FK.mjs.map +1 -0
  17. package/dist/build/index.cjs +5 -5
  18. package/dist/build/index.mjs +5 -5
  19. package/dist/{build-Ajg356_5.cjs → build-BVng9MQX.cjs} +5 -5
  20. package/dist/{build-Ajg356_5.cjs.map → build-BVng9MQX.cjs.map} +1 -1
  21. package/dist/{build-zpABVsc0.mjs → build-BqexeI-W.mjs} +5 -5
  22. package/dist/{build-zpABVsc0.mjs.map → build-BqexeI-W.mjs.map} +1 -1
  23. package/dist/dev/index.cjs +13 -0
  24. package/dist/dev/index.mjs +11 -0
  25. package/dist/dev-DbtyToc7.cjs +259 -0
  26. package/dist/dev-DbtyToc7.cjs.map +1 -0
  27. package/dist/dev-DnGYXuMn.mjs +241 -0
  28. package/dist/dev-DnGYXuMn.mjs.map +1 -0
  29. package/dist/generators/CronGenerator.cjs +1 -1
  30. package/dist/generators/CronGenerator.mjs +1 -1
  31. package/dist/generators/EndpointGenerator.cjs +1 -1
  32. package/dist/generators/EndpointGenerator.mjs +1 -1
  33. package/dist/generators/FunctionGenerator.cjs +1 -1
  34. package/dist/generators/FunctionGenerator.mjs +1 -1
  35. package/dist/generators/SubscriberGenerator.cjs +1 -1
  36. package/dist/generators/SubscriberGenerator.mjs +1 -1
  37. package/dist/generators/index.cjs +4 -4
  38. package/dist/generators/index.mjs +4 -4
  39. package/dist/index.cjs +33 -12
  40. package/dist/index.cjs.map +1 -1
  41. package/dist/index.mjs +33 -12
  42. package/dist/index.mjs.map +1 -1
  43. package/dist/{openapi-BQx3_JbM.mjs → openapi-BTHbPrxS.mjs} +2 -2
  44. package/dist/{openapi-BQx3_JbM.mjs.map → openapi-BTHbPrxS.mjs.map} +1 -1
  45. package/dist/{openapi-CMLr04cz.cjs → openapi-CewcfoRH.cjs} +2 -2
  46. package/dist/{openapi-CMLr04cz.cjs.map → openapi-CewcfoRH.cjs.map} +1 -1
  47. package/dist/{openapi-react-query-Dvjqx_Eo.cjs → openapi-react-query-D9Z7lh0p.cjs} +1 -1
  48. package/dist/{openapi-react-query-Dvjqx_Eo.cjs.map → openapi-react-query-D9Z7lh0p.cjs.map} +1 -1
  49. package/dist/{openapi-react-query-DbrWwQzb.mjs → openapi-react-query-MEBlYIM1.mjs} +1 -1
  50. package/dist/{openapi-react-query-DbrWwQzb.mjs.map → openapi-react-query-MEBlYIM1.mjs.map} +1 -1
  51. package/dist/openapi-react-query.cjs +1 -1
  52. package/dist/openapi-react-query.mjs +1 -1
  53. package/dist/openapi.cjs +2 -2
  54. package/dist/openapi.mjs +2 -2
  55. package/package.json +12 -6
  56. package/src/dev/__tests__/index.spec.ts +208 -0
  57. package/src/dev/index.ts +377 -0
  58. package/src/generators/CronGenerator.ts +8 -3
  59. package/src/generators/EndpointGenerator.ts +94 -6
  60. package/src/generators/FunctionGenerator.ts +21 -3
  61. package/src/generators/SubscriberGenerator.ts +1 -0
  62. package/src/generators/__tests__/SubscriberGenerator.spec.ts +12 -9
  63. package/src/index.ts +27 -0
  64. package/src/types.ts +3 -0
  65. package/dist/CronGenerator-1PflEYe2.cjs.map +0 -1
  66. package/dist/CronGenerator-DXRfHQcV.mjs.map +0 -1
  67. package/dist/EndpointGenerator-BbGrDiCP.cjs.map +0 -1
  68. package/dist/EndpointGenerator-BmZ9BxbO.mjs.map +0 -1
  69. package/dist/FunctionGenerator-Clw64SwQ.cjs.map +0 -1
  70. package/dist/FunctionGenerator-DOEB_yPh.mjs.map +0 -1
  71. package/dist/SubscriberGenerator-CB-NHtZW.cjs.map +0 -1
  72. package/dist/SubscriberGenerator-Cuu4co3-.mjs.map +0 -1
@@ -0,0 +1,208 @@
1
+ import { createServer } from 'node:net';
2
+ import { describe, expect, it } from 'vitest';
3
+ import { findAvailablePort, isPortAvailable } from '../index';
4
+
5
+ /**
6
+ * Helper to occupy a port for testing
7
+ */
8
+ function occupyPort(port: number): Promise<ReturnType<typeof createServer>> {
9
+ return new Promise((resolve, reject) => {
10
+ const server = createServer();
11
+
12
+ server.once('error', (err) => {
13
+ reject(err);
14
+ });
15
+
16
+ server.once('listening', () => {
17
+ resolve(server);
18
+ });
19
+
20
+ server.listen(port);
21
+ });
22
+ }
23
+
24
+ describe('Port Availability Functions', () => {
25
+ describe('isPortAvailable', () => {
26
+ it('should return true for an available port', async () => {
27
+ // Use a high port number to avoid conflicts
28
+ const port = 45000;
29
+ const available = await isPortAvailable(port);
30
+ expect(available).toBe(true);
31
+ });
32
+
33
+ it('should return false for a port in use', async () => {
34
+ const port = 45001;
35
+ const server = await occupyPort(port);
36
+
37
+ try {
38
+ const available = await isPortAvailable(port);
39
+ expect(available).toBe(false);
40
+ } finally {
41
+ server.close();
42
+ }
43
+ });
44
+
45
+ it('should handle multiple sequential checks correctly', async () => {
46
+ const port = 45002;
47
+
48
+ // First check - port should be available
49
+ const firstCheck = await isPortAvailable(port);
50
+ expect(firstCheck).toBe(true);
51
+
52
+ // Occupy the port
53
+ const server = await occupyPort(port);
54
+
55
+ try {
56
+ // Second check - port should be unavailable
57
+ const secondCheck = await isPortAvailable(port);
58
+ expect(secondCheck).toBe(false);
59
+ } finally {
60
+ server.close();
61
+ }
62
+
63
+ // Give a moment for the port to be released
64
+ await new Promise((resolve) => setTimeout(resolve, 100));
65
+
66
+ // Third check - port should be available again
67
+ const thirdCheck = await isPortAvailable(port);
68
+ expect(thirdCheck).toBe(true);
69
+ });
70
+ });
71
+
72
+ describe('findAvailablePort', () => {
73
+ it('should return the preferred port if available', async () => {
74
+ const preferredPort = 45100;
75
+ const foundPort = await findAvailablePort(preferredPort);
76
+ expect(foundPort).toBe(preferredPort);
77
+ });
78
+
79
+ it('should return the next available port if preferred is in use', async () => {
80
+ const preferredPort = 45101;
81
+ const server = await occupyPort(preferredPort);
82
+
83
+ try {
84
+ const foundPort = await findAvailablePort(preferredPort);
85
+ expect(foundPort).toBe(preferredPort + 1);
86
+ } finally {
87
+ server.close();
88
+ }
89
+ });
90
+
91
+ it('should skip multiple occupied ports', async () => {
92
+ const preferredPort = 45102;
93
+ const server1 = await occupyPort(preferredPort);
94
+ const server2 = await occupyPort(preferredPort + 1);
95
+ const server3 = await occupyPort(preferredPort + 2);
96
+
97
+ try {
98
+ const foundPort = await findAvailablePort(preferredPort);
99
+ expect(foundPort).toBe(preferredPort + 3);
100
+ } finally {
101
+ server1.close();
102
+ server2.close();
103
+ server3.close();
104
+ }
105
+ });
106
+
107
+ it('should throw error if no available port found within max attempts', async () => {
108
+ const preferredPort = 45103;
109
+ const maxAttempts = 3;
110
+
111
+ // Occupy ports 45103, 45104, 45105
112
+ const servers = await Promise.all([
113
+ occupyPort(preferredPort),
114
+ occupyPort(preferredPort + 1),
115
+ occupyPort(preferredPort + 2),
116
+ ]);
117
+
118
+ try {
119
+ await expect(
120
+ findAvailablePort(preferredPort, maxAttempts),
121
+ ).rejects.toThrow(
122
+ `Could not find an available port after trying ${maxAttempts} ports starting from ${preferredPort}`,
123
+ );
124
+ } finally {
125
+ servers.forEach((server) => server.close());
126
+ }
127
+ });
128
+
129
+ it('should respect custom maxAttempts parameter', async () => {
130
+ const preferredPort = 45104;
131
+ const maxAttempts = 5;
132
+
133
+ // Occupy first 4 ports
134
+ const servers = await Promise.all([
135
+ occupyPort(preferredPort),
136
+ occupyPort(preferredPort + 1),
137
+ occupyPort(preferredPort + 2),
138
+ occupyPort(preferredPort + 3),
139
+ ]);
140
+
141
+ try {
142
+ const foundPort = await findAvailablePort(preferredPort, maxAttempts);
143
+ // Should find port at preferredPort + 4 (within 5 attempts)
144
+ expect(foundPort).toBe(preferredPort + 4);
145
+ } finally {
146
+ servers.forEach((server) => server.close());
147
+ }
148
+ });
149
+ });
150
+ });
151
+
152
+ describe('DevServer', () => {
153
+ describe('port selection', () => {
154
+ it('should use requested port when available', async () => {
155
+ // This is more of an integration test that would need the actual DevServer
156
+ // For now, we test the underlying logic
157
+ const requestedPort = 45200;
158
+ const actualPort = await findAvailablePort(requestedPort);
159
+ expect(actualPort).toBe(requestedPort);
160
+ });
161
+
162
+ it('should select alternative port when requested is in use', async () => {
163
+ const requestedPort = 45201;
164
+ const server = await occupyPort(requestedPort);
165
+
166
+ try {
167
+ const actualPort = await findAvailablePort(requestedPort);
168
+ expect(actualPort).not.toBe(requestedPort);
169
+ expect(actualPort).toBeGreaterThan(requestedPort);
170
+ expect(actualPort).toBeLessThanOrEqual(requestedPort + 10);
171
+ } finally {
172
+ server.close();
173
+ }
174
+ });
175
+ });
176
+ });
177
+
178
+ describe('devCommand edge cases', () => {
179
+ it('should handle port conflicts gracefully', async () => {
180
+ const port = 45300;
181
+ const server = await occupyPort(port);
182
+
183
+ try {
184
+ // The dev command should find an alternative port
185
+ const alternativePort = await findAvailablePort(port);
186
+ expect(alternativePort).toBeGreaterThan(port);
187
+ } finally {
188
+ server.close();
189
+ }
190
+ });
191
+
192
+ it('should handle concurrent port checks', async () => {
193
+ const basePort = 45400;
194
+
195
+ // Run multiple port checks concurrently
196
+ const results = await Promise.all([
197
+ findAvailablePort(basePort),
198
+ findAvailablePort(basePort + 5),
199
+ findAvailablePort(basePort + 10),
200
+ ]);
201
+
202
+ // All should succeed and return valid ports
203
+ expect(results).toHaveLength(3);
204
+ results.forEach((port) => {
205
+ expect(port).toBeGreaterThanOrEqual(basePort);
206
+ });
207
+ });
208
+ });
@@ -0,0 +1,377 @@
1
+ import { type ChildProcess, spawn } from 'node:child_process';
2
+ import { mkdir } from 'node:fs/promises';
3
+ import { createServer } from 'node:net';
4
+ import { join } from 'node:path';
5
+ import chokidar from 'chokidar';
6
+ import { resolveProviders } from '../build/providerResolver';
7
+ import type { BuildContext } from '../build/types';
8
+ import { loadConfig } from '../config';
9
+ import {
10
+ CronGenerator,
11
+ EndpointGenerator,
12
+ FunctionGenerator,
13
+ SubscriberGenerator,
14
+ } from '../generators';
15
+ import type { LegacyProvider } from '../types';
16
+
17
+ const logger = console;
18
+
19
+ /**
20
+ * Check if a port is available
21
+ * @internal Exported for testing
22
+ */
23
+ export async function isPortAvailable(port: number): Promise<boolean> {
24
+ return new Promise((resolve) => {
25
+ const server = createServer();
26
+
27
+ server.once('error', (err: NodeJS.ErrnoException) => {
28
+ if (err.code === 'EADDRINUSE') {
29
+ resolve(false);
30
+ } else {
31
+ resolve(false);
32
+ }
33
+ });
34
+
35
+ server.once('listening', () => {
36
+ server.close();
37
+ resolve(true);
38
+ });
39
+
40
+ server.listen(port);
41
+ });
42
+ }
43
+
44
+ /**
45
+ * Find an available port starting from the preferred port
46
+ * @internal Exported for testing
47
+ */
48
+ export async function findAvailablePort(
49
+ preferredPort: number,
50
+ maxAttempts = 10,
51
+ ): Promise<number> {
52
+ for (let i = 0; i < maxAttempts; i++) {
53
+ const port = preferredPort + i;
54
+ if (await isPortAvailable(port)) {
55
+ return port;
56
+ }
57
+ logger.log(`⚠️ Port ${port} is in use, trying ${port + 1}...`);
58
+ }
59
+
60
+ throw new Error(
61
+ `Could not find an available port after trying ${maxAttempts} ports starting from ${preferredPort}`,
62
+ );
63
+ }
64
+
65
+ export interface DevOptions {
66
+ port?: number;
67
+ enableOpenApi?: boolean;
68
+ }
69
+
70
+ export async function devCommand(options: DevOptions): Promise<void> {
71
+ const config = await loadConfig();
72
+
73
+ // Force server provider for dev mode
74
+ const resolved = resolveProviders(config, { provider: 'server' });
75
+
76
+ logger.log('🚀 Starting development server...');
77
+ logger.log(`Loading routes from: ${config.routes}`);
78
+ if (config.functions) {
79
+ logger.log(`Loading functions from: ${config.functions}`);
80
+ }
81
+ if (config.crons) {
82
+ logger.log(`Loading crons from: ${config.crons}`);
83
+ }
84
+ if (config.subscribers) {
85
+ logger.log(`Loading subscribers from: ${config.subscribers}`);
86
+ }
87
+ logger.log(`Using envParser: ${config.envParser}`);
88
+
89
+ // Parse envParser configuration
90
+ const [envParserPath, envParserName] = config.envParser.split('#');
91
+ const envParserImportPattern = !envParserName
92
+ ? 'envParser'
93
+ : envParserName === 'envParser'
94
+ ? '{ envParser }'
95
+ : `{ ${envParserName} as envParser }`;
96
+
97
+ // Parse logger configuration
98
+ const [loggerPath, loggerName] = config.logger.split('#');
99
+ const loggerImportPattern = !loggerName
100
+ ? 'logger'
101
+ : loggerName === 'logger'
102
+ ? '{ logger }'
103
+ : `{ ${loggerName} as logger }`;
104
+
105
+ const buildContext: BuildContext = {
106
+ envParserPath,
107
+ envParserImportPattern,
108
+ loggerPath,
109
+ loggerImportPattern,
110
+ };
111
+
112
+ // Build initial version
113
+ await buildServer(
114
+ config,
115
+ buildContext,
116
+ resolved.providers[0] as LegacyProvider,
117
+ resolved.enableOpenApi,
118
+ );
119
+
120
+ // Start the dev server
121
+ const devServer = new DevServer(
122
+ resolved.providers[0] as LegacyProvider,
123
+ options.port || 3000,
124
+ resolved.enableOpenApi,
125
+ );
126
+
127
+ await devServer.start();
128
+
129
+ // Watch for file changes
130
+ const watchPatterns = [
131
+ config.routes,
132
+ ...(config.functions ? [config.functions] : []),
133
+ ...(config.crons ? [config.crons] : []),
134
+ ...(config.subscribers ? [config.subscribers] : []),
135
+ config.envParser.split('#')[0],
136
+ config.logger.split('#')[0],
137
+ ].flat();
138
+
139
+ logger.log(`👀 Watching for changes in: ${watchPatterns.join(', ')}`);
140
+
141
+ const watcher = chokidar.watch(watchPatterns, {
142
+ ignored: /(^|[\/\\])\../, // ignore dotfiles
143
+ persistent: true,
144
+ ignoreInitial: true,
145
+ });
146
+
147
+ let rebuildTimeout: NodeJS.Timeout | null = null;
148
+
149
+ watcher.on('change', async (path) => {
150
+ logger.log(`📝 File changed: ${path}`);
151
+
152
+ // Debounce rebuilds
153
+ if (rebuildTimeout) {
154
+ clearTimeout(rebuildTimeout);
155
+ }
156
+
157
+ rebuildTimeout = setTimeout(async () => {
158
+ try {
159
+ logger.log('🔄 Rebuilding...');
160
+ await buildServer(
161
+ config,
162
+ buildContext,
163
+ resolved.providers[0] as LegacyProvider,
164
+ resolved.enableOpenApi,
165
+ );
166
+ logger.log('✅ Rebuild complete, restarting server...');
167
+ await devServer.restart();
168
+ } catch (error) {
169
+ logger.error('❌ Rebuild failed:', (error as Error).message);
170
+ }
171
+ }, 300);
172
+ });
173
+
174
+ // Handle graceful shutdown
175
+ const shutdown = async () => {
176
+ logger.log('\n🛑 Shutting down...');
177
+ await watcher.close();
178
+ await devServer.stop();
179
+ process.exit(0);
180
+ };
181
+
182
+ process.on('SIGINT', shutdown);
183
+ process.on('SIGTERM', shutdown);
184
+ }
185
+
186
+ async function buildServer(
187
+ config: any,
188
+ context: BuildContext,
189
+ provider: LegacyProvider,
190
+ enableOpenApi: boolean,
191
+ ): Promise<void> {
192
+ // Initialize generators
193
+ const endpointGenerator = new EndpointGenerator();
194
+ const functionGenerator = new FunctionGenerator();
195
+ const cronGenerator = new CronGenerator();
196
+ const subscriberGenerator = new SubscriberGenerator();
197
+
198
+ // Load all constructs
199
+ const [allEndpoints, allFunctions, allCrons, allSubscribers] =
200
+ await Promise.all([
201
+ endpointGenerator.load(config.routes),
202
+ config.functions ? functionGenerator.load(config.functions) : [],
203
+ config.crons ? cronGenerator.load(config.crons) : [],
204
+ config.subscribers ? subscriberGenerator.load(config.subscribers) : [],
205
+ ]);
206
+
207
+ // Ensure .gkm directory exists
208
+ const outputDir = join(process.cwd(), '.gkm', provider);
209
+ await mkdir(outputDir, { recursive: true });
210
+
211
+ // Build for server provider
212
+ await Promise.all([
213
+ endpointGenerator.build(context, allEndpoints, outputDir, {
214
+ provider,
215
+ enableOpenApi,
216
+ }),
217
+ functionGenerator.build(context, allFunctions, outputDir, { provider }),
218
+ cronGenerator.build(context, allCrons, outputDir, { provider }),
219
+ subscriberGenerator.build(context, allSubscribers, outputDir, { provider }),
220
+ ]);
221
+ }
222
+
223
+ class DevServer {
224
+ private serverProcess: ChildProcess | null = null;
225
+ private isRunning = false;
226
+ private actualPort: number;
227
+
228
+ constructor(
229
+ private provider: LegacyProvider,
230
+ private requestedPort: number,
231
+ private enableOpenApi: boolean,
232
+ ) {
233
+ this.actualPort = requestedPort;
234
+ }
235
+
236
+ async start(): Promise<void> {
237
+ if (this.isRunning) {
238
+ await this.stop();
239
+ }
240
+
241
+ // Find an available port
242
+ this.actualPort = await findAvailablePort(this.requestedPort);
243
+
244
+ if (this.actualPort !== this.requestedPort) {
245
+ logger.log(
246
+ `ℹ️ Port ${this.requestedPort} was in use, using port ${this.actualPort} instead`,
247
+ );
248
+ }
249
+
250
+ const serverEntryPath = join(
251
+ process.cwd(),
252
+ '.gkm',
253
+ this.provider,
254
+ 'server.ts',
255
+ );
256
+
257
+ // Create server entry file
258
+ await this.createServerEntry();
259
+
260
+ logger.log(`\n✨ Starting server on port ${this.actualPort}...`);
261
+
262
+ // Start the server using tsx (TypeScript execution)
263
+ this.serverProcess = spawn(
264
+ 'npx',
265
+ ['tsx', serverEntryPath, '--port', this.actualPort.toString()],
266
+ {
267
+ stdio: 'inherit',
268
+ env: { ...process.env, NODE_ENV: 'development' },
269
+ },
270
+ );
271
+
272
+ this.isRunning = true;
273
+
274
+ this.serverProcess.on('error', (error) => {
275
+ logger.error('❌ Server error:', error);
276
+ });
277
+
278
+ this.serverProcess.on('exit', (code, signal) => {
279
+ if (code !== null && code !== 0 && signal !== 'SIGTERM') {
280
+ logger.error(`❌ Server exited with code ${code}`);
281
+ }
282
+ this.isRunning = false;
283
+ });
284
+
285
+ // Give the server a moment to start
286
+ await new Promise((resolve) => setTimeout(resolve, 1000));
287
+
288
+ if (this.isRunning) {
289
+ logger.log(`\n🎉 Server running at http://localhost:${this.actualPort}`);
290
+ if (this.enableOpenApi) {
291
+ logger.log(
292
+ `📚 API Docs available at http://localhost:${this.actualPort}/docs`,
293
+ );
294
+ }
295
+ }
296
+ }
297
+
298
+ async stop(): Promise<void> {
299
+ if (this.serverProcess && this.isRunning) {
300
+ this.serverProcess.kill('SIGTERM');
301
+
302
+ // Wait for process to exit
303
+ await new Promise<void>((resolve) => {
304
+ const timeout = setTimeout(() => {
305
+ this.serverProcess?.kill('SIGKILL');
306
+ resolve();
307
+ }, 5000);
308
+
309
+ this.serverProcess?.on('exit', () => {
310
+ clearTimeout(timeout);
311
+ resolve();
312
+ });
313
+ });
314
+
315
+ this.serverProcess = null;
316
+ this.isRunning = false;
317
+ }
318
+ }
319
+
320
+ async restart(): Promise<void> {
321
+ await this.stop();
322
+ await this.start();
323
+ }
324
+
325
+ private async createServerEntry(): Promise<void> {
326
+ const { writeFile } = await import('node:fs/promises');
327
+ const { relative, dirname } = await import('node:path');
328
+
329
+ const serverPath = join(process.cwd(), '.gkm', this.provider, 'server.ts');
330
+
331
+ const relativeAppPath = relative(
332
+ dirname(serverPath),
333
+ join(dirname(serverPath), 'app.js'),
334
+ );
335
+
336
+ const content = `#!/usr/bin/env node
337
+ /**
338
+ * Development server entry point
339
+ * This file is auto-generated by 'gkm dev'
340
+ */
341
+ import { createApp } from './${relativeAppPath.startsWith('.') ? relativeAppPath : './' + relativeAppPath}';
342
+
343
+ const port = process.argv.includes('--port')
344
+ ? Number.parseInt(process.argv[process.argv.indexOf('--port') + 1])
345
+ : 3000;
346
+
347
+ const { app, start } = createApp(undefined, ${this.enableOpenApi});
348
+
349
+ // Start the server
350
+ start({
351
+ port,
352
+ serve: async (app, port) => {
353
+ // Detect runtime and use appropriate server
354
+ if (typeof Bun !== 'undefined') {
355
+ // Bun runtime
356
+ Bun.serve({
357
+ port,
358
+ fetch: app.fetch,
359
+ });
360
+ } else {
361
+ // Node.js runtime with @hono/node-server
362
+ const { serve } = await import('@hono/node-server');
363
+ serve({
364
+ fetch: app.fetch,
365
+ port,
366
+ });
367
+ }
368
+ },
369
+ }).catch((error) => {
370
+ console.error('Failed to start server:', error);
371
+ process.exit(1);
372
+ });
373
+ `;
374
+
375
+ await writeFile(serverPath, content);
376
+ }
377
+ }
@@ -10,12 +10,14 @@ import {
10
10
  } from './Generator';
11
11
 
12
12
  export class CronGenerator extends ConstructGenerator<
13
- Cron<any, any, any, any>,
13
+ Cron<any, any, any, any, any, any, any, any>,
14
14
  CronInfo[]
15
15
  > {
16
16
  async build(
17
17
  context: BuildContext,
18
- constructs: GeneratedConstruct<Cron<any, any, any, any>>[],
18
+ constructs: GeneratedConstruct<
19
+ Cron<any, any, any, any, any, any, any, any>
20
+ >[],
19
21
  outputDir: string,
20
22
  options?: GeneratorOptions,
21
23
  ): Promise<CronInfo[]> {
@@ -48,6 +50,7 @@ export class CronGenerator extends ConstructGenerator<
48
50
  ),
49
51
  schedule: construct.schedule || 'rate(1 hour)',
50
52
  timeout: construct.timeout,
53
+ memorySize: construct.memorySize,
51
54
  environment: await construct.getEnvironment(),
52
55
  });
53
56
 
@@ -57,7 +60,9 @@ export class CronGenerator extends ConstructGenerator<
57
60
  return cronInfos;
58
61
  }
59
62
 
60
- isConstruct(value: any): value is Cron<any, any, any, any> {
63
+ isConstruct(
64
+ value: any,
65
+ ): value is Cron<any, any, any, any, any, any, any, any> {
61
66
  return Cron.isCron(value);
62
67
  }
63
68