create-nexwire 4.3.0 → 4.4.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.
- package/README.md +10 -5
- package/express/src/framework/facade.ts +1 -0
- package/express/src/framework/http/logger.ts +13 -4
- package/express/src/framework/maker-cli/runtime/core.mjs +1 -2
- package/express/src/framework/notification/channels/mail.ts +8 -6
- package/express/src/middlewares/auth-middleware.ts +1 -2
- package/express/src/middlewares/role-middleware.ts +3 -13
- package/express/src/modules/auth/routes/index.ts +1 -2
- package/express/src/providers/notification.ts +2 -2
- package/express/src/resources/src/components/Switch.vue +60 -65
- package/express/src/resources/src/pages/auth/forgetPassword.vue +23 -30
- package/express/src/resources/src/pages/auth/login.vue +25 -28
- package/express/src/resources/src/pages/auth/register.vue +35 -39
- package/express/src/resources/src/pages/auth/resetPassword.vue +29 -37
- package/express/src/resources/src/pages/auth/verifyEmail.vue +15 -19
- package/express/src/resources/src/plugins/gum.ts +519 -203
- package/express/src/resources/src/stores/auth.ts +9 -6
- package/express/src/storage/database/nexwire.sqlite +0 -0
- package/fastify/.dockerignore +14 -0
- package/fastify/.env +44 -0
- package/fastify/.env.example +42 -0
- package/fastify/.vscode/extensions.json +6 -0
- package/fastify/.vscode/settings.json +37 -0
- package/fastify/LICENSE +21 -0
- package/fastify/README.md +232 -0
- package/fastify/biome.json +99 -0
- package/fastify/drizzle.config.ts +25 -0
- package/fastify/gitignore-stub +31 -0
- package/fastify/package.json +130 -0
- package/fastify/public/.gitkeep +0 -0
- package/fastify/src/config/app.ts +41 -0
- package/fastify/src/config/auth.ts +12 -0
- package/fastify/src/config/cache.ts +16 -0
- package/fastify/src/config/cookie.ts +15 -0
- package/fastify/src/config/cors.ts +12 -0
- package/fastify/src/config/database.ts +48 -0
- package/fastify/src/config/index.ts +83 -0
- package/fastify/src/config/jwt.ts +19 -0
- package/fastify/src/config/logging.ts +13 -0
- package/fastify/src/config/mail.ts +24 -0
- package/fastify/src/config/openapi.ts +36 -0
- package/fastify/src/config/queue.ts +30 -0
- package/fastify/src/config/rateLimit.ts +17 -0
- package/fastify/src/config/realtime.ts +15 -0
- package/fastify/src/config/redis.ts +18 -0
- package/fastify/src/config/security.ts +44 -0
- package/fastify/src/config/session.ts +17 -0
- package/fastify/src/config/storage.ts +22 -0
- package/fastify/src/config/validate.ts +50 -0
- package/fastify/src/database/migrations/sqlite/0000_init.sql +48 -0
- package/fastify/src/database/migrations/sqlite/meta/0000_snapshot.json +324 -0
- package/fastify/src/database/migrations/sqlite/meta/_journal.json +13 -0
- package/fastify/src/database/schema.ts +2 -0
- package/fastify/src/env.ts +76 -0
- package/fastify/src/framework/cache/cache.ts +114 -0
- package/fastify/src/framework/circuit-breaker/cb.ts +88 -0
- package/fastify/src/framework/database/app-schema.ts +37 -0
- package/fastify/src/framework/database/connection.ts +223 -0
- package/fastify/src/framework/database/migrate-hooks.ts +87 -0
- package/fastify/src/framework/database/paginate.ts +226 -0
- package/fastify/src/framework/database/schema.ts +20 -0
- package/fastify/src/framework/database/seed.ts +152 -0
- package/fastify/src/framework/events/dispatcher.ts +61 -0
- package/fastify/src/framework/facade.ts +96 -0
- package/fastify/src/framework/http/app.ts +176 -0
- package/fastify/src/framework/http/cors.ts +13 -0
- package/fastify/src/framework/http/fastify.d.ts +13 -0
- package/fastify/src/framework/http/logger.ts +110 -0
- package/fastify/src/framework/http/metrics.ts +22 -0
- package/fastify/src/framework/http/openapi.ts +48 -0
- package/fastify/src/framework/http/ratelimiter.ts +47 -0
- package/fastify/src/framework/http/security.ts +25 -0
- package/fastify/src/framework/http/static.ts +83 -0
- package/fastify/src/framework/http/validation.ts +49 -0
- package/fastify/src/framework/kernel.ts +43 -0
- package/fastify/src/framework/maker-cli/db/core.mjs +291 -0
- package/fastify/src/framework/maker-cli/db/index.mjs +204 -0
- package/fastify/src/framework/maker-cli/deploy/core.mjs +1046 -0
- package/fastify/src/framework/maker-cli/deploy/index.mjs +91 -0
- package/fastify/src/framework/maker-cli/index.mjs +65 -0
- package/fastify/src/framework/maker-cli/middleware/core.mjs +41 -0
- package/fastify/src/framework/maker-cli/middleware/index.mjs +17 -0
- package/fastify/src/framework/maker-cli/module/core.mjs +1066 -0
- package/fastify/src/framework/maker-cli/module/index.mjs +211 -0
- package/fastify/src/framework/maker-cli/provider/core.mjs +49 -0
- package/fastify/src/framework/maker-cli/provider/index.mjs +12 -0
- package/fastify/src/framework/maker-cli/runtime/build-ui.mjs +16 -0
- package/fastify/src/framework/maker-cli/runtime/core.mjs +560 -0
- package/fastify/src/framework/maker-cli/runtime/index.mjs +120 -0
- package/fastify/src/framework/maker-cli/runtime/redis-commander-patch.cjs +20 -0
- package/fastify/src/framework/maker-cli/stubs/controller/openapi.ts.stub +74 -0
- package/fastify/src/framework/maker-cli/stubs/controller/plain.ts.stub +77 -0
- package/fastify/src/framework/maker-cli/stubs/controller/standalone.ts.stub +51 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/Dockerfile.bun.stub +52 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/Dockerfile.pnpm.stub +55 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/Dockerfile.stub +51 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/Dockerfile.yarn.stub +55 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/README.stub +59 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/compose/mysql.server.stub +45 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/compose/postgres.server.stub +45 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/compose/sqlite.stub +45 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/env/mysql.server.stub +68 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/env/postgres.server.stub +68 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/env/sqlite.stub +67 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/scripts/auto-migrate.sh.stub +41 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/server/README.stub +78 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/server/compose/noredis.stub +120 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/server/compose/redis.dev.stub +133 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/server/compose/redis.stub +131 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/server/env/local.example.stub +11 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/server/env/noredis.stub +25 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/server/env/redis.stub +25 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/server/nginx-vhost/README.stub +16 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/server/nginx-vhost/app.example.com.stub +13 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/server/pgadmin/servers.stub +14 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/server/redis/redis.conf.stub +7 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/supervisor/noredis.stub +54 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/supervisor/redis.stub +70 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/workflow/local.json.stub +25 -0
- package/fastify/src/framework/maker-cli/stubs/deploy/workflow/remote.json.stub +24 -0
- package/fastify/src/framework/maker-cli/stubs/facade/name.ts.stub +12 -0
- package/fastify/src/framework/maker-cli/stubs/helper/name.ts.stub +14 -0
- package/fastify/src/framework/maker-cli/stubs/job/name.ts.stub +25 -0
- package/fastify/src/framework/maker-cli/stubs/middleware/name.local.ts.stub +15 -0
- package/fastify/src/framework/maker-cli/stubs/middleware/name.ts.stub +15 -0
- package/fastify/src/framework/maker-cli/stubs/model/name.mysql.ts.stub +10 -0
- package/fastify/src/framework/maker-cli/stubs/model/name.postgresql.ts.stub +10 -0
- package/fastify/src/framework/maker-cli/stubs/model/name.sqlite.ts.stub +10 -0
- package/fastify/src/framework/maker-cli/stubs/notification/controller.ts.stub +79 -0
- package/fastify/src/framework/maker-cli/stubs/notification/job.ts.stub +11 -0
- package/fastify/src/framework/maker-cli/stubs/notification/model.ts.stub +29 -0
- package/fastify/src/framework/maker-cli/stubs/notification/provider.ts.stub +174 -0
- package/fastify/src/framework/maker-cli/stubs/notification/route.api.ts.stub +71 -0
- package/fastify/src/framework/maker-cli/stubs/notification/route.plain.ts.stub +15 -0
- package/fastify/src/framework/maker-cli/stubs/notification/schema.plain.ts.stub +26 -0
- package/fastify/src/framework/maker-cli/stubs/notification/schema.ts.stub +26 -0
- package/fastify/src/framework/maker-cli/stubs/provider/module-barrel.ts.stub +19 -0
- package/fastify/src/framework/maker-cli/stubs/provider/module.ts.stub +21 -0
- package/fastify/src/framework/maker-cli/stubs/provider/name.ts.stub +21 -0
- package/fastify/src/framework/maker-cli/stubs/route/api.ts.stub +101 -0
- package/fastify/src/framework/maker-cli/stubs/route/plain.ts.stub +23 -0
- package/fastify/src/framework/maker-cli/stubs/route/standalone.api.ts.stub +120 -0
- package/fastify/src/framework/maker-cli/stubs/route/standalone.plain.ts.stub +50 -0
- package/fastify/src/framework/maker-cli/stubs/schedule/name.ts.stub +36 -0
- package/fastify/src/framework/maker-cli/stubs/schema/name.plain.ts.stub +14 -0
- package/fastify/src/framework/maker-cli/stubs/schema/name.ts.stub +33 -0
- package/fastify/src/framework/maker-cli/stubs/seeder/name.ts.stub +18 -0
- package/fastify/src/framework/maker-cli/stubs/seeder/standalone.ts.stub +22 -0
- package/fastify/src/framework/maker-cli/stubs/service/name.plain.ts.stub +67 -0
- package/fastify/src/framework/maker-cli/stubs/service/name.ts.stub +65 -0
- package/fastify/src/framework/maker-cli/stubs/test/unit.ts.stub +22 -0
- package/fastify/src/framework/maker-cli/stubs/type/name.ts.stub +22 -0
- package/fastify/src/framework/maker-cli/utils/env-config.mjs +44 -0
- package/fastify/src/framework/maker-cli/utils/env-db.mjs +61 -0
- package/fastify/src/framework/maker-cli/utils/file-ops.mjs +30 -0
- package/fastify/src/framework/maker-cli/utils/flags.mjs +14 -0
- package/fastify/src/framework/maker-cli/utils/help.mjs +379 -0
- package/fastify/src/framework/maker-cli/utils/naming.mjs +119 -0
- package/fastify/src/framework/maker-cli/utils/process.mjs +52 -0
- package/fastify/src/framework/maker-cli/utils/project-commands.mjs +201 -0
- package/fastify/src/framework/modules/discover.ts +57 -0
- package/fastify/src/framework/modules/routes.ts +31 -0
- package/fastify/src/framework/notification/channels/broadcast.ts +15 -0
- package/fastify/src/framework/notification/channels/mail.ts +42 -0
- package/fastify/src/framework/notification/contracts.ts +29 -0
- package/fastify/src/framework/notification/index.ts +59 -0
- package/fastify/src/framework/notification/registry.ts +15 -0
- package/fastify/src/framework/providers/adapter.ts +149 -0
- package/fastify/src/framework/providers/contracts.ts +126 -0
- package/fastify/src/framework/providers/loader.ts +105 -0
- package/fastify/src/framework/providers/registry.ts +88 -0
- package/fastify/src/framework/queue/clear.ts +22 -0
- package/fastify/src/framework/queue/queue.ts +401 -0
- package/fastify/src/framework/queue/ui.ts +194 -0
- package/fastify/src/framework/queue/worker.ts +61 -0
- package/fastify/src/framework/realtime/broadcast.ts +70 -0
- package/fastify/src/framework/realtime/index.ts +2 -0
- package/fastify/src/framework/realtime/socket-cookie.ts +72 -0
- package/fastify/src/framework/realtime/socket.ts +149 -0
- package/fastify/src/framework/realtime/types.ts +6 -0
- package/fastify/src/framework/realtime/ui.ts +16 -0
- package/fastify/src/framework/redis/client.ts +148 -0
- package/fastify/src/framework/runtime/runtime.ts +23 -0
- package/fastify/src/framework/runtime/server-adapter.ts +33 -0
- package/fastify/src/framework/scheduler/lock.ts +128 -0
- package/fastify/src/framework/scheduler/run.ts +48 -0
- package/fastify/src/framework/scheduler/scheduler.ts +155 -0
- package/fastify/src/framework/server.ts +162 -0
- package/fastify/src/framework/session/session.ts +160 -0
- package/fastify/src/framework/storage/storage.ts +744 -0
- package/fastify/src/framework/support/cookie.ts +71 -0
- package/fastify/src/framework/support/jwt.ts +33 -0
- package/fastify/src/framework/support/lifecycle.ts +35 -0
- package/fastify/src/framework/support/logger.ts +148 -0
- package/fastify/src/framework/support/mail.ts +56 -0
- package/fastify/src/framework/support/password.ts +23 -0
- package/fastify/src/framework/support/status-codes.ts +75 -0
- package/fastify/src/framework/support/upload.ts +181 -0
- package/fastify/src/framework/support/url.ts +41 -0
- package/fastify/src/middlewares/auth-middleware.ts +134 -0
- package/fastify/src/middlewares/role-middleware.ts +19 -0
- package/fastify/src/modules/auth/__tests__/forgetpass.test.ts +200 -0
- package/fastify/src/modules/auth/__tests__/login.test.ts +171 -0
- package/fastify/src/modules/auth/__tests__/logout.test.ts +60 -0
- package/fastify/src/modules/auth/__tests__/logoutalldevices.test.ts +49 -0
- package/fastify/src/modules/auth/__tests__/me.test.ts +91 -0
- package/fastify/src/modules/auth/__tests__/refreshtoken.test.ts +305 -0
- package/fastify/src/modules/auth/__tests__/register.test.ts +291 -0
- package/fastify/src/modules/auth/__tests__/resetpass.test.ts +206 -0
- package/fastify/src/modules/auth/__tests__/verifyemail.test.ts +183 -0
- package/fastify/src/modules/auth/controllers/auth.ts +252 -0
- package/fastify/src/modules/auth/controllers/role.ts +35 -0
- package/fastify/src/modules/auth/database/models/role.ts +14 -0
- package/fastify/src/modules/auth/database/models/user.ts +46 -0
- package/fastify/src/modules/auth/database/seeders/role.ts +18 -0
- package/fastify/src/modules/auth/database/seeders/user.ts +35 -0
- package/fastify/src/modules/auth/facade.ts +12 -0
- package/fastify/src/modules/auth/helpers/auth.ts +115 -0
- package/fastify/src/modules/auth/jobs/forgetpass.ts +20 -0
- package/fastify/src/modules/auth/jobs/registeruser.ts +23 -0
- package/fastify/src/modules/auth/jobs/verifyemail.ts +18 -0
- package/fastify/src/modules/auth/routes/index.ts +185 -0
- package/fastify/src/modules/auth/routes/role.ts +56 -0
- package/fastify/src/modules/auth/schemas/auth.ts +116 -0
- package/fastify/src/modules/auth/services/forgetpassword.ts +66 -0
- package/fastify/src/modules/auth/services/login.ts +63 -0
- package/fastify/src/modules/auth/services/logout.ts +25 -0
- package/fastify/src/modules/auth/services/logoutalldevices.ts +19 -0
- package/fastify/src/modules/auth/services/me.ts +31 -0
- package/fastify/src/modules/auth/services/refreshtoken.ts +110 -0
- package/fastify/src/modules/auth/services/register.ts +141 -0
- package/fastify/src/modules/auth/services/resetpassword.ts +69 -0
- package/fastify/src/modules/auth/services/verifyemail.ts +77 -0
- package/fastify/src/modules/auth/types/auth.ts +31 -0
- package/fastify/src/providers/README.md +107 -0
- package/fastify/src/providers/notification.ts +4 -0
- package/fastify/src/resources/index.html +17 -0
- package/fastify/src/resources/src/App.vue +5 -0
- package/fastify/src/resources/src/assets/css/styles.css +14761 -0
- package/fastify/src/resources/src/assets/css/styles.css.map +1 -0
- package/fastify/src/resources/src/assets/fonts/public-sans/public-sans-latin-ext.woff2 +0 -0
- package/fastify/src/resources/src/assets/fonts/public-sans/public-sans-latin.woff2 +0 -0
- package/fastify/src/resources/src/assets/images/favicon/favicon.ico +0 -0
- package/fastify/src/resources/src/assets/images/logo.png +0 -0
- package/fastify/src/resources/src/assets/scss/custom.css +217 -0
- package/fastify/src/resources/src/assets/scss/custom.css.map +1 -0
- package/fastify/src/resources/src/assets/scss/custom.scss +1346 -0
- package/fastify/src/resources/src/components/Button.vue +32 -0
- package/fastify/src/resources/src/components/Checkbox.vue +32 -0
- package/fastify/src/resources/src/components/Datepicker.vue +106 -0
- package/fastify/src/resources/src/components/FeatureButton.vue +53 -0
- package/fastify/src/resources/src/components/FloatButton.vue +33 -0
- package/fastify/src/resources/src/components/Href.vue +32 -0
- package/fastify/src/resources/src/components/Input.vue +243 -0
- package/fastify/src/resources/src/components/InputGroup.vue +147 -0
- package/fastify/src/resources/src/components/InputPasswordToggle.vue +229 -0
- package/fastify/src/resources/src/components/Modal.vue +71 -0
- package/fastify/src/resources/src/components/Pagebar.vue +26 -0
- package/fastify/src/resources/src/components/Refresh.vue +19 -0
- package/fastify/src/resources/src/components/Select.vue +384 -0
- package/fastify/src/resources/src/components/Spinner.vue +42 -0
- package/fastify/src/resources/src/components/Switch.vue +60 -0
- package/fastify/src/resources/src/components/TextArea.vue +117 -0
- package/fastify/src/resources/src/components/Toast.vue +56 -0
- package/fastify/src/resources/src/components/datatable/DataTableSkeleton.vue +98 -0
- package/fastify/src/resources/src/components/datatable/Pagination.vue +147 -0
- package/fastify/src/resources/src/components/datatable/SelectOpption.vue +64 -0
- package/fastify/src/resources/src/components/datatable/index.vue +199 -0
- package/fastify/src/resources/src/composables/useAuth.ts +52 -0
- package/fastify/src/resources/src/helpers/assets.ts +51 -0
- package/fastify/src/resources/src/helpers/nformatter.ts +10 -0
- package/fastify/src/resources/src/helpers/utils.ts +75 -0
- package/fastify/src/resources/src/layouts/AuthLayout.vue +20 -0
- package/fastify/src/resources/src/layouts/Layout/Footer.vue +23 -0
- package/fastify/src/resources/src/layouts/Layout/Header.vue +124 -0
- package/fastify/src/resources/src/layouts/Layout/Sidebar.vue +137 -0
- package/fastify/src/resources/src/layouts/Layout/index.vue +87 -0
- package/fastify/src/resources/src/main.ts +28 -0
- package/fastify/src/resources/src/pages/auth/forgetPassword.vue +76 -0
- package/fastify/src/resources/src/pages/auth/login.vue +96 -0
- package/fastify/src/resources/src/pages/auth/register.vue +129 -0
- package/fastify/src/resources/src/pages/auth/resetPassword.vue +110 -0
- package/fastify/src/resources/src/pages/auth/verifyEmail.vue +64 -0
- package/fastify/src/resources/src/pages/dashboard/index.vue +66 -0
- package/fastify/src/resources/src/plugins/axios.ts +31 -0
- package/fastify/src/resources/src/plugins/browserDetect.ts +55 -0
- package/fastify/src/resources/src/plugins/dialog.ts +216 -0
- package/fastify/src/resources/src/plugins/gum.ts +683 -0
- package/fastify/src/resources/src/plugins/pulse.ts +157 -0
- package/fastify/src/resources/src/plugins/routeProgress.ts +87 -0
- package/fastify/src/resources/src/router/index.ts +85 -0
- package/fastify/src/resources/src/stores/admin-ui.ts +148 -0
- package/fastify/src/resources/src/stores/auth.ts +187 -0
- package/fastify/src/resources/tsconfig.json +19 -0
- package/fastify/src/resources/vite.config.ts +64 -0
- package/fastify/src/storage/app/private/.gitkeep +0 -0
- package/fastify/src/storage/app/public/.gitkeep +0 -0
- package/fastify/src/storage/app/tmp/generated/.gitkeep +0 -0
- package/fastify/src/storage/database/nexwire.sqlite +0 -0
- package/fastify/src/storage/logs/.gitkeep +0 -0
- package/fastify/src/storage/logs/app.log +0 -0
- package/fastify/src/storage/logs/fatal.log +0 -0
- package/fastify/src/storage/tmp/.gitkeep +0 -0
- package/fastify/src/storage/trash/modules/.gitkeep +0 -0
- package/fastify/src/types/global.d.ts +32 -0
- package/fastify/tsconfig.json +19 -0
- package/fastify/vitest.config.ts +38 -0
- package/hono/src/framework/facade.ts +1 -0
- package/hono/src/framework/http/logger.ts +86 -81
- package/hono/src/framework/maker-cli/runtime/core.mjs +1 -2
- package/hono/src/framework/notification/channels/mail.ts +8 -6
- package/hono/src/middlewares/auth-middleware.ts +1 -2
- package/hono/src/middlewares/role-middleware.ts +3 -13
- package/hono/src/modules/auth/routes/index.ts +1 -2
- package/hono/src/providers/notification.ts +2 -2
- package/hono/src/resources/src/components/Switch.vue +60 -65
- package/hono/src/resources/src/pages/auth/forgetPassword.vue +23 -29
- package/hono/src/resources/src/pages/auth/login.vue +26 -29
- package/hono/src/resources/src/pages/auth/register.vue +36 -40
- package/hono/src/resources/src/pages/auth/resetPassword.vue +29 -37
- package/hono/src/resources/src/pages/auth/verifyEmail.vue +15 -19
- package/hono/src/resources/src/plugins/gum.ts +519 -203
- package/hono/src/resources/src/stores/auth.ts +9 -6
- package/hono/src/storage/database/nexwire.sqlite +0 -0
- package/package.json +3 -2
- package/src/index.js +11 -8
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
2
|
+
import fsSync from "node:fs";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import http from "node:http";
|
|
5
|
+
import net from "node:net";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { glob } from "glob";
|
|
9
|
+
import { readCliConfig } from "../utils/env-config.mjs";
|
|
10
|
+
import { getOption, hasFlag } from "../utils/flags.mjs";
|
|
11
|
+
import { localBin, packageScript, runCommand, runNodeScript } from "../utils/process.mjs";
|
|
12
|
+
|
|
13
|
+
/** Parse --with/--view/--viewer flags to determine which dev tools to launch.
|
|
14
|
+
* npm swallows --view/--with into its own config and re-exposes them as
|
|
15
|
+
* npm_config_viewer/npm_config_with env vars, so read those too. */
|
|
16
|
+
function parseWithOptions(flags = []) {
|
|
17
|
+
const supported = new Set(["redis", "maildev", "studio"]);
|
|
18
|
+
const selected = new Set();
|
|
19
|
+
|
|
20
|
+
const candidates = [
|
|
21
|
+
getOption(flags, "--with"),
|
|
22
|
+
getOption(flags, "--view"),
|
|
23
|
+
getOption(flags, "--viewer"),
|
|
24
|
+
process.env.npm_config_with,
|
|
25
|
+
process.env.npm_config_view,
|
|
26
|
+
process.env.npm_config_viewer
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
for (const raw of candidates) {
|
|
30
|
+
if (!raw) continue;
|
|
31
|
+
for (const item of raw.split(/[,\s]+/)) {
|
|
32
|
+
const key = item.trim().toLowerCase();
|
|
33
|
+
if (!key) continue;
|
|
34
|
+
|
|
35
|
+
if (!supported.has(key)) {
|
|
36
|
+
console.warn(`[dev] Unknown --with option '${key}' (supported: redis, maildev, studio)`);
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
selected.add(key);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return selected;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Build a list of dev UI tools to launch based on flags and with-options. */
|
|
48
|
+
function devViewList(flags = [], withOptions = new Set()) {
|
|
49
|
+
const selected = new Set(withOptions);
|
|
50
|
+
if (hasFlag(flags, "--with-redis-view")) selected.add("redis");
|
|
51
|
+
if (hasFlag(flags, "--with-maildev")) selected.add("maildev");
|
|
52
|
+
if (hasFlag(flags, "--with-db-studio")) selected.add("studio");
|
|
53
|
+
return Array.from(selected);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Resolve the Redis endpoint from REDIS_URL (falls back to local default). */
|
|
57
|
+
function redisEndpoint() {
|
|
58
|
+
const redisUrl = new URL(process.env.REDIS_URL || "redis://127.0.0.1:6379");
|
|
59
|
+
return {
|
|
60
|
+
url: process.env.REDIS_URL || "redis://127.0.0.1:6379",
|
|
61
|
+
hostname: redisUrl.hostname || "127.0.0.1",
|
|
62
|
+
port: redisUrl.port || "6379",
|
|
63
|
+
password: redisUrl.password ? decodeURIComponent(redisUrl.password) : ""
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Build redis-commander CLI arguments from REDIS_URL env. */
|
|
68
|
+
function redisConnectionArgs() {
|
|
69
|
+
const endpoint = redisEndpoint();
|
|
70
|
+
const result = [
|
|
71
|
+
"--port",
|
|
72
|
+
String(readCliConfig().commanderPort || process.env.REDIS_COMMANDER_PORT || "1369"),
|
|
73
|
+
"--redis-host",
|
|
74
|
+
endpoint.hostname,
|
|
75
|
+
"--redis-port",
|
|
76
|
+
endpoint.port
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
if (endpoint.password) result.push("--redis-password", endpoint.password);
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Commander (redis view) port, resolved the same way for every consumer. */
|
|
84
|
+
function commanderPort() {
|
|
85
|
+
return String(readCliConfig().commanderPort || process.env.REDIS_COMMANDER_PORT || "1369");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Why: redis-commander only serves its own UI once it has a live Redis socket.
|
|
90
|
+
* When Redis is down it either exits or renders a raw connection error, and
|
|
91
|
+
* when the commander was never spawned the port is simply closed -- the
|
|
92
|
+
* browser shows "unable to connect" with no hint about the cause.
|
|
93
|
+
* When: Before launching the redis view, and when REDIS is disabled in env.
|
|
94
|
+
* Where: Dev tool bootstrap (`redis:view`).
|
|
95
|
+
* How: Serves a Redis Commander diagnostic on the commander port. This is
|
|
96
|
+
* deliberately NOT the bull-board page from `framework/queue/ui.ts`: that
|
|
97
|
+
* one is about queue/job state on `/queues`, this one is the standalone
|
|
98
|
+
* Redis key browser, so it names Redis Commander and the key browser
|
|
99
|
+
* rather than the queue dashboard.
|
|
100
|
+
*/
|
|
101
|
+
function serveRedisUnavailablePage(port, endpoint) {
|
|
102
|
+
const body = `<!doctype html>
|
|
103
|
+
<html>
|
|
104
|
+
<head>
|
|
105
|
+
<title>Redis Commander Unavailable</title>
|
|
106
|
+
<style>
|
|
107
|
+
body { font-family: system-ui, sans-serif; padding: 48px; text-align: center; }
|
|
108
|
+
h1 { color: #dc2626; }
|
|
109
|
+
code { background: #f3f4f6; padding: 2px 6px; border-radius: 4px; }
|
|
110
|
+
.note { color: #6b7280; font-size: 14px; }
|
|
111
|
+
</style>
|
|
112
|
+
</head>
|
|
113
|
+
<body>
|
|
114
|
+
<h1>Redis Commander Unavailable</h1>
|
|
115
|
+
<p>Redis is not connected, so the Redis key browser cannot start.</p>
|
|
116
|
+
<p>Redis URL: <code>${endpoint.url}</code></p>
|
|
117
|
+
<p>Start Redis, then run <code>npm run maker redis:view</code> again.</p>
|
|
118
|
+
<p class="note">This port serves Redis Commander (browse Redis keys).<br>Queue and job state lives on the app's own dashboard at <code>/queues</code>.</p>
|
|
119
|
+
</body>
|
|
120
|
+
</html>
|
|
121
|
+
`;
|
|
122
|
+
|
|
123
|
+
return new Promise((_resolve, reject) => {
|
|
124
|
+
const server = http.createServer((_req, res) => {
|
|
125
|
+
res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
126
|
+
res.end(body);
|
|
127
|
+
});
|
|
128
|
+
server.once("error", reject);
|
|
129
|
+
server.listen(Number(port), () => {
|
|
130
|
+
console.log(`[redis-view] Redis is not reachable — serving the unavailable page on http://localhost:${port}`);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Poll the API health endpoint until it responds or retries exhausted. */
|
|
136
|
+
async function waitForHealth(url, maxRetries = 240, intervalMs = 500) {
|
|
137
|
+
let warned = false;
|
|
138
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
139
|
+
try {
|
|
140
|
+
const res = await fetch(url);
|
|
141
|
+
if (res.ok) return;
|
|
142
|
+
} catch {}
|
|
143
|
+
if (!warned) {
|
|
144
|
+
warned = true;
|
|
145
|
+
console.log(`Waiting for API health (${url})...`);
|
|
146
|
+
}
|
|
147
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
148
|
+
}
|
|
149
|
+
throw new Error(`API health-check failed after ${maxRetries} attempts (${url})`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Wait for the API to be reachable before launching the Vite dev server.
|
|
153
|
+
* Why: the UI boots with /api/auth/me and socket.io requests immediately;
|
|
154
|
+
* if the backend is not up yet the browser logs ws and API errors. Applies to
|
|
155
|
+
* ui:dev/admin:dev started on their own (bun run dev already waits for
|
|
156
|
+
* health before spawning the UI). Set SKIP_BACKEND_WAIT=1 to disable. */
|
|
157
|
+
export async function waitForBackendIfNeeded() {
|
|
158
|
+
if (process.env.SKIP_BACKEND_WAIT === "1") return;
|
|
159
|
+
const healthUrl = `${process.env.APP_URL || "http://localhost:3000"}/health`;
|
|
160
|
+
await waitForHealth(healthUrl);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Run the full dev stack: API, UI, queue worker, and optional UI tools. */
|
|
164
|
+
async function runDevStack(flags = []) {
|
|
165
|
+
const cliConfig = readCliConfig();
|
|
166
|
+
const commands = [{ label: "api", args: ["serve", "--src"], required: true, hint: "http://localhost:3000" }];
|
|
167
|
+
|
|
168
|
+
if (process.env.UI !== "false" && process.env.UI !== "0") {
|
|
169
|
+
commands.push({
|
|
170
|
+
label: "UI",
|
|
171
|
+
args: ["ui:dev"],
|
|
172
|
+
required: true,
|
|
173
|
+
hint: "http://localhost:5173"
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (process.env.REDIS !== "false" && process.env.REDIS !== "0") {
|
|
178
|
+
commands.push({
|
|
179
|
+
label: "queue-worker",
|
|
180
|
+
args: ["queue:work"],
|
|
181
|
+
required: false,
|
|
182
|
+
hint: "BullMQ worker"
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const withOptions = parseWithOptions(flags);
|
|
187
|
+
const views = devViewList(flags, withOptions);
|
|
188
|
+
// Keep launching the view when it was explicitly asked for, even with Redis
|
|
189
|
+
// disabled: the view then serves the "Redis is not connected" page instead of
|
|
190
|
+
// leaving the advertised URL as a dead port.
|
|
191
|
+
const enableRedisView = hasFlag(flags, "--with-redis-view") || withOptions.has("redis");
|
|
192
|
+
const enableMaildev = hasFlag(flags, "--with-maildev") || withOptions.has("maildev");
|
|
193
|
+
const enableDbStudio = hasFlag(flags, "--with-db-studio") || withOptions.has("studio");
|
|
194
|
+
|
|
195
|
+
if (enableRedisView) {
|
|
196
|
+
commands.push({
|
|
197
|
+
label: "redis-view",
|
|
198
|
+
args: ["redis:view", "--quiet"],
|
|
199
|
+
required: false,
|
|
200
|
+
hint: `http://localhost:${commanderPort()}`
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
if (enableMaildev) {
|
|
204
|
+
commands.push({
|
|
205
|
+
label: "maildev-view",
|
|
206
|
+
args: ["maildev:view", "--quiet"],
|
|
207
|
+
required: false,
|
|
208
|
+
hint: `http://localhost:${cliConfig.maildev?.webPort || 1080}`
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
if (enableDbStudio) {
|
|
212
|
+
commands.push({
|
|
213
|
+
label: "db-studio",
|
|
214
|
+
args: ["db:studio", "--quiet"],
|
|
215
|
+
required: false,
|
|
216
|
+
hint: "https://local.drizzle.studio"
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const children = [];
|
|
221
|
+
let shuttingDown = false;
|
|
222
|
+
let settled = false;
|
|
223
|
+
|
|
224
|
+
const killTree = (child) => {
|
|
225
|
+
if (!child || child.killed) return;
|
|
226
|
+
try {
|
|
227
|
+
if (process.platform === "win32" && child.pid) {
|
|
228
|
+
spawnSync("taskkill", ["/PID", String(child.pid), "/T", "/F"], { stdio: "ignore" });
|
|
229
|
+
} else {
|
|
230
|
+
child.kill("SIGTERM");
|
|
231
|
+
}
|
|
232
|
+
} catch {}
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const killAll = () => {
|
|
236
|
+
for (const child of children) killTree(child);
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const shutdown = () => {
|
|
240
|
+
if (shuttingDown) return;
|
|
241
|
+
shuttingDown = true;
|
|
242
|
+
killAll();
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
process.on("SIGINT", shutdown);
|
|
246
|
+
process.on("SIGTERM", shutdown);
|
|
247
|
+
|
|
248
|
+
const apiCmd = commands.find((c) => c.label === "api");
|
|
249
|
+
const rest = commands.filter((c) => c.label !== "api");
|
|
250
|
+
|
|
251
|
+
await new Promise((resolve, reject) => {
|
|
252
|
+
const handleChildExit = (child, command) => {
|
|
253
|
+
child.on("exit", (code, signal) => {
|
|
254
|
+
if (settled) return;
|
|
255
|
+
if (!shuttingDown && (code !== null || signal !== null)) {
|
|
256
|
+
if (command.required) {
|
|
257
|
+
settled = true;
|
|
258
|
+
shutdown();
|
|
259
|
+
reject(new Error(`${command.label} exited (${signal || code})`));
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
if (code !== 0 || signal) {
|
|
263
|
+
console.warn(`[dev] Optional process '${command.label}' exited (${signal || code}); continuing`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const allExited = children.every((item) => item.exitCode !== null || item.signalCode !== null);
|
|
268
|
+
if (allExited) {
|
|
269
|
+
settled = true;
|
|
270
|
+
resolve();
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
const run = async () => {
|
|
276
|
+
const apiChild = spawn(process.execPath, [process.argv[1], ...apiCmd.args], {
|
|
277
|
+
stdio: "inherit",
|
|
278
|
+
env: {
|
|
279
|
+
...process.env,
|
|
280
|
+
NEXWIRE_DEV_VIEWS: views.join(","),
|
|
281
|
+
NEXWIRE_FRONTEND_URL: "http://localhost:5173"
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
children.push(apiChild);
|
|
285
|
+
|
|
286
|
+
handleChildExit(apiChild, apiCmd);
|
|
287
|
+
apiChild.on("error", (error) => {
|
|
288
|
+
if (settled) return;
|
|
289
|
+
settled = true;
|
|
290
|
+
shutdown();
|
|
291
|
+
reject(error);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
try {
|
|
295
|
+
await waitForHealth("http://localhost:3000/health");
|
|
296
|
+
} catch (err) {
|
|
297
|
+
settled = true;
|
|
298
|
+
shutdown();
|
|
299
|
+
reject(err);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
for (const command of rest) {
|
|
304
|
+
const child = spawn(process.execPath, [process.argv[1], ...command.args], {
|
|
305
|
+
stdio: command.label === "queue-worker" ? "inherit" : ["ignore", "ignore", "inherit"],
|
|
306
|
+
env: { ...process.env }
|
|
307
|
+
});
|
|
308
|
+
children.push(child);
|
|
309
|
+
|
|
310
|
+
handleChildExit(child, command);
|
|
311
|
+
|
|
312
|
+
child.on("error", (error) => {
|
|
313
|
+
if (settled) return;
|
|
314
|
+
settled = true;
|
|
315
|
+
shutdown();
|
|
316
|
+
reject(error);
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
run();
|
|
322
|
+
}).finally(() => {
|
|
323
|
+
process.off("SIGINT", shutdown);
|
|
324
|
+
process.off("SIGTERM", shutdown);
|
|
325
|
+
killAll();
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/** Build the runtime-appropriate command/args for a built dist entrypoint. */
|
|
330
|
+
function runtimeEntryCommand(runtime, entry, runtimeArgs = []) {
|
|
331
|
+
if (runtime === "bun") return { command: "bun", args: [entry, ...runtimeArgs] };
|
|
332
|
+
return { command: process.execPath, args: [entry, ...runtimeArgs] };
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** Run a runtime command: dev, serve, queue:work, queue:clear, schedule:work. */
|
|
336
|
+
export async function runRuntime(commandName, rawArgs = []) {
|
|
337
|
+
const prod = rawArgs.includes("--prod");
|
|
338
|
+
const forceSrc = rawArgs.includes("--src");
|
|
339
|
+
const watch = rawArgs.includes("--watch");
|
|
340
|
+
const runtimeArg = rawArgs.find((arg) => arg.startsWith("--runtime="));
|
|
341
|
+
const runtime = runtimeArg ? runtimeArg.split("=")[1].trim().toLowerCase() : "node";
|
|
342
|
+
|
|
343
|
+
if (!["node", "bun"].includes(runtime)) {
|
|
344
|
+
throw new Error(`Invalid runtime '${runtime}'. Supported values: node, bun`);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const runtimeArgs = rawArgs.filter((arg, i) => i > 0 && !arg.startsWith("--runtime=") && arg !== "--prod" && arg !== "--src");
|
|
348
|
+
const hasDistServer = fsSync.existsSync(path.resolve(process.cwd(), "dist/src/framework/server.js"));
|
|
349
|
+
const hasDistWorker = fsSync.existsSync(path.resolve(process.cwd(), "dist/src/framework/queue/worker.js"));
|
|
350
|
+
const hasDistScheduler = fsSync.existsSync(path.resolve(process.cwd(), "dist/src/framework/scheduler/run.js"));
|
|
351
|
+
|
|
352
|
+
if (commandName === "dev") {
|
|
353
|
+
await runDevStack(rawArgs.slice(1));
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (commandName === "serve") {
|
|
358
|
+
if (!forceSrc && (prod || hasDistServer)) {
|
|
359
|
+
const entry = path.resolve(process.cwd(), "dist/src/framework/server.js");
|
|
360
|
+
const { command, args } = runtimeEntryCommand(runtime, entry, runtimeArgs);
|
|
361
|
+
await runCommand(command, args);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const useWatch = watch || (!prod && forceSrc);
|
|
366
|
+
const tsxWatchIgnore = [
|
|
367
|
+
"--include",
|
|
368
|
+
"src/**/*.ts",
|
|
369
|
+
"--include",
|
|
370
|
+
"src/framework/maker-cli/**/*.mjs",
|
|
371
|
+
"--exclude",
|
|
372
|
+
"src/storage/**",
|
|
373
|
+
"--exclude",
|
|
374
|
+
"src/storage/logs/**",
|
|
375
|
+
"--exclude",
|
|
376
|
+
"src/storage/tmp/**",
|
|
377
|
+
"--exclude",
|
|
378
|
+
"src/database/migrations/**",
|
|
379
|
+
"--exclude",
|
|
380
|
+
"src/database/schema.ts",
|
|
381
|
+
"--exclude",
|
|
382
|
+
"public/**",
|
|
383
|
+
"--exclude",
|
|
384
|
+
"dist/**",
|
|
385
|
+
"--exclude",
|
|
386
|
+
".git/**",
|
|
387
|
+
"--exclude",
|
|
388
|
+
"node_modules/**",
|
|
389
|
+
"--exclude",
|
|
390
|
+
"**/.DS_Store",
|
|
391
|
+
"--exclude",
|
|
392
|
+
"**/Thumbs.db",
|
|
393
|
+
"--exclude",
|
|
394
|
+
"**/*.log"
|
|
395
|
+
];
|
|
396
|
+
const tsxArgs = useWatch
|
|
397
|
+
? ["watch", ...tsxWatchIgnore, "src/framework/server.ts", ...runtimeArgs]
|
|
398
|
+
: ["src/framework/server.ts", ...runtimeArgs];
|
|
399
|
+
await runCommand(localBin("tsx"), tsxArgs);
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (commandName === "queue:work") {
|
|
404
|
+
if (!forceSrc && (prod || hasDistWorker)) {
|
|
405
|
+
const entry = path.resolve(process.cwd(), "dist/src/framework/queue/worker.js");
|
|
406
|
+
const { command, args } = runtimeEntryCommand(runtime, entry, runtimeArgs);
|
|
407
|
+
await runCommand(command, args);
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
await runNodeScript(packageScript("tsx", "dist/cli.mjs"), ["src/framework/queue/worker.ts", ...rawArgs.slice(1)]);
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (commandName === "queue:clear") {
|
|
416
|
+
await runNodeScript(packageScript("tsx", "dist/cli.mjs"), ["src/framework/queue/clear.ts"]);
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (commandName === "schedule:work") {
|
|
421
|
+
if (!forceSrc && (prod || hasDistScheduler)) {
|
|
422
|
+
const entry = path.resolve(process.cwd(), "dist/src/framework/scheduler/run.js");
|
|
423
|
+
const { command, args } = runtimeEntryCommand(runtime, entry, runtimeArgs);
|
|
424
|
+
await runCommand(command, args);
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
await runNodeScript(packageScript("tsx", "dist/cli.mjs"), ["src/framework/scheduler/run.ts", ...rawArgs.slice(1)]);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Why: Deciding whether to launch redis-commander needs to know if Redis is
|
|
434
|
+
* actually accepting connections, which REDIS=true does not guarantee.
|
|
435
|
+
* When: Before spawning the redis view.
|
|
436
|
+
* Where: Dev tool bootstrap.
|
|
437
|
+
* How: Opens a short-lived TCP socket to the configured host/port.
|
|
438
|
+
*/
|
|
439
|
+
function isRedisReachable(endpoint, timeoutMs = 1500) {
|
|
440
|
+
return new Promise((resolve) => {
|
|
441
|
+
const socket = net.connect({ host: endpoint.hostname, port: Number(endpoint.port) });
|
|
442
|
+
let settled = false;
|
|
443
|
+
const done = (ok) => {
|
|
444
|
+
if (settled) return;
|
|
445
|
+
settled = true;
|
|
446
|
+
socket.destroy();
|
|
447
|
+
resolve(ok);
|
|
448
|
+
};
|
|
449
|
+
socket.setTimeout(timeoutMs);
|
|
450
|
+
socket.once("connect", () => done(true));
|
|
451
|
+
socket.once("timeout", () => done(false));
|
|
452
|
+
socket.once("error", () => done(false));
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/** Launch a UI tool: maildev or redis-commander. */
|
|
457
|
+
export async function runUi(commandName) {
|
|
458
|
+
if (commandName === "maildev:view") {
|
|
459
|
+
const maildev = readCliConfig().maildev || {};
|
|
460
|
+
await runCommand(localBin("maildev"), [
|
|
461
|
+
"--smtp",
|
|
462
|
+
String(maildev.smtpPort || process.env.MAIL_PORT || process.env.MAILDEV_SMTP_PORT || "1025"),
|
|
463
|
+
"--web",
|
|
464
|
+
String(maildev.webPort || process.env.MAILDEV_WEB_PORT || "1080")
|
|
465
|
+
]);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if (commandName === "redis:view") {
|
|
470
|
+
const endpoint = redisEndpoint();
|
|
471
|
+
|
|
472
|
+
if (process.env.REDIS === "false" || process.env.REDIS === "0") {
|
|
473
|
+
console.log("[redis-view] REDIS is disabled in .env — Redis Commander will not connect.");
|
|
474
|
+
await serveRedisUnavailablePage(commanderPort(), endpoint);
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (!(await isRedisReachable(endpoint))) {
|
|
479
|
+
console.log(`[redis-view] No Redis server responded at ${endpoint.hostname}:${endpoint.port}.`);
|
|
480
|
+
await serveRedisUnavailablePage(commanderPort(), endpoint);
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const patch = fileURLToPath(new URL("./redis-commander-patch.cjs", import.meta.url));
|
|
485
|
+
await runCommand(process.execPath, [
|
|
486
|
+
"--require",
|
|
487
|
+
patch,
|
|
488
|
+
packageScript("redis-commander", "bin/redis-commander.js"),
|
|
489
|
+
...redisConnectionArgs()
|
|
490
|
+
]);
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/** Start Vite dev server for a specific config file. */
|
|
496
|
+
export async function runVite(configPath) {
|
|
497
|
+
await waitForBackendIfNeeded();
|
|
498
|
+
await runCommand(localBin("vite"), ["--config", configPath]);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Strip the argument separator npm inserts.
|
|
503
|
+
*
|
|
504
|
+
* `npm run maker test -- posts` reaches us as `test posts`, but
|
|
505
|
+
* `npm run maker test -- -- posts` reaches us as `test -- posts`, because npm
|
|
506
|
+
* eats one separator and forwards the other. Vitest reads a bare `--` as a
|
|
507
|
+
* positional filter that matches no test file, which silently runs the whole
|
|
508
|
+
* suite instead of the one you asked for - so the command appears to work while
|
|
509
|
+
* doing something else. Dropping a leading `--` makes both spellings behave the
|
|
510
|
+
* same, and makes the passthrough behave identically across package managers.
|
|
511
|
+
*/
|
|
512
|
+
function vitestArgs(rawArgs) {
|
|
513
|
+
return rawArgs[0] === "--" ? rawArgs.slice(1) : rawArgs;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/** Run Vitest tests once (backend). */
|
|
517
|
+
export async function runTest(rawArgs = []) {
|
|
518
|
+
// "run" is required. Bare `vitest` is watch mode, which never exits - so
|
|
519
|
+
// without this, `maker test` hung in any interactive terminal while CI and
|
|
520
|
+
// piped runs exited normally, and the two looked identical from a log.
|
|
521
|
+
await runCommand(localBin("vitest"), ["run", ...vitestArgs(rawArgs)]);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/** Run Vitest in watch mode (backend). */
|
|
525
|
+
export async function runTestWatch(rawArgs = []) {
|
|
526
|
+
await runCommand(localBin("vitest"), vitestArgs(rawArgs));
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/** Run Vitest once with coverage (backend). */
|
|
530
|
+
export async function runTestCoverage(rawArgs = []) {
|
|
531
|
+
await runCommand(localBin("vitest"), ["run", "--coverage", ...vitestArgs(rawArgs)]);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/** Run Vitest in UI mode (backend). */
|
|
535
|
+
export async function runTestUI(rawArgs = []) {
|
|
536
|
+
await runCommand(localBin("vitest"), ["--ui", ...vitestArgs(rawArgs)]);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/** Clear Vite cache directories. */
|
|
540
|
+
export async function clearViteCache() {
|
|
541
|
+
const dirs = [path.resolve(process.cwd(), "node_modules/.vite"), path.resolve(process.cwd(), "src/resources/node_modules/.vite")];
|
|
542
|
+
|
|
543
|
+
for (const dir of dirs) {
|
|
544
|
+
await fs.rm(dir, { recursive: true, force: true }).catch(() => {});
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const strayCaches = await glob("**/.vite", {
|
|
548
|
+
cwd: process.cwd(),
|
|
549
|
+
nodir: false,
|
|
550
|
+
ignore: ["**/node_modules/**/.vite/**", "**/.git/**"],
|
|
551
|
+
windowsPathsNoEscape: true
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
for (const rel of strayCaches) {
|
|
555
|
+
const fullPath = path.resolve(process.cwd(), rel);
|
|
556
|
+
await fs.rm(fullPath, { recursive: true, force: true }).catch(() => {});
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
console.log("Vite cache cleared.");
|
|
560
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { clearViteCache, runRuntime, runTest, runTestCoverage, runTestUI, runTestWatch, runUi, runVite } from "./core.mjs";
|
|
2
|
+
import { initProjectCommands, PROJECT_COMMANDS_PATH } from "../utils/project-commands.mjs";
|
|
3
|
+
|
|
4
|
+
/** Register runtime commands (dev, serve, queue, schedule, UI tools) on the CLI program. */
|
|
5
|
+
export function registerRuntimeCommands(program, rawArgs) {
|
|
6
|
+
program
|
|
7
|
+
.command("dev")
|
|
8
|
+
.description("Start API + Vue 3 UI plus optional workers/tools")
|
|
9
|
+
.option("--view <tools>", "Comma-separated UI tools to launch (redis,maildev,studio)")
|
|
10
|
+
.option("--viewer <tools>", "Alias for --view (npm expands --view to --viewer)")
|
|
11
|
+
.option("--with <tools>", "Alias for --view")
|
|
12
|
+
.option("--with-redis-view", "Launch Redis Commander")
|
|
13
|
+
.option("--with-maildev", "Launch MailDev")
|
|
14
|
+
.option("--with-db-studio", "Launch Drizzle Studio")
|
|
15
|
+
.allowUnknownOption(true)
|
|
16
|
+
.action(async () => runRuntime("dev", rawArgs));
|
|
17
|
+
program
|
|
18
|
+
.command("serve")
|
|
19
|
+
.description("Start HTTP server (dist first if built, else src)")
|
|
20
|
+
.option("--prod", "Run production build")
|
|
21
|
+
.option("--runtime <name>", "Runtime: node or bun", "node")
|
|
22
|
+
.option("--watch", "Watch for file changes (src mode)")
|
|
23
|
+
.option("--src", "Force source mode even if dist exists")
|
|
24
|
+
.allowUnknownOption(true)
|
|
25
|
+
.action(async () => runRuntime("serve", rawArgs));
|
|
26
|
+
program
|
|
27
|
+
.command("queue:work")
|
|
28
|
+
.description("Start queue worker (dist first if built, else src)")
|
|
29
|
+
.option("--queue <name>", "Queue name", "default")
|
|
30
|
+
.option("--prod", "Run production build")
|
|
31
|
+
.option("--runtime <name>", "Runtime: node or bun", "node")
|
|
32
|
+
.option("--src", "Force source mode even if dist exists")
|
|
33
|
+
.allowUnknownOption(true)
|
|
34
|
+
.action(async () => runRuntime("queue:work", rawArgs));
|
|
35
|
+
program
|
|
36
|
+
.command("queue:clear")
|
|
37
|
+
.description("Clear all queue keys")
|
|
38
|
+
.allowUnknownOption(true)
|
|
39
|
+
.action(async () => runRuntime("queue:clear", rawArgs));
|
|
40
|
+
program
|
|
41
|
+
.command("schedule:work")
|
|
42
|
+
.description("Start scheduler worker (dist first if built, else src)")
|
|
43
|
+
.option("--prod", "Run production build")
|
|
44
|
+
.option("--runtime <name>", "Runtime: node or bun", "node")
|
|
45
|
+
.option("--src", "Force source mode even if dist exists")
|
|
46
|
+
.allowUnknownOption(true)
|
|
47
|
+
.action(async () => runRuntime("schedule:work", rawArgs));
|
|
48
|
+
program
|
|
49
|
+
.command("maildev:view")
|
|
50
|
+
.description("Start MailDev SMTP and web UI")
|
|
51
|
+
.allowUnknownOption(true)
|
|
52
|
+
.action(async () => runUi("maildev:view"));
|
|
53
|
+
program
|
|
54
|
+
.command("redis:view")
|
|
55
|
+
.description("Start Redis Commander UI")
|
|
56
|
+
.allowUnknownOption(true)
|
|
57
|
+
.action(async () => runUi("redis:view"));
|
|
58
|
+
program
|
|
59
|
+
.command("ui:dev")
|
|
60
|
+
.description("Start Vue 3 UI from src/resources")
|
|
61
|
+
.allowUnknownOption(true)
|
|
62
|
+
.action(async () => runVite("src/resources/vite.config.ts"));
|
|
63
|
+
program
|
|
64
|
+
.command("admin:dev")
|
|
65
|
+
.description("Start admin UI dev server")
|
|
66
|
+
.allowUnknownOption(true)
|
|
67
|
+
.action(async () => runVite("src/resources/vite.config.ts"));
|
|
68
|
+
program
|
|
69
|
+
.command("vite:cache:clear")
|
|
70
|
+
.description("Clear Vite cache folders (cross-platform)")
|
|
71
|
+
.allowUnknownOption(true)
|
|
72
|
+
.action(async () => clearViteCache());
|
|
73
|
+
program
|
|
74
|
+
.command("maker:init")
|
|
75
|
+
.description(`Create ${PROJECT_COMMANDS_PATH} for your own commands`)
|
|
76
|
+
.option("--force", "Overwrite an existing file")
|
|
77
|
+
.allowUnknownOption(true)
|
|
78
|
+
.action(async (options) => {
|
|
79
|
+
const { created, overwritten } = await initProjectCommands(process.cwd(), { force: Boolean(options?.force) });
|
|
80
|
+
// created = written into a new file; overwritten = replaced under --force.
|
|
81
|
+
// Neither means the file was already there and was left alone.
|
|
82
|
+
console.log(
|
|
83
|
+
created || overwritten
|
|
84
|
+
? `Add your commands to ${PROJECT_COMMANDS_PATH}, then run: maker --help`
|
|
85
|
+
: `${PROJECT_COMMANDS_PATH} already exists and was left alone. Edit it to add commands, or re-run with --force to replace it.`
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
program
|
|
89
|
+
.command("test")
|
|
90
|
+
.description("Run Vitest backend tests")
|
|
91
|
+
.allowUnknownOption(true)
|
|
92
|
+
.action(async () => {
|
|
93
|
+
const args = process.argv.slice(3);
|
|
94
|
+
await runTest(args);
|
|
95
|
+
});
|
|
96
|
+
program
|
|
97
|
+
.command("test:watch")
|
|
98
|
+
.description("Run Vitest backend tests in watch mode")
|
|
99
|
+
.allowUnknownOption(true)
|
|
100
|
+
.action(async () => {
|
|
101
|
+
const args = process.argv.slice(3);
|
|
102
|
+
await runTestWatch(args);
|
|
103
|
+
});
|
|
104
|
+
program
|
|
105
|
+
.command("test:coverage")
|
|
106
|
+
.description("Run Vitest backend tests with code coverage")
|
|
107
|
+
.allowUnknownOption(true)
|
|
108
|
+
.action(async () => {
|
|
109
|
+
const args = process.argv.slice(3);
|
|
110
|
+
await runTestCoverage(args);
|
|
111
|
+
});
|
|
112
|
+
program
|
|
113
|
+
.command("test:ui")
|
|
114
|
+
.description("Run Vitest backend tests in UI mode")
|
|
115
|
+
.allowUnknownOption(true)
|
|
116
|
+
.action(async () => {
|
|
117
|
+
const args = process.argv.slice(3);
|
|
118
|
+
await runTestUI(args);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Why: Redis Commander 0.4.5 bundles legacy `connect`/`serve-static`/`send`
|
|
2
|
+
// whose `SendStream#isFresh` reads `http.ServerResponse#_headers`, which
|
|
3
|
+
// Node removed in v15. Without this shim it crashes with:
|
|
4
|
+
// TypeError: Cannot read properties of undefined (reading 'last-modified')
|
|
5
|
+
// When: Loaded via `node --require` before Redis Commander starts.
|
|
6
|
+
// Where: maker-cli redis:view launcher.
|
|
7
|
+
// How: Re-exposes `_headers` on the ServerResponse prototype, backed by the
|
|
8
|
+
// modern `getHeaders()` API. Safe on older Node too (getHeaders is the
|
|
9
|
+
// same data `_headers` used to return).
|
|
10
|
+
const http = require("node:http");
|
|
11
|
+
|
|
12
|
+
if (!("_headers" in http.ServerResponse.prototype)) {
|
|
13
|
+
Object.defineProperty(http.ServerResponse.prototype, "_headers", {
|
|
14
|
+
configurable: true,
|
|
15
|
+
enumerable: false,
|
|
16
|
+
get() {
|
|
17
|
+
return this.getHeaders();
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|