@triophore/falcon-cli 1.0.0 → 1.0.2

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.
@@ -6,122 +6,120 @@ const { spawn } = require('child_process');
6
6
  const { showSuccess, runNpmInstall, createDirectory } = require('./utils');
7
7
 
8
8
  class ProjectGenerator {
9
- constructor(options = {}) {
10
- this.verbose = options.verbose || false;
11
- this.skipInstall = options.skipInstall || false;
12
- this.useDefaults = options.useDefaults || false;
9
+ constructor(options = {}) {
10
+ this.verbose = options.verbose || false;
11
+ this.skipInstall = options.skipInstall || false;
12
+ this.useDefaults = options.useDefaults || false;
13
+ }
14
+
15
+ async create(projectName, template = 'basic') {
16
+ const currentDir = process.cwd();
17
+
18
+ // Get project name if not provided
19
+ if (!projectName) {
20
+ projectName = await input({
21
+ message: 'Project name:',
22
+ validate: v => v.trim() ? true : 'Project name is required',
23
+ });
13
24
  }
14
25
 
15
- async create(projectName, template = 'basic') {
16
- const currentDir = process.cwd();
17
-
18
- // Get project name if not provided
19
- if (!projectName) {
20
- projectName = await input({
21
- message: 'Project name:',
22
- validate: v => v.trim() ? true : 'Project name is required',
23
- });
24
- }
25
-
26
- const projectPath = path.join(currentDir, projectName);
27
-
28
- // Check if directory exists
29
- try {
30
- await fs.access(projectPath);
31
- const overwrite = await confirm({
32
- message: `Directory "${projectName}" already exists. Overwrite?`,
33
- default: false
34
- });
35
- if (!overwrite) {
36
- console.log('āŒ Project creation cancelled');
37
- return;
38
- }
39
- } catch (error) {
40
- // Directory doesn't exist, which is good
41
- }
42
-
43
- console.log(`\nšŸš€ Creating Falcon.js project: ${projectName}\n`);
44
-
45
- // Create project structure
46
- await this.createProjectStructure(projectPath, projectName, template);
47
-
48
- // Install dependencies
49
- if (!this.skipInstall) {
50
- console.log('šŸ“¦ Installing dependencies...');
51
- await runNpmInstall(projectPath);
52
- }
53
-
54
- showSuccess(projectName, this.skipInstall);
26
+ const projectPath = path.join(currentDir, projectName);
27
+
28
+ // Check if directory exists
29
+ try {
30
+ await fs.access(projectPath);
31
+ const overwrite = await confirm({
32
+ message: `Directory "${projectName}" already exists. Overwrite?`,
33
+ default: false
34
+ });
35
+ if (!overwrite) {
36
+ console.log('āŒ Project creation cancelled');
37
+ return;
38
+ }
39
+ } catch (error) {
40
+ // Directory doesn't exist, which is good
55
41
  }
56
42
 
57
- async createProjectStructure(projectPath, projectName, template) {
58
- // Create directories
59
- const directories = [
60
- 'models/mongo',
61
- 'routes',
62
- 'services',
63
- 'workers',
64
- 'validators',
65
- 'init',
66
- 'logs',
67
- 'public'
68
- ];
69
-
70
- for (const dir of directories) {
71
- await createDirectory(path.join(projectPath, dir));
72
- }
73
-
74
- // Create files
75
- await this.createPackageJson(projectPath, projectName);
76
- await this.createSettings(projectPath, projectName);
77
- await this.createIndexJs(projectPath);
78
- await this.createEnvFile(projectPath, projectName);
79
- await this.createExampleFiles(projectPath);
43
+ console.log(`\nšŸš€ Creating Falcon.js project: ${projectName}\n`);
44
+
45
+ // Create project structure
46
+ await this.createProjectStructure(projectPath, projectName, template);
47
+
48
+ // Install dependencies
49
+ if (!this.skipInstall) {
50
+ console.log('šŸ“¦ Installing dependencies...');
51
+ await runNpmInstall(projectPath);
80
52
  }
81
53
 
82
- async createPackageJson(projectPath, projectName) {
83
- const packageJson = {
84
- name: projectName,
85
- version: "1.0.0",
86
- description: "Falcon.js application",
87
- main: "index.js",
88
- scripts: {
89
- start: "node index.js",
90
- dev: "nodemon index.js",
91
- test: "echo \"Error: no test specified\" && exit 1"
92
- },
93
- keywords: ["falcon", "nodejs", "api"],
94
- author: "",
95
- license: "ISC",
96
- type: "commonjs",
97
- dependencies: {
98
- "falconjs": "file:../falconjs",
99
- "@hapi/boom": "^10.0.1",
100
- "@hapi/inert": "^7.1.0",
101
- "@hapi/vision": "^7.0.3",
102
- "joi": "^17.13.3",
103
- "mongoose": "^8.3.1",
104
- "redis": "^4.7.0",
105
- "mqtt": "^5.13.3",
106
- "log4js": "^6.9.1",
107
- "rate-limiter-flexible": "^2.4.2",
108
- "hapi-alive": "^2.0.4",
109
- "hapi-swagger": "^17.2.1",
110
- "dotenv": "^16.0.0"
111
- },
112
- devDependencies: {
113
- "nodemon": "^3.1.10"
114
- }
115
- };
116
-
117
- await fs.writeFile(
118
- path.join(projectPath, 'package.json'),
119
- JSON.stringify(packageJson, null, 2)
120
- );
54
+ showSuccess(projectName, this.skipInstall);
55
+ }
56
+
57
+ async createProjectStructure(projectPath, projectName, template) {
58
+ // Create directories
59
+ const directories = [
60
+ 'models/mongo',
61
+ 'routes',
62
+ 'services',
63
+ 'workers',
64
+ 'validators',
65
+ 'init',
66
+ 'logs',
67
+ 'public'
68
+ ];
69
+
70
+ for (const dir of directories) {
71
+ await createDirectory(path.join(projectPath, dir));
121
72
  }
122
73
 
123
- async createSettings(projectPath, projectName) {
124
- const settings = `module.exports.settings = {
74
+ // Create files
75
+ await this.createPackageJson(projectPath, projectName);
76
+ await this.createSettings(projectPath, projectName);
77
+ await this.createIndexJs(projectPath);
78
+ await this.createEnvFile(projectPath, projectName);
79
+ await this.createExampleFiles(projectPath);
80
+ }
81
+
82
+ async createPackageJson(projectPath, projectName) {
83
+ const packageJson = {
84
+ name: projectName,
85
+ version: "1.0.0",
86
+ description: "Falcon.js application",
87
+ main: "index.js",
88
+ scripts: {
89
+ start: "node index.js",
90
+ dev: "nodemon index.js",
91
+ test: "echo \"Error: no test specified\" && exit 1"
92
+ },
93
+ keywords: ["falcon", "nodejs", "api"],
94
+ author: "",
95
+ license: "ISC",
96
+ type: "commonjs",
97
+ dependencies: {
98
+ "@triophore/falconjs": "^1.0.0",
99
+ "@hapi/boom": "^10.0.1",
100
+ "@hapi/inert": "^7.1.0",
101
+ "@hapi/vision": "^7.0.3",
102
+ "joi": "^17.13.3",
103
+ "mongoose": "^8.3.1",
104
+ "redis": "^4.7.0",
105
+ "rate-limiter-flexible": "^2.4.2",
106
+ "hapi-alive": "^2.0.4",
107
+ "hapi-swagger": "^17.2.1",
108
+ "dotenv": "^16.0.0"
109
+ },
110
+ devDependencies: {
111
+ "nodemon": "^3.1.10"
112
+ }
113
+ };
114
+
115
+ await fs.writeFile(
116
+ path.join(projectPath, 'package.json'),
117
+ JSON.stringify(packageJson, null, 2)
118
+ );
119
+ }
120
+
121
+ async createSettings(projectPath, projectName) {
122
+ const settings = `module.exports.settings = {
125
123
  name: "${projectName}",
126
124
  http: {
127
125
  host: process.env.HTTP_HOST || "localhost",
@@ -179,19 +177,19 @@ class ProjectGenerator {
179
177
  postInit: "post"
180
178
  };`;
181
179
 
182
- await fs.writeFile(path.join(projectPath, 'settings.js'), settings);
183
- }
180
+ await fs.writeFile(path.join(projectPath, 'settings.js'), settings);
181
+ }
184
182
 
185
- async createIndexJs(projectPath) {
186
- const indexContent = await fs.readFile(
187
- path.join(__dirname, '../templates/index.txt'),
188
- 'utf8'
189
- );
190
- await fs.writeFile(path.join(projectPath, 'index.js'), indexContent);
191
- }
183
+ async createIndexJs(projectPath) {
184
+ const indexContent = await fs.readFile(
185
+ path.join(__dirname, '../templates/index.txt'),
186
+ 'utf8'
187
+ );
188
+ await fs.writeFile(path.join(projectPath, 'index.js'), indexContent);
189
+ }
192
190
 
193
- async createEnvFile(projectPath, projectName) {
194
- const envContent = `# Falcon.js Environment Configuration
191
+ async createEnvFile(projectPath, projectName) {
192
+ const envContent = `# Falcon.js Environment Configuration
195
193
  # Generated by falcon-cli
196
194
 
197
195
  # Server Configuration
@@ -221,26 +219,26 @@ LOG_MAX_SIZE=10M
221
219
  MODE=DEV
222
220
  `;
223
221
 
224
- await fs.writeFile(path.join(projectPath, '.env'), envContent);
225
- }
226
-
227
- async createExampleFiles(projectPath) {
228
- const templates = [
229
- { src: 'example-route.js', dest: 'routes/example.js' },
230
- { src: 'example-validator.js', dest: 'validators/ExamplePayload.js' },
231
- { src: 'example-service.js', dest: 'services/example.js' },
232
- { src: 'example-worker.js', dest: 'workers/example.js' },
233
- { src: 'post-init.js', dest: 'init/post.js' }
234
- ];
235
-
236
- for (const template of templates) {
237
- const content = await fs.readFile(
238
- path.join(__dirname, '../templates', template.src),
239
- 'utf8'
240
- );
241
- await fs.writeFile(path.join(projectPath, template.dest), content);
242
- }
222
+ await fs.writeFile(path.join(projectPath, '.env'), envContent);
223
+ }
224
+
225
+ async createExampleFiles(projectPath) {
226
+ const templates = [
227
+ { src: 'example-route.js', dest: 'routes/example.js' },
228
+ { src: 'example-validator.js', dest: 'validators/ExamplePayload.js' },
229
+ { src: 'example-service.js', dest: 'services/example.js' },
230
+ { src: 'example-worker.js', dest: 'workers/example.js' },
231
+ { src: 'post-init.js', dest: 'init/post.js' }
232
+ ];
233
+
234
+ for (const template of templates) {
235
+ const content = await fs.readFile(
236
+ path.join(__dirname, '../templates', template.src),
237
+ 'utf8'
238
+ );
239
+ await fs.writeFile(path.join(projectPath, template.dest), content);
243
240
  }
241
+ }
244
242
  }
245
243
 
246
244
  module.exports = ProjectGenerator;
@@ -0,0 +1,101 @@
1
+ const { input, select, confirm } = require("@inquirer/prompts");
2
+ const fs = require("fs").promises;
3
+ const path = require("path");
4
+
5
+ class RouteGenerator {
6
+ constructor(options = {}) {
7
+ this.verbose = options.verbose || false;
8
+ this.type = options.type || 'route'; // 'route' or 'validator'
9
+ }
10
+
11
+ async generate(name) {
12
+ const currentDir = process.cwd();
13
+
14
+ // Check if we're in a Falcon project
15
+ try {
16
+ await fs.access(path.join(currentDir, 'settings.js'));
17
+ } catch (error) {
18
+ throw new Error('Not in a Falcon.js project directory. Run this command from your project root.');
19
+ }
20
+
21
+ if (!name) {
22
+ name = await input({
23
+ message: `${this.type === 'validator' ? 'Validator' : 'Route'} name (e.g., User):`,
24
+ validate: v => v.trim() ? true : 'Name is required',
25
+ });
26
+ }
27
+
28
+ if (this.type === 'validator') {
29
+ await this.generateValidator(currentDir, name);
30
+ } else {
31
+ await this.generateRoute(currentDir, name);
32
+ }
33
+ }
34
+
35
+ async generateRoute(projectDir, name) {
36
+ const routesDir = path.join(projectDir, 'routes');
37
+ await fs.mkdir(routesDir, { recursive: true });
38
+
39
+ // Determine path and filename
40
+ // If name contains /, treat as path
41
+ const parts = name.split('/');
42
+ const fileName = parts.pop();
43
+ const subDir = parts.join('/');
44
+
45
+ const targetDir = path.join(routesDir, subDir);
46
+ await fs.mkdir(targetDir, { recursive: true });
47
+
48
+ const filePath = path.join(targetDir, `${fileName}.js`);
49
+
50
+ // Check if file exists
51
+ try {
52
+ await fs.access(filePath);
53
+ const overwrite = await confirm({
54
+ message: `Route file ${filePath} already exists. Overwrite?`,
55
+ default: false
56
+ });
57
+ if (!overwrite) return;
58
+ } catch (e) {
59
+ // File doesn't exist, proceed
60
+ }
61
+
62
+ // Helper to get template content
63
+ const templatePath = path.join(__dirname, '..', 'templates', 'example-route.js');
64
+ let content = await fs.readFile(templatePath, 'utf8');
65
+
66
+ // Simple replacements if needed, or just use the example as a starter
67
+ // For now, we'll just write the template
68
+
69
+ await fs.writeFile(filePath, content);
70
+ console.log(`āœ… Route created: ${filePath}`);
71
+ }
72
+
73
+ async generateValidator(projectDir, name) {
74
+ const validatorsDir = path.join(projectDir, 'validators');
75
+ await fs.mkdir(validatorsDir, { recursive: true });
76
+
77
+ const filePath = path.join(validatorsDir, `${name}.js`);
78
+
79
+ // Check if file exists
80
+ try {
81
+ await fs.access(filePath);
82
+ const overwrite = await confirm({
83
+ message: `Validator file ${filePath} already exists. Overwrite?`,
84
+ default: false
85
+ });
86
+ if (!overwrite) return;
87
+ } catch (e) {
88
+ // File doesn't exist, proceed
89
+ }
90
+
91
+ const templatePath = path.join(__dirname, '..', 'templates', 'example-validator.js');
92
+ let content = await fs.readFile(templatePath, 'utf8');
93
+
94
+ // Replace Joi with Joi (no change needed usually, but good for future)
95
+
96
+ await fs.writeFile(filePath, content);
97
+ console.log(`āœ… Validator created: ${filePath}`);
98
+ }
99
+ }
100
+
101
+ module.exports = RouteGenerator;
@@ -0,0 +1,85 @@
1
+ const { input, confirm } = require("@inquirer/prompts");
2
+ const fs = require("fs").promises;
3
+ const path = require("path");
4
+
5
+ class ServiceGenerator {
6
+ constructor(options = {}) {
7
+ this.verbose = options.verbose || false;
8
+ this.type = options.type || 'service'; // 'service' or 'worker'
9
+ }
10
+
11
+ async generate(name) {
12
+ const currentDir = process.cwd();
13
+
14
+ // Check if we're in a Falcon project
15
+ try {
16
+ await fs.access(path.join(currentDir, 'settings.js'));
17
+ } catch (error) {
18
+ throw new Error('Not in a Falcon.js project directory. Run this command from your project root.');
19
+ }
20
+
21
+ if (!name) {
22
+ name = await input({
23
+ message: `${this.type === 'worker' ? 'Worker' : 'Service'} name (e.g., Email):`,
24
+ validate: v => v.trim() ? true : 'Name is required',
25
+ });
26
+ }
27
+
28
+ const dirName = this.type === 'worker' ? 'workers' : 'services';
29
+ const targetDir = path.join(currentDir, dirName);
30
+ await fs.mkdir(targetDir, { recursive: true });
31
+
32
+ const filePath = path.join(targetDir, `${name}.js`);
33
+
34
+ // Check if file exists
35
+ try {
36
+ await fs.access(filePath);
37
+ const overwrite = await confirm({
38
+ message: `${this.type === 'worker' ? 'Worker' : 'Service'} file ${filePath} already exists. Overwrite?`,
39
+ default: false
40
+ });
41
+ if (!overwrite) return;
42
+ } catch (e) {
43
+ // File doesn't exist, proceed
44
+ }
45
+
46
+ // Get template
47
+ const templateName = this.type === 'worker' ? 'example-worker.js' : 'example-service.js';
48
+ const templatePath = path.join(__dirname, '..', 'templates', templateName);
49
+ let content = await fs.readFile(templatePath, 'utf8');
50
+
51
+ await fs.writeFile(filePath, content);
52
+ console.log(`āœ… ${this.type === 'worker' ? 'Worker' : 'Service'} created: ${filePath}`);
53
+
54
+ // Update settings.js to include the service/worker
55
+ await this.updateSettings(currentDir, name, dirName);
56
+ }
57
+
58
+ async updateSettings(projectDir, name, listName) {
59
+ const settingsPath = path.join(projectDir, 'settings.js');
60
+ let settingsContent = await fs.readFile(settingsPath, 'utf8');
61
+
62
+ // Regex to find the array: services: [...] or workers: [...]
63
+ const regex = new RegExp(`${listName}:\\s*\\[(.*?)\\]`, 's');
64
+ const match = settingsContent.match(regex);
65
+
66
+ if (match) {
67
+ const listContent = match[1];
68
+ // Simple check if name is already there
69
+ if (!listContent.includes(`"${name}"`) && !listContent.includes(`'${name}'`)) {
70
+ const newListContent = listContent.trim()
71
+ ? `${listContent.trim()}, "${name}"`
72
+ : `"${name}"`;
73
+
74
+ settingsContent = settingsContent.replace(
75
+ regex,
76
+ `${listName}: [${newListContent}]`
77
+ );
78
+ await fs.writeFile(settingsPath, settingsContent);
79
+ console.log(`āœ… Added "${name}" to settings.js ${listName} array`);
80
+ }
81
+ }
82
+ }
83
+ }
84
+
85
+ module.exports = ServiceGenerator;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@triophore/falcon-cli",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -17,6 +17,7 @@
17
17
  "access": "public"
18
18
  },
19
19
  "dependencies": {
20
+ "@hapi/boom": "^10.0.1",
20
21
  "@inquirer/prompts": "^7.9.0",
21
22
  "acorn": "^8.15.0",
22
23
  "ajv": "^8.17.1",
@@ -25,11 +26,10 @@
25
26
  "joi": "^18.0.1",
26
27
  "mongoose": "^8.19.3",
27
28
  "prompt-sync": "^4.2.0",
29
+ "rate-limiter-flexible": "^2.4.2",
28
30
  "reflect-metadata": "^0.2.2",
29
31
  "sequelize": "^6.37.7",
30
32
  "unzipper": "^0.12.3",
31
- "yargs": "^18.0.0",
32
- "@hapi/boom": "^10.0.1",
33
- "rate-limiter-flexible": "^2.4.2"
33
+ "yargs": "^18.0.0"
34
34
  }
35
35
  }
@@ -1,10 +1,10 @@
1
- const { FalconBaseService } = require('falconjs');
1
+ const { FalconBaseService } = require('@triophore/falconjs');
2
2
 
3
3
  /**
4
4
  * Example service - rename and customize as needed
5
5
  */
6
6
  class ExampleService extends FalconBaseService {
7
-
7
+
8
8
  constructor() {
9
9
  super('example'); // Service ID for MQTT topics
10
10
  }
@@ -15,7 +15,7 @@ class ExampleService extends FalconBaseService {
15
15
  async onMessage(topic, msg) {
16
16
  try {
17
17
  const message = JSON.parse(msg.toString());
18
-
18
+
19
19
  switch (topic) {
20
20
  case 'service_example':
21
21
  await this.handleRequest(message);
@@ -33,9 +33,9 @@ class ExampleService extends FalconBaseService {
33
33
  */
34
34
  async handleRequest(request) {
35
35
  console.log('Processing request:', request);
36
-
36
+
37
37
  // Your service logic here
38
-
38
+
39
39
  // Send response back via MQTT
40
40
  this.publish({
41
41
  status: 'completed',
@@ -49,7 +49,7 @@ class ExampleService extends FalconBaseService {
49
49
  */
50
50
  async run() {
51
51
  console.log('Example service is running...');
52
-
52
+
53
53
  // Your initialization logic here
54
54
  }
55
55
  }
@@ -1,10 +1,10 @@
1
- const { FalconBaseWorker } = require('falconjs');
1
+ const { FalconBaseWorker } = require('@triophore/falconjs');
2
2
 
3
3
  /**
4
4
  * Example worker - rename and customize as needed
5
5
  */
6
6
  class ExampleWorker extends FalconBaseWorker {
7
-
7
+
8
8
  constructor() {
9
9
  super('example'); // Worker ID for MQTT topics
10
10
  }
@@ -15,7 +15,7 @@ class ExampleWorker extends FalconBaseWorker {
15
15
  async onMessage(topic, msg) {
16
16
  try {
17
17
  const message = JSON.parse(msg.toString());
18
-
18
+
19
19
  switch (topic) {
20
20
  case 'worker_example_job':
21
21
  await this.processJob(message);
@@ -33,12 +33,12 @@ class ExampleWorker extends FalconBaseWorker {
33
33
  */
34
34
  async processJob(job) {
35
35
  const { jobId, data } = job;
36
-
36
+
37
37
  console.log(`Processing job ${jobId}:`, data);
38
-
38
+
39
39
  // Simulate work
40
40
  await new Promise(resolve => setTimeout(resolve, 2000));
41
-
41
+
42
42
  // Update job status in database if available
43
43
  if (this.models.job) {
44
44
  await this.models.job.findByIdAndUpdate(jobId, {
@@ -47,7 +47,7 @@ class ExampleWorker extends FalconBaseWorker {
47
47
  result: { processed: true }
48
48
  });
49
49
  }
50
-
50
+
51
51
  // Send completion notification
52
52
  await this.publish('worker_example_complete', {
53
53
  jobId: jobId,
@@ -62,13 +62,13 @@ class ExampleWorker extends FalconBaseWorker {
62
62
  async run() {
63
63
  const args = this.parseArgs();
64
64
  console.log('Example worker started with args:', args);
65
-
65
+
66
66
  // If specific job passed as argument, process it immediately
67
67
  if (args.jobId) {
68
68
  await this.processJob(args);
69
69
  process.exit(0); // Exit after processing single job
70
70
  }
71
-
71
+
72
72
  // Otherwise, keep running and wait for MQTT jobs
73
73
  console.log('Waiting for jobs...');
74
74
  }
@@ -2,7 +2,7 @@
2
2
  * Falcon.js Application Entry Point
3
3
  */
4
4
  require('dotenv').config();
5
- const { FalconServer } = require("falconjs");
5
+ const { FalconServer } = require("@triophore/falconjs");
6
6
 
7
7
  async function main() {
8
8
  try {