diff --git a/Mode1Svelte/README.md b/Mode1Svelte/README.md
new file mode 100644
index 0000000..fa8fb0a
--- /dev/null
+++ b/Mode1Svelte/README.md
@@ -0,0 +1,158 @@
+# Mode1Svelte - Auth Module Test Suite
+
+Self-contained test environment for the auth-module. This version is identical to Mode1 currently, serving as a foundation for future Svelte integration.
+
+## Structure
+
+```
+Mode1Svelte/
+├── auth-module/ # Local copy of auth-module (not linked to root)
+├── backend/ # Backend server with auth endpoints
+│ └── server.ts # Hono app with Graph token and PB validation APIs
+├── frontend/ # Frontend test interface
+│ └── index.html # Interactive test dashboard
+├── logs/ # Application logs
+├── package.json # Dependencies (auth module + Hono + PocketBase)
+└── bun.lock # Locked dependencies
+```
+
+## Features
+
+✓ **Frontend Auth (PocketBaseAuth)**
+ - OAuth2 login initialization
+ - Auth state retrieval
+ - Token refresh
+ - Logout
+
+✓ **Backend Auth (GraphTokenManager)**
+ - Microsoft Graph token acquisition
+ - Token caching (with 60s expiration buffer)
+ - Cache validity checking
+ - Cache refresh
+
+✓ **PocketBase Validation (PocketBaseValidator)**
+ - User token validation
+ - User record retrieval
+ - Token comparison (Graph vs PocketBase)
+
+## Running Mode1Svelte
+
+### Via Orchestrator (recommended)
+
+```bash
+# Switch to Mode1Svelte
+curl -X POST http://localhost:3006/switch/Mode1Svelte
+
+# Check status
+curl http://localhost:3006/status
+```
+
+### Direct
+
+```bash
+cd Mode1Svelte
+bun install
+PORT=3005 bun run backend/server.ts
+```
+
+Then visit: `http://localhost:3005`
+
+## Future: Svelte Integration
+
+This project is set up as an independent Mode to eventually convert to SvelteKit while maintaining the same API endpoints and auth-module integration.
+
+## Structure
+
+```
+Mode1/
+├── auth-module/ # Local copy of auth-module (not linked to root)
+├── backend/ # Backend server with auth endpoints
+│ └── server.ts # Hono app with Graph token and PB validation APIs
+├── frontend/ # Frontend test interface
+│ └── index.html # Interactive test dashboard
+├── logs/ # Application logs
+├── package.json # Dependencies (auth module + Hono + PocketBase)
+└── bun.lock # Locked dependencies
+```
+
+## Features Tested
+
+✓ **Frontend Auth (PocketBaseAuth)**
+ - OAuth2 login initialization
+ - Auth state retrieval
+ - Token refresh
+ - Logout
+
+✓ **Backend Auth (GraphTokenManager)**
+ - Microsoft Graph token acquisition
+ - Token caching (with 60s expiration buffer)
+ - Cache validity checking
+ - Cache refresh
+
+✓ **PocketBase Validation (PocketBaseValidator)**
+ - User token validation
+ - User record retrieval
+
+## Running Mode1
+
+### Via Orchestrator (recommended)
+
+```bash
+# Switch to Mode1
+curl -X POST http://localhost:3006/switch/Mode1
+
+# Check status
+curl http://localhost:3006/status
+```
+
+### Direct
+
+```bash
+# Install dependencies (already done)
+bun install
+
+# Start server
+bun run backend/server.ts
+
+# Access dashboard
+open http://localhost:3005
+```
+
+## API Endpoints
+
+### Configuration
+- `GET /api/auth/config` - Get frontend configuration
+
+### Graph Token Management
+- `GET /api/auth/graph/token` - Get current Graph token (with expiration)
+- `POST /api/auth/refresh-graph` - Force refresh Graph token
+
+### PocketBase Validation
+- `POST /api/auth/validate-pb-token` - Validate a PocketBase token
+
+### Health
+- `GET /health` - Health check
+
+## Environment Variables
+
+Uses `/home/admin/secrets/.env` via auth-module:
+- `CLIENT_ID` - Microsoft application ID
+- `TENANT_ID` - Azure tenant ID
+- `CLIENT_SECRET` - Microsoft client secret
+- `PB_URL` - PocketBase frontend URL
+- `PB_DB` - PocketBase backend URL
+
+## Dependencies
+
+All dependencies are installed locally in `node_modules/`:
+- `hono` - Web framework
+- `pocketbase` - PocketBase client
+- `@azure/msal-node` - Microsoft authentication
+- `dotenv` - Environment variable loading
+
+## Notes
+
+- This Mode1 is **completely self-contained** with its own auth-module copy
+- No dependencies on root auth-module
+- Safe to modify and test independently
+- All secrets come from `/home/admin/secrets/.env`
diff --git a/Mode1Svelte/auth-module/README.md b/Mode1Svelte/auth-module/README.md
new file mode 100644
index 0000000..2d55281
--- /dev/null
+++ b/Mode1Svelte/auth-module/README.md
@@ -0,0 +1,248 @@
+# Auth Module
+
+Standalone authentication module for PocketBase OAuth2 + Microsoft Graph integration.
+
+## Features
+
+- **Frontend**: PocketBase OAuth2 authentication with Microsoft provider
+- **Backend**: Microsoft Graph token management with automatic caching
+- **No dependencies on project-specific code** - Use in any project
+
+## Installation
+
+Copy the `auth-module` folder to your project:
+
+```bash
+cp -r auth-module /path/to/your/project/
+```
+
+Install dependencies:
+
+```bash
+bun add pocketbase @azure/msal-node
+```
+
+## Frontend Usage
+
+### Basic Setup
+
+**Important**: The frontend is browser-side code. It must receive `pbUrl` from the consuming application (not from the backend).
+
+```typescript
+import { PocketBaseAuth } from './auth-module/frontend';
+
+// In your project, load PB_URL from environment/config and pass it to frontend
+const auth = new PocketBaseAuth({
+ pbUrl: process.env.PB_URL, // Required - must be provided by consuming application
+ collection: 'Users',
+ provider: 'microsoft',
+ loginContainerId: 'loginContainer',
+ userDisplayNameId: 'userDisplayName',
+ userEmailId: 'userEmailValue',
+ loginBtnId: 'loginBtn',
+ loginErrorId: 'loginError',
+});
+```
+
+### HTML Required
+
+```html
+
+
+
+
+
+
+
+
+
+```
+
+### Event Callbacks
+
+```typescript
+const auth = new PocketBaseAuth(config, {
+ onAuthSuccess: (state) => {
+ console.log('Logged in:', state.user.email);
+ // Initialize other systems here
+ },
+ onAuthFailure: (error) => {
+ console.error('Login failed:', error);
+ },
+ onTokenUpdate: (state) => {
+ console.log('Token refreshed');
+ },
+ onUiUpdate: (state) => {
+ console.log('UI updated');
+ },
+});
+```
+
+### Methods
+
+```typescript
+// Get current auth state
+const state = auth.getAuthState();
+console.log(state.isAuthenticated, state.user);
+
+// Check token status
+const status = await auth.checkTokenStatus();
+
+// Logout
+auth.logout();
+
+// Get raw PocketBase instance if needed
+const pb = auth.getPocketBase();
+```
+
+## Backend Usage
+
+### Setup (server.ts)
+
+```typescript
+import { GraphTokenManager, PocketBaseValidator, BackendAuth } from './auth-module/backend';
+
+// Option 1: Use individual managers
+const graphMgr = new GraphTokenManager({
+ clientId: process.env.CLIENT_ID,
+ tenantId: process.env.TENANT_ID,
+ clientSecret: process.env.CLIENT_SECRET,
+});
+
+const pbValidator = new PocketBaseValidator(process.env.PB_DB);
+
+// Option 2: Use combined manager
+const backendAuth = new BackendAuth({
+ clientId: process.env.CLIENT_ID,
+ tenantId: process.env.TENANT_ID,
+ clientSecret: process.env.CLIENT_SECRET,
+}, process.env.PB_DB);
+```
+
+### Get Graph Token
+
+```typescript
+// Get token string
+const token = await graphMgr.getToken();
+
+// Get token with expiration
+const { token, expiresOnISO } = await graphMgr.getTokenWithExpiry();
+
+// Check if cached and valid
+if (graphMgr.isTokenValid()) {
+ console.log('Using cached token');
+}
+
+// Clear cache (force refresh)
+graphMgr.clearCache();
+```
+
+### Validate PocketBase Token
+
+```typescript
+// Validate token
+const isValid = await pbValidator.validateUserToken(token);
+
+// Get user record
+const user = await pbValidator.getUserRecord(token);
+
+// Get PocketBase instance
+const pb = pbValidator.getPocketBase();
+```
+
+### Endpoint Example
+
+```typescript
+app.get('/api/graph/status', async (c) => {
+ try {
+ const { token, expiresOnISO } = await graphMgr.getTokenWithExpiry();
+ return c.json({
+ active: true,
+ expiresOnISO,
+ });
+ } catch (error) {
+ return c.json(
+ { active: false, error: error.message },
+ 500
+ );
+ }
+});
+
+app.post('/api/submit', async (c) => {
+ const body = await c.req.json();
+ const pbToken = body.pbToken;
+
+ if (!pbToken) {
+ return c.json({ error: 'Missing pbToken' }, 401);
+ }
+
+ const isValid = await pbValidator.validateUserToken(pbToken);
+ if (!isValid) {
+ return c.json({ error: 'Invalid token' }, 401);
+ }
+
+ // Token is valid, proceed with business logic
+ // ...
+});
+```
+
+## Environment Variables
+
+Required in `.env` file:
+
+```env
+CLIENT_ID=your-microsoft-app-id
+TENANT_ID=your-azure-tenant-id
+CLIENT_SECRET=your-microsoft-client-secret
+PB_DB=https://your-pocketbase-instance.com
+PB_URL=https://your-pocketbase-instance.com
+```
+
+## Configuration
+
+### Frontend Config Options
+
+```typescript
+interface AuthConfig {
+ pbUrl: string; // PocketBase URL (required)
+ collection?: string; // Auth collection (default: Users)
+ provider?: string; // OAuth provider (default: microsoft)
+ loginContainerId?: string; // ID of login container element
+ userDisplayNameId?: string; // ID of user name display element
+ userEmailId?: string; // ID of user email display element
+ loginBtnId?: string; // ID of login button element
+ loginErrorId?: string; // ID of error message element
+}
+```
+
+### Backend Config Options
+
+```typescript
+interface BackendAuthConfig {
+ clientId?: string; // Microsoft app ID (or CLIENT_ID env var)
+ tenantId?: string; // Azure tenant ID (or TENANT_ID env var)
+ clientSecret?: string; // Client secret (or CLIENT_SECRET env var)
+}
+```
+
+## TypeScript
+
+The module exports TypeScript types for type safety:
+
+```typescript
+import { AuthState, AuthConfig, GraphTokenCache } from './auth-module/types';
+
+const state: AuthState = auth.getAuthState();
+```
+
+## Notes
+
+- **Frontend** manages user authentication only
+- **Backend** manages Graph API tokens (never exposed to client)
+- Tokens are cached in-memory on backend with automatic refresh
+- Module does not include project-specific features (alerts, etc.)
+- Each project can implement its own business logic on top
+
+## License
+
+Same as parent project
diff --git a/Mode1Svelte/auth-module/backend.ts b/Mode1Svelte/auth-module/backend.ts
new file mode 100644
index 0000000..a28027f
--- /dev/null
+++ b/Mode1Svelte/auth-module/backend.ts
@@ -0,0 +1,204 @@
+import { config } from 'dotenv';
+import { ConfidentialClientApplication } from '@azure/msal-node';
+import PocketBase from 'pocketbase';
+import { GraphTokenCache, BackendAuthConfig } from './types';
+
+// Load environment variables from shared secrets directory
+config({ path: '/home/admin/secrets/.env' });
+
+/**
+ * Configuration Manager
+ * Exposes frontend-safe configuration loaded from environment
+ */
+export class AuthConfigManager {
+ /**
+ * Get frontend configuration (safe to expose to browser)
+ */
+ static getFrontendConfig() {
+ return {
+ pbUrl: process.env.PB_URL!,
+ provider: 'microsoft',
+ collection: 'Users',
+ };
+ }
+
+ /**
+ * Get all backend secrets (never expose to frontend)
+ */
+ static getBackendConfig() {
+ return {
+ clientId: process.env.CLIENT_ID!,
+ tenantId: process.env.TENANT_ID!,
+ clientSecret: process.env.CLIENT_SECRET!,
+ pbDb: process.env.PB_DB!,
+ };
+ }
+}
+
+/**
+ * Microsoft Graph Token Management (Backend)
+ * Handles token acquisition and caching for backend Graph API calls
+ */
+export class GraphTokenManager {
+ private cca: ConfidentialClientApplication;
+ private cache: GraphTokenCache | null = null;
+ private config: Required;
+
+ constructor(config: BackendAuthConfig) {
+ this.config = {
+ clientId: config.clientId || process.env.CLIENT_ID || '',
+ tenantId: config.tenantId || process.env.TENANT_ID || '',
+ clientSecret: config.clientSecret || process.env.CLIENT_SECRET || '',
+ };
+
+ this.cca = new ConfidentialClientApplication({
+ auth: {
+ clientId: this.config.clientId,
+ authority: `https://login.microsoftonline.com/${this.config.tenantId}`,
+ clientSecret: this.config.clientSecret,
+ },
+ });
+ }
+
+ /**
+ * Get Graph token (from cache or acquire new)
+ */
+ async getToken(): Promise {
+ const now = Date.now();
+
+ // Check cache validity (with 60s buffer for expiration)
+ if (this.cache && this.cache.expiresOn - 60000 > now) {
+ return this.cache.token;
+ }
+
+ try {
+ const result = await this.cca.acquireTokenByClientCredential({
+ scopes: ['https://graph.microsoft.com/.default'],
+ });
+
+ if (!result?.accessToken) {
+ throw new Error('Failed to acquire Graph token');
+ }
+
+ const expiresOn = result.expiresOn
+ ? new Date(result.expiresOn).getTime()
+ : now + 55 * 60 * 1000; // Default 55 minutes
+
+ this.cache = {
+ token: result.accessToken,
+ expiresOn,
+ };
+
+ return result.accessToken;
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Unknown error';
+ throw new Error(`Failed to acquire Graph token: ${message}`);
+ }
+ }
+
+ /**
+ * Get token with expiration info
+ */
+ async getTokenWithExpiry(): Promise<{ token: string; expiresOnISO: string }> {
+ const token = await this.getToken();
+ const expiresOn = this.cache?.expiresOn || Date.now();
+ return {
+ token,
+ expiresOnISO: new Date(expiresOn).toISOString(),
+ };
+ }
+
+ /**
+ * Check if token is cached and valid
+ */
+ isTokenValid(): boolean {
+ if (!this.cache) return false;
+ const now = Date.now();
+ return this.cache.expiresOn - 60000 > now;
+ }
+
+ /**
+ * Clear cache (force refresh on next call)
+ */
+ clearCache(): void {
+ this.cache = null;
+ }
+}
+
+/**
+ * PocketBase Token Validation (Backend)
+ * Validates and uses user PocketBase tokens
+ */
+export class PocketBaseValidator {
+ private pb: PocketBase;
+
+ constructor(pbUrl?: string) {
+ this.pb = new PocketBase(pbUrl || process.env.PB_DB!);
+ }
+
+ /**
+ * Set user token and validate it
+ */
+ async validateUserToken(token: string): Promise {
+ try {
+ this.pb.authStore.save(token, null);
+ await this.pb.collection('Users').authRefresh();
+ return true;
+ } catch (error) {
+ console.error('Token validation failed:', error instanceof Error ? error.message : error);
+ return false;
+ }
+ }
+
+ /**
+ * Get user record from token
+ */
+ async getUserRecord(token: string): Promise {
+ try {
+ this.pb.authStore.save(token, null);
+ const record = this.pb.authStore.record || this.pb.authStore.model;
+ return record;
+ } catch (error) {
+ console.error('Failed to get user record:', error);
+ return null;
+ }
+ }
+
+ /**
+ * Get PocketBase instance
+ */
+ getPocketBase(): PocketBase {
+ return this.pb;
+ }
+}
+
+/**
+ * Combined backend auth manager
+ */
+export class BackendAuth {
+ graphTokenManager: GraphTokenManager;
+ pbValidator: PocketBaseValidator;
+
+ constructor(config: BackendAuthConfig, pbUrl?: string) {
+ this.graphTokenManager = new GraphTokenManager(config);
+ this.pbValidator = new PocketBaseValidator(pbUrl);
+ }
+
+ /**
+ * Middleware to validate PocketBase token in requests
+ * Usage: app.use('/*', (c, next) => backendAuth.validateTokenMiddleware(c, next))
+ */
+ async validateTokenMiddleware(c: any, next: any): Promise {
+ const token = c.req.header('Authorization')?.replace('Bearer ', '') ||
+ (await c.req.json().catch(() => ({})))?.pbToken;
+
+ if (token) {
+ const isValid = await this.pbValidator.validateUserToken(token);
+ if (!isValid) {
+ return c.json({ error: 'Invalid token' }, 401);
+ }
+ }
+
+ return next();
+ }
+}
diff --git a/Mode1Svelte/auth-module/frontend.ts b/Mode1Svelte/auth-module/frontend.ts
new file mode 100644
index 0000000..650805a
--- /dev/null
+++ b/Mode1Svelte/auth-module/frontend.ts
@@ -0,0 +1,250 @@
+import PocketBase from 'pocketbase';
+import { AuthConfig, AuthState, AuthCallbacks } from './types';
+
+/**
+ * PocketBase OAuth2 Frontend Module
+ * Handles user authentication and token management
+ */
+export class PocketBaseAuth {
+ private pb: PocketBase;
+ private config: Required;
+ private callbacks: AuthCallbacks;
+ private state: AuthState;
+
+ constructor(config: AuthConfig, callbacks?: Partial) {
+ this.config = {
+ pbUrl: config.pbUrl!,
+ collection: config.collection || 'Users',
+ provider: config.provider || 'microsoft',
+ loginContainerId: config.loginContainerId || 'loginContainer',
+ userDisplayNameId: config.userDisplayNameId || 'userDisplayName',
+ userEmailId: config.userEmailId || 'userEmailValue',
+ loginBtnId: config.loginBtnId || 'loginBtn',
+ loginErrorId: config.loginErrorId || 'loginError',
+ };
+
+ this.callbacks = {
+ onAuthSuccess: callbacks?.onAuthSuccess || (() => {}),
+ onAuthFailure: callbacks?.onAuthFailure || (() => {}),
+ onTokenUpdate: callbacks?.onTokenUpdate || (() => {}),
+ onUiUpdate: callbacks?.onUiUpdate || (() => {}),
+ };
+
+ this.pb = new PocketBase(this.config.pbUrl);
+ this.state = {
+ isAuthenticated: false,
+ user: null,
+ token: null,
+ };
+
+ this.init();
+ }
+
+ /**
+ * Initialize auth module
+ */
+ private init(): void {
+ this.updateAuthUI();
+ if (this.pb.authStore.isValid && this.pb.authStore.token) {
+ this.ensureUserLogged();
+ }
+ this.setupLoginButton();
+ }
+
+ /**
+ * Setup login button click handler
+ */
+ private setupLoginButton(): void {
+ const loginBtn = document.getElementById(this.config.loginBtnId);
+ if (!loginBtn) {
+ console.warn(`Login button with id "${this.config.loginBtnId}" not found`);
+ return;
+ }
+
+ loginBtn.addEventListener('click', async (e) => {
+ e.preventDefault();
+ await this.handleLogin();
+ });
+ }
+
+ /**
+ * Handle login button click
+ */
+ private async handleLogin(): Promise {
+ const loginBtn = document.getElementById(this.config.loginBtnId) as HTMLButtonElement;
+ const loginError = document.getElementById(this.config.loginErrorId) as HTMLElement;
+
+ if (!loginBtn || !loginError) return;
+
+ loginBtn.disabled = true;
+ loginBtn.textContent = 'Checking session...';
+ loginError.classList.add('hidden');
+
+ try {
+ // Try to use existing token first
+ const hadToken = await this.ensureUserLogged();
+ if (hadToken) {
+ this.resetLoginButton();
+ return;
+ }
+
+ // Otherwise perform OAuth
+ loginBtn.textContent = 'Logging in...';
+ const authData = await this.pb.collection(this.config.collection).authWithOAuth2({
+ provider: this.config.provider,
+ });
+
+ this.updateState(authData);
+ this.updateAuthUI();
+ this.callbacks.onAuthSuccess?.(this.state);
+
+ console.log('✓ Logged in with', this.config.provider);
+ } catch (error) {
+ const message = error instanceof Error ? error.message : 'Unknown error';
+ console.error('Login failed:', message);
+ loginError.textContent = `Login failed: ${message}`;
+ loginError.classList.remove('hidden');
+ this.callbacks.onAuthFailure?.(error);
+ } finally {
+ this.resetLoginButton();
+ }
+ }
+
+ /**
+ * Ensure user is logged in (check existing token)
+ */
+ async ensureUserLogged(): Promise {
+ if (!this.pb.authStore.isValid || !this.pb.authStore.token) {
+ return false;
+ }
+
+ try {
+ const refresh = await this.pb.collection(this.config.collection).authRefresh();
+ this.updateState(refresh);
+ this.updateAuthUI();
+ this.callbacks.onTokenUpdate?.(this.state);
+ console.log('✓ Token refreshed');
+ return true;
+ } catch (error) {
+ console.warn('Existing token invalid, clearing auth');
+ this.pb.authStore.clear();
+ this.updateState(null);
+ this.updateAuthUI();
+ return false;
+ }
+ }
+
+ /**
+ * Update internal auth state
+ */
+ private updateState(data: any): void {
+ if (!data) {
+ this.state = {
+ isAuthenticated: false,
+ user: null,
+ token: null,
+ };
+ return;
+ }
+
+ const record = data.record || this.pb.authStore.record || this.pb.authStore.model;
+ const meta = data.meta || {};
+ const model = this.pb.authStore.model;
+
+ this.state = {
+ isAuthenticated: true,
+ user: {
+ id: record?.id || model?.id || 'unknown',
+ name: record?.name || model?.name || meta?.name || record?.email || model?.email || 'Unknown User',
+ email: record?.email || model?.email || meta?.email || '(no email)',
+ },
+ token: this.pb.authStore.token,
+ };
+ }
+
+ /**
+ * Update UI based on auth state
+ */
+ updateAuthUI(): void {
+ const loginContainer = document.getElementById(this.config.loginContainerId);
+ const userDisplayName = document.getElementById(this.config.userDisplayNameId);
+ const userEmail = document.getElementById(this.config.userEmailId);
+
+ if (!loginContainer) return;
+
+ if (this.pb.authStore.isValid) {
+ loginContainer.classList.add('hidden');
+ if (this.state.user) {
+ if (userDisplayName) userDisplayName.textContent = this.state.user.name;
+ if (userEmail) userEmail.textContent = this.state.user.email;
+ }
+ } else {
+ loginContainer.classList.remove('hidden');
+ const loginError = document.getElementById(this.config.loginErrorId);
+ if (loginError) loginError.classList.add('hidden');
+ }
+
+ this.callbacks.onUiUpdate?.(this.state);
+ }
+
+ /**
+ * Check token status (for verification/display)
+ */
+ async checkTokenStatus(): Promise<{ pbToken: boolean; tokenExpiry?: string }> {
+ const pbToken = !!this.pb.authStore.token;
+ return { pbToken };
+ }
+
+ /**
+ * Get current auth state
+ */
+ getAuthState(): AuthState {
+ return { ...this.state };
+ }
+
+ /**
+ * Get PocketBase instance (for direct usage if needed)
+ */
+ getPocketBase(): PocketBase {
+ return this.pb;
+ }
+
+ /**
+ * Logout
+ */
+ logout(): void {
+ this.pb.authStore.clear();
+ this.updateState(null);
+ this.updateAuthUI();
+ console.log('✓ Logged out');
+ }
+
+ /**
+ * Reset login button to initial state
+ */
+ private resetLoginButton(): void {
+ const loginBtn = document.getElementById(this.config.loginBtnId) as HTMLButtonElement;
+ if (loginBtn) {
+ loginBtn.disabled = false;
+ loginBtn.textContent = 'Login with Microsoft';
+ }
+ }
+}
+
+/**
+ * Quick init function - fetches config from backend
+ */
+export async function initPocketBaseAuth(
+ backendUrl: string,
+ callbacks?: Partial
+): Promise {
+ // Fetch frontend config from backend
+ const response = await fetch(`${backendUrl}/api/auth/config`);
+ if (!response.ok) {
+ throw new Error('Failed to fetch auth configuration from backend');
+ }
+ const config = await response.json();
+
+ const auth = new PocketBaseAuth(config, callbacks);
+ return auth;
+}
diff --git a/Mode1Svelte/auth-module/types.ts b/Mode1Svelte/auth-module/types.ts
new file mode 100644
index 0000000..b320f76
--- /dev/null
+++ b/Mode1Svelte/auth-module/types.ts
@@ -0,0 +1,62 @@
+/**
+ * Frontend Auth Configuration
+ */
+export interface AuthConfig {
+ pbUrl: string; // PocketBase URL (required)
+ collection?: string;
+ provider?: string;
+ loginContainerId?: string;
+ userDisplayNameId?: string;
+ userEmailId?: string;
+ loginBtnId?: string;
+ loginErrorId?: string;
+}
+
+/**
+ * Frontend configuration provided by backend
+ */
+export interface FrontendConfig {
+ pbUrl: string;
+ provider?: string;
+ collection?: string;
+}
+
+/**
+ * Backend Auth Configuration
+ */
+export interface BackendAuthConfig {
+ clientId?: string;
+ tenantId?: string;
+ clientSecret?: string;
+}
+
+/**
+ * Auth state object
+ */
+export interface AuthState {
+ isAuthenticated: boolean;
+ user: {
+ id: string;
+ name: string;
+ email: string;
+ } | null;
+ token: string | null;
+}
+
+/**
+ * Graph token cache object
+ */
+export interface GraphTokenCache {
+ token: string;
+ expiresOn: number;
+}
+
+/**
+ * Auth event callbacks
+ */
+export interface AuthCallbacks {
+ onAuthSuccess?: (state: AuthState) => void;
+ onAuthFailure?: (error: any) => void;
+ onTokenUpdate?: (state: AuthState) => void;
+ onUiUpdate?: (state: AuthState) => void;
+}
diff --git a/Mode1Svelte/backend/server.ts b/Mode1Svelte/backend/server.ts
new file mode 100644
index 0000000..a8bf8d3
--- /dev/null
+++ b/Mode1Svelte/backend/server.ts
@@ -0,0 +1,226 @@
+import { Hono } from 'hono';
+import { serveStatic } from 'hono/bun';
+import { cors } from 'hono/cors';
+import path from 'path';
+import { fileURLToPath } from 'url';
+import { readFile } from 'fs/promises';
+import { existsSync } from 'fs';
+import { AuthConfigManager, BackendAuth } from '../auth-module/backend';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+
+const app = new Hono();
+const PORT = process.env.PORT || 3005;
+
+// Initialize auth
+const backendAuth = new BackendAuth({
+ clientId: process.env.CLIENT_ID,
+ tenantId: process.env.TENANT_ID,
+ clientSecret: process.env.CLIENT_SECRET,
+}, process.env.PB_DB);
+
+// CORS Configuration - Allow requests from frontend domains
+app.use('*', cors({
+ origin: ['https://ji-test.ccllc.pro', 'http://localhost:3005', 'http://localhost:5173'],
+ allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
+ allowHeaders: ['Content-Type', 'Authorization'],
+ credentials: true,
+}));
+
+// Serve bundled auth module JavaScript
+app.get('/auth-module.js', async (c) => {
+ const filePath = path.join(__dirname, '../frontend/dist/auth.js');
+ if (existsSync(filePath)) {
+ const content = await readFile(filePath);
+ c.header('Content-Type', 'application/javascript');
+ return c.body(content);
+ }
+ return c.notFound();
+});
+
+// Serve frontend static files
+app.use('/', serveStatic({ root: path.join(__dirname, '../frontend') }));
+
+// API: Get frontend config
+app.get('/api/auth/config', (c) => {
+ try {
+ const config = AuthConfigManager.getFrontendConfig();
+ return c.json(config);
+ } catch (error) {
+ return c.json(
+ { error: 'Failed to get config', message: error instanceof Error ? error.message : 'Unknown error' },
+ 500
+ );
+ }
+});
+
+// API: Get Graph token status
+app.get('/api/auth/graph/token', async (c) => {
+ try {
+ const { token, expiresOnISO } = await backendAuth.graphTokenManager.getTokenWithExpiry();
+
+ if (!token) {
+ return c.json({ success: false, error: 'Failed to acquire Graph token' }, 500);
+ }
+
+ // Decode JWT to show token details (for verification)
+ const parts = token.split('.');
+ let payload = {};
+ if (parts.length === 3) {
+ try {
+ const decoded = JSON.parse(Buffer.from(parts[1], 'base64').toString());
+ payload = {
+ aud: decoded.aud,
+ iss: decoded.iss,
+ scp: decoded.scp,
+ app_displayname: decoded.app_displayname,
+ iat: new Date(decoded.iat * 1000).toISOString(),
+ exp: new Date(decoded.exp * 1000).toISOString(),
+ };
+ } catch (e) {
+ // Silent fail - just show truncated token
+ }
+ }
+
+ return c.json({
+ success: true,
+ token: token.substring(0, 50) + '...',
+ fullToken: token, // Include full token for testing
+ expiresOn: expiresOnISO,
+ isValid: backendAuth.graphTokenManager.isTokenValid(),
+ tokenDetails: Object.keys(payload).length > 0 ? payload : 'Could not decode',
+ });
+ } catch (error) {
+ return c.json(
+ { success: false, error: error instanceof Error ? error.message : 'Failed to acquire token' },
+ 500
+ );
+ }
+});
+
+// API: Validate PocketBase token
+app.post('/api/auth/validate-pb-token', async (c) => {
+ try {
+ const body = await c.req.json();
+ const token = body.pbToken;
+
+ if (!token) {
+ return c.json({ valid: false, error: 'No token provided' }, 400);
+ }
+
+ const isValid = await backendAuth.pbValidator.validateUserToken(token);
+ const user = isValid ? await backendAuth.pbValidator.getUserRecord(token) : null;
+
+ return c.json({
+ valid: isValid,
+ user: user ? {
+ id: user.id,
+ email: user.email,
+ name: user.name,
+ } : null,
+ });
+ } catch (error) {
+ return c.json(
+ { valid: false, error: error instanceof Error ? error.message : 'Validation failed' },
+ 500
+ );
+ }
+});
+
+// API: Refresh Graph token (test cache)
+app.post('/api/auth/refresh-graph', async (c) => {
+ try {
+ backendAuth.graphTokenManager.clearCache();
+ const { token, expiresOnISO } = await backendAuth.graphTokenManager.getTokenWithExpiry();
+ return c.json({
+ success: true,
+ refreshed: true,
+ expiresOn: expiresOnISO,
+ });
+ } catch (error) {
+ return c.json(
+ { success: false, error: error instanceof Error ? error.message : 'Refresh failed' },
+ 500
+ );
+ }
+});
+
+// API: Compare PB and Graph tokens
+app.post('/api/auth/compare-tokens', async (c) => {
+ try {
+ const body = await c.req.json();
+ const pbToken = body.pbToken;
+
+ // Get Graph token
+ const { token: graphToken, expiresOnISO: graphExpires } = await backendAuth.graphTokenManager.getTokenWithExpiry();
+
+ if (!graphToken) {
+ return c.json({ success: false, error: 'Failed to acquire Graph token' }, 500);
+ }
+
+ // Decode both tokens to compare
+ const decodeJWT = (token: string) => {
+ try {
+ const parts = token.split('.');
+ if (parts.length !== 3) return null;
+ return JSON.parse(Buffer.from(parts[1], 'base64').toString());
+ } catch {
+ return null;
+ }
+ };
+
+ const graphPayload = decodeJWT(graphToken);
+
+ // Validate PB token to get actual user
+ let authenticatedUser = 'Unknown';
+ if (pbToken) {
+ try {
+ const isValid = await backendAuth.pbValidator.validateUserToken(pbToken);
+ if (isValid) {
+ const userRecord = await backendAuth.pbValidator.getUserRecord(pbToken);
+ authenticatedUser = userRecord?.email || userRecord?.id || 'Unknown';
+ }
+ } catch {
+ authenticatedUser = 'Token invalid or expired';
+ }
+ }
+
+ return c.json({
+ success: true,
+ comparison: {
+ graph_token: {
+ issuer: graphPayload?.iss || 'N/A',
+ audience: graphPayload?.aud || 'N/A',
+ app: graphPayload?.app_displayname || 'N/A',
+ scopes: graphPayload?.scp || 'N/A',
+ issued_at: graphPayload?.iat ? new Date(graphPayload.iat * 1000).toISOString() : 'N/A',
+ expires_at: graphPayload?.exp ? new Date(graphPayload.exp * 1000).toISOString() : 'N/A',
+ type: 'Microsoft Graph Token (Backend/App-only)',
+ source: '@azure/msal-node',
+ purpose: 'Backend API calls to Microsoft Graph',
+ },
+ pocketbase_token: {
+ note: 'User token from PocketBase OAuth2',
+ type: 'PocketBase User Token (Frontend/User)',
+ source: 'PocketBase OAuth2 flow',
+ purpose: 'User authentication and authorization',
+ authenticated_user: authenticatedUser,
+ },
+ are_different: true,
+ explanation: 'These are completely different tokens from different systems: Graph token is for app-to-app Microsoft API access, PocketBase token is for user authentication in the application.',
+ },
+ });
+ } catch (error) {
+ return c.json(
+ { success: false, error: error instanceof Error ? error.message : 'Failed to compare tokens' },
+ 500
+ );
+ }
+});
+
+// Start server
+console.log(`Mode1 Auth Test Server running on port ${PORT}`);
+export default {
+ port: PORT,
+ fetch: app.fetch,
+};
diff --git a/Mode1Svelte/bun.lock b/Mode1Svelte/bun.lock
new file mode 100644
index 0000000..9091d9e
--- /dev/null
+++ b/Mode1Svelte/bun.lock
@@ -0,0 +1,226 @@
+{
+ "lockfileVersion": 1,
+ "configVersion": 1,
+ "workspaces": {
+ "": {
+ "name": "mode1-auth-test",
+ "dependencies": {
+ "@azure/msal-node": "^5.0.2",
+ "dotenv": "^17.2.3",
+ "hono": "^4.10.8",
+ "pocketbase": "^0.26.5",
+ },
+ "devDependencies": {
+ "@types/bun": "latest",
+ "typescript": "^5",
+ "vite": "^7.3.1",
+ },
+ "peerDependencies": {
+ "bun": "latest",
+ },
+ },
+ },
+ "packages": {
+ "@azure/msal-common": ["@azure/msal-common@16.0.2", "", {}, "sha512-ZJ/UR7lyqIntURrIJCyvScwJFanM9QhJYcJCheB21jZofGKpP9QxWgvADANo7UkresHKzV+6YwoeZYP7P7HvUg=="],
+
+ "@azure/msal-node": ["@azure/msal-node@5.0.2", "", { "dependencies": { "@azure/msal-common": "16.0.2", "jsonwebtoken": "^9.0.0", "uuid": "^8.3.0" } }, "sha512-3tHeJghckgpTX98TowJoXOjKGuds0L+FKfeHJtoZFl2xvwE6RF65shZJzMQ5EQZWXzh3sE1i9gE+m3aRMachjA=="],
+
+ "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw=="],
+
+ "@esbuild/android-arm": ["@esbuild/android-arm@0.27.2", "", { "os": "android", "cpu": "arm" }, "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA=="],
+
+ "@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.2", "", { "os": "android", "cpu": "arm64" }, "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA=="],
+
+ "@esbuild/android-x64": ["@esbuild/android-x64@0.27.2", "", { "os": "android", "cpu": "x64" }, "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A=="],
+
+ "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg=="],
+
+ "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA=="],
+
+ "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g=="],
+
+ "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA=="],
+
+ "@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.2", "", { "os": "linux", "cpu": "arm" }, "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw=="],
+
+ "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw=="],
+
+ "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.2", "", { "os": "linux", "cpu": "ia32" }, "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w=="],
+
+ "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg=="],
+
+ "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw=="],
+
+ "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ=="],
+
+ "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA=="],
+
+ "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w=="],
+
+ "@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.2", "", { "os": "linux", "cpu": "x64" }, "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA=="],
+
+ "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.2", "", { "os": "none", "cpu": "arm64" }, "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw=="],
+
+ "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.2", "", { "os": "none", "cpu": "x64" }, "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA=="],
+
+ "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA=="],
+
+ "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg=="],
+
+ "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.2", "", { "os": "none", "cpu": "arm64" }, "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag=="],
+
+ "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg=="],
+
+ "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg=="],
+
+ "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ=="],
+
+ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.2", "", { "os": "win32", "cpu": "x64" }, "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ=="],
+
+ "@oven/bun-darwin-aarch64": ["@oven/bun-darwin-aarch64@1.3.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-27rypIapNkYboOSylkf1tD9UW9Ado2I+P1NBL46Qz29KmOjTL6WuJ7mHDC5O66CYxlOkF5r93NPDAC3lFHYBXw=="],
+
+ "@oven/bun-darwin-x64": ["@oven/bun-darwin-x64@1.3.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-I82xGzPkBxzBKgbl8DsA0RfMQCWTWjNmLjIEkW1ECiv3qK02kHGQ5FGUr/29L/SuvnGsULW4tBTRNZiMzL37nA=="],
+
+ "@oven/bun-darwin-x64-baseline": ["@oven/bun-darwin-x64-baseline@1.3.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-nqtr+pTsHqusYpG2OZc6s+AmpWDB/FmBvstrK0y5zkti4OqnCuu7Ev2xNjS7uyb47NrAFF40pWqkpaio5XEd7w=="],
+
+ "@oven/bun-linux-aarch64": ["@oven/bun-linux-aarch64@1.3.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-YaQEAYjBanoOOtpqk/c5GGcfZIyxIIkQ2m1TbHjedRmJNwxzWBhGinSARFkrRIc3F8pRIGAopXKvJ/2rjN1LzQ=="],
+
+ "@oven/bun-linux-aarch64-musl": ["@oven/bun-linux-aarch64-musl@1.3.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-FR+iJt17rfFgYgpxL3M67AUwujOgjw52ZJzB9vElI5jQXNjTyOKf8eH4meSk4vjlYF3h/AjKYd6pmN0OIUlVKQ=="],
+
+ "@oven/bun-linux-x64": ["@oven/bun-linux-x64@1.3.6", "", { "os": "linux", "cpu": "x64" }, "sha512-egfngj0dfJ868cf30E7B+ye9KUWSebYxOG4l9YP5eWeMXCtenpenx0zdKtAn9qxJgEJym5AN6trtlk+J6x8Lig=="],
+
+ "@oven/bun-linux-x64-baseline": ["@oven/bun-linux-x64-baseline@1.3.6", "", { "os": "linux", "cpu": "x64" }, "sha512-jRmnX18ak8WzqLrex3siw0PoVKyIeI5AiCv4wJLgSs7VKfOqrPycfHIWfIX2jdn7ngqbHFPzI09VBKANZ4Pckg=="],
+
+ "@oven/bun-linux-x64-musl": ["@oven/bun-linux-x64-musl@1.3.6", "", { "os": "linux", "cpu": "x64" }, "sha512-YeXcJ9K6vJAt1zSkeA21J6pTe7PgDMLTHKGI3nQBiMYnYf7Ob3K+b/ChSCznrJG7No5PCPiQPg4zTgA+BOTmSA=="],
+
+ "@oven/bun-linux-x64-musl-baseline": ["@oven/bun-linux-x64-musl-baseline@1.3.6", "", { "os": "linux", "cpu": "x64" }, "sha512-7FjVnxnRTp/AgWqSQRT/Vt9TYmvnZ+4M+d9QOKh/Lf++wIFXFGSeAgD6bV1X/yr2UPVmZDk+xdhr2XkU7l2v3w=="],
+
+ "@oven/bun-windows-x64": ["@oven/bun-windows-x64@1.3.6", "", { "os": "win32", "cpu": "x64" }, "sha512-Sr1KwUcbB0SEpnSPO22tNJppku2khjFluEst+mTGhxHzAGQTQncNeJxDnt3F15n+p9Q+mlcorxehd68n1siikQ=="],
+
+ "@oven/bun-windows-x64-baseline": ["@oven/bun-windows-x64-baseline@1.3.6", "", { "os": "win32", "cpu": "x64" }, "sha512-PFUa7JL4lGoyyppeS4zqfuoXXih+gSE0XxhDMrCPVEUev0yhGNd/tbWBvcdpYnUth80owENoGjc8s5Knopv9wA=="],
+
+ "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.55.3", "", { "os": "android", "cpu": "arm" }, "sha512-qyX8+93kK/7R5BEXPC2PjUt0+fS/VO2BVHjEHyIEWiYn88rcRBHmdLgoJjktBltgAf+NY7RfCGB1SoyKS/p9kg=="],
+
+ "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.55.3", "", { "os": "android", "cpu": "arm64" }, "sha512-6sHrL42bjt5dHQzJ12Q4vMKfN+kUnZ0atHHnv4V0Wd9JMTk7FDzSY35+7qbz3ypQYMBPANbpGK7JpnWNnhGt8g=="],
+
+ "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.55.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1ht2SpGIjEl2igJ9AbNpPIKzb1B5goXOcmtD0RFxnwNuMxqkR6AUaaErZz+4o+FKmzxcSNBOLrzsICZVNYa1Rw=="],
+
+ "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.55.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-FYZ4iVunXxtT+CZqQoPVwPhH7549e/Gy7PIRRtq4t5f/vt54pX6eG9ebttRH6QSH7r/zxAFA4EZGlQ0h0FvXiA=="],
+
+ "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.55.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-M/mwDCJ4wLsIgyxv2Lj7Len+UMHd4zAXu4GQ2UaCdksStglWhP61U3uowkaYBQBhVoNpwx5Hputo8eSqM7K82Q=="],
+
+ "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.55.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-5jZT2c7jBCrMegKYTYTpni8mg8y3uY8gzeq2ndFOANwNuC/xJbVAoGKR9LhMDA0H3nIhvaqUoBEuJoICBudFrA=="],
+
+ "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.55.3", "", { "os": "linux", "cpu": "arm" }, "sha512-YeGUhkN1oA+iSPzzhEjVPS29YbViOr8s4lSsFaZKLHswgqP911xx25fPOyE9+khmN6W4VeM0aevbDp4kkEoHiA=="],
+
+ "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.55.3", "", { "os": "linux", "cpu": "arm" }, "sha512-eo0iOIOvcAlWB3Z3eh8pVM8hZ0oVkK3AjEM9nSrkSug2l15qHzF3TOwT0747omI6+CJJvl7drwZepT+re6Fy/w=="],
+
+ "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.55.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-DJay3ep76bKUDImmn//W5SvpjRN5LmK/ntWyeJs/dcnwiiHESd3N4uteK9FDLf0S0W8E6Y0sVRXpOCoQclQqNg=="],
+
+ "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.55.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-BKKWQkY2WgJ5MC/ayvIJTHjy0JUGb5efaHCUiG/39sSUvAYRBaO3+/EK0AZT1RF3pSj86O24GLLik9mAYu0IJg=="],
+
+ "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.55.3", "", { "os": "linux", "cpu": "none" }, "sha512-Q9nVlWtKAG7ISW80OiZGxTr6rYtyDSkauHUtvkQI6TNOJjFvpj4gcH+KaJihqYInnAzEEUetPQubRwHef4exVg=="],
+
+ "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.55.3", "", { "os": "linux", "cpu": "none" }, "sha512-2H5LmhzrpC4fFRNwknzmmTvvyJPHwESoJgyReXeFoYYuIDfBhP29TEXOkCJE/KxHi27mj7wDUClNq78ue3QEBQ=="],
+
+ "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.55.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9S542V0ie9LCTznPYlvaeySwBeIEa7rDBgLHKZ5S9DBgcqdJYburabm8TqiqG6mrdTzfV5uttQRHcbKff9lWtA=="],
+
+ "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.55.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-ukxw+YH3XXpcezLgbJeasgxyTbdpnNAkrIlFGDl7t+pgCxZ89/6n1a+MxlY7CegU+nDgrgdqDelPRNQ/47zs0g=="],
+
+ "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.55.3", "", { "os": "linux", "cpu": "none" }, "sha512-Iauw9UsTTvlF++FhghFJjqYxyXdggXsOqGpFBylaRopVpcbfyIIsNvkf9oGwfgIcf57z3m8+/oSYTo6HutBFNw=="],
+
+ "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.55.3", "", { "os": "linux", "cpu": "none" }, "sha512-3OqKAHSEQXKdq9mQ4eajqUgNIK27VZPW3I26EP8miIzuKzCJ3aW3oEn2pzF+4/Hj/Moc0YDsOtBgT5bZ56/vcA=="],
+
+ "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.55.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-0CM8dSVzVIaqMcXIFej8zZrSFLnGrAE8qlNbbHfTw1EEPnFTg1U1ekI0JdzjPyzSfUsHWtodilQQG/RA55berA=="],
+
+ "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.55.3", "", { "os": "linux", "cpu": "x64" }, "sha512-+fgJE12FZMIgBaKIAGd45rxf+5ftcycANJRWk8Vz0NnMTM5rADPGuRFTYar+Mqs560xuART7XsX2lSACa1iOmQ=="],
+
+ "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.55.3", "", { "os": "linux", "cpu": "x64" }, "sha512-tMD7NnbAolWPzQlJQJjVFh/fNH3K/KnA7K8gv2dJWCwwnaK6DFCYST1QXYWfu5V0cDwarWC8Sf/cfMHniNq21A=="],
+
+ "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.55.3", "", { "os": "openbsd", "cpu": "x64" }, "sha512-u5KsqxOxjEeIbn7bUK1MPM34jrnPwjeqgyin4/N6e/KzXKfpE9Mi0nCxcQjaM9lLmPcHmn/xx1yOjgTMtu1jWQ=="],
+
+ "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.55.3", "", { "os": "none", "cpu": "arm64" }, "sha512-vo54aXwjpTtsAnb3ca7Yxs9t2INZg7QdXN/7yaoG7nPGbOBXYXQY41Km+S1Ov26vzOAzLcAjmMdjyEqS1JkVhw=="],
+
+ "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.55.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-HI+PIVZ+m+9AgpnY3pt6rinUdRYrGHvmVdsNQ4odNqQ/eRF78DVpMR7mOq7nW06QxpczibwBmeQzB68wJ+4W4A=="],
+
+ "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.55.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-vRByotbdMo3Wdi+8oC2nVxtc3RkkFKrGaok+a62AT8lz/YBuQjaVYAS5Zcs3tPzW43Vsf9J0wehJbUY5xRSekA=="],
+
+ "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.55.3", "", { "os": "win32", "cpu": "x64" }, "sha512-POZHq7UeuzMJljC5NjKi8vKMFN6/5EOqcX1yGntNLp7rUTpBAXQ1hW8kWPFxYLv07QMcNM75xqVLGPWQq6TKFA=="],
+
+ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.55.3", "", { "os": "win32", "cpu": "x64" }, "sha512-aPFONczE4fUFKNXszdvnd2GqKEYQdV5oEsIbKPujJmWlCI9zEsv1Otig8RKK+X9bed9gFUN6LAeN4ZcNuu4zjg=="],
+
+ "@types/bun": ["@types/bun@1.3.6", "", { "dependencies": { "bun-types": "1.3.6" } }, "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA=="],
+
+ "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
+
+ "@types/node": ["@types/node@25.0.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg=="],
+
+ "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="],
+
+ "bun": ["bun@1.3.6", "", { "optionalDependencies": { "@oven/bun-darwin-aarch64": "1.3.6", "@oven/bun-darwin-x64": "1.3.6", "@oven/bun-darwin-x64-baseline": "1.3.6", "@oven/bun-linux-aarch64": "1.3.6", "@oven/bun-linux-aarch64-musl": "1.3.6", "@oven/bun-linux-x64": "1.3.6", "@oven/bun-linux-x64-baseline": "1.3.6", "@oven/bun-linux-x64-musl": "1.3.6", "@oven/bun-linux-x64-musl-baseline": "1.3.6", "@oven/bun-windows-x64": "1.3.6", "@oven/bun-windows-x64-baseline": "1.3.6" }, "os": [ "linux", "win32", "darwin", ], "cpu": [ "x64", "arm64", ], "bin": { "bun": "bin/bun.exe", "bunx": "bin/bunx.exe" } }, "sha512-Tn98GlZVN2WM7+lg/uGn5DzUao37Yc0PUz7yzYHdeF5hd+SmHQGbCUIKE4Sspdgtxn49LunK3mDNBC2Qn6GJjw=="],
+
+ "bun-types": ["bun-types@1.3.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-OlFwHcnNV99r//9v5IIOgQ9Uk37gZqrNMCcqEaExdkVq3Avwqok1bJFmvGMCkCE0FqzdY8VMOZpfpR3lwI+CsQ=="],
+
+ "dotenv": ["dotenv@17.2.3", "", {}, "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w=="],
+
+ "ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
+
+ "esbuild": ["esbuild@0.27.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.2", "@esbuild/android-arm": "0.27.2", "@esbuild/android-arm64": "0.27.2", "@esbuild/android-x64": "0.27.2", "@esbuild/darwin-arm64": "0.27.2", "@esbuild/darwin-x64": "0.27.2", "@esbuild/freebsd-arm64": "0.27.2", "@esbuild/freebsd-x64": "0.27.2", "@esbuild/linux-arm": "0.27.2", "@esbuild/linux-arm64": "0.27.2", "@esbuild/linux-ia32": "0.27.2", "@esbuild/linux-loong64": "0.27.2", "@esbuild/linux-mips64el": "0.27.2", "@esbuild/linux-ppc64": "0.27.2", "@esbuild/linux-riscv64": "0.27.2", "@esbuild/linux-s390x": "0.27.2", "@esbuild/linux-x64": "0.27.2", "@esbuild/netbsd-arm64": "0.27.2", "@esbuild/netbsd-x64": "0.27.2", "@esbuild/openbsd-arm64": "0.27.2", "@esbuild/openbsd-x64": "0.27.2", "@esbuild/openharmony-arm64": "0.27.2", "@esbuild/sunos-x64": "0.27.2", "@esbuild/win32-arm64": "0.27.2", "@esbuild/win32-ia32": "0.27.2", "@esbuild/win32-x64": "0.27.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw=="],
+
+ "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
+
+ "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
+
+ "hono": ["hono@4.11.5", "", {}, "sha512-WemPi9/WfyMwZs+ZUXdiwcCh9Y+m7L+8vki9MzDw3jJ+W9Lc+12HGsd368Qc1vZi1xwW8BWMMsnK5efYKPdt4g=="],
+
+ "jsonwebtoken": ["jsonwebtoken@9.0.3", "", { "dependencies": { "jws": "^4.0.1", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g=="],
+
+ "jwa": ["jwa@2.0.1", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="],
+
+ "jws": ["jws@4.0.1", "", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="],
+
+ "lodash.includes": ["lodash.includes@4.3.0", "", {}, "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="],
+
+ "lodash.isboolean": ["lodash.isboolean@3.0.3", "", {}, "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="],
+
+ "lodash.isinteger": ["lodash.isinteger@4.0.4", "", {}, "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="],
+
+ "lodash.isnumber": ["lodash.isnumber@3.0.3", "", {}, "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="],
+
+ "lodash.isplainobject": ["lodash.isplainobject@4.0.6", "", {}, "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="],
+
+ "lodash.isstring": ["lodash.isstring@4.0.1", "", {}, "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="],
+
+ "lodash.once": ["lodash.once@4.1.1", "", {}, "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="],
+
+ "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
+
+ "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
+
+ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
+
+ "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
+
+ "pocketbase": ["pocketbase@0.26.6", "", {}, "sha512-Pl7V4y3DWglYITC4cBpclmuIzePRGsb/sXk/Wyqxznwu5JsHA5IILJY81PT2XQ3OSKCakWjbxjYBqtdcghzKvA=="],
+
+ "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
+
+ "rollup": ["rollup@4.55.3", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.55.3", "@rollup/rollup-android-arm64": "4.55.3", "@rollup/rollup-darwin-arm64": "4.55.3", "@rollup/rollup-darwin-x64": "4.55.3", "@rollup/rollup-freebsd-arm64": "4.55.3", "@rollup/rollup-freebsd-x64": "4.55.3", "@rollup/rollup-linux-arm-gnueabihf": "4.55.3", "@rollup/rollup-linux-arm-musleabihf": "4.55.3", "@rollup/rollup-linux-arm64-gnu": "4.55.3", "@rollup/rollup-linux-arm64-musl": "4.55.3", "@rollup/rollup-linux-loong64-gnu": "4.55.3", "@rollup/rollup-linux-loong64-musl": "4.55.3", "@rollup/rollup-linux-ppc64-gnu": "4.55.3", "@rollup/rollup-linux-ppc64-musl": "4.55.3", "@rollup/rollup-linux-riscv64-gnu": "4.55.3", "@rollup/rollup-linux-riscv64-musl": "4.55.3", "@rollup/rollup-linux-s390x-gnu": "4.55.3", "@rollup/rollup-linux-x64-gnu": "4.55.3", "@rollup/rollup-linux-x64-musl": "4.55.3", "@rollup/rollup-openbsd-x64": "4.55.3", "@rollup/rollup-openharmony-arm64": "4.55.3", "@rollup/rollup-win32-arm64-msvc": "4.55.3", "@rollup/rollup-win32-ia32-msvc": "4.55.3", "@rollup/rollup-win32-x64-gnu": "4.55.3", "@rollup/rollup-win32-x64-msvc": "4.55.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-y9yUpfQvetAjiDLtNMf1hL9NXchIJgWt6zIKeoB+tCd3npX08Eqfzg60V9DhIGVMtQ0AlMkFw5xa+AQ37zxnAA=="],
+
+ "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
+
+ "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
+
+ "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
+
+ "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
+
+ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
+
+ "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
+
+ "uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="],
+
+ "vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="],
+ }
+}
diff --git a/Mode1Svelte/frontend/auth.ts b/Mode1Svelte/frontend/auth.ts
new file mode 100644
index 0000000..cb198e7
--- /dev/null
+++ b/Mode1Svelte/frontend/auth.ts
@@ -0,0 +1,3 @@
+// Frontend entry point - exports auth module for browser
+export { PocketBaseAuth, initPocketBaseAuth } from '../auth-module/frontend';
+export type { AuthConfig, AuthState, AuthCallbacks } from '../auth-module/types';
diff --git a/Mode1Svelte/frontend/index.html b/Mode1Svelte/frontend/index.html
new file mode 100644
index 0000000..c55e615
--- /dev/null
+++ b/Mode1Svelte/frontend/index.html
@@ -0,0 +1,545 @@
+
+
+
+
+
+ Mode1 - Auth Module Test
+
+
+
+
+
🔐 Auth Module Test Suite (Mode1)
+
+
+
+
+
+
+ Frontend: PocketBase Auth
+
+
+
+ Tests OAuth2 login, token refresh, and user state management.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Backend: Graph Token & PB Validation
+
+
+
+ Tests Microsoft Graph token acquisition, caching, and PocketBase token validation.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Job Info (First 4 Records)
+
+
+
+ Fetch and display first 4 records from Job_Info_Prod collection.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Mode1Svelte/package.json b/Mode1Svelte/package.json
new file mode 100644
index 0000000..1c1fd7e
--- /dev/null
+++ b/Mode1Svelte/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "mode1-auth-test",
+ "version": "1.0.0",
+ "type": "module",
+ "private": true,
+ "scripts": {
+ "start": "bun run backend/server.ts",
+ "dev": "bun --watch backend/server.ts",
+ "build": "vite build",
+ "prebuild": "vite build"
+ },
+ "dependencies": {
+ "@azure/msal-node": "^5.0.2",
+ "dotenv": "^17.2.3",
+ "hono": "^4.10.8",
+ "pocketbase": "^0.26.5"
+ },
+ "devDependencies": {
+ "@types/bun": "latest",
+ "typescript": "^5",
+ "vite": "^7.3.1"
+ },
+ "peerDependencies": {
+ "bun": "latest"
+ }
+}
diff --git a/Mode1Svelte/vite.config.ts b/Mode1Svelte/vite.config.ts
new file mode 100644
index 0000000..486b3db
--- /dev/null
+++ b/Mode1Svelte/vite.config.ts
@@ -0,0 +1,17 @@
+import { defineConfig } from 'vite';
+
+export default defineConfig({
+ build: {
+ outDir: 'frontend/dist',
+ emptyOutDir: true,
+ lib: {
+ entry: 'frontend/auth.ts',
+ name: 'AuthModule',
+ fileName: () => 'auth.js',
+ formats: ['umd']
+ }
+ },
+ server: {
+ middlewareMode: true
+ }
+});
diff --git a/ModeSwitch/backend/orchestrator.ts b/ModeSwitch/backend/orchestrator.ts
index 9bea8c6..5c86f6d 100644
--- a/ModeSwitch/backend/orchestrator.ts
+++ b/ModeSwitch/backend/orchestrator.ts
@@ -11,10 +11,11 @@ const ORCHESTRATOR_PORT = 3006; // Orchestrator API runs here
// Mode folder mappings - only Mode6Test exists currently
const MODE_FOLDERS: Record = {
'Mode1': 'Mode1',
+ 'Mode1Svelte': 'Mode1Svelte',
'Mode6Test': 'Mode6Test',
};
-let currentMode: string = 'Mode6Test';
+let currentMode: string = 'Mode1Svelte';
let currentProcess: any = null;
// Helper: Read active mode from config file