Files
Job-Info/SVELTE_DEVELOPMENT_SYSTEM.md
T
aewing 42ff3e8f33
Build & Deploy / Test & Lint (push) Has been cancelled
Build & Deploy / Security Scan (push) Has been cancelled
Code Quality / Code Quality Checks (push) Has been cancelled
Code Quality / Dependency Vulnerabilities (push) Has been cancelled
Code Quality / Performance Testing (push) Has been cancelled
Build & Deploy / Build Docker Images (push) Has been cancelled
Build & Deploy / Deploy to Kubernetes (push) Has been cancelled
Add Mode6Test: copy of Mode5Test running on port 3000
2026-01-20 13:43:29 +00:00

11 KiB

Svelte + Microsoft OAuth Development System

Comprehensive Methodology, Standards, and Reference Tools


PHASE 1: RESEARCH & ARCHITECTURE (IN PROGRESS)

1. Technology Stack (Monica-Enforced Standards)

Frontend Runtime: Svelte (with SvelteKit for routing/build) Backend: Bun + Hono (unchanged - proven working) Build Tool: Vite (integrated with SvelteKit) Language: TypeScript (both frontend and backend) Styling: TailwindCSS + PostCSS Storage: IndexedDB (device storage), PocketBase (token persistence) Auth: Microsoft OAuth2 directly + Service Worker credential management


PART A: MICROSOFT OAUTH2 ARCHITECTURE

A1. Overall Flow

User clicks "Sign In" 
  → Service Worker intercepts Microsoft OAuth redirect URL
  → Service Worker extracts auth code
  → Backend (Hono) exchanges code for tokens using Service Worker credentials
  → Backend stores refresh token in PocketBase under user collection
  → Backend returns access token to frontend
  → Frontend stores short-lived access token in memory/sessionStorage
  → Service Worker manages token refresh on expiration

A2. Why This Approach

  • Direct OAuth with Microsoft: No PocketBase auth, eliminates middle-layer complexity
  • Service Worker credentials: Never expose client secret to frontend, server handles all token exchanges
  • PocketBase for token storage: Persistent, synced, secure backend storage of refresh tokens
  • Device storage (IndexedDB): User data (jobs, cache) stored locally without external service
  • Token refresh automation: Service Worker checks token expiry and refreshes proactively

A3. Key Endpoints Needed

POST /api/auth/authorize
  - Accepts: { code, state }
  - Returns: { accessToken, expiresIn, user }
  - Action: Exchanges OAuth code for tokens via service worker credentials

POST /api/auth/refresh
  - Accepts: { }
  - Returns: { accessToken, expiresIn }
  - Action: Uses stored refresh token to get new access token

GET /api/auth/status
  - Returns: { isAuthenticated, user, tokenExpiry }

POST /api/auth/logout
  - Clears refresh token from PocketBase

A4. Microsoft Graph API Integration

  • Endpoint: https://graph.microsoft.com/v1.0/me/drive/root/children
  • Token required: accessToken in Authorization header
  • Caching strategy: IndexedDB + device storage for file listings
  • Refresh strategy: Access token → 1 hour expiry, Service Worker refreshes automatically

PART B: SVELTE ARCHITECTURE

B1. Project Structure

frontend/
  src/
    routes/
      +page.svelte (home/jobs list)
      signin/
        +page.svelte (Microsoft OAuth login)
      folder/
        [id]/
          +page.svelte (folder view)
    components/
      JobCard.svelte
      FileListItem.svelte
      SearchBar.svelte
    stores/
      auth.ts (writable stores for user, tokens)
      jobs.ts (derived store for job data)
      files.ts (derived store for file listings)
    services/
      oauth.ts (OAuth2 flow handler)
      graph.ts (Microsoft Graph API calls)
      storage.ts (IndexedDB operations)
    utils/
      constants.ts
    app.svelte (layout wrapper)
  svelte.config.js
  vite.config.ts
  tailwind.config.ts

B2. Svelte Stores Strategy

// auth.ts - Global auth state
export const user = writable<User | null>(null);
export const accessToken = writable<string | null>(null);
export const isAuthenticated = derived([user], ([$user]) => !!$user);

// jobs.ts - Derived from IndexedDB cache
export const jobs = writable<Job[]>([]);
export const jobsLoading = writable(false);

// files.ts - Derived from Microsoft Graph
export const files = writable<MicrosoftFile[]>([]);
export const filesLoading = writable(false);

B3. Service Worker Role

Purpose: Token refresh automation + OAuth redirect interception
Tasks:
  - Listens for token expiry (via postMessage from frontend)
  - Automatically calls POST /api/auth/refresh before expiry
  - Updates accessToken in frontend via postMessage
  - Handles Microsoft OAuth redirect URL capture (if needed)
  - Persists tokens in sessionStorage (never exposed to main thread)

PART C: DEVICE STORAGE STRATEGY (IndexedDB)

C1. Database Schema

// Database: "jobinfo-app"
// Stores:
// 1. jobs
//    Key: jobId (string)
//    Value: { id, title, company, postedDate, ... }
//    Index: "company", "postedDate"

// 2. fileCache
//    Key: folderId (string)
//    Value: { folderId, files: [...], lastRefreshed: timestamp }
//    Index: "lastRefreshed"

// 3. userCache
//    Key: "current"
//    Value: { user object from Microsoft }

// 4. tokenMetadata (not storing tokens, just metadata)
//    Key: "current"
//    Value: { expiresAt: timestamp, refreshedAt: timestamp }

C2. IndexedDB Helper Functions

// storage.ts
export async function saveJobs(jobs: Job[]): Promise<void>
export async function getJobs(): Promise<Job[]>
export async function saveFileCache(folderId: string, files: MicrosoftFile[]): Promise<void>
export async function getFileCache(folderId: string): Promise<MicrosoftFile[] | null>
export async function clearExpiredCache(): Promise<void>

PART D: BACKEND (HONO) UPDATES

D1. New Routes

// backend/auth-routes.ts
app.post('/auth/authorize', async (c) => {
  // 1. Validate request { code, state }
  // 2. Exchange code for tokens using Microsoft OAuth credentials
  // 3. Store refresh token in PocketBase
  // 4. Return accessToken to frontend
})

app.post('/auth/refresh', async (c) => {
  // 1. Get user from auth header
  // 2. Retrieve refresh token from PocketBase
  // 3. Exchange for new access token
  // 4. Return new access token
})

app.get('/auth/status', async (c) => {
  // Return current auth status
})

D2. Environment Variables Required

MICROSOFT_CLIENT_ID=
MICROSOFT_CLIENT_SECRET=
MICROSOFT_TENANT_ID=
MICROSOFT_REDIRECT_URI=http://localhost:5173/auth/callback
POCKETBASE_URL=https://pocketbase.ccllc.pro
POCKETBASE_ADMIN_EMAIL=
POCKETBASE_ADMIN_PASSWORD=

PART E: DEVELOPMENT RULES (NON-NEGOTIABLE)

RULE E1: Code Documentation Standard

Every file, every function, every store must have:

/**
 * MODULE: [Name]
 * PURPOSE: [What it does]
 * DEPENDENCIES: [External services, stores, APIs]
 * PERSISTENCE: [How/where data is stored]
 * NOTES: [Any gotchas, edge cases, security considerations]
 */

RULE E2: Component Structure

Every Svelte component must have:

<!-- 
  COMPONENT: ComponentName
  PURPOSE: [What it renders]
  PROPS: [Detailed prop documentation]
  EVENTS: [Events this component dispatches]
  STATE: [Local reactive state]
-->

<script lang="ts">
  // Props with detailed comments
  // Reactive declarations
  // Lifecycle hooks
  // Event handlers
</script>

<style>
  /* TailwindCSS classes only, no custom CSS unless documented as necessary */
</style>

RULE E3: Store Pattern

Every Svelte store follows this pattern:

/**
 * STORE: storeName
 * PURPOSE: [What data it holds]
 * UPDATES: [How and when it's updated]
 * SUBSCRIPTIONS: [Components that use it]
 */

export const storeName = writable<Type>(initialValue);

// Every update must be wrapped in a clearly named function:
export async function updateStoreName(newValue: Type): Promise<void> {
  // Detailed comment explaining the update
  storeName.set(newValue);
}

RULE E4: API Call Pattern

Every API call to backend must follow:

/**
 * API: /endpoint
 * METHOD: GET/POST/etc
 * HEADERS REQUIRED: [Auth, Content-Type, etc]
 * REQUEST BODY: { ... with detailed types }
 * RESPONSE: { ... with detailed types }
 * ERRORS: [What can go wrong and how we handle it]
 * RETRY STRATEGY: [Is this retried? When?]
 */

export async function apiCallName(params: Type): Promise<ResponseType> {
  const token = get(accessToken);
  if (!token) throw new Error('Not authenticated');
  
  try {
    const response = await fetch(`/api/endpoint`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`,
      },
      body: JSON.stringify(params),
    });
    
    if (!response.ok) throw new Error(`API error: ${response.status}`);
    return response.json();
  } catch (error) {
    console.error('API call failed:', error);
    throw error;
  }
}

RULE E5: Error Handling Standard

No silent failures. Every promise, every fetch, every async operation:

try {
  // Operation
} catch (error) {
  // 1. Log error with context
  console.error('Context: what were we trying to do', error);
  
  // 2. Determine if recoverable
  if (isRecoverable(error)) {
    // Attempt recovery
  } else {
    // Notify user or escalate
  }
}

RULE E6: Token Handling

  • Never store tokens in localStorage (vulnerable to XSS)
  • Access token: In memory only (lost on refresh, that's intentional)
  • Refresh token: Only on backend in PocketBase
  • Service Worker: Can read from sessionStorage only, never localStorage
  • Token expiry check: Frontend checks before each API call, Service Worker proactively refreshes

RULE E7: Code Quality Standards

  • Every TypeScript file has strict: true in tsconfig
  • No any types without explicit comment explaining why
  • No console.log in production code (use proper logging)
  • All async operations have error handling
  • All state changes are intentional and logged in stores
  • No magic numbers or strings (use CONST_DEFINED_AT_TOP)

PART F: DEVELOPMENT CHECKLIST

Pre-Build Checklist

  • All imports are TypeScript (.ts, not .js)
  • All stores have update functions with documentation
  • All API calls have error handling
  • All tokens are handled per RULE E6
  • All components follow component structure in RULE E2
  • No hardcoded URLs (use constants)
  • No console.log statements
  • All environment variables are documented

Testing Checklist

  • Login flow works (OAuth → token storage → API calls)
  • Token refresh works (Service Worker updates token)
  • File listing loads from Microsoft Graph
  • File listing cached in IndexedDB
  • Search/filter works from cached files
  • Logout clears tokens and cache
  • Offline mode works (uses cached data)

PART G: SESSION LOG LOCATION

All work logged to: /home/admin/Job-Info-Test/logs/SVELTE_SESSION_LOG.txt

Format:

[2026-01-19 10:00] Component/Store Created: ComponentName
- PURPOSE: [What it does]
- IMPLEMENTATION: [Key decisions]
- DEPENDENCIES: [What it depends on]
- STATUS: Completed / In Progress / Blocked

[2026-01-19 10:15] Fix: [Issue resolved]
- PROBLEM: [What was broken]
- DIAGNOSIS: [How we found the issue]
- SOLUTION: [What we changed]
- TESTING: [How we verified it works]
- STATUS: Verified working

NEXT STEPS

  1. Create this document (DONE)
  2. Research Svelte + SvelteKit best practices
  3. Research Microsoft OAuth2 + Service Worker patterns
  4. Create backend OAuth routes
  5. Create Svelte project structure
  6. Create auth store and OAuth service
  7. Create IndexedDB service
  8. Create Microsoft Graph service
  9. Build UI components
  10. Integrate and test end-to-end

Status: FOUNDATION DOCUMENT CREATED - Ready for Research Phase