@triophore/falcon-cli 1.0.4 → 1.0.8

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.
@@ -37,7 +37,7 @@ class ModelGenerator {
37
37
 
38
38
  // Generate model interactively
39
39
  const modelContent = await this.buildModelInteractively(modelName);
40
-
40
+
41
41
  // Write model file
42
42
  const modelPath = path.join(modelsDir, `${modelName.toLowerCase()}.js`);
43
43
  await fs.writeFile(modelPath, modelContent);
@@ -56,7 +56,7 @@ class ModelGenerator {
56
56
  async buildModelInteractively(modelName) {
57
57
  const fields = {};
58
58
  const indexes = [];
59
-
59
+
60
60
  // Add timestamps option
61
61
  const useTimestamps = await confirm({
62
62
  message: 'Enable timestamps (createdAt, updatedAt)?',
@@ -76,7 +76,7 @@ class ModelGenerator {
76
76
 
77
77
  const fieldName = await input({
78
78
  message: 'Field name:',
79
- validate: v => v && !fields[v] ? true : 'Invalid or duplicate field name',
79
+ validate: v => v.trim() && !fields[v.trim()] ? true : 'Invalid or duplicate field name',
80
80
  });
81
81
 
82
82
  const fieldType = await select({
@@ -120,6 +120,38 @@ class ModelGenerator {
120
120
  validate: v => !v || !isNaN(v) ? true : 'Must be a number'
121
121
  });
122
122
  if (maxLength) fieldOptions.maxlength = parseInt(maxLength);
123
+
124
+ // Enum support for String
125
+ const hasEnum = await confirm({
126
+ message: 'Should this field have enum values (restricted choices)?',
127
+ default: false
128
+ });
129
+ if (hasEnum) {
130
+ const enumValues = await input({
131
+ message: 'Enter enum values (comma-separated):',
132
+ validate: v => v.trim() ? true : 'At least one enum value is required'
133
+ });
134
+ fieldOptions.enum = enumValues.split(',').map(v => v.trim()).filter(v => v);
135
+ }
136
+ }
137
+
138
+ if (fieldType === 'Number') {
139
+ // Enum support for Number
140
+ const hasEnum = await confirm({
141
+ message: 'Should this field have enum values (restricted choices)?',
142
+ default: false
143
+ });
144
+ if (hasEnum) {
145
+ const enumValues = await input({
146
+ message: 'Enter enum values (comma-separated numbers):',
147
+ validate: v => {
148
+ if (!v.trim()) return 'At least one enum value is required';
149
+ const values = v.split(',').map(n => n.trim());
150
+ return values.every(n => !isNaN(n)) ? true : 'All values must be numbers';
151
+ }
152
+ });
153
+ fieldOptions.enum = enumValues.split(',').map(v => parseFloat(v.trim()));
154
+ }
123
155
  }
124
156
 
125
157
  if (fieldType === 'ObjectId') {
@@ -130,7 +162,7 @@ class ModelGenerator {
130
162
  fieldOptions.ref = refModel;
131
163
  }
132
164
 
133
- fields[fieldName] = {
165
+ fields[fieldName.trim()] = {
134
166
  type: fieldType,
135
167
  ...fieldOptions
136
168
  };
@@ -180,7 +212,7 @@ ${fieldsCode}
180
212
  if (match) {
181
213
  const modelsArray = match[1];
182
214
  if (!modelsArray.includes(`"${modelName}"`)) {
183
- const newModelsArray = modelsArray.trim()
215
+ const newModelsArray = modelsArray.trim()
184
216
  ? `${modelsArray.trim()}, "${modelName}"`
185
217
  : `"${modelName}"`;
186
218
  settingsContent = settingsContent.replace(
@@ -1,6 +1,6 @@
1
1
  const path = require('path');
2
2
  const fs = require('fs').promises;
3
- const { input } = require('@inquirer/prompts');
3
+ const { input, select } = require('@inquirer/prompts');
4
4
 
5
5
  class RouteGenerator {
6
6
  constructor(options = {}) {
@@ -25,43 +25,85 @@ class RouteGenerator {
25
25
  });
26
26
  }
27
27
 
28
- console.log(`\nšŸ”§ Generating route: ${name}\n`);
28
+ const method = await select({
29
+ message: 'HTTP Method:',
30
+ choices: [
31
+ { name: 'GET', value: 'GET' },
32
+ { name: 'POST', value: 'POST' },
33
+ { name: 'PUT', value: 'PUT' },
34
+ { name: 'DELETE', value: 'DELETE' },
35
+ { name: 'PATCH', value: 'PATCH' },
36
+ { name: 'ANY', value: '*' },
37
+ ]
38
+ });
29
39
 
30
- // Create routes directory if it doesn't exist
31
- try {
32
- await fs.mkdir(routesDir, { recursive: true });
33
- } catch (error) {
34
- // Directory already exists
40
+ console.log(`\nšŸ”§ Generating route: ${name} [${method}]\n`);
41
+
42
+ // Construct filename: [METHOD]#name.js
43
+ const filename = `[${method.toUpperCase()}]#${name.toLowerCase()}.js`;
44
+
45
+ // Handle nested paths
46
+ // If name contains slashes, we need to handle directory creation
47
+ // But wait, if name is "auth/login", filename becomes "[GET]#auth/login.js" which is wrong.
48
+ // We want "auth/[GET]#login.js".
49
+
50
+ let finalPath;
51
+ if (name.includes('/') || name.includes('\\')) {
52
+ const parts = name.split(/[/\\]/);
53
+ const baseName = parts.pop();
54
+ const dirPath = parts.join('/');
55
+
56
+ finalPath = path.join(routesDir, dirPath, `[${method.toUpperCase()}]#${baseName.toLowerCase()}.js`);
57
+
58
+ // Create subdirectories
59
+ try {
60
+ await fs.mkdir(path.dirname(finalPath), { recursive: true });
61
+ } catch (error) {
62
+ // Ignore
63
+ }
64
+ } else {
65
+ finalPath = path.join(routesDir, filename);
66
+ // Ensure routes dir exists
67
+ try {
68
+ await fs.mkdir(routesDir, { recursive: true });
69
+ } catch (err) { }
35
70
  }
36
71
 
37
- const routeContent = this.getRouteTemplate(name);
38
- const routePath = path.join(routesDir, `${name.toLowerCase()}.js`);
39
72
 
40
- await fs.writeFile(routePath, routeContent);
73
+ const routeContent = this.getRouteTemplate(name, method);
74
+ await fs.writeFile(finalPath, routeContent);
41
75
 
42
- console.log(`āœ… Route created: ${routePath}`);
76
+ console.log(`āœ… Route created: ${finalPath}`);
43
77
  }
44
78
 
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
- }
62
- });
79
+ getRouteTemplate(name, method) {
80
+ method = method === '*' ? 'ANY' : method; //${method}
81
+ return `/**
82
+ * ${method} Route for ${name}
83
+ */
84
+
85
+ module.exports.route = async function (context) {
86
+ return {
87
+ options: {
88
+ tags: ['api'],
89
+ description: 'Auto-generated ${method} route',
90
+ notes: 'Returns example data',
91
+ validate: {} // Add payload/query/params validation here
92
+ },
93
+ handler: async (request, h) => {
94
+ return {
95
+ success: true,
96
+ message: 'Hello from Falcon.js!',
97
+ timestamp: new Date().toISOString(),
98
+ // data: request.payload // for POST/PUT
99
+ };
100
+ }
101
+ };
102
+ };
103
+
104
+ `
63
105
 
64
- };`;
106
+ ;
65
107
  }
66
108
  }
67
109
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@triophore/falcon-cli",
3
- "version": "1.0.4",
3
+ "version": "1.0.8",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -4,91 +4,22 @@
4
4
  */
5
5
 
6
6
  module.exports.route = async function (context) {
7
-
8
- // Example GET route
9
- context.server.route({
10
- method: 'GET',
11
- path: '/api/example',
12
- options: {
13
- tags: ['api', 'example'],
14
- description: 'Example GET endpoint',
15
- notes: 'Returns example data'
16
- },
17
- handler: async (request, h) => {
18
- return {
19
- success: true,
20
- message: 'Hello from Falcon.js!',
21
- timestamp: new Date().toISOString()
22
- };
23
- }
24
- });
25
-
26
- // Example POST route with validation
27
- context.server.route({
7
+ return {
28
8
  method: 'POST',
29
9
  path: '/api/example',
30
10
  options: {
31
- tags: ['api', 'example'],
32
- description: 'Example POST endpoint',
33
- validate: {
34
- payload: context.validators?.ExamplePayload || undefined
35
- }
11
+ tags: ['api'],
12
+ description: 'Auto-generated POST route',
13
+ notes: 'Returns example data',
14
+ validate: {} // Add payload/query/params validation here
36
15
  },
37
16
  handler: async (request, h) => {
38
- const { name, email } = request.payload;
39
-
40
- // Example: Save to database if models are available
41
- if (context.models.example) {
42
- const result = await context.models.example.create({
43
- name,
44
- email,
45
- createdAt: new Date()
46
- });
47
-
48
- return {
49
- success: true,
50
- data: result,
51
- message: 'Data saved successfully'
52
- };
53
- }
54
-
55
17
  return {
56
18
  success: true,
57
- message: 'Data received',
58
- data: { name, email }
59
- };
60
- }
61
- });
62
-
63
- // Example route that sends job to worker
64
- context.server.route({
65
- method: 'POST',
66
- path: '/api/example/job',
67
- options: {
68
- tags: ['api', 'jobs'],
69
- description: 'Queue a job for processing'
70
- },
71
- handler: async (request, h) => {
72
- const jobData = request.payload;
73
-
74
- // Send job to worker via MQTT (if available)
75
- if (context.mqtt_client) {
76
- context.mqtt_client.publish('worker_example_job', JSON.stringify({
77
- jobId: Date.now().toString(),
78
- data: jobData,
79
- timestamp: new Date().toISOString()
80
- }));
81
-
82
- return {
83
- success: true,
84
- message: 'Job queued for processing'
85
- };
86
- }
87
-
88
- return {
89
- success: false,
90
- message: 'Job queue not available'
19
+ message: 'Hello from Falcon.js!',
20
+ timestamp: new Date().toISOString(),
21
+ // data: request.payload // for POST/PUT
91
22
  };
92
23
  }
93
- });
94
- };
24
+ };
25
+ };