bassbot/ ← root (bun workspace)
├── package.json ← bot deps + scripts
├── tsconfig.json ← bot + lib type-checking
├── eslint.config.ts ← bot + lib ESLint
├── prettier.config.js ← shared prettier config
├── drizzle.config.ts ← bot drizzle config
├── bunfig.toml
├── bun.lock ← single lockfile
├── lib/ ← shared library (@lib/*)
│ ├── index.ts ← barrel export
│ ├── *.ts ← command framework, jwt, logger, etc.
│ ├── command/ ← sub-module
│ ├── scripts/ ← CLI scripts (dev, generate, register, etc.)
│ └── __tests__/ ← lib tests
├── src/ ← bot app (@bot/*)
│ ├── index.ts ← entry point
│ ├── config.ts, bot.ts, player.ts, queue.ts
│ ├── api/ ← Elysia REST API
│ ├── commands/ ← slash commands
│ ├── events/ ← lavalink events
│ ├── middlewares/
│ ├── modals/
│ ├── util/ ← db, helpers, schema
│ ├── validators/
│ └── constants/
├── web/ ← dashboard app (@web/*)
│ ├── package.json ← web deps
│ ├── tsconfig.json ← web type-checking
│ ├── eslint.config.mjs ← web ESLint (next)
│ ├── drizzle.config.ts
│ ├── next.config.ts
│ ├── postcss.config.mjs
│ ├── components.json ← shadcn
│ └── src/ ← Next.js app
├── drizzle/ ← bot migrations
├── config/ ← deployment configs
└── docker/ ← Dockerfiles
bassbot/ ← turborepo root
├── turbo.json ← pipeline config
├── package.json ← root workspaces + top-level scripts
├── bunfig.toml
├── bun.lock ← single lockfile
├── apps/
│ ├── bot/ ← bot app (@bot/*)
│ │ ├── package.json ← "name": "@bassbot/bot"
│ │ ├── tsconfig.json ← @bot/* → ./src/*
│ │ ├── eslint.config.ts
│ │ ├── drizzle.config.ts
│ │ ├── drizzle/ ← bot migrations
│ │ └── src/ ← current src/ contents
│ │ ├── index.ts
│ │ ├── config.ts, bot.ts, player.ts, queue.ts
│ │ ├── api/, commands/, events/, middlewares/, …
│ │ └── ...
│ └── web/ ← web dashboard (@web/*)
│ ├── package.json ← "name": "@bassbot/web"
│ ├── tsconfig.json ← @web/* → ./src/*
│ ├── eslint.config.mjs
│ ├── drizzle.config.ts
│ ├── drizzle/ ← web migrations (if any)
│ ├── next.config.ts
│ ├── postcss.config.mjs
│ ├── components.json
│ └── src/ ← current web/src/ contents
├── packages/
│ ├── lib/ ← shared library (@bassbot/lib, alias @lib/*)
│ │ ├── package.json ← "name": "@bassbot/lib"
│ │ ├── tsconfig.json ← @lib/* → ./src/*
│ │ └── src/ ← current lib/ contents (moved from lib/*.ts)
│ │ ├── index.ts
│ │ ├── command/, jwt.ts, logger.ts, ...
│ │ ├── scripts/ ← generate, register, etc.
│ │ └── __tests__/
│ └── config/ ← shared configs (@bassbot/config)
│ ├── package.json ← "name": "@bassbot/config"
│ ├── prettier.config.js
│ └── eslint-base.ts ← shared ESLint base config (optional)
├── config/ ← deployment configs (compose, .env example)
└── docker/ ← Dockerfiles
| From | To | Import | Type | Count |
|---|---|---|---|---|
src/ (bot) |
lib/ |
@lib/* |
runtime | ~61 imports across ~30 files |
web/src/ |
lib/ |
@lib/init-env, @lib/jwt |
runtime | 3 files |
web/src/ |
src/ (bot) |
@bot/api |
type-only | 1 file (api-client.ts) |
lib/scripts/register-commands.ts |
src/ (bot) |
@bot/config |
runtime (CLI script) | 1 file |
lib/scripts/generate-commands-json.ts |
src/ (bot) |
path resolution (../../src/commands) |
runtime (CLI script) | 1 file |
lib/scripts/generate.ts |
src/ (bot) |
writes to src/commands/, src/validators/, src/middlewares/ |
filesystem | 1 file |
lib/scripts/dev.ts |
both | spawns processes in root + web/ |
subprocess | 1 file |
-
src/→@lib/*(61 imports) —@bassbot/botdeclares@bassbot/libas a workspace dependency.@bot/*→./src/*stays local;@lib/*→ resolved via package dep + tsconfigpathspointing into../../packages/lib/src/*. -
web/src/→@lib/*(3 imports) — Same approach:@bassbot/webdeclares@bassbot/libas a workspace dependency. -
web/src/→@bot/api(1 type-only import) — This is the only cross-app dependency. Since it's type-only,@bassbot/webdeclares@bassbot/botas a dev dependency and the tsconfigpathsmaps@bot/*→../../apps/bot/src/*. No runtime cost. -
lib/scripts/register-commands.ts→@bot/config— This script is bot-specific. Move it toapps/bot/so it naturally resolves@bot/config. It still imports@lib/commandand@lib/registervia the lib dependency. -
lib/scripts/generate-commands-json.ts— Uses../../src/commandspath. Move toapps/bot/since it reads bot commands. Update path to../src/commandsand output to../../apps/web/src/assets/commands.json. -
lib/scripts/generate.ts— Scaffolds files intosrc/. Move toapps/bot/so relative paths work naturally. -
lib/scripts/dev.ts— Replaced by turborepo'sturbo devwhich runs both apps concurrently. Delete. -
lib/scripts/docker-publish-check.ts— CI only, no cross-deps. Keep in root or move toapps/bot/. -
Template files (
lib/scripts/templates/) — Used bygenerate.ts. Move together toapps/bot/scripts/templates/.
- Create
turbo.jsonwith pipeline definitions - Create
apps/bot/package.json,apps/web/package.json,packages/lib/package.json,packages/config/package.json - Update root
package.jsonto define workspaces as["apps/*", "packages/*"]
- Move
lib/*.ts,lib/command/,lib/__tests__/→packages/lib/src/ - Move
lib/scripts/register-commands.ts,lib/scripts/generate.ts,lib/scripts/generate-commands-json.ts,lib/scripts/templates/→apps/bot/scripts/ - Move
lib/scripts/docker-publish-check.ts→ rootscripts/orapps/bot/scripts/ - Move
src/→apps/bot/src/ - Move
drizzle/→apps/bot/drizzle/,drizzle.config.ts→apps/bot/ - Move
eslint.config.ts→apps/bot/ - Move
web/→apps/web/(the entire directory including its own package.json, tsconfig, etc.) - Move
prettier.config.js→packages/config/prettier.config.js - Delete
lib/scripts/dev.ts(replaced by turborepo)
- Create
packages/lib/tsconfig.json—@lib/*→./src/* - Create
apps/bot/tsconfig.json—@bot/*→./src/*, inherits deps on@lib/* - Update
apps/web/tsconfig.json— keep@web/*→./src/*, update@lib/*and@bot/*paths - Update
apps/web/next.config.ts—outputFileTracingRootnow points to monorepo root (../../) - Update
apps/web/components.json— shadcn aliases stay on@web/*(unchanged) - Update all
drizzle.config.tspaths - Update each app's
eslint.config.ts
-
apps/bot/package.jsonscripts:"dev": "bun --watch src/index.ts""start": "bun run src/index.ts""lint": "tsc""eslint": "eslint .""register": "bun scripts/register-commands.ts""g": "bun scripts/generate.ts""gen:commands": "bun scripts/generate-commands-json.ts""db:generate": "drizzle-kit generate"
-
apps/web/package.jsonscripts:"dev": "next dev""build": "next build""start": "next start""lint": "tsc""eslint": "eslint"- remove
predev/prebuildhooks (generate-commands-json moves to bot)
-
Root
package.jsonscripts:"dev": "turbo dev""build": "turbo build""lint": "turbo lint""eslint": "turbo eslint""g": "turbo g --filter=@bassbot/bot""register": "turbo register --filter=@bassbot/bot""build-bot": "docker build -t bassbot:latest -f docker/Dockerfile.bot .""build-web": "docker build -t bassbot:latest -f docker/Dockerfile.web ."
-
Bot scripts (moved from
lib/scripts/toapps/bot/scripts/):register-commands.ts:@bot/configstays (now resolves within same app). Updateimport.meta.dir-relative paths for command directory.generate-commands-json.ts: UpdateCOMMANDS_DIRandOUTPUT_FILEpaths.generate.ts: UpdateTEMPLATE_DIR,COMMANDS_DIRpaths.
-
Lib tests (
packages/lib/src/__tests__/):type-inference.test.ts: change@lib/command→../command,@lib/middleware→../middleware,@lib/validator→../validator(now relative within package). Or keep@lib/*and set path alias in lib's tsconfig.
-
Web config (
apps/web/src/lib/config.ts):dataDirfallback path:resolve(join(process.cwd(), "..", "data"))→resolve(join(process.cwd(), "data"))or useDATA_DIRenv.
-
docker/Dockerfile.bot— Update COPY commands:COPY package.json bun.lock ./ COPY apps/bot/package.json apps/bot/ COPY packages/lib/package.json packages/lib/ COPY packages/config/package.json packages/config/ RUN bun install --frozen-lockfile COPY packages/lib/ packages/lib/ COPY apps/bot/ apps/bot/ WORKDIR /app/apps/bot CMD ["bun", "start"]
-
docker/Dockerfile.web— Update COPY and build commands:# deps stage copies all package.jsons # builder stage copies full repo, builds in apps/web # runner stage copies standalone output
outputFileTracingRootnow 2 levels up → standalone output mirrors monorepo layout
- Run
bun install(regenerate lockfile for new workspace layout) - Run
turbo lint— verify all type-checking passes - Run
turbo build— verify web build succeeds - Run
bun testinpackages/lib— verify all lib tests pass - Test
bun gfrom root (shorthand for bot generator) - Verify Docker builds:
docker build -f docker/Dockerfile.bot .anddocker build -f docker/Dockerfile.web .
-
@bot/apitype-only import in web — The web's Eden treaty client needs the bot'sApptype. This works because TypeScript can resolve it at build time through tsconfig paths. The web Dockerfile already copies the full repo for the build stage, so this continues to work. -
Next.js standalone output —
outputFileTracingRootmust point to the monorepo root (path.join(__dirname, "../..")) so thatpackages/lib/runtime imports are included in the standalone bundle. -
Lockfile — Currently a single
bun.lockat root. Turborepo with bun workspaces keeps this pattern — no change needed.bun installat root resolves all workspace packages. -
shadcn — The
components.jsonuses@web/*aliases. These are internal to the web app and stay unchanged. -
generate-commands-json — This script reads bot commands and writes to web's assets. After the move, it lives in
apps/bot/scripts/but writes to../../apps/web/src/assets/commands.json. Alternatively, make this a turbo task that runs as part of web's build pipeline. -
Prettier —
packages/config/prettier.config.jsis referenced by each app via"prettier": "@bassbot/config/prettier"in their package.json, or apps can have their own config that imports from it.