exp-node-server 1.1.7 → 2.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 (181) hide show
  1. package/README.md +82 -20
  2. package/dist/cli.js +525 -0
  3. package/package.json +29 -9
  4. package/templates/mongoose/.dockerignore +10 -0
  5. package/templates/mongoose/.env.example +56 -7
  6. package/templates/mongoose/.prettierignore +3 -0
  7. package/templates/mongoose/.prettierrc +7 -0
  8. package/templates/mongoose/Dockerfile +33 -0
  9. package/templates/mongoose/_gitignore +32 -0
  10. package/templates/mongoose/docker-compose.yml +21 -0
  11. package/templates/mongoose/eslint.config.js +35 -0
  12. package/templates/mongoose/package-lock.json +6377 -0
  13. package/templates/mongoose/package.json +49 -10
  14. package/templates/mongoose/readme.md +85 -0
  15. package/templates/mongoose/src/app/configs/index.ts +178 -26
  16. package/templates/mongoose/src/app/errors/zodError.ts +16 -16
  17. package/templates/mongoose/src/app/lib/db.ts +18 -0
  18. package/templates/mongoose/src/app/lib/logger.ts +12 -0
  19. package/templates/mongoose/src/app/middlewares/auth.ts +48 -44
  20. package/templates/mongoose/src/app/middlewares/global_error_handler.ts +59 -65
  21. package/templates/mongoose/src/app/middlewares/not_found_api.ts +7 -7
  22. package/templates/mongoose/src/app/middlewares/rate_limiter.ts +153 -0
  23. package/templates/mongoose/src/app/middlewares/request_validator.ts +11 -10
  24. package/templates/mongoose/src/app/middlewares/uploader.ts +17 -13
  25. package/templates/mongoose/src/app/modules/account/account.controller.ts +156 -0
  26. package/templates/mongoose/src/app/modules/account/account.interface.ts +65 -0
  27. package/templates/mongoose/src/app/modules/account/account.route.ts +101 -0
  28. package/templates/mongoose/src/app/modules/account/account.schema.ts +97 -0
  29. package/templates/mongoose/src/app/modules/account/account.service.ts +422 -0
  30. package/templates/mongoose/src/app/modules/account/account.swagger.ts +278 -0
  31. package/templates/mongoose/src/app/modules/account/account.validation.ts +63 -0
  32. package/templates/mongoose/src/app/modules/profile/profile.controller.ts +17 -0
  33. package/templates/mongoose/src/app/modules/profile/profile.interface.ts +7 -0
  34. package/templates/mongoose/src/app/modules/profile/profile.route.ts +28 -0
  35. package/templates/mongoose/src/app/modules/profile/profile.schema.ts +13 -0
  36. package/templates/mongoose/src/app/modules/profile/profile.service.ts +28 -0
  37. package/templates/mongoose/src/app/modules/profile/profile.swagger.ts +34 -0
  38. package/templates/mongoose/src/app/modules/profile/profile.validation.ts +8 -0
  39. package/templates/mongoose/src/app/passport-js/index.ts +87 -0
  40. package/templates/mongoose/src/app/passport-js/oauth-state.ts +150 -0
  41. package/templates/mongoose/src/app/passport-js/providers.ts +258 -0
  42. package/templates/mongoose/src/app/passport-js/social-account.ts +141 -0
  43. package/templates/mongoose/src/app/queues/agenda.ts +24 -0
  44. package/templates/mongoose/src/app/queues/email.queue.ts +53 -0
  45. package/templates/mongoose/src/app/queues/index.ts +13 -0
  46. package/templates/mongoose/src/app/templates/otpEmailTemplate.ts +185 -0
  47. package/templates/mongoose/src/app/templates/resetPasswordEmailTemplate.ts +157 -0
  48. package/templates/mongoose/src/app/types/error.d.ts +7 -7
  49. package/templates/mongoose/src/app/types/express.d.ts +11 -0
  50. package/templates/mongoose/src/app/types/passport-oauth1.d.ts +51 -0
  51. package/templates/mongoose/src/app/utils/JWT.ts +38 -18
  52. package/templates/mongoose/src/app/utils/app_error.ts +10 -10
  53. package/templates/mongoose/src/app/utils/auth_cookie.ts +27 -0
  54. package/templates/mongoose/src/app/utils/catch_async.ts +8 -8
  55. package/templates/mongoose/src/app/utils/cloudinary.ts +26 -59
  56. package/templates/mongoose/src/app/utils/mail_sender.ts +26 -92
  57. package/templates/mongoose/src/app/utils/manage_response.ts +19 -19
  58. package/templates/mongoose/src/app/utils/otp.ts +20 -0
  59. package/templates/mongoose/src/app/utils/session.ts +55 -0
  60. package/templates/mongoose/src/app.ts +73 -42
  61. package/templates/mongoose/src/routes.ts +13 -13
  62. package/templates/mongoose/src/server.ts +38 -18
  63. package/templates/mongoose/src/swaggerOptions.ts +23 -23
  64. package/templates/mongoose/tests/app.test.ts +97 -0
  65. package/templates/mongoose/tests/global-setup.ts +15 -0
  66. package/templates/mongoose/tests/helpers/fixtures.ts +42 -0
  67. package/templates/mongoose/tests/routes/account.test.ts +614 -0
  68. package/templates/mongoose/tests/routes/profile.test.ts +95 -0
  69. package/templates/mongoose/tests/routes/social.test.ts +310 -0
  70. package/templates/mongoose/tests/setup.ts +25 -0
  71. package/templates/mongoose/tsconfig.json +16 -20
  72. package/templates/mongoose/vitest.config.ts +43 -0
  73. package/templates/prisma/.dockerignore +2 -0
  74. package/templates/prisma/.env.example +62 -6
  75. package/templates/prisma/.prettierignore +3 -0
  76. package/templates/prisma/.prettierrc +7 -0
  77. package/templates/prisma/.projectrc.json +1 -1
  78. package/templates/prisma/Dockerfile +21 -7
  79. package/templates/prisma/_gitignore +35 -0
  80. package/templates/prisma/docker-compose.yml +8 -7
  81. package/templates/prisma/eslint.config.js +35 -0
  82. package/templates/prisma/package-lock.json +7327 -0
  83. package/templates/prisma/package.json +47 -9
  84. package/templates/prisma/prisma/schema/account.schema.prisma +92 -10
  85. package/templates/prisma/prisma/schema/profile.schema.prisma +8 -3
  86. package/templates/prisma/prisma/schema/schema.prisma +1 -2
  87. package/templates/prisma/prisma.config.ts +5 -5
  88. package/templates/prisma/readme.md +81 -0
  89. package/templates/prisma/src/app/configs/index.ts +175 -26
  90. package/templates/prisma/src/app/errors/zodError.ts +16 -16
  91. package/templates/prisma/src/app/lib/logger.ts +12 -0
  92. package/templates/prisma/src/app/lib/prisma.ts +4 -6
  93. package/templates/prisma/src/app/middlewares/auth.ts +37 -16
  94. package/templates/prisma/src/app/middlewares/global_error_handler.ts +42 -35
  95. package/templates/prisma/src/app/middlewares/not_found_api.ts +7 -7
  96. package/templates/prisma/src/app/middlewares/rate_limiter.ts +124 -0
  97. package/templates/prisma/src/app/middlewares/request_validator.ts +4 -3
  98. package/templates/prisma/src/app/middlewares/uploader.ts +17 -13
  99. package/templates/prisma/src/app/modules/account/account.controller.ts +74 -34
  100. package/templates/prisma/src/app/modules/account/account.route.ts +68 -22
  101. package/templates/prisma/src/app/modules/account/account.service.ts +329 -309
  102. package/templates/prisma/src/app/modules/account/account.swagger.ts +167 -76
  103. package/templates/prisma/src/app/modules/account/account.validation.ts +41 -19
  104. package/templates/prisma/src/app/modules/profile/profile.controller.ts +4 -4
  105. package/templates/prisma/src/app/modules/profile/profile.route.ts +18 -12
  106. package/templates/prisma/src/app/modules/profile/profile.service.ts +11 -18
  107. package/templates/prisma/src/app/modules/profile/profile.swagger.ts +11 -11
  108. package/templates/prisma/src/app/modules/profile/profile.validation.ts +2 -2
  109. package/templates/prisma/src/app/passport-js/index.ts +87 -0
  110. package/templates/prisma/src/app/passport-js/oauth-state.ts +150 -0
  111. package/templates/prisma/src/app/passport-js/providers.ts +263 -0
  112. package/templates/prisma/src/app/passport-js/social-account.ts +147 -0
  113. package/templates/prisma/src/app/queues/boss.ts +8 -0
  114. package/templates/prisma/src/app/queues/email.queue.ts +56 -0
  115. package/templates/prisma/src/app/queues/index.ts +14 -0
  116. package/templates/prisma/src/app/queues/maintenance.queue.ts +30 -0
  117. package/templates/prisma/src/app/templates/otpEmailTemplate.ts +185 -0
  118. package/templates/prisma/src/app/templates/resetPasswordEmailTemplate.ts +157 -0
  119. package/templates/prisma/src/app/types/error.d.ts +7 -7
  120. package/templates/prisma/src/app/types/express.d.ts +11 -0
  121. package/templates/prisma/src/app/types/passport-oauth1.d.ts +51 -0
  122. package/templates/prisma/src/app/utils/JWT.ts +36 -17
  123. package/templates/prisma/src/app/utils/app_error.ts +10 -10
  124. package/templates/prisma/src/app/utils/auth_cookie.ts +27 -0
  125. package/templates/prisma/src/app/utils/catch_async.ts +8 -8
  126. package/templates/prisma/src/app/utils/cloudinary.ts +26 -59
  127. package/templates/prisma/src/app/utils/mail_sender.ts +26 -92
  128. package/templates/prisma/src/app/utils/manage_response.ts +19 -19
  129. package/templates/prisma/src/app/utils/otp.ts +20 -0
  130. package/templates/prisma/src/app/utils/session.ts +57 -0
  131. package/templates/prisma/src/app.ts +73 -42
  132. package/templates/prisma/src/routes.ts +12 -10
  133. package/templates/prisma/src/server.ts +37 -19
  134. package/templates/prisma/src/swaggerOptions.ts +22 -26
  135. package/templates/prisma/tests/app.test.ts +86 -0
  136. package/templates/prisma/tests/helpers/fixtures.ts +35 -0
  137. package/templates/prisma/tests/helpers/prisma.ts +32 -0
  138. package/templates/prisma/tests/routes/account.test.ts +627 -0
  139. package/templates/prisma/tests/routes/profile.test.ts +91 -0
  140. package/templates/prisma/tests/routes/social.test.ts +317 -0
  141. package/templates/prisma/tests/setup.ts +10 -0
  142. package/templates/prisma/tsconfig.json +16 -23
  143. package/templates/prisma/vitest.config.ts +38 -0
  144. package/bin/cli.js +0 -41
  145. package/src/createProject.js +0 -61
  146. package/src/generateModule.js +0 -93
  147. package/src/utils/getProjectType.js +0 -13
  148. package/src/utils/mongoose/controllerGenerator.js +0 -70
  149. package/src/utils/mongoose/routeGenerator.js +0 -27
  150. package/src/utils/mongoose/schemaGenerator.js +0 -11
  151. package/src/utils/mongoose/serviceGenerator.js +0 -49
  152. package/src/utils/mongoose/swaggerGenerator.js +0 -112
  153. package/src/utils/mongoose/validationGenerator.js +0 -13
  154. package/src/utils/prisma/prismaSchemaGenerator.js +0 -10
  155. package/src/utils/prisma/prismaServiceGenerator.js +0 -47
  156. package/src/utils/routeInjector.js +0 -40
  157. package/src/utils/swaggerInjector.js +0 -52
  158. package/src/utils/validateModuleName.js +0 -17
  159. package/src/utils/validateProjectName.js +0 -15
  160. package/src/utils.js +0 -26
  161. package/templates/mongoose/src/app/errors/castError.ts +0 -23
  162. package/templates/mongoose/src/app/errors/duplicateError.ts +0 -26
  163. package/templates/mongoose/src/app/errors/validationError.ts +0 -25
  164. package/templates/mongoose/src/app/modules/auth/auth.controller.ts +0 -127
  165. package/templates/mongoose/src/app/modules/auth/auth.interface.ts +0 -24
  166. package/templates/mongoose/src/app/modules/auth/auth.route.ts +0 -46
  167. package/templates/mongoose/src/app/modules/auth/auth.schema.ts +0 -19
  168. package/templates/mongoose/src/app/modules/auth/auth.service.ts +0 -324
  169. package/templates/mongoose/src/app/modules/auth/auth.swagger.ts +0 -214
  170. package/templates/mongoose/src/app/modules/auth/auth.validation.ts +0 -37
  171. package/templates/mongoose/src/app/modules/user/user.controller.ts +0 -17
  172. package/templates/mongoose/src/app/modules/user/user.interface.ts +0 -15
  173. package/templates/mongoose/src/app/modules/user/user.route.ts +0 -20
  174. package/templates/mongoose/src/app/modules/user/user.schema.ts +0 -22
  175. package/templates/mongoose/src/app/modules/user/user.service.ts +0 -22
  176. package/templates/mongoose/src/app/modules/user/user.swagger.ts +0 -75
  177. package/templates/mongoose/src/app/modules/user/user.validation.ts +0 -18
  178. package/templates/mongoose/src/app/types/index.d.ts +0 -12
  179. package/templates/mongoose/src/app/utils/isAccountExist.ts +0 -31
  180. package/templates/prisma/src/app/types/index.d.ts +0 -12
  181. package/templates/prisma/src/app/utils/otpGenerator.ts +0 -29
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  [![npm downloads](https://img.shields.io/npm/dw/exp-node-server.svg)](https://www.npmjs.com/package/exp-node-server)
5
5
  [![npm total downloads](https://img.shields.io/npm/dt/exp-node-server.svg)](https://www.npmjs.com/package/exp-node-server)
6
6
  [![Made with TypeScript](https://img.shields.io/badge/Made%20with-TypeScript-blue.svg)](https://www.typescriptlang.org/)
7
- [![Node.js](https://img.shields.io/badge/Node.js-18+-green.svg)](https://nodejs.org/)
7
+ [![Node.js](https://img.shields.io/badge/Node.js-20.17+-green.svg)](https://nodejs.org/)
8
8
 
9
9
  ---
10
10
 
@@ -18,7 +18,6 @@ This tool helps developers quickly scaffold `clean`, `modular` `Express` APIs wi
18
18
 
19
19
  - ⚡ Generate complete `Express` + `TypeScript` modules
20
20
  - 🧩 Built-in `Mongoose` / `Prisma` support
21
- - 📘 Adds Swagger documentation automatically
22
21
  - 📘 Automatic `Swagger` documentation
23
22
  - 🔐 `Zod` validation ready
24
23
  - 🏗️ `Modular` clean architecture
@@ -30,19 +29,27 @@ This tool helps developers quickly scaffold `clean`, `modular` `Express` APIs wi
30
29
 
31
30
  Run directly using npx (recommended):
32
31
 
33
- ```Bash
32
+ ```bash
34
33
  npx exp-node-server -c my-api
35
34
  ```
36
35
 
37
- This will:
36
+ You'll be asked to pick a template (Mongoose or Prisma). To skip the prompt:
37
+
38
+ ```bash
39
+ npx exp-node-server create my-api --template prisma
40
+ ```
38
41
 
39
- - Create an Express starter project
40
- - Install dependencies
41
- - Prepare the project for development
42
+ Then:
43
+
44
+ ```bash
45
+ cd my-api
46
+ npm install
47
+ npm run dev
48
+ ```
42
49
 
43
50
  ## 🧩 Generate a Module
44
51
 
45
- Inside your project run:
52
+ Inside your project root run:
46
53
 
47
54
  ```bash
48
55
  npx exp-node-server -g <module-name>
@@ -52,33 +59,71 @@ Example: `npx exp-node-server -g order`
52
59
 
53
60
  Output:
54
61
 
55
- ```Bash
56
- ✔ order.interface.ts created
57
- ✔ order.schema.ts created
58
- ✔ order.validation.ts created
59
- ✔ order.route.ts created
60
- ✔ order.controller.ts created
61
- ✔ order.service.ts created
62
- ✔ order.swagger.ts created
62
+ ```bash
63
+ ✔ src/app/modules/order/order.controller.ts created
64
+ ✔ src/app/modules/order/order.route.ts created
65
+ ✔ src/app/modules/order/order.validation.ts created
66
+ ✔ src/app/modules/order/order.swagger.ts created
67
+ ✔ src/app/modules/order/order.interface.ts created
68
+ ✔ src/app/modules/order/order.schema.ts created
69
+ ✔ src/app/modules/order/order.service.ts created
70
+ ✔ tests/routes/order.test.ts created
71
+ ✔ Route registered in routes.ts
72
+ ✔ Swagger docs registered in swaggerOptions.ts
73
+ ```
63
74
 
64
- 🔗 Route registered in routes.ts
65
- 📘 Swagger docs registered
75
+ The route and Swagger docs are wired up automatically. For Prisma projects a
76
+ `prisma/schema/<module>.prisma` model is created as well.
66
77
 
78
+ A ready-to-run [supertest](https://github.com/ladjs/supertest) suite is created in
79
+ `tests/routes/<module>.test.ts`. It covers create, list, get, update and delete,
80
+ including the 404s, and passes as generated:
81
+
82
+ ```bash
83
+ npm test -- tests/routes/order.test.ts
67
84
  ```
68
85
 
86
+ When you add fields, fill in `createPayload` / `updatePayload` at the top of the test.
87
+ Mongoose tests run against an in-memory MongoDB; Prisma tests use the Prisma mock in
88
+ `tests/helpers/prisma.ts`. Module names whose path is already taken (e.g. `auth`) are
89
+ refused.
90
+
91
+ ## 🛠️ CLI Reference
92
+
93
+ ```text
94
+ npx exp-node-server create <project-name> [--template <mongoose|prisma>]
95
+ npx exp-node-server generate <module-name> [--force]
96
+
97
+ Commands
98
+ create, -c <name> Create a new project from a template
99
+ generate, -g <name> Generate a module in the current project
100
+
101
+ Options
102
+ -t, --template <name> Template to use with create (mongoose | prisma)
103
+ -f, --force Overwrite an existing module when generating
104
+ -h, --help Show help
105
+ -v, --version Show version
106
+ ```
107
+
108
+ - Project names are lowercased and spaces become dashes (`My API` → `my-api`).
109
+ - Module names are lowercased and spaces/dashes become underscores (`order-item` → `order_item`).
110
+
69
111
  ## 📁 Generated Module Structure
70
112
 
71
113
  Each module follows a clean structure:
72
114
 
73
115
  ```Bash
74
116
  src/app/modules/<module-name>/
75
- ├── <module>.interface.ts
76
- ├── <module>.schema.ts
117
+ ├── <module>.interface.ts # mongoose
118
+ ├── <module>.schema.ts # mongoose (prisma: prisma/schema/<module>.prisma)
77
119
  ├── <module>.validation.ts
78
120
  ├── <module>.route.ts
79
121
  ├── <module>.controller.ts
80
122
  ├── <module>.service.ts
81
123
  └── <module>.swagger.ts
124
+
125
+ tests/routes/
126
+ └── <module>.test.ts
82
127
  ```
83
128
 
84
129
  ## ⚙️ Run the Project
@@ -103,6 +148,22 @@ http://localhost:5000/docs
103
148
  - JWT — Authentication
104
149
  - Swagger — API documentation
105
150
 
151
+ ## 🧑‍🔧 Development
152
+
153
+ ```bash
154
+ npm install
155
+ npm run dev # rebuild on change (tsup --watch)
156
+ npm run typecheck # tsc
157
+ npm test # vitest (unit + end-to-end against dist/)
158
+ npm run build # bundle to dist/cli.js
159
+ ```
160
+
161
+ Try the local build with `node dist/cli.js --help`, or run `npm link` to use
162
+ `exp-node-server` globally.
163
+
164
+ > Templates live in `templates/`. npm strips `.gitignore` files when
165
+ > publishing, so name them `_gitignore`. They're renamed when a project is created.
166
+
106
167
  ## 👨‍💻 Author
107
168
 
108
169
  ### Abumahid
@@ -124,3 +185,4 @@ https://linkedin.com/in/md-abu-mahid-islam
124
185
  If you find this project helpful, consider giving it a ⭐ on `GitHub`.
125
186
 
126
187
  It helps the project grow and reach more developers.
188
+
package/dist/cli.js ADDED
@@ -0,0 +1,525 @@
1
+ #!/usr/bin/env node
2
+ var c=class extends Error{exitCode;constructor(t,r=1){super(t),this.name="CliError",this.exitCode=r}};import T from"chalk";var a={info:e=>console.log(e),success:e=>console.log(`${T.green("\u2714")} ${e}`),warn:e=>console.warn(`${T.yellow("\u26A0")} ${e}`),error:e=>console.error(`${T.red("\u2716")} ${e}`)};import S from"chalk";import{parseArgs as xe}from"util";import de from"@inquirer/select";import g from"chalk";import m from"fs";import h from"path";import pe from"ora";import j from"path";import{fileURLToPath as ce}from"url";var B=j.dirname(ce(import.meta.url)),C=j.basename(B)==="lib"?j.resolve(B,"../../templates"):j.resolve(B,"../templates");var le={_gitignore:".gitignore"},ue=new Set(["node_modules","dist","coverage",".env"]);async function k(e,{template:t,cwd:r=process.cwd()}={}){let s=h.join(r,e);if(m.existsSync(s))throw new c(`Folder '${e}' already exists.`);let n=t??await me(),i=h.join(C,n);if(!m.existsSync(i))throw new c(`Template '${n}' not found at ${i}.`);let o=pe(`Creating ${n} project...`).start();try{await m.promises.cp(i,s,{recursive:!0,filter:d=>!ue.has(h.basename(d))});for(let[d,f]of Object.entries(le)){let p=h.join(s,d);m.existsSync(p)&&await m.promises.rename(p,h.join(s,f))}await fe(s,e)}catch(d){throw o.fail("Failed to create project"),await m.promises.rm(s,{recursive:!0,force:!0}),d}o.succeed(`Project '${e}' created`),a.info(g.green(`
3
+ Next steps:`)),a.info(g.cyan(` cd ${e}`)),a.info(g.cyan(" npm install")),a.info(g.cyan(" cp .env.example .env # then fill in the secrets")),a.info(g.cyan(" docker compose up -d # starts the database")),n==="prisma"&&a.info(g.cyan(" npm run db:migrate")),a.info(g.cyan(" npm run dev")),a.info(g.cyan(" npm test"))}async function me(){return de({message:"Select a project template:",choices:[{name:"Mongoose (MongoDB)",value:"mongoose"},{name:"Prisma (PostgreSQL)",value:"prisma"}]})}async function fe(e,t){let r=h.join(e,"package.json");if(!m.existsSync(r))return;let s=JSON.parse(await m.promises.readFile(r,"utf-8"));s.name=t,await m.promises.writeFile(r,`${JSON.stringify(s,null,2)}
4
+ `)}import y from"chalk";import $ from"fs";import l from"path";var _=(e,t)=>{let r=`await ${e}_service.${t}(req);`,s=` const result = ${r}`;return s.length<=100?s:` const result =
5
+ ${r}`},D=e=>`import catchAsync from '../../utils/catch_async.js';
6
+ import manageResponse from '../../utils/manage_response.js';
7
+ import { ${e}_service } from './${e}.service.js';
8
+
9
+ const get_all_${e} = catchAsync(async (req, res) => {
10
+ ${_(e,`get_all_${e}_from_db`)}
11
+ manageResponse(res, {
12
+ success: true,
13
+ statusCode: 200,
14
+ message: 'All ${e} fetched successfully.',
15
+ data: result,
16
+ meta: {},
17
+ });
18
+ });
19
+
20
+ const get_single_${e} = catchAsync(async (req, res) => {
21
+ ${_(e,`get_single_${e}_from_db`)}
22
+ manageResponse(res, {
23
+ success: true,
24
+ statusCode: 200,
25
+ message: 'Single ${e} fetched successfully.',
26
+ data: result,
27
+ meta: {},
28
+ });
29
+ });
30
+
31
+ const create_${e} = catchAsync(async (req, res) => {
32
+ ${_(e,`create_${e}_into_db`)}
33
+ manageResponse(res, {
34
+ success: true,
35
+ statusCode: 201,
36
+ message: '${e} created successfully.',
37
+ data: result,
38
+ meta: {},
39
+ });
40
+ });
41
+
42
+ const update_${e} = catchAsync(async (req, res) => {
43
+ ${_(e,`update_${e}_into_db`)}
44
+ manageResponse(res, {
45
+ success: true,
46
+ statusCode: 200,
47
+ message: '${e} updated successfully.',
48
+ data: result,
49
+ meta: {},
50
+ });
51
+ });
52
+
53
+ const delete_${e} = catchAsync(async (req, res) => {
54
+ ${_(e,`delete_${e}_from_db`)}
55
+ manageResponse(res, {
56
+ success: true,
57
+ statusCode: 200,
58
+ message: '${e} deleted successfully.',
59
+ data: result,
60
+ meta: {},
61
+ });
62
+ });
63
+
64
+ export const ${e}_controller = {
65
+ get_all_${e},
66
+ get_single_${e},
67
+ create_${e},
68
+ update_${e},
69
+ delete_${e},
70
+ };
71
+ `;var x=(e,t)=>{let r=`router.${e}(${t.join(", ")});`;return r.length<=100?r:`router.${e}(
72
+ ${t.map(s=>` ${s},`).join(`
73
+ `)}
74
+ );`},G=e=>`import { Router } from 'express';
75
+ import RequestValidator from '../../middlewares/request_validator.js';
76
+ import { ${e}_controller } from './${e}.controller.js';
77
+ import { ${e}_validations } from './${e}.validation.js';
78
+
79
+ const router = Router();
80
+
81
+ ${x("get",["'/'",`${e}_controller.get_all_${e}`])}
82
+ ${x("post",["'/'",`RequestValidator(${e}_validations.create_${e})`,`${e}_controller.create_${e}`])}
83
+ ${x("get",["'/:id'",`${e}_controller.get_single_${e}`])}
84
+ ${x("patch",["'/:id'",`RequestValidator(${e}_validations.update_${e})`,`${e}_controller.update_${e}`])}
85
+ ${x("delete",["'/:id'",`${e}_controller.delete_${e}`])}
86
+
87
+ export default router;
88
+ `;var A=`{
89
+ name: 'id',
90
+ in: 'path',
91
+ required: true,
92
+ schema: { type: 'string' },
93
+ }`,F=e=>`export const ${e}SwaggerDocs = {
94
+ '/api/${e}': {
95
+ post: {
96
+ tags: ['${e}'],
97
+ summary: 'Create new ${e}',
98
+ description: '',
99
+ requestBody: {
100
+ required: true,
101
+ content: {
102
+ 'application/json': {
103
+ example: JSON.stringify({}), // put your request body
104
+ },
105
+ },
106
+ },
107
+ responses: {
108
+ 201: { description: '${e} created successfully' },
109
+ 500: { description: 'Validation error or internal server error' },
110
+ },
111
+ },
112
+ get: {
113
+ tags: ['${e}'],
114
+ summary: 'Get all ${e}',
115
+ description: '',
116
+ parameters: [
117
+ {
118
+ name: 'page',
119
+ in: 'query',
120
+ required: false,
121
+ schema: { type: 'number' },
122
+ },
123
+ {
124
+ name: 'limit',
125
+ in: 'query',
126
+ required: false,
127
+ schema: { type: 'number' },
128
+ },
129
+ ],
130
+ responses: {
131
+ 200: { description: '${e} fetched successfully' },
132
+ 401: { description: 'unauthorized' },
133
+ },
134
+ },
135
+ },
136
+
137
+ '/api/${e}/{id}': {
138
+ get: {
139
+ tags: ['${e}'],
140
+ summary: 'Get single ${e}',
141
+ description: '',
142
+ parameters: [
143
+ ${A},
144
+ ],
145
+ responses: {
146
+ 200: { description: '${e} fetched successfully' },
147
+ 401: { description: 'unauthorized' },
148
+ },
149
+ },
150
+ patch: {
151
+ tags: ['${e}'],
152
+ summary: 'Update ${e}',
153
+ description: '',
154
+ parameters: [
155
+ ${A},
156
+ ],
157
+ requestBody: {
158
+ required: true,
159
+ content: {
160
+ 'application/json': {
161
+ example: JSON.stringify({}), // put your request body
162
+ },
163
+ },
164
+ },
165
+ responses: {
166
+ 200: { description: '${e} updated successfully' },
167
+ 500: { description: 'Validation error or internal server error' },
168
+ },
169
+ },
170
+ delete: {
171
+ tags: ['${e}'],
172
+ summary: 'Delete ${e}',
173
+ description: '',
174
+ parameters: [
175
+ ${A},
176
+ ],
177
+ responses: {
178
+ 200: { description: '${e} deleted successfully' },
179
+ 401: { description: 'unauthorized' },
180
+ },
181
+ },
182
+ },
183
+ };
184
+ `;var O=e=>`import { z } from 'zod';
185
+
186
+ const create_${e} = z.object({});
187
+ const update_${e} = z.object({});
188
+
189
+ export const ${e}_validations = {
190
+ create_${e},
191
+ update_${e},
192
+ };
193
+ `;var N=e=>`// add the fields of a ${e} here
194
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
195
+ export type T_${e} = {};
196
+ `;var M=e=>`import { Schema, model } from 'mongoose';
197
+ import type { T_${e} } from './${e}.interface.js';
198
+
199
+ const schema = new Schema<T_${e}>({}, { timestamps: true, versionKey: false });
200
+
201
+ export const ${e}_model = model('${e}', schema);
202
+ `;var L=e=>`import type { Request } from 'express';
203
+ import { AppError } from '../../utils/app_error.js';
204
+ import { ${e}_model } from './${e}.schema.js';
205
+
206
+ const notFound = () => new AppError('${e} not found', 404);
207
+
208
+ const get_all_${e}_from_db = async (_req: Request) => {
209
+ // define your own logic here
210
+ const result = await ${e}_model.find().lean();
211
+ return result;
212
+ };
213
+
214
+ const get_single_${e}_from_db = async (req: Request) => {
215
+ // define your own logic here
216
+ const { id } = req.params;
217
+ const result = await ${e}_model.findById(id).lean();
218
+ if (!result) throw notFound();
219
+ return result;
220
+ };
221
+
222
+ const create_${e}_into_db = async (req: Request) => {
223
+ // define your own logic here
224
+ const result = await ${e}_model.create(req.body);
225
+ return result;
226
+ };
227
+
228
+ const update_${e}_into_db = async (req: Request) => {
229
+ // define your own logic here
230
+ const { id } = req.params;
231
+ const result = await ${e}_model
232
+ .findByIdAndUpdate(id, req.body, { returnDocument: 'after' })
233
+ .lean();
234
+ if (!result) throw notFound();
235
+ return result;
236
+ };
237
+
238
+ const delete_${e}_from_db = async (req: Request) => {
239
+ // define your own logic here
240
+ const { id } = req.params;
241
+ const result = await ${e}_model.findByIdAndDelete(id).lean();
242
+ if (!result) throw notFound();
243
+ return result;
244
+ };
245
+
246
+ export const ${e}_service = {
247
+ get_all_${e}_from_db,
248
+ get_single_${e}_from_db,
249
+ create_${e}_into_db,
250
+ update_${e}_into_db,
251
+ delete_${e}_from_db,
252
+ };
253
+ `;var U=e=>`import { Types } from 'mongoose';
254
+ import request from 'supertest';
255
+ import { describe, expect, it } from 'vitest';
256
+ import app from '../../src/app.js';
257
+ import { ${e}_model } from '../../src/app/modules/${e}/${e}.schema.js';
258
+
259
+ const BASE = '/api/${e}';
260
+
261
+ // Valid request bodies. Fill them in as you add fields to ${e}.schema.ts and
262
+ // ${e}.validation.ts (and protect the routes with auth() once they need it).
263
+ const createPayload = {};
264
+ const updatePayload = {};
265
+
266
+ /** Inserts a ${e} directly in the database, for the read / update / delete tests. */
267
+ const insert = () => ${e}_model.create(createPayload);
268
+
269
+ describe(BASE, () => {
270
+ it('POST / creates one', async () => {
271
+ const res = await request(app).post(BASE).send(createPayload);
272
+
273
+ expect(res.status).toBe(201);
274
+ expect(await ${e}_model.countDocuments()).toBe(1);
275
+ });
276
+
277
+ it('GET / lists them', async () => {
278
+ await insert();
279
+
280
+ const res = await request(app).get(BASE);
281
+
282
+ expect(res.status).toBe(200);
283
+ expect(res.body.data).toHaveLength(1);
284
+ });
285
+
286
+ it('GET /:id returns one', async () => {
287
+ const doc = await insert();
288
+
289
+ const res = await request(app).get(\`\${BASE}/\${doc._id}\`);
290
+
291
+ expect(res.status).toBe(200);
292
+ expect(res.body.data._id).toBe(String(doc._id));
293
+ });
294
+
295
+ it('GET /:id is 404 when it does not exist', async () => {
296
+ const res = await request(app).get(\`\${BASE}/\${new Types.ObjectId()}\`);
297
+
298
+ expect(res.status).toBe(404);
299
+ });
300
+
301
+ it('GET /:id is 400 for a malformed id', async () => {
302
+ const res = await request(app).get(\`\${BASE}/not-an-id\`);
303
+
304
+ expect(res.status).toBe(400);
305
+ });
306
+
307
+ it('PATCH /:id updates it', async () => {
308
+ const doc = await insert();
309
+
310
+ const res = await request(app).patch(\`\${BASE}/\${doc._id}\`).send(updatePayload);
311
+
312
+ expect(res.status).toBe(200);
313
+ const stored = await ${e}_model.findById(doc._id).lean();
314
+ expect(stored).toMatchObject(updatePayload);
315
+ });
316
+
317
+ it('PATCH /:id is 404 when it does not exist', async () => {
318
+ const res = await request(app).patch(\`\${BASE}/\${new Types.ObjectId()}\`).send(updatePayload);
319
+
320
+ expect(res.status).toBe(404);
321
+ });
322
+
323
+ it('DELETE /:id deletes it', async () => {
324
+ const doc = await insert();
325
+
326
+ const res = await request(app).delete(\`\${BASE}/\${doc._id}\`);
327
+
328
+ expect(res.status).toBe(200);
329
+ const stillThere = await ${e}_model.exists({ _id: doc._id });
330
+ expect(stillThere).toBeNull();
331
+ });
332
+
333
+ it('DELETE /:id is 404 when it does not exist', async () => {
334
+ const res = await request(app).delete(\`\${BASE}/\${new Types.ObjectId()}\`);
335
+
336
+ expect(res.status).toBe(404);
337
+ });
338
+ });
339
+ `;var V=e=>e.charAt(0).toUpperCase()+e.slice(1);var H=e=>`model ${V(e)} {
340
+ id String @id @default(uuid())
341
+ createdAt DateTime @default(now())
342
+ updatedAt DateTime @updatedAt
343
+ }
344
+ `;var z=e=>`import type { Request } from 'express';
345
+ import { prisma } from '../../lib/prisma.js';
346
+ import { AppError } from '../../utils/app_error.js';
347
+
348
+ const get_all_${e}_from_db = async (_req: Request) => {
349
+ // define your own logic here
350
+ const result = await prisma.${e}.findMany();
351
+ return result;
352
+ };
353
+
354
+ const get_single_${e}_from_db = async (req: Request) => {
355
+ // define your own logic here
356
+ const { id } = req.params as { id: string };
357
+ const result = await prisma.${e}.findUnique({ where: { id } });
358
+ if (!result) throw new AppError('${e} not found', 404);
359
+ return result;
360
+ };
361
+
362
+ const create_${e}_into_db = async (req: Request) => {
363
+ // define your own logic here
364
+ const result = await prisma.${e}.create({ data: req.body });
365
+ return result;
366
+ };
367
+
368
+ // update / delete of a missing record throw Prisma's P2025, which the global
369
+ // error handler turns into a 404
370
+ const update_${e}_into_db = async (req: Request) => {
371
+ // define your own logic here
372
+ const { id } = req.params as { id: string };
373
+ const result = await prisma.${e}.update({ where: { id }, data: req.body });
374
+ return result;
375
+ };
376
+
377
+ const delete_${e}_from_db = async (req: Request) => {
378
+ // define your own logic here
379
+ const { id } = req.params as { id: string };
380
+ const result = await prisma.${e}.delete({ where: { id } });
381
+ return result;
382
+ };
383
+
384
+ export const ${e}_service = {
385
+ get_all_${e}_from_db,
386
+ get_single_${e}_from_db,
387
+ create_${e}_into_db,
388
+ update_${e}_into_db,
389
+ delete_${e}_from_db,
390
+ };
391
+ `;var J=e=>`import { Prisma } from '@prisma/client';
392
+ import request from 'supertest';
393
+ import { describe, expect, it } from 'vitest';
394
+ import app from '../../src/app.js';
395
+ import { prismaMock } from '../helpers/prisma.js';
396
+
397
+ const BASE = '/api/${e}';
398
+ const db = prismaMock.${e};
399
+
400
+ // Valid request bodies. Fill them in as you add fields to prisma/schema/${e}.prisma
401
+ // and ${e}.validation.ts (and protect the routes with auth() once they need it).
402
+ const createPayload = {};
403
+ const updatePayload = {};
404
+
405
+ // a row as the database would return it
406
+ const row = {
407
+ id: '9b2f3c1e-7a41-4c8e-9d6b-2f4e8a1c5b70',
408
+ ...createPayload,
409
+ createdAt: new Date('2025-01-01'),
410
+ updatedAt: new Date('2025-01-01'),
411
+ };
412
+ // ...and as it looks in a JSON response
413
+ const json = JSON.parse(JSON.stringify(row));
414
+
415
+ const recordNotFound = () =>
416
+ new Prisma.PrismaClientKnownRequestError('Record not found', {
417
+ code: 'P2025',
418
+ clientVersion: 'test',
419
+ });
420
+
421
+ describe(BASE, () => {
422
+ it('POST / creates one', async () => {
423
+ db.create.mockResolvedValue(row);
424
+
425
+ const res = await request(app).post(BASE).send(createPayload);
426
+
427
+ expect(res.status).toBe(201);
428
+ expect(res.body.data).toEqual(json);
429
+ expect(db.create).toHaveBeenCalledWith({ data: createPayload });
430
+ });
431
+
432
+ it('GET / lists them', async () => {
433
+ db.findMany.mockResolvedValue([row]);
434
+
435
+ const res = await request(app).get(BASE);
436
+
437
+ expect(res.status).toBe(200);
438
+ expect(res.body.data).toEqual([json]);
439
+ });
440
+
441
+ it('GET /:id returns one', async () => {
442
+ db.findUnique.mockResolvedValue(row);
443
+
444
+ const res = await request(app).get(\`\${BASE}/\${row.id}\`);
445
+
446
+ expect(res.status).toBe(200);
447
+ expect(res.body.data).toEqual(json);
448
+ expect(db.findUnique).toHaveBeenCalledWith({ where: { id: row.id } });
449
+ });
450
+
451
+ it('GET /:id is 404 when it does not exist', async () => {
452
+ db.findUnique.mockResolvedValue(null);
453
+
454
+ const res = await request(app).get(\`\${BASE}/\${row.id}\`);
455
+
456
+ expect(res.status).toBe(404);
457
+ });
458
+
459
+ it('PATCH /:id updates it', async () => {
460
+ db.update.mockResolvedValue(row);
461
+
462
+ const res = await request(app).patch(\`\${BASE}/\${row.id}\`).send(updatePayload);
463
+
464
+ expect(res.status).toBe(200);
465
+ expect(db.update).toHaveBeenCalledWith({ where: { id: row.id }, data: updatePayload });
466
+ });
467
+
468
+ it('PATCH /:id is 404 when it does not exist', async () => {
469
+ db.update.mockRejectedValue(recordNotFound());
470
+
471
+ const res = await request(app).patch(\`\${BASE}/\${row.id}\`).send(updatePayload);
472
+
473
+ expect(res.status).toBe(404);
474
+ });
475
+
476
+ it('DELETE /:id deletes it', async () => {
477
+ db.delete.mockResolvedValue(row);
478
+
479
+ const res = await request(app).delete(\`\${BASE}/\${row.id}\`);
480
+
481
+ expect(res.status).toBe(200);
482
+ expect(db.delete).toHaveBeenCalledWith({ where: { id: row.id } });
483
+ });
484
+
485
+ it('DELETE /:id is 404 when it does not exist', async () => {
486
+ db.delete.mockRejectedValue(recordNotFound());
487
+
488
+ const res = await request(app).delete(\`\${BASE}/\${row.id}\`);
489
+
490
+ expect(res.status).toBe(404);
491
+ });
492
+ });
493
+ `;var ge=/^import\s[\s\S]*?from\s*(['"])[^'"]+\1;?[ \t]*$|^import\s*(['"])[^'"]+\2;?[ \t]*$/gm;function E(e){return{quote:e.match(/^import[\s\S]*?(['"])/m)?.[1]==="'"?"'":'"',jsExtension:/from\s*['"]\.{1,2}\/[^'"]+\.js['"]/.test(e)}}function q(e,t){let r=-1;for(let s of e.matchAll(ge))r=s.index+s[0].length;return r===-1?`${t}
494
+ ${e}`:`${e.slice(0,r)}
495
+ ${t}${e.slice(r)}`}function v(e,t,r,s){let n=e.match(t);if(n?.index===void 0)return null;let i=n.index+n[0].length,o=e.indexOf(r,i);if(o===-1)return null;let d=e.slice(i,o),f=d.match(/\n([ \t]*)$/)?.[1]??"",p=d.match(/\n([ \t]+)\S/)?.[1]??`${f} `,w=d.trimEnd(),ie=w.length>0&&!/[,[{]$/.test(w),ae=`${w}${ie?",":""}
496
+ ${p}${s}
497
+ ${f}`;return e.slice(0,i)+ae+e.slice(o)}function R(e,t){let r=e.includes(`\r
498
+ `),s=t(r?e.replace(/\r\n/g,`
499
+ `):e);return!r||s.status!=="updated"?s:{status:"updated",content:s.content.replace(/\n/g,`\r
500
+ `)}}function W(e,t){return R(e,r=>$e(r,t))}function $e(e,t){let{quote:r,jsExtension:s}=E(e),n=`./app/modules/${t}/${t}.route`,i=`${t}Route`,o=e;if(o.includes(n)||(o=q(o,`import ${i} from ${r}${n}${s?".js":""}${r};`)),!new RegExp(`route:\\s*${i}\\b`).test(o)){let d=v(o,/const moduleRoutes\s*=\s*\[/,"]",`{ path: ${r}/${t}${r}, route: ${i} },`);if(d===null)return{status:"failed",reason:"could not find `const moduleRoutes = [...]`"};o=d}return o===e?{status:"unchanged"}:{status:"updated",content:o}}function K(e,t){return R(e,r=>he(r,t))}function he(e,t){let{quote:r,jsExtension:s}=E(e),n=`./app/modules/${t}/${t}.swagger`,i=`${t}SwaggerDocs`,o=e;if(o.includes(n)||(o=q(o,`import { ${i} } from ${r}${n}${s?".js":""}${r};`)),!o.includes(`...${i}`)){let d=v(o,/paths:\s*\{/,"}",`...${i},`);if(d===null)return{status:"failed",reason:"could not find `paths: { ... }`"};o=d}return o===e?{status:"unchanged"}:{status:"updated",content:o}}import Q from"fs";import X from"path";function Y(e,t){for(let r of e)Q.mkdirSync(X.dirname(r.path),{recursive:!0}),Q.writeFileSync(r.path,r.content),a.success(`${X.relative(t,r.path)} created`)}import Z from"fs";import ye from"path";var b=["mongoose","prisma"],P=".projectrc.json",I=e=>typeof e=="string"&&b.includes(e);function ee(e){let t=ye.join(e,P);if(!Z.existsSync(t))throw new c(`${P} not found. Run this command from the root of a project created with exp-node-server.`);let r;try{r=JSON.parse(Z.readFileSync(t,"utf-8"))}catch{throw new c(`${P} is not valid JSON.`)}let s=r?.database;if(!I(s))throw new c(`Invalid "database" in ${P}. Expected one of: ${b.join(", ")}.`);return s}async function re(e,{cwd:t=process.cwd(),force:r=!1}={}){let s=ee(t),n=l.join(t,"src/app/modules",e);if($.existsSync(n)&&!r)throw new c(`Module '${e}' already exists at ${l.relative(t,n)}. Use --force to overwrite it.`);_e(l.join(t,"src/routes.ts"),e);let i=(p,w)=>({path:l.join(n,`${e}.${p}`),content:w}),o=[i("controller.ts",D(e)),i("route.ts",G(e)),i("validation.ts",O(e)),i("swagger.ts",F(e))];s==="mongoose"?o.push(i("interface.ts",N(e)),i("schema.ts",M(e)),i("service.ts",L(e))):o.push(i("service.ts",z(e)),{path:l.join(t,"prisma/schema",`${e}.prisma`),content:H(e)});let d=l.join(t,"tests/routes",`${e}.test.ts`),f=we.some(p=>$.existsSync(l.join(t,p)));f&&o.push({path:d,content:s==="mongoose"?U(e):J(e)}),Y(o,t),f||a.info(y.dim(" no vitest config found, skipped tests/routes test")),te(l.join(t,"src/routes.ts"),p=>W(p,e),"Route registered"),te(l.join(t,"src/swaggerOptions.ts"),p=>K(p,e),"Swagger docs registered"),s==="prisma"&&(a.info(y.blue(`
501
+ Next, run:`)),a.info(y.yellow(" npx prisma generate && npx prisma migrate dev"))),f&&a.info(y.blue(`
502
+ Test it with: npm test -- tests/routes/${e}.test.ts`)),a.info(y.green(`
503
+ Module '${e}' created successfully`))}var we=["vitest.config.ts","vitest.config.mts","vitest.config.js"];function _e(e,t){if(!$.existsSync(e))return;let r=$.readFileSync(e,"utf-8"),s=new RegExp(`path:\\s*['"]/${t}['"]\\s*,\\s*route:\\s*(\\w+)`).exec(r);if(s&&s[1]!==`${t}Route`)throw new c(`The path '/${t}' is already used by ${s[1]} in src/routes.ts. Pick another module name.`)}function te(e,t,r){let s=l.basename(e);if(!$.existsSync(e)){a.warn(`${s} not found, skipping registration`);return}let n=t($.readFileSync(e,"utf-8"));switch(n.status){case"updated":$.writeFileSync(e,n.content),a.success(`${r} in ${s}`);break;case"unchanged":a.info(y.dim(` already registered in ${s}`));break;case"failed":a.warn(`Couldn't update ${s} (${n.reason}). Register it manually.`);break}}function se(e){if(!e)return null;let t=e.trim().toLowerCase().replace(/[-\s]+/g,"_");return/^[a-z][a-z_]*$/.test(t)?t:null}function oe(e){if(!e)return null;let t=e.trim().toLowerCase().replace(/\s+/g,"-");return/^[a-z0-9][a-z0-9-]*$/.test(t)?t:null}var u="exp-node-server",be=`
504
+ ${S.bold(u)} - scaffold Express + TypeScript APIs with Mongoose or Prisma
505
+
506
+ ${S.bold("Usage")}
507
+ npx ${u} create <project-name> [--template <mongoose|prisma>]
508
+ npx ${u} generate <module-name> [--force]
509
+
510
+ ${S.bold("Commands")}
511
+ create, -c <name> Create a new project from a template
512
+ generate, -g <name> Generate a module in the current project
513
+
514
+ ${S.bold("Options")}
515
+ -t, --template <name> Template to use with create (${b.join(" | ")})
516
+ -f, --force Overwrite an existing module when generating
517
+ -h, --help Show this help
518
+ -v, --version Show version
519
+
520
+ ${S.bold("Examples")}
521
+ npx ${u} -c my-api
522
+ npx ${u} create my-api --template prisma
523
+ npx ${u} -g order
524
+ `;async function ne(e){let{values:t,positionals:r}=Se(e);if(t.version){console.log("2.0.0");return}let s=je(t,r);if(t.help||!s){console.log(be);return}if(s.kind==="create"){let i=oe(s.name);if(!i)throw new c(s.name?`Invalid project name '${s.name}'. Use letters, numbers and dashes.`:"Project name required. Example: exp-node-server -c my-api");let{template:o}=t;if(o!==void 0&&!I(o))throw new c(`Unknown template '${o}'. Use one of: ${b.join(", ")}.`);await k(i,{template:o});return}let n=se(s.name);if(!n)throw new c(s.name?`Invalid module name '${s.name}'. Use letters and underscores, starting with a letter.`:"Module name required. Example: exp-node-server -g order");await re(n,{force:t.force??!1})}function Se(e){try{return xe({args:e,allowPositionals:!0,strict:!0,options:{create:{type:"string",short:"c"},generate:{type:"string",short:"g"},template:{type:"string",short:"t"},force:{type:"boolean",short:"f"},help:{type:"boolean",short:"h"},version:{type:"boolean",short:"v"}}})}catch(t){let r=t instanceof Error?t.message:String(t);throw new c(`${r}
525
+ Run '${u} --help' for usage.`)}}function je(e,t){if(e.create!==void 0&&e.generate!==void 0)throw new c("Use either -c (create) or -g (generate), not both.");if((e.create!==void 0||e.generate!==void 0)&&t.length>0)throw new c(`Unexpected argument '${t[0]}'. Run '${u} --help' for usage.`);if(e.create!==void 0)return{kind:"create",name:e.create};if(e.generate!==void 0)return{kind:"generate",name:e.generate};let[r,s,...n]=t;if(r===void 0)return null;if(n.length>0)throw new c(`Unexpected argument '${n[0]}'. Run '${u} --help' for usage.`);switch(r){case"create":case"c":return{kind:"create",name:s};case"generate":case"g":return{kind:"generate",name:s};default:throw new c(`Unknown command '${r}'. Run '${u} --help' for usage.`)}}try{await ne(process.argv.slice(2))}catch(e){e instanceof c?(a.error(e.message),process.exitCode=e.exitCode):e instanceof Error&&e.name==="ExitPromptError"?process.exitCode=130:(a.error("Unexpected error:"),console.error(e),process.exitCode=1)}