@triophore/falcon-cli 1.0.1 → 1.0.3

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Triophore Technologies
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/auth/basic.js CHANGED
@@ -1,7 +1,5 @@
1
1
  module.exports.validate = async function (request, username, password, CONTXET) {
2
- // TODO: Implement your own logic here
3
- // Example: Check against database or environment variables
4
- if (username === process.env.ADMIN_USER && password === process.env.ADMIN_PASSWORD) {
2
+ if (username === 'admin' && password === 'secret') {
5
3
  return { isValid: true, credentials: { id: 1, name: 'Admin', roles: ['admin'], permissions: { admin: ['*'] } } };
6
4
  }
7
5
  return { isValid: false };
package/auth/cookie.js CHANGED
@@ -1,10 +1,6 @@
1
1
  module.exports.validate = async function (request, session, CONTXET) {
2
- // TODO: Implement your own logic here
3
- // Example: Validate session against database or Redis
4
- /*
5
- if (session.valid) {
6
- return { isValid: true, credentials: { id: session.userId, ... } };
2
+ if (username === 'admin' && password === 'secret') {
3
+ return { isValid: true, credentials: { id: 1, name: 'Admin', roles: ['admin'], permissions: { admin: ['*'] } } };
7
4
  }
8
- */
9
5
  return { isValid: false };
10
6
  }
package/auth/jwt.js CHANGED
@@ -1,9 +1,6 @@
1
1
  module.exports.validate = async function (decoded, CONTXET) {
2
- // TODO: Implement your own logic here
3
- // Example: Check against database or environment variables
4
- // For example, check if a specific environment variable matches a decoded value
5
- if (decoded.role === process.env.EXPECTED_ADMIN_ROLE && decoded.secret === process.env.EXPECTED_ADMIN_SECRET) {
6
- return { isValid: true, credentials: { id: decoded.sub, name: 'Admin', roles: ['admin'] } };
2
+ if (username === 'admin' && password === 'secret') {
3
+ return { isValid: true, credentials: { id: 1, name: 'Admin', roles: ['admin'], permissions: { admin: ['*'] } } };
7
4
  }
8
5
  return { isValid: false };
9
6
  }
package/auth/openid.js CHANGED
@@ -1,5 +1,6 @@
1
1
  module.exports.validate = async function (decoded, CONTXET) {
2
- // TODO: Implement your own logic here
3
- // Example: Validate OpenID token claims
2
+ if (username === 'admin' && password === 'secret') {
3
+ return { isValid: true, credentials: { id: 1, name: 'Admin', roles: ['admin'], permissions: { admin: ['*'] } } };
4
+ }
4
5
  return { isValid: false };
5
6
  }
package/index.js CHANGED
@@ -227,17 +227,17 @@ async function start() {
227
227
  }
228
228
 
229
229
  mkDir.push("models/mongo") // Fixed: falconjs expects models in models/mongo/
230
- mkDir.push("routes")
230
+ mkDir.push("routes")
231
231
  mkDir.push("services")
232
232
  mkDir.push("workers")
233
233
  mkDir.push("validators")
234
234
  mkDir.push("init")
235
235
  mkDir.push("logs")
236
-
236
+
237
237
  if (enable_static) {
238
238
  mkDir.push("public")
239
239
  }
240
-
240
+
241
241
  if (enable_templates) {
242
242
  mkDir.push("templates")
243
243
  }
@@ -332,27 +332,27 @@ async function start() {
332
332
 
333
333
  // Create example files
334
334
  await fs.promises.writeFile(
335
- path.join(currentDir, "routes", "example.js"),
335
+ path.join(currentDir, "routes", "example.js"),
336
336
  await fs.promises.readFile(path.join(__dirname, "templates", "example-route.js"))
337
337
  );
338
-
338
+
339
339
  await fs.promises.writeFile(
340
- path.join(currentDir, "validators", "ExamplePayload.js"),
340
+ path.join(currentDir, "validators", "ExamplePayload.js"),
341
341
  await fs.promises.readFile(path.join(__dirname, "templates", "example-validator.js"))
342
342
  );
343
-
343
+
344
344
  await fs.promises.writeFile(
345
- path.join(currentDir, "services", "example.js"),
345
+ path.join(currentDir, "services", "example.js"),
346
346
  await fs.promises.readFile(path.join(__dirname, "templates", "example-service.js"))
347
347
  );
348
-
348
+
349
349
  await fs.promises.writeFile(
350
- path.join(currentDir, "workers", "example.js"),
350
+ path.join(currentDir, "workers", "example.js"),
351
351
  await fs.promises.readFile(path.join(__dirname, "templates", "example-worker.js"))
352
352
  );
353
-
353
+
354
354
  await fs.promises.writeFile(
355
- path.join(currentDir, "init", "post.js"),
355
+ path.join(currentDir, "init", "post.js"),
356
356
  await fs.promises.readFile(path.join(__dirname, "templates", "post-init.js"))
357
357
  );
358
358
 
@@ -408,8 +408,8 @@ MODE=DEV
408
408
  2. npm install
409
409
  3. npm run dev
410
410
 
411
- 📚 Documentation will be available at: http://localhost:[PORT]/documentation
412
- ❤️ Health check at: http://localhost:[PORT]/health
411
+ 📚 Documentation will be available at: http://localhost:3000/documentation
412
+ ❤️ Health check at: http://localhost:3000/health
413
413
  `);
414
414
 
415
415
 
@@ -486,7 +486,7 @@ async function getProjectInfo() {
486
486
  async function create_mongoose_model() {
487
487
  mongoose_schema_builder(path.join(currentDir, "models", "mongo")) // Correct path for falconjs
488
488
  }
489
- // await fs.writeFileSync(model_path, JSON.stringify(res,null,2));
489
+ // await fs.writeFileSync(model_path, JSON.stringify(res,null,2));
490
490
 
491
491
 
492
492
 
@@ -4,124 +4,224 @@ const path = require("path");
4
4
  const { spawn } = require('child_process');
5
5
 
6
6
  const { showSuccess, runNpmInstall, createDirectory } = require('./utils');
7
+ const ModelGenerator = require('./ModelGenerator');
8
+ const RouteGenerator = require('./RouteGenerator');
7
9
 
8
10
  class ProjectGenerator {
9
- constructor(options = {}) {
10
- this.verbose = options.verbose || false;
11
- this.skipInstall = options.skipInstall || false;
12
- this.useDefaults = options.useDefaults || false;
11
+ constructor(options = {}) {
12
+ this.verbose = options.verbose || false;
13
+ this.skipInstall = options.skipInstall || false;
14
+ this.useDefaults = options.useDefaults || false;
15
+ }
16
+
17
+ async create(projectName, template = 'basic') {
18
+ const currentDir = process.cwd();
19
+
20
+ // Get project name if not provided
21
+ if (!projectName) {
22
+ projectName = await input({
23
+ message: 'Project name:',
24
+ validate: v => v.trim() ? true : 'Project name is required',
25
+ });
13
26
  }
14
27
 
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
- }
28
+ // Feature prompts
29
+ console.log('\n⚙️ Project Configuration:\n');
25
30
 
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
- }
31
+ const useSwagger = await confirm({
32
+ message: 'Enable Swagger/OpenAPI documentation?',
33
+ default: true
34
+ });
42
35
 
43
- console.log(`\n🚀 Creating Falcon.js project: ${projectName}\n`);
36
+ const useAuth = await confirm({
37
+ message: 'Include JWT Authentication setup?',
38
+ default: true
39
+ });
44
40
 
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
- }
41
+ // Collect Models
42
+ const initialModels = []; // Array of { name, content }
43
+ const createModels = await confirm({
44
+ message: 'Would you like to create some initial database models?',
45
+ default: false
46
+ });
47
+
48
+ if (createModels) {
49
+ const modelGenerator = new ModelGenerator({ verbose: this.verbose });
53
50
 
54
- showSuccess(projectName, this.skipInstall);
51
+ while (true) {
52
+ const modelName = await input({
53
+ message: 'Model name (leave empty to stop adding models):'
54
+ });
55
+
56
+ if (!modelName.trim()) break;
57
+
58
+ // Immediately build the model content (fields, etc.)
59
+ console.log(`\n📝 Defining model: ${modelName.trim()}`);
60
+ const content = await modelGenerator.buildModelInteractively(modelName.trim());
61
+
62
+ initialModels.push({
63
+ name: modelName.trim(),
64
+ content: content
65
+ });
66
+
67
+ console.log(`✅ Model definition saved for: ${modelName.trim()}\n`);
68
+ }
55
69
  }
56
70
 
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));
71
+ // Collect Routes
72
+ const initialRoutes = [];
73
+ const createRoutes = await confirm({
74
+ message: 'Would you like to create some initial API routes?',
75
+ default: false
76
+ });
77
+
78
+ if (createRoutes) {
79
+ while (true) {
80
+ const routeName = await input({
81
+ message: 'Route name (leave empty to stop adding routes):'
82
+ });
83
+ if (!routeName.trim()) break;
84
+ initialRoutes.push(routeName.trim());
85
+ }
86
+ }
87
+
88
+ const projectPath = path.join(currentDir, projectName);
89
+
90
+ // Check if directory exists
91
+ try {
92
+ await fs.access(projectPath);
93
+ const overwrite = await confirm({
94
+ message: `Directory "${projectName}" already exists. Overwrite?`,
95
+ default: false
96
+ });
97
+ if (!overwrite) {
98
+ console.log('❌ Project creation cancelled');
99
+ return;
100
+ }
101
+ } catch (error) {
102
+ // Directory doesn't exist, which is good
103
+ }
104
+
105
+ console.log(`\n🚀 Creating Falcon.js project: ${projectName}\n`);
106
+
107
+ // Create project structure
108
+ await this.createProjectStructure(projectPath, projectName, template, {
109
+ swagger: useSwagger,
110
+ auth: useAuth,
111
+ models: initialModels.map(m => m.name),
112
+ routes: initialRoutes
113
+ });
114
+
115
+ // Generate Models and Routes
116
+ if (initialModels.length > 0 || initialRoutes.length > 0) {
117
+ const originalCwd = process.cwd();
118
+ try {
119
+ process.chdir(projectPath);
120
+
121
+ if (initialModels.length > 0) {
122
+ console.log('\n🏗️ Generatings initial models...');
123
+ const modelsDir = path.join(projectPath, 'models', 'mongo');
124
+
125
+ for (const model of initialModels) {
126
+ const modelPath = path.join(modelsDir, `${model.name.toLowerCase()}.js`);
127
+ await fs.writeFile(modelPath, model.content);
128
+ console.log(`✅ Model file created: ${modelPath}`);
129
+ }
72
130
  }
73
131
 
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);
132
+ if (initialRoutes.length > 0) {
133
+ console.log('\n🛣️ Generating initial routes...');
134
+ const routeGenerator = new RouteGenerator({ verbose: this.verbose });
135
+ for (const route of initialRoutes) {
136
+ await routeGenerator.generate(route);
137
+ }
138
+ }
139
+ } catch (err) {
140
+ console.error('⚠️ Failed to generate initial resources:', err.message);
141
+ } finally {
142
+ process.chdir(originalCwd);
143
+ }
144
+ }
145
+
146
+ // Install dependencies
147
+ if (!this.skipInstall) {
148
+ console.log('📦 Installing dependencies...');
149
+ await runNpmInstall(projectPath);
80
150
  }
81
151
 
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
- );
152
+ showSuccess(projectName, this.skipInstall);
153
+ }
154
+
155
+ async createProjectStructure(projectPath, projectName, template, options = {}) {
156
+ // Create directories
157
+ const directories = [
158
+ 'models/mongo',
159
+ 'routes',
160
+ 'services',
161
+ 'workers',
162
+ 'validators',
163
+ 'init',
164
+ 'logs',
165
+ 'public'
166
+ ];
167
+
168
+ for (const dir of directories) {
169
+ await createDirectory(path.join(projectPath, dir));
121
170
  }
122
171
 
123
- async createSettings(projectPath, projectName) {
124
- const settings = `module.exports.settings = {
172
+ // Create files
173
+ await this.createPackageJson(projectPath, projectName);
174
+ await this.createSettings(projectPath, projectName, options);
175
+ await this.createIndexJs(projectPath);
176
+ await this.createEnvFile(projectPath, projectName);
177
+ await this.createExampleFiles(projectPath);
178
+ }
179
+
180
+ async createPackageJson(projectPath, projectName) {
181
+ const packageJson = {
182
+ name: projectName,
183
+ version: "1.0.0",
184
+ description: "Falcon.js application",
185
+ main: "index.js",
186
+ scripts: {
187
+ start: "node index.js",
188
+ dev: "nodemon index.js",
189
+ test: "echo \"Error: no test specified\" && exit 1"
190
+ },
191
+ keywords: ["falcon", "nodejs", "api"],
192
+ author: "",
193
+ license: "ISC",
194
+ type: "commonjs",
195
+ dependencies: {
196
+ "falconjs": "file:../falconjs",
197
+ "@hapi/boom": "^10.0.1",
198
+ "@hapi/inert": "^7.1.0",
199
+ "@hapi/vision": "^7.0.3",
200
+ "joi": "^17.13.3",
201
+ "mongoose": "^8.3.1",
202
+ "redis": "^4.7.0",
203
+ "mqtt": "^5.13.3",
204
+ "log4js": "^6.9.1",
205
+ "rate-limiter-flexible": "^2.4.2",
206
+ "hapi-alive": "^2.0.4",
207
+ "hapi-swagger": "^17.2.1",
208
+ "dotenv": "^16.0.0"
209
+ },
210
+ devDependencies: {
211
+ "nodemon": "^3.1.10"
212
+ }
213
+ };
214
+
215
+ await fs.writeFile(
216
+ path.join(projectPath, 'package.json'),
217
+ JSON.stringify(packageJson, null, 2)
218
+ );
219
+ }
220
+
221
+ async createSettings(projectPath, projectName, options = {}) {
222
+ const { swagger = true, auth = true, models = [], routes = [] } = options;
223
+
224
+ const settings = `module.exports.settings = {
125
225
  name: "${projectName}",
126
226
  http: {
127
227
  host: process.env.HTTP_HOST || "localhost",
@@ -156,7 +256,7 @@ class ProjectGenerator {
156
256
  }
157
257
  },
158
258
  swagger: {
159
- enabled: true,
259
+ enabled: ${swagger},
160
260
  path: "/documentation"
161
261
  },
162
262
  auth: {
@@ -167,31 +267,31 @@ class ProjectGenerator {
167
267
  return { id: decoded.id, roles: decoded.roles };
168
268
  }
169
269
  },
170
- default: "jwt"
270
+ default: "${auth ? 'jwt' : 'none'}"
171
271
  },
172
272
  services: [],
173
273
  workers: [],
174
- models: [],
175
- routes: [],
274
+ models: ${JSON.stringify(models.map(m => m.toLowerCase()))},
275
+ routes: ${JSON.stringify(routes.map(r => r.toLowerCase()))},
176
276
  crud: {
177
277
  exclude: []
178
278
  },
179
279
  postInit: "post"
180
280
  };`;
181
281
 
182
- await fs.writeFile(path.join(projectPath, 'settings.js'), settings);
183
- }
282
+ await fs.writeFile(path.join(projectPath, 'settings.js'), settings);
283
+ }
184
284
 
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
- }
285
+ async createIndexJs(projectPath) {
286
+ const indexContent = await fs.readFile(
287
+ path.join(__dirname, '../templates/index.txt'),
288
+ 'utf8'
289
+ );
290
+ await fs.writeFile(path.join(projectPath, 'index.js'), indexContent);
291
+ }
192
292
 
193
- async createEnvFile(projectPath, projectName) {
194
- const envContent = `# Falcon.js Environment Configuration
293
+ async createEnvFile(projectPath, projectName) {
294
+ const envContent = `# Falcon.js Environment Configuration
195
295
  # Generated by falcon-cli
196
296
 
197
297
  # Server Configuration
@@ -221,26 +321,26 @@ LOG_MAX_SIZE=10M
221
321
  MODE=DEV
222
322
  `;
223
323
 
224
- await fs.writeFile(path.join(projectPath, '.env'), envContent);
225
- }
324
+ await fs.writeFile(path.join(projectPath, '.env'), envContent);
325
+ }
226
326
 
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
- }
327
+ async createExampleFiles(projectPath) {
328
+ const templates = [
329
+ { src: 'example-route.js', dest: 'routes/example.js' },
330
+ { src: 'example-validator.js', dest: 'validators/ExamplePayload.js' },
331
+ { src: 'example-service.js', dest: 'services/example.js' },
332
+ { src: 'example-worker.js', dest: 'workers/example.js' },
333
+ { src: 'post-init.js', dest: 'init/post.js' }
334
+ ];
335
+
336
+ for (const template of templates) {
337
+ const content = await fs.readFile(
338
+ path.join(__dirname, '../templates', template.src),
339
+ 'utf8'
340
+ );
341
+ await fs.writeFile(path.join(projectPath, template.dest), content);
243
342
  }
343
+ }
244
344
  }
245
345
 
246
346
  module.exports = ProjectGenerator;
@@ -1,15 +1,15 @@
1
- const { input, select, confirm } = require("@inquirer/prompts");
2
- const fs = require("fs").promises;
3
- const path = require("path");
1
+ const path = require('path');
2
+ const fs = require('fs').promises;
3
+ const { input } = require('@inquirer/prompts');
4
4
 
5
5
  class RouteGenerator {
6
6
  constructor(options = {}) {
7
7
  this.verbose = options.verbose || false;
8
- this.type = options.type || 'route'; // 'route' or 'validator'
9
8
  }
10
9
 
11
10
  async generate(name) {
12
11
  const currentDir = process.cwd();
12
+ const routesDir = path.join(currentDir, 'routes');
13
13
 
14
14
  // Check if we're in a Falcon project
15
15
  try {
@@ -20,81 +20,48 @@ class RouteGenerator {
20
20
 
21
21
  if (!name) {
22
22
  name = await input({
23
- message: `${this.type === 'validator' ? 'Validator' : 'Route'} name (e.g., User):`,
24
- validate: v => v.trim() ? true : 'Name is required',
23
+ message: 'Route name (e.g., users):',
24
+ validate: v => v.trim() ? true : 'Route name is required',
25
25
  });
26
26
  }
27
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('/');
28
+ console.log(`\n🔧 Generating route: ${name}\n`);
44
29
 
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
30
+ // Create routes directory if it doesn't exist
51
31
  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
32
+ await fs.mkdir(routesDir, { recursive: true });
33
+ } catch (error) {
34
+ // Directory already exists
60
35
  }
61
36
 
62
- // Helper to get template content
63
- const templatePath = path.join(__dirname, '..', 'templates', 'example-route.js');
64
- let content = await fs.readFile(templatePath, 'utf8');
37
+ const routeContent = this.getRouteTemplate(name);
38
+ const routePath = path.join(routesDir, `${name.toLowerCase()}.js`);
65
39
 
66
- // Simple replacements if needed, or just use the example as a starter
67
- // For now, we'll just write the template
40
+ await fs.writeFile(routePath, routeContent);
68
41
 
69
- await fs.writeFile(filePath, content);
70
- console.log(`✅ Route created: ${filePath}`);
42
+ console.log(`✅ Route created: ${routePath}`);
71
43
  }
72
44
 
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
45
+ getRouteTemplate(name) {
46
+ return `module.exports.route = async function (context) {
47
+ context.server.route({
48
+ method: 'GET',
49
+ path: '/${name.toLowerCase()}',
50
+ options: {
51
+ tags: ['api', 'example'],
52
+ description: 'Example GET endpoint',
53
+ notes: 'Returns example data'
54
+ },
55
+ handler: async (request, h) => {
56
+ return {
57
+ success: true,
58
+ message: 'Hello from Falcon.js!',
59
+ timestamp: new Date().toISOString()
60
+ };
61
+ }
85
62
  });
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
63
 
96
- await fs.writeFile(filePath, content);
97
- console.log(`✅ Validator created: ${filePath}`);
64
+ };`;
98
65
  }
99
66
  }
100
67
 
@@ -1,15 +1,17 @@
1
- const { input, confirm } = require("@inquirer/prompts");
2
- const fs = require("fs").promises;
3
- const path = require("path");
1
+ const path = require('path');
2
+ const fs = require('fs').promises;
3
+ const { input } = require('@inquirer/prompts');
4
4
 
5
5
  class ServiceGenerator {
6
6
  constructor(options = {}) {
7
7
  this.verbose = options.verbose || false;
8
- this.type = options.type || 'service'; // 'service' or 'worker'
8
+ this.type = options.type || 'service'; // can be 'service' or 'worker'
9
9
  }
10
10
 
11
11
  async generate(name) {
12
12
  const currentDir = process.cwd();
13
+ const baseDir = this.type === 'worker' ? 'workers' : 'services';
14
+ const targetDir = path.join(currentDir, baseDir);
13
15
 
14
16
  // Check if we're in a Falcon project
15
17
  try {
@@ -20,65 +22,49 @@ class ServiceGenerator {
20
22
 
21
23
  if (!name) {
22
24
  name = await input({
23
- message: `${this.type === 'worker' ? 'Worker' : 'Service'} name (e.g., Email):`,
25
+ message: `${this.type.charAt(0).toUpperCase() + this.type.slice(1)} name:`,
24
26
  validate: v => v.trim() ? true : 'Name is required',
25
27
  });
26
28
  }
27
29
 
28
- const dirName = this.type === 'worker' ? 'workers' : 'services';
29
- const targetDir = path.join(currentDir, dirName);
30
- await fs.mkdir(targetDir, { recursive: true });
30
+ console.log(`\n🔧 Generating ${this.type}: ${name}\n`);
31
31
 
32
- const filePath = path.join(targetDir, `${name}.js`);
33
-
34
- // Check if file exists
32
+ // Create directory if it doesn't exist
35
33
  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
34
+ await fs.mkdir(targetDir, { recursive: true });
35
+ } catch (error) {
36
+ // Directory already exists
44
37
  }
45
38
 
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');
39
+ const content = this.getTemplate(name);
40
+ const fileName = `${name.toLowerCase()}.js`;
41
+ const filePath = path.join(targetDir, fileName);
50
42
 
51
43
  await fs.writeFile(filePath, content);
52
- console.log(`✅ ${this.type === 'worker' ? 'Worker' : 'Service'} created: ${filePath}`);
53
44
 
54
- // Update settings.js to include the service/worker
55
- await this.updateSettings(currentDir, name, dirName);
45
+ console.log(`✅ ${this.type.charAt(0).toUpperCase() + this.type.slice(1)} created: ${filePath}`);
56
46
  }
57
47
 
58
- async updateSettings(projectDir, name, listName) {
59
- const settingsPath = path.join(projectDir, 'settings.js');
60
- let settingsContent = await fs.readFile(settingsPath, 'utf8');
48
+ getTemplate(name) {
49
+ if (this.type === 'worker') {
50
+ return `module.exports = async function (job) {
51
+ // Process job here
52
+ console.log('Processing job:', job.id);
53
+ return { done: true };
54
+ };`;
55
+ }
61
56
 
62
- // Regex to find the array: services: [...] or workers: [...]
63
- const regex = new RegExp(`${listName}:\\s*\\[(.*?)\\]`, 's');
64
- const match = settingsContent.match(regex);
57
+ return `class ${name}Service {
58
+ constructor(app) {
59
+ this.app = app;
60
+ }
65
61
 
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}"`;
62
+ async doSomething() {
63
+ return 'done';
64
+ }
65
+ }
73
66
 
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
- }
67
+ module.exports = ${name}Service;`;
82
68
  }
83
69
  }
84
70
 
package/lib/utils.js CHANGED
@@ -11,6 +11,8 @@ const logo = `
11
11
  ╚═╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═══╝
12
12
 
13
13
  🚀 Falcon.js CLI - Build powerful Node.js APIs
14
+ Powered by Triophore Technologies
15
+ Visit us at https://triophore.com
14
16
  `;
15
17
 
16
18
  function showLogo() {
@@ -79,15 +81,15 @@ function validateProjectName(name) {
79
81
  if (!name || typeof name !== 'string') {
80
82
  return 'Project name is required';
81
83
  }
82
-
84
+
83
85
  if (!/^[a-z0-9-_]+$/i.test(name)) {
84
86
  return 'Project name can only contain letters, numbers, hyphens, and underscores';
85
87
  }
86
-
88
+
87
89
  if (name.length < 2) {
88
90
  return 'Project name must be at least 2 characters long';
89
91
  }
90
-
92
+
91
93
  return true;
92
94
  }
93
95
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@triophore/falcon-cli",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/readme.md CHANGED
@@ -1,2 +1,62 @@
1
- - create project
2
- - create models for both mongo and sequalize
1
+ # Falcon CLI
2
+
3
+ Command-line interface for creating and managing Falcon.js applications.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g falcon-cli
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Create a new project
14
+ ```bash
15
+ falcon-cli create my-app
16
+ falcon-cli create my-app --template api
17
+ falcon-cli create my-app --skip-install
18
+ ```
19
+
20
+ ### Generate components
21
+ ```bash
22
+ # Generate a model
23
+ falcon-cli generate model User
24
+ falcon-cli g model Product --crud
25
+
26
+ # Generate a route
27
+ falcon-cli generate route users
28
+
29
+ # Generate a service
30
+ falcon-cli generate service email
31
+
32
+ # Generate a worker
33
+ falcon-cli generate worker image-processor
34
+ ```
35
+
36
+ ## Commands
37
+
38
+ - `create [name]` - Create a new Falcon.js project
39
+ - `generate <type> [name]` - Generate project components
40
+ - Types: `model`, `route`, `service`, `worker`, `validator`
41
+
42
+ ## Options
43
+
44
+ - `--verbose, -V` - Run with verbose logging
45
+ - `--help, -h` - Show help
46
+ - `--version, -v` - Show version
47
+
48
+ ## Project Structure
49
+
50
+ ```
51
+ my-falcon-app/
52
+ ├── index.js # Application entry point
53
+ ├── settings.js # Falcon configuration
54
+ ├── .env # Environment variables
55
+ ├── models/mongo/ # Mongoose models
56
+ ├── routes/ # API routes
57
+ ├── services/ # Background services
58
+ ├── workers/ # Job processors
59
+ ├── validators/ # Joi validation schemas
60
+ ├── init/ # Initialization scripts
61
+ └── logs/ # Application logs
62
+ ```
@@ -3,10 +3,10 @@
3
3
  * This file demonstrates the route structure expected by Falcon.js
4
4
  */
5
5
 
6
- module.exports.route = async function (server, context) {
7
-
6
+ module.exports.route = async function (context) {
7
+
8
8
  // Example GET route
9
- server.route({
9
+ context.server.route({
10
10
  method: 'GET',
11
11
  path: '/api/example',
12
12
  options: {
@@ -24,7 +24,7 @@ module.exports.route = async function (server, context) {
24
24
  });
25
25
 
26
26
  // Example POST route with validation
27
- server.route({
27
+ context.server.route({
28
28
  method: 'POST',
29
29
  path: '/api/example',
30
30
  options: {
@@ -36,7 +36,7 @@ module.exports.route = async function (server, context) {
36
36
  },
37
37
  handler: async (request, h) => {
38
38
  const { name, email } = request.payload;
39
-
39
+
40
40
  // Example: Save to database if models are available
41
41
  if (context.models.example) {
42
42
  const result = await context.models.example.create({
@@ -44,14 +44,14 @@ module.exports.route = async function (server, context) {
44
44
  email,
45
45
  createdAt: new Date()
46
46
  });
47
-
47
+
48
48
  return {
49
49
  success: true,
50
50
  data: result,
51
51
  message: 'Data saved successfully'
52
52
  };
53
53
  }
54
-
54
+
55
55
  return {
56
56
  success: true,
57
57
  message: 'Data received',
@@ -61,7 +61,7 @@ module.exports.route = async function (server, context) {
61
61
  });
62
62
 
63
63
  // Example route that sends job to worker
64
- server.route({
64
+ context.server.route({
65
65
  method: 'POST',
66
66
  path: '/api/example/job',
67
67
  options: {
@@ -70,7 +70,7 @@ module.exports.route = async function (server, context) {
70
70
  },
71
71
  handler: async (request, h) => {
72
72
  const jobData = request.payload;
73
-
73
+
74
74
  // Send job to worker via MQTT (if available)
75
75
  if (context.mqtt_client) {
76
76
  context.mqtt_client.publish('worker_example_job', JSON.stringify({
@@ -78,13 +78,13 @@ module.exports.route = async function (server, context) {
78
78
  data: jobData,
79
79
  timestamp: new Date().toISOString()
80
80
  }));
81
-
81
+
82
82
  return {
83
83
  success: true,
84
84
  message: 'Job queued for processing'
85
85
  };
86
86
  }
87
-
87
+
88
88
  return {
89
89
  success: false,
90
90
  message: 'Job queue not available'
@@ -1,4 +1,4 @@
1
- const { FalconBaseService } = require('falconjs');
1
+ const { FalconBaseService } = require('@triophore/falconjs');
2
2
 
3
3
  /**
4
4
  * Example service - rename and customize as needed
@@ -1,4 +1,4 @@
1
- const { FalconBaseWorker } = require('falconjs');
1
+ const { FalconBaseWorker } = require('@triophore/falconjs');
2
2
 
3
3
  /**
4
4
  * Example worker - rename and customize as needed
@@ -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 {
@@ -8,16 +8,12 @@
8
8
  "dev": "nodemon index.js",
9
9
  "test": "echo \"Error: no test specified\" && exit 1"
10
10
  },
11
- "keywords": [
12
- "falcon",
13
- "nodejs",
14
- "api"
15
- ],
11
+ "keywords": ["falcon", "nodejs", "api"],
16
12
  "author": "",
17
13
  "license": "ISC",
18
14
  "type": "commonjs",
19
15
  "dependencies": {
20
- "falconjs": "@triophore/falconjs",
16
+ "@triophore/falconjs": "^1.0.0",
21
17
  "@hapi/boom": "^10.0.1",
22
18
  "@hapi/inert": "^7.1.0",
23
19
  "@hapi/vision": "^7.0.3",
@@ -35,4 +31,4 @@
35
31
  "devDependencies": {
36
32
  "nodemon": "^3.1.10"
37
33
  }
38
- }
34
+ }