create-efc-app 0.3.0 → 0.3.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.
package/dist/index.js CHANGED
@@ -2606,16 +2606,14 @@ export const GET = async (_req: Request, res: Response) => {
2606
2606
  ${roleGuard} // TODO: generate CSV of all users and stream response
2607
2607
  res.setHeader('Content-Type', 'text/csv');
2608
2608
  res.setHeader('Content-Disposition', 'attachment; filename="users.csv"');
2609
- res.send('id,name,email,role,createdAt
2610
- ');
2609
+ res.send('id,name,email,role,createdAt\\n');
2611
2610
  };
2612
2611
  ` : `${RA}${rr3}${exportMeta}${mwAdmin3}
2613
2612
  export const GET = async (_req, res) => {
2614
2613
  ${roleGuard} // TODO: generate CSV of all users and stream response
2615
2614
  res.setHeader('Content-Type', 'text/csv');
2616
2615
  res.setHeader('Content-Disposition', 'attachment; filename="users.csv"');
2617
- res.send('id,name,email,role,createdAt
2618
- ');
2616
+ res.send('id,name,email,role,createdAt\\n');
2619
2617
  };
2620
2618
  `;
2621
2619
  await fs.outputFile(path.join(dest, "src", "api", "admin", "users", `export.${ext}`), exportContent);
@@ -3016,6 +3014,10 @@ ${roleGuard} const { status, page = 1, limit = 20 } = req.query;
3016
3014
  }
3017
3015
 
3018
3016
  // src/index.ts
3017
+ function cancel2(msg = "Cancelled") {
3018
+ p.cancel(msg);
3019
+ process.exit(0);
3020
+ }
3019
3021
  async function main() {
3020
3022
  console.log();
3021
3023
  p.intro(pc.bgCyan(pc.black(" create-efc-app ")));
@@ -3025,10 +3027,7 @@ async function main() {
3025
3027
  defaultValue: "my-api",
3026
3028
  validate: (v) => !v.trim() ? "Project name is required" : void 0
3027
3029
  });
3028
- if (p.isCancel(projectName)) {
3029
- p.cancel("Cancelled");
3030
- process.exit(0);
3031
- }
3030
+ if (p.isCancel(projectName)) cancel2();
3032
3031
  const language = await p.select({
3033
3032
  message: "Language:",
3034
3033
  options: [
@@ -3036,10 +3035,7 @@ async function main() {
3036
3035
  { value: "javascript", label: "JavaScript" }
3037
3036
  ]
3038
3037
  });
3039
- if (p.isCancel(language)) {
3040
- p.cancel("Cancelled");
3041
- process.exit(0);
3042
- }
3038
+ if (p.isCancel(language)) cancel2();
3043
3039
  const database = await p.select({
3044
3040
  message: "Database:",
3045
3041
  options: [
@@ -3047,10 +3043,7 @@ async function main() {
3047
3043
  { value: "postgresql", label: "PostgreSQL", hint: "Drizzle ORM" }
3048
3044
  ]
3049
3045
  });
3050
- if (p.isCancel(database)) {
3051
- p.cancel("Cancelled");
3052
- process.exit(0);
3053
- }
3046
+ if (p.isCancel(database)) cancel2();
3054
3047
  const authStrategy = await p.select({
3055
3048
  message: "Authentication strategy:",
3056
3049
  options: [
@@ -3058,28 +3051,25 @@ async function main() {
3058
3051
  { value: "localStorage", label: "localStorage", hint: "bearer token \u2014 for SPAs" }
3059
3052
  ]
3060
3053
  });
3061
- if (p.isCancel(authStrategy)) {
3062
- p.cancel("Cancelled");
3063
- process.exit(0);
3064
- }
3065
- const cluster = await p.confirm({
3066
- message: "Enable multi-core clustering?",
3067
- initialValue: true
3068
- });
3069
- if (p.isCancel(cluster)) {
3070
- p.cancel("Cancelled");
3071
- process.exit(0);
3072
- }
3073
- const tasks = await p.confirm({
3074
- message: "Enable background tasks?",
3075
- initialValue: true
3054
+ if (p.isCancel(authStrategy)) cancel2();
3055
+ const features = await p.multiselect({
3056
+ message: "Features: (space to toggle, enter to confirm)",
3057
+ options: [
3058
+ { value: "cluster", label: "Multi-core clustering", hint: "Node cluster module" },
3059
+ { value: "tasks", label: "Background tasks", hint: "BullMQ / pg-boss" },
3060
+ { value: "routeDocs", label: "API route documentation", hint: "meta exports + dashboard" },
3061
+ { value: "userPortal", label: "User portal", hint: "auth, profile, billing routes" },
3062
+ { value: "adminPortal", label: "Admin portal", hint: "dashboard, user mgmt, analytics" },
3063
+ { value: "rbac", label: "Role-based access control", hint: "requireRole middleware" },
3064
+ { value: "mailer", label: "Mailer", hint: "nodemailer + SMTP" }
3065
+ ],
3066
+ initialValues: ["cluster", "tasks", "routeDocs", "userPortal", "adminPortal", "rbac"],
3067
+ required: false
3076
3068
  });
3077
- if (p.isCancel(tasks)) {
3078
- p.cancel("Cancelled");
3079
- process.exit(0);
3080
- }
3069
+ if (p.isCancel(features)) cancel2();
3070
+ const selected = new Set(features);
3081
3071
  let taskBackend;
3082
- if (tasks) {
3072
+ if (selected.has("tasks")) {
3083
3073
  const backend = await p.select({
3084
3074
  message: "Task queue backend:",
3085
3075
  options: [
@@ -3087,79 +3077,25 @@ async function main() {
3087
3077
  { value: "pg-boss", label: "pg-boss", hint: "PostgreSQL" }
3088
3078
  ]
3089
3079
  });
3090
- if (p.isCancel(backend)) {
3091
- p.cancel("Cancelled");
3092
- process.exit(0);
3093
- }
3080
+ if (p.isCancel(backend)) cancel2();
3094
3081
  taskBackend = backend;
3095
3082
  }
3096
- const routeDocs = await p.confirm({
3097
- message: "Include route documentation (meta exports for the API dashboard)?",
3098
- initialValue: true
3099
- });
3100
- if (p.isCancel(routeDocs)) {
3101
- p.cancel("Cancelled");
3102
- process.exit(0);
3103
- }
3104
- const userPortal = await p.confirm({
3105
- message: "Build user portal?",
3106
- initialValue: true
3107
- });
3108
- if (p.isCancel(userPortal)) {
3109
- p.cancel("Cancelled");
3110
- process.exit(0);
3111
- }
3112
- const adminPortal = await p.confirm({
3113
- message: "Build admin portal?",
3114
- initialValue: true
3115
- });
3116
- if (p.isCancel(adminPortal)) {
3117
- p.cancel("Cancelled");
3118
- process.exit(0);
3119
- }
3120
- let rbac = false;
3121
- if (userPortal || adminPortal) {
3122
- const rbacAnswer = await p.confirm({
3123
- message: "Enable role-based access control (RBAC)?",
3124
- initialValue: true
3125
- });
3126
- if (p.isCancel(rbacAnswer)) {
3127
- p.cancel("Cancelled");
3128
- process.exit(0);
3129
- }
3130
- rbac = rbacAnswer;
3131
- }
3132
- const mailerAnswer = await p.confirm({
3133
- message: "Enable mailer (nodemailer)?",
3134
- initialValue: false
3135
- });
3136
- if (p.isCancel(mailerAnswer)) {
3137
- p.cancel("Cancelled");
3138
- process.exit(0);
3139
- }
3140
- const mailer = mailerAnswer;
3141
3083
  let smtpHost;
3142
3084
  let smtpPort;
3143
- if (mailer) {
3085
+ if (selected.has("mailer")) {
3144
3086
  const host = await p.text({
3145
3087
  message: "SMTP host:",
3146
3088
  placeholder: "smtp.gmail.com",
3147
3089
  defaultValue: "smtp.gmail.com"
3148
3090
  });
3149
- if (p.isCancel(host)) {
3150
- p.cancel("Cancelled");
3151
- process.exit(0);
3152
- }
3091
+ if (p.isCancel(host)) cancel2();
3153
3092
  smtpHost = host;
3154
3093
  const port = await p.text({
3155
3094
  message: "SMTP port:",
3156
3095
  placeholder: "587",
3157
3096
  defaultValue: "587"
3158
3097
  });
3159
- if (p.isCancel(port)) {
3160
- p.cancel("Cancelled");
3161
- process.exit(0);
3162
- }
3098
+ if (p.isCancel(port)) cancel2();
3163
3099
  smtpPort = port;
3164
3100
  }
3165
3101
  const opts = {
@@ -3167,13 +3103,13 @@ async function main() {
3167
3103
  language,
3168
3104
  database,
3169
3105
  authStrategy,
3170
- cluster,
3171
- tasks,
3172
- routeDocs,
3173
- userPortal,
3174
- adminPortal,
3175
- rbac,
3176
- mailer,
3106
+ cluster: selected.has("cluster"),
3107
+ tasks: selected.has("tasks"),
3108
+ routeDocs: selected.has("routeDocs"),
3109
+ userPortal: selected.has("userPortal"),
3110
+ adminPortal: selected.has("adminPortal"),
3111
+ rbac: selected.has("rbac"),
3112
+ mailer: selected.has("mailer"),
3177
3113
  ...taskBackend !== void 0 && { taskBackend },
3178
3114
  ...smtpHost !== void 0 && { smtpHost },
3179
3115
  ...smtpPort !== void 0 && { smtpPort }
@@ -3208,22 +3144,14 @@ Your project is ready!
3208
3144
  }
3209
3145
  function npmInstall(projectDir) {
3210
3146
  return new Promise((resolve, reject) => {
3211
- const child = spawn("npm", ["install"], {
3212
- cwd: projectDir,
3213
- stdio: "ignore"
3214
- });
3215
- child.on("exit", (code) => code === 0 ? resolve() : reject(new Error(`npm install failed`)));
3147
+ const child = spawn("npm", ["install"], { cwd: projectDir, stdio: "ignore" });
3148
+ child.on("exit", (code) => code === 0 ? resolve() : reject(new Error("npm install failed")));
3216
3149
  });
3217
3150
  }
3218
3151
  function npmInstallGlobal() {
3219
3152
  return new Promise((resolve, reject) => {
3220
- const child = spawn("npm", ["install", "-g", "express-file-cluster"], {
3221
- stdio: "ignore"
3222
- });
3223
- child.on(
3224
- "exit",
3225
- (code) => code === 0 ? resolve() : reject(new Error("global install failed"))
3226
- );
3153
+ const child = spawn("npm", ["install", "-g", "express-file-cluster"], { stdio: "ignore" });
3154
+ child.on("exit", (code) => code === 0 ? resolve() : reject(new Error("global install failed")));
3227
3155
  });
3228
3156
  }
3229
3157
  main().catch((err) => {