@triophore/falcon-cli 1.0.0

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 (56) hide show
  1. package/README.md +62 -0
  2. package/auth/basic.js +8 -0
  3. package/auth/cookie.js +10 -0
  4. package/auth/jwks.js +6 -0
  5. package/auth/jwt.js +9 -0
  6. package/auth/openid.js +5 -0
  7. package/auth/webscoket.js +6 -0
  8. package/builder/EnvBuilder.js +0 -0
  9. package/builder/createCjsModule.js +95 -0
  10. package/builder/editModelInteractive.js +159 -0
  11. package/builder/interactiveModelBuilder.js +215 -0
  12. package/builder/interactiveMongobuilder.js +189 -0
  13. package/builder/interactiveUnifiedBuilder.js +277 -0
  14. package/builder/joiValidatorBuilder.js +218 -0
  15. package/builder/mongooseModelBuilder.js +290 -0
  16. package/builder/mongooseModelBuilder2.js +313 -0
  17. package/builder/runMigrations.js +106 -0
  18. package/builder/sequelizeModelBuilder.js +180 -0
  19. package/cli.js +60 -0
  20. package/commands/create.js +57 -0
  21. package/commands/generate.js +74 -0
  22. package/dev/Uset.schema.json +18 -0
  23. package/dev/buildSchemaInteractive.js +189 -0
  24. package/dev/buildSequelizeSchemaInteractive.js +128 -0
  25. package/dev/createJoiSchemaFromJson.js +137 -0
  26. package/dev/createModelFromJson.js +280 -0
  27. package/dev/generateAllFiles.js +45 -0
  28. package/dev/generateJoiFile.js +95 -0
  29. package/dev/generateSequelizeFiles.js +167 -0
  30. package/dev/interactiveJoiBuilder.js +177 -0
  31. package/dev/ra.js +22 -0
  32. package/dev/rj.js +18 -0
  33. package/dev/run.js +16 -0
  34. package/dev/run_seq.js +18 -0
  35. package/dev/tracker.js +23 -0
  36. package/editJsConfig.js +188 -0
  37. package/index.js +548 -0
  38. package/lib/ModelGenerator.js +203 -0
  39. package/lib/ProjectGenerator.js +246 -0
  40. package/lib/utils.js +100 -0
  41. package/logo.js +3 -0
  42. package/package.json +35 -0
  43. package/readme.md +2 -0
  44. package/schema.json +42 -0
  45. package/templates/auth_vals.json +3 -0
  46. package/templates/config.js +0 -0
  47. package/templates/example-route.js +94 -0
  48. package/templates/example-service.js +63 -0
  49. package/templates/example-validator.js +15 -0
  50. package/templates/example-worker.js +83 -0
  51. package/templates/index.txt +41 -0
  52. package/templates/post-init.js +78 -0
  53. package/templates/settings.js +192 -0
  54. package/templates/template1.settings.txt +15 -0
  55. package/templates/templatev1.json +38 -0
  56. package/validateJsConfig.js +125 -0
package/index.js ADDED
@@ -0,0 +1,548 @@
1
+ #!/usr/bin/env node
2
+
3
+ const {
4
+ input,
5
+ select,
6
+ Separator,
7
+ confirm,
8
+ checkbox,
9
+ password,
10
+ } = require("@inquirer/prompts");
11
+
12
+ process.on('SIGINT', shutdown);
13
+
14
+ process.on('uncaughtException', (error) => {
15
+ if (error instanceof Error && error.name === 'ExitPromptError') {
16
+ // This catches the prompt rejection globally
17
+ console.log('\nšŸ‘‹ Until next time!');
18
+ process.exit(0); // Exit gracefully after logging
19
+ } else {
20
+ // For other uncaught errors, log them and exit
21
+ console.error('Uncaught Exception:', error);
22
+ process.exit(1);
23
+ }
24
+ });
25
+
26
+ // Do graceful shutdown
27
+ function shutdown() {
28
+ console.log('Shutting down');
29
+ process.exit(0)
30
+ }
31
+
32
+ const fs = require("fs");
33
+ const path = require("path");
34
+ const yargs = require("yargs");
35
+ const { hideBin } = require("yargs/helpers");
36
+ const argv = yargs(hideBin(process.argv)).parse();
37
+ const { spawn } = require('child_process');
38
+
39
+ const settings_template = require("./templates/settings").settings
40
+
41
+ const currentDir = process.cwd();
42
+
43
+ const templatev1 = require("./templates/templatev1.json");
44
+
45
+ var model = {};
46
+
47
+ const mongoose_builder = require("./builder/interactiveMongobuilder").buildMongooseSchemaInteractive;
48
+
49
+ const interactiveModelBuilder = require("./builder/interactiveUnifiedBuilder").interactiveUnifiedBuilder;
50
+
51
+ const mongoose_schema_builder = require("./builder/mongooseModelBuilder").mongooseModelBuilder
52
+
53
+ var EnvVals = []
54
+
55
+ var NpmDeps = []
56
+
57
+ var ImportsArry = [];
58
+
59
+ var mkDir = []
60
+
61
+ var mkFile = []
62
+
63
+ async function start() {
64
+
65
+ // create other things
66
+ if (argv.create) {
67
+ switch (argv.create) {
68
+ case "model":
69
+ console.log("Creating model");
70
+ await create_mongoose_model()
71
+ break;
72
+ case "array":
73
+ console.log("Creating array...");
74
+ break;
75
+ default:
76
+ console.log("Invalid option");
77
+ }
78
+ } else {
79
+ //create new project
80
+ console.log(require("./logo").logo)
81
+ console.log("--------------------------------------")
82
+ console.log("Falcon CLI")
83
+ console.log("--------------------------------------")
84
+
85
+ var app_settings = {};
86
+
87
+ const confirm_start = await confirm({
88
+ message: "Create new Falcon app here?",
89
+ });
90
+
91
+ if (!confirm_start) {
92
+ console.log("Exiting...");
93
+ process.exit(0);
94
+ }
95
+
96
+ const app_name = await input({
97
+ message: 'App name:',
98
+ validate: v => v.trim() ? true : 'Required',
99
+ });
100
+
101
+ templatev1["name"] = app_name
102
+ app_settings["name"] = app_name
103
+
104
+ app_settings["http"] = settings_template.http.params;
105
+
106
+ EnvVals.push("HTTP_HOST=''")
107
+ EnvVals.push("HTTP_PORT=")
108
+
109
+ // console.log("Project Config")
110
+
111
+ const log_config = await select({
112
+ message: 'Log Configure',
113
+ choices: [
114
+ { name: 'File', value: 'file' },
115
+ { name: 'Stdio', value: 'stdio' },
116
+ { name: 'Stdio and File', value: 'both' },
117
+ ],
118
+ });
119
+
120
+ app_settings["log"] = log_config
121
+
122
+ const enable_static = await confirm({
123
+ message: 'Enable Static Serving',
124
+ default: false,
125
+ });
126
+
127
+ if (enable_static) {
128
+ app_settings["static"] = settings_template["static"].params
129
+ settings_template["static"].deps.forEach((dep) => { NpmDeps.push(dep) })
130
+ ImportsArry.push(settings_template["static"].require)
131
+ mkDir.push("public")
132
+ }
133
+
134
+ const enable_templates = await confirm({
135
+ message: 'Enable Templates',
136
+ default: false,
137
+ });
138
+
139
+ if (enable_templates) {
140
+ app_settings["templates"] = settings_template.templates["eta"].params
141
+ settings_template.templates.deps.forEach((dep) => { NpmDeps.push(dep) })
142
+ settings_template.templates["eta"].deps.forEach((dep) => { NpmDeps.push(dep) })
143
+ mkDir.push("templates")
144
+ }
145
+
146
+ const enable_crumb = await confirm({
147
+ message: 'Enable Crumb for CSRF',
148
+ default: false,
149
+ });
150
+
151
+ app_settings["crumb"] = enable_crumb
152
+
153
+ const enable_blipp = await confirm({
154
+ message: 'Enable Blipp (Route printing in log)',
155
+ default: false,
156
+ });
157
+
158
+
159
+ const enable_database = await confirm({
160
+ message: 'Enable Database?',
161
+ default: false,
162
+ });
163
+
164
+ if (enable_database) {
165
+ app_settings["database"] = {};
166
+ app_settings["database"]["mongodb"] = settings_template.database.mongodb.params
167
+ }
168
+
169
+ // app_settings["blipp"] = enable_blipp
170
+
171
+ const enable_redis = await confirm({
172
+ message: 'Enable Redis',
173
+ default: false,
174
+ });
175
+
176
+ app_settings["redis"] = enable_redis
177
+
178
+ const mqtt_choice = await select({
179
+ message: 'MQTT type?',
180
+ choices: [
181
+ { name: 'Inbuilt', value: 'aedes' },
182
+ { name: 'External', value: 'external' },
183
+ ],
184
+ });
185
+
186
+ app_settings["mqtt"] = mqtt_choice
187
+
188
+ const enable_websocket = await confirm({
189
+ message: 'Enable Websocket (Socket.io)',
190
+ default: true,
191
+ });
192
+
193
+ if (enable_websocket) {
194
+ app_settings["websocket"] = {}
195
+ mkDir.push("websocket")
196
+ const web_sock_transport = await select({
197
+ message: 'WebSocket Transport type?',
198
+ choices: [
199
+ { name: 'text', value: 'text' },
200
+ { name: 'binary', value: 'binary' },
201
+ ],
202
+ });
203
+ console.log(web_sock_transport)
204
+ app_settings["websocket"][web_sock_transport] = {};
205
+ app_settings["websocket"][web_sock_transport] = settings_template.websocket[web_sock_transport].params
206
+ }
207
+
208
+
209
+ const enable_auth = await confirm({
210
+ message: 'Enable Authentication',
211
+ default: true,
212
+ });
213
+
214
+ if (enable_auth) {
215
+ app_settings["auth"] = {}
216
+ const auth_choice = await select({
217
+ message: 'Select Auth type(s)?',
218
+ choices: [
219
+ { name: 'Basic', value: 'basic' },
220
+ { name: 'JWT', value: 'jwt' },
221
+ { name: 'JWKS', value: 'jwks' },
222
+ { name: 'Cookie', value: 'cookie' },
223
+ { name: 'Openid', value: 'openid' },
224
+ ],
225
+ });
226
+ mkDir.push("auth")
227
+ }
228
+
229
+ mkDir.push("models/mongo") // Fixed: falconjs expects models in models/mongo/
230
+ mkDir.push("routes")
231
+ mkDir.push("services")
232
+ mkDir.push("workers")
233
+ mkDir.push("validators")
234
+ mkDir.push("init")
235
+ mkDir.push("logs")
236
+
237
+ if (enable_static) {
238
+ mkDir.push("public")
239
+ }
240
+
241
+ if (enable_templates) {
242
+ mkDir.push("templates")
243
+ }
244
+
245
+ // const enable_init_script = await confirm({
246
+ // message: 'Create Init Script: ?',
247
+ // default: false,
248
+ // });
249
+
250
+ // app_settings["init"] = enable_init_script
251
+
252
+ // const enable_post_script = await confirm({
253
+ // message: 'Create Post Init Script: ?',
254
+ // default: false,
255
+ // });
256
+
257
+ // app_settings["post"] = enable_post_script
258
+
259
+ // const enable_service = await confirm({
260
+ // message: 'Create a Falcon Service',
261
+ // default: false,
262
+ // });
263
+
264
+ // app_settings["service"] = enable_service
265
+
266
+ // const enable_worker = await confirm({
267
+ // message: 'Create a Falcon Worker',
268
+ // default: false,
269
+ // });
270
+
271
+ // app_settings["worker"] = enable_worker
272
+
273
+
274
+
275
+ // app_settings["database"] = db_choice
276
+
277
+ // app_settings["databasasd"] = 'process.env.API_URL || "mongo://localhost:27017/"'
278
+
279
+ // console.log(app_settings)
280
+
281
+ // await writeJsonToCjsModule(app_settings,"./settings.js")
282
+
283
+ await fs.promises.appendFile(path.join(currentDir, ".env"), "# Created by falcon CLI" + '\n')
284
+ for (var i = 0; i < EnvVals.length; i++) {
285
+ await fs.promises.appendFile(path.join(currentDir, ".env"), EnvVals[i] + '\n')
286
+ }
287
+ await fs.promises.appendFile(path.join(currentDir, ".env"), "#------------------------------------------------------#" + '\n')
288
+ await fs.promises.appendFile(path.join(currentDir, ".env"), "# Please add app specific ENV values below" + '\n')
289
+ await fs.promises.appendFile(path.join(currentDir, ".env"), "#------------------------------------------------------#" + '\n')
290
+
291
+ var imports_text = "";
292
+ for (var i = 0; i < ImportsArry.length; i++) {
293
+ imports_text = imports_text + ImportsArry[i].slice(3).trim() + "\n";
294
+ }
295
+
296
+ if (ImportsArry.length) {
297
+ await require("./builder/createCjsModule").createCjsModule(path.join(currentDir, "settings.js"), app_settings, {
298
+ exportStyle: "default",
299
+ functionName: "settings",
300
+ pretty: true,
301
+ async: false,
302
+ imports: imports_text,
303
+ })
304
+ } else {
305
+ await require("./builder/createCjsModule").createCjsModule(path.join(currentDir, "settings.js"), app_settings, {
306
+ exportStyle: "default",
307
+ functionName: "settings",
308
+ pretty: true,
309
+ async: false,
310
+ imports: false,
311
+ })
312
+ }
313
+
314
+ await fs.promises.writeFile(path.join(currentDir, "package.json"), JSON.stringify(templatev1, null, 2))
315
+ console.log("installing core packages")
316
+ runNpmInstall(currentDir)
317
+ console.log("Installing project packages")
318
+
319
+ for (var i = 0; i < NpmDeps.length; i++) {
320
+ runNpmInstall(currentDir, NpmDeps[i])
321
+ }
322
+
323
+ for (var i = 0; i < mkDir.length; i++) {
324
+ try {
325
+ await fs.promises.access(path.join(currentDir, mkDir[i]))
326
+ } catch (error) {
327
+ await fs.promises.mkdir(path.join(currentDir, mkDir[i]))
328
+ }
329
+ }
330
+
331
+ await fs.promises.writeFile(path.join(currentDir, "index.js"), await fs.promises.readFile(path.join(__dirname, "templates", "index.txt")))
332
+
333
+ // Create example files
334
+ await fs.promises.writeFile(
335
+ path.join(currentDir, "routes", "example.js"),
336
+ await fs.promises.readFile(path.join(__dirname, "templates", "example-route.js"))
337
+ );
338
+
339
+ await fs.promises.writeFile(
340
+ path.join(currentDir, "validators", "ExamplePayload.js"),
341
+ await fs.promises.readFile(path.join(__dirname, "templates", "example-validator.js"))
342
+ );
343
+
344
+ await fs.promises.writeFile(
345
+ path.join(currentDir, "services", "example.js"),
346
+ await fs.promises.readFile(path.join(__dirname, "templates", "example-service.js"))
347
+ );
348
+
349
+ await fs.promises.writeFile(
350
+ path.join(currentDir, "workers", "example.js"),
351
+ await fs.promises.readFile(path.join(__dirname, "templates", "example-worker.js"))
352
+ );
353
+
354
+ await fs.promises.writeFile(
355
+ path.join(currentDir, "init", "post.js"),
356
+ await fs.promises.readFile(path.join(__dirname, "templates", "post-init.js"))
357
+ );
358
+
359
+ // Create .env file
360
+ await fs.promises.writeFile(path.join(currentDir, ".env"), `# Falcon.js Environment Configuration
361
+ # Generated by falcon-cli
362
+
363
+ # Server Configuration
364
+ HTTP_HOST=localhost
365
+ HTTP_PORT=3000
366
+
367
+ # Database Configuration
368
+ MONGODB_URL=mongodb://localhost:27017/${app_name}_db
369
+
370
+ # MQTT Configuration
371
+ MQTT_PORT=1883
372
+ MQTT_URL=mqtt://localhost:1883
373
+
374
+ # Redis Configuration (optional)
375
+ REDIS_ENABLE=false
376
+ REDIS_URL=redis://localhost:6379
377
+
378
+ # Authentication
379
+ JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
380
+
381
+ # Logging
382
+ LOG_LEVEL=info
383
+ LOG_FILE_NAME=logs/app.log
384
+ LOG_MAX_SIZE=10M
385
+
386
+ # Development Mode
387
+ MODE=DEV
388
+ `);
389
+
390
+ console.log(`
391
+ āœ… Falcon.js project created successfully!
392
+
393
+ šŸ“ Project structure:
394
+ ā”œā”€ā”€ index.js # Main application entry point
395
+ ā”œā”€ā”€ settings.js # Falcon configuration
396
+ ā”œā”€ā”€ .env # Environment variables
397
+ ā”œā”€ā”€ package.json # Dependencies
398
+ ā”œā”€ā”€ routes/ # API route handlers
399
+ ā”œā”€ā”€ models/ # Database models
400
+ ā”œā”€ā”€ services/ # Background services
401
+ ā”œā”€ā”€ workers/ # Job processors
402
+ ā”œā”€ā”€ validators/ # Joi validation schemas
403
+ ā”œā”€ā”€ init/ # Initialization scripts
404
+ └── logs/ # Application logs
405
+
406
+ šŸš€ Next steps:
407
+ 1. cd ${app_name}
408
+ 2. npm install
409
+ 3. npm run dev
410
+
411
+ šŸ“š Documentation will be available at: http://localhost:[PORT]/documentation
412
+ ā¤ļø Health check at: http://localhost:[PORT]/health
413
+ `);
414
+
415
+
416
+ }
417
+ }
418
+
419
+ try {
420
+ start();
421
+ } catch (error) { }
422
+
423
+
424
+ async function writeJsonToCjsModule(dataObject, filePath) {
425
+ try {
426
+ // 1. Convert the JavaScript object into a formatted JSON string.
427
+ // The arguments (null, 2) ensure the output is pretty-printed with 2 spaces.
428
+ const jsonString = JSON.stringify(dataObject, null, 2);
429
+
430
+ // 2. Wrap the string in the CommonJS export syntax.
431
+ const cjsContent = `module.exports = ${jsonString};\n`;
432
+
433
+ // 3. Write the content to the file.
434
+ await fs.promises.writeFile(filePath, cjsContent, 'utf-8');
435
+
436
+ console.log(`āœ… Successfully created CommonJS module at: ${path.resolve(filePath)}`);
437
+ } catch (error) {
438
+ console.error(`āŒ Error writing CJS module to ${filePath}:`, error.message);
439
+ }
440
+ }
441
+
442
+ function isDirectoryEmpty(dirPath) {
443
+ try {
444
+ // 1. Get stats to check if the path is a directory
445
+ const stats = fs.statSync(dirPath);
446
+
447
+ if (!stats.isDirectory()) {
448
+ // If the path exists but is not a directory (e.g., it's a file),
449
+ // we can consider it "not empty" in the context of checking a directory.
450
+ // Alternatively, you could throw an error if the path points to a file.
451
+ console.log(`Path ${dirPath} exists but is not a directory.`);
452
+ return false;
453
+ }
454
+
455
+ // 2. Read the contents of the directory
456
+ const files = fs.readdirSync(dirPath);
457
+
458
+ // 3. Check the length of the returned array
459
+ return files.length === 0;
460
+
461
+ } catch (error) {
462
+ return false;
463
+ // Handle specific error codes
464
+ if (error.code === 'ENOENT') {
465
+ // 'ENOENT' means 'Error NO ENTry', i.e., the file or directory does not exist.
466
+ console.error(`Error: Directory not found at path: ${dirPath}`);
467
+ // Based on context, you might want to return true here (since it has 0 contents)
468
+ // but throwing an error is usually safer if you expect the directory to exist.
469
+ throw new Error(`Directory not found: ${dirPath}`);
470
+ } else if (error.code === 'EACCES') {
471
+ console.error(`Error: Permission denied to access: ${dirPath}`);
472
+ throw error;
473
+ } else {
474
+ // Handle other potential file system errors
475
+ console.error(`An unexpected error occurred: ${error.message}`);
476
+ throw error;
477
+ }
478
+ }
479
+ }
480
+
481
+ async function getProjectInfo() {
482
+
483
+ }
484
+
485
+
486
+ async function create_mongoose_model() {
487
+ mongoose_schema_builder(path.join(currentDir, "models", "mongo")) // Correct path for falconjs
488
+ }
489
+ // await fs.writeFileSync(model_path, JSON.stringify(res,null,2));
490
+
491
+
492
+
493
+ async function precheck() {
494
+ //check if package.json
495
+ if (fs.existsSync(path.join(currentDir, "package.json"))) {
496
+
497
+ } else {
498
+
499
+ }
500
+ }
501
+
502
+ function runNpmInstall(directory, package) {
503
+ // Use 'npm' for Windows and other OSs, or 'npm.cmd' on Windows if needed,
504
+ // but usually 'npm' works if Node/npm is in the PATH.
505
+ const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
506
+
507
+ if (package) {
508
+ const child = spawn(npm, ['install', package], {
509
+ // This is the directory where 'npm install' will run
510
+ cwd: directory,
511
+ // Pipe the output directly to the main process's stdout/stderr
512
+ stdio: 'inherit'
513
+ });
514
+
515
+ child.on('error', (err) => {
516
+ console.error(`Failed to start npm process: ${err}`);
517
+ });
518
+
519
+ child.on('close', (code) => {
520
+ if (code === 0) {
521
+ console.log('āœ… npm install completed successfully.');
522
+ } else {
523
+ console.error(`āŒ npm install failed with code ${code}`);
524
+ }
525
+ });
526
+ } else {
527
+ const child = spawn(npm, ['install'], {
528
+ // This is the directory where 'npm install' will run
529
+ cwd: directory,
530
+ // Pipe the output directly to the main process's stdout/stderr
531
+ stdio: 'inherit'
532
+ });
533
+
534
+ child.on('error', (err) => {
535
+ console.error(`Failed to start npm process: ${err}`);
536
+ });
537
+
538
+ child.on('close', (code) => {
539
+ if (code === 0) {
540
+ console.log('āœ… npm install completed successfully.');
541
+ } else {
542
+ console.error(`āŒ npm install failed with code ${code}`);
543
+ }
544
+ });
545
+ }
546
+
547
+
548
+ }