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,299 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { glob } from "glob";
4
+ import { databaseNameFromUrl, databaseUrl, detectDialect, parsedDatabaseUrl, sqlitePathFromUrl } from "../../level-1/env-db.mjs";
5
+ import { packageScript, runCommand, runNodeScript } from "../../level-1/process.mjs";
6
+
7
+ /** Detect SQL dialect from raw SQL content by matching against known syntax hints. */
8
+ function detectMigrationDialectFromSql(sql = "") {
9
+ const source = sql.toLowerCase();
10
+ if (!source.trim()) return "unknown";
11
+
12
+ const sqliteHints = ["autoincrement", "integer primary key", "pragma"];
13
+ const mysqlHints = ["auto_increment", "engine=", "datetime", "varchar("];
14
+ const postgresHints = ["serial", "bigserial", "timestamp with time zone", "public."];
15
+
16
+ if (mysqlHints.some((hint) => source.includes(hint))) return "mysql";
17
+ if (postgresHints.some((hint) => source.includes(hint))) return "postgresql";
18
+ if (sqliteHints.some((hint) => source.includes(hint))) return "sqlite";
19
+ return "unknown";
20
+ }
21
+
22
+ /** List existing migration .sql files for the current dialect, sorted. */
23
+ export async function migrationFiles() {
24
+ const files = await glob(`src/database/migrations/${detectDialect()}/*.sql`, {
25
+ cwd: process.cwd(),
26
+ nodir: true,
27
+ windowsPathsNoEscape: true
28
+ });
29
+ return files.sort();
30
+ }
31
+
32
+ /** Generate drizzle-kit CLI args for migration generation. Uses --name init for the first migration. */
33
+ export async function drizzleGenerateArgs() {
34
+ const files = await migrationFiles();
35
+ if (!files.length) return ["generate", "--name", "init"];
36
+ return ["generate"];
37
+ }
38
+
39
+ /** Create MySQL database if it does not exist. Uses DATABASE_URL credentials. */
40
+ async function ensureMysqlDatabaseExists() {
41
+ const targetUrl = parsedDatabaseUrl();
42
+ const database = databaseNameFromUrl(targetUrl);
43
+ if (!targetUrl || !database) return;
44
+ const mysql = await import("mysql2/promise");
45
+ const connection = await mysql.createConnection({
46
+ host: targetUrl.hostname,
47
+ port: targetUrl.port ? Number(targetUrl.port) : 3306,
48
+ user: decodeURIComponent(targetUrl.username || "root"),
49
+ password: decodeURIComponent(targetUrl.password || ""),
50
+ database: "mysql",
51
+ ssl: targetUrl.searchParams.get("ssl") === "true" ? {} : undefined
52
+ });
53
+ try {
54
+ const [rows] = await connection.query(
55
+ "SELECT SCHEMA_NAME as name FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = ?",
56
+ [database]
57
+ );
58
+ const exists = Array.isArray(rows) && rows.length > 0;
59
+ if (!exists) {
60
+ const safe = database.replace(/`/g, "``");
61
+ await connection.query(`CREATE DATABASE \`${safe}\``);
62
+ console.log(`Created MySQL database: ${database}`);
63
+ }
64
+ } finally {
65
+ await connection.end();
66
+ }
67
+ }
68
+
69
+ /** Create Postgres database if it does not exist. Uses DATABASE_URL credentials. */
70
+ async function ensurePostgresDatabaseExists() {
71
+ const targetUrl = parsedDatabaseUrl();
72
+ const database = databaseNameFromUrl(targetUrl);
73
+ if (!targetUrl || !database) return;
74
+ const pg = await import("pg");
75
+ const admin = new pg.Client({
76
+ host: targetUrl.hostname,
77
+ port: targetUrl.port ? Number(targetUrl.port) : 5432,
78
+ user: decodeURIComponent(targetUrl.username || "postgres"),
79
+ password: decodeURIComponent(targetUrl.password || ""),
80
+ database: "postgres",
81
+ ssl: targetUrl.searchParams.get("ssl") === "true" ? { rejectUnauthorized: false } : undefined
82
+ });
83
+ await admin.connect();
84
+ try {
85
+ const { rows } = await admin.query("SELECT 1 FROM pg_database WHERE datname = $1", [database]);
86
+ if (!rows.length) {
87
+ const safe = database.replace(/"/g, '""');
88
+ await admin.query(`CREATE DATABASE "${safe}"`);
89
+ console.log(`Created Postgres database: ${database}`);
90
+ }
91
+ } finally {
92
+ await admin.end();
93
+ }
94
+ }
95
+
96
+ /** Ensure the database exists for the current DATABASE_URL dialect. Creates it if missing (MySQL/Postgres). */
97
+ export async function ensureDatabaseExists() {
98
+ const dialect = detectDialect();
99
+ if (dialect === "sqlite") {
100
+ await ensureDatabaseDirectory();
101
+ return;
102
+ }
103
+ if (dialect === "mysql") {
104
+ await ensureMysqlDatabaseExists();
105
+ return;
106
+ }
107
+ if (dialect === "postgresql") {
108
+ await ensurePostgresDatabaseExists();
109
+ }
110
+ }
111
+
112
+ /** Resolve the filesystem path for the SQLite database file. */
113
+ function sqliteDatabasePath() {
114
+ return path.resolve(process.cwd(), sqlitePathFromUrl(databaseUrl()));
115
+ }
116
+
117
+ /** Delete and recreate the SQLite database file. */
118
+ async function resetSqliteDatabase() {
119
+ const file = sqliteDatabasePath();
120
+ await fs.rm(file, { force: true });
121
+ await fs.mkdir(path.dirname(file), { recursive: true });
122
+ }
123
+
124
+ /** Drop and recreate the MySQL database. */
125
+ async function resetMysqlDatabase() {
126
+ const targetUrl = parsedDatabaseUrl();
127
+ const database = databaseNameFromUrl(targetUrl);
128
+ if (!targetUrl || !database) return;
129
+ const mysql = await import("mysql2/promise");
130
+ const connection = await mysql.createConnection({
131
+ host: targetUrl.hostname,
132
+ port: targetUrl.port ? Number(targetUrl.port) : 3306,
133
+ user: decodeURIComponent(targetUrl.username || "root"),
134
+ password: decodeURIComponent(targetUrl.password || ""),
135
+ database: "mysql",
136
+ ssl: targetUrl.searchParams.get("ssl") === "true" ? {} : undefined
137
+ });
138
+ try {
139
+ const safe = database.replace(/`/g, "``");
140
+ await connection.query(`DROP DATABASE IF EXISTS \`${safe}\``);
141
+ await connection.query(`CREATE DATABASE \`${safe}\``);
142
+ } finally {
143
+ await connection.end();
144
+ }
145
+ }
146
+
147
+ /** Drop and recreate the Postgres database. Terminates active connections first. */
148
+ async function resetPostgresDatabase() {
149
+ const targetUrl = parsedDatabaseUrl();
150
+ const database = databaseNameFromUrl(targetUrl);
151
+ if (!targetUrl || !database) return;
152
+ const pg = await import("pg");
153
+ const admin = new pg.Client({
154
+ host: targetUrl.hostname,
155
+ port: targetUrl.port ? Number(targetUrl.port) : 5432,
156
+ user: decodeURIComponent(targetUrl.username || "postgres"),
157
+ password: decodeURIComponent(targetUrl.password || ""),
158
+ database: "postgres",
159
+ ssl: targetUrl.searchParams.get("ssl") === "true" ? { rejectUnauthorized: false } : undefined
160
+ });
161
+ await admin.connect();
162
+ try {
163
+ const safe = database.replace(/"/g, '""');
164
+ await admin.query(
165
+ "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = $1 AND pid <> pg_backend_pid()",
166
+ [database]
167
+ );
168
+ await admin.query(`DROP DATABASE IF EXISTS "${safe}"`);
169
+ await admin.query(`CREATE DATABASE "${safe}"`);
170
+ } finally {
171
+ await admin.end();
172
+ }
173
+ }
174
+
175
+ /** Drop and recreate the database for the current dialect. */
176
+ export async function resetDatabase() {
177
+ const dialect = detectDialect();
178
+ if (dialect === "sqlite") return await resetSqliteDatabase();
179
+ if (dialect === "mysql") return await resetMysqlDatabase();
180
+ if (dialect === "postgresql") return await resetPostgresDatabase();
181
+ }
182
+
183
+ /** Check if the database has existing app tables (excluding drizzle meta tables). */
184
+ export async function hasExistingAppTables() {
185
+ const dialect = detectDialect();
186
+ if (dialect === "sqlite") {
187
+ const sqlite = await import("better-sqlite3");
188
+ const file = sqliteDatabasePath();
189
+ const db = sqlite.default(file);
190
+ try {
191
+ const rows = db
192
+ .prepare(
193
+ "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name != '__drizzle_migrations'"
194
+ )
195
+ .all();
196
+ return rows.length > 0;
197
+ } finally {
198
+ db.close();
199
+ }
200
+ }
201
+
202
+ if (dialect === "mysql") {
203
+ const mysql = await import("mysql2/promise");
204
+ const connection = await mysql.createConnection(databaseUrl());
205
+ try {
206
+ const [rows] = await connection.query(
207
+ "SELECT table_name as name FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name != '__drizzle_migrations'"
208
+ );
209
+ return Array.isArray(rows) && rows.length > 0;
210
+ } finally {
211
+ await connection.end();
212
+ }
213
+ }
214
+
215
+ const pg = await import("pg");
216
+ const client = new pg.Client({ connectionString: databaseUrl() });
217
+ await client.connect();
218
+ try {
219
+ const result = await client.query("SELECT tablename FROM pg_tables WHERE schemaname = 'public' AND tablename != '__drizzle_migrations'");
220
+ return result.rows.length > 0;
221
+ } finally {
222
+ await client.end();
223
+ }
224
+ }
225
+
226
+ /** Run the application seed script via tsx or Bun. */
227
+ export async function runSeed(moduleName = "") {
228
+ const seedArgs = ["src/framework/database/seed.ts"];
229
+ if (moduleName) seedArgs.push(`--module=${moduleName}`);
230
+ if (process.versions?.bun) {
231
+ await runCommand("bun", seedArgs);
232
+ return;
233
+ }
234
+ await runNodeScript(packageScript("tsx", "dist/cli.mjs"), seedArgs);
235
+ }
236
+
237
+ /** Ensure the SQLite database directory exists. No-op for MySQL/Postgres. */
238
+ export async function ensureDatabaseDirectory() {
239
+ if (detectDialect() !== "sqlite") return;
240
+ const file = path.resolve(process.cwd(), sqlitePathFromUrl(databaseUrl()));
241
+ await fs.mkdir(path.dirname(file), { recursive: true });
242
+ }
243
+
244
+ /** Ensure the drizzle migration meta directory and _journal.json exist. */
245
+ export async function ensureMigrationMeta() {
246
+ const migrationRoot = path.resolve(process.cwd(), "src/database/migrations", detectDialect());
247
+ const metaRoot = path.join(migrationRoot, "meta");
248
+ const journalPath = path.join(metaRoot, "_journal.json");
249
+ await fs.mkdir(metaRoot, { recursive: true });
250
+ try {
251
+ await fs.access(journalPath);
252
+ } catch {
253
+ const dialect = detectDialect();
254
+ const journalDialect = dialect === "postgresql" ? "postgresql" : dialect;
255
+ await fs.writeFile(
256
+ journalPath,
257
+ `{
258
+ "version": "7",
259
+ "dialect": "${journalDialect}",
260
+ "entries": []
261
+ }
262
+ `
263
+ );
264
+ }
265
+ }
266
+
267
+ /** Sync the migration dialect to match the current DATABASE_URL. Resets migrations if dialect changed. */
268
+ export async function syncMigrationDialect() {
269
+ async function existingMigrationDialect() {
270
+ const files = await migrationFiles();
271
+ if (!files.length) return null;
272
+ const first = await fs.readFile(path.resolve(process.cwd(), files[0]), "utf8");
273
+ return detectMigrationDialectFromSql(first);
274
+ }
275
+
276
+ async function resetMigrationFiles() {
277
+ const migrationRoot = path.resolve(process.cwd(), "src/database/migrations", detectDialect());
278
+ const dialect = detectDialect();
279
+ const journalDialect = dialect === "postgresql" ? "postgresql" : dialect;
280
+ await fs.rm(migrationRoot, { recursive: true, force: true });
281
+ await fs.mkdir(path.join(migrationRoot, "meta"), { recursive: true });
282
+ await fs.writeFile(
283
+ path.join(migrationRoot, "meta", "_journal.json"),
284
+ `{
285
+ "version": "7",
286
+ "dialect": "${journalDialect}",
287
+ "entries": []
288
+ }
289
+ `
290
+ );
291
+ console.log("Reset existing migration files due to dialect change.");
292
+ }
293
+
294
+ const target = detectDialect();
295
+ const current = await existingMigrationDialect();
296
+ if (!current || current === "unknown" || current === target) return;
297
+ console.log(`Migration dialect changed (${current} -> ${target}). Regenerating migration files for current DATABASE_URL.`);
298
+ await resetMigrationFiles();
299
+ }
@@ -0,0 +1,177 @@
1
+ import { syncMigrationDialect, ensureMigrationMeta, drizzleGenerateArgs, migrationFiles, ensureDatabaseDirectory, ensureDatabaseExists, hasExistingAppTables, runSeed, resetDatabase } from "./core.mjs";
2
+ import { generateSchema } from "../module/core.mjs";
3
+ import { assertName as assertNameLeaf } from "../../level-1/naming.mjs";
4
+ import { showHelp } from "../../level-1/help.mjs";
5
+ import { packageScript, runNodeScript } from "../../level-1/process.mjs";
6
+
7
+ let args = [];
8
+ let command = "", firstArg = "", secondArg = "";
9
+ let dbProgram = null;
10
+
11
+ /** Wrapper around assertName that exits with help on failure. */
12
+ function assertName(value, label) {
13
+ try {
14
+ return assertNameLeaf(value, label);
15
+ } catch {
16
+ console.error(`${label} is required.`);
17
+ showHelp(dbProgram);
18
+ process.exit(1);
19
+ }
20
+ }
21
+
22
+ function unsupported() {
23
+ console.error(
24
+ 'Drizzle Kit does not support Laravel-style rollback/refresh migrations. Use "bun maker db fresh --seed" for a SQLite development reset, or manage rollback SQL manually for production databases.'
25
+ );
26
+ process.exit(1);
27
+ }
28
+
29
+ async function handleFresh() {
30
+ await ensureDatabaseExists();
31
+ await resetDatabase();
32
+ await generateSchema();
33
+ await runNodeScript(packageScript("drizzle-kit", "bin.cjs"), await drizzleGenerateArgs());
34
+ await runNodeScript(packageScript("drizzle-kit", "bin.cjs"), ["migrate"]);
35
+ if (args.includes("--seed")) await runSeed();
36
+ }
37
+
38
+ async function handleReset() {
39
+ await ensureDatabaseExists();
40
+ await resetDatabase();
41
+ }
42
+
43
+ async function handleStatus() {
44
+ await generateSchema();
45
+ const files = await migrationFiles();
46
+ if (!files.length) {
47
+ console.log("No generated migration files found.");
48
+ return;
49
+ }
50
+ console.log("Generated migrations:");
51
+ for (const file of files) console.log(` - ${file}`);
52
+ }
53
+
54
+ const handlers = {
55
+ schema: () => generateSchema(),
56
+
57
+ generate: async () => {
58
+ await syncMigrationDialect();
59
+ await ensureMigrationMeta();
60
+ await generateSchema();
61
+ await ensureDatabaseDirectory();
62
+ await runNodeScript(packageScript("drizzle-kit", "bin.cjs"), await drizzleGenerateArgs());
63
+ },
64
+
65
+ migrate: async () => {
66
+ await syncMigrationDialect();
67
+ await ensureMigrationMeta();
68
+ const hadMigrationsBeforeGenerate = (await migrationFiles()).length > 0;
69
+ await ensureDatabaseExists();
70
+ const hadExistingTablesBeforeGenerate = await hasExistingAppTables();
71
+ await generateSchema();
72
+ await runNodeScript(packageScript("drizzle-kit", "bin.cjs"), await drizzleGenerateArgs());
73
+ if (!hadMigrationsBeforeGenerate && hadExistingTablesBeforeGenerate) {
74
+ throw new Error(
75
+ "Initial migration was generated, but the database already contains tables. " +
76
+ "This usually means migration files were deleted while DB data still exists. " +
77
+ "Use 'bun maker db:fresh --seed' to rebuild locally, or restore migration files before running db:migrate."
78
+ );
79
+ }
80
+ await runNodeScript(packageScript("drizzle-kit", "bin.cjs"), ["migrate"]);
81
+ if (args.includes("--seed")) await runSeed();
82
+ },
83
+
84
+ "migrate:run": async () => {
85
+ await generateSchema();
86
+ await ensureDatabaseExists();
87
+ await runNodeScript(packageScript("drizzle-kit", "bin.cjs"), ["migrate"]);
88
+ },
89
+
90
+ fresh: handleFresh,
91
+ "migrate:fresh": handleFresh,
92
+
93
+ reset: handleReset,
94
+ wipe: handleReset,
95
+ "migrate:reset": handleReset,
96
+
97
+ status: handleStatus,
98
+ "migrate:status": handleStatus,
99
+
100
+ push: async () => {
101
+ await generateSchema();
102
+ await ensureDatabaseExists();
103
+ await runNodeScript(packageScript("drizzle-kit", "bin.cjs"), ["push"]);
104
+ },
105
+
106
+ check: async () => {
107
+ await generateSchema();
108
+ await ensureDatabaseExists();
109
+ await runNodeScript(packageScript("drizzle-kit", "bin.cjs"), ["check"]);
110
+ },
111
+
112
+ studio: async () => {
113
+ const quiet = args.includes("--quiet");
114
+ await generateSchema({ silent: quiet });
115
+ await ensureDatabaseExists();
116
+ try {
117
+ await runNodeScript(packageScript("drizzle-kit", "bin.cjs"), ["studio"], { silent: quiet });
118
+ } catch (error) {
119
+ if (quiet) throw new Error("db:studio unavailable");
120
+ throw error;
121
+ }
122
+ },
123
+
124
+ seed: async () => {
125
+ const possibleModule = command?.startsWith("db:") ? firstArg : secondArg;
126
+ const moduleName = possibleModule && !possibleModule.startsWith("--") ? possibleModule : null;
127
+ await runSeed(moduleName || "");
128
+ },
129
+
130
+ "module:seed": async () => {
131
+ const moduleName = assertName(secondArg, "Module name");
132
+ await runSeed(moduleName);
133
+ },
134
+
135
+ rollback: unsupported,
136
+ "migrate:rollback": unsupported,
137
+ refresh: unsupported,
138
+ "migrate:refresh": unsupported
139
+ };
140
+
141
+ /** Register db:* subcommands on the CLI program. */
142
+ export function registerDbCommands(program, rawArgs) {
143
+ dbProgram = program;
144
+ args = rawArgs;
145
+ command = rawArgs[0] || "";
146
+ firstArg = rawArgs[1] || "";
147
+ secondArg = rawArgs[2] || "";
148
+ program
149
+ .command("db [subcommand]")
150
+ .description("Run database operations (schema, generate, migrate, fresh, seed, etc.)")
151
+ .allowUnknownOption(true)
152
+ .action(async (subcommand) => {
153
+ const handler = handlers[subcommand];
154
+ if (handler) return await handler();
155
+ console.error(`Unknown db command: ${subcommand || ""}`);
156
+ showHelp(dbProgram);
157
+ process.exit(1);
158
+ });
159
+
160
+ const cmd = (name, desc, opts = []) => {
161
+ const c = program.command(`db:${name}`).description(desc).allowUnknownOption(true);
162
+ for (const o of opts) c.option(o.flag, o.description);
163
+ c.action(handlers[name]);
164
+ };
165
+
166
+ cmd("schema", "Generate src/database/schema.ts from module models");
167
+ cmd("generate", "Generate Drizzle migrations after schema discovery");
168
+ cmd("migrate", "Generate then run Drizzle migrations", [{ flag: "--seed", description: "Run seeders after migration" }]);
169
+ cmd("migrate:run", "Run existing Drizzle migrations only");
170
+ cmd("fresh", "Reset database then generate, migrate, optionally seed", [{ flag: "--seed", description: "Run seeders after fresh" }]);
171
+ cmd("seed", "Run all seeders or one module seeder set", [{ flag: "--module", description: "Optional module name" }]);
172
+ cmd("status", "Show generated migration files");
173
+ cmd("push", "Run drizzle-kit push after schema discovery");
174
+ cmd("check", "Run drizzle-kit check after schema discovery");
175
+ cmd("studio", "Open Drizzle Studio after schema discovery", [{ flag: "--quiet", description: "Suppress output" }]);
176
+ cmd("module:seed", "Run seeders for a specific module");
177
+ }