Monorepo for Launch Bay Next, containing:
- A Next.js web app with tRPC API and PostgreSQL persistence
- An Expo React Native mobile app
- A shared
lbn-corepackage with game data, types, and helper logic
platform/
├── apps/
│ ├── web/ # Next.js frontend + API routes + tRPC server
│ └── app/ # Expo mobile app (iOS/Android)
├── packages/
│ └── core/ # Shared X-Wing data, types, converters, serializers
└── package.json # Root scripts + Bun workspaces (web + packages/*)
- Runtime/package manager: Bun
- Web: Next.js 14, React, Tailwind, tRPC, Drizzle ORM
- Mobile: Expo Router, React Native, Zustand, SWR
- Shared domain logic: TypeScript package (
lbn-core)
- Bun 1.x
- Node-compatible tooling required by Expo/Next.js
- PostgreSQL (for
apps/web) - Xcode/Android Studio if building native mobile binaries locally
Install root workspace dependencies:
bun install
apps/appis currently not listed in root workspaces and has its own lockfile. Install its dependencies separately:
cd apps/app
bun installFrom repo root:
bun run dev:webOr directly:
cd apps/web
bun run devBuild production web bundle:
bun run web:buildcd apps/app
bun run startRun on simulators/devices:
cd apps/app
bun run ios
bun run androidCommon variables used by the web/API layer:
DATABASE_URL(required): PostgreSQL connection stringJWT_SECRET(required): token signing/verification secretPOSTMARK_API_TOKEN(required for email login flow)NEXT_PUBLIC_HOSTING_URL(optional, defaults tohttp://localhost:3000)
Additional provider/deployment variables referenced in config:
FACEBOOK_ID,FACEBOOK_SECRETGOOGLE_ID,GOOGLE_SECRETPOSTGRES_URL,POSTGRES_URL_NON_POOLING,POSTGRES_PRISMA_URL
APP_VARIANTcontrols app identity (developmentvs production metadata inapp.config.js)EXPO_PUBLIC_SERVER_URLis configured in EAS profiles, though the current mobile tRPC URL is hardcoded inapps/app/src/helpers/trpc.ts
- UI routes and API route handlers live in
apps/web/src/app - tRPC entrypoint:
apps/web/src/app/api/trpc/[trpc]/route.ts - tRPC router and subrouters:
apps/web/src/server - Database schema and SQL migrations:
apps/web/src/server/drizzle
Notable API route handlers:
POST /api/linkconverts inbound squad data to a Launch Bay Next URLPOST /api/standardvalidates standard legalityGET /api/xws?lbx=...expands serialized link data back to XWS JSON
A server-side game data management interface behind admin authentication. Provides CRUD operations for all game entities and is accessible only to users with IsAdmin = true.
Pages:
/admin— Dashboard showing per-ruleset version hashes/admin/ships— List, create, edit, delete ships/admin/pilots— List, create, edit, delete pilots (linked to ships)/admin/upgrades— List, create, edit, delete upgrades/admin/conditions— List, create, edit, delete conditions/admin/sources— List, create, edit, delete product sources/admin/users— Search users, toggle admin status
Backend:
- Admin tRPC sub-routers live in
apps/web/src/server/routers/admin/ - All admin mutations use
adminProcedure(requiresIsAdminon the user) - Mutations auto-recompute the per-ruleset game data version hash via
computeAndUpdateVersion()(src/server/helpers/versionHash.ts) - UI components are built with shadcn/ui (
src/components/ui/)
A public tRPC router for serving game data to clients:
gameData.version(ruleset)— returns the current version hash for a rulesetgameData.all(ruleset)— returns the fullGameDatapayload (ships, pilots, upgrades, conditions)gameData.sources()— returns all product sourcesgameData.manifest(ruleset)— returns the manifest ID mapping
Each ruleset (xwa, legacy) has a version hash stored in the GameDataVersions table. The hash is an MD5 of all ships, pilots, upgrades, and conditions for that ruleset. It is recomputed whenever admin CRUD mutations modify game data.
The mobile app polls the version endpoint and refetches data only when the hash changes (see apps/app/src/stores/gameData.ts).
The database uses Drizzle ORM with PostgreSQL. Key tables:
- Users — user accounts with
IsAdminflag - Ships — ship definitions per ruleset/faction (unique on Ruleset + Faction + Xws)
- Pilots — pilot cards linked to Ships via FK with cascade delete (unique on ShipId + Xws)
- GameUpgrades — upgrade cards per ruleset (unique on Ruleset + Slot + Xws)
- GameConditions — condition cards per ruleset (unique on Ruleset + Xws)
- Sources — product/expansion metadata with contents references
- ManifestEntries — numeric ID ↔ xws key mappings for compact serialization
- GameDataVersions — per-ruleset version hashes
- Collections, Squadrons, UserLoginCodes — existing user data tables
Migrations are in src/server/drizzle/. Run bunx drizzle-kit generate to create new migrations and bunx drizzle-kit migrate to apply them.
apps/web/src/server/scripts/seed.ts populates game data tables from lbn-core assets:
cd apps/web
bun run src/server/scripts/seed.tsThis truncates existing game data, seeds ships/pilots/upgrades/conditions/sources/manifest entries for all rulesets, and computes version hashes.
- Expo Router screens are organized in
apps/app/src/app - Main tab routes are in
apps/app/src/app/(tabs):- collection
- database
- overview/account/login
- squadrons flow
- Shared client-side state and helpers live in
apps/app/src/storesandapps/app/src/helpers - Game data from the server is managed by
apps/app/src/stores/gameData.ts(Zustand + AsyncStorage, with version-based cache invalidation)
- Canonical game assets by ruleset in
packages/core/src/assets/{xwa,legacy,amg} - Types in
packages/core/src/types.ts(includesGameDataandManifestDatatypes for server API contracts) - Domain helpers (loaders, serializers, import/export, conversion) in
packages/core/src/helpers - Source data bundles in
packages/core/src/sources
cd apps/web
bun run dev
bun run build
bun run start
bun run lintcd apps/app
bun run start
bun run ios
bun run android
bun run web
bun run lint
bun run testcd packages/core
bun run update:xwa
bun run update:legacypackages/core/scripts/xwing-data contains merge/update scripts that pull upstream data and rewrite local assets.
Use:
update:xwato sync XWA dataupdate:legacyto sync Legacy data
After updating, verify generated changes in packages/core/src/assets before committing.