Local Development Workflows
Local development in Wavemap is split between workspace package watchers, app runtimes, Docker-backed backend services, generated assets, and browser test runners. Use this page to choose the right local loop before reaching for deployed-dev commands.
Before the first dependency install, complete Private npm Access so pnpm and frontend Docker builds can resolve the private Codon UI CLI without placing a token in repository files.
This page is for developer-machine setup and local verification. Shared cloud environments, deployed runtime config, and operator recovery paths live in the operations docs.
Common Loops
Section titled “Common Loops”| Loop | Use For | Primary Commands |
|---|---|---|
| Docker-backed backend, local frontend | Normal app development with local Postgres, seeded data, dependent packages watching for changes, and dev server Next.js. | pnpm dev:docker:up, then pnpm -C apps/wavemap-front-end dev. |
| Non-Docker local watchers | Local services already exist and you want package/backend/frontend watchers in one terminal. | pnpm dev. |
| Package development | Shared package contract or utility work. | pnpm -F @wavemap/api-contracts dev, pnpm -F @wavemap/i18n dev, package tests. |
| Browser E2E local debugging | Real browser behavior against local runtimes. | pnpm test:e2e:prepare, frontend dev, then front-end Playwright commands. |
| CI-style built frontend | Build/runtime parity for Playwright or production-like checks. | Build i18n assets, pnpm -C apps/wavemap-front-end build, then start. |
| Pseudolocalization audit | Rendered-output proof that translated copy is transformed and naked JSX strings are detectable. | Build with pseudolocalization env, then pnpm verify:i18n-pseudolocalization. |
| Docs site work | Starlight content and navigation changes. | pnpm -C apps/wavemap-docs dev, pnpm build:docs. |
Prefer the Docker-backed backend plus local frontend for most product work. The front-end Docker compose path exists,
but local next dev is the ordinary faster loop on macOS and Windows.
For the separate anonymous service, use Public API Foundation for pnpm dev:public-api, restricted local credentials and the disposable demo.
Local Env Files
Section titled “Local Env Files”The app .env.example files document the local values expected by each runtime:
apps/wavemap-back-end/.env.exampleapps/wavemap-front-end/.env.exampleapps/wavemap-public-api/.env.examplepackages/i18n/.env.example
Local .env.dev and .env.shared files are not tracked in git. Create local files from the example values and keep real
secrets out of the repository.
Important local boundaries:
- Frontend browser values use
NEXT_PUBLIC_*and are visible to the browser. - Frontend server-only values do not use the
NEXT_PUBLIC_*prefix. - Backend runtime values are consumed by the backend env parser and Docker Compose.
- Shared i18n values exist in both frontend and backend-facing forms so the app and package agree on locale, namespace, and asset version behavior.
- Pseudolocalization uses both public and non-public i18n env values when the browser and server runtime should agree on the selected replacement character set.
- Local development values should not be copied into deployed-dev SSM, GitHub environments, or Pulumi config just because they have the same names.
Use Configuration And Secrets when adding, renaming, or moving an environment value.
Docker-Backed Backend
Section titled “Docker-Backed Backend”The main local backend path is:
pnpm dev:docker:upBefore package work begins, the command confirms that the Docker daemon is available and that the resolved Compose topology parses. It then synchronously builds @wavemap/shared-utils, @wavemap/api-contracts, @wavemap/data-access, and @wavemap/i18n, including Wavemap’s required tsc-alias rewriting, before it starts the ownership-checked background watcher. If that initial build fails, Docker startup does not continue. Detailed watcher output is written to .wavemap/dev-docker/package-watcher.log.
The command then starts the root-owned backend development Compose project, resolves the active local media mode from
the backend env file, and waits for database and database-connected API readiness. The backend service converges the
local database before it launches the API, so readiness cannot predate pending migrations and an ordinary Compose or
container restart follows the same convergence path. Compose owns the project’s ordinary network, so the backend reaches
PostgreSQL through the wavemap_db service name without a separately created external network. A genuinely empty local
database receives migrations, base fixtures, and development fixtures once. Later starts run migrations while preserving
retained database and media state. An existing pre-marker Wavemap schema is adopted without reseeding; unknown non-empty
schemas or database markers are refused.
Then run the frontend locally:
pnpm -C apps/wavemap-front-end devDirect frontend startup first builds the existing three-package runtime graph, including alias rewriting, and launches Next only after that build succeeds. The filtered form (pnpm -F wavemap-front-end dev) and root pnpm dev:frontend use the same path. Next arguments such as --port 3001 are forwarded. This preparation does not start another package watcher; use the Docker-backed or foreground package loop above when editing shared-package source. The combined pnpm dev command reuses its own initial build before launching Next.
Default local URLs:
- Frontend:
http://localhost:3000 - Backend:
http://localhost:6001 - Readiness:
http://localhost:6001/api/v1/ready - PostgreSQL:
localhost:5434
These ports are loopback-only and do not conflict with Waveguide’s defaults. Override them when necessary with
WAVEMAP_BACKEND_PORT and WAVEMAP_DATABASE_PORT.
Useful stack controls:
pnpm dev:docker:stoppnpm dev:docker:reloadpnpm dev:docker:downpnpm dev:docker:hard-reloadpnpm dev:packages:stopRoutine lifecycle commands preserve local data:
stopstops the backend/media containers and owned watcher while retaining containers and volumes.downremoves backend/media containers and the ordinary Compose project network, but retains named volumes.reloadkeeps the owned watcher, database, and media state; it rebuilds and force-recreates only the backend service, which converges pending migrations before it becomes ready.- None of these commands parses or controls the legacy Dockerized frontend project. Run the frontend natively.
hard-reload is the coordinated destructive path for the selected backend/media Compose graph. It requires an exact
confirmation before Docker inspection, verifies the exact wavemap_dev target and its repository-owned disposable
marker, then removes only the resolved Wavemap development volumes and recreates the fixture baseline:
pnpm dev:docker:hard-reload -- --confirm=remove-wavemap-dev-volumesFor the narrower expert-only database reset, which intentionally leaves media state untouched, use:
pnpm -F wavemap-back-end db:reset-dev-db --confirm=reset-wavemap-dev-database-onlyThis path validates the exact confirmation before Docker work, then the backend reset command independently verifies the
local-dev reset context, exact Compose database identity, and repository-owned disposable marker. Only then does it
clear the application and Drizzle migration schemas and run migrations, base seed, and development seed against the empty
database. Ordinary startup convergence and ordinary migrations remain data-preserving.
Use dev:packages:stop only when you need to stop the watcher without touching Docker. Use SKIP_DOCKER_WATCHERS=1 pnpm dev:docker:up when you intentionally do not want Docker startup to manage package watchers. This setting and CI=true still build the complete runtime package graph once before Compose starts; the host dist mounts need those outputs even when the image already contains built packages. The same build can be run independently with node bin/dev/watch-workspace-packages.mjs --build-only.
pgweb is available behind the root Compose tools profile and is not part of the normal graph:
docker compose -f compose.dev.yml --profile tools up --detach --wait pgwebIt binds to http://localhost:8081 by default and uses the same retained PostgreSQL service. Stop it explicitly with
docker compose -f compose.dev.yml --profile tools stop pgweb when it is no longer needed.
Diagnose Startup
Section titled “Diagnose Startup”dev:docker:up refuses package builds and watcher startup when the Docker daemon is unavailable. On macOS, start Docker
Desktop and wait for the engine to be ready before retrying. The native Docker error and a short recovery hint remain
visible in the terminal.
Compose build and startup output is streamed without filtering. If Compose fails, the lifecycle also prints current project status and the last 120 log lines. For further inspection:
docker compose -p wavemap_be_dev -f compose.dev.yml psdocker compose -p wavemap_be_dev -f compose.dev.yml logs --tail=120node bin/dev/manage-workspace-package-watcher.mjs statusThe managed watcher log remains .wavemap/dev-docker/package-watcher.log.
Non-Docker Local Dev
Section titled “Non-Docker Local Dev”The root command:
pnpm devuses the cross-platform local dev process manager. It runs these watcher/runtime processes in one terminal:
@wavemap/shared-utils@wavemap/api-contracts@wavemap/i18n@wavemap/data-accesswavemap-back-endwavemap-front-end
Use this loop when the required local services and env values already exist. The @wavemap/i18n watcher keeps package
source exports flowing into dist for the app runtimes, while ordinary translation JSON edits are still read directly by
local next dev through the rewrite-backed i18n asset routes.
Press Ctrl-C in the pnpm dev terminal to stop all child processes.
To run only the shared package watchers, use:
pnpm dev:packagesWhen pnpm dev:docker:up starts shared package watchers in the background, stop them with:
pnpm dev:packages:stopMedia Modes
Section titled “Media Modes”Local media behavior is selected through backend env values:
MEDIA_STORAGE_PROVIDERMEDIA_STORAGE_EXECUTION_TARGETMEDIA_STORAGE_APPLICATION_ENVIRONMENT
Current local modes:
| Mode | Env Shape | Use For |
|---|---|---|
| Mocked storage | Provider: mockedTarget: mockedApp env: development or test. | Day-to-day app logic and tests that do not need object-store behavior. |
| S3 emulator | Provider: s3Target: emulatorApp env: development or test. | Local AWS-shaped upload/delete/path behavior through S3Mock. |
| Azure emulator | Provider: azureTarget: emulatorApp env: development. | Azure continuity smoke through Azurite. |
| Real S3 validation | Provider: s3Target: cloudApp env: development. | Explicit real-cloud validation with a selected read-only AWS config directory. |
| Real Azure validation | Provider: azureTarget: cloudApp env: development. | Explicit real-cloud validation with selected Azure connection values. |
When MEDIA_STORAGE_APPLICATION_ENVIRONMENT is omitted, the backend falls back to NODE_ENV. When
MEDIA_STORAGE_EXECUTION_TARGET is omitted, development and test resolve to emulator, while staging and
production resolve to cloud.
Emulators are execution targets, not persisted providers. S3Mock and Azurite should not appear as stored media provider values.
pnpm dev:docker:up accepts only mocked/mocked, s3/emulator, azure/emulator, s3/cloud, or azure/cloud, and adds
only the selected provider overlay. S3Mock retains its object filesystem and Azurite retains blob state in named volumes
across routine lifecycle commands. The
cloud overlays receive only their selected provider’s inputs; S3 cloud mode additionally requires
WAVEMAP_AWS_CONFIG_DIR and mounts that directory read-only at /home/node/.aws.
The coordinated destructive command resets the database and the selected emulator volume together. To clear only the currently selected emulator while intentionally leaving database records untouched, use the expert-only exact-confirmed command:
pnpm -F wavemap-back-end media:reset-dev-emulator --confirm=reset-wavemap-dev-media-emulator-onlyReal cloud modes are never targeted by either local emulator reset path.
Use Media Workflow And Validation for choosing the right media proof lane. Use Media Storage And Delivery for the durable media architecture boundary.
i18n Assets
Section titled “i18n Assets”In local next dev, i18n asset requests are rewritten to API routes that read from Wavemap’s locale root or the
foundation package’s locale root for package-owned common namespaces. Ordinary translation edits therefore do not require
regenerating frontend public assets on every save.
Production-like frontend builds need a generated static snapshot:
pnpm -C packages/i18n build-and-validate:i18n-assetsRun that before frontend build/start flows when you need built-runtime parity:
pnpm -C packages/i18n build-and-validate:i18n-assetspnpm -C apps/wavemap-front-end buildpnpm -C apps/wavemap-front-end startUse i18n Assets And Delivery for the full asset and CDN boundary.
Pseudolocalization Audit
Section titled “Pseudolocalization Audit”Use pseudolocalization when you want a browser-level signal for untranslated visible JSX text.
The fastest local proof uses the same command CI runs, but it expects a frontend runtime to already be serving:
pnpm verify:i18n-pseudolocalization -- --project=chromiumFor built-runtime parity, generate assets and build the frontend with pseudolocalization active:
I18N_VERSION=0.1.0 pnpm -C packages/i18n build-and-validate:i18n-assets
NEXT_PUBLIC_AVAILABLE_LOCALES=en,fr \NEXT_PUBLIC_DEFAULT_LOCALE=en \NEXT_PUBLIC_I18N_VERSION=0.1.0 \NEXT_PUBLIC_PSEUDOLOCALIZATION_ACTIVE=true \NEXT_PUBLIC_PSEUDOLOCALIZATION_CHARACTER_SET=accented \AVAILABLE_LOCALES=en,fr \DEFAULT_LOCALE=en \I18N_VERSION=0.1.0 \PSEUDOLOCALIZATION_ACTIVE=true \PSEUDOLOCALIZATION_CHARACTER_SET=accented \pnpm -C apps/wavemap-front-end buildStart that built runtime in another shell, keeping the same pseudolocalization env values:
PLAYWRIGHT_FRONTEND_RUNTIME=build pnpm -C apps/wavemap-front-end startThen run:
PLAYWRIGHT_FRONTEND_RUNTIME=build pnpm verify:i18n-pseudolocalization -- --project=chromiumThe suite includes a deterministic probe route at /en/i18n-pseudolocalization-probe. That route intentionally renders
one naked string so the scanner proves it can fail for the right reason. Do not treat that route as product UI.
Browser E2E Runtime
Section titled “Browser E2E Runtime”Playwright does not start the app runtime for you. Start the backend and frontend first, then run browser tests.
Default local sequence:
pnpm dev:docker:uppnpm test:e2e:preparepnpm -C apps/wavemap-front-end exec playwright install chromiumStart the frontend in its own shell:
pnpm -C apps/wavemap-front-end devThen run the smoke suite from another shell once http://localhost:3000 is serving:
pnpm -C apps/wavemap-front-end test:e2e:smokepnpm test:e2e:prepare builds the shared workspace packages whose package exports point at dist outputs. This keeps
front-end Playwright imports aligned with package export behavior.
CI smoke uses a built frontend runtime instead of next dev so browser timing reflects built artifacts rather than
on-demand route compilation:
pnpm -C packages/i18n build-and-validate:i18n-assetspnpm -C apps/wavemap-front-end buildStart the built runtime in its own shell:
PLAYWRIGHT_FRONTEND_RUNTIME=build pnpm -C apps/wavemap-front-end startThen run smoke against that built runtime from another shell:
pnpm -C apps/wavemap-front-end test:e2e:smoke -- --project=chromiumUse Testing for layer selection and browser-suite scope.
Package And Docs Checks
Section titled “Package And Docs Checks”pnpm typecheck:packages refreshes the project-reference declarations and rewrites their internal aliases, but does not emit runtime JavaScript. Running it directly or through pnpm verify:tooling therefore preserves the runnable JavaScript used by an active frontend or backend. A typecheck is not a runtime build: use the package build scripts or the development startup commands to update executable output after source changes.
Useful local verification commands:
pnpm test:packagespnpm test:unitpnpm verify:i18npnpm verify:scriptspnpm build:docspnpm -C apps/wavemap-docs devPrefer package-scoped commands when changing one package:
pnpm -F @wavemap/api-contracts testpnpm -F @wavemap/i18n testpnpm -F @wavemap/shared-utils testpnpm -C apps/wavemap-front-end test:cipnpm -C apps/wavemap-back-end test:ciWhat Local Development Is Not
Section titled “What Local Development Is Not”Local development commands should not silently:
- Run Pulumi.
- Mutate deployed-dev runtime hosts.
- Populate SSM parameters.
- Push Docker images.
- Publish docs to S3/CloudFront.
- Reset deployed-dev data.
- Use real cloud media storage without an explicit validation choice.
When the task crosses those boundaries, switch to the operations docs and move through the appropriate approval gates.
Related Pages
Section titled “Related Pages”- Monorepo Map for workspace ownership.
- Feature Slice Workflow for cross-app implementation flow.
- Front End Patterns for frontend page and component state.
- Configuration And Secrets for env value ownership.
- Testing for local test commands and browser runtime scope.
- i18n Assets And Delivery for translation asset behavior.
- Deployed Dev Environment for the shared cloud-backed dev environment.