# Erfaring: SpacetimeDB-integrasjon ## 1. Genererte bindings — navnekonvensjoner SpacetimeDB sin `spacetime generate --lang typescript` produserer bindings med inkonsistente konvensjoner. Sjekk **alltid** de genererte filene i stedet for å gjette. | Hva | Konvensjon | Eksempel | |---|---|---| | Tabell-accessor på `conn.db` | snake_case | `conn.db.chat_message` | | Reducer-accessor på `conn.reducers` | camelCase | `conn.reducers.sendMessage()` | | Felt-navn i tabellrader | camelCase | `row.channelId`, `row.authorName` | | Reducer-parametere | Enkelt objekt | `sendMessage({ id, channelId, body, ... })` | **Felle:** Man forventer at tabell-accessorer er camelCase (`chatMessage`), men de er snake_case. ## 2. `DbConnection.builder()` — API-detaljer (SDK v2) ```typescript DbConnection.builder() .withUri(spacetimeUrl) .withDatabaseName(moduleName) // IKKE withModuleName .withToken(token) .onConnect((connection) => { // første param er DbConnection, ikke EventContext connection.subscriptionBuilder() .subscribe([`SELECT * FROM chat_message WHERE channel_id = '${id}'`]); }) .build(); ``` **Feller:** - `withModuleName()` finnes ikke — bruk `withDatabaseName()` - `onConnect`-callback mottar `DbConnection`, ikke `EventContext` - `onConnectError`-callback har signatur `(ctx, errMessage)` der `errMessage` er en string ## 3. BigInt-tidsstempler SpacetimeDB `Timestamp`-type bruker `microsSinceEpoch` som er `BigInt`. JavaScript kan ikke blande BigInt med Number: ```typescript // FEIL — TypeError: Cannot mix BigInt and other types const ms = micros / 1000; // RIKTIG — sjekk type, bruk BigInt-divisjon const ms = typeof micros === 'bigint' ? Number(micros / 1000n) : Number(micros) / 1000; ``` **Referanse:** `web/src/lib/chat/spacetime.svelte.ts` — `spacetimeRowToMessage()`. ## 4. Rust-modul — borrow checker med SpacetimeDB-makroer SpacetimeDB-makroer genererer kode som tar eierskap over struct-felter. Bruk verdier **før** du flytter dem inn i structs: ```rust // FEIL — channel_id er moved inn i ChatMessage, kan ikke bruke i log!() etterpå let msg = ChatMessage { channel_id, body, ... }; log::info!("Melding i kanal {}", channel_id); // borrow after move // RIKTIG — bruk verdien før struct-opprettelse let log_msg = format!("Melding i kanal {}", channel_id); let msg = ChatMessage { channel_id, body, ... }; log::info!("{}", log_msg); ``` ## 5. Publisering og lokal testing ```bash # Publiser modul mot lokal SpacetimeDB (må kjøre i Docker først) cd spacetimedb spacetime publish sidelinja-realtime --server local # Generer TypeScript-bindings spacetime generate --lang typescript --out-dir ../web/src/lib/chat/module_bindings \ --module-path . ``` **Merk:** Bruk `./dev.sh` for å starte hele stacken automatisk (inkl. SpacetimeDB publish + binding-generering). `./dev.sh --clean` starter blankt. ## 6. Arkitekturendring: SpacetimeDB som cache foran PG (mars 2026) Tidligere: Hybrid-adapter der frontend merget data fra PG (historikk) og SpacetimeDB (sanntid) med dedup, deletedIds og BigInt-workarounds. Ny modell: - **PG autoritativ** — all persistent data i PostgreSQL - **SpacetimeDB = varm cache** — worker gjør warmup (PG → ST) ved oppstart - **Frontend snakker KUN med ST** — ingen PG API-kall fra chat-adapteren - **Worker håndterer toveissynk** — ST → PG for nye/redigerte/slettede meldinger og reaksjoner ### Warmup-flyt 1. Worker starter → `warmup::run()` leser kanaler med config fra PG 2. Per kanal: sjekker `channels.config.warmup_mode` (all/messages/days/none) 3. Kaller `clear_channel` reducer (unngår duplikater ved restart) 4. **Trådbasert henting:** Finner kvalifiserende tråder, henter alle meldinger i disse (komplett med svar) - `messages`-modus: de N nyeste trådene (sortert etter siste aktivitet) - `days`-modus: alle tråder med minst én melding i tidsvinduet - Et svar som kvalifiserer tar med hele tråden (inkludert eldre trådstarter) 5. Kaller `load_messages` reducer med JSON-array 6. Laster også reaksjoner via `load_reactions` reducer ### Per-kanal konfigurasjon - Lagres i `channels.config` JSONB: `warmup_mode` + `warmup_value` - Admin-UI: `/admin/channels` — tabell med inline-redigering - Default: `"all"` (last alt). Andre: `"messages"` (siste N tråder), `"days"` (siste N dager), `"none"` (inaktiv) ### Sync-flyt (ST → PG) - SyncOutbox-events prosesseres hver 1. sekund - Støtter: `messages/insert`, `messages/delete`, `messages/update`, `message_reactions/insert`, `message_reactions/delete` ### Fallback PG-polling adapter (`pg.svelte.ts`) brukes kun når SpacetimeDB ikke er konfigurert. Markeres som `readonly: true`.