diff --git a/modules/rcal/mod.ts b/modules/rcal/mod.ts index 25e9647..3080f2e 100644 --- a/modules/rcal/mod.ts +++ b/modules/rcal/mod.ts @@ -275,10 +275,7 @@ function seedDemoIfEmpty(space: string) { if (d.meta) (d.meta as any).seeded = true; }); - // Verify tags survived Automerge change - const verifyDoc = _syncServer!.getDoc(docId); - const firstEv = verifyDoc ? Object.values(verifyDoc.events)[0] : null; - console.log(`[Cal] Demo data seeded: 2 sources, 7 events — tags: ${JSON.stringify(firstEv?.tags)}, has_key: ${'tags' in (firstEv || {})}`); + console.log("[Cal] Demo data seeded: 2 sources, 7 events"); } // ── API: Events ── @@ -340,25 +337,6 @@ routes.get("/api/events", async (c) => { return c.json({ count: rows.length, results: rows }); }); -// DEBUG: raw Automerge event data (temporary) -routes.get("/api/events-debug", async (c) => { - const space = c.req.param("space") || "demo"; - const dataSpace = c.get("effectiveSpace") || space; - const doc = ensureDoc(dataSpace); - const events = Object.values(doc.events).slice(0, 3); - const raw = events.map((e) => ({ - title: e.title, - tags_value: e.tags, - tags_type: typeof e.tags, - tags_truthy: !!e.tags, - tags_isArray: Array.isArray(e.tags), - tags_json: JSON.stringify(e.tags), - tags_arrayFrom: e.tags ? Array.from(e.tags) : 'WAS_FALSY', - has_tags_key: 'tags' in e, - all_keys: Object.keys(e), - })); - return c.json(raw); -}); // POST /api/events — create event routes.post("/api/events", async (c) => { @@ -1054,6 +1032,41 @@ export function getUpcomingEventsForMI(space: string, days = 14, limit = 5): MIC })); } +/** + * Migrate existing events: add `tags` field where missing. + * Automerge docs created before the tags schema update won't have the key. + * This runs on every startup — it's a no-op if tags already exist. + */ +function migrateTagsField(space: string) { + if (!_syncServer) return; + const docId = calendarDocId(space); + const doc = _syncServer.getDoc(docId); + if (!doc) return; + + const needsMigration = Object.values(doc.events).some((e) => !('tags' in e)); + if (!needsMigration) return; + + // Tag assignment for known demo events + const demoTags: Record = { + "rSpace Launch Party": ["launch", "community"], + "Provider Onboarding Workshop": ["onboarding", "cosmolocal"], + "Weekly Community Standup": ["recurring", "community"], + "Sprint: Module Seeding & Polish": ["sprint", "dev"], + "rFlows Budget Review": ["governance", "finance"], + "Cosmolocal Design Sprint": ["sprint", "design", "travel"], + "Q1 Retrospective": ["recurring", "community", "retrospective"], + }; + + _syncServer.changeDoc(docId, 'migrate: add tags field', (d) => { + for (const ev of Object.values(d.events)) { + if (!('tags' in ev)) { + (ev as any).tags = demoTags[ev.title] || null; + } + } + }); + console.log(`[Cal] Migrated tags field for ${space}`); +} + export const calModule: RSpaceModule = { id: "rcal", name: "rCal", @@ -1069,6 +1082,16 @@ export const calModule: RSpaceModule = { _syncServer = ctx.syncServer; // Seed demo data for the default space seedDemoIfEmpty("demo"); + + // Migrate tags field for all spaces that have cal docs (runs after loadAllDocs via setTimeout) + setTimeout(() => { + for (const docId of _syncServer!.listDocs()) { + if (docId.endsWith(':cal:events')) { + const space = docId.split(':')[0]; + migrateTagsField(space); + } + } + }, 5000); // Run after loadAllDocs completes }, feeds: [ { diff --git a/server/local-first/doc-persistence.ts b/server/local-first/doc-persistence.ts index 6e9b350..3cdd13a 100644 --- a/server/local-first/doc-persistence.ts +++ b/server/local-first/doc-persistence.ts @@ -186,14 +186,6 @@ async function scanDir(dir: string, syncServer: SyncServer): Promise { const doc = Automerge.load(bytes); const docId = pathToDocId(fullPath); - // Debug: check cal doc tags during load - if (docId.includes(':cal:events')) { - const events = (doc as any).events; - if (events) { - const first = Object.values(events)[0] as any; - console.log(`[DocStore] Loading ${docId}: tags=${JSON.stringify(first?.tags)}, has_tags=${'tags' in (first || {})}, keys=${Object.keys(first || {}).filter((k: string) => k.startsWith('t'))}`); - } - } syncServer.setDoc(docId, doc); count++; } catch (e) {