260 lines
10 KiB
JSON
260 lines
10 KiB
JSON
{
|
|
"_meta": {
|
|
"updated_at": "2026-06-09T00:00:00Z",
|
|
"commit": "01f7456b81dd55d477d0bd7530df818f61a873c2",
|
|
"version": 1
|
|
},
|
|
"entries": {
|
|
"apps/api/src/index.ts": {
|
|
"exports": ["app"],
|
|
"imports": [
|
|
"@hono/node-server",
|
|
"@hono/node-server/serve-static",
|
|
"hono",
|
|
"./routes/health.js",
|
|
"./routes/me.js",
|
|
"./routes/events.js",
|
|
"./routes/sse.js",
|
|
"./auth/middleware.js",
|
|
"./auth/devBypass.js",
|
|
"./broker/poller.js",
|
|
"./broker/outboxWorker.js"
|
|
],
|
|
"type": "entry-point",
|
|
"notes": "Hono app factory + HTTP server; mounts routes, OIDC guard, static PWA assets. Broker workers started only when isMainModule()."
|
|
},
|
|
"apps/api/src/routes/events.ts": {
|
|
"exports": ["eventsRouter"],
|
|
"imports": [
|
|
"node:crypto",
|
|
"hono",
|
|
"@hono/zod-validator",
|
|
"zod",
|
|
"drizzle-orm",
|
|
"../db/client.js",
|
|
"../db/schema.js",
|
|
"../broker/expand.js",
|
|
"../broker/vevent.js",
|
|
"../auth/middleware.js",
|
|
"../auth/user.js",
|
|
"../auth/devBypass.js"
|
|
],
|
|
"type": "module",
|
|
"notes": "GET /api/events (windowed), POST /api/events/create, PATCH /api/events/:uid/edit, DELETE /api/events/:uid, GET /api/events/sync-status, GET /api/events/writable-calendars. Writes enqueue to calendarOutbox only — never calls Fastmail directly."
|
|
},
|
|
"apps/api/src/routes/me.ts": {
|
|
"exports": ["meRouter"],
|
|
"imports": [
|
|
"hono",
|
|
"../auth/middleware.js",
|
|
"../auth/user.js",
|
|
"../auth/devBypass.js"
|
|
],
|
|
"type": "module",
|
|
"notes": "GET /api/me — returns { user: { id, displayName, color } }. Upserts user on first login."
|
|
},
|
|
"apps/api/src/routes/sse.ts": {
|
|
"exports": ["sseRouter"],
|
|
"imports": ["hono", "hono/streaming"],
|
|
"type": "module",
|
|
"notes": "GET /api/sse/heartbeat — server-sent events smoke-test; 10s interval heartbeat. Phase 4 SSE fan-out to be added."
|
|
},
|
|
"apps/api/src/routes/health.ts": {
|
|
"exports": ["healthRouter"],
|
|
"imports": ["hono", "../db/client.js", "drizzle-orm"],
|
|
"type": "module",
|
|
"notes": "GET /health — unauthenticated. Runs SELECT 1 against DB; returns { ok, db }."
|
|
},
|
|
"apps/api/src/db/schema.ts": {
|
|
"exports": ["users", "memberCredentials", "calendars", "calendarEvents", "calendarOutbox"],
|
|
"imports": ["drizzle-orm/mysql-core"],
|
|
"type": "config",
|
|
"notes": "Drizzle schema for all 5 MariaDB tables. calendarOutbox status enum: pending|done|failed|dead. calendarEvents dual-field dtstart (dtstartUtc / dtstartDate) for timed vs all-day."
|
|
},
|
|
"apps/api/src/db/client.ts": {
|
|
"exports": ["db"],
|
|
"imports": ["drizzle-orm/mysql2", "mysql2/promise"],
|
|
"type": "module",
|
|
"notes": "Drizzle client bound to mysql2 pool. Reads DB_HOST/DB_PORT/DB_USER/DB_PASSWORD/DB_NAME from env."
|
|
},
|
|
"apps/api/src/auth/middleware.ts": {
|
|
"exports": ["oidcAuthMiddleware", "processOAuthCallback", "getAuth"],
|
|
"imports": ["@hono/oidc-auth", "hono"],
|
|
"type": "module",
|
|
"notes": "OIDC middleware for Hono. Reads OIDC_AUTH_EXTERNAL_URL (mandatory behind Pangolin), OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, OIDC_ISSUER from env."
|
|
},
|
|
"apps/api/src/auth/devBypass.ts": {
|
|
"exports": ["devAuthBypass", "DEV_USER"],
|
|
"imports": ["hono"],
|
|
"type": "module",
|
|
"notes": "Dev-only auth bypass middleware. Active only when DEV_AUTH_BYPASS=true AND NODE_ENV!=production. Augments Hono ContextVariableMap with 'user' key."
|
|
},
|
|
"apps/api/src/auth/user.ts": {
|
|
"exports": ["upsertUser", "deriveDisplayName"],
|
|
"imports": ["../db/client.js", "../db/schema.js", "drizzle-orm"],
|
|
"type": "module",
|
|
"notes": "User upsert keyed on oidc_iss + oidc_sub. deriveDisplayName: name → preferred_username → email → sub."
|
|
},
|
|
"apps/api/src/broker/poller.ts": {
|
|
"exports": ["startBrokerPoller"],
|
|
"imports": ["node-cron", "./sync.js", "../db/client.js", "../db/schema.js"],
|
|
"type": "module",
|
|
"notes": "5-minute cron that polls Fastmail CalDAV for each member credential. ctag change-detection (D-13)."
|
|
},
|
|
"apps/api/src/broker/outboxWorker.ts": {
|
|
"exports": ["startOutboxWorker"],
|
|
"imports": ["node-cron", "./write.js", "../db/client.js", "../db/schema.js"],
|
|
"type": "module",
|
|
"notes": "15-second cron that drains pending calendarOutbox rows. Dispatches create/update/delete to Fastmail. Status machine: pending → done|failed|dead."
|
|
},
|
|
"apps/api/src/broker/sync.ts": {
|
|
"exports": ["syncCalendarsForCredential"],
|
|
"imports": ["./client.js", "./expand.js", "../db/client.js", "../db/schema.js", "ical.js"],
|
|
"type": "module",
|
|
"notes": "CalDAV PROPFIND + REPORT → upserts calendars and calendarEvents rows."
|
|
},
|
|
"apps/api/src/broker/write.ts": {
|
|
"exports": ["executeOutboxRow"],
|
|
"imports": ["./client.js", "./vevent.js", "../db/client.js", "../db/schema.js"],
|
|
"type": "module",
|
|
"notes": "Executes a single outbox row: builds VEVENT, calls tsdav PUT/DELETE with If-Match etag."
|
|
},
|
|
"apps/api/src/broker/client.ts": {
|
|
"exports": ["createFastmailClient"],
|
|
"imports": ["tsdav", "./crypto.js", "../db/client.js", "../db/schema.js"],
|
|
"type": "module",
|
|
"notes": "Creates a tsdav DAVClient per member credential (decrypted AES-256-GCM)."
|
|
},
|
|
"apps/api/src/broker/crypto.ts": {
|
|
"exports": ["encrypt", "decrypt"],
|
|
"imports": ["node:crypto"],
|
|
"type": "module",
|
|
"notes": "AES-256-GCM encrypt/decrypt for Fastmail app passwords stored in memberCredentials."
|
|
},
|
|
"apps/api/src/broker/expand.ts": {
|
|
"exports": ["expandOccurrences"],
|
|
"imports": ["ical.js", "temporal-polyfill"],
|
|
"type": "module",
|
|
"notes": "Expands raw VCALENDAR string into CalendarOccurrence[] for a [start, end) window. Handles RRULE, EXDATE, DST via ical.js + Temporal."
|
|
},
|
|
"apps/api/src/broker/vevent.ts": {
|
|
"exports": ["buildVevent", "extractRruleString"],
|
|
"imports": ["ical.js"],
|
|
"type": "module",
|
|
"notes": "Builds VCALENDAR/VEVENT strings from CreateEventPayload. extractRruleString preserves RRULE on calendar-move edits."
|
|
},
|
|
"apps/pwa/src/main.tsx": {
|
|
"exports": [],
|
|
"imports": ["react-dom/client", "./App.tsx"],
|
|
"type": "entry-point",
|
|
"notes": "React root mount."
|
|
},
|
|
"apps/pwa/src/App.tsx": {
|
|
"exports": ["default"],
|
|
"imports": [
|
|
"react",
|
|
"@tanstack/react-query",
|
|
"./components/CalendarShell.tsx",
|
|
"./components/InstallPrompt.tsx"
|
|
],
|
|
"type": "entry-point",
|
|
"notes": "Root component. Sets up QueryClient, renders CalendarShell + InstallPrompt."
|
|
},
|
|
"apps/pwa/src/api/client.ts": {
|
|
"exports": [
|
|
"fetchMe",
|
|
"fetchEvents",
|
|
"createEvent",
|
|
"updateEvent",
|
|
"deleteEvent",
|
|
"fetchSyncStatus",
|
|
"fetchWritableCalendars"
|
|
],
|
|
"imports": [],
|
|
"type": "module",
|
|
"notes": "Typed fetch wrappers for all API endpoints. Uses credentials: 'include' + redirect: 'manual' for OIDC opaqueredirect detection."
|
|
},
|
|
"apps/pwa/src/components/CalendarShell.tsx": {
|
|
"exports": ["CalendarShell"],
|
|
"imports": [
|
|
"react",
|
|
"@tanstack/react-query",
|
|
"@schedule-x/react",
|
|
"../api/client.ts",
|
|
"../lib/calendarConfig.ts",
|
|
"../lib/hydrateEvents.ts",
|
|
"../lib/loginRedirect.ts",
|
|
"../store/calendarStore.ts",
|
|
"./EventDetailPopover.tsx",
|
|
"./EventForm.tsx",
|
|
"./SyncStateToast.tsx",
|
|
"./ColorLegend.tsx",
|
|
"./SkeletonCalendar.tsx",
|
|
"./ErrorBoundary.tsx"
|
|
],
|
|
"type": "module",
|
|
"notes": "Top-level calendar view. Orchestrates TanStack Query fetches, Schedule-X calendar, event create/edit/delete flows, sync toasts."
|
|
},
|
|
"apps/pwa/src/components/EventForm.tsx": {
|
|
"exports": ["EventForm"],
|
|
"imports": ["react", "../api/client.ts"],
|
|
"type": "module",
|
|
"notes": "Create/edit event form. Posts to createEvent/updateEvent. Supports recurrence presets, allDay toggle, calendar picker."
|
|
},
|
|
"apps/pwa/src/components/EventDetailPopover.tsx": {
|
|
"exports": ["EventDetailPopover"],
|
|
"imports": ["react", "../api/client.ts"],
|
|
"type": "module",
|
|
"notes": "Popover shown on event click. Shows title/time/location/description, edit/delete actions."
|
|
},
|
|
"apps/pwa/src/components/SyncStateToast.tsx": {
|
|
"exports": ["SyncStateToast"],
|
|
"imports": ["react", "@tanstack/react-query", "../api/client.ts"],
|
|
"type": "module",
|
|
"notes": "Polls /api/events/sync-status to show pending → done | failed toast for async CalDAV writes."
|
|
},
|
|
"apps/pwa/src/components/ColorLegend.tsx": {
|
|
"exports": ["ColorLegend"],
|
|
"imports": ["react", "../store/calendarStore.ts"],
|
|
"type": "module",
|
|
"notes": "Displays per-member color swatches from calendarStore."
|
|
},
|
|
"apps/pwa/src/store/calendarStore.ts": {
|
|
"exports": ["useCalendarStore"],
|
|
"imports": ["zustand"],
|
|
"type": "module",
|
|
"notes": "Zustand store for UI-only state: selectedDateRange, calendarId→color map, drawer open/closed. No server state."
|
|
},
|
|
"apps/pwa/src/lib/calendarConfig.ts": {
|
|
"exports": ["buildCalendarConfig"],
|
|
"imports": [],
|
|
"type": "module",
|
|
"notes": "Builds Schedule-X calendar config from member color map and MeUser."
|
|
},
|
|
"apps/pwa/src/lib/hydrateEvents.ts": {
|
|
"exports": ["hydrateEvents"],
|
|
"imports": ["../api/client.ts"],
|
|
"type": "module",
|
|
"notes": "Maps CalendarOccurrence[] → Schedule-X event objects. Routes by isShared/ownerUserId (never calendarId)."
|
|
},
|
|
"apps/pwa/src/lib/eventDateTime.ts": {
|
|
"exports": ["formatEventDateTime", "toScheduleXDateTime"],
|
|
"imports": ["temporal-polyfill"],
|
|
"type": "module",
|
|
"notes": "Date/time formatting helpers for Schedule-X event start/end fields."
|
|
},
|
|
"apps/pwa/src/lib/loginRedirect.ts": {
|
|
"exports": ["maybeRedirectToLogin"],
|
|
"imports": [],
|
|
"type": "module",
|
|
"notes": "Top-level navigation to /api/login when OIDC 302/opaqueredirect detected. CORS-bypass strategy."
|
|
},
|
|
"apps/pwa/src/lib/colorUtils.ts": {
|
|
"exports": ["assignMemberColors"],
|
|
"imports": [],
|
|
"type": "module",
|
|
"notes": "Assigns hex colors from palette to members deterministically."
|
|
}
|
|
}
|
|
}
|