create-nexgen 1.0.4

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 (240) hide show
  1. package/package.json +26 -0
  2. package/src/index.js +108 -0
  3. package/template/.dockerignore +14 -0
  4. package/template/.env +58 -0
  5. package/template/.env.example +59 -0
  6. package/template/.prettierignore +5 -0
  7. package/template/.prettierrc +8 -0
  8. package/template/README.md +447 -0
  9. package/template/drizzle.config.ts +29 -0
  10. package/template/eslint.config.js +52 -0
  11. package/template/gitignore-stub +24 -0
  12. package/template/package.json +96 -0
  13. package/template/public/assets/AuthLayout-CbswhpjJ.js +1 -0
  14. package/template/public/assets/Button-_7aQ7gHL.js +1 -0
  15. package/template/public/assets/Input-CLNJXmKc.css +1 -0
  16. package/template/public/assets/Input-z8GI8Aqo.js +1 -0
  17. package/template/public/assets/InputPasswordToggle-BxlzVGp3.js +1 -0
  18. package/template/public/assets/InputPasswordToggle-C77FI9Eg.css +1 -0
  19. package/template/public/assets/Layout-DotR1sQC.js +1 -0
  20. package/template/public/assets/Refresh-BdqsPPBC.js +1 -0
  21. package/template/public/assets/admin-ui-CU34rLdN.js +1 -0
  22. package/template/public/assets/bootstrap-icons-BeopsB42.woff +0 -0
  23. package/template/public/assets/bootstrap-icons-mSm7cUeB.woff2 +0 -0
  24. package/template/public/assets/dashboard-CwybEyLc.js +1 -0
  25. package/template/public/assets/dashboard-Dc4d-Pi7.css +1 -0
  26. package/template/public/assets/forgetPassword-CKEJaXsq.js +1 -0
  27. package/template/public/assets/index-Bleyx5dm.js +64 -0
  28. package/template/public/assets/index-DUw8E6Yg.css +1 -0
  29. package/template/public/assets/login-DC7PTlQF.js +1 -0
  30. package/template/public/assets/realtime-test-BPQdrFym.css +1 -0
  31. package/template/public/assets/realtime-test-tQZ0rBEJ.js +1 -0
  32. package/template/public/assets/register-3O7Qs28C.js +1 -0
  33. package/template/public/assets/resetPassword-A5AzMWKs.js +1 -0
  34. package/template/public/assets/verifyEmail-DDBEQHOv.js +1 -0
  35. package/template/public/index.html +17 -0
  36. package/template/src/database/migrations/mysql/0000_init.sql +73 -0
  37. package/template/src/database/migrations/mysql/meta/0000_snapshot.json +484 -0
  38. package/template/src/database/migrations/mysql/meta/_journal.json +13 -0
  39. package/template/src/database/schema.ts +4 -0
  40. package/template/src/env.ts +107 -0
  41. package/template/src/framework/cache/cache.ts +81 -0
  42. package/template/src/framework/database/connection.ts +168 -0
  43. package/template/src/framework/database/optional-db-drivers.d.ts +9 -0
  44. package/template/src/framework/database/paginate.ts +200 -0
  45. package/template/src/framework/database/schema.ts +26 -0
  46. package/template/src/framework/database/seed.ts +33 -0
  47. package/template/src/framework/events/dispatcher.ts +57 -0
  48. package/template/src/framework/facade.ts +27 -0
  49. package/template/src/framework/http/app.ts +61 -0
  50. package/template/src/framework/http/cors.ts +19 -0
  51. package/template/src/framework/http/logger.ts +85 -0
  52. package/template/src/framework/http/openapi.ts +34 -0
  53. package/template/src/framework/http/ratelimiter.ts +13 -0
  54. package/template/src/framework/http/router.ts +76 -0
  55. package/template/src/framework/http/static.ts +33 -0
  56. package/template/src/framework/http/validation.ts +24 -0
  57. package/template/src/framework/kernel.ts +40 -0
  58. package/template/src/framework/maker-cli/src/index.mjs +51 -0
  59. package/template/src/framework/maker-cli/src/levels/level-1/env-db.mjs +57 -0
  60. package/template/src/framework/maker-cli/src/levels/level-1/file-ops.mjs +30 -0
  61. package/template/src/framework/maker-cli/src/levels/level-1/flags.mjs +16 -0
  62. package/template/src/framework/maker-cli/src/levels/level-1/help.mjs +24 -0
  63. package/template/src/framework/maker-cli/src/levels/level-1/naming.mjs +13 -0
  64. package/template/src/framework/maker-cli/src/levels/level-1/process.mjs +47 -0
  65. package/template/src/framework/maker-cli/src/levels/level-2/db/core.mjs +299 -0
  66. package/template/src/framework/maker-cli/src/levels/level-2/db/index.mjs +177 -0
  67. package/template/src/framework/maker-cli/src/levels/level-2/deploy/core.mjs +635 -0
  68. package/template/src/framework/maker-cli/src/levels/level-2/deploy/index.mjs +145 -0
  69. package/template/src/framework/maker-cli/src/levels/level-2/module/core.mjs +707 -0
  70. package/template/src/framework/maker-cli/src/levels/level-2/module/index.mjs +116 -0
  71. package/template/src/framework/maker-cli/src/levels/level-2/runtime/build-frontend.mjs +16 -0
  72. package/template/src/framework/maker-cli/src/levels/level-2/runtime/core.mjs +311 -0
  73. package/template/src/framework/maker-cli/src/levels/level-2/runtime/index.mjs +71 -0
  74. package/template/src/framework/maker-cli/stubs/controller/openapi.ts.stub +55 -0
  75. package/template/src/framework/maker-cli/stubs/controller/openapi.with-model.ts.stub +56 -0
  76. package/template/src/framework/maker-cli/stubs/controller/plain.ts.stub +57 -0
  77. package/template/src/framework/maker-cli/stubs/controller/schema.plain.ts.stub +13 -0
  78. package/template/src/framework/maker-cli/stubs/controller/schema.ts.stub +32 -0
  79. package/template/src/framework/maker-cli/stubs/deploy/Dockerfile.bun.stub +49 -0
  80. package/template/src/framework/maker-cli/stubs/deploy/Dockerfile.pnpm.stub +53 -0
  81. package/template/src/framework/maker-cli/stubs/deploy/Dockerfile.stub +49 -0
  82. package/template/src/framework/maker-cli/stubs/deploy/Dockerfile.yarn.stub +53 -0
  83. package/template/src/framework/maker-cli/stubs/deploy/README.stub +55 -0
  84. package/template/src/framework/maker-cli/stubs/deploy/compose/mysql.server.stub +29 -0
  85. package/template/src/framework/maker-cli/stubs/deploy/compose/postgres.server.stub +29 -0
  86. package/template/src/framework/maker-cli/stubs/deploy/compose/sqlite.stub +29 -0
  87. package/template/src/framework/maker-cli/stubs/deploy/env/mysql.server.stub +73 -0
  88. package/template/src/framework/maker-cli/stubs/deploy/env/postgres.server.stub +73 -0
  89. package/template/src/framework/maker-cli/stubs/deploy/env/sqlite.stub +72 -0
  90. package/template/src/framework/maker-cli/stubs/deploy/scripts/auto-migrate.sh.stub +15 -0
  91. package/template/src/framework/maker-cli/stubs/deploy/server/README.stub +77 -0
  92. package/template/src/framework/maker-cli/stubs/deploy/server/compose/noredis.stub +118 -0
  93. package/template/src/framework/maker-cli/stubs/deploy/server/compose/redis.dev.stub +131 -0
  94. package/template/src/framework/maker-cli/stubs/deploy/server/compose/redis.stub +129 -0
  95. package/template/src/framework/maker-cli/stubs/deploy/server/env/local.example.stub +10 -0
  96. package/template/src/framework/maker-cli/stubs/deploy/server/env/noredis.stub +24 -0
  97. package/template/src/framework/maker-cli/stubs/deploy/server/env/redis.stub +24 -0
  98. package/template/src/framework/maker-cli/stubs/deploy/server/nginx-vhost/README.stub +15 -0
  99. package/template/src/framework/maker-cli/stubs/deploy/server/nginx-vhost/app.example.com.stub +12 -0
  100. package/template/src/framework/maker-cli/stubs/deploy/server/pgadmin/servers.stub +13 -0
  101. package/template/src/framework/maker-cli/stubs/deploy/server/redis/redis.conf.stub +6 -0
  102. package/template/src/framework/maker-cli/stubs/deploy/supervisor/noredis.stub +53 -0
  103. package/template/src/framework/maker-cli/stubs/deploy/supervisor/redis.stub +69 -0
  104. package/template/src/framework/maker-cli/stubs/deploy/workflow/local.json.stub +24 -0
  105. package/template/src/framework/maker-cli/stubs/deploy/workflow/remote.json.stub +20 -0
  106. package/template/src/framework/maker-cli/stubs/example/console.ts.stub +33 -0
  107. package/template/src/framework/maker-cli/stubs/example/controller.ts.stub +503 -0
  108. package/template/src/framework/maker-cli/stubs/example/job.ts.stub +74 -0
  109. package/template/src/framework/maker-cli/stubs/example/route.api.ts.stub +206 -0
  110. package/template/src/framework/maker-cli/stubs/example/schema.ts.stub +41 -0
  111. package/template/src/framework/maker-cli/stubs/job/name.ts.stub +24 -0
  112. package/template/src/framework/maker-cli/stubs/model/name.mysql.ts.stub +8 -0
  113. package/template/src/framework/maker-cli/stubs/model/name.postgresql.ts.stub +8 -0
  114. package/template/src/framework/maker-cli/stubs/model/name.sqlite.ts.stub +8 -0
  115. package/template/src/framework/maker-cli/stubs/notification/NotificationBell.vue.stub +218 -0
  116. package/template/src/framework/maker-cli/stubs/notification/controller.ts.stub +85 -0
  117. package/template/src/framework/maker-cli/stubs/notification/index.vue.stub +211 -0
  118. package/template/src/framework/maker-cli/stubs/notification/job.ts.stub +12 -0
  119. package/template/src/framework/maker-cli/stubs/notification/route.api.ts.stub +49 -0
  120. package/template/src/framework/maker-cli/stubs/notification/schema.ts.stub +25 -0
  121. package/template/src/framework/maker-cli/stubs/route/api.ts.stub +79 -0
  122. package/template/src/framework/maker-cli/stubs/route/plain.ts.stub +10 -0
  123. package/template/src/framework/maker-cli/stubs/schedule/name.ts.stub +35 -0
  124. package/template/src/framework/maker-cli/stubs/seeder/name.ts.stub +17 -0
  125. package/template/src/framework/modules/discover.ts +54 -0
  126. package/template/src/framework/modules/routes.ts +26 -0
  127. package/template/src/framework/notification/index.ts +109 -0
  128. package/template/src/framework/queue/clear.ts +20 -0
  129. package/template/src/framework/queue/queue.ts +213 -0
  130. package/template/src/framework/queue/ui.ts +104 -0
  131. package/template/src/framework/queue/worker.ts +33 -0
  132. package/template/src/framework/realtime/broadcast.ts +27 -0
  133. package/template/src/framework/realtime/index.ts +1 -0
  134. package/template/src/framework/realtime/socket-cookie.ts +65 -0
  135. package/template/src/framework/realtime/socket.ts +132 -0
  136. package/template/src/framework/realtime/types.ts +6 -0
  137. package/template/src/framework/realtime/ui.ts +16 -0
  138. package/template/src/framework/redis/client.ts +126 -0
  139. package/template/src/framework/scheduler/lock.ts +124 -0
  140. package/template/src/framework/scheduler/run.ts +26 -0
  141. package/template/src/framework/scheduler/scheduler.ts +82 -0
  142. package/template/src/framework/server.ts +147 -0
  143. package/template/src/framework/session/session.ts +116 -0
  144. package/template/src/framework/storage/storage.ts +743 -0
  145. package/template/src/framework/support/cookie.ts +78 -0
  146. package/template/src/framework/support/jwt.ts +45 -0
  147. package/template/src/framework/support/lifecycle.ts +35 -0
  148. package/template/src/framework/support/logger.ts +102 -0
  149. package/template/src/framework/support/mail.ts +43 -0
  150. package/template/src/framework/support/password.ts +23 -0
  151. package/template/src/framework/support/url.ts +25 -0
  152. package/template/src/middlewares/auth-middleware.ts +98 -0
  153. package/template/src/middlewares/role-middleware.ts +24 -0
  154. package/template/src/modules/auth/controllers/auth.controller.ts +445 -0
  155. package/template/src/modules/auth/controllers/auth.helpers.ts +110 -0
  156. package/template/src/modules/auth/controllers/auth.schema.ts +102 -0
  157. package/template/src/modules/auth/controllers/role.controller.ts +25 -0
  158. package/template/src/modules/auth/database/models/notifications.ts +22 -0
  159. package/template/src/modules/auth/database/models/role.ts +14 -0
  160. package/template/src/modules/auth/database/models/user.ts +46 -0
  161. package/template/src/modules/auth/database/seeders/role.ts +19 -0
  162. package/template/src/modules/auth/database/seeders/user.ts +33 -0
  163. package/template/src/modules/auth/jobs/forgetpass.ts +18 -0
  164. package/template/src/modules/auth/jobs/registeruser.ts +31 -0
  165. package/template/src/modules/auth/jobs/verifyemail.ts +18 -0
  166. package/template/src/modules/auth/routes/api.ts +151 -0
  167. package/template/src/modules/auth/routes/role.ts +39 -0
  168. package/template/src/modules/welcome/controllers/welcome.controller.ts +14 -0
  169. package/template/src/modules/welcome/controllers/welcome.schema.ts +6 -0
  170. package/template/src/modules/welcome/database/models/welcome.ts +6 -0
  171. package/template/src/modules/welcome/routes/api.ts +20 -0
  172. package/template/src/resources/index.html +16 -0
  173. package/template/src/resources/src/App.vue +5 -0
  174. package/template/src/resources/src/assets/css/styles.css +14934 -0
  175. package/template/src/resources/src/assets/css/styles.css.map +1 -0
  176. package/template/src/resources/src/assets/images/favicon/favicon.ico +0 -0
  177. package/template/src/resources/src/assets/images/favicon/favicon1.ico +0 -0
  178. package/template/src/resources/src/assets/images/logo-1.png +0 -0
  179. package/template/src/resources/src/assets/images/logo-dark-sm.png +0 -0
  180. package/template/src/resources/src/assets/images/logo-dark.png +0 -0
  181. package/template/src/resources/src/assets/images/logo-dark1.png +0 -0
  182. package/template/src/resources/src/assets/images/logo-sm.png +0 -0
  183. package/template/src/resources/src/assets/images/logo1.png +0 -0
  184. package/template/src/resources/src/assets/images/logo2.png +0 -0
  185. package/template/src/resources/src/assets/scss/custom.css +217 -0
  186. package/template/src/resources/src/assets/scss/custom.css.map +1 -0
  187. package/template/src/resources/src/assets/scss/custom.scss +1100 -0
  188. package/template/src/resources/src/components/Button.vue +35 -0
  189. package/template/src/resources/src/components/Checkbox.vue +29 -0
  190. package/template/src/resources/src/components/FloatButton.vue +36 -0
  191. package/template/src/resources/src/components/Href.vue +32 -0
  192. package/template/src/resources/src/components/Input.vue +227 -0
  193. package/template/src/resources/src/components/InputGroup.vue +153 -0
  194. package/template/src/resources/src/components/InputPasswordToggle.vue +226 -0
  195. package/template/src/resources/src/components/Modal.vue +102 -0
  196. package/template/src/resources/src/components/Pagebar.vue +28 -0
  197. package/template/src/resources/src/components/Refresh.vue +26 -0
  198. package/template/src/resources/src/components/Select.vue +390 -0
  199. package/template/src/resources/src/components/Spinner.vue +42 -0
  200. package/template/src/resources/src/components/Switch.vue +65 -0
  201. package/template/src/resources/src/components/TextArea.vue +121 -0
  202. package/template/src/resources/src/components/Toast.vue +56 -0
  203. package/template/src/resources/src/components/datatable/DataTableSkeleton.vue +99 -0
  204. package/template/src/resources/src/components/datatable/Pagination.vue +161 -0
  205. package/template/src/resources/src/components/datatable/SelectOpption.vue +54 -0
  206. package/template/src/resources/src/components/datatable/index.vue +237 -0
  207. package/template/src/resources/src/composables/useAuth.ts +52 -0
  208. package/template/src/resources/src/composables/useBrowserDetect.ts +5 -0
  209. package/template/src/resources/src/composables/useDialog.ts +5 -0
  210. package/template/src/resources/src/composables/useGum.ts +3 -0
  211. package/template/src/resources/src/composables/usePulse.ts +5 -0
  212. package/template/src/resources/src/env.d.ts +20 -0
  213. package/template/src/resources/src/helpers/nformatter.ts +10 -0
  214. package/template/src/resources/src/helpers/utils.ts +68 -0
  215. package/template/src/resources/src/layouts/AuthLayout.vue +20 -0
  216. package/template/src/resources/src/layouts/Layout/Footer.vue +23 -0
  217. package/template/src/resources/src/layouts/Layout/Header.vue +90 -0
  218. package/template/src/resources/src/layouts/Layout/Sidebar.vue +137 -0
  219. package/template/src/resources/src/layouts/Layout/index.vue +76 -0
  220. package/template/src/resources/src/main.ts +27 -0
  221. package/template/src/resources/src/pages/auth/forgetPassword.vue +76 -0
  222. package/template/src/resources/src/pages/auth/login.vue +93 -0
  223. package/template/src/resources/src/pages/auth/register.vue +130 -0
  224. package/template/src/resources/src/pages/auth/resetPassword.vue +119 -0
  225. package/template/src/resources/src/pages/auth/verifyEmail.vue +60 -0
  226. package/template/src/resources/src/pages/dashboard/index.vue +76 -0
  227. package/template/src/resources/src/plugins/axios.ts +33 -0
  228. package/template/src/resources/src/plugins/browserDetect.ts +55 -0
  229. package/template/src/resources/src/plugins/dialog.ts +167 -0
  230. package/template/src/resources/src/plugins/gum.ts +343 -0
  231. package/template/src/resources/src/plugins/pulse.ts +141 -0
  232. package/template/src/resources/src/plugins/routeProgress.ts +87 -0
  233. package/template/src/resources/src/router/index.ts +85 -0
  234. package/template/src/resources/src/stores/admin-ui.ts +148 -0
  235. package/template/src/resources/src/stores/auth.ts +151 -0
  236. package/template/src/resources/tsconfig.json +19 -0
  237. package/template/src/resources/vite.config.ts +43 -0
  238. package/template/src/storage/logs/app.log +20179 -0
  239. package/template/src/storage/logs/fatal.log +727 -0
  240. package/template/tsconfig.json +20 -0
@@ -0,0 +1,116 @@
1
+ import { assertName } from "../../level-1/naming.mjs";
2
+ import { runSeed } from "../db/core.mjs";
3
+ import { assertModuleHasSeeders, cleanModuleTrash, deleteModule, deleteNotificationModule, listModules, makeController, makeExampleModule, makeJob, makeModel, makeModule, makeNotificationModule, makeRoute, makeSchedule, makeSeeder, runModuleMigrate } from "./core.mjs";
4
+
5
+ /** Register module:* subcommands on the CLI program. */
6
+ export function registerModuleCommands(program, rawArgs) {
7
+ program
8
+ .command("module:make <name>")
9
+ .description("Create a module with controller, route, model, and seeder folders")
10
+ .allowUnknownOption(true)
11
+ .action(async (name) => makeModule(name));
12
+ program
13
+ .command("module:make-notification [name]")
14
+ .description("Generate notification module (controller, routes, job, bell, page)")
15
+ .allowUnknownOption(true)
16
+ .action(async (name) => {
17
+ const maybeModule = name && !name.startsWith("--") ? name : "";
18
+ await makeNotificationModule(maybeModule || "notification");
19
+ });
20
+ program
21
+ .command("module:example [name]")
22
+ .description("Generate one-shot example module with queue/broadcast/scheduler cases")
23
+ .allowUnknownOption(true)
24
+ .action(async (name) => makeExampleModule(name || "example"));
25
+ program
26
+ .command("module:delete-notification [name]")
27
+ .description("Remove notification module, bell, and page (moves to trash)")
28
+ .option("--yes", "Confirm deletion without prompt")
29
+ .option("--dry-run", "Print what would be deleted without executing")
30
+ .allowUnknownOption(true)
31
+ .action(async (name) => {
32
+ const maybeModule = name && !name.startsWith("--") ? name : "";
33
+ const flagStart = maybeModule ? 2 : 1;
34
+ await deleteNotificationModule(maybeModule || "notification", rawArgs.slice(flagStart));
35
+ });
36
+ program
37
+ .command("module:delete <name>")
38
+ .description("Move a module directory to storage trash (soft delete)")
39
+ .option("--yes", "Confirm deletion without prompt")
40
+ .option("--dry-run", "Print what would be deleted without executing")
41
+ .allowUnknownOption(true)
42
+ .action(async (name) => deleteModule(name, rawArgs.slice(2)));
43
+ program
44
+ .command("module:trash:clean [name]")
45
+ .description("Permanently remove entries from module trash storage")
46
+ .option("--yes", "Confirm cleanup without prompt")
47
+ .option("--dry-run", "Print what would be cleaned without executing")
48
+ .allowUnknownOption(true)
49
+ .action(async (name) => {
50
+ const maybeModule = name && !name.startsWith("--") ? name : "";
51
+ const flagStart = maybeModule ? 2 : 1;
52
+ await cleanModuleTrash(maybeModule, rawArgs.slice(flagStart));
53
+ });
54
+ program
55
+ .command("module:make-route <module> [controller]")
56
+ .description("Generate a route file for an existing module")
57
+ .option("--force", "Overwrite existing route file")
58
+ .option("--dry-run", "Print what would be created without executing")
59
+ .allowUnknownOption(true)
60
+ .action(async (moduleName, controller) => makeRoute(moduleName, controller, rawArgs.slice(3)));
61
+ program
62
+ .command("module:make-controller <module> [name]")
63
+ .description("Generate a controller for an existing module")
64
+ .option("--force", "Overwrite existing controller files")
65
+ .option("--dry-run", "Print what would be created without executing")
66
+ .allowUnknownOption(true)
67
+ .action(async (moduleName, name) => makeController(moduleName, name, rawArgs.slice(3)));
68
+ program
69
+ .command("module:make-model <module> [name]")
70
+ .description("Generate a model file for an existing module")
71
+ .option("--force", "Overwrite existing model file")
72
+ .option("--dry-run", "Print what would be created without executing")
73
+ .allowUnknownOption(true)
74
+ .action(async (moduleName, name) => makeModel(moduleName, name, rawArgs.slice(3)));
75
+ program
76
+ .command("module:make-seeder <module> [name]")
77
+ .description("Generate a seeder file for an existing module model")
78
+ .option("--force", "Overwrite existing seeder file")
79
+ .option("--dry-run", "Print what would be created without executing")
80
+ .allowUnknownOption(true)
81
+ .action(async (moduleName, name) => makeSeeder(moduleName, name, rawArgs.slice(3)));
82
+ program
83
+ .command("module:make-job <module> [name]")
84
+ .description("Generate a job file for an existing module")
85
+ .option("--force", "Overwrite existing job file")
86
+ .option("--dry-run", "Print what would be created without executing")
87
+ .allowUnknownOption(true)
88
+ .action(async (moduleName, name) => makeJob(moduleName, name, rawArgs.slice(3)));
89
+ program
90
+ .command("module:make-console <module> [name]")
91
+ .description("Generate a scheduler/console file for an existing module")
92
+ .option("--force", "Overwrite existing console file")
93
+ .option("--dry-run", "Print what would be created without executing")
94
+ .allowUnknownOption(true)
95
+ .action(async (moduleName, name) => makeSchedule(moduleName, name, rawArgs.slice(3)));
96
+ program
97
+ .command("module:list")
98
+ .description("List all discovered modules")
99
+ .allowUnknownOption(true)
100
+ .action(async () => listModules());
101
+ program
102
+ .command("module:seed <module>")
103
+ .description("Run seeders for one module")
104
+ .allowUnknownOption(true)
105
+ .action(async (moduleName) => {
106
+ const normalized = assertName(moduleName, "Module name");
107
+ await assertModuleHasSeeders(normalized);
108
+ await runSeed(normalized);
109
+ });
110
+ program
111
+ .command("module:migrate <module>")
112
+ .description("Generate module-only schema, generate migration, then run migrate")
113
+ .option("--keep-temp", "Keep temporary schema file after migration")
114
+ .allowUnknownOption(true)
115
+ .action(async (moduleName) => runModuleMigrate(moduleName, rawArgs));
116
+ }
@@ -0,0 +1,16 @@
1
+ import { execSync } from "node:child_process";
2
+ import { existsSync, mkdirSync, readFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+
5
+ const isFalse = (v) => v === "false";
6
+
7
+ const envFile = resolve(process.cwd(), ".env");
8
+ const fileDisabled = existsSync(envFile) && /^FRONTEND=false\s*$/m.test(readFileSync(envFile, "utf-8"));
9
+
10
+ if (isFalse(process.env.FRONTEND) || fileDisabled) {
11
+ console.log("Frontend build skipped (FRONTEND=false)");
12
+ mkdirSync("public", { recursive: true });
13
+ process.exit(0);
14
+ }
15
+
16
+ execSync("vite build --config src/resources/vite.config.ts", { stdio: "inherit" });
@@ -0,0 +1,311 @@
1
+ import { spawn } from "node:child_process";
2
+ import fsSync from "node:fs";
3
+ import fs from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { glob } from "glob";
6
+ import { getOption, hasFlag } from "../../level-1/flags.mjs";
7
+ import { localBin, packageScript, runCommand, runNodeScript } from "../../level-1/process.mjs";
8
+
9
+ /** Parse --with or --view flags to determine which dev tools to launch. */
10
+ function parseWithOptions(flags = []) {
11
+ const supported = new Set(["redis", "maildev", "studio", "bullmq"]);
12
+ const selected = new Set();
13
+
14
+ for (const raw of [getOption(flags, "--with"), getOption(flags, "--view")]) {
15
+ if (!raw) continue;
16
+ for (const item of raw.split(",")) {
17
+ const key = item.trim().toLowerCase();
18
+ if (!key) continue;
19
+
20
+ const normalized = key === "queue" ? "bullmq" : key;
21
+ if (!supported.has(normalized)) {
22
+ console.warn(`[dev] Unknown --with option '${key}' (supported: redis, maildev, studio, bullmq)`);
23
+ continue;
24
+ }
25
+
26
+ selected.add(normalized);
27
+ }
28
+ }
29
+
30
+ return selected;
31
+ }
32
+
33
+ /** Build a list of dev UI tools to launch based on flags and with-options. */
34
+ function devViewList(flags = [], withOptions = new Set()) {
35
+ const selected = new Set(withOptions);
36
+ if (hasFlag(flags, "--with-redis-view")) selected.add("redis");
37
+ if (hasFlag(flags, "--with-maildev")) selected.add("maildev");
38
+ if (hasFlag(flags, "--with-db-studio")) selected.add("studio");
39
+ return Array.from(selected);
40
+ }
41
+
42
+ /** Build redis-commander CLI arguments from REDIS_URL env. */
43
+ function redisConnectionArgs() {
44
+ const redisUrl = new URL(process.env.REDIS_URL || "redis://127.0.0.1:6379");
45
+ const result = [
46
+ "--port",
47
+ process.env.REDIS_COMMANDER_PORT || "1369",
48
+ "--redis-host",
49
+ redisUrl.hostname || "127.0.0.1",
50
+ "--redis-port",
51
+ redisUrl.port || "6379"
52
+ ];
53
+
54
+ if (redisUrl.password) result.push("--redis-password", decodeURIComponent(redisUrl.password));
55
+ return result;
56
+ }
57
+
58
+ /** Run the full dev stack: API, frontend, queue worker, and optional UI tools. */
59
+ async function runDevStack(flags = []) {
60
+ const commands = [
61
+ { label: "api", args: ["serve", "--src"], required: true, hint: "http://localhost:3000" }
62
+ ];
63
+
64
+ if (process.env.FRONTEND !== "false") {
65
+ commands.push({ label: "frontend", args: ["frontend:dev"], required: true, hint: "http://localhost:5173" });
66
+ }
67
+
68
+ if (process.env.REDIS !== "false") {
69
+ commands.push({ label: "queue-worker", args: ["queue:work", "--queue=default,mail", "--quiet", "--src"], required: false });
70
+ }
71
+
72
+ const withOptions = parseWithOptions(flags);
73
+ const views = devViewList(flags, withOptions);
74
+ const enableRedisView =
75
+ process.env.REDIS !== "false" &&
76
+ (hasFlag(flags, "--with-redis-view") || withOptions.has("redis"));
77
+ const enableMaildev = hasFlag(flags, "--with-maildev") || withOptions.has("maildev");
78
+ const enableDbStudio = hasFlag(flags, "--with-db-studio") || withOptions.has("studio");
79
+
80
+ if (enableRedisView) {
81
+ commands.push({ label: "redis-view", args: ["redis:view", "--quiet"], required: false, hint: "http://localhost:1369" });
82
+ }
83
+ if (enableMaildev) {
84
+ commands.push({ label: "maildev-view", args: ["maildev:view", "--quiet"], required: false, hint: "http://localhost:1080" });
85
+ }
86
+ if (enableDbStudio) {
87
+ commands.push({ label: "db-studio", args: ["db:studio", "--quiet"], required: false, hint: "https://local.drizzle.studio" });
88
+ }
89
+
90
+ const children = [];
91
+ let shuttingDown = false;
92
+ let settled = false;
93
+
94
+ const killAll = () => {
95
+ for (const child of children) {
96
+ if (child.killed) continue;
97
+ try {
98
+ child.kill("SIGTERM");
99
+ } catch {}
100
+ }
101
+ };
102
+
103
+ const shutdown = () => {
104
+ if (shuttingDown) return;
105
+ shuttingDown = true;
106
+ killAll();
107
+ };
108
+
109
+ process.on("SIGINT", shutdown);
110
+ process.on("SIGTERM", shutdown);
111
+
112
+ await new Promise((resolve, reject) => {
113
+ for (const command of commands) {
114
+ const extraEnv =
115
+ command.label === "api"
116
+ ? { NEXGEN_DEV_VIEWS: views.join(","), NEXGEN_FRONTEND_URL: "http://localhost:5173" }
117
+ : {};
118
+ const child = spawn(process.execPath, [process.argv[1], ...command.args], {
119
+ stdio: command.label === "api" || command.label === "queue-worker" ? "inherit" : ["ignore", "pipe", "inherit"],
120
+ env: { ...process.env, ...extraEnv }
121
+ });
122
+ children.push(child);
123
+
124
+ child.on("exit", (code, signal) => {
125
+ if (settled) return;
126
+ if (!shuttingDown && (code !== null || signal !== null)) {
127
+ if (command.required) {
128
+ settled = true;
129
+ shutdown();
130
+ reject(new Error(`${command.label} exited (${signal || code})`));
131
+ return;
132
+ }
133
+ if (code !== 0 || signal) {
134
+ console.warn(`[dev] Optional process '${command.label}' exited (${signal || code}); continuing`);
135
+ }
136
+ }
137
+
138
+ const allExited = children.every((item) => item.exitCode !== null || item.signalCode !== null);
139
+ if (allExited) {
140
+ settled = true;
141
+ resolve();
142
+ }
143
+ });
144
+
145
+ child.on("error", (error) => {
146
+ if (settled) return;
147
+ settled = true;
148
+ shutdown();
149
+ reject(error);
150
+ });
151
+ }
152
+
153
+
154
+ }).finally(() => {
155
+ process.off("SIGINT", shutdown);
156
+ process.off("SIGTERM", shutdown);
157
+ killAll();
158
+ });
159
+ }
160
+
161
+ /** Run a runtime command: dev, serve, queue:work, queue:clear, schedule:work. */
162
+ export async function runRuntime(commandName, rawArgs = []) {
163
+ const prod = rawArgs.includes("--prod");
164
+ const forceSrc = rawArgs.includes("--src");
165
+ const watch = rawArgs.includes("--watch");
166
+ const runtimeArg = rawArgs.find((arg) => arg.startsWith("--runtime="));
167
+ const runtime = runtimeArg ? runtimeArg.split("=")[1].trim().toLowerCase() : "node";
168
+
169
+ if (!["node", "bun"].includes(runtime)) {
170
+ throw new Error(`Invalid runtime '${runtime}'. Supported values: node, bun`);
171
+ }
172
+
173
+ const runtimeArgs = rawArgs.filter(
174
+ (arg, i) => i > 0 && !arg.startsWith("--runtime=") && arg !== "--prod" && arg !== "--src"
175
+ );
176
+ const hasDistServer = fsSync.existsSync(path.resolve(process.cwd(), "dist/src/framework/server.js"));
177
+ const hasDistWorker = fsSync.existsSync(path.resolve(process.cwd(), "dist/src/framework/queue/worker.js"));
178
+ const hasDistScheduler = fsSync.existsSync(
179
+ path.resolve(process.cwd(), "dist/src/framework/scheduler/run.js")
180
+ );
181
+
182
+ if (commandName === "dev") {
183
+ await runDevStack(rawArgs.slice(1));
184
+ return;
185
+ }
186
+
187
+ if (commandName === "serve") {
188
+ if (!forceSrc && (prod || hasDistServer)) {
189
+ const entry = path.resolve(process.cwd(), "dist/src/framework/server.js");
190
+ const command = runtime === "bun" ? "bun" : process.execPath;
191
+ await runCommand(command, [entry, ...runtimeArgs]);
192
+ return;
193
+ }
194
+
195
+ const useWatch = watch || (!prod && forceSrc);
196
+ const tsxWatchIgnore = [
197
+ "--include",
198
+ "src/**/*.ts",
199
+ "--include",
200
+ "src/framework/maker-cli/src/**/*.mjs",
201
+ "--exclude",
202
+ "src/storage/**",
203
+ "--exclude",
204
+ "src/storage/logs/**",
205
+ "--exclude",
206
+ "src/storage/tmp/**",
207
+ "--exclude",
208
+ "src/database/migrations/**",
209
+ "--exclude",
210
+ "src/database/schema.ts",
211
+ "--exclude",
212
+ "public/**",
213
+ "--exclude",
214
+ "dist/**",
215
+ "--exclude",
216
+ ".git/**",
217
+ "--exclude",
218
+ "node_modules/**",
219
+ "--exclude",
220
+ "**/.DS_Store",
221
+ "--exclude",
222
+ "**/Thumbs.db",
223
+ "--exclude",
224
+ "**/*.log"
225
+ ];
226
+ const tsxArgs = useWatch
227
+ ? ["watch", ...tsxWatchIgnore, "src/framework/server.ts", ...runtimeArgs]
228
+ : ["src/framework/server.ts", ...runtimeArgs];
229
+ await runCommand(localBin("tsx"), tsxArgs);
230
+ return;
231
+ }
232
+
233
+ if (commandName === "queue:work") {
234
+ if (!forceSrc && (prod || hasDistWorker)) {
235
+ const entry = path.resolve(process.cwd(), "dist/src/framework/queue/worker.js");
236
+ const command = runtime === "bun" ? "bun" : process.execPath;
237
+ await runCommand(command, [entry, ...runtimeArgs]);
238
+ return;
239
+ }
240
+
241
+ await runNodeScript(packageScript("tsx", "dist/cli.mjs"), ["src/framework/queue/worker.ts", ...rawArgs.slice(1)]);
242
+ return;
243
+ }
244
+
245
+ if (commandName === "queue:clear") {
246
+ await runNodeScript(packageScript("tsx", "dist/cli.mjs"), ["src/framework/queue/clear.ts"]);
247
+ return;
248
+ }
249
+
250
+ if (commandName === "schedule:work") {
251
+ if (!forceSrc && (prod || hasDistScheduler)) {
252
+ const entry = path.resolve(process.cwd(), "dist/src/framework/scheduler/run.js");
253
+ const command = runtime === "bun" ? "bun" : process.execPath;
254
+ await runCommand(command, [entry, ...runtimeArgs]);
255
+ return;
256
+ }
257
+
258
+ await runNodeScript(packageScript("tsx", "dist/cli.mjs"), [
259
+ "src/framework/scheduler/run.ts",
260
+ ...rawArgs.slice(1)
261
+ ]);
262
+ }
263
+ }
264
+
265
+ /** Launch a UI tool: maildev or redis-commander. */
266
+ export async function runUi(commandName) {
267
+ if (commandName === "maildev:view") {
268
+ await runCommand(localBin("maildev"), [
269
+ "--smtp",
270
+ process.env.MAIL_PORT || process.env.MAILDEV_SMTP_PORT || "1025",
271
+ "--web",
272
+ process.env.MAILDEV_WEB_PORT || "1080"
273
+ ]);
274
+ return;
275
+ }
276
+
277
+ if (commandName === "redis:view") {
278
+ await runCommand(localBin("redis-commander"), redisConnectionArgs());
279
+ }
280
+ }
281
+
282
+ /** Start Vite dev server for a specific config file. */
283
+ export async function runVite(configPath) {
284
+ await runCommand(localBin("vite"), ["--config", configPath]);
285
+ }
286
+
287
+ /** Clear Vite cache directories. */
288
+ export async function clearViteCache() {
289
+ const dirs = [
290
+ path.resolve(process.cwd(), "node_modules/.vite"),
291
+ path.resolve(process.cwd(), "src/resources/node_modules/.vite")
292
+ ];
293
+
294
+ for (const dir of dirs) {
295
+ await fs.rm(dir, { recursive: true, force: true }).catch(() => {});
296
+ }
297
+
298
+ const strayCaches = await glob("**/.vite", {
299
+ cwd: process.cwd(),
300
+ nodir: false,
301
+ ignore: ["**/node_modules/**/.vite/**", "**/.git/**"],
302
+ windowsPathsNoEscape: true
303
+ });
304
+
305
+ for (const rel of strayCaches) {
306
+ const fullPath = path.resolve(process.cwd(), rel);
307
+ await fs.rm(fullPath, { recursive: true, force: true }).catch(() => {});
308
+ }
309
+
310
+ console.log("Vite cache cleared.");
311
+ }
@@ -0,0 +1,71 @@
1
+ import { runRuntime, runUi, runVite, clearViteCache } from "./core.mjs";
2
+
3
+ /** Register runtime commands (dev, serve, queue, schedule, UI tools) on the CLI program. */
4
+ export function registerRuntimeCommands(program, rawArgs) {
5
+ program
6
+ .command("dev")
7
+ .description("Start API + Vue 3 frontend plus optional workers/tools")
8
+ .option("--view <tools>", "Comma-separated UI tools to launch (redis,maildev,studio,bullmq)")
9
+ .option("--with <tools>", "Alias for --view")
10
+ .option("--with-redis-view", "Launch Redis Commander")
11
+ .option("--with-maildev", "Launch MailDev")
12
+ .option("--with-db-studio", "Launch Drizzle Studio")
13
+ .allowUnknownOption(true)
14
+ .action(async () => runRuntime("dev", rawArgs));
15
+ program
16
+ .command("serve")
17
+ .description("Start HTTP server (dist first if built, else src)")
18
+ .option("--prod", "Run production build")
19
+ .option("--runtime <name>", "Runtime: node or bun", "node")
20
+ .option("--watch", "Watch for file changes (src mode)")
21
+ .option("--src", "Force source mode even if dist exists")
22
+ .allowUnknownOption(true)
23
+ .action(async () => runRuntime("serve", rawArgs));
24
+ program
25
+ .command("queue:work")
26
+ .description("Start queue worker (dist first if built, else src)")
27
+ .option("--queue <name>", "Queue name", "default")
28
+ .option("--prod", "Run production build")
29
+ .option("--runtime <name>", "Runtime: node or bun", "node")
30
+ .option("--src", "Force source mode even if dist exists")
31
+ .allowUnknownOption(true)
32
+ .action(async () => runRuntime("queue:work", rawArgs));
33
+ program
34
+ .command("queue:clear")
35
+ .description("Clear all queue keys")
36
+ .allowUnknownOption(true)
37
+ .action(async () => runRuntime("queue:clear", rawArgs));
38
+ program
39
+ .command("schedule:work")
40
+ .description("Start scheduler worker (dist first if built, else src)")
41
+ .option("--prod", "Run production build")
42
+ .option("--runtime <name>", "Runtime: node or bun", "node")
43
+ .option("--src", "Force source mode even if dist exists")
44
+ .allowUnknownOption(true)
45
+ .action(async () => runRuntime("schedule:work", rawArgs));
46
+ program
47
+ .command("maildev:view")
48
+ .description("Start MailDev SMTP and web UI")
49
+ .allowUnknownOption(true)
50
+ .action(async () => runUi("maildev:view"));
51
+ program
52
+ .command("redis:view")
53
+ .description("Start Redis Commander UI")
54
+ .allowUnknownOption(true)
55
+ .action(async () => runUi("redis:view"));
56
+ program
57
+ .command("frontend:dev")
58
+ .description("Start Vue 3 frontend from src/resources")
59
+ .allowUnknownOption(true)
60
+ .action(async () => runVite("src/resources/vite.config.ts"));
61
+ program
62
+ .command("admin:dev")
63
+ .description("Start admin UI dev server")
64
+ .allowUnknownOption(true)
65
+ .action(async () => runVite("src/resources/vite.config.ts"));
66
+ program
67
+ .command("vite:cache:clear")
68
+ .description("Clear Vite cache folders (cross-platform)")
69
+ .allowUnknownOption(true)
70
+ .action(async () => clearViteCache());
71
+ }
@@ -0,0 +1,55 @@
1
+ import type { Handler } from "hono";
2
+
3
+ /**
4
+ * Why: Returns collection response shape for list pages.
5
+ * When: Used by GET list endpoint.
6
+ * Where: GET /{{controller}} route.
7
+ */
8
+ export const index: Handler = async (c: any) => {
9
+ return c.json({ message: "{{controller}} list fetched successfully", data: [] });
10
+ };
11
+
12
+ /**
13
+ * Why: Returns one resource by validated route id.
14
+ * When: Used by detail screens or fetch-by-id calls.
15
+ * Where: GET /{{controller}}/{id} route.
16
+ */
17
+ export const show: Handler = async (c: any) => {
18
+ const params = c.req.valid("param");
19
+
20
+ return c.json({ message: "{{controller}} fetched successfully", data: { id: params.id, name: "" } });
21
+ };
22
+
23
+ /**
24
+ * Why: Creates a new resource from validated request body.
25
+ * When: Used by create form submissions.
26
+ * Where: POST /{{controller}} route.
27
+ */
28
+ export const store: Handler = async (c: any) => {
29
+ c.req.valid("json");
30
+
31
+ return c.json({ message: "{{controller}} created successfully" }, 201);
32
+ };
33
+
34
+ /**
35
+ * Why: Updates an existing resource using id + body validation.
36
+ * When: Used by edit/update form submissions.
37
+ * Where: PUT/PATCH /{{controller}}/{id} route.
38
+ */
39
+ export const update: Handler = async (c: any) => {
40
+ c.req.valid("param");
41
+ c.req.valid("json");
42
+
43
+ return c.json({ message: "{{controller}} updated successfully" });
44
+ };
45
+
46
+ /**
47
+ * Why: Deletes one resource by validated route id.
48
+ * When: Used by delete actions.
49
+ * Where: DELETE /{{controller}}/{id} route.
50
+ */
51
+ export const destroy: Handler = async (c: any) => {
52
+ c.req.valid("param");
53
+
54
+ return c.json({ message: "{{controller}} deleted successfully" });
55
+ };
@@ -0,0 +1,56 @@
1
+ import type { Handler } from "hono";
2
+ import { {{tableVariable}} } from "@/modules/{{module}}/database/models/{{controller}}.js";
3
+
4
+ /**
5
+ * Why: Returns collection response shape for list pages.
6
+ * When: Used by GET list endpoint.
7
+ * Where: GET /{{controller}} route.
8
+ */
9
+ export const index: Handler = async (c: any) => {
10
+ return c.json({ message: "{{controller}} list fetched successfully", data: [] });
11
+ };
12
+
13
+ /**
14
+ * Why: Returns one resource by validated route id.
15
+ * When: Used by detail screens or fetch-by-id calls.
16
+ * Where: GET /{{controller}}/{id} route.
17
+ */
18
+ export const show: Handler = async (c: any) => {
19
+ const params = c.req.valid("param");
20
+
21
+ return c.json({ message: "{{controller}} fetched successfully", data: { id: params.id, name: "" } });
22
+ };
23
+
24
+ /**
25
+ * Why: Creates a new resource from validated request body.
26
+ * When: Used by create form submissions.
27
+ * Where: POST /{{controller}} route.
28
+ */
29
+ export const store: Handler = async (c: any) => {
30
+ c.req.valid("json");
31
+
32
+ return c.json({ message: "{{controller}} created successfully" }, 201);
33
+ };
34
+
35
+ /**
36
+ * Why: Updates an existing resource using id + body validation.
37
+ * When: Used by edit/update form submissions.
38
+ * Where: PUT/PATCH /{{controller}}/{id} route.
39
+ */
40
+ export const update: Handler = async (c: any) => {
41
+ c.req.valid("param");
42
+ c.req.valid("json");
43
+
44
+ return c.json({ message: "{{controller}} updated successfully" });
45
+ };
46
+
47
+ /**
48
+ * Why: Deletes one resource by validated route id.
49
+ * When: Used by delete actions.
50
+ * Where: DELETE /{{controller}}/{id} route.
51
+ */
52
+ export const destroy: Handler = async (c: any) => {
53
+ c.req.valid("param");
54
+
55
+ return c.json({ message: "{{controller}} deleted successfully" });
56
+ };
@@ -0,0 +1,57 @@
1
+ import type { Handler } from "hono";
2
+ import { validate } from "@/framework/facade.js";
3
+ import { Create{{ClassName}}Schema, Update{{ClassName}}Schema, {{ClassName}}IdParamsSchema } from "@/modules/{{module}}/controllers/{{controller}}.schema.js";
4
+
5
+ /**
6
+ * Why: Returns collection response shape for list pages.
7
+ * When: Used by GET list endpoint.
8
+ * Where: GET /{{controller}} route.
9
+ */
10
+ export const index: Handler = async (c: any) => {
11
+ return c.json({ message: "{{controller}} list fetched successfully", data: [] });
12
+ };
13
+
14
+ /**
15
+ * Why: Returns one resource by validated route id.
16
+ * When: Used by detail screens or fetch-by-id calls.
17
+ * Where: GET /{{controller}}/{id} route.
18
+ */
19
+ export const show: Handler = async (c: any) => {
20
+ const params = await validate({{ClassName}}IdParamsSchema, c.req.param());
21
+
22
+ return c.json({ message: "{{controller}} fetched successfully", data: { id: params.id, name: "" } });
23
+ };
24
+
25
+ /**
26
+ * Why: Creates a new resource from validated request body.
27
+ * When: Used by create form submissions.
28
+ * Where: POST /{{controller}} route.
29
+ */
30
+ export const store: Handler = async (c: any) => {
31
+ await validate(Create{{ClassName}}Schema, await c.req.json());
32
+
33
+ return c.json({ message: "{{controller}} created successfully" }, 201);
34
+ };
35
+
36
+ /**
37
+ * Why: Updates an existing resource using id + body validation.
38
+ * When: Used by edit/update form submissions.
39
+ * Where: PUT/PATCH /{{controller}}/{id} route.
40
+ */
41
+ export const update: Handler = async (c: any) => {
42
+ await validate({{ClassName}}IdParamsSchema, c.req.param());
43
+ await validate(Update{{ClassName}}Schema, await c.req.json());
44
+
45
+ return c.json({ message: "{{controller}} updated successfully" });
46
+ };
47
+
48
+ /**
49
+ * Why: Deletes one resource by validated route id.
50
+ * When: Used by delete actions.
51
+ * Where: DELETE /{{controller}}/{id} route.
52
+ */
53
+ export const destroy: Handler = async (c: any) => {
54
+ await validate({{ClassName}}IdParamsSchema, c.req.param());
55
+
56
+ return c.json({ message: "{{controller}} deleted successfully" });
57
+ };