4bnode 3.5.3 → 4.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.
Files changed (62) hide show
  1. package/README.md +144 -109
  2. package/add-api.js +288 -156
  3. package/add-crud.js +226 -0
  4. package/add-login.js +152 -319
  5. package/add-mongo-delete.js +67 -138
  6. package/add-mongo-insert.js +87 -204
  7. package/add-mongo-read.js +100 -216
  8. package/add-mongo-schema.js +431 -199
  9. package/add-mongo-update.js +89 -202
  10. package/add-mongo.js +125 -218
  11. package/add-serialport.js +53 -98
  12. package/add-socket.js +43 -66
  13. package/add-websocket.js +68 -140
  14. package/index.js +341 -156
  15. package/lib/env.js +18 -0
  16. package/lib/indexFile.js +134 -0
  17. package/lib/names.js +21 -0
  18. package/lib/project.js +122 -0
  19. package/lib/routeFile.js +56 -0
  20. package/lib/ui.js +161 -0
  21. package/package.json +9 -16
  22. package/skeleton/.4bnode/assets/fonts/NotoSans-Bold.woff2 +0 -0
  23. package/skeleton/.4bnode/assets/fonts/NotoSans-Light.woff2 +0 -0
  24. package/skeleton/.4bnode/assets/fonts/NotoSans-Medium.woff2 +0 -0
  25. package/skeleton/.4bnode/assets/fonts/NotoSans-Regular.woff2 +0 -0
  26. package/skeleton/.4bnode/assets/fonts/NotoSans-SemiBold.woff2 +0 -0
  27. package/skeleton/.4bnode/assets/images/mongo.png +0 -0
  28. package/skeleton/.4bnode/assets/images/mysql.png +0 -0
  29. package/skeleton/.4bnode/assets/images/postgresql.png +0 -0
  30. package/skeleton/.4bnode/assets/images/sqlite.png +0 -0
  31. package/skeleton/.4bnode/assets/js/App.js +115 -0
  32. package/skeleton/.4bnode/assets/js/Root.js +44 -0
  33. package/skeleton/.4bnode/assets/js/components/Modal.js +52 -0
  34. package/skeleton/.4bnode/assets/js/components/Sidebar.js +68 -0
  35. package/skeleton/.4bnode/assets/js/components/ToastContainer.js +15 -0
  36. package/skeleton/.4bnode/assets/js/context.js +7 -0
  37. package/skeleton/.4bnode/assets/js/lib.js +83 -0
  38. package/skeleton/.4bnode/assets/js/main.js +5 -0
  39. package/skeleton/.4bnode/assets/js/pages/ApiTesterPage.js +1594 -0
  40. package/skeleton/.4bnode/assets/js/pages/DashboardPage.js +435 -0
  41. package/skeleton/.4bnode/assets/js/pages/DatabasePage.js +461 -0
  42. package/skeleton/.4bnode/assets/js/pages/EnvironmentPage.js +141 -0
  43. package/skeleton/.4bnode/assets/js/pages/RealtimePage.js +186 -0
  44. package/skeleton/.4bnode/assets/js/pages/RoutesPage.js +1656 -0
  45. package/skeleton/.4bnode/assets/js/pages/SchemasPage.js +901 -0
  46. package/skeleton/.4bnode/assets/js/pages/SerialPortPage.js +170 -0
  47. package/skeleton/.4bnode/assets/js/screens/PasskeyScreen.js +105 -0
  48. package/skeleton/.4bnode/assets/js/screens/SplashScreen.js +66 -0
  49. package/skeleton/.4bnode/dev-api.js +2930 -0
  50. package/skeleton/.4bnode/ui.html +1556 -0
  51. package/skeleton/index.js +70 -17
  52. package/skeleton/package.json +9 -5
  53. package/skeleton/public/index.html +288 -86
  54. package/README_4bnode.md +0 -197
  55. package/src/auth/passport.js +0 -21
  56. package/src/db.js +0 -13
  57. package/test-server/.env.development +0 -1
  58. package/test-server/.env.production +0 -1
  59. package/test-server/index.js +0 -82
  60. package/test-server/package-lock.json +0 -1267
  61. package/test-server/package.json +0 -20
  62. package/test-server/public/index.html +0 -90
package/add-mongo.js CHANGED
@@ -2,256 +2,163 @@
2
2
 
3
3
  import fs from "fs";
4
4
  import path from "path";
5
- import readline from "readline";
6
- import { execSync } from "child_process";
7
- import chalk from "chalk";
8
-
9
- const rl = readline.createInterface({
10
- input: process.stdin,
11
- output: process.stdout,
12
- });
5
+ import { input, select, confirm } from "@inquirer/prompts";
6
+ import { getProjectRoot } from "./lib/project.js";
7
+ import { updateEnvFile } from "./lib/env.js";
8
+ import { addImport, readIndex, writeIndex } from "./lib/indexFile.js";
9
+ import {
10
+ showHeader,
11
+ showTaskDone,
12
+ showWarning,
13
+ showError,
14
+ showFileAction,
15
+ showInfo,
16
+ installDeps,
17
+ } from "./lib/ui.js";
13
18
 
14
19
  function isDatabaseConfigured() {
15
- const projectRoot = process.cwd();
16
-
20
+ const projectRoot = getProjectRoot();
17
21
  const devEnvPath = path.join(projectRoot, ".env.development");
18
22
  const prodEnvPath = path.join(projectRoot, ".env.production");
19
-
20
- const isDevConfigured =
23
+ const hasDev =
21
24
  fs.existsSync(devEnvPath) &&
22
25
  fs.readFileSync(devEnvPath, "utf8").includes("MONGO_URI=");
23
- const isProdConfigured =
26
+ const hasProd =
24
27
  fs.existsSync(prodEnvPath) &&
25
28
  fs.readFileSync(prodEnvPath, "utf8").includes("MONGO_URI=");
26
-
27
- return isDevConfigured || isProdConfigured;
28
- }
29
-
30
- function updateEnvFile(filePath, uri) {
31
- const mongoUriLine = `MONGO_URI=${uri}\n`;
32
-
33
- if (fs.existsSync(filePath)) {
34
- let envContent = fs.readFileSync(filePath, "utf8");
35
- if (envContent.includes("MONGO_URI=")) {
36
- envContent = envContent.replace(/MONGO_URI=.*/, mongoUriLine.trim());
37
- } else {
38
- envContent = envContent.trim() + "\n" + mongoUriLine;
39
- }
40
- fs.writeFileSync(filePath, envContent, "utf8");
41
- } else {
42
- fs.writeFileSync(filePath, mongoUriLine, "utf8");
43
- }
29
+ return hasDev || hasProd;
44
30
  }
45
31
 
46
32
  function addMongoDB(devUri, prodUri) {
47
- const projectRoot = process.cwd();
33
+ const projectRoot = getProjectRoot();
48
34
 
49
- // Add MongoDB connection file
50
- const dbContent = `
51
- import mongoose from 'mongoose';
35
+ const dbContent = `import mongoose from 'mongoose';
52
36
 
53
- const connectDB = async () => {
54
- try {
55
- const conn = await mongoose.connect(process.env.MONGO_URI);
56
- console.log(\`MongoDB connected: \${conn.connection.host}\`);
57
- } catch (err) {
58
- console.error(err);
59
- process.exit(1);
60
- }
61
- };
62
-
63
- export default connectDB;
64
- `;
65
- if (!fs.existsSync(path.join(projectRoot, "src"))) {
66
- fs.mkdirSync(path.join(projectRoot, "src"), { recursive: true });
37
+ const connectDB = async () => {
38
+ try {
39
+ const conn = await mongoose.connect(process.env.MONGO_URI);
40
+ console.log(\`MongoDB connected: \${conn.connection.host}\`);
41
+ } catch (err) {
42
+ console.error(err);
43
+ process.exit(1);
67
44
  }
68
- fs.writeFileSync(
69
- path.join(projectRoot, "src", "db.js"),
70
- dbContent.trim(),
71
- "utf8"
72
- );
45
+ };
73
46
 
74
- // Update index.js to import and use MongoDB connection
75
- const indexPath = path.join(projectRoot, "index.js");
76
- const dbImport = `import connectDB from './src/db.js';\n`;
77
- const dbUse = `connectDB();\n`;
47
+ export default connectDB;`;
78
48
 
79
- let indexContent = fs.readFileSync(indexPath, "utf8");
80
- if (!indexContent.includes(dbImport)) {
81
- indexContent = dbImport + indexContent;
49
+ const srcDir = path.join(projectRoot, "src");
50
+ if (!fs.existsSync(srcDir)) {
51
+ fs.mkdirSync(srcDir, { recursive: true });
82
52
  }
83
- if (!indexContent.includes(dbUse)) {
53
+ fs.writeFileSync(path.join(srcDir, "db.js"), dbContent + "\n", "utf8");
54
+ showFileAction("created", "src/db.js");
55
+
56
+ addImport("import connectDB from './src/db.js';");
57
+ let indexContent = readIndex();
58
+ const dbCall = "connectDB();";
59
+ if (!indexContent.includes(dbCall)) {
84
60
  indexContent = indexContent.replace(
85
61
  "const app = express();",
86
- `${dbUse}const app = express();`
62
+ `${dbCall}\nconst app = express();`
87
63
  );
64
+ writeIndex(indexContent);
88
65
  }
89
- fs.writeFileSync(indexPath, indexContent, "utf8");
66
+ showFileAction("updated", "index.js");
90
67
 
91
- // Update .env files
92
- updateEnvFile(path.join(projectRoot, ".env.development"), devUri);
93
- updateEnvFile(path.join(projectRoot, ".env.production"), prodUri);
68
+ updateEnvFile(path.join(projectRoot, ".env.development"), "MONGO_URI", devUri);
69
+ showFileAction("updated", ".env.development");
70
+ updateEnvFile(path.join(projectRoot, ".env.production"), "MONGO_URI", prodUri);
71
+ showFileAction("updated", ".env.production");
72
+ }
94
73
 
95
- // Add mongoose to dependencies
96
- execSync("npm install mongoose", { stdio: "inherit" });
74
+ async function main() {
75
+ showHeader("add-mongo", "Configure MongoDB integration");
97
76
 
98
- console.log(
99
- chalk.green(
100
- `MongoDB integration added with development URI: ${devUri} and production URI: ${prodUri}`
101
- )
102
- );
103
- }
77
+ if (isDatabaseConfigured()) {
78
+ showWarning("MongoDB is already configured. Aborting.");
79
+ process.exit(0);
80
+ }
104
81
 
105
- if (isDatabaseConfigured()) {
106
- console.log(
107
- chalk.yellow(
108
- "MongoDB is already configured in your environment files. Aborting operation."
109
- )
110
- );
111
- rl.close();
112
- process.exit(0);
113
- } else {
114
- function promptForDatabaseDetails(env, callback) {
115
- console.log(chalk.cyan(`\nEnter details for the ${env} database:`));
82
+ const method = await select({
83
+ message: "How do you want to configure MongoDB?",
84
+ choices: [
85
+ { name: "Quick setup (just database name)", value: "quick" },
86
+ { name: "Paste a connection URI", value: "uri" },
87
+ ],
88
+ });
116
89
 
117
- function askDatabaseName() {
118
- rl.question(
119
- chalk.yellow("Enter the MongoDB database name: "),
120
- (dbName) => {
121
- if (!dbName.trim()) {
122
- console.log(chalk.red("Database name is required."));
123
- askDatabaseName();
124
- } else {
125
- callback(dbName);
126
- }
127
- }
128
- );
90
+ let devUri;
91
+
92
+ if (method === "quick") {
93
+ const dbName = await input({
94
+ message: "Database name:",
95
+ validate: (v) => (v.trim() ? true : "Required."),
96
+ });
97
+
98
+ let ip = "127.0.0.1";
99
+ let port = "27017";
100
+ let auth = "";
101
+
102
+ const customize = await confirm({
103
+ message: "Configure IP, port, or auth? (optional)",
104
+ default: false,
105
+ });
106
+
107
+ if (customize) {
108
+ ip = (await input({ message: "IP address:", default: "127.0.0.1" })).trim();
109
+ port = (await input({ message: "Port:", default: "27017" })).trim();
110
+ const user = (await input({ message: "Username (blank to skip):" })).trim();
111
+ if (user) {
112
+ const pass = (await input({ message: "Password:" })).trim();
113
+ auth = `${user}:${pass}@`;
114
+ }
129
115
  }
130
116
 
131
- askDatabaseName();
117
+ devUri = `mongodb://${auth}${ip}:${port}/${dbName.trim()}`;
118
+ showInfo(`URI: ${devUri}`);
119
+ } else {
120
+ devUri = await input({
121
+ message: "MongoDB URI:",
122
+ default: "mongodb://127.0.0.1:27017/mydb",
123
+ validate: (v) =>
124
+ v.trim().startsWith("mongodb://") || v.trim().startsWith("mongodb+srv://")
125
+ ? true
126
+ : "Must start with mongodb:// or mongodb+srv://",
127
+ });
128
+ devUri = devUri.trim();
132
129
  }
133
130
 
134
- promptForDatabaseDetails("development", (devDbName) => {
135
- rl.question(
136
- chalk.yellow("Enter the MongoDB IP address (default: 127.0.0.1): "),
137
- (devIpAddress) => {
138
- const devIp = devIpAddress.trim() || "127.0.0.1";
139
- rl.question(
140
- chalk.yellow("Enter the MongoDB port (default: 27017): "),
141
- (devPort) => {
142
- const devDbPort = devPort.trim() || "27017";
143
- rl.question(
144
- chalk.yellow("Enter the MongoDB user ID (leave blank if none): "),
145
- (devUserId) => {
146
- rl.question(
147
- chalk.yellow(
148
- "Enter the MongoDB password (leave blank if none): "
149
- ),
150
- (devPassword) => {
151
- let devUri = `mongodb://${devIp}:${devDbPort}/${devDbName}`;
152
- if (devUserId && devPassword) {
153
- devUri = `mongodb://${devUserId}:${devPassword}@${devIp}:${devDbPort}/${devDbName}`;
154
- } else if (devUserId || devPassword) {
155
- console.error(
156
- chalk.red(
157
- "Both user ID and password must be provided together."
158
- )
159
- );
160
- process.exit(1);
161
- }
131
+ const useSameForProd = await confirm({
132
+ message: "Use the same URI for production?",
133
+ default: true,
134
+ });
162
135
 
163
- // Ask if the same details should be used for production
164
- function askSameForProduction() {
165
- rl.question(
166
- chalk.blueBright(
167
- "\nUse the same database details for production? (Yes/No): "
168
- ),
169
- (answer) => {
170
- const response = answer.trim().toLowerCase();
136
+ let prodUri = devUri;
137
+ if (!useSameForProd) {
138
+ prodUri = await input({
139
+ message: "Production MongoDB URI:",
140
+ default: devUri,
141
+ validate: (v) =>
142
+ v.trim().startsWith("mongodb://") || v.trim().startsWith("mongodb+srv://")
143
+ ? true
144
+ : "Must start with mongodb:// or mongodb+srv://",
145
+ });
146
+ prodUri = prodUri.trim();
147
+ }
171
148
 
172
- if (response === "y" || response === "yes") {
173
- addMongoDB(devUri, devUri);
174
- rl.close();
175
- process.exit(0);
176
- } else if (response === "n" || response === "no") {
177
- promptForDatabaseDetails(
178
- "production",
179
- (prodDbName) => {
180
- rl.question(
181
- chalk.yellow(
182
- "Enter the MongoDB IP address (default: 127.0.0.1): "
183
- ),
184
- (prodIpAddress) => {
185
- const prodIp =
186
- prodIpAddress.trim() || "127.0.0.1";
187
- rl.question(
188
- chalk.yellow(
189
- "Enter the MongoDB port (default: 27017): "
190
- ),
191
- (prodPort) => {
192
- const prodDbPort =
193
- prodPort.trim() || "27017";
194
- rl.question(
195
- chalk.yellow(
196
- "Enter the MongoDB user ID (leave blank if none): "
197
- ),
198
- (prodUserId) => {
199
- rl.question(
200
- chalk.yellow(
201
- "Enter the MongoDB password (leave blank if none): "
202
- ),
203
- (prodPassword) => {
204
- let prodUri = `mongodb://${prodIp}:${prodDbPort}/${prodDbName}`;
205
- if (
206
- prodUserId &&
207
- prodPassword
208
- ) {
209
- prodUri = `mongodb://${prodUserId}:${prodPassword}@${prodIp}:${prodDbPort}/${prodDbName}`;
210
- } else if (
211
- prodUserId ||
212
- prodPassword
213
- ) {
214
- console.error(
215
- chalk.red(
216
- "Both user ID and password must be provided together."
217
- )
218
- );
219
- process.exit(1);
220
- }
149
+ console.log();
150
+ addMongoDB(devUri, prodUri);
221
151
 
222
- addMongoDB(devUri, prodUri);
223
- rl.close();
224
- process.exit(0);
225
- }
226
- );
227
- }
228
- );
229
- }
230
- );
231
- }
232
- );
233
- }
234
- );
235
- } else {
236
- console.log(
237
- chalk.red(
238
- "Invalid input. Please enter Yes or No."
239
- )
240
- );
241
- askSameForProduction();
242
- }
243
- }
244
- );
245
- }
152
+ await installDeps("mongoose");
246
153
 
247
- askSameForProduction();
248
- }
249
- );
250
- }
251
- );
252
- }
253
- );
254
- }
255
- );
256
- });
154
+ showTaskDone("MongoDB integration complete", [
155
+ `Dev: ${devUri}`,
156
+ `Prod: ${prodUri}`,
157
+ ]);
257
158
  }
159
+
160
+ main().catch((err) => {
161
+ if (err.name === "ExitPromptError") process.exit(0);
162
+ showError(err.message);
163
+ process.exit(1);
164
+ });
package/add-serialport.js CHANGED
@@ -1,49 +1,37 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import fs from "fs";
4
3
  import path from "path";
5
- import readline from "readline";
6
- import { execSync } from "child_process";
7
- import chalk from "chalk";
8
-
9
- const rl = readline.createInterface({
10
- input: process.stdin,
11
- output: process.stdout,
12
- });
13
-
14
- function updateEnvFile(filePath, serialPortPath, baudRate) {
15
- const serialPortPathLine = `SERIALPORT_PATH=${serialPortPath}\n`;
16
- const baudRateLine = `BAUDRATE=${baudRate}\n`;
4
+ import { input } from "@inquirer/prompts";
5
+ import { getProjectRoot } from "./lib/project.js";
6
+ import { updateEnvFile } from "./lib/env.js";
7
+ import { addImport, insertBeforeListen } from "./lib/indexFile.js";
8
+ import {
9
+ showHeader,
10
+ showTaskDone,
11
+ showError,
12
+ showFileAction,
13
+ installDeps,
14
+ } from "./lib/ui.js";
15
+
16
+ async function main() {
17
+ showHeader("add-serialport", "Add serial port communication");
18
+
19
+ const serialPortPath = await input({
20
+ message: "Serial port path:",
21
+ default: "/dev/tty-usbserial1",
22
+ });
17
23
 
18
- if (fs.existsSync(filePath)) {
19
- let envContent = fs.readFileSync(filePath, "utf8");
20
- if (envContent.includes("SERIALPORT_PATH=")) {
21
- envContent = envContent.replace(
22
- /SERIALPORT_PATH=.*/,
23
- serialPortPathLine.trim()
24
- );
25
- } else {
26
- envContent = envContent.trim() + "\n" + serialPortPathLine;
27
- }
28
- if (envContent.includes("BAUDRATE=")) {
29
- envContent = envContent.replace(/BAUDRATE=.*/, baudRateLine.trim());
30
- } else {
31
- envContent = envContent.trim() + "\n" + baudRateLine;
32
- }
33
- fs.writeFileSync(filePath, envContent, "utf8");
34
- } else {
35
- fs.writeFileSync(filePath, serialPortPathLine + baudRateLine, "utf8");
36
- }
37
- }
24
+ const baudRate = await input({
25
+ message: "Baud rate:",
26
+ default: "9600",
27
+ validate: (v) => (!isNaN(parseInt(v, 10)) ? true : "Enter a valid number."),
28
+ });
38
29
 
39
- function addSerialPort(serialPortPath, baudRate) {
40
- const projectRoot = process.cwd();
41
- const indexPath = path.join(projectRoot, "index.js");
30
+ console.log();
42
31
 
43
- const serialportImport = `
44
- import { SerialPort } from 'serialport';
45
- import { ReadlineParser } from '@serialport/parser-readline';
46
- `;
32
+ addImport("import { SerialPort } from 'serialport';");
33
+ addImport("import { ReadlineParser } from '@serialport/parser-readline';");
34
+ showFileAction("updated", "index.js");
47
35
 
48
36
  const serialportSetup = `
49
37
  try {
@@ -62,63 +50,30 @@ try {
62
50
  console.error("SerialPort Error: ", err.message);
63
51
  });
64
52
  } catch (error) {
65
- console.error("Error initializing SerialPort or Parser:", error);
66
- }
67
- `;
68
-
69
- let indexContent = fs.readFileSync(indexPath, "utf8");
70
-
71
- // Add imports if they don't already exist
72
- if (!indexContent.includes("import { SerialPort }")) {
73
- indexContent = serialportImport + indexContent;
74
- }
75
-
76
- // Ensure SerialPort setup is added just before app.listen or server.listen
77
- const listenMatch = indexContent.match(/(app|server)\.listen\(.*\);/);
78
- if (listenMatch) {
79
- indexContent = indexContent.replace(
80
- listenMatch[0],
81
- serialportSetup + "\n" + listenMatch[0]
82
- );
83
- } else {
84
- indexContent += serialportSetup;
85
- }
86
-
87
- fs.writeFileSync(indexPath, indexContent, "utf8");
88
-
89
- // Update .env files
90
- updateEnvFile(
91
- path.join(projectRoot, ".env.development"),
92
- serialPortPath,
93
- baudRate
94
- );
95
- updateEnvFile(
96
- path.join(projectRoot, ".env.production"),
97
- serialPortPath,
98
- baudRate
99
- );
100
-
101
- // Install serialport and @serialport/parser-readline
102
- execSync("npm install serialport @serialport/parser-readline", {
103
- stdio: "inherit",
104
- });
105
-
106
- console.log(chalk.green("SerialPort integration added successfully."));
53
+ console.error("Error initializing SerialPort:", error);
54
+ }`;
55
+
56
+ insertBeforeListen(serialportSetup);
57
+
58
+ const projectRoot = getProjectRoot();
59
+ updateEnvFile(path.join(projectRoot, ".env.development"), "SERIALPORT_PATH", serialPortPath);
60
+ updateEnvFile(path.join(projectRoot, ".env.development"), "BAUDRATE", baudRate);
61
+ updateEnvFile(path.join(projectRoot, ".env.production"), "SERIALPORT_PATH", serialPortPath);
62
+ updateEnvFile(path.join(projectRoot, ".env.production"), "BAUDRATE", baudRate);
63
+ showFileAction("updated", ".env.development");
64
+ showFileAction("updated", ".env.production");
65
+
66
+ await installDeps(["serialport", "@serialport/parser-readline"]);
67
+
68
+ showTaskDone("SerialPort integration complete", [
69
+ `Path: ${serialPortPath}`,
70
+ `Baud rate: ${baudRate}`,
71
+ "Data logged to console on receive",
72
+ ]);
107
73
  }
108
74
 
109
- rl.question(
110
- chalk.yellow(
111
- "Enter the serial port path (e.g., /dev/tty-usbserial1) [default: /dev/tty-usbserial1]: "
112
- ),
113
- (serialPortPath) => {
114
- serialPortPath = serialPortPath.trim() || "/dev/tty-usbserial1";
115
- rl.question(
116
- chalk.yellow("Enter the baud rate (e.g., 9600) [default: 9600]: "),
117
- (baudRate) => {
118
- baudRate = baudRate.trim() || "9600";
119
- addSerialPort(serialPortPath, baudRate);
120
- rl.close();
121
- }
122
- );
123
- }
124
- );
75
+ main().catch((err) => {
76
+ if (err.name === "ExitPromptError") process.exit(0);
77
+ showError(err.message);
78
+ process.exit(1);
79
+ });