Source Tree Analysis
Annotated directory tree for the spieli monorepo. Excludes node_modules, dist, .git, and build artefacts.
spieli/ ← Monorepo root
│
├── app/ ← Svelte 5 frontend (Part: frontend)
│ ├── index.html ← Vite entry point
│ ├── vite.config.js ← Vite build config
│ ├── package.json ← Frontend deps (Svelte, OL, Bootstrap, Tailwind)
│ ├── postcss.config.js ← Tailwind PostCSS integration
│ ├── public/ ← Static assets served by Vite / nginx
│ │ ├── config.js ← Runtime config (overwritten by docker-entrypoint.sh)
│ │ └── img/ ← Static images
│ └── src/ ← Frontend source
│ ├── main.js ← Entry point — mounts StandaloneApp or HubApp
│ ├── styles/app.css ← Global styles (Tailwind base + Bootstrap overrides)
│ │
│ ├── lib/ ← Pure JS modules (no Svelte)
│ │ ├── api.js ★ All PostgREST HTTP calls — the API client
│ │ ├── config.js ★ Reads window.APP_CONFIG; exports typed constants
│ │ ├── tieredOrchestrator.js ★ Debounced moveend handler, tier switching, AbortController
│ │ ├── completeness.js ★ 3-state completeness logic (mirrors api.sql's SQL)
│ │ ├── deeplink.js ★ URL hash parse/write (#W<osm_id> / #<slug>/W<osm_id>)
│ │ ├── objPlaygroundEquipment.js ★ Single source of truth for all device types
│ │ ├── equipmentGrouping.js ← Groups devices inside playground=structure polygons
│ │ ├── equipmentAttributes.js ← Renders per-device detail HTML from OSM tags
│ │ ├── vectorStyles.js ← OL style functions (playground fill, equipment points)
│ │ ├── clusterStyle.js ← Canvas renderer for cluster rings (stacked ring style)
│ │ ├── i18n.js ← svelte-i18n setup and locale detection
│ │ ├── panoramax.js ← Panoramax photo integration helpers
│ │ ├── playgroundHelpers.js ← Misc. feature property helpers
│ │ ├── region.js ← Region name resolution
│ │ ├── reviews.js ← Mangrove.reviews API integration
│ │ ├── utils.js ← Debounce, escapeHtml, misc.
│ │ └── equipmentGrouping.test.js ← Unit tests (run via `make test-unit`)
│ │
│ ├── stores/ ← Svelte writable stores (shared reactive state)
│ │ ├── selection.js ★ Selected playground feature + backend URL + hash write
│ │ ├── filters.js ★ Active filter state + matchesFilters() predicate
│ │ ├── tier.js ★ Active zoom tier ('cluster'|'polygon'|'macro'|null)
│ │ ├── overlayLayer.js ← Equipment + tree arrays (PlaygroundPanel → Map bridge)
│ │ ├── playgroundSource.js ← Shared OL VectorSource reference (Map → other widgets)
│ │ ├── map.js ← OL Map instance reference
│ │ └── hubLoading.js ← Hub fan-out load progress {loaded, total, settling}
│ │
│ ├── components/ ← Shared Svelte UI components
│ │ ├── Map.svelte ★ OL map, 5 layers, click/hover handlers
│ │ ├── PlaygroundPanel.svelte ★ Detail panel (equipment, trees, POIs, reviews)
│ │ ├── FilterPanel.svelte ← Filter dropdown + layer toggles
│ │ ├── SearchBar.svelte ← Nominatim location search
│ │ ├── EquipmentList.svelte ← Renders device/pitch/bench lists in detail panel
│ │ ├── HoverPreview.svelte ← Floating card on playground hover (desktop)
│ │ ├── EquipmentTooltip.svelte ← Tooltip on equipment hover
│ │ ├── NearbyPlaygrounds.svelte ← Nearby list (uses playgroundSourceStore)
│ │ ├── LocateButton.svelte ← Geolocation button
│ │ ├── AppShell.svelte ← Top-level layout shell + deeplink restore
│ │ ├── BottomSheet.svelte ← Mobile bottom sheet
│ │ ├── CompletenessLegend.svelte ← Colour legend
│ │ ├── FilterChips.svelte ← Active filter chip row
│ │ ├── AgeChip.svelte ← Age range badge
│ │ ├── DataContributionModal.svelte ← "Daten ergänzen" OSM link modal
│ │ ├── MapCompleteLink.svelte ← MapComplete contribution link
│ │ ├── PanoramaxViewer.svelte ← Panoramax photo iframe
│ │ ├── POIPanel.svelte ← Nearby POIs panel
│ │ ├── ReviewsPanel.svelte ← Mangrove reviews panel
│ │ └── ui/ ← Low-level design system primitives
│ │ ├── Badge.svelte
│ │ ├── Button.svelte
│ │ ├── Card.svelte
│ │ ├── Input.svelte
│ │ └── Sheet.svelte
│ │
│ ├── standalone/ ← Standalone mode root
│ │ └── StandaloneApp.svelte ★ Full standalone layout (sidebar, bottom sheet, etc.)
│ │
│ └── hub/ ← Hub mode (APP_MODE=hub)
│ ├── HubApp.svelte ★ Hub layout root
│ ├── hubOrchestrator.js ★ Hub fan-out to all backends, Supercluster merge
│ ├── InstancePanel.svelte ← Backend list drawer
│ ├── InstancePanelDrawer.svelte ← Drawer wrapper
│ ├── MacroView.svelte ← Country-level macro ring view (zoom ≤ macroMaxZoom)
│ ├── bboxRouter.js ← Filters backends by viewport bbox
│ ├── fanOut.js ← Parallel fetch to all matching backends
│ ├── federationHealth.js ← /federation-status.json polling
│ ├── macroRingStyle.js ← OL style for macro rings
│ ├── osmIdDedup.js ← Deduplicates features with the same osm_id across backends
│ └── registry.js ← Fetches and parses registry.json
│
├── db/
│ └── init.sql ← One-time DB init: PostGIS, hstore, api schema, web_anon role
│
├── importer/
│ ├── import.sh ★ Full import script: download → osmium clip → osm2pgsql → api.sql
│ ├── api.sql ★ All PostgREST API functions + playground_stats matview
│ └── Dockerfile ← Importer image (osm2pgsql, osmium-tool, psql)
│
├── processing/ ← OSM data pipeline (used by import.sh / osm2pgsql)
│ ├── lua/
│ │ └── osm_import.lua ★ osm2pgsql Lua rules — defines which OSM tags → which tables
│ ├── sql/
│ │ ├── playground_processing.sql ← Post-import SQL: merge playground fragments, etc.
│ │ ├── completeness_processing.sql ← Completeness scoring SQL
│ │ ├── shadow_preparation.sql ← Shadow layer prep (trees, buildings)
│ │ └── shadow_processing.sql ← Shadow computation
│ └── osm_data_pipeline.sh ← Pipeline orchestration script
│
├── oci/ ← Docker build contexts
│ ├── app/
│ │ ├── Dockerfile ← App image (nginx + Svelte build)
│ │ ├── nginx.conf ← nginx: static serving, /api/ proxy, security headers, CORS
│ │ ├── docker-entrypoint.sh ★ Startup: writes config.js from env, starts nginx
│ │ └── poll-federation.sh ← Cron script: polls backends, writes federation-status.json + /metrics
│ └── docker-entrypoint.sh ← Top-level entrypoint (hub cron setup)
│
├── locales/ ← Translation files (svelte-i18n format)
│ ├── de.json ← German (primary)
│ ├── en.json ← English
│ └── cs.json fr.json … ← Other languages
│
├── deploy/ ← Systemd units for automated import on Linux hosts
│ ├── spieli-import.service ← Runs `docker compose run --rm importer`
│ └── spieli-import.timer ← Weekly trigger (adjust OnCalendar= for your schedule)
│
├── tests/ ← Playwright E2E tests (run via `make test`)
│ ├── helpers.js ★ Shared helpers: injectApiConfig(), stubApiRoutes()
│ ├── smoke.spec.js ← Basic page load + canvas visibility
│ ├── tiered.spec.js ← Cluster/polygon tier RPC selection
│ ├── selection.spec.js ← Playground selection + panel open
│ ├── hash-restore.spec.js ← Deeplink URL hash restore
│ ├── hub-smoke.spec.js ← Hub mode basic load
│ ├── hub-multi-backend.spec.js ← Hub fan-out + Supercluster merge
│ ├── hub-deeplink.spec.js ← Hub deeplink with backend slug
│ ├── hub-pill.spec.js ← Instance pill count display
│ ├── hub-federation-health.spec.js ← Federation status polling
│ ├── hub-osm-id-dedup.spec.js ← Cross-backend osm_id deduplication
│ ├── cluster-position.spec.js ← Cluster position from member centroids
│ ├── osmIdDedup.spec.js ← osmIdDedup unit-style test
│ ├── xss.spec.js ← XSS / injection safety checks
│ └── fixtures/ ← JSON fixtures for stubbed API responses
│
├── openspec/ ← Capability specs + archived change decisions
│ ├── specs/ ← Durable per-capability requirements
│ └── changes/archive/ ← Frozen records of shipped changes (tracked in git)
│
├── .github/workflows/
│ ├── build.yml ← Builds app + Docker images; publishes to GHCR on main/tags
│ ├── e2e.yml ← Playwright E2E on pull_request + main
│ └── docs.yml ← MkDocs build + GitHub Pages deploy
│
├── compose.yml ← Dev Docker Compose stack (db, postgrest, app, db2, postgrest2)
├── compose.prod.yml ← Production Docker Compose (uses :latest images)
├── Makefile ← All common operations (make help for full list)
├── playwright.config.js ← Playwright config (baseURL: localhost:8080)
├── mkdocs.yml ← MkDocs config
├── README.md ← Brief overview + deploy link
├── CONTRIBUTING.md ← Contribution workflow (branch → commit → PR)
└── RELEASING.md ← Release checklist (version bump → tag → GHCR publish)
Entry points
| Scenario |
Entry |
| Browser visit |
app/src/main.js → mounts StandaloneApp or HubApp |
| Docker container start |
oci/app/docker-entrypoint.sh → writes config.js → starts nginx |
| First data import |
importer/import.sh → osmium pipeline → osm2pgsql → api.sql |
| API function update (no re-import) |
make db-apply → runs api.sql against the running DB |
| Adding a device type |
app/src/lib/objPlaygroundEquipment.js |
| Adding a sport pitch translation |
locales/en.json + locales/de.json |
| Adding a filter |
app/src/stores/filters.js + app/src/lib/api.js + importer/api.sql + app/src/components/FilterPanel.svelte |
Integration points
| From |
To |
Protocol |
| Browser |
nginx |
HTTP |
| nginx |
PostgREST |
HTTP proxy (/api/ → :3000) |
| PostgREST |
PostgreSQL |
SQL (pg wire protocol) |
| Hub browser |
Data-node nginx |
CORS HTTP (/api/rpc/*) |
| Importer |
Geofabrik |
HTTPS download |
| Importer |
Nominatim |
HTTPS REST (bbox lookup) |
| Frontend |
Nominatim |
HTTPS REST (search) |
| Frontend |
Panoramax |
HTTPS iframe + API |
| Frontend |
Mangrove.reviews |
HTTPS REST (reviews) |
| Frontend |
CartoDB |
HTTPS (map tiles) |