Social Annotations in the Atmosphere
15
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: via.seams.so proxy

Build Caddy-based via proxy with hot reloading and shared core package

Architecture:
- Caddy (port 8082) serves landing page and proxies /proxy/* and /static/* to pywb
- pywb (port 8080) handles HTML proxying with head_insert template injection
- Vite watch mode rebuilds client scripts to pywb-test/static/ automatically

Shared @seams/core package:
- StorageAdapter interface with WebStorageAdapter (localStorage + BroadcastChannel)
- BackgroundWorker: fetches annotations from backend, writes to storage
- ContentScript: reads from storage, renders highlights on page
- Sidebar (iframe) runs BackgroundWorker to fetch (bypasses pywb interception)
- Client (main.ts) runs ContentScript to render highlights
- Communication via BroadcastChannel - fully decoupled architecture

Fixed injection issues:
- Changed custom_banner → head_insert template (custom_banner requires enable_content_rewrite)
- Set enable_banner: false and enable_wombat: false for minimal rewriting
- Added /static/* Caddy proxy route for correct MIME types
- Changed iframe src to relative URL to avoid CSP violations

Development: Run `pnpm via` to start all services (Vite, pywb, Caddy).

+2990 -55
+5
.gitignore
··· 41 41 42 42 # DNS credentials 43 43 dns/creds.json 44 + 45 + # Via proxy build artifacts 46 + pywb-test/static/seams-*.js 47 + pywb-test/static/seams-*.js.map 48 + pywb-test/static/assets/
+33
Caddyfile
··· 1 + # Seams Via Proxy - Development Configuration 2 + # Use http:// to disable automatic HTTPS 3 + http://localhost:8082 { 4 + # Proxy routes to pywb (must come first) 5 + handle /proxy/* { 6 + reverse_proxy localhost:8080 7 + } 8 + 9 + # Proxy static files served by pywb 10 + handle /static/* { 11 + reverse_proxy localhost:8080 12 + } 13 + 14 + # Via landing page at root 15 + handle / { 16 + root * pywb-test/static 17 + rewrite * /via-landing.html 18 + file_server 19 + } 20 + 21 + # Serve static files (CSS, JS, etc) from pywb-test/static 22 + handle /* { 23 + root * pywb-test/static 24 + file_server 25 + } 26 + 27 + # Enable logging 28 + log { 29 + output stdout 30 + format console 31 + level INFO 32 + } 33 + }
-49
Dockerfile
··· 1 - # syntax = docker/dockerfile:1 2 - 3 - # Adjust NODE_VERSION as desired 4 - ARG NODE_VERSION=22.20.0 5 - FROM node:${NODE_VERSION}-slim AS base 6 - 7 - LABEL fly_launch_runtime="Node.js" 8 - 9 - # Node.js app lives here 10 - WORKDIR /app 11 - 12 - # Set production environment 13 - ENV NODE_ENV="production" 14 - 15 - # Install pnpm 16 - ARG PNPM_VERSION=10.20.0 17 - RUN npm install -g pnpm@$PNPM_VERSION 18 - 19 - 20 - # Throw-away build stage to reduce size of final image 21 - FROM base AS build 22 - 23 - # Install packages needed to build node modules 24 - RUN apt-get update -qq && \ 25 - apt-get install --no-install-recommends -y build-essential node-gyp pkg-config python-is-python3 26 - 27 - # Install node modules 28 - COPY package-lock.json package.json pnpm-lock.yaml ./ 29 - RUN pnpm install --frozen-lockfile --prod=false 30 - 31 - # Copy application code 32 - COPY . . 33 - 34 - # Build application 35 - RUN pnpm run build 36 - 37 - # Remove development dependencies 38 - RUN pnpm prune --prod 39 - 40 - 41 - # Final stage for app image 42 - FROM base 43 - 44 - # Copy built application 45 - COPY --from=build /app /app 46 - 47 - # Start the server by default, this can be overwritten at runtime 48 - EXPOSE 3000 49 - CMD [ "node", "index.js" ]
+251
entrypoints/via-client/main.ts
··· 1 + // Via proxy client - injects sidebar iframe and handles page interaction 2 + import { applyHighlights, clearHighlights } from '@/lib/highlights/apply'; 3 + import { WebStorageAdapter, BackgroundWorker, ContentScript } from '@seams/core'; 4 + import type { Annotation } from '@seams/core'; 5 + import { listAnnotationsForPage } from '@/lib/pds'; 6 + 7 + console.log('🔬 Seams via client loaded!'); 8 + 9 + // Initialize web storage adapter 10 + const storage = new WebStorageAdapter(); 11 + 12 + const SIDEBAR_ID = 'seams-sidebar-iframe'; 13 + const SIDEBAR_WIDTH = 400; 14 + const ADDER_ID = 'seams-selection-adder'; 15 + 16 + let currentAdder: HTMLElement | null = null; 17 + 18 + // Initialize content script only (sidebar handles fetching via BackgroundWorker) 19 + const contentScript = new ContentScript({ 20 + storage, 21 + getCurrentUrl: getActualUrl, 22 + applyHighlights, 23 + clearHighlights, 24 + }); 25 + 26 + function injectSidebar() { 27 + // Don't inject if already present 28 + if (document.getElementById(SIDEBAR_ID)) { 29 + console.log('[seams-via] Sidebar already injected'); 30 + return; 31 + } 32 + 33 + // Create iframe for sidebar 34 + const iframe = document.createElement('iframe'); 35 + iframe.id = SIDEBAR_ID; 36 + // Use relative URL to load through Caddy proxy (same origin for CSP) 37 + iframe.src = '/static/seams-sidebar.html'; 38 + iframe.style.cssText = ` 39 + position: fixed; 40 + top: 0; 41 + right: 0; 42 + width: ${SIDEBAR_WIDTH}px; 43 + height: 100vh; 44 + border: none; 45 + border-left: 1px solid #ccc; 46 + z-index: 2147483647; 47 + background: white; 48 + box-shadow: -2px 0 8px rgba(0,0,0,0.1); 49 + `; 50 + 51 + document.body.appendChild(iframe); 52 + console.log('[seams-via] Sidebar injected'); 53 + 54 + // Adjust page content to avoid overlap 55 + document.body.style.marginRight = `${SIDEBAR_WIDTH}px`; 56 + } 57 + 58 + function showSelectionAdder() { 59 + const selection = window.getSelection(); 60 + if (!selection || selection.rangeCount === 0 || selection.toString().trim().length === 0) { 61 + hideSelectionAdder(); 62 + return; 63 + } 64 + 65 + const text = selection.toString().trim(); 66 + console.log('[seams-via] Selection:', text); 67 + 68 + // Get selection bounding rect 69 + const range = selection.getRangeAt(0); 70 + const rect = range.getBoundingClientRect(); 71 + 72 + // Remove existing adder 73 + hideSelectionAdder(); 74 + 75 + // Create adder button 76 + const adder = document.createElement('div'); 77 + adder.id = ADDER_ID; 78 + adder.innerHTML = ` 79 + <button style=" 80 + background: #0085ff; 81 + color: white; 82 + border: none; 83 + border-radius: 4px; 84 + padding: 8px 16px; 85 + font-size: 14px; 86 + font-weight: 500; 87 + cursor: pointer; 88 + box-shadow: 0 2px 8px rgba(0,0,0,0.15); 89 + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; 90 + "> 91 + Annotate 92 + </button> 93 + `; 94 + 95 + adder.style.cssText = ` 96 + position: absolute; 97 + z-index: 2147483646; 98 + `; 99 + 100 + // Position above or below selection based on available space 101 + const spaceAbove = rect.top; 102 + const spaceBelow = window.innerHeight - rect.bottom; 103 + 104 + if (spaceAbove > spaceBelow && spaceAbove > 50) { 105 + // Position above 106 + adder.style.left = `${rect.left + window.scrollX + (rect.width / 2)}px`; 107 + adder.style.top = `${rect.top + window.scrollY - 40}px`; 108 + adder.style.transform = 'translateX(-50%)'; 109 + } else { 110 + // Position below 111 + adder.style.left = `${rect.left + window.scrollX + (rect.width / 2)}px`; 112 + adder.style.top = `${rect.bottom + window.scrollY + 5}px`; 113 + adder.style.transform = 'translateX(-50%)'; 114 + } 115 + 116 + document.body.appendChild(adder); 117 + currentAdder = adder; 118 + 119 + // Handle button click 120 + const button = adder.querySelector('button'); 121 + button?.addEventListener('click', (e) => { 122 + e.stopPropagation(); 123 + handleAnnotateClick(); 124 + }); 125 + 126 + // Close on click outside 127 + setTimeout(() => { 128 + document.addEventListener('click', handleClickOutside); 129 + }, 0); 130 + 131 + // Stop propagation on adder clicks 132 + adder.addEventListener('click', (e) => { 133 + e.stopPropagation(); 134 + }); 135 + } 136 + 137 + function hideSelectionAdder() { 138 + if (currentAdder) { 139 + currentAdder.remove(); 140 + currentAdder = null; 141 + document.removeEventListener('click', handleClickOutside); 142 + } 143 + } 144 + 145 + function handleClickOutside() { 146 + hideSelectionAdder(); 147 + } 148 + 149 + function handleAnnotateClick() { 150 + const selection = window.getSelection(); 151 + if (!selection || selection.toString().trim().length === 0) { 152 + return; 153 + } 154 + 155 + const text = selection.toString().trim(); 156 + console.log('[seams-via] Creating annotation for:', text); 157 + 158 + // TODO: Send to sidebar to create annotation 159 + window.postMessage({ 160 + type: 'SEAMS_CREATE_ANNOTATION', 161 + text, 162 + }, '*'); 163 + 164 + hideSelectionAdder(); 165 + } 166 + 167 + // Track text selection 168 + document.addEventListener('mouseup', (e) => { 169 + // Don't show adder if clicking on the adder itself 170 + if (currentAdder && currentAdder.contains(e.target as Node)) { 171 + return; 172 + } 173 + 174 + setTimeout(() => { 175 + showSelectionAdder(); 176 + }, 10); 177 + }); 178 + 179 + function normalizeUrl(url: string): string { 180 + try { 181 + const parsed = new URL(url); 182 + // Remove fragment 183 + parsed.hash = ''; 184 + // Remove trailing slash 185 + let path = parsed.pathname; 186 + if (path.endsWith('/') && path !== '/') { 187 + path = path.slice(0, -1); 188 + } 189 + parsed.pathname = path; 190 + return parsed.toString(); 191 + } catch { 192 + return url; 193 + } 194 + } 195 + 196 + function getActualUrl(): string { 197 + // Extract actual URL from pywb proxy URL 198 + // URL format: http://localhost:8081/proxy/https://example.com 199 + const proxyUrl = window.location.href; 200 + 201 + // Try to get from wbinfo (pywb metadata) 202 + const wbinfo = (window as any).wbinfo; 203 + if (wbinfo && wbinfo.url) { 204 + const rawUrl = wbinfo.url; 205 + const normalized = normalizeUrl(rawUrl); 206 + console.log('[seams-via] Got URL from wbinfo:', rawUrl); 207 + console.log('[seams-via] Normalized URL:', normalized); 208 + return normalized; 209 + } 210 + 211 + // Fallback: parse from proxy URL 212 + const match = proxyUrl.match(/\/proxy\/(https?:\/\/.+)/); 213 + if (match) { 214 + const rawUrl = match[1]; 215 + const normalized = normalizeUrl(rawUrl); 216 + console.log('[seams-via] Extracted URL from proxy path:', rawUrl); 217 + console.log('[seams-via] Normalized URL:', normalized); 218 + return normalized; 219 + } 220 + 221 + console.warn('[seams-via] Could not extract actual URL, using:', proxyUrl); 222 + return proxyUrl; 223 + } 224 + 225 + // Initialize 226 + function init() { 227 + if (document.body) { 228 + injectSidebar(); 229 + } else { 230 + document.addEventListener('DOMContentLoaded', () => { 231 + injectSidebar(); 232 + }); 233 + } 234 + 235 + // Start content script (loads and renders annotations from localStorage) 236 + contentScript.start(); 237 + } 238 + 239 + init(); 240 + 241 + // Listen for sidebar ready, then send current URL so it can fetch 242 + window.addEventListener('message', (event) => { 243 + if (event.data.type === 'SEAMS_SIDEBAR_READY') { 244 + console.log('[seams-via] Sidebar ready, sending URL'); 245 + const iframe = document.getElementById(SIDEBAR_ID) as HTMLIFrameElement; 246 + iframe?.contentWindow?.postMessage({ 247 + type: 'SEAMS_PAGE_URL', 248 + url: getActualUrl(), 249 + }, '*'); 250 + } 251 + });
+47
entrypoints/via-client/oauth-callback.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 + <title>Seams OAuth Callback</title> 7 + <style> 8 + body { 9 + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; 10 + display: flex; 11 + align-items: center; 12 + justify-content: center; 13 + height: 100vh; 14 + margin: 0; 15 + background: #f5f5f5; 16 + } 17 + .message { 18 + text-align: center; 19 + padding: 32px; 20 + background: white; 21 + border-radius: 8px; 22 + box-shadow: 0 2px 8px rgba(0,0,0,0.1); 23 + } 24 + .spinner { 25 + border: 3px solid #f3f3f3; 26 + border-top: 3px solid #0085ff; 27 + border-radius: 50%; 28 + width: 40px; 29 + height: 40px; 30 + animation: spin 1s linear infinite; 31 + margin: 0 auto 16px; 32 + } 33 + @keyframes spin { 34 + 0% { transform: rotate(0deg); } 35 + 100% { transform: rotate(360deg); } 36 + } 37 + </style> 38 + </head> 39 + <body> 40 + <div class="message"> 41 + <div class="spinner"></div> 42 + <h2>Completing login...</h2> 43 + <p id="status">Processing OAuth response</p> 44 + </div> 45 + <script type="module" src="/static/seams-oauth-callback.js"></script> 46 + </body> 47 + </html>
+34
entrypoints/via-client/oauth-callback.ts
··· 1 + // OAuth callback handler for via-client 2 + import { handleOAuthCallback } from '@/lib/oauth-web'; 3 + 4 + console.log('[oauth-callback] Processing OAuth callback'); 5 + 6 + const statusEl = document.getElementById('status'); 7 + 8 + async function processCallback() { 9 + try { 10 + const session = await handleOAuthCallback(); 11 + 12 + if (session) { 13 + if (statusEl) statusEl.textContent = 'Login successful! Redirecting...'; 14 + console.log('[oauth-callback] Login successful'); 15 + 16 + // Redirect back to the page the user was on 17 + // For now, redirect to the proxy home 18 + setTimeout(() => { 19 + window.location.href = '/proxy/https://example.com'; 20 + }, 1000); 21 + } else { 22 + if (statusEl) statusEl.textContent = 'No OAuth response found'; 23 + console.log('[oauth-callback] No OAuth response'); 24 + } 25 + } catch (error) { 26 + console.error('[oauth-callback] Error processing callback:', error); 27 + if (statusEl) { 28 + statusEl.textContent = 'Login failed: ' + (error as Error).message; 29 + statusEl.style.color = '#e74c3c'; 30 + } 31 + } 32 + } 33 + 34 + processCallback();
+135
entrypoints/via-client/sidebar.css
··· 1 + * { 2 + margin: 0; 3 + padding: 0; 4 + box-sizing: border-box; 5 + } 6 + 7 + body { 8 + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; 9 + font-size: 14px; 10 + line-height: 1.5; 11 + color: #333; 12 + } 13 + 14 + .sidebar { 15 + display: flex; 16 + flex-direction: column; 17 + height: 100vh; 18 + background: white; 19 + } 20 + 21 + .sidebar-header { 22 + padding: 16px; 23 + border-bottom: 1px solid #e0e0e0; 24 + background: #f5f5f5; 25 + } 26 + 27 + .sidebar-header h1 { 28 + font-size: 20px; 29 + font-weight: 600; 30 + margin-bottom: 4px; 31 + } 32 + 33 + .sidebar-header p { 34 + font-size: 12px; 35 + color: #666; 36 + } 37 + 38 + .profile-info { 39 + display: flex; 40 + align-items: center; 41 + gap: 8px; 42 + margin-top: 8px; 43 + } 44 + 45 + .profile-avatar { 46 + width: 32px; 47 + height: 32px; 48 + border-radius: 50%; 49 + } 50 + 51 + .profile-handle { 52 + font-size: 14px; 53 + color: #333; 54 + } 55 + 56 + .sidebar-content { 57 + flex: 1; 58 + overflow-y: auto; 59 + padding: 16px; 60 + } 61 + 62 + .login-container { 63 + display: flex; 64 + flex-direction: column; 65 + gap: 12px; 66 + } 67 + 68 + .login-container h2 { 69 + font-size: 18px; 70 + font-weight: 600; 71 + margin-bottom: 8px; 72 + } 73 + 74 + .input-wrapper { 75 + position: relative; 76 + display: flex; 77 + align-items: center; 78 + } 79 + 80 + .at-symbol { 81 + position: absolute; 82 + left: 12px; 83 + color: #666; 84 + font-size: 14px; 85 + pointer-events: none; 86 + } 87 + 88 + .handle-input { 89 + width: 100%; 90 + padding: 10px 12px 10px 28px; 91 + border: 1px solid #ccc; 92 + border-radius: 4px; 93 + font-size: 14px; 94 + font-family: inherit; 95 + } 96 + 97 + .handle-input:focus { 98 + outline: none; 99 + border-color: #0085ff; 100 + } 101 + 102 + button { 103 + padding: 10px 16px; 104 + background: #0085ff; 105 + color: white; 106 + border: none; 107 + border-radius: 4px; 108 + font-size: 14px; 109 + font-weight: 500; 110 + cursor: pointer; 111 + font-family: inherit; 112 + } 113 + 114 + button:hover { 115 + background: #0073e6; 116 + } 117 + 118 + button:active { 119 + background: #0061c2; 120 + } 121 + 122 + #auth-status { 123 + font-size: 12px; 124 + color: #666; 125 + min-height: 16px; 126 + } 127 + 128 + #logout-btn { 129 + background: #e74c3c; 130 + margin-top: 8px; 131 + } 132 + 133 + #logout-btn:hover { 134 + background: #c0392b; 135 + }
+13
entrypoints/via-client/sidebar.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 + <title>Seams Sidebar</title> 7 + <link rel="stylesheet" href="/static/assets/sidebar-J3iG1W2k.css"> 8 + </head> 9 + <body> 10 + <div id="app"></div> 11 + <script type="module" src="/static/seams-sidebar.js"></script> 12 + </body> 13 + </html>
+145
entrypoints/via-client/sidebar.ts
··· 1 + // Sidebar UI for via proxy client 2 + import './sidebar.css'; 3 + import { initializeOAuth, startLoginProcess, loadSession, clearSession, getProfile } from '@/lib/oauth-web'; 4 + import { WebStorageAdapter, BackgroundWorker } from '@seams/core'; 5 + import { listAnnotationsForPage } from '@/lib/pds'; 6 + 7 + console.log('[seams-sidebar] Loading sidebar...'); 8 + 9 + // Initialize web storage adapter AND background worker 10 + // Sidebar can fetch without pywb interference (not proxied) 11 + const storage = new WebStorageAdapter(); 12 + const backgroundWorker = new BackgroundWorker({ 13 + storage, 14 + fetchAnnotations: listAnnotationsForPage, 15 + }); 16 + 17 + const app = document.getElementById('app'); 18 + if (!app) { 19 + throw new Error('App element not found'); 20 + } 21 + 22 + // Initialize OAuth 23 + initializeOAuth(); 24 + 25 + let isLoggedIn = false; 26 + 27 + // Fetch annotations for the given URL 28 + function syncAnnotations(url: string) { 29 + console.log('[seams-sidebar] Syncing annotations for:', url); 30 + backgroundWorker.setCurrentUrl(url); 31 + } 32 + 33 + async function render() { 34 + const session = await loadSession(); 35 + 36 + if (session) { 37 + try { 38 + const profile = await getProfile(session); 39 + renderLoggedIn(profile); 40 + } catch (error) { 41 + console.error('[seams-sidebar] Failed to fetch profile:', error); 42 + renderLoggedIn(null); 43 + } 44 + } else { 45 + renderLoginForm(); 46 + } 47 + } 48 + 49 + function renderLoginForm() { 50 + isLoggedIn = false; 51 + app.innerHTML = ` 52 + <div class="sidebar"> 53 + <div class="sidebar-header"> 54 + <h1>Seams</h1> 55 + <p>Via Proxy Client</p> 56 + </div> 57 + <div class="sidebar-content"> 58 + <div class="login-container"> 59 + <h2>Login to Seams</h2> 60 + <div class="input-wrapper"> 61 + <span class="at-symbol">@</span> 62 + <input type="text" id="handle-input" class="handle-input" placeholder="you.bsky.social" /> 63 + </div> 64 + <button id="login-btn">Login with ATProto</button> 65 + <div id="auth-status"></div> 66 + </div> 67 + </div> 68 + </div> 69 + `; 70 + 71 + const handleInput = document.getElementById('handle-input') as HTMLInputElement; 72 + const loginBtn = document.getElementById('login-btn'); 73 + const authStatus = document.getElementById('auth-status'); 74 + 75 + const handleLogin = async () => { 76 + let handle = handleInput?.value.trim(); 77 + if (!handle) { 78 + alert('Please enter your handle'); 79 + return; 80 + } 81 + 82 + if (handle.startsWith('@')) { 83 + handle = handle.slice(1); 84 + } 85 + 86 + try { 87 + if (authStatus) authStatus.textContent = 'Logging in...'; 88 + await startLoginProcess(handle); 89 + } catch (error) { 90 + if (authStatus) authStatus.textContent = 'Login failed'; 91 + console.error('[seams-sidebar] Login error:', error); 92 + } 93 + }; 94 + 95 + loginBtn?.addEventListener('click', handleLogin); 96 + handleInput?.addEventListener('keydown', (e) => { 97 + if (e.key === 'Enter') { 98 + e.preventDefault(); 99 + handleLogin(); 100 + } 101 + }); 102 + } 103 + 104 + function renderLoggedIn(profile: any) { 105 + isLoggedIn = true; 106 + app.innerHTML = ` 107 + <div class="sidebar"> 108 + <div class="sidebar-header"> 109 + <h1>Seams</h1> 110 + ${profile ? ` 111 + <div class="profile-info"> 112 + ${profile.avatar ? `<img src="${profile.avatar}" class="profile-avatar" />` : ''} 113 + <span class="profile-handle">@${profile.handle || 'Unknown'}</span> 114 + </div> 115 + ` : ''} 116 + </div> 117 + <div class="sidebar-content"> 118 + <p>You are logged in!</p> 119 + <button id="logout-btn">Logout</button> 120 + </div> 121 + </div> 122 + `; 123 + 124 + const logoutBtn = document.getElementById('logout-btn'); 125 + logoutBtn?.addEventListener('click', async () => { 126 + await clearSession(); 127 + render(); 128 + }); 129 + } 130 + 131 + // Initial render 132 + render(); 133 + 134 + // Listen for messages from parent (page URL) 135 + window.addEventListener('message', (event) => { 136 + if (event.data.type === 'SEAMS_PAGE_URL') { 137 + const url = event.data.url; 138 + console.log('[seams-sidebar] Received page URL:', url); 139 + syncAnnotations(url); 140 + } 141 + }); 142 + 143 + // Notify parent window that sidebar is ready 144 + window.parent.postMessage({ type: 'SEAMS_SIDEBAR_READY' }, '*'); 145 + console.log('[seams-sidebar] Ready');
+42 -3
flake.nix
··· 88 88 gcc 89 89 air 90 90 dnscontrol 91 + caddy 92 + python311 93 + uv 94 + stdenv.cc.cc.lib 91 95 ] ++ (if pkgs.stdenv.isLinux then [ chromium ] else []); 92 96 93 97 shellHook = '' 94 98 export PATH="$PWD/node_modules/.bin:$PATH" 95 99 export API_PORT=''${API_PORT:-3000} 100 + export LD_LIBRARY_PATH="${pkgs.stdenv.cc.cc.lib}/lib:$LD_LIBRARY_PATH" 96 101 97 102 echo "🚀 Full development environment (Extension + Server)" 98 103 echo "" 99 - echo "Extension: pnpm dev, pnpm build, pnpm zip" 104 + echo "Extension: pnpm dev, pnpm build, pnpm zip, pnpm via" 100 105 echo "Server: cd server && air" 101 106 ''; 102 107 }; ··· 112 117 git 113 118 curl 114 119 jq 120 + caddy 121 + python311 122 + uv 123 + stdenv.cc.cc.lib 115 124 ] ++ (if pkgs.stdenv.isLinux then [ chromium ] else []); 116 125 117 126 shellHook = '' 118 127 export PATH="$PWD/node_modules/.bin:$PATH" 119 128 export API_PORT=''${API_PORT:-3000} 129 + export LD_LIBRARY_PATH="${pkgs.stdenv.cc.cc.lib}/lib:$LD_LIBRARY_PATH" 120 130 121 131 echo "🌐 Extension development environment" 122 - echo "Commands: pnpm dev, pnpm build, pnpm zip" 132 + echo "Commands:" 133 + echo " pnpm dev - Extension development server" 134 + echo " pnpm build - Build extension" 135 + echo " pnpm via - Start via proxy (http://localhost:8082)" 123 136 ''; 124 137 }; 125 138 ··· 144 157 ''; 145 158 }; 146 159 147 - # pywb proxy test environment 160 + # Via proxy development environment (Caddy + pywb) 161 + via = pkgs.mkShell { 162 + buildInputs = with pkgs; [ 163 + caddy 164 + python311 165 + uv 166 + nodejs_20 167 + pnpm 168 + ]; 169 + 170 + shellHook = '' 171 + echo "🌐 Via proxy development environment" 172 + echo "" 173 + echo "📦 Available tools:" 174 + echo " caddy run - Start Caddy reverse proxy (port 8082)" 175 + echo " wayback - Start pywb proxy server (port 8081)" 176 + echo " pnpm build:via - Build via client scripts" 177 + echo "" 178 + echo "🚀 Quick start:" 179 + echo " 1. Terminal 1: wayback" 180 + echo " 2. Terminal 2: caddy run" 181 + echo " 3. Visit http://localhost:8082" 182 + ''; 183 + }; 184 + 185 + # pywb proxy test environment (legacy) 148 186 pywb = pkgs.mkShell { 149 187 buildInputs = with pkgs; [ 150 188 python311 ··· 167 205 } 168 206 ); 169 207 } 208 +
+1 -2
lib/pds.ts
··· 5 5 6 6 const ANNOTATION_COLLECTION = "community.lexicon.annotation.annotation"; 7 7 const COMMENT_COLLECTION = "pub.leaflet.comment"; 8 - const BACKEND_URL = import.meta.env.BACKEND_URL || 'http://localhost:8080'; 8 + const BACKEND_URL = import.meta.env.VITE_BACKEND_URL || import.meta.env.BACKEND_URL || 'https://seams.so'; 9 9 10 10 export async function createAnnotation(annotation: Annotation): Promise<Annotation> { 11 11 const session = await loadSession(); ··· 247 247 selector: selectors, 248 248 }], 249 249 body: ann.body || '', 250 - tags: ann.tags ? JSON.parse(ann.tags) : [], 251 250 createdAt: ann.createdAt, 252 251 author: ann.authorHandle ? { 253 252 did: ann.authorDid,
+4 -1
package.json
··· 7 7 "dev": "wxt", 8 8 "build": "wxt build", 9 9 "zip": "wxt zip", 10 - "build:via": "vite build --config vite.via.config.ts" 10 + "build:via": "vite build --config vite.via.config.ts", 11 + "dev:via": "vite build --config vite.via.config.ts --watch", 12 + "via": "bash scripts/start-via.sh" 11 13 }, 12 14 "repository": { 13 15 "type": "git", ··· 21 23 }, 22 24 "homepage": "https://seams.so", 23 25 "dependencies": { 26 + "@seams/core": "workspace:*", 24 27 "@atcute/oauth-browser-client": "^1.0.27", 25 28 "@atproto/api": "^0.17.3", 26 29 "dom-anchor-text-position": "^5.0.0",
+19
packages/core/package.json
··· 1 + { 2 + "name": "@seams/core", 3 + "version": "1.0.0", 4 + "type": "module", 5 + "main": "./src/index.ts", 6 + "exports": { 7 + ".": "./src/index.ts", 8 + "./storage": "./src/storage/index.ts", 9 + "./background": "./src/background/index.ts", 10 + "./content": "./src/content/index.ts", 11 + "./sidebar": "./src/sidebar/index.ts" 12 + }, 13 + "dependencies": { 14 + "@atcute/oauth-browser-client": "^1.0.27", 15 + "@atproto/api": "^0.17.3", 16 + "dom-anchor-text-position": "^5.0.0", 17 + "dom-anchor-text-quote": "^4.0.2" 18 + } 19 + }
+2
packages/core/src/background/index.ts
··· 1 + export { BackgroundWorker } from './worker'; 2 + export type { BackgroundWorkerOptions } from './worker';
+46
packages/core/src/background/worker.ts
··· 1 + // Background worker - fetches annotations and writes to storage 2 + import type { StorageAdapter } from '../storage/adapter'; 3 + import type { Annotation } from '../types'; 4 + 5 + export interface BackgroundWorkerOptions { 6 + storage: StorageAdapter; 7 + fetchAnnotations: (url: string) => Promise<Annotation[]>; 8 + } 9 + 10 + export class BackgroundWorker { 11 + private storage: StorageAdapter; 12 + private fetchAnnotations: (url: string) => Promise<Annotation[]>; 13 + private currentUrl: string | null = null; 14 + 15 + constructor(options: BackgroundWorkerOptions) { 16 + this.storage = options.storage; 17 + this.fetchAnnotations = options.fetchAnnotations; 18 + } 19 + 20 + async syncAnnotationsForUrl(url: string): Promise<void> { 21 + console.log('[BackgroundWorker] Syncing annotations for:', url); 22 + 23 + try { 24 + const annotations = await this.fetchAnnotations(url); 25 + console.log('[BackgroundWorker] Fetched', annotations.length, 'annotations'); 26 + 27 + const key = `annotations:${url}`; 28 + await this.storage.set(key, annotations); 29 + console.log('[BackgroundWorker] Stored', annotations.length, 'annotations'); 30 + } catch (error) { 31 + console.error('[BackgroundWorker] Failed to sync annotations:', error); 32 + throw error; 33 + } 34 + } 35 + 36 + setCurrentUrl(url: string): void { 37 + if (this.currentUrl !== url) { 38 + this.currentUrl = url; 39 + this.syncAnnotationsForUrl(url); 40 + } 41 + } 42 + 43 + getCurrentUrl(): string | null { 44 + return this.currentUrl; 45 + } 46 + }
+2
packages/core/src/content/index.ts
··· 1 + export { ContentScript } from './script'; 2 + export type { ContentScriptOptions } from './script';
+65
packages/core/src/content/script.ts
··· 1 + // Content script - renders highlights and handles page interaction 2 + import type { StorageAdapter } from '../storage/adapter'; 3 + import type { Annotation } from '../types'; 4 + 5 + export interface ContentScriptOptions { 6 + storage: StorageAdapter; 7 + getCurrentUrl: () => string; 8 + applyHighlights: (annotations: Annotation[]) => void; 9 + clearHighlights: () => void; 10 + } 11 + 12 + export class ContentScript { 13 + private storage: StorageAdapter; 14 + private getCurrentUrl: () => string; 15 + private applyHighlights: (annotations: Annotation[]) => void; 16 + private clearHighlights: () => void; 17 + private currentAnnotations: Annotation[] = []; 18 + 19 + constructor(options: ContentScriptOptions) { 20 + this.storage = options.storage; 21 + this.getCurrentUrl = options.getCurrentUrl; 22 + this.applyHighlights = options.applyHighlights; 23 + this.clearHighlights = options.clearHighlights; 24 + } 25 + 26 + async start(): Promise<void> { 27 + // Load annotations for current URL 28 + await this.renderAnnotationsForCurrentUrl(); 29 + 30 + // Listen for storage changes 31 + this.storage.onChange(({ key, newValue }) => { 32 + const currentUrl = this.getCurrentUrl(); 33 + const expectedKey = `annotations:${currentUrl}`; 34 + 35 + if (key === expectedKey) { 36 + this.renderAnnotations(newValue || []); 37 + } 38 + }); 39 + } 40 + 41 + async renderAnnotationsForCurrentUrl(): Promise<void> { 42 + const url = this.getCurrentUrl(); 43 + const key = `annotations:${url}`; 44 + const annotations = await this.storage.get(key); 45 + 46 + if (annotations) { 47 + this.renderAnnotations(annotations); 48 + } else { 49 + this.clearHighlights(); 50 + } 51 + } 52 + 53 + private renderAnnotations(annotations: Annotation[]): void { 54 + this.currentAnnotations = annotations; 55 + this.clearHighlights(); 56 + 57 + if (annotations.length > 0) { 58 + this.applyHighlights(annotations); 59 + } 60 + } 61 + 62 + getCurrentAnnotations(): Annotation[] { 63 + return this.currentAnnotations; 64 + } 65 + }
+5
packages/core/src/index.ts
··· 1 + // Core package exports 2 + export * from './types'; 3 + export * from './storage'; 4 + export * from './background'; 5 + export * from './content';
+13
packages/core/src/storage/adapter.ts
··· 1 + // Storage adapter interface - implemented by both browser.storage and localStorage+BroadcastChannel 2 + 3 + export interface StorageChange { 4 + key: string; 5 + newValue: any; 6 + oldValue?: any; 7 + } 8 + 9 + export interface StorageAdapter { 10 + get(key: string): Promise<any>; 11 + set(key: string, value: any): Promise<void>; 12 + onChange(callback: (change: StorageChange) => void): void; 13 + }
+2
packages/core/src/storage/index.ts
··· 1 + export type { StorageAdapter, StorageChange } from './adapter'; 2 + export { WebStorageAdapter } from './web';
+36
packages/core/src/storage/web.ts
··· 1 + // Web storage adapter using localStorage + BroadcastChannel 2 + import type { StorageAdapter, StorageChange } from './adapter'; 3 + 4 + export class WebStorageAdapter implements StorageAdapter { 5 + private channel: BroadcastChannel; 6 + private listeners: Array<(change: StorageChange) => void> = []; 7 + 8 + constructor(channelName: string = 'seams-storage') { 9 + this.channel = new BroadcastChannel(channelName); 10 + 11 + this.channel.onmessage = (event) => { 12 + this.listeners.forEach(callback => callback(event.data)); 13 + }; 14 + } 15 + 16 + async get(key: string): Promise<any> { 17 + const value = localStorage.getItem(key); 18 + return value ? JSON.parse(value) : null; 19 + } 20 + 21 + async set(key: string, value: any): Promise<void> { 22 + const oldValue = await this.get(key); 23 + localStorage.setItem(key, JSON.stringify(value)); 24 + 25 + const change: StorageChange = { key, newValue: value, oldValue }; 26 + this.channel.postMessage(change); 27 + } 28 + 29 + onChange(callback: (change: StorageChange) => void): void { 30 + this.listeners.push(callback); 31 + } 32 + 33 + close(): void { 34 + this.channel.close(); 35 + } 36 + }
+26
packages/core/src/types.ts
··· 1 + // Shared types 2 + 3 + export interface Annotation { 4 + uri: string; 5 + cid: string; 6 + value: { 7 + target: { 8 + url: string; 9 + selector?: Array<{ 10 + type: string; 11 + exact?: string; 12 + prefix?: string; 13 + suffix?: string; 14 + start?: number; 15 + end?: number; 16 + }>; 17 + }; 18 + body: string; 19 + createdAt: string; 20 + }; 21 + author?: { 22 + did: string; 23 + handle?: string; 24 + avatar?: string; 25 + }; 26 + }
+18
pnpm-lock.yaml
··· 17 17 '@atproto/api': 18 18 specifier: ^0.17.3 19 19 version: 0.17.3 20 + '@seams/core': 21 + specifier: workspace:* 22 + version: link:packages/core 20 23 dom-anchor-text-position: 21 24 specifier: ^5.0.0 22 25 version: 5.0.0 ··· 33 36 wxt: 34 37 specifier: 0.20.9 35 38 version: 0.20.9(@types/node@24.8.1)(rollup@4.52.5) 39 + 40 + packages/core: 41 + dependencies: 42 + '@atcute/oauth-browser-client': 43 + specifier: ^1.0.27 44 + version: 1.0.27 45 + '@atproto/api': 46 + specifier: ^0.17.3 47 + version: 0.17.3 48 + dom-anchor-text-position: 49 + specifier: ^5.0.0 50 + version: 5.0.0 51 + dom-anchor-text-quote: 52 + specifier: ^4.0.2 53 + version: 4.0.2 36 54 37 55 packages: 38 56
+3
pnpm-workspace.yaml
··· 1 + packages: 2 + - 'packages/*' 3 + - '.'
+2
pywb-test/collections/proxy/templates/head_insert.html
··· 1 + <!-- Seams via client injection --> 2 + <script type="module" src="/static/seams-client.js"></script>
+18
pywb-test/config.yaml
··· 1 + collections: 2 + proxy: 3 + index: $live 4 + templates: 5 + head_insert: collections/proxy/templates/head_insert.html 6 + # Minimal rewriting to inject head_insert only 7 + enable_banner: false 8 + enable_wombat: false 9 + # Exclude API calls from proxy interception 10 + ignore_prefixes: 11 + - https://seams.so/api/ 12 + - http://localhost:8080/api/ 13 + 14 + framed_replay: false 15 + 16 + # Serve our static client script 17 + static_routes: 18 + static: ./static/
+323
pywb-test/static/about.css
··· 1 + @import url('fonts.css'); 2 + 3 + * { 4 + box-sizing: border-box; 5 + margin: 0; 6 + padding: 0; 7 + } 8 + 9 + :root { 10 + --forest-green: #2d5016; 11 + --forest-green-light: #3d6b1f; 12 + --forest-green-dark: #1f3810; 13 + } 14 + 15 + body { 16 + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; 17 + background: #fafafa; 18 + color: #1a1a1a; 19 + position: relative; 20 + line-height: 1.6; 21 + margin: 0; 22 + padding: 0; 23 + } 24 + 25 + /* Blueprint grid background */ 26 + body::before { 27 + content: ''; 28 + position: fixed; 29 + inset: 0; 30 + pointer-events: none; 31 + background-image: 32 + radial-gradient(circle, rgba(45, 80, 22, 0.06) 1px, transparent 1px); 33 + background-size: 20px 20px; 34 + z-index: 0; 35 + } 36 + 37 + /* Layout - Sidebar + Main Content */ 38 + .layout { 39 + display: flex; 40 + min-height: 100vh; 41 + position: relative; 42 + z-index: 1; 43 + } 44 + 45 + /* Sidebar */ 46 + .sidebar { 47 + width: 420px; 48 + background: #fff; 49 + border-right: 2px dashed #d0d0d0; 50 + position: sticky; 51 + top: 0; 52 + height: 100vh; 53 + overflow-y: auto; 54 + flex-shrink: 0; 55 + } 56 + 57 + .sidebar-content { 58 + display: flex; 59 + flex-direction: column; 60 + justify-content: space-between; 61 + min-height: 100vh; 62 + padding: 60px 0; 63 + } 64 + 65 + /* Hero Section in Sidebar */ 66 + .hero { 67 + flex: 1; 68 + display: flex; 69 + flex-direction: column; 70 + justify-content: center; 71 + align-items: center; 72 + text-align: center; 73 + padding: 0 48px; 74 + } 75 + 76 + .logo { 77 + font-family: 'Spectral', serif; 78 + font-size: 16px; 79 + font-weight: 700; 80 + letter-spacing: 0.1em; 81 + text-transform: uppercase; 82 + color: #666; 83 + margin-bottom: 24px; 84 + } 85 + 86 + .hero h1 { 87 + font-family: 'Fraunces', serif; 88 + font-size: clamp(28px, 3vw, 40px); 89 + font-weight: 600; 90 + margin-bottom: 24px; 91 + color: #1a1a1a; 92 + line-height: 1.2; 93 + letter-spacing: -0.01em; 94 + } 95 + 96 + /* CTA Buttons */ 97 + .cta-buttons { 98 + display: flex; 99 + gap: 12px; 100 + flex-direction: column; 101 + align-items: center; 102 + } 103 + 104 + .cta-primary, 105 + .cta-secondary { 106 + padding: 12px 28px; 107 + border-radius: 2px; 108 + font-size: 14px; 109 + font-weight: 500; 110 + text-decoration: none; 111 + transition: all 0.2s; 112 + border: 1px dashed; 113 + display: inline-block; 114 + } 115 + 116 + .cta-primary { 117 + background: var(--forest-green); 118 + color: #fff; 119 + border-color: var(--forest-green-dark); 120 + } 121 + 122 + .cta-primary:hover { 123 + background: var(--forest-green-dark); 124 + transform: translateY(-1px); 125 + } 126 + 127 + .cta-secondary { 128 + background: transparent; 129 + color: var(--forest-green); 130 + border-color: var(--forest-green); 131 + } 132 + 133 + .cta-secondary:hover { 134 + border-color: var(--forest-green-dark); 135 + background: rgba(45, 80, 22, 0.05); 136 + } 137 + 138 + /* Main Content Area */ 139 + .main-content { 140 + flex: 1; 141 + background: #fafafa; 142 + overflow-y: auto; 143 + } 144 + 145 + /* Content Section */ 146 + .content-section { 147 + padding: 60px 48px; 148 + max-width: 900px; 149 + } 150 + 151 + .content-container h1 { 152 + font-family: 'Fraunces', serif; 153 + font-size: 36px; 154 + font-weight: 700; 155 + margin-bottom: 24px; 156 + color: #1a1a1a; 157 + line-height: 1.2; 158 + } 159 + 160 + .content-container h2 { 161 + font-family: 'Fraunces', serif; 162 + font-size: 24px; 163 + font-weight: 600; 164 + margin-top: 40px; 165 + margin-bottom: 16px; 166 + color: #1a1a1a; 167 + line-height: 1.3; 168 + } 169 + 170 + .content-container p { 171 + font-family: 'Fraunces', serif; 172 + margin-bottom: 16px; 173 + line-height: 1.7; 174 + color: #333; 175 + font-size: 16px; 176 + } 177 + 178 + .content-container ul { 179 + font-family: 'Fraunces', serif; 180 + margin-bottom: 16px; 181 + padding-left: 24px; 182 + } 183 + 184 + .content-container li { 185 + margin-bottom: 12px; 186 + line-height: 1.7; 187 + color: #333; 188 + } 189 + 190 + .content-container a { 191 + color: var(--forest-green); 192 + text-decoration: none; 193 + border-bottom: 1px dashed var(--forest-green); 194 + transition: border-color 0.2s; 195 + } 196 + 197 + .content-container a:hover { 198 + border-bottom-color: var(--forest-green-dark); 199 + } 200 + 201 + .content-container strong { 202 + font-weight: 600; 203 + color: #1a1a1a; 204 + } 205 + 206 + /* Footer in Sidebar */ 207 + .footer { 208 + padding-top: 40px; 209 + color: #999; 210 + font-size: 13px; 211 + text-align: center; 212 + margin: 0; 213 + border-top: 2px dashed #d0d0d0; 214 + } 215 + 216 + .mobile-footer { 217 + display: none; 218 + } 219 + 220 + .desktop-footer { 221 + display: block; 222 + } 223 + 224 + .footer-icons { 225 + display: flex; 226 + gap: 20px; 227 + justify-content: center; 228 + align-items: center; 229 + } 230 + 231 + .footer-icons a { 232 + color: #1a1a1a; 233 + text-decoration: none; 234 + transition: all 0.2s; 235 + display: flex; 236 + align-items: center; 237 + justify-content: center; 238 + } 239 + 240 + .footer-icons a img, 241 + .footer-icons a svg { 242 + width: 32px; 243 + height: 32px; 244 + opacity: 0.6; 245 + transition: opacity 0.2s; 246 + } 247 + 248 + .footer-icons a:hover img, 249 + .footer-icons a:hover svg { 250 + opacity: 1; 251 + } 252 + 253 + /* Responsive */ 254 + @media (max-width: 1024px) { 255 + .layout { 256 + flex-direction: column; 257 + } 258 + 259 + .sidebar { 260 + width: 100%; 261 + height: auto; 262 + position: relative; 263 + border-right: none; 264 + border-bottom: 2px dashed #d0d0d0; 265 + } 266 + 267 + .sidebar-content { 268 + min-height: auto; 269 + padding: 40px 0; 270 + } 271 + 272 + .hero { 273 + justify-content: flex-start; 274 + padding: 0 32px; 275 + } 276 + 277 + .desktop-footer { 278 + display: none; 279 + } 280 + 281 + .mobile-footer { 282 + display: block; 283 + padding: 40px 32px; 284 + margin: 0; 285 + border-top: 2px dashed #d0d0d0; 286 + background: #fafafa; 287 + } 288 + 289 + .content-section { 290 + padding: 48px 32px; 291 + } 292 + } 293 + 294 + @media (max-width: 640px) { 295 + .sidebar-content { 296 + padding: 32px 0; 297 + } 298 + 299 + .hero { 300 + padding: 0 24px; 301 + } 302 + 303 + .hero h1 { 304 + font-size: 28px; 305 + } 306 + 307 + .mobile-footer { 308 + padding: 32px 24px; 309 + font-size: 12px; 310 + } 311 + 312 + .content-section { 313 + padding: 32px 24px; 314 + } 315 + 316 + .content-container h1 { 317 + font-size: 28px; 318 + } 319 + 320 + .content-container h2 { 321 + font-size: 20px; 322 + } 323 + }
+85
pywb-test/static/about.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 + <title>About - Seams.so</title> 7 + <link rel="stylesheet" href="about.css"> 8 + </head> 9 + <body> 10 + <div class="layout"> 11 + <aside class="sidebar"> 12 + <div class="sidebar-content"> 13 + <header class="hero"> 14 + <div class="logo">Seams</div> 15 + <h1>About</h1> 16 + <div class="cta-buttons"> 17 + <a href="/" class="cta-secondary">← Back to Home</a> 18 + </div> 19 + </header> 20 + 21 + <footer class="footer desktop-footer"> 22 + <div class="footer-icons"> 23 + <a href="https://tangled.org/@hyl.st/seams.so" target="_blank" aria-label="Tangled"> 24 + <img src="https://semble.so/_next/static/media/tangled-icon.b95d4d65.svg" alt="Tangled" /> 25 + </a> 26 + <a href="https://bsky.app" target="_blank" aria-label="Bluesky"> 27 + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 360 320"> 28 + <path fill="currentColor" d="M180 142c-16.3-31.7-60.7-90.8-102-120C38.5-5.9 23.4-1 13.5 3.4 2.1 8.6 0 26.2 0 36.5c0 10.4 5.7 84.8 9.4 97.2 12.2 41 55.7 55 95.7 50.5-58.7 8.6-110.8 30-42.4 106.1 75.1 77.9 103-16.7 117.3-64.6 14.3 48 30.8 139 116 64.6 64-64.6 17.6-97.5-41.1-106.1 40 4.4 83.5-9.5 95.7-50.5 3.7-12.4 9.4-86.8 9.4-97.2 0-10.3-2-27.9-13.5-33C336.5-1 321.5-6 282 22c-41.3 29.2-85.7 88.3-102 120Z"/> 29 + </svg> 30 + </a> 31 + </div> 32 + </footer> 33 + </div> 34 + </aside> 35 + 36 + <main class="main-content"> 37 + <div class="content-section"> 38 + <div class="content-container"> 39 + <h1>About Seams</h1> 40 + 41 + <p>Seams is an open social annotation tool built on AT Protocol. It's an experiment in collaborative knowledge-building, inspired by tools like Hypothesis, but designed for the decentralized web. <br/> We believe strongly that new tools that respect their users, and allow them to work together can bring around more positive futures</p> 42 + 43 + <h2>Why AT Protocol?</h2> 44 + <ul> 45 + <li><strong>You own your content</strong> - Your annotations are stored in your AT Protocol Personal Data Server (PDS), which in most cases is hosted by Bluesky. If Seams shuts down, all your annotations remain accessible.</li> 46 + <li><strong>It's portable</strong> - Your data can move with you. If you switch PDS providers, Seams continues to work with your annotations.</li> 47 + </ul> 48 + 49 + <h2>Get Started</h2> 50 + <p>To start using Seams, you'll need:</p> 51 + <ul> 52 + <li>An AT Protocol account (most likely a Bluesky account)</li> 53 + <li>The Seams browser extension (available for Chrome and Firefox)</li> 54 + </ul> 55 + <p>Once installed, simply highlight text on any webpage and add your annotation. Your insights become part of the collective knowledge layer.</p> 56 + 57 + <h2>Privacy</h2> 58 + <p>...or "We don't want you data"</p> 59 + <ul> 60 + <li><strong>Your annotations are yours</strong> - They're stored in your AT Protocol Personal Data Server (PDS), which you control. We don't own and won't try to sell your data.</li> 61 + <li><strong>Public by default</strong> - Annotations you create are publicly visible to anyone who visits the same page or finds them through our feed. This is a social annotation tool, not a private archive. When AT Protocol introduces more granular privacy controls, we will adopt their approach.</li> 62 + <li><strong>What we collect</strong> - The app indexes annotations from your PDS for display. We cache profile information (handle, display name, avatar) for performance. We don't track your browsing behavior or use analytics that can personally identify you.</li> 63 + </ul> 64 + 65 + <h2>Contact</h2> 66 + <p>Questions or feedback? Reach out to <a href="https://bsky.app/profile/seams.so" target="_blank">@seams.so</a> on Bluesky.</p> 67 + </div> 68 + </div> 69 + </main> 70 + 71 + <footer class="footer mobile-footer"> 72 + <div class="footer-icons"> 73 + <a href="https://tangled.org/@sealight.xyz/seams.so" target="_blank" aria-label="Tangled"> 74 + <img src="https://semble.so/_next/static/media/tangled-icon.b95d4d65.svg" alt="Tangled" /> 75 + </a> 76 + <a href="https://bsky.app" target="_blank" aria-label="Bluesky"> 77 + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 360 320"> 78 + <path fill="currentColor" d="M180 142c-16.3-31.7-60.7-90.8-102-120C38.5-5.9 23.4-1 13.5 3.4 2.1 8.6 0 26.2 0 36.5c0 10.4 5.7 84.8 9.4 97.2 12.2 41 55.7 55 95.7 50.5-58.7 8.6-110.8 30-42.4 106.1 75.1 77.9 103-16.7 117.3-64.6 14.3 48 30.8 139 116 64.6 64-64.6 17.6-97.5-41.1-106.1 40 4.4 83.5-9.5 95.7-50.5 3.7-12.4 9.4-86.8 9.4-97.2 0-10.3-2-27.9-13.5-33C336.5-1 321.5-6 282 22c-41.3 29.2-85.7 88.3-102 120Z"/> 79 + </svg> 80 + </a> 81 + </div> 82 + </footer> 83 + </div> 84 + </body> 85 + </html>
+2
pywb-test/static/assets/oauth-web-B1Uotnw-.js
··· 1 + var ue=Object.defineProperty;var G=t=>{throw TypeError(t)};var de=(t,e,r)=>e in t?ue(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r;var f=(t,e,r)=>de(t,typeof e!="symbol"?e+"":e,r),H=(t,e,r)=>e.has(t)||G("Cannot "+r);var m=(t,e,r)=>(H(t,e,"read from private field"),r?r.call(t):e.get(t)),E=(t,e,r)=>e.has(t)?G("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(t):e.set(t,r),k=(t,e,r,s)=>(H(t,e,"write to private field"),s?s.call(t,r):e.set(t,r),r),L=(t,e,r)=>(H(t,e,"access private method"),r);const he="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";let W=(t=21)=>{let e="",r=crypto.getRandomValues(new Uint8Array(t|=0));for(;t--;)e+=he[r[t]&63];return e};const pe=new TextEncoder;new TextDecoder;const fe=crypto.subtle,we=t=>new Uint8Array(t),ge=we,$=t=>pe.encode(t),ye=async t=>new Uint8Array(await fe.digest("SHA-256",t)),me=(t,e,r)=>s=>{const n=(1<<e)-1;let o="",a=0,i=0;for(let c=0;c<s.length;++c)for(i=i<<8|s[c],a+=8;a>e;)a-=e,o+=t[n&i>>a];if(a!==0&&(o+=t[n&i<<e-a]),r)for(;o.length*e&7;)o+="=";return o},_e=(t,e,r)=>{const s={};for(let n=0;n<t.length;++n)s[t[n]]=n;return n=>{let o=n.length;for(;r&&n[o-1]==="=";)--o;const a=ge(o*e/8|0);let i=0,c=0,l=0;for(let u=0;u<o;++u){const h=s[n[u]];if(h===void 0)throw new SyntaxError("invalid base string");c=c<<e|h,i+=e,i>=8&&(i-=8,a[l++]=255&c>>i)}if(i>=e||255&c<<8-i)throw new SyntaxError("unexpected end of data");return a}},ve=t=>Uint8Array.fromBase64(t,{alphabet:"base64url",lastChunkHandling:"loose"}),Se=t=>t.toBase64({alphabet:"base64url",omitPadding:!0}),te="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",be=_e(te,6,!1),Ae=me(te,6,!1),re="fromBase64"in Uint8Array,Ee=re?ve:be,z=re?Se:Ae,N=typeof navigator<"u"?navigator.locks:void 0,se=async t=>{const e=$(t),r=await ye(e);return z(r)},ke=async()=>{const t=W(64);return{verifier:t,challenge:await se(t),method:"S256"}},Re=t=>{if(t!=null){const e=JSON.parse(t);if(e!=null)return e}return{}},xe=({name:t})=>{const e=new AbortController,r=e.signal,s=(n,o,a=!1)=>{let i;const c=`${t}:${n}`,l=()=>i&&localStorage.setItem(c,JSON.stringify(i)),u=()=>{if(r.aborted)throw new Error("store closed");return i??(i=Re(localStorage.getItem(c)))};{const h=d=>{d.key===c&&(i=void 0)};globalThis.addEventListener("storage",h,{signal:r})}{const h=async d=>{if(!d||r.aborted||(await new Promise(w=>setTimeout(w,1e4)),r.aborted))return;let g=Date.now(),v=!1;u();for(const w in i){const U=i[w].expiresAt;U!==null&&g>U&&(v=!0,delete i[w])}v&&l()};N?N.request(`${c}:cleanup`,{ifAvailable:!0},h):h(!0)}return{get(h){u();const d=i[h];if(!d)return;const g=d.expiresAt;if(g!==null&&Date.now()>g){delete i[h],l();return}return d.value},getWithLapsed(h){u();const d=i[h],g=Date.now();if(!d)return[void 0,1/0];const v=d.updatedAt;return v===void 0?[d.value,1/0]:[d.value,g-v]},set(h,d){u();const g={value:d,expiresAt:o(d),updatedAt:a?Date.now():void 0};i[h]=g,l()},delete(h){u(),i[h]!==void 0&&(delete i[h],l())},keys(){return u(),Object.keys(i)}}};return{dispose:()=>{e.abort()},sessions:s("sessions",({token:n})=>n.refresh?null:n.expires_at??null),states:s("states",n=>Date.now()+10*60*1e3),dpopNonces:s("dpopNonces",n=>Date.now()+24*60*60*1e3,!0),inflightDpop:new Map}};let M,Z,_;const Ue=t=>{({client_id:M,redirect_uri:Z}=t.metadata),_=xe({name:t.storageName??"atcute-oauth"})};class D extends Error{constructor(){super(...arguments);f(this,"name","LoginError")}}class je extends Error{constructor(){super(...arguments);f(this,"name","AuthorizationError")}}class p extends Error{constructor(){super(...arguments);f(this,"name","ResolverError")}}class q extends Error{constructor(r,s,n){super(s,n);f(this,"sub");f(this,"name","TokenRefreshError");this.sub=r}}class ne extends Error{constructor(r,s){var l,u;const n=Y((l=Q(s))==null?void 0:l.error),o=Y((u=Q(s))==null?void 0:u.error_description),a=n?`"${n}"`:"unknown",i=o?`: ${o}`:"",c=`OAuth ${a} error${i}`;super(c);f(this,"response");f(this,"data");f(this,"name","OAuthResponseError");f(this,"error");f(this,"description");this.response=r,this.data=s,this.error=n,this.description=o}get status(){return this.response.status}get headers(){return this.response.headers}}class De extends Error{constructor(r,s,n){super(n);f(this,"response");f(this,"status");f(this,"name","FetchResponseError");this.response=r,this.status=s}}const Y=t=>typeof t=="string"?t:void 0,Q=t=>typeof t=="object"&&t!==null&&!Array.isArray(t)?t:void 0,ze=/^did:([a-z]+):([a-zA-Z0-9._:%\-]*[a-zA-Z0-9._\-])$/,Oe=t=>typeof t=="string"&&t.length>=7&&t.length<=2048&&ze.test(t),Pe="parse"in URL,Ie=t=>{let e=null;if(Pe)e=URL.parse(t);else try{e=new URL(t)}catch{}return e!==null&&(e.protocol==="https:"||e.protocol==="http:")&&e.pathname==="/"&&e.search===""&&e.hash===""},Le=(t,e)=>{const r=t.service;if(r)for(let s=0,n=r.length;s<n;s++){const{id:o,type:a,serviceEndpoint:i}=r[s];if(!(o!==e.id&&o!==t.id+e.id)){if(e.type!==void 0){if(Array.isArray(a)){if(!a.includes(e.type))continue}else if(a!==e.type)continue}if(!(typeof i!="string"||!Ie(i)))return i}}},Te=t=>Le(t,{id:"#atproto_pds",type:"AtprotoPersonalDataServer"}),$e="https://public.api.bsky.app",B=t=>{var e;return(e=t.get("content-type"))==null?void 0:e.split(";")[0]},Ne="parse"in URL,qe=t=>{let e=null;if(Ne)e=URL.parse(t);else try{e=new URL(t)}catch{}return e!==null?e.protocol==="https:"||e.protocol==="http:":!1},Ke=/^([a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*(?:\.[a-zA-Z]{2,}))$/,Be=async t=>{const e=$e+`/xrpc/com.atproto.identity.resolveHandle?handle=${t}`,r=await fetch(e);if(r.status===400)throw new p("domain handle not found");if(!r.ok)throw new p("directory is unreachable");return(await r.json()).did},Fe=async t=>{const e=t.indexOf(":",4),r=t.slice(4,e),s=t.slice(e+1);let n;if(r==="plc"){const o=await fetch(`https://plc.directory/${t}`);if(o.status===404)throw new p("did not found in directory");if(!o.ok)throw new p("directory is unreachable");n=await o.json()}else if(r==="web"){if(!Ke.test(s))throw new p("invalid identifier");const o=await fetch(`https://${s}/.well-known/did.json`);if(!o.ok)throw new p("did document is unreachable");n=await o.json()}else throw new p("unsupported did method");return n},Je=async t=>{const e=new URL("/.well-known/oauth-protected-resource",t),r=await fetch(e,{redirect:"manual",headers:{accept:"application/json"}});if(r.status!==200||B(r.headers)!=="application/json")throw new p("unexpected response");const s=await r.json();if(s.resource!==e.origin)throw new p("unexpected issuer");return s},He=async t=>{const e=new URL("/.well-known/oauth-authorization-server",t),r=await fetch(e,{redirect:"manual",headers:{accept:"application/json"}});if(r.status!==200||B(r.headers)!=="application/json")throw new p("unexpected response");const s=await r.json();if(s.issuer!==e.origin)throw new p("unexpected issuer");if(!qe(s.authorization_endpoint))throw new p("authorization server provided incorrect authorization endpoint");if(!s.client_id_metadata_document_supported)throw new p("authorization server does not support 'client_id_metadata_document'");if(!s.pushed_authorization_request_endpoint)throw new p("authorization server does not support 'pushed_authorization request'");if(s.response_types_supported&&!s.response_types_supported.includes("code"))throw new p("authorization server does not support 'code' response type");return s},oe=async t=>{let e;Oe(t)?e=t:e=await Be(t);const r=await Fe(e),s=Te(r);if(!s)throw new p("missing pds endpoint");return{identity:{id:e,raw:t,pds:new URL(s)},metadata:await Ce(s)}},Ce=async t=>{var n;const e=await Je(t);if(((n=e.authorization_servers)==null?void 0:n.length)!==1)throw new p("expected exactly one authorization server in the listing");const r=e.authorization_servers[0],s=await He(r);if(s.protected_resources&&!s.protected_resources.includes(e.resource))throw new p("server is not in authorization server's jurisdiction");return s},ae={name:"ECDSA",namedCurve:"P-256"},We=async()=>{const t=await crypto.subtle.generateKey(ae,!0,["sign","verify"]),e=await crypto.subtle.exportKey("pkcs8",t.privateKey),{ext:r,key_ops:s,...n}=await crypto.subtle.exportKey("jwk",t.publicKey);return{typ:"ES256",key:z(new Uint8Array(e)),jwt:z($(JSON.stringify({typ:"dpop+jwt",alg:"ES256",jwk:n})))}},Me=t=>{const e=t.jwt,r=crypto.subtle.importKey("pkcs8",Ee(t.key),ae,!0,["sign"]),s=(n,o,a,i)=>{const c={ath:i,htm:n,htu:o,iat:Math.floor(Date.now()/1e3),jti:W(24),nonce:a};return z($(JSON.stringify(c)))};return async(n,o,a,i)=>{const c=s(n,o,a,i),l=await crypto.subtle.sign({name:"ECDSA",hash:{name:"SHA-256"}},await r,$(e+"."+c)),u=z(new Uint8Array(l));return e+"."+c+"."+u}},ie=(t,e)=>{const r=_.dpopNonces,s=_.inflightDpop,n=Me(t);return async(o,a)=>{const i=new Request(o,a),c=i.headers.get("authorization"),l=c!=null&&c.startsWith("DPoP ")?await se(c.slice(5)):void 0,{method:u,url:h}=i,{origin:d,pathname:g}=new URL(h),v=d+g;let w=s.get(d);w&&(await w.promise,w=void 0);let I,U=!1;try{const[j,y]=r.getWithLapsed(d);I=j,U=y>3*60*1e3}catch{}U&&s.set(d,w=Promise.withResolvers());let A;try{const j=await n(u,v,I,l);i.headers.set("dpop",j);const y=await fetch(i);if(A=y.headers.get("dpop-nonce"),A===null||A===I)return y;try{r.set(d,A)}catch{}if(!await Ze(y,e)||o===i||(a==null?void 0:a.body)instanceof ReadableStream)return y}finally{w&&(s.delete(d),w.resolve())}{const j=await n(u,v,A,l),y=new Request(o,a);y.headers.set("dpop",j);const F=await fetch(y),J=F.headers.get("dpop-nonce");if(J!==null&&J!==A)try{r.set(d,J)}catch{}return F}}},Ze=async(t,e)=>{if((e===void 0||e===!1)&&t.status===401){const r=t.headers.get("www-authenticate");if(r!=null&&r.startsWith("DPoP"))return r.includes('error="use_dpop_nonce"')}if((e===void 0||e===!0)&&t.status===400&&B(t.headers)==="application/json")try{const r=await t.clone().json();return typeof r=="object"&&(r==null?void 0:r.error)==="use_dpop_nonce"}catch{return!1}return!1},Ve=(t,e)=>{const r={};for(let s=0,n=e.length;s<n;s++){const o=e[s];r[o]=t[o]}return r};var O,R,b,C,ce;class P{constructor(e,r){E(this,b);E(this,O);E(this,R);k(this,R,e),k(this,O,ie(r,!0))}async request(e,r){const s=m(this,R)[`${e}_endpoint`];if(!s)throw new Error(`no endpoint for ${e}`);const n=await m(this,O).call(this,s,{method:"post",headers:{"content-type":"application/json"},body:JSON.stringify({...r,client_id:M})});if(B(n.headers)!=="application/json")throw new De(n,2,"unexpected content-type");const o=await n.json();if(n.ok)return o;throw new ne(n,o)}async revoke(e){try{await this.request("revocation",{token:e})}catch{}}async exchangeCode(e,r){const s=await this.request("token",{grant_type:"authorization_code",redirect_uri:Z,code:e,code_verifier:r});try{return await L(this,b,ce).call(this,s)}catch(n){throw await this.revoke(s.access_token),n}}async refresh({sub:e,token:r}){if(!r.refresh)throw new q(e,"no refresh token available");const s=await this.request("token",{grant_type:"refresh_token",refresh_token:r.refresh});try{if(e!==s.sub)throw new q(e,`sub mismatch in token response; got ${s.sub}`);return L(this,b,C).call(this,s)}catch(n){throw await this.revoke(s.access_token),n}}}O=new WeakMap,R=new WeakMap,b=new WeakSet,C=function(e){if(!e.sub)throw new TypeError("missing sub field in token response");if(!e.scope)throw new TypeError("missing scope field in token response");if(e.token_type!=="DPoP")throw new TypeError("token response returned a non-dpop token");return{scope:e.scope,refresh:e.refresh_token,access:e.access_token,type:e.token_type,expires_at:typeof e.expires_in=="number"?Date.now()+e.expires_in*1e3:void 0}},ce=async function(e){const r=e.sub;if(!r)throw new TypeError("missing sub field in token response");const s=L(this,b,C).call(this,e),n=await oe(r);if(n.metadata.issuer!==m(this,R).issuer)throw new TypeError(`issuer mismatch; got ${n.metadata.issuer}`);return{token:s,info:{sub:r,aud:n.identity.pds.href,server:Ve(n.metadata,["issuer","authorization_endpoint","introspection_endpoint","pushed_authorization_request_endpoint","revocation_endpoint","token_endpoint"])}}};const T=new Map,X=async(t,e)=>{var i,c;(i=e==null?void 0:e.signal)==null||i.throwIfAborted();let r=tt;e!=null&&e.noCache?r=Qe:e!=null&&e.allowStale&&(r=Ye);let s;for(;s=T.get(t);){try{const{isFresh:l,value:u}=await s;if(l||r(u))return u}catch{}(c=e==null?void 0:e.signal)==null||c.throwIfAborted()}const n=async()=>{const l=_.sessions.get(t);if(l&&r(l))return{isFresh:!1,value:l};const u=await Xe(t,l);return await le(t,u),{isFresh:!0,value:u}};let o;if(N?o=N.request(`atcute-oauth:${t}`,n):o=n(),o=o.finally(()=>T.delete(t)),T.has(t))throw new Error("concurrent request for the same key");T.set(t,o);const{value:a}=await o;return a},le=async(t,e)=>{try{_.sessions.set(t,e)}catch(r){throw await et(e),r}},Ge=t=>{_.sessions.delete(t)},Ye=()=>!0,Qe=()=>!1,Xe=async(t,e)=>{if(e===void 0)throw new q(t,"session deleted by another tab");const{dpopKey:r,info:s,token:n}=e,o=new P(s.server,r);try{const a=await o.refresh({sub:s.sub,token:n});return{dpopKey:r,info:s,token:a}}catch(a){throw a instanceof ne&&a.status===400&&a.error==="invalid_grant"?new q(t,"session was revoked",{cause:a}):a}},et=async({dpopKey:t,info:e,token:r})=>{await new P(e.server,t).revoke(r.refresh??r.access)},tt=({token:t})=>{const e=t.expires_at;return e==null||Date.now()+6e4<=e},rt=async({metadata:t,identity:e,scope:r})=>{const s=W(24),n=await ke(),o=await We(),a={redirect_uri:Z,code_challenge:n.challenge,code_challenge_method:n.method,state:s,login_hint:e==null?void 0:e.raw,response_mode:"fragment",response_type:"code",display:"page",scope:r};_.states.set(s,{dpopKey:o,metadata:t,verifier:n.verifier});const c=await new P(t,o).request("pushed_authorization_request",a),l=new URL(t.authorization_endpoint);return l.searchParams.set("client_id",M),l.searchParams.set("request_uri",c.request_uri),l},st=async t=>{const e=t.get("iss"),r=t.get("state"),s=t.get("code"),n=t.get("error");if(!r||!(s||n))throw new D("missing parameters");const o=_.states.get(r);if(o)_.states.delete(r);else throw new D("unknown state provided");const a=o.dpopKey,i=o.metadata;if(n)throw new je(t.get("error_description")||n);if(!s)throw new D("missing code parameter");if(e===null)throw new D("missing issuer parameter");if(e!==i.issuer)throw new D("issuer mismatch");const c=new P(i,a),{info:l,token:u}=await c.exchangeCode(s,o.verifier),h=l.sub,d={dpopKey:a,info:l,token:u};return await le(h,d),d};var x,S;class nt{constructor(e){f(this,"session");E(this,x);E(this,S);this.session=e,k(this,x,ie(e.dpopKey,!1))}get sub(){return this.session.info.sub}getSession(e){const r=X(this.session.info.sub,e);return r.then(s=>{this.session=s}).finally(()=>{k(this,S,void 0)}),k(this,S,r)}async signOut(){const e=this.session.info.sub;try{const{dpopKey:r,info:s,token:n}=await X(e,{allowStale:!0});await new P(s.server,r).revoke(n.refresh??n.access)}finally{Ge(e)}}async handle(e,r){await m(this,S);const s=new Headers(r==null?void 0:r.headers);let n=this.session,o=new URL(e,n.info.aud);s.set("authorization",`${n.token.type} ${n.token.access}`);let a=await m(this,x).call(this,o,{...r,headers:s});if(!ot(a))return a;try{m(this,S)?n=await m(this,S):n=await this.getSession()}catch{return a}return(r==null?void 0:r.body)instanceof ReadableStream?a:(o=new URL(e,n.info.aud),s.set("authorization",`${n.token.type} ${n.token.access}`),await m(this,x).call(this,o,{...r,headers:s}))}}x=new WeakMap,S=new WeakMap;const ot=t=>{if(t.status!==401)return!1;const e=t.headers.get("www-authenticate");return e!=null&&(e.startsWith("Bearer ")||e.startsWith("DPoP "))&&e.includes('error="invalid_token"')},V={local:{async get(t){if(!t){const r={};for(let s=0;s<localStorage.length;s++){const n=localStorage.key(s);if(n)try{r[n]=JSON.parse(localStorage.getItem(n)||"null")}catch{r[n]=localStorage.getItem(n)}}return r}const e={};if(typeof t=="string")try{const r=localStorage.getItem(t);e[t]=r?JSON.parse(r):null}catch{e[t]=localStorage.getItem(t)}else Array.isArray(t)?t.forEach(r=>{try{const s=localStorage.getItem(r);e[r]=s?JSON.parse(s):null}catch{e[r]=localStorage.getItem(r)}}):Object.keys(t).forEach(r=>{try{const s=localStorage.getItem(r);e[r]=s?JSON.parse(s):t[r]}catch{e[r]=localStorage.getItem(r)||t[r]}});return e},async set(t){Object.entries(t).forEach(([e,r])=>{localStorage.setItem(e,JSON.stringify(r))})},async remove(t){(Array.isArray(t)?t:[t]).forEach(r=>localStorage.removeItem(r))},async clear(){localStorage.clear()}}},K="synthesis-oauth:session";let ee=!1;function at(){typeof window<"u"&&!ee&&(Ue({metadata:{client_id:"http://localhost:8081/static/client-metadata.json",redirect_uri:"http://localhost:8081/static/oauth-callback.html"}}),ee=!0)}async function lt(t){console.log("[oauth-web] Starting login process for handle:",t),at(),console.log("[oauth-web] Resolving identity...");const{metadata:e}=await oe(t);console.log("[oauth-web] PDS metadata:",e),console.log("[oauth-web] Creating authorization URL...");const r=await rt({metadata:e,scope:"atproto transition:generic"});console.log("[oauth-web] Auth URL:",r.toString()),window.location.href=r.toString()}async function ut(){console.log("[oauth-web] Handling OAuth callback");const t=new URL(window.location.href),e=t.search||t.hash.slice(1),r=new URLSearchParams(e);if(console.log("[oauth-web] OAuth params:",Object.fromEntries(r)),!r.has("code")&&!r.has("error"))return console.log("[oauth-web] No OAuth params found"),null;if(r.has("error")){const n=r.get("error"),o=r.get("error_description");throw console.error("[oauth-web] OAuth error:",n,o),new Error(`OAuth error: ${n} - ${o}`)}console.log("[oauth-web] Finalizing authorization...");const s=await st(r);return console.log("[oauth-web] Authorization complete, session:",s),await it(s),console.log("[oauth-web] Session saved successfully"),s}async function it(t){await V.local.set({[K]:t})}async function dt(){return(await V.local.get(K))[K]||null}async function ht(){await V.local.remove(K)}async function pt(t){return await(await new nt(t).handle("/xrpc/app.bsky.actor.getProfile?actor="+t.info.sub)).json()}export{ht as c,pt as g,ut as h,at as i,dt as l,lt as s}; 2 + //# sourceMappingURL=oauth-web-B1Uotnw-.js.map
+1
pywb-test/static/assets/oauth-web-B1Uotnw-.js.map
··· 1 + {"version":3,"file":"oauth-web-B1Uotnw-.js","sources":["../../../node_modules/.pnpm/nanoid@5.1.6/node_modules/nanoid/url-alphabet/index.js","../../../node_modules/.pnpm/nanoid@5.1.6/node_modules/nanoid/index.browser.js","../../../node_modules/.pnpm/@atcute+uint8array@1.0.5/node_modules/@atcute/uint8array/dist/index.js","../../../node_modules/.pnpm/@atcute+multibase@1.1.6/node_modules/@atcute/multibase/dist/utils.js","../../../node_modules/.pnpm/@atcute+multibase@1.1.6/node_modules/@atcute/multibase/dist/bases/base64-web-native.js","../../../node_modules/.pnpm/@atcute+multibase@1.1.6/node_modules/@atcute/multibase/dist/bases/base64-web-polyfill.js","../../../node_modules/.pnpm/@atcute+multibase@1.1.6/node_modules/@atcute/multibase/dist/bases/base64-web.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/utils/runtime.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/store/db.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/environment.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/errors.js","../../../node_modules/.pnpm/@atcute+lexicons@1.2.2/node_modules/@atcute/lexicons/dist/syntax/did.js","../../../node_modules/.pnpm/@atcute+identity@1.1.1/node_modules/@atcute/identity/dist/utils.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/constants.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/utils/response.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/utils/strings.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/resolvers.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/dpop.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/utils/misc.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/agents/server-agent.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/agents/sessions.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/agents/exchange.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/agents/user-agent.js","../../../lib/storage-adapter.ts","../../../lib/oauth-web.ts"],"sourcesContent":["export const urlAlphabet =\n 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict'\n","/* @ts-self-types=\"./index.d.ts\" */\nimport { urlAlphabet as scopedUrlAlphabet } from './url-alphabet/index.js'\nexport { urlAlphabet } from './url-alphabet/index.js'\nexport let random = bytes => crypto.getRandomValues(new Uint8Array(bytes))\nexport let customRandom = (alphabet, defaultSize, getRandom) => {\n let mask = (2 << Math.log2(alphabet.length - 1)) - 1\n let step = -~((1.6 * mask * defaultSize) / alphabet.length)\n return (size = defaultSize) => {\n let id = ''\n while (true) {\n let bytes = getRandom(step)\n let j = step | 0\n while (j--) {\n id += alphabet[bytes[j] & mask] || ''\n if (id.length >= size) return id\n }\n }\n }\n}\nexport let customAlphabet = (alphabet, size = 21) =>\n customRandom(alphabet, size | 0, random)\nexport let nanoid = (size = 21) => {\n let id = ''\n let bytes = crypto.getRandomValues(new Uint8Array((size |= 0)))\n while (size--) {\n id += scopedUrlAlphabet[bytes[size] & 63]\n }\n return id\n}\n","const textEncoder = new TextEncoder();\nconst textDecoder = new TextDecoder();\nconst subtle = crypto.subtle;\n/**\n * creates an Uint8Array of the requested size, with the contents zeroed\n */\nexport const alloc = (size) => {\n return new Uint8Array(size);\n};\n/**\n * creates an Uint8Array of the requested size, where the contents may not be\n * zeroed out. only use if you're certain that the contents will be overwritten\n */\nexport const allocUnsafe = alloc;\n/**\n * compares two Uint8Array buffers\n */\nexport const compare = (a, b) => {\n const alen = a.length;\n const blen = b.length;\n if (alen > blen) {\n return 1;\n }\n if (alen < blen) {\n return -1;\n }\n for (let i = 0; i < alen; i++) {\n const ax = a[i];\n const bx = b[i];\n if (ax < bx) {\n return -1;\n }\n if (ax > bx) {\n return 1;\n }\n }\n return 0;\n};\n/**\n * checks if the two Uint8Array buffers are equal\n */\nexport const equals = (a, b) => {\n if (a === b) {\n return true;\n }\n let len;\n if ((len = a.length) === b.length) {\n while (len--) {\n if (a[len] !== b[len]) {\n return false;\n }\n }\n }\n return len === -1;\n};\n/**\n * checks if the two Uint8Array buffers are equal, timing-safe version\n */\nexport const timingSafeEquals = (a, b) => {\n let len;\n let out = 0;\n if ((len = a.length) === b.length) {\n while (len--) {\n out |= a[len] ^ b[len];\n }\n }\n return len === -1 && out === 0;\n};\n/**\n * concatenates multiple Uint8Array buffers into one\n */\nexport const concat = (arrays, size) => {\n let written = 0;\n let len = arrays.length;\n let idx;\n if (size === undefined) {\n for (idx = size = 0; idx < len; idx++) {\n const chunk = arrays[idx];\n size += chunk.length;\n }\n }\n const buffer = new Uint8Array(size);\n for (idx = 0; idx < len; idx++) {\n const chunk = arrays[idx];\n buffer.set(chunk, written);\n written += chunk.length;\n }\n return buffer;\n};\n/**\n * encodes a UTF-8 string\n */\nexport const encodeUtf8 = (str) => {\n return textEncoder.encode(str);\n};\n/**\n * encodes a UTF-8 string into a given buffer\n */\nexport const encodeUtf8Into = (to, str, offset, length) => {\n let buffer;\n if (offset === undefined) {\n buffer = to;\n }\n else if (length === undefined) {\n buffer = to.subarray(offset);\n }\n else {\n buffer = to.subarray(offset, offset + length);\n }\n const result = textEncoder.encodeInto(str, buffer);\n return result.written;\n};\nconst fromCharCode = String.fromCharCode;\n/**\n * decodes a UTF-8 string from a given buffer\n */\nexport const decodeUtf8From = (from, offset, length) => {\n let buffer;\n if (offset === undefined) {\n buffer = from;\n }\n else if (length === undefined) {\n buffer = from.subarray(offset);\n }\n else {\n buffer = from.subarray(offset, offset + length);\n }\n const end = buffer.length;\n if (end > 24) {\n return textDecoder.decode(buffer);\n }\n {\n let str = '';\n let idx = 0;\n for (; idx + 3 < end; idx += 4) {\n const a = buffer[idx];\n const b = buffer[idx + 1];\n const c = buffer[idx + 2];\n const d = buffer[idx + 3];\n if ((a | b | c | d) & 0x80) {\n return str + textDecoder.decode(buffer.subarray(idx));\n }\n str += fromCharCode(a, b, c, d);\n }\n for (; idx < end; idx++) {\n const x = buffer[idx];\n if (x & 0x80) {\n return str + textDecoder.decode(buffer.subarray(idx));\n }\n str += fromCharCode(x);\n }\n return str;\n }\n};\n/**\n * get a SHA-256 digest of this buffer\n */\nexport const toSha256 = async (buffer) => {\n return new Uint8Array(await subtle.digest('SHA-256', buffer));\n};\n//# sourceMappingURL=index.js.map","import { alloc, allocUnsafe } from '@atcute/uint8array';\nexport const createRfc4648Encode = (alphabet, bitsPerChar, pad) => {\n return (bytes) => {\n const mask = (1 << bitsPerChar) - 1;\n let str = '';\n let bits = 0; // Number of bits currently in the buffer\n let buffer = 0; // Bits waiting to be written out, MSB first\n for (let i = 0; i < bytes.length; ++i) {\n // Slurp data into the buffer:\n buffer = (buffer << 8) | bytes[i];\n bits += 8;\n // Write out as much as we can:\n while (bits > bitsPerChar) {\n bits -= bitsPerChar;\n str += alphabet[mask & (buffer >> bits)];\n }\n }\n // Partial character:\n if (bits !== 0) {\n str += alphabet[mask & (buffer << (bitsPerChar - bits))];\n }\n // Add padding characters until we hit a byte boundary:\n if (pad) {\n while (((str.length * bitsPerChar) & 7) !== 0) {\n str += '=';\n }\n }\n return str;\n };\n};\nexport const createRfc4648Decode = (alphabet, bitsPerChar, pad) => {\n // Build the character lookup table:\n const codes = {};\n for (let i = 0; i < alphabet.length; ++i) {\n codes[alphabet[i]] = i;\n }\n return (str) => {\n // Count the padding bytes:\n let end = str.length;\n while (pad && str[end - 1] === '=') {\n --end;\n }\n // Allocate the output:\n const bytes = allocUnsafe(((end * bitsPerChar) / 8) | 0);\n // Parse the data:\n let bits = 0; // Number of bits currently in the buffer\n let buffer = 0; // Bits waiting to be written out, MSB first\n let written = 0; // Next byte to write\n for (let i = 0; i < end; ++i) {\n // Read one character from the string:\n const value = codes[str[i]];\n if (value === undefined) {\n throw new SyntaxError(`invalid base string`);\n }\n // Append the bits to the buffer:\n buffer = (buffer << bitsPerChar) | value;\n bits += bitsPerChar;\n // Write out some bits if the buffer has a byte's worth:\n if (bits >= 8) {\n bits -= 8;\n bytes[written++] = 0xff & (buffer >> bits);\n }\n }\n // Verify that we have received just enough bits:\n if (bits >= bitsPerChar || (0xff & (buffer << (8 - bits))) !== 0) {\n throw new SyntaxError('unexpected end of data');\n }\n return bytes;\n };\n};\nexport const createBtcBaseEncode = (alphabet) => {\n if (alphabet.length >= 255) {\n throw new RangeError(`alphabet too long`);\n }\n const BASE = alphabet.length;\n const LEADER = alphabet.charAt(0);\n const iFACTOR = Math.log(256) / Math.log(BASE); // log(256) / log(BASE), rounded up\n return (source) => {\n if (source.length === 0) {\n return '';\n }\n // Skip & count leading zeroes.\n let zeroes = 0;\n let length = 0;\n let pbegin = 0;\n const pend = source.length;\n while (pbegin !== pend && source[pbegin] === 0) {\n pbegin++;\n zeroes++;\n }\n // Allocate enough space in big-endian base58 representation.\n const size = ((pend - pbegin) * iFACTOR + 1) >>> 0;\n const b58 = alloc(size);\n // Process the bytes.\n while (pbegin !== pend) {\n let carry = source[pbegin];\n // Apply \"b58 = b58 * 256 + ch\".\n let i = 0;\n for (let it1 = size - 1; (carry !== 0 || i < length) && it1 !== -1; it1--, i++) {\n carry += (256 * b58[it1]) >>> 0;\n b58[it1] = carry % BASE >>> 0;\n carry = (carry / BASE) >>> 0;\n }\n if (carry !== 0) {\n throw new Error('non-zero carry');\n }\n length = i;\n pbegin++;\n }\n // Skip leading zeroes in base58 result.\n let it2 = size - length;\n while (it2 !== size && b58[it2] === 0) {\n it2++;\n }\n // Translate the result into a string.\n let str = LEADER.repeat(zeroes);\n for (; it2 < size; ++it2) {\n str += alphabet.charAt(b58[it2]);\n }\n return str;\n };\n};\nexport const createBtcBaseDecode = (alphabet) => {\n if (alphabet.length >= 255) {\n throw new RangeError(`alphabet too long`);\n }\n const BASE_MAP = allocUnsafe(256).fill(255);\n for (let i = 0; i < alphabet.length; i++) {\n const xc = alphabet.charCodeAt(i);\n if (BASE_MAP[xc] !== 255) {\n throw new RangeError(`${alphabet[i]} is ambiguous`);\n }\n BASE_MAP[xc] = i;\n }\n const BASE = alphabet.length;\n const LEADER = alphabet.charAt(0);\n const FACTOR = Math.log(BASE) / Math.log(256); // log(BASE) / log(256), rounded up\n return (source) => {\n if (source.length === 0) {\n return allocUnsafe(0);\n }\n // Skip and count leading '1's.\n let psz = 0;\n let zeroes = 0;\n let length = 0;\n while (source[psz] === LEADER) {\n zeroes++;\n psz++;\n }\n // Allocate enough space in big-endian base256 representation.\n const size = ((source.length - psz) * FACTOR + 1) >>> 0; // log(58) / log(256), rounded up.\n const b256 = alloc(size);\n // Process the characters.\n while (psz < source.length) {\n // Decode character\n let carry = BASE_MAP[source.charCodeAt(psz)];\n // Invalid character\n if (carry === 255) {\n throw new Error(`invalid string`);\n }\n let i = 0;\n for (let it3 = size - 1; (carry !== 0 || i < length) && it3 !== -1; it3--, i++) {\n carry += (BASE * b256[it3]) >>> 0;\n b256[it3] = carry % 256 >>> 0;\n carry = (carry / 256) >>> 0;\n }\n if (carry !== 0) {\n throw new Error('non-zero carry');\n }\n length = i;\n psz++;\n }\n // Skip leading zeroes in b256.\n let it4 = size - length;\n while (it4 !== size && b256[it4] === 0) {\n it4++;\n }\n if (it4 === zeroes) {\n return b256;\n }\n const vch = allocUnsafe(zeroes + (size - it4));\n vch.fill(0, 0, zeroes);\n vch.set(b256.subarray(it4), zeroes);\n return vch;\n };\n};\n//# sourceMappingURL=utils.js.map","// #region base64\nexport const fromBase64 = (str) => {\n return Uint8Array.fromBase64(str, { alphabet: 'base64', lastChunkHandling: 'loose' });\n};\nexport const toBase64 = (bytes) => {\n return bytes.toBase64({ alphabet: 'base64', omitPadding: true });\n};\n// #endregion\n// #region base64pad\nexport const fromBase64Pad = (str) => {\n return Uint8Array.fromBase64(str, { alphabet: 'base64', lastChunkHandling: 'strict' });\n};\nexport const toBase64Pad = (bytes) => {\n return bytes.toBase64({ alphabet: 'base64', omitPadding: false });\n};\n// #endregion\n// #region base64url\nexport const fromBase64Url = (str) => {\n return Uint8Array.fromBase64(str, { alphabet: 'base64url', lastChunkHandling: 'loose' });\n};\nexport const toBase64Url = (bytes) => {\n return bytes.toBase64({ alphabet: 'base64url', omitPadding: true });\n};\n// #endregion\n// #region base64urlpad\nexport const fromBase64UrlPad = (str) => {\n return Uint8Array.fromBase64(str, { alphabet: 'base64url', lastChunkHandling: 'strict' });\n};\nexport const toBase64UrlPad = (bytes) => {\n return bytes.toBase64({ alphabet: 'base64url', omitPadding: false });\n};\n// #endregion\n//# sourceMappingURL=base64-web-native.js.map","import { createRfc4648Decode, createRfc4648Encode } from '../utils.js';\nconst BASE64_CHARSET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\nconst BASE64URL_CHARSET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';\n// #region base64\nexport const fromBase64 = /*#__PURE__*/ createRfc4648Decode(BASE64_CHARSET, 6, false);\nexport const toBase64 = /*#__PURE__*/ createRfc4648Encode(BASE64_CHARSET, 6, false);\n// #endregion\n// #region base64pad\nexport const fromBase64Pad = /*#__PURE__*/ createRfc4648Decode(BASE64_CHARSET, 6, true);\nexport const toBase64Pad = /*#__PURE__*/ createRfc4648Encode(BASE64_CHARSET, 6, true);\n// #endregion\n// #region base64url\nexport const fromBase64Url = /*#__PURE__*/ createRfc4648Decode(BASE64URL_CHARSET, 6, false);\nexport const toBase64Url = /*#__PURE__*/ createRfc4648Encode(BASE64URL_CHARSET, 6, false);\n// #endregion\n// #region base64urlpad\nexport const fromBase64UrlPad = /*#__PURE__*/ createRfc4648Decode(BASE64URL_CHARSET, 6, true);\nexport const toBase64UrlPad = /*#__PURE__*/ createRfc4648Encode(BASE64URL_CHARSET, 6, true);\n// #endregion\n//# sourceMappingURL=base64-web-polyfill.js.map","import { fromBase64 as fromBase64Native, fromBase64Pad as fromBase64PadNative, fromBase64Url as fromBase64UrlNative, fromBase64UrlPad as fromBase64UrlPadNative, toBase64 as toBase64Native, toBase64Pad as toBase64PadNative, toBase64Url as toBase64UrlNative, toBase64UrlPad as toBase64UrlPadNative, } from './base64-web-native.js';\nimport { fromBase64Pad as fromBase64PadPolyfill, fromBase64 as fromBase64Polyfill, fromBase64UrlPad as fromBase64UrlPadPolyfill, fromBase64Url as fromBase64UrlPolyfill, toBase64Pad as toBase64PadPolyfill, toBase64 as toBase64Polyfill, toBase64UrlPad as toBase64UrlPadPolyfill, toBase64Url as toBase64UrlPolyfill, } from './base64-web-polyfill.js';\nconst HAS_NATIVE_SUPPORT = 'fromBase64' in Uint8Array;\n// #region base64\nexport const fromBase64 = !HAS_NATIVE_SUPPORT ? fromBase64Polyfill : fromBase64Native;\nexport const toBase64 = !HAS_NATIVE_SUPPORT ? toBase64Polyfill : toBase64Native;\n// #endregion\n// #region base64pad\nexport const fromBase64Pad = !HAS_NATIVE_SUPPORT ? fromBase64PadPolyfill : fromBase64PadNative;\nexport const toBase64Pad = !HAS_NATIVE_SUPPORT ? toBase64PadPolyfill : toBase64PadNative;\n// #endregion\n// #region base64url\nexport const fromBase64Url = !HAS_NATIVE_SUPPORT ? fromBase64UrlPolyfill : fromBase64UrlNative;\nexport const toBase64Url = !HAS_NATIVE_SUPPORT ? toBase64UrlPolyfill : toBase64UrlNative;\n// #endregion\n// #region base64urlpad\nexport const fromBase64UrlPad = !HAS_NATIVE_SUPPORT ? fromBase64UrlPadPolyfill : fromBase64UrlPadNative;\nexport const toBase64UrlPad = !HAS_NATIVE_SUPPORT ? toBase64UrlPadPolyfill : toBase64UrlPadNative;\n// #endregion\n//# sourceMappingURL=base64-web.js.map","import { nanoid } from 'nanoid';\nimport { toBase64Url } from '@atcute/multibase';\nimport { encodeUtf8, toSha256 } from '@atcute/uint8array';\nexport const locks = typeof navigator !== 'undefined' ? navigator.locks : undefined;\nexport const stringToSha256 = async (input) => {\n const bytes = encodeUtf8(input);\n const digest = await toSha256(bytes);\n return toBase64Url(digest);\n};\nexport const generatePKCE = async () => {\n const verifier = nanoid(64);\n return {\n verifier: verifier,\n challenge: await stringToSha256(verifier),\n method: 'S256',\n };\n};\n//# sourceMappingURL=runtime.js.map","import { locks } from '../utils/runtime.js';\nconst parse = (raw) => {\n if (raw != null) {\n const parsed = JSON.parse(raw);\n if (parsed != null) {\n return parsed;\n }\n }\n return {};\n};\nexport const createOAuthDatabase = ({ name }) => {\n const controller = new AbortController();\n const signal = controller.signal;\n const createStore = (subname, expiresAt, persistUpdatedAt = false) => {\n let store;\n const storageKey = `${name}:${subname}`;\n const persist = () => store && localStorage.setItem(storageKey, JSON.stringify(store));\n const read = () => {\n if (signal.aborted) {\n throw new Error(`store closed`);\n }\n return (store ??= parse(localStorage.getItem(storageKey)));\n };\n {\n const listener = (ev) => {\n if (ev.key === storageKey) {\n store = undefined;\n }\n };\n globalThis.addEventListener('storage', listener, { signal });\n }\n {\n const cleanup = async (lock) => {\n if (!lock || signal.aborted) {\n return;\n }\n await new Promise((resolve) => setTimeout(resolve, 10_000));\n if (signal.aborted) {\n return;\n }\n let now = Date.now();\n let changed = false;\n read();\n for (const key in store) {\n const item = store[key];\n const expiresAt = item.expiresAt;\n if (expiresAt !== null && now > expiresAt) {\n changed = true;\n delete store[key];\n }\n }\n if (changed) {\n persist();\n }\n };\n if (locks) {\n locks.request(`${storageKey}:cleanup`, { ifAvailable: true }, cleanup);\n }\n else {\n cleanup(true);\n }\n }\n return {\n get(key) {\n read();\n const item = store[key];\n if (!item) {\n return;\n }\n const expiresAt = item.expiresAt;\n if (expiresAt !== null && Date.now() > expiresAt) {\n delete store[key];\n persist();\n return;\n }\n return item.value;\n },\n getWithLapsed(key) {\n read();\n const item = store[key];\n const now = Date.now();\n if (!item) {\n return [undefined, Infinity];\n }\n const updatedAt = item.updatedAt;\n if (updatedAt === undefined) {\n return [item.value, Infinity];\n }\n return [item.value, now - updatedAt];\n },\n set(key, value) {\n read();\n const item = {\n value: value,\n expiresAt: expiresAt(value),\n updatedAt: persistUpdatedAt ? Date.now() : undefined,\n };\n store[key] = item;\n persist();\n },\n delete(key) {\n read();\n if (store[key] !== undefined) {\n delete store[key];\n persist();\n }\n },\n keys() {\n read();\n return Object.keys(store);\n },\n };\n };\n return {\n dispose: () => {\n controller.abort();\n },\n sessions: createStore('sessions', ({ token }) => {\n if (token.refresh) {\n return null;\n }\n return token.expires_at ?? null;\n }),\n states: createStore('states', (_item) => Date.now() + 10 * 60 * 1_000), // 10 minutes\n // The reference PDS have nonces that expire after 3 minutes, while other\n // implementations can have varying expiration times.\n // Stored for 24 hours.\n dpopNonces: createStore('dpopNonces', (_item) => Date.now() + 24 * 60 * 60 * 1_000, true),\n inflightDpop: new Map(),\n };\n};\n//# sourceMappingURL=db.js.map","import { createOAuthDatabase } from './store/db.js';\nexport let CLIENT_ID;\nexport let REDIRECT_URI;\nexport let database;\nexport const configureOAuth = (options) => {\n ({ client_id: CLIENT_ID, redirect_uri: REDIRECT_URI } = options.metadata);\n database = createOAuthDatabase({ name: options.storageName ?? 'atcute-oauth' });\n};\n//# sourceMappingURL=environment.js.map","export class LoginError extends Error {\n name = 'LoginError';\n}\nexport class AuthorizationError extends Error {\n name = 'AuthorizationError';\n}\nexport class ResolverError extends Error {\n name = 'ResolverError';\n}\nexport class TokenRefreshError extends Error {\n sub;\n name = 'TokenRefreshError';\n constructor(sub, message, options) {\n super(message, options);\n this.sub = sub;\n }\n}\nexport class OAuthResponseError extends Error {\n response;\n data;\n name = 'OAuthResponseError';\n error;\n description;\n constructor(response, data) {\n const error = ifString(ifObject(data)?.['error']);\n const errorDescription = ifString(ifObject(data)?.['error_description']);\n const messageError = error ? `\"${error}\"` : 'unknown';\n const messageDesc = errorDescription ? `: ${errorDescription}` : '';\n const message = `OAuth ${messageError} error${messageDesc}`;\n super(message);\n this.response = response;\n this.data = data;\n this.error = error;\n this.description = errorDescription;\n }\n get status() {\n return this.response.status;\n }\n get headers() {\n return this.response.headers;\n }\n}\nexport class FetchResponseError extends Error {\n response;\n status;\n name = 'FetchResponseError';\n constructor(response, status, message) {\n super(message);\n this.response = response;\n this.status = status;\n }\n}\nconst ifString = (v) => {\n return typeof v === 'string' ? v : undefined;\n};\nconst ifObject = (v) => {\n return typeof v === 'object' && v !== null && !Array.isArray(v) ? v : undefined;\n};\n//# sourceMappingURL=errors.js.map","const DID_RE = /^did:([a-z]+):([a-zA-Z0-9._:%\\-]*[a-zA-Z0-9._\\-])$/;\n// #__NO_SIDE_EFFECTS__\nexport const isDid = (input) => {\n return typeof input === 'string' && input.length >= 7 && input.length <= 2048 && DID_RE.test(input);\n};\n//# sourceMappingURL=did.js.map","import { isHandle } from '@atcute/lexicons/syntax';\nimport * as t from './types.js';\nconst isUrlParseSupported = 'parse' in URL;\nexport const isAtprotoServiceEndpoint = (input) => {\n let url = null;\n if (isUrlParseSupported) {\n url = URL.parse(input);\n }\n else {\n try {\n url = new URL(input);\n }\n catch { }\n }\n return (url !== null &&\n (url.protocol === 'https:' || url.protocol === 'http:') &&\n url.pathname === '/' &&\n url.search === '' &&\n url.hash === '');\n};\nexport const getVerificationMaterial = (doc, id) => {\n const verificationMethods = doc.verificationMethod;\n if (!verificationMethods) {\n return;\n }\n const expectedId = `${doc.id}${id}`;\n for (let idx = 0, len = verificationMethods.length; idx < len; idx++) {\n const { id, type, publicKeyMultibase } = verificationMethods[idx];\n if (id !== expectedId) {\n continue;\n }\n if (publicKeyMultibase === undefined) {\n continue;\n }\n return { type, publicKeyMultibase };\n }\n};\nexport const getAtprotoVerificationMaterial = (doc) => {\n return getVerificationMaterial(doc, '#atproto');\n};\nexport const getAtprotoLabelerVerificationMaterial = (doc) => {\n return getVerificationMaterial(doc, '#atproto_label');\n};\nexport const getAtprotoHandle = (doc) => {\n const alsoKnownAs = doc.alsoKnownAs;\n if (!alsoKnownAs) {\n return null;\n }\n const PREFIX = 'at://';\n for (let idx = 0, len = alsoKnownAs.length; idx < len; idx++) {\n const aka = alsoKnownAs[idx];\n if (!aka.startsWith(PREFIX)) {\n continue;\n }\n const raw = aka.slice(PREFIX.length);\n if (!isHandle(raw)) {\n return undefined;\n }\n return raw;\n }\n return null;\n};\nexport const getAtprotoServiceEndpoint = (doc, predicate) => {\n const services = doc.service;\n if (!services) {\n return;\n }\n for (let idx = 0, len = services.length; idx < len; idx++) {\n const { id, type, serviceEndpoint } = services[idx];\n if (id !== predicate.id && id !== doc.id + predicate.id) {\n continue;\n }\n if (predicate.type !== undefined) {\n if (Array.isArray(type)) {\n if (!type.includes(predicate.type)) {\n continue;\n }\n }\n else {\n if (type !== predicate.type) {\n continue;\n }\n }\n }\n if (typeof serviceEndpoint !== 'string' || !isAtprotoServiceEndpoint(serviceEndpoint)) {\n continue;\n }\n return serviceEndpoint;\n }\n};\nexport const getPdsEndpoint = (doc) => {\n return getAtprotoServiceEndpoint(doc, {\n id: '#atproto_pds',\n type: 'AtprotoPersonalDataServer',\n });\n};\nexport const getLabelerEndpoint = (doc) => {\n return getAtprotoServiceEndpoint(doc, {\n id: '#atproto_labeler',\n type: 'AtprotoLabeler',\n });\n};\nexport const getBlueskyChatEndpoint = (doc) => {\n return getAtprotoServiceEndpoint(doc, {\n id: '#bsky_chat',\n type: 'BskyChatService',\n });\n};\nexport const getBlueskyFeedgenEndpoint = (doc) => {\n return getAtprotoServiceEndpoint(doc, {\n id: '#bsky_fg',\n type: 'BskyFeedGenerator',\n });\n};\nexport const getBlueskyNotificationEndpoint = (doc) => {\n return getAtprotoServiceEndpoint(doc, {\n id: '#bsky_notif',\n type: 'BskyNotificationService',\n });\n};\n//# sourceMappingURL=utils.js.map","export const DEFAULT_APPVIEW_URL = 'https://public.api.bsky.app';\n//# sourceMappingURL=constants.js.map","export const extractContentType = (headers) => {\n return headers.get('content-type')?.split(';')[0];\n};\n//# sourceMappingURL=response.js.map","const isUrlParseSupported = 'parse' in URL;\nexport const isValidUrl = (urlString) => {\n let url = null;\n if (isUrlParseSupported) {\n url = URL.parse(urlString);\n }\n else {\n try {\n url = new URL(urlString);\n }\n catch { }\n }\n if (url !== null) {\n return url.protocol === 'https:' || url.protocol === 'http:';\n }\n return false;\n};\n//# sourceMappingURL=strings.js.map","import { getPdsEndpoint } from '@atcute/identity';\nimport { isDid } from '@atcute/lexicons/syntax';\nimport { DEFAULT_APPVIEW_URL } from './constants.js';\nimport { ResolverError } from './errors.js';\nimport { extractContentType } from './utils/response.js';\nimport { isValidUrl } from './utils/strings.js';\nconst DID_WEB_RE = /^([a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*(?:\\.[a-zA-Z]{2,}))$/;\n/**\n * Resolves domain handles into DID identifiers, by requesting Bluesky's AppView\n * for identity resolution.\n * @param handle Domain handle to resolve\n * @returns DID identifier resolved from the domain handle\n */\nexport const resolveHandle = async (handle) => {\n const url = DEFAULT_APPVIEW_URL + `/xrpc/com.atproto.identity.resolveHandle` + `?handle=${handle}`;\n const response = await fetch(url);\n if (response.status === 400) {\n throw new ResolverError(`domain handle not found`);\n }\n else if (!response.ok) {\n throw new ResolverError(`directory is unreachable`);\n }\n const json = (await response.json());\n return json.did;\n};\n/**\n * Get DID documents of did:plc (via plc.directory) and did:web identifiers\n * @param did DID identifier we're seeking DID doc from\n * @returns Retrieved DID document\n */\nexport const getDidDocument = async (did) => {\n const colon_index = did.indexOf(':', 4);\n const type = did.slice(4, colon_index);\n const ident = did.slice(colon_index + 1);\n // 2. retrieve their DID documents\n let doc;\n if (type === 'plc') {\n const response = await fetch(`https://plc.directory/${did}`);\n if (response.status === 404) {\n throw new ResolverError(`did not found in directory`);\n }\n else if (!response.ok) {\n throw new ResolverError(`directory is unreachable`);\n }\n const json = await response.json();\n doc = json;\n }\n else if (type === 'web') {\n if (!DID_WEB_RE.test(ident)) {\n throw new ResolverError(`invalid identifier`);\n }\n const response = await fetch(`https://${ident}/.well-known/did.json`);\n if (!response.ok) {\n throw new ResolverError(`did document is unreachable`);\n }\n const json = await response.json();\n doc = json;\n }\n else {\n throw new ResolverError(`unsupported did method`);\n }\n return doc;\n};\n/**\n * Get OAuth protected resource metadata from a host\n * @param host URL of the host\n * @returns Retrieved protected resource metadata\n */\nexport const getProtectedResourceMetadata = async (host) => {\n const url = new URL(`/.well-known/oauth-protected-resource`, host);\n const response = await fetch(url, {\n redirect: 'manual',\n headers: {\n accept: 'application/json',\n },\n });\n if (response.status !== 200 || extractContentType(response.headers) !== 'application/json') {\n throw new ResolverError(`unexpected response`);\n }\n const metadata = (await response.json());\n if (metadata.resource !== url.origin) {\n throw new ResolverError(`unexpected issuer`);\n }\n return metadata;\n};\n/**\n * Get OAuth authorization server metadata from a host\n * @param host URL of the host\n * @returns Retrieved authorization server metadata\n */\nexport const getAuthorizationServerMetadata = async (host) => {\n const url = new URL(`/.well-known/oauth-authorization-server`, host);\n const response = await fetch(url, {\n redirect: 'manual',\n headers: {\n accept: 'application/json',\n },\n });\n if (response.status !== 200 || extractContentType(response.headers) !== 'application/json') {\n throw new ResolverError(`unexpected response`);\n }\n const metadata = (await response.json());\n if (metadata.issuer !== url.origin) {\n throw new ResolverError(`unexpected issuer`);\n }\n if (!isValidUrl(metadata.authorization_endpoint)) {\n throw new ResolverError(`authorization server provided incorrect authorization endpoint`);\n }\n if (!metadata.client_id_metadata_document_supported) {\n throw new ResolverError(`authorization server does not support 'client_id_metadata_document'`);\n }\n if (!metadata.pushed_authorization_request_endpoint) {\n throw new ResolverError(`authorization server does not support 'pushed_authorization request'`);\n }\n if (metadata.response_types_supported) {\n if (!metadata.response_types_supported.includes('code')) {\n throw new ResolverError(`authorization server does not support 'code' response type`);\n }\n }\n return metadata;\n};\n/**\n * Resolve handle domains or DID identifiers to get their PDS and its authorization server metadata\n * @param ident Handle domain or DID identifier to resolve\n * @returns Resolved PDS and authorization server metadata\n */\nexport const resolveFromIdentity = async (ident) => {\n let did;\n if (isDid(ident)) {\n did = ident;\n }\n else {\n const resolved = await resolveHandle(ident);\n did = resolved;\n }\n const doc = await getDidDocument(did);\n const pds = getPdsEndpoint(doc);\n if (!pds) {\n throw new ResolverError(`missing pds endpoint`);\n }\n return {\n identity: {\n id: did,\n raw: ident,\n pds: new URL(pds),\n },\n metadata: await getMetadataFromResourceServer(pds),\n };\n};\n/**\n * Request authorization server metadata from a PDS\n * @param host URL of the host\n * @returns Resolved authorization server metadata\n */\nexport const resolveFromService = async (host) => {\n try {\n const metadata = await getMetadataFromResourceServer(host);\n return { metadata };\n }\n catch (err) {\n if (err instanceof ResolverError) {\n try {\n const metadata = await getAuthorizationServerMetadata(host);\n return { metadata };\n }\n catch { }\n }\n throw err;\n }\n};\n/**\n * Request authorization server metadata from its protected resource metadata\n * @param input URL of the host whose authorization server is delegated\n * @returns Resolved authorization server metadata\n */\nexport const getMetadataFromResourceServer = async (input) => {\n const rs_metadata = await getProtectedResourceMetadata(input);\n if (rs_metadata.authorization_servers?.length !== 1) {\n throw new ResolverError(`expected exactly one authorization server in the listing`);\n }\n const issuer = rs_metadata.authorization_servers[0];\n const as_metadata = await getAuthorizationServerMetadata(issuer);\n if (as_metadata.protected_resources) {\n if (!as_metadata.protected_resources.includes(rs_metadata.resource)) {\n throw new ResolverError(`server is not in authorization server's jurisdiction`);\n }\n }\n return as_metadata;\n};\n//# sourceMappingURL=resolvers.js.map","import { fromBase64Url, toBase64Url } from '@atcute/multibase';\nimport { encodeUtf8 } from '@atcute/uint8array';\nimport { nanoid } from 'nanoid';\nimport { database } from './environment.js';\nimport { extractContentType } from './utils/response.js';\nimport { stringToSha256 } from './utils/runtime.js';\nconst ES256_ALG = { name: 'ECDSA', namedCurve: 'P-256' };\nexport const createES256Key = async () => {\n const pair = await crypto.subtle.generateKey(ES256_ALG, true, ['sign', 'verify']);\n const key = await crypto.subtle.exportKey('pkcs8', pair.privateKey);\n const { ext: _ext, key_ops: _key_opts, ...jwk } = await crypto.subtle.exportKey('jwk', pair.publicKey);\n return {\n typ: 'ES256',\n key: toBase64Url(new Uint8Array(key)),\n jwt: toBase64Url(encodeUtf8(JSON.stringify({ typ: 'dpop+jwt', alg: 'ES256', jwk: jwk }))),\n };\n};\nexport const createDPoPSignage = (dpopKey) => {\n const headerString = dpopKey.jwt;\n const keyPromise = crypto.subtle.importKey('pkcs8', fromBase64Url(dpopKey.key), ES256_ALG, true, ['sign']);\n const constructPayload = (htm, htu, nonce, ath) => {\n const payload = {\n ath: ath,\n htm: htm,\n htu: htu,\n iat: Math.floor(Date.now() / 1_000),\n jti: nanoid(24),\n nonce: nonce,\n };\n return toBase64Url(encodeUtf8(JSON.stringify(payload)));\n };\n return async (method, htu, nonce, ath) => {\n const payloadString = constructPayload(method, htu, nonce, ath);\n const signed = await crypto.subtle.sign({ name: 'ECDSA', hash: { name: 'SHA-256' } }, await keyPromise, encodeUtf8(headerString + '.' + payloadString));\n const signatureString = toBase64Url(new Uint8Array(signed));\n return headerString + '.' + payloadString + '.' + signatureString;\n };\n};\nexport const createDPoPFetch = (dpopKey, isAuthServer) => {\n const nonces = database.dpopNonces;\n const pending = database.inflightDpop;\n const sign = createDPoPSignage(dpopKey);\n return async (input, init) => {\n const request = new Request(input, init);\n const authorizationHeader = request.headers.get('authorization');\n const ath = authorizationHeader?.startsWith('DPoP ')\n ? await stringToSha256(authorizationHeader.slice(5))\n : undefined;\n const { method, url } = request;\n const { origin, pathname } = new URL(url);\n const htu = origin + pathname;\n // See if we have a pending promise for this origin, we'll await before\n // proceeding with this request, next comment describes what the promise\n // is meant to be.\n let deferred = pending.get(origin);\n if (deferred) {\n await deferred.promise;\n deferred = undefined;\n }\n // Get our persisted nonce value for this origin\n let initNonce;\n let expiredOrMissing = false;\n try {\n const [nonce, lapsed] = nonces.getWithLapsed(origin);\n initNonce = nonce;\n // The problem with DPoP nonces is that we don't have insight as to when\n // they'll expire, either we have a nonce value or we don't.\n //\n // Which is very unfortunate, if the client makes multiple requests at the\n // same time, there's a chance that all of them will fail due to the nonce\n // value having expired.\n //\n // To make this less painful, if it's been over 3 minutes since we last\n // had a nonce value, or we never had one to begin with, we'll let this\n // request through and defer everyone else until we get a possibly fresh\n // nonce value.\n //\n // 3 minutes being the DPoP nonce expiration time set by the reference PDS\n // implementation.\n expiredOrMissing = lapsed > 3 * 60 * 1_000;\n }\n catch {\n // Ignore read errors, we'll just act like we're missing a nonce.\n }\n if (expiredOrMissing) {\n // Defer everyone else until this request finishes.\n pending.set(origin, (deferred = Promise.withResolvers()));\n }\n let nextNonce;\n try {\n const initProof = await sign(method, htu, initNonce, ath);\n request.headers.set('dpop', initProof);\n const initResponse = await fetch(request);\n nextNonce = initResponse.headers.get('dpop-nonce');\n if (nextNonce === null || nextNonce === initNonce) {\n // No nonce was returned or it is the same as the one we sent. No need to\n // update the nonce store, or retry the request.\n return initResponse;\n }\n // Store the fresh nonce for future requests\n try {\n nonces.set(origin, nextNonce);\n }\n catch {\n // Ignore write errors\n }\n const shouldRetry = await isUseDpopNonceError(initResponse, isAuthServer);\n if (!shouldRetry) {\n // Not a \"use_dpop_nonce\" error, so there is no need to retry\n return initResponse;\n }\n if (input === request || init?.body instanceof ReadableStream) {\n // If the input stream was already consumed, we cannot retry the request. A\n // solution would be to clone() the request but that would bufferize the\n // entire stream in memory which can lead to memory starvation. Instead, we\n // will return the original response and let the calling code handle retries.\n return initResponse;\n }\n }\n finally {\n // Now everyone can have their turn.\n if (deferred) {\n pending.delete(origin);\n deferred.resolve();\n }\n }\n // We got here because we were asked to retry the request (due to missing\n // nonce value in the first request), let's do just that.\n {\n const nextProof = await sign(method, htu, nextNonce, ath);\n const nextRequest = new Request(input, init);\n nextRequest.headers.set('dpop', nextProof);\n const retryResponse = await fetch(nextRequest);\n // Check if the server returned another new nonce in the retry response\n const retryNonce = retryResponse.headers.get('dpop-nonce');\n if (retryNonce !== null && retryNonce !== nextNonce) {\n try {\n nonces.set(origin, retryNonce);\n }\n catch {\n // Ignore write errors\n }\n }\n return retryResponse;\n }\n };\n};\nconst isUseDpopNonceError = async (response, isAuthServer) => {\n // https://datatracker.ietf.org/doc/html/rfc6750#section-3\n // https://datatracker.ietf.org/doc/html/rfc9449#name-resource-server-provided-no\n if (isAuthServer === undefined || isAuthServer === false) {\n if (response.status === 401) {\n const wwwAuth = response.headers.get('www-authenticate');\n if (wwwAuth?.startsWith('DPoP')) {\n return wwwAuth.includes('error=\"use_dpop_nonce\"');\n }\n }\n }\n // https://datatracker.ietf.org/doc/html/rfc9449#name-authorization-server-provid\n if (isAuthServer === undefined || isAuthServer === true) {\n if (response.status === 400 && extractContentType(response.headers) === 'application/json') {\n try {\n const json = await response.clone().json();\n return typeof json === 'object' && json?.['error'] === 'use_dpop_nonce';\n }\n catch {\n // Response too big (to be \"use_dpop_nonce\" error) or invalid JSON\n return false;\n }\n }\n }\n return false;\n};\n//# sourceMappingURL=dpop.js.map","export const pick = (obj, keys) => {\n const cloned = {};\n for (let idx = 0, len = keys.length; idx < len; idx++) {\n const key = keys[idx];\n // @ts-expect-error\n cloned[key] = obj[key];\n }\n return cloned;\n};\n//# sourceMappingURL=misc.js.map","import { createDPoPFetch } from '../dpop.js';\nimport { CLIENT_ID, REDIRECT_URI } from '../environment.js';\nimport { FetchResponseError, OAuthResponseError, TokenRefreshError } from '../errors.js';\nimport { resolveFromIdentity } from '../resolvers.js';\nimport { pick } from '../utils/misc.js';\nimport { extractContentType } from '../utils/response.js';\nexport class OAuthServerAgent {\n #fetch;\n #metadata;\n constructor(metadata, dpopKey) {\n this.#metadata = metadata;\n this.#fetch = createDPoPFetch(dpopKey, true);\n }\n async request(endpoint, payload) {\n const url = this.#metadata[`${endpoint}_endpoint`];\n if (!url) {\n throw new Error(`no endpoint for ${endpoint}`);\n }\n const response = await this.#fetch(url, {\n method: 'post',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ ...payload, client_id: CLIENT_ID }),\n });\n if (extractContentType(response.headers) !== 'application/json') {\n throw new FetchResponseError(response, 2, `unexpected content-type`);\n }\n const json = await response.json();\n if (response.ok) {\n return json;\n }\n else {\n throw new OAuthResponseError(response, json);\n }\n }\n async revoke(token) {\n try {\n await this.request('revocation', { token: token });\n }\n catch { }\n }\n async exchangeCode(code, verifier) {\n const response = await this.request('token', {\n grant_type: 'authorization_code',\n redirect_uri: REDIRECT_URI,\n code: code,\n code_verifier: verifier,\n });\n try {\n return await this.#processExchangeResponse(response);\n }\n catch (err) {\n await this.revoke(response.access_token);\n throw err;\n }\n }\n async refresh({ sub, token }) {\n if (!token.refresh) {\n throw new TokenRefreshError(sub, 'no refresh token available');\n }\n const response = await this.request('token', {\n grant_type: 'refresh_token',\n refresh_token: token.refresh,\n });\n try {\n if (sub !== response.sub) {\n throw new TokenRefreshError(sub, `sub mismatch in token response; got ${response.sub}`);\n }\n return this.#processTokenResponse(response);\n }\n catch (err) {\n await this.revoke(response.access_token);\n throw err;\n }\n }\n #processTokenResponse(res) {\n if (!res.sub) {\n throw new TypeError(`missing sub field in token response`);\n }\n if (!res.scope) {\n throw new TypeError(`missing scope field in token response`);\n }\n if (res.token_type !== 'DPoP') {\n throw new TypeError(`token response returned a non-dpop token`);\n }\n return {\n scope: res.scope,\n refresh: res.refresh_token,\n access: res.access_token,\n type: res.token_type,\n expires_at: typeof res.expires_in === 'number' ? Date.now() + res.expires_in * 1_000 : undefined,\n };\n }\n async #processExchangeResponse(res) {\n const sub = res.sub;\n if (!sub) {\n throw new TypeError(`missing sub field in token response`);\n }\n const token = this.#processTokenResponse(res);\n const resolved = await resolveFromIdentity(sub);\n if (resolved.metadata.issuer !== this.#metadata.issuer) {\n throw new TypeError(`issuer mismatch; got ${resolved.metadata.issuer}`);\n }\n return {\n token: token,\n info: {\n sub: sub,\n aud: resolved.identity.pds.href,\n server: pick(resolved.metadata, [\n 'issuer',\n 'authorization_endpoint',\n 'introspection_endpoint',\n 'pushed_authorization_request_endpoint',\n 'revocation_endpoint',\n 'token_endpoint',\n ]),\n },\n };\n }\n}\n//# sourceMappingURL=server-agent.js.map","import { database } from '../environment.js';\nimport { OAuthResponseError, TokenRefreshError } from '../errors.js';\nimport { locks } from '../utils/runtime.js';\nimport { OAuthServerAgent } from './server-agent.js';\nconst pending = new Map();\nexport const getSession = async (sub, options) => {\n options?.signal?.throwIfAborted();\n let allowStored = isTokenUsable;\n if (options?.noCache) {\n allowStored = returnFalse;\n }\n else if (options?.allowStale) {\n allowStored = returnTrue;\n }\n // As long as concurrent requests are made for the same key, only one\n // request will be made to the cache & getter function at a time. This works\n // because there is no async operation between the while() loop and the\n // pending.set() call. Because of the \"single threaded\" nature of\n // JavaScript, the pending item will be set before the next iteration of the\n // while loop.\n let previousExecutionFlow;\n while ((previousExecutionFlow = pending.get(sub))) {\n try {\n const { isFresh, value } = await previousExecutionFlow;\n if (isFresh || allowStored(value)) {\n return value;\n }\n }\n catch {\n // Ignore errors from previous execution flows (they will have been\n // propagated by that flow).\n }\n options?.signal?.throwIfAborted();\n }\n const run = async () => {\n const storedSession = database.sessions.get(sub);\n if (storedSession && allowStored(storedSession)) {\n // Use the stored value as return value for the current execution\n // flow. Notify other concurrent execution flows (that should be\n // \"stuck\" in the loop before until this promise resolves) that we got\n // a value, but that it came from the store (isFresh = false).\n return { isFresh: false, value: storedSession };\n }\n const newSession = await refreshToken(sub, storedSession);\n await storeSession(sub, newSession);\n return { isFresh: true, value: newSession };\n };\n let promise;\n if (locks) {\n promise = locks.request(`atcute-oauth:${sub}`, run);\n }\n else {\n promise = run();\n }\n promise = promise.finally(() => pending.delete(sub));\n if (pending.has(sub)) {\n // This should never happen. Indeed, there must not be any 'await'\n // statement between this and the loop iteration check meaning that\n // this.pending.get returned undefined. It is there to catch bugs that\n // would occur in future changes to the code.\n throw new Error('concurrent request for the same key');\n }\n pending.set(sub, promise);\n const { value } = await promise;\n return value;\n};\nexport const storeSession = async (sub, newSession) => {\n try {\n database.sessions.set(sub, newSession);\n }\n catch (err) {\n await onRefreshError(newSession);\n throw err;\n }\n};\nexport const deleteStoredSession = (sub) => {\n database.sessions.delete(sub);\n};\nexport const listStoredSessions = () => {\n return database.sessions.keys();\n};\nconst returnTrue = () => true;\nconst returnFalse = () => false;\nconst refreshToken = async (sub, storedSession) => {\n if (storedSession === undefined) {\n throw new TokenRefreshError(sub, `session deleted by another tab`);\n }\n const { dpopKey, info, token } = storedSession;\n const server = new OAuthServerAgent(info.server, dpopKey);\n try {\n const newToken = await server.refresh({ sub: info.sub, token });\n return { dpopKey, info, token: newToken };\n }\n catch (cause) {\n if (cause instanceof OAuthResponseError && cause.status === 400 && cause.error === 'invalid_grant') {\n throw new TokenRefreshError(sub, `session was revoked`, { cause });\n }\n throw cause;\n }\n};\nconst onRefreshError = async ({ dpopKey, info, token }) => {\n // If the token data cannot be stored, let's revoke it\n const server = new OAuthServerAgent(info.server, dpopKey);\n await server.revoke(token.refresh ?? token.access);\n};\nconst isTokenUsable = ({ token }) => {\n const expires = token.expires_at;\n return expires == null || Date.now() + 60_000 <= expires;\n};\n//# sourceMappingURL=sessions.js.map","import { nanoid } from 'nanoid';\nimport { createES256Key } from '../dpop.js';\nimport { CLIENT_ID, database, REDIRECT_URI } from '../environment.js';\nimport { AuthorizationError, LoginError } from '../errors.js';\nimport { generatePKCE } from '../utils/runtime.js';\nimport { OAuthServerAgent } from './server-agent.js';\nimport { storeSession } from './sessions.js';\n/**\n * Create authentication URL for authorization\n * @param options\n * @returns URL to redirect the user for authorization\n */\nexport const createAuthorizationUrl = async ({ metadata, identity, scope, }) => {\n const state = nanoid(24);\n const pkce = await generatePKCE();\n const dpopKey = await createES256Key();\n const params = {\n redirect_uri: REDIRECT_URI,\n code_challenge: pkce.challenge,\n code_challenge_method: pkce.method,\n state: state,\n login_hint: identity?.raw,\n response_mode: 'fragment',\n response_type: 'code',\n display: 'page',\n // id_token_hint: undefined,\n // max_age: undefined,\n // prompt: undefined,\n scope: scope,\n // ui_locales: undefined,\n };\n database.states.set(state, {\n dpopKey: dpopKey,\n metadata: metadata,\n verifier: pkce.verifier,\n });\n const server = new OAuthServerAgent(metadata, dpopKey);\n const response = await server.request('pushed_authorization_request', params);\n const authUrl = new URL(metadata.authorization_endpoint);\n authUrl.searchParams.set('client_id', CLIENT_ID);\n authUrl.searchParams.set('request_uri', response.request_uri);\n return authUrl;\n};\n/**\n * Finalize authorization\n * @param params Search params\n * @returns Session object, which you can use to instantiate user agents\n */\nexport const finalizeAuthorization = async (params) => {\n const issuer = params.get('iss');\n const state = params.get('state');\n const code = params.get('code');\n const error = params.get('error');\n if (!state || !(code || error)) {\n throw new LoginError(`missing parameters`);\n }\n const stored = database.states.get(state);\n if (stored) {\n // Delete now that we've caught it\n database.states.delete(state);\n }\n else {\n throw new LoginError(`unknown state provided`);\n }\n const dpopKey = stored.dpopKey;\n const metadata = stored.metadata;\n if (error) {\n throw new AuthorizationError(params.get('error_description') || error);\n }\n if (!code) {\n throw new LoginError(`missing code parameter`);\n }\n if (issuer === null) {\n throw new LoginError(`missing issuer parameter`);\n }\n else if (issuer !== metadata.issuer) {\n throw new LoginError(`issuer mismatch`);\n }\n // Retrieve authentication tokens\n const server = new OAuthServerAgent(metadata, dpopKey);\n const { info, token } = await server.exchangeCode(code, stored.verifier);\n // We're finished!\n const sub = info.sub;\n const session = { dpopKey, info, token };\n await storeSession(sub, session);\n return session;\n};\n//# sourceMappingURL=exchange.js.map","import { createDPoPFetch } from '../dpop.js';\nimport { OAuthServerAgent } from './server-agent.js';\nimport { deleteStoredSession, getSession } from './sessions.js';\nexport class OAuthUserAgent {\n session;\n #fetch;\n #getSessionPromise;\n constructor(session) {\n this.session = session;\n this.#fetch = createDPoPFetch(session.dpopKey, false);\n }\n get sub() {\n return this.session.info.sub;\n }\n getSession(options) {\n const promise = getSession(this.session.info.sub, options);\n promise\n .then((session) => {\n this.session = session;\n })\n .finally(() => {\n this.#getSessionPromise = undefined;\n });\n return (this.#getSessionPromise = promise);\n }\n async signOut() {\n const sub = this.session.info.sub;\n try {\n const { dpopKey, info, token } = await getSession(sub, { allowStale: true });\n const server = new OAuthServerAgent(info.server, dpopKey);\n await server.revoke(token.refresh ?? token.access);\n }\n finally {\n deleteStoredSession(sub);\n }\n }\n async handle(pathname, init) {\n await this.#getSessionPromise;\n const headers = new Headers(init?.headers);\n let session = this.session;\n let url = new URL(pathname, session.info.aud);\n headers.set('authorization', `${session.token.type} ${session.token.access}`);\n let response = await this.#fetch(url, { ...init, headers });\n if (!isInvalidTokenResponse(response)) {\n return response;\n }\n try {\n if (this.#getSessionPromise) {\n session = await this.#getSessionPromise;\n }\n else {\n session = await this.getSession();\n }\n }\n catch {\n return response;\n }\n // Stream already consumed, can't retry.\n if (init?.body instanceof ReadableStream) {\n return response;\n }\n url = new URL(pathname, session.info.aud);\n headers.set('authorization', `${session.token.type} ${session.token.access}`);\n return await this.#fetch(url, { ...init, headers });\n }\n}\nconst isInvalidTokenResponse = (response) => {\n if (response.status !== 401) {\n return false;\n }\n const auth = response.headers.get('www-authenticate');\n return (auth != null &&\n (auth.startsWith('Bearer ') || auth.startsWith('DPoP ')) &&\n auth.includes('error=\"invalid_token\"'));\n};\n//# sourceMappingURL=user-agent.js.map","// Storage adapter that mimics browser.storage.local API but uses localStorage\n// This allows sharing code between extension and via-client\n\nexport const storage = {\n local: {\n async get(keys?: string | string[] | Record<string, any>): Promise<Record<string, any>> {\n if (!keys) {\n // Get all items\n const result: Record<string, any> = {};\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key) {\n try {\n result[key] = JSON.parse(localStorage.getItem(key) || 'null');\n } catch {\n result[key] = localStorage.getItem(key);\n }\n }\n }\n return result;\n }\n\n const result: Record<string, any> = {};\n \n if (typeof keys === 'string') {\n // Single key\n try {\n const value = localStorage.getItem(keys);\n result[keys] = value ? JSON.parse(value) : null;\n } catch {\n result[keys] = localStorage.getItem(keys);\n }\n } else if (Array.isArray(keys)) {\n // Array of keys\n keys.forEach(key => {\n try {\n const value = localStorage.getItem(key);\n result[key] = value ? JSON.parse(value) : null;\n } catch {\n result[key] = localStorage.getItem(key);\n }\n });\n } else {\n // Object with default values\n Object.keys(keys).forEach(key => {\n try {\n const value = localStorage.getItem(key);\n result[key] = value ? JSON.parse(value) : keys[key];\n } catch {\n result[key] = localStorage.getItem(key) || keys[key];\n }\n });\n }\n \n return result;\n },\n\n async set(items: Record<string, any>): Promise<void> {\n Object.entries(items).forEach(([key, value]) => {\n localStorage.setItem(key, JSON.stringify(value));\n });\n },\n\n async remove(keys: string | string[]): Promise<void> {\n const keysArray = Array.isArray(keys) ? keys : [keys];\n keysArray.forEach(key => localStorage.removeItem(key));\n },\n\n async clear(): Promise<void> {\n localStorage.clear();\n },\n },\n};\n","// Web-compatible OAuth implementation (for via-client)\n// Adapted from lib/oauth.ts to work without browser.* APIs\n\nimport {\n configureOAuth,\n createAuthorizationUrl,\n finalizeAuthorization,\n resolveFromIdentity,\n OAuthUserAgent,\n type OAuthSession,\n} from \"@atcute/oauth-browser-client\";\nimport { storage } from \"./storage-adapter\";\n\nconst OAUTH_SESSION_KEY = \"synthesis-oauth:session\";\n\nlet isOAuthInitialized = false;\n\nexport function initializeOAuth() {\n if (typeof window !== \"undefined\" && !isOAuthInitialized) {\n // Use web redirect URL for via proxy\n configureOAuth({\n metadata: {\n client_id: import.meta.env.VITE_OAUTH_CLIENT_ID || 'http://localhost:8081/static/client-metadata.json',\n redirect_uri: import.meta.env.VITE_OAUTH_REDIRECT_URI || 'http://localhost:8081/static/oauth-callback.html',\n },\n });\n isOAuthInitialized = true;\n }\n}\n\nexport async function startLoginProcess(handle: string): Promise<void> {\n console.log('[oauth-web] Starting login process for handle:', handle);\n initializeOAuth();\n \n console.log('[oauth-web] Resolving identity...');\n const { metadata } = await resolveFromIdentity(handle);\n console.log('[oauth-web] PDS metadata:', metadata);\n \n console.log('[oauth-web] Creating authorization URL...');\n const authUrl = await createAuthorizationUrl({\n metadata: metadata,\n scope: import.meta.env.VITE_OAUTH_SCOPE || 'atproto transition:generic',\n });\n console.log('[oauth-web] Auth URL:', authUrl.toString());\n\n // For web context, redirect to auth URL\n window.location.href = authUrl.toString();\n}\n\nexport async function handleOAuthCallback(): Promise<OAuthSession | null> {\n console.log('[oauth-web] Handling OAuth callback');\n \n // Parse OAuth response from URL (params can be in search or hash)\n const url = new URL(window.location.href);\n const paramString = url.search || url.hash.slice(1);\n const params = new URLSearchParams(paramString);\n \n console.log('[oauth-web] OAuth params:', Object.fromEntries(params));\n\n if (!params.has('code') && !params.has('error')) {\n console.log('[oauth-web] No OAuth params found');\n return null;\n }\n\n if (params.has('error')) {\n const error = params.get('error');\n const errorDesc = params.get('error_description');\n console.error('[oauth-web] OAuth error:', error, errorDesc);\n throw new Error(`OAuth error: ${error} - ${errorDesc}`);\n }\n\n // Finalize authorization with the params\n console.log('[oauth-web] Finalizing authorization...');\n const session = await finalizeAuthorization(params);\n console.log('[oauth-web] Authorization complete, session:', session);\n\n // Store session\n await saveSession(session);\n console.log('[oauth-web] Session saved successfully');\n\n return session;\n}\n\nexport async function saveSession(session: OAuthSession): Promise<void> {\n await storage.local.set({ [OAUTH_SESSION_KEY]: session });\n}\n\nexport async function loadSession(): Promise<OAuthSession | null> {\n const result = await storage.local.get(OAUTH_SESSION_KEY);\n return result[OAUTH_SESSION_KEY] || null;\n}\n\nexport async function clearSession(): Promise<void> {\n await storage.local.remove(OAUTH_SESSION_KEY);\n}\n\nexport async function getProfile(session: OAuthSession): Promise<any> {\n const agent = new OAuthUserAgent(session);\n const response = await agent.handle('/xrpc/app.bsky.actor.getProfile?actor=' + session.info.sub);\n return await response.json();\n}\n"],"names":["urlAlphabet","nanoid","size","id","bytes","scopedUrlAlphabet","textEncoder","subtle","alloc","allocUnsafe","encodeUtf8","str","toSha256","buffer","createRfc4648Encode","alphabet","bitsPerChar","pad","mask","bits","i","createRfc4648Decode","codes","end","written","value","fromBase64Url","toBase64Url","BASE64URL_CHARSET","HAS_NATIVE_SUPPORT","fromBase64UrlNative","fromBase64UrlPolyfill","toBase64UrlNative","toBase64UrlPolyfill","locks","stringToSha256","input","digest","generatePKCE","verifier","parse","raw","parsed","createOAuthDatabase","name","controller","signal","createStore","subname","expiresAt","persistUpdatedAt","store","storageKey","persist","read","listener","ev","cleanup","lock","resolve","now","changed","key","item","updatedAt","token","_item","CLIENT_ID","REDIRECT_URI","database","configureOAuth","options","LoginError","__publicField","AuthorizationError","ResolverError","TokenRefreshError","sub","message","OAuthResponseError","response","data","_a","_b","error","ifString","ifObject","errorDescription","messageError","messageDesc","FetchResponseError","status","v","DID_RE","isDid","isUrlParseSupported","isAtprotoServiceEndpoint","url","getAtprotoServiceEndpoint","doc","predicate","services","idx","len","type","serviceEndpoint","getPdsEndpoint","DEFAULT_APPVIEW_URL","extractContentType","headers","isValidUrl","urlString","DID_WEB_RE","resolveHandle","handle","getDidDocument","did","colon_index","ident","getProtectedResourceMetadata","host","metadata","getAuthorizationServerMetadata","resolveFromIdentity","pds","getMetadataFromResourceServer","rs_metadata","issuer","as_metadata","ES256_ALG","createES256Key","pair","_ext","_key_opts","jwk","createDPoPSignage","dpopKey","headerString","keyPromise","constructPayload","htm","htu","nonce","ath","payload","method","payloadString","signed","signatureString","createDPoPFetch","isAuthServer","nonces","pending","sign","init","request","authorizationHeader","origin","pathname","deferred","initNonce","expiredOrMissing","lapsed","nextNonce","initProof","initResponse","isUseDpopNonceError","nextProof","nextRequest","retryResponse","retryNonce","wwwAuth","json","pick","obj","keys","cloned","_fetch","_metadata","_OAuthServerAgent_instances","processTokenResponse_fn","processExchangeResponse_fn","OAuthServerAgent","__privateAdd","__privateSet","endpoint","__privateGet","code","__privateMethod","err","res","resolved","getSession","allowStored","isTokenUsable","returnFalse","returnTrue","previousExecutionFlow","isFresh","run","storedSession","newSession","refreshToken","storeSession","promise","onRefreshError","deleteStoredSession","info","server","newToken","cause","expires","createAuthorizationUrl","identity","scope","state","pkce","params","authUrl","finalizeAuthorization","stored","session","_getSessionPromise","OAuthUserAgent","isInvalidTokenResponse","auth","storage","result","items","OAUTH_SESSION_KEY","isOAuthInitialized","initializeOAuth","startLoginProcess","handleOAuthCallback","paramString","errorDesc","saveSession","loadSession","clearSession","getProfile"],"mappings":"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","x_google_ignoreList":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22]}
+2
pywb-test/static/assets/oauth-web-D0e6TxJF.js
··· 1 + var ue=Object.defineProperty;var G=t=>{throw TypeError(t)};var de=(t,e,r)=>e in t?ue(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r;var f=(t,e,r)=>de(t,typeof e!="symbol"?e+"":e,r),H=(t,e,r)=>e.has(t)||G("Cannot "+r);var m=(t,e,r)=>(H(t,e,"read from private field"),r?r.call(t):e.get(t)),E=(t,e,r)=>e.has(t)?G("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(t):e.set(t,r),k=(t,e,r,s)=>(H(t,e,"write to private field"),s?s.call(t,r):e.set(t,r),r),L=(t,e,r)=>(H(t,e,"access private method"),r);const he="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";let W=(t=21)=>{let e="",r=crypto.getRandomValues(new Uint8Array(t|=0));for(;t--;)e+=he[r[t]&63];return e};const pe=new TextEncoder;new TextDecoder;const fe=crypto.subtle,we=t=>new Uint8Array(t),ge=we,$=t=>pe.encode(t),ye=async t=>new Uint8Array(await fe.digest("SHA-256",t)),me=(t,e,r)=>s=>{const n=(1<<e)-1;let o="",a=0,i=0;for(let c=0;c<s.length;++c)for(i=i<<8|s[c],a+=8;a>e;)a-=e,o+=t[n&i>>a];if(a!==0&&(o+=t[n&i<<e-a]),r)for(;o.length*e&7;)o+="=";return o},_e=(t,e,r)=>{const s={};for(let n=0;n<t.length;++n)s[t[n]]=n;return n=>{let o=n.length;for(;r&&n[o-1]==="=";)--o;const a=ge(o*e/8|0);let i=0,c=0,l=0;for(let u=0;u<o;++u){const h=s[n[u]];if(h===void 0)throw new SyntaxError("invalid base string");c=c<<e|h,i+=e,i>=8&&(i-=8,a[l++]=255&c>>i)}if(i>=e||255&c<<8-i)throw new SyntaxError("unexpected end of data");return a}},ve=t=>Uint8Array.fromBase64(t,{alphabet:"base64url",lastChunkHandling:"loose"}),Se=t=>t.toBase64({alphabet:"base64url",omitPadding:!0}),te="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",be=_e(te,6,!1),Ae=me(te,6,!1),re="fromBase64"in Uint8Array,Ee=re?ve:be,z=re?Se:Ae,N=typeof navigator<"u"?navigator.locks:void 0,se=async t=>{const e=$(t),r=await ye(e);return z(r)},ke=async()=>{const t=W(64);return{verifier:t,challenge:await se(t),method:"S256"}},Re=t=>{if(t!=null){const e=JSON.parse(t);if(e!=null)return e}return{}},xe=({name:t})=>{const e=new AbortController,r=e.signal,s=(n,o,a=!1)=>{let i;const c=`${t}:${n}`,l=()=>i&&localStorage.setItem(c,JSON.stringify(i)),u=()=>{if(r.aborted)throw new Error("store closed");return i??(i=Re(localStorage.getItem(c)))};{const h=d=>{d.key===c&&(i=void 0)};globalThis.addEventListener("storage",h,{signal:r})}{const h=async d=>{if(!d||r.aborted||(await new Promise(w=>setTimeout(w,1e4)),r.aborted))return;let g=Date.now(),v=!1;u();for(const w in i){const U=i[w].expiresAt;U!==null&&g>U&&(v=!0,delete i[w])}v&&l()};N?N.request(`${c}:cleanup`,{ifAvailable:!0},h):h(!0)}return{get(h){u();const d=i[h];if(!d)return;const g=d.expiresAt;if(g!==null&&Date.now()>g){delete i[h],l();return}return d.value},getWithLapsed(h){u();const d=i[h],g=Date.now();if(!d)return[void 0,1/0];const v=d.updatedAt;return v===void 0?[d.value,1/0]:[d.value,g-v]},set(h,d){u();const g={value:d,expiresAt:o(d),updatedAt:a?Date.now():void 0};i[h]=g,l()},delete(h){u(),i[h]!==void 0&&(delete i[h],l())},keys(){return u(),Object.keys(i)}}};return{dispose:()=>{e.abort()},sessions:s("sessions",({token:n})=>n.refresh?null:n.expires_at??null),states:s("states",n=>Date.now()+10*60*1e3),dpopNonces:s("dpopNonces",n=>Date.now()+24*60*60*1e3,!0),inflightDpop:new Map}};let M,Z,_;const Ue=t=>{({client_id:M,redirect_uri:Z}=t.metadata),_=xe({name:t.storageName??"atcute-oauth"})};class D extends Error{constructor(){super(...arguments);f(this,"name","LoginError")}}class je extends Error{constructor(){super(...arguments);f(this,"name","AuthorizationError")}}class p extends Error{constructor(){super(...arguments);f(this,"name","ResolverError")}}class q extends Error{constructor(r,s,n){super(s,n);f(this,"sub");f(this,"name","TokenRefreshError");this.sub=r}}class ne extends Error{constructor(r,s){var l,u;const n=Y((l=Q(s))==null?void 0:l.error),o=Y((u=Q(s))==null?void 0:u.error_description),a=n?`"${n}"`:"unknown",i=o?`: ${o}`:"",c=`OAuth ${a} error${i}`;super(c);f(this,"response");f(this,"data");f(this,"name","OAuthResponseError");f(this,"error");f(this,"description");this.response=r,this.data=s,this.error=n,this.description=o}get status(){return this.response.status}get headers(){return this.response.headers}}class De extends Error{constructor(r,s,n){super(n);f(this,"response");f(this,"status");f(this,"name","FetchResponseError");this.response=r,this.status=s}}const Y=t=>typeof t=="string"?t:void 0,Q=t=>typeof t=="object"&&t!==null&&!Array.isArray(t)?t:void 0,ze=/^did:([a-z]+):([a-zA-Z0-9._:%\-]*[a-zA-Z0-9._\-])$/,Oe=t=>typeof t=="string"&&t.length>=7&&t.length<=2048&&ze.test(t),Pe="parse"in URL,Ie=t=>{let e=null;if(Pe)e=URL.parse(t);else try{e=new URL(t)}catch{}return e!==null&&(e.protocol==="https:"||e.protocol==="http:")&&e.pathname==="/"&&e.search===""&&e.hash===""},Le=(t,e)=>{const r=t.service;if(r)for(let s=0,n=r.length;s<n;s++){const{id:o,type:a,serviceEndpoint:i}=r[s];if(!(o!==e.id&&o!==t.id+e.id)){if(e.type!==void 0){if(Array.isArray(a)){if(!a.includes(e.type))continue}else if(a!==e.type)continue}if(!(typeof i!="string"||!Ie(i)))return i}}},Te=t=>Le(t,{id:"#atproto_pds",type:"AtprotoPersonalDataServer"}),$e="https://public.api.bsky.app",B=t=>{var e;return(e=t.get("content-type"))==null?void 0:e.split(";")[0]},Ne="parse"in URL,qe=t=>{let e=null;if(Ne)e=URL.parse(t);else try{e=new URL(t)}catch{}return e!==null?e.protocol==="https:"||e.protocol==="http:":!1},Ke=/^([a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*(?:\.[a-zA-Z]{2,}))$/,Be=async t=>{const e=$e+`/xrpc/com.atproto.identity.resolveHandle?handle=${t}`,r=await fetch(e);if(r.status===400)throw new p("domain handle not found");if(!r.ok)throw new p("directory is unreachable");return(await r.json()).did},Fe=async t=>{const e=t.indexOf(":",4),r=t.slice(4,e),s=t.slice(e+1);let n;if(r==="plc"){const o=await fetch(`https://plc.directory/${t}`);if(o.status===404)throw new p("did not found in directory");if(!o.ok)throw new p("directory is unreachable");n=await o.json()}else if(r==="web"){if(!Ke.test(s))throw new p("invalid identifier");const o=await fetch(`https://${s}/.well-known/did.json`);if(!o.ok)throw new p("did document is unreachable");n=await o.json()}else throw new p("unsupported did method");return n},Je=async t=>{const e=new URL("/.well-known/oauth-protected-resource",t),r=await fetch(e,{redirect:"manual",headers:{accept:"application/json"}});if(r.status!==200||B(r.headers)!=="application/json")throw new p("unexpected response");const s=await r.json();if(s.resource!==e.origin)throw new p("unexpected issuer");return s},He=async t=>{const e=new URL("/.well-known/oauth-authorization-server",t),r=await fetch(e,{redirect:"manual",headers:{accept:"application/json"}});if(r.status!==200||B(r.headers)!=="application/json")throw new p("unexpected response");const s=await r.json();if(s.issuer!==e.origin)throw new p("unexpected issuer");if(!qe(s.authorization_endpoint))throw new p("authorization server provided incorrect authorization endpoint");if(!s.client_id_metadata_document_supported)throw new p("authorization server does not support 'client_id_metadata_document'");if(!s.pushed_authorization_request_endpoint)throw new p("authorization server does not support 'pushed_authorization request'");if(s.response_types_supported&&!s.response_types_supported.includes("code"))throw new p("authorization server does not support 'code' response type");return s},oe=async t=>{let e;Oe(t)?e=t:e=await Be(t);const r=await Fe(e),s=Te(r);if(!s)throw new p("missing pds endpoint");return{identity:{id:e,raw:t,pds:new URL(s)},metadata:await Ce(s)}},Ce=async t=>{var n;const e=await Je(t);if(((n=e.authorization_servers)==null?void 0:n.length)!==1)throw new p("expected exactly one authorization server in the listing");const r=e.authorization_servers[0],s=await He(r);if(s.protected_resources&&!s.protected_resources.includes(e.resource))throw new p("server is not in authorization server's jurisdiction");return s},ae={name:"ECDSA",namedCurve:"P-256"},We=async()=>{const t=await crypto.subtle.generateKey(ae,!0,["sign","verify"]),e=await crypto.subtle.exportKey("pkcs8",t.privateKey),{ext:r,key_ops:s,...n}=await crypto.subtle.exportKey("jwk",t.publicKey);return{typ:"ES256",key:z(new Uint8Array(e)),jwt:z($(JSON.stringify({typ:"dpop+jwt",alg:"ES256",jwk:n})))}},Me=t=>{const e=t.jwt,r=crypto.subtle.importKey("pkcs8",Ee(t.key),ae,!0,["sign"]),s=(n,o,a,i)=>{const c={ath:i,htm:n,htu:o,iat:Math.floor(Date.now()/1e3),jti:W(24),nonce:a};return z($(JSON.stringify(c)))};return async(n,o,a,i)=>{const c=s(n,o,a,i),l=await crypto.subtle.sign({name:"ECDSA",hash:{name:"SHA-256"}},await r,$(e+"."+c)),u=z(new Uint8Array(l));return e+"."+c+"."+u}},ie=(t,e)=>{const r=_.dpopNonces,s=_.inflightDpop,n=Me(t);return async(o,a)=>{const i=new Request(o,a),c=i.headers.get("authorization"),l=c!=null&&c.startsWith("DPoP ")?await se(c.slice(5)):void 0,{method:u,url:h}=i,{origin:d,pathname:g}=new URL(h),v=d+g;let w=s.get(d);w&&(await w.promise,w=void 0);let I,U=!1;try{const[j,y]=r.getWithLapsed(d);I=j,U=y>3*60*1e3}catch{}U&&s.set(d,w=Promise.withResolvers());let A;try{const j=await n(u,v,I,l);i.headers.set("dpop",j);const y=await fetch(i);if(A=y.headers.get("dpop-nonce"),A===null||A===I)return y;try{r.set(d,A)}catch{}if(!await Ze(y,e)||o===i||(a==null?void 0:a.body)instanceof ReadableStream)return y}finally{w&&(s.delete(d),w.resolve())}{const j=await n(u,v,A,l),y=new Request(o,a);y.headers.set("dpop",j);const F=await fetch(y),J=F.headers.get("dpop-nonce");if(J!==null&&J!==A)try{r.set(d,J)}catch{}return F}}},Ze=async(t,e)=>{if((e===void 0||e===!1)&&t.status===401){const r=t.headers.get("www-authenticate");if(r!=null&&r.startsWith("DPoP"))return r.includes('error="use_dpop_nonce"')}if((e===void 0||e===!0)&&t.status===400&&B(t.headers)==="application/json")try{const r=await t.clone().json();return typeof r=="object"&&(r==null?void 0:r.error)==="use_dpop_nonce"}catch{return!1}return!1},Ve=(t,e)=>{const r={};for(let s=0,n=e.length;s<n;s++){const o=e[s];r[o]=t[o]}return r};var O,R,b,C,ce;class P{constructor(e,r){E(this,b);E(this,O);E(this,R);k(this,R,e),k(this,O,ie(r,!0))}async request(e,r){const s=m(this,R)[`${e}_endpoint`];if(!s)throw new Error(`no endpoint for ${e}`);const n=await m(this,O).call(this,s,{method:"post",headers:{"content-type":"application/json"},body:JSON.stringify({...r,client_id:M})});if(B(n.headers)!=="application/json")throw new De(n,2,"unexpected content-type");const o=await n.json();if(n.ok)return o;throw new ne(n,o)}async revoke(e){try{await this.request("revocation",{token:e})}catch{}}async exchangeCode(e,r){const s=await this.request("token",{grant_type:"authorization_code",redirect_uri:Z,code:e,code_verifier:r});try{return await L(this,b,ce).call(this,s)}catch(n){throw await this.revoke(s.access_token),n}}async refresh({sub:e,token:r}){if(!r.refresh)throw new q(e,"no refresh token available");const s=await this.request("token",{grant_type:"refresh_token",refresh_token:r.refresh});try{if(e!==s.sub)throw new q(e,`sub mismatch in token response; got ${s.sub}`);return L(this,b,C).call(this,s)}catch(n){throw await this.revoke(s.access_token),n}}}O=new WeakMap,R=new WeakMap,b=new WeakSet,C=function(e){if(!e.sub)throw new TypeError("missing sub field in token response");if(!e.scope)throw new TypeError("missing scope field in token response");if(e.token_type!=="DPoP")throw new TypeError("token response returned a non-dpop token");return{scope:e.scope,refresh:e.refresh_token,access:e.access_token,type:e.token_type,expires_at:typeof e.expires_in=="number"?Date.now()+e.expires_in*1e3:void 0}},ce=async function(e){const r=e.sub;if(!r)throw new TypeError("missing sub field in token response");const s=L(this,b,C).call(this,e),n=await oe(r);if(n.metadata.issuer!==m(this,R).issuer)throw new TypeError(`issuer mismatch; got ${n.metadata.issuer}`);return{token:s,info:{sub:r,aud:n.identity.pds.href,server:Ve(n.metadata,["issuer","authorization_endpoint","introspection_endpoint","pushed_authorization_request_endpoint","revocation_endpoint","token_endpoint"])}}};const T=new Map,X=async(t,e)=>{var i,c;(i=e==null?void 0:e.signal)==null||i.throwIfAborted();let r=tt;e!=null&&e.noCache?r=Qe:e!=null&&e.allowStale&&(r=Ye);let s;for(;s=T.get(t);){try{const{isFresh:l,value:u}=await s;if(l||r(u))return u}catch{}(c=e==null?void 0:e.signal)==null||c.throwIfAborted()}const n=async()=>{const l=_.sessions.get(t);if(l&&r(l))return{isFresh:!1,value:l};const u=await Xe(t,l);return await le(t,u),{isFresh:!0,value:u}};let o;if(N?o=N.request(`atcute-oauth:${t}`,n):o=n(),o=o.finally(()=>T.delete(t)),T.has(t))throw new Error("concurrent request for the same key");T.set(t,o);const{value:a}=await o;return a},le=async(t,e)=>{try{_.sessions.set(t,e)}catch(r){throw await et(e),r}},Ge=t=>{_.sessions.delete(t)},Ye=()=>!0,Qe=()=>!1,Xe=async(t,e)=>{if(e===void 0)throw new q(t,"session deleted by another tab");const{dpopKey:r,info:s,token:n}=e,o=new P(s.server,r);try{const a=await o.refresh({sub:s.sub,token:n});return{dpopKey:r,info:s,token:a}}catch(a){throw a instanceof ne&&a.status===400&&a.error==="invalid_grant"?new q(t,"session was revoked",{cause:a}):a}},et=async({dpopKey:t,info:e,token:r})=>{await new P(e.server,t).revoke(r.refresh??r.access)},tt=({token:t})=>{const e=t.expires_at;return e==null||Date.now()+6e4<=e},rt=async({metadata:t,identity:e,scope:r})=>{const s=W(24),n=await ke(),o=await We(),a={redirect_uri:Z,code_challenge:n.challenge,code_challenge_method:n.method,state:s,login_hint:e==null?void 0:e.raw,response_mode:"fragment",response_type:"code",display:"page",scope:r};_.states.set(s,{dpopKey:o,metadata:t,verifier:n.verifier});const c=await new P(t,o).request("pushed_authorization_request",a),l=new URL(t.authorization_endpoint);return l.searchParams.set("client_id",M),l.searchParams.set("request_uri",c.request_uri),l},st=async t=>{const e=t.get("iss"),r=t.get("state"),s=t.get("code"),n=t.get("error");if(!r||!(s||n))throw new D("missing parameters");const o=_.states.get(r);if(o)_.states.delete(r);else throw new D("unknown state provided");const a=o.dpopKey,i=o.metadata;if(n)throw new je(t.get("error_description")||n);if(!s)throw new D("missing code parameter");if(e===null)throw new D("missing issuer parameter");if(e!==i.issuer)throw new D("issuer mismatch");const c=new P(i,a),{info:l,token:u}=await c.exchangeCode(s,o.verifier),h=l.sub,d={dpopKey:a,info:l,token:u};return await le(h,d),d};var x,S;class nt{constructor(e){f(this,"session");E(this,x);E(this,S);this.session=e,k(this,x,ie(e.dpopKey,!1))}get sub(){return this.session.info.sub}getSession(e){const r=X(this.session.info.sub,e);return r.then(s=>{this.session=s}).finally(()=>{k(this,S,void 0)}),k(this,S,r)}async signOut(){const e=this.session.info.sub;try{const{dpopKey:r,info:s,token:n}=await X(e,{allowStale:!0});await new P(s.server,r).revoke(n.refresh??n.access)}finally{Ge(e)}}async handle(e,r){await m(this,S);const s=new Headers(r==null?void 0:r.headers);let n=this.session,o=new URL(e,n.info.aud);s.set("authorization",`${n.token.type} ${n.token.access}`);let a=await m(this,x).call(this,o,{...r,headers:s});if(!ot(a))return a;try{m(this,S)?n=await m(this,S):n=await this.getSession()}catch{return a}return(r==null?void 0:r.body)instanceof ReadableStream?a:(o=new URL(e,n.info.aud),s.set("authorization",`${n.token.type} ${n.token.access}`),await m(this,x).call(this,o,{...r,headers:s}))}}x=new WeakMap,S=new WeakMap;const ot=t=>{if(t.status!==401)return!1;const e=t.headers.get("www-authenticate");return e!=null&&(e.startsWith("Bearer ")||e.startsWith("DPoP "))&&e.includes('error="invalid_token"')},V={local:{async get(t){if(!t){const r={};for(let s=0;s<localStorage.length;s++){const n=localStorage.key(s);if(n)try{r[n]=JSON.parse(localStorage.getItem(n)||"null")}catch{r[n]=localStorage.getItem(n)}}return r}const e={};if(typeof t=="string")try{const r=localStorage.getItem(t);e[t]=r?JSON.parse(r):null}catch{e[t]=localStorage.getItem(t)}else Array.isArray(t)?t.forEach(r=>{try{const s=localStorage.getItem(r);e[r]=s?JSON.parse(s):null}catch{e[r]=localStorage.getItem(r)}}):Object.keys(t).forEach(r=>{try{const s=localStorage.getItem(r);e[r]=s?JSON.parse(s):t[r]}catch{e[r]=localStorage.getItem(r)||t[r]}});return e},async set(t){Object.entries(t).forEach(([e,r])=>{localStorage.setItem(e,JSON.stringify(r))})},async remove(t){(Array.isArray(t)?t:[t]).forEach(r=>localStorage.removeItem(r))},async clear(){localStorage.clear()}}},K="synthesis-oauth:session";let ee=!1;function at(){typeof window<"u"&&!ee&&(Ue({metadata:{client_id:"http://localhost:8081/static/client-metadata.json",redirect_uri:"http://localhost:8081/static/oauth-callback.html"}}),ee=!0)}async function lt(t){console.log("[oauth-web] Starting login process for handle:",t),at(),console.log("[oauth-web] Resolving identity...");const{metadata:e}=await oe(t);console.log("[oauth-web] PDS metadata:",e),console.log("[oauth-web] Creating authorization URL...");const r=await rt({metadata:e,scope:"atproto transition:generic"});console.log("[oauth-web] Auth URL:",r.toString()),window.location.href=r.toString()}async function ut(){console.log("[oauth-web] Handling OAuth callback");const t=new URL(window.location.href),e=t.search||t.hash.slice(1),r=new URLSearchParams(e);if(console.log("[oauth-web] OAuth params:",Object.fromEntries(r)),!r.has("code")&&!r.has("error"))return console.log("[oauth-web] No OAuth params found"),null;if(r.has("error")){const n=r.get("error"),o=r.get("error_description");throw console.error("[oauth-web] OAuth error:",n,o),new Error(`OAuth error: ${n} - ${o}`)}console.log("[oauth-web] Finalizing authorization...");const s=await st(r);return console.log("[oauth-web] Authorization complete, session:",s),await it(s),console.log("[oauth-web] Session saved successfully"),s}async function it(t){await V.local.set({[K]:t})}async function dt(){return(await V.local.get(K))[K]||null}async function ht(){await V.local.remove(K)}async function pt(t){return await(await new nt(t).handle("/xrpc/app.bsky.actor.getProfile?actor="+t.info.sub)).json()}export{lt as a,ht as c,pt as g,ut as h,at as i,dt as l,V as s}; 2 + //# sourceMappingURL=oauth-web-D0e6TxJF.js.map
+1
pywb-test/static/assets/oauth-web-D0e6TxJF.js.map
··· 1 + {"version":3,"file":"oauth-web-D0e6TxJF.js","sources":["../../../node_modules/.pnpm/nanoid@5.1.6/node_modules/nanoid/url-alphabet/index.js","../../../node_modules/.pnpm/nanoid@5.1.6/node_modules/nanoid/index.browser.js","../../../node_modules/.pnpm/@atcute+uint8array@1.0.5/node_modules/@atcute/uint8array/dist/index.js","../../../node_modules/.pnpm/@atcute+multibase@1.1.6/node_modules/@atcute/multibase/dist/utils.js","../../../node_modules/.pnpm/@atcute+multibase@1.1.6/node_modules/@atcute/multibase/dist/bases/base64-web-native.js","../../../node_modules/.pnpm/@atcute+multibase@1.1.6/node_modules/@atcute/multibase/dist/bases/base64-web-polyfill.js","../../../node_modules/.pnpm/@atcute+multibase@1.1.6/node_modules/@atcute/multibase/dist/bases/base64-web.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/utils/runtime.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/store/db.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/environment.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/errors.js","../../../node_modules/.pnpm/@atcute+lexicons@1.2.2/node_modules/@atcute/lexicons/dist/syntax/did.js","../../../node_modules/.pnpm/@atcute+identity@1.1.1/node_modules/@atcute/identity/dist/utils.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/constants.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/utils/response.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/utils/strings.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/resolvers.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/dpop.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/utils/misc.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/agents/server-agent.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/agents/sessions.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/agents/exchange.js","../../../node_modules/.pnpm/@atcute+oauth-browser-client@1.0.27/node_modules/@atcute/oauth-browser-client/dist/agents/user-agent.js","../../../lib/storage-adapter.ts","../../../lib/oauth-web.ts"],"sourcesContent":["export const urlAlphabet =\n 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict'\n","/* @ts-self-types=\"./index.d.ts\" */\nimport { urlAlphabet as scopedUrlAlphabet } from './url-alphabet/index.js'\nexport { urlAlphabet } from './url-alphabet/index.js'\nexport let random = bytes => crypto.getRandomValues(new Uint8Array(bytes))\nexport let customRandom = (alphabet, defaultSize, getRandom) => {\n let mask = (2 << Math.log2(alphabet.length - 1)) - 1\n let step = -~((1.6 * mask * defaultSize) / alphabet.length)\n return (size = defaultSize) => {\n let id = ''\n while (true) {\n let bytes = getRandom(step)\n let j = step | 0\n while (j--) {\n id += alphabet[bytes[j] & mask] || ''\n if (id.length >= size) return id\n }\n }\n }\n}\nexport let customAlphabet = (alphabet, size = 21) =>\n customRandom(alphabet, size | 0, random)\nexport let nanoid = (size = 21) => {\n let id = ''\n let bytes = crypto.getRandomValues(new Uint8Array((size |= 0)))\n while (size--) {\n id += scopedUrlAlphabet[bytes[size] & 63]\n }\n return id\n}\n","const textEncoder = new TextEncoder();\nconst textDecoder = new TextDecoder();\nconst subtle = crypto.subtle;\n/**\n * creates an Uint8Array of the requested size, with the contents zeroed\n */\nexport const alloc = (size) => {\n return new Uint8Array(size);\n};\n/**\n * creates an Uint8Array of the requested size, where the contents may not be\n * zeroed out. only use if you're certain that the contents will be overwritten\n */\nexport const allocUnsafe = alloc;\n/**\n * compares two Uint8Array buffers\n */\nexport const compare = (a, b) => {\n const alen = a.length;\n const blen = b.length;\n if (alen > blen) {\n return 1;\n }\n if (alen < blen) {\n return -1;\n }\n for (let i = 0; i < alen; i++) {\n const ax = a[i];\n const bx = b[i];\n if (ax < bx) {\n return -1;\n }\n if (ax > bx) {\n return 1;\n }\n }\n return 0;\n};\n/**\n * checks if the two Uint8Array buffers are equal\n */\nexport const equals = (a, b) => {\n if (a === b) {\n return true;\n }\n let len;\n if ((len = a.length) === b.length) {\n while (len--) {\n if (a[len] !== b[len]) {\n return false;\n }\n }\n }\n return len === -1;\n};\n/**\n * checks if the two Uint8Array buffers are equal, timing-safe version\n */\nexport const timingSafeEquals = (a, b) => {\n let len;\n let out = 0;\n if ((len = a.length) === b.length) {\n while (len--) {\n out |= a[len] ^ b[len];\n }\n }\n return len === -1 && out === 0;\n};\n/**\n * concatenates multiple Uint8Array buffers into one\n */\nexport const concat = (arrays, size) => {\n let written = 0;\n let len = arrays.length;\n let idx;\n if (size === undefined) {\n for (idx = size = 0; idx < len; idx++) {\n const chunk = arrays[idx];\n size += chunk.length;\n }\n }\n const buffer = new Uint8Array(size);\n for (idx = 0; idx < len; idx++) {\n const chunk = arrays[idx];\n buffer.set(chunk, written);\n written += chunk.length;\n }\n return buffer;\n};\n/**\n * encodes a UTF-8 string\n */\nexport const encodeUtf8 = (str) => {\n return textEncoder.encode(str);\n};\n/**\n * encodes a UTF-8 string into a given buffer\n */\nexport const encodeUtf8Into = (to, str, offset, length) => {\n let buffer;\n if (offset === undefined) {\n buffer = to;\n }\n else if (length === undefined) {\n buffer = to.subarray(offset);\n }\n else {\n buffer = to.subarray(offset, offset + length);\n }\n const result = textEncoder.encodeInto(str, buffer);\n return result.written;\n};\nconst fromCharCode = String.fromCharCode;\n/**\n * decodes a UTF-8 string from a given buffer\n */\nexport const decodeUtf8From = (from, offset, length) => {\n let buffer;\n if (offset === undefined) {\n buffer = from;\n }\n else if (length === undefined) {\n buffer = from.subarray(offset);\n }\n else {\n buffer = from.subarray(offset, offset + length);\n }\n const end = buffer.length;\n if (end > 24) {\n return textDecoder.decode(buffer);\n }\n {\n let str = '';\n let idx = 0;\n for (; idx + 3 < end; idx += 4) {\n const a = buffer[idx];\n const b = buffer[idx + 1];\n const c = buffer[idx + 2];\n const d = buffer[idx + 3];\n if ((a | b | c | d) & 0x80) {\n return str + textDecoder.decode(buffer.subarray(idx));\n }\n str += fromCharCode(a, b, c, d);\n }\n for (; idx < end; idx++) {\n const x = buffer[idx];\n if (x & 0x80) {\n return str + textDecoder.decode(buffer.subarray(idx));\n }\n str += fromCharCode(x);\n }\n return str;\n }\n};\n/**\n * get a SHA-256 digest of this buffer\n */\nexport const toSha256 = async (buffer) => {\n return new Uint8Array(await subtle.digest('SHA-256', buffer));\n};\n//# sourceMappingURL=index.js.map","import { alloc, allocUnsafe } from '@atcute/uint8array';\nexport const createRfc4648Encode = (alphabet, bitsPerChar, pad) => {\n return (bytes) => {\n const mask = (1 << bitsPerChar) - 1;\n let str = '';\n let bits = 0; // Number of bits currently in the buffer\n let buffer = 0; // Bits waiting to be written out, MSB first\n for (let i = 0; i < bytes.length; ++i) {\n // Slurp data into the buffer:\n buffer = (buffer << 8) | bytes[i];\n bits += 8;\n // Write out as much as we can:\n while (bits > bitsPerChar) {\n bits -= bitsPerChar;\n str += alphabet[mask & (buffer >> bits)];\n }\n }\n // Partial character:\n if (bits !== 0) {\n str += alphabet[mask & (buffer << (bitsPerChar - bits))];\n }\n // Add padding characters until we hit a byte boundary:\n if (pad) {\n while (((str.length * bitsPerChar) & 7) !== 0) {\n str += '=';\n }\n }\n return str;\n };\n};\nexport const createRfc4648Decode = (alphabet, bitsPerChar, pad) => {\n // Build the character lookup table:\n const codes = {};\n for (let i = 0; i < alphabet.length; ++i) {\n codes[alphabet[i]] = i;\n }\n return (str) => {\n // Count the padding bytes:\n let end = str.length;\n while (pad && str[end - 1] === '=') {\n --end;\n }\n // Allocate the output:\n const bytes = allocUnsafe(((end * bitsPerChar) / 8) | 0);\n // Parse the data:\n let bits = 0; // Number of bits currently in the buffer\n let buffer = 0; // Bits waiting to be written out, MSB first\n let written = 0; // Next byte to write\n for (let i = 0; i < end; ++i) {\n // Read one character from the string:\n const value = codes[str[i]];\n if (value === undefined) {\n throw new SyntaxError(`invalid base string`);\n }\n // Append the bits to the buffer:\n buffer = (buffer << bitsPerChar) | value;\n bits += bitsPerChar;\n // Write out some bits if the buffer has a byte's worth:\n if (bits >= 8) {\n bits -= 8;\n bytes[written++] = 0xff & (buffer >> bits);\n }\n }\n // Verify that we have received just enough bits:\n if (bits >= bitsPerChar || (0xff & (buffer << (8 - bits))) !== 0) {\n throw new SyntaxError('unexpected end of data');\n }\n return bytes;\n };\n};\nexport const createBtcBaseEncode = (alphabet) => {\n if (alphabet.length >= 255) {\n throw new RangeError(`alphabet too long`);\n }\n const BASE = alphabet.length;\n const LEADER = alphabet.charAt(0);\n const iFACTOR = Math.log(256) / Math.log(BASE); // log(256) / log(BASE), rounded up\n return (source) => {\n if (source.length === 0) {\n return '';\n }\n // Skip & count leading zeroes.\n let zeroes = 0;\n let length = 0;\n let pbegin = 0;\n const pend = source.length;\n while (pbegin !== pend && source[pbegin] === 0) {\n pbegin++;\n zeroes++;\n }\n // Allocate enough space in big-endian base58 representation.\n const size = ((pend - pbegin) * iFACTOR + 1) >>> 0;\n const b58 = alloc(size);\n // Process the bytes.\n while (pbegin !== pend) {\n let carry = source[pbegin];\n // Apply \"b58 = b58 * 256 + ch\".\n let i = 0;\n for (let it1 = size - 1; (carry !== 0 || i < length) && it1 !== -1; it1--, i++) {\n carry += (256 * b58[it1]) >>> 0;\n b58[it1] = carry % BASE >>> 0;\n carry = (carry / BASE) >>> 0;\n }\n if (carry !== 0) {\n throw new Error('non-zero carry');\n }\n length = i;\n pbegin++;\n }\n // Skip leading zeroes in base58 result.\n let it2 = size - length;\n while (it2 !== size && b58[it2] === 0) {\n it2++;\n }\n // Translate the result into a string.\n let str = LEADER.repeat(zeroes);\n for (; it2 < size; ++it2) {\n str += alphabet.charAt(b58[it2]);\n }\n return str;\n };\n};\nexport const createBtcBaseDecode = (alphabet) => {\n if (alphabet.length >= 255) {\n throw new RangeError(`alphabet too long`);\n }\n const BASE_MAP = allocUnsafe(256).fill(255);\n for (let i = 0; i < alphabet.length; i++) {\n const xc = alphabet.charCodeAt(i);\n if (BASE_MAP[xc] !== 255) {\n throw new RangeError(`${alphabet[i]} is ambiguous`);\n }\n BASE_MAP[xc] = i;\n }\n const BASE = alphabet.length;\n const LEADER = alphabet.charAt(0);\n const FACTOR = Math.log(BASE) / Math.log(256); // log(BASE) / log(256), rounded up\n return (source) => {\n if (source.length === 0) {\n return allocUnsafe(0);\n }\n // Skip and count leading '1's.\n let psz = 0;\n let zeroes = 0;\n let length = 0;\n while (source[psz] === LEADER) {\n zeroes++;\n psz++;\n }\n // Allocate enough space in big-endian base256 representation.\n const size = ((source.length - psz) * FACTOR + 1) >>> 0; // log(58) / log(256), rounded up.\n const b256 = alloc(size);\n // Process the characters.\n while (psz < source.length) {\n // Decode character\n let carry = BASE_MAP[source.charCodeAt(psz)];\n // Invalid character\n if (carry === 255) {\n throw new Error(`invalid string`);\n }\n let i = 0;\n for (let it3 = size - 1; (carry !== 0 || i < length) && it3 !== -1; it3--, i++) {\n carry += (BASE * b256[it3]) >>> 0;\n b256[it3] = carry % 256 >>> 0;\n carry = (carry / 256) >>> 0;\n }\n if (carry !== 0) {\n throw new Error('non-zero carry');\n }\n length = i;\n psz++;\n }\n // Skip leading zeroes in b256.\n let it4 = size - length;\n while (it4 !== size && b256[it4] === 0) {\n it4++;\n }\n if (it4 === zeroes) {\n return b256;\n }\n const vch = allocUnsafe(zeroes + (size - it4));\n vch.fill(0, 0, zeroes);\n vch.set(b256.subarray(it4), zeroes);\n return vch;\n };\n};\n//# sourceMappingURL=utils.js.map","// #region base64\nexport const fromBase64 = (str) => {\n return Uint8Array.fromBase64(str, { alphabet: 'base64', lastChunkHandling: 'loose' });\n};\nexport const toBase64 = (bytes) => {\n return bytes.toBase64({ alphabet: 'base64', omitPadding: true });\n};\n// #endregion\n// #region base64pad\nexport const fromBase64Pad = (str) => {\n return Uint8Array.fromBase64(str, { alphabet: 'base64', lastChunkHandling: 'strict' });\n};\nexport const toBase64Pad = (bytes) => {\n return bytes.toBase64({ alphabet: 'base64', omitPadding: false });\n};\n// #endregion\n// #region base64url\nexport const fromBase64Url = (str) => {\n return Uint8Array.fromBase64(str, { alphabet: 'base64url', lastChunkHandling: 'loose' });\n};\nexport const toBase64Url = (bytes) => {\n return bytes.toBase64({ alphabet: 'base64url', omitPadding: true });\n};\n// #endregion\n// #region base64urlpad\nexport const fromBase64UrlPad = (str) => {\n return Uint8Array.fromBase64(str, { alphabet: 'base64url', lastChunkHandling: 'strict' });\n};\nexport const toBase64UrlPad = (bytes) => {\n return bytes.toBase64({ alphabet: 'base64url', omitPadding: false });\n};\n// #endregion\n//# sourceMappingURL=base64-web-native.js.map","import { createRfc4648Decode, createRfc4648Encode } from '../utils.js';\nconst BASE64_CHARSET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\nconst BASE64URL_CHARSET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';\n// #region base64\nexport const fromBase64 = /*#__PURE__*/ createRfc4648Decode(BASE64_CHARSET, 6, false);\nexport const toBase64 = /*#__PURE__*/ createRfc4648Encode(BASE64_CHARSET, 6, false);\n// #endregion\n// #region base64pad\nexport const fromBase64Pad = /*#__PURE__*/ createRfc4648Decode(BASE64_CHARSET, 6, true);\nexport const toBase64Pad = /*#__PURE__*/ createRfc4648Encode(BASE64_CHARSET, 6, true);\n// #endregion\n// #region base64url\nexport const fromBase64Url = /*#__PURE__*/ createRfc4648Decode(BASE64URL_CHARSET, 6, false);\nexport const toBase64Url = /*#__PURE__*/ createRfc4648Encode(BASE64URL_CHARSET, 6, false);\n// #endregion\n// #region base64urlpad\nexport const fromBase64UrlPad = /*#__PURE__*/ createRfc4648Decode(BASE64URL_CHARSET, 6, true);\nexport const toBase64UrlPad = /*#__PURE__*/ createRfc4648Encode(BASE64URL_CHARSET, 6, true);\n// #endregion\n//# sourceMappingURL=base64-web-polyfill.js.map","import { fromBase64 as fromBase64Native, fromBase64Pad as fromBase64PadNative, fromBase64Url as fromBase64UrlNative, fromBase64UrlPad as fromBase64UrlPadNative, toBase64 as toBase64Native, toBase64Pad as toBase64PadNative, toBase64Url as toBase64UrlNative, toBase64UrlPad as toBase64UrlPadNative, } from './base64-web-native.js';\nimport { fromBase64Pad as fromBase64PadPolyfill, fromBase64 as fromBase64Polyfill, fromBase64UrlPad as fromBase64UrlPadPolyfill, fromBase64Url as fromBase64UrlPolyfill, toBase64Pad as toBase64PadPolyfill, toBase64 as toBase64Polyfill, toBase64UrlPad as toBase64UrlPadPolyfill, toBase64Url as toBase64UrlPolyfill, } from './base64-web-polyfill.js';\nconst HAS_NATIVE_SUPPORT = 'fromBase64' in Uint8Array;\n// #region base64\nexport const fromBase64 = !HAS_NATIVE_SUPPORT ? fromBase64Polyfill : fromBase64Native;\nexport const toBase64 = !HAS_NATIVE_SUPPORT ? toBase64Polyfill : toBase64Native;\n// #endregion\n// #region base64pad\nexport const fromBase64Pad = !HAS_NATIVE_SUPPORT ? fromBase64PadPolyfill : fromBase64PadNative;\nexport const toBase64Pad = !HAS_NATIVE_SUPPORT ? toBase64PadPolyfill : toBase64PadNative;\n// #endregion\n// #region base64url\nexport const fromBase64Url = !HAS_NATIVE_SUPPORT ? fromBase64UrlPolyfill : fromBase64UrlNative;\nexport const toBase64Url = !HAS_NATIVE_SUPPORT ? toBase64UrlPolyfill : toBase64UrlNative;\n// #endregion\n// #region base64urlpad\nexport const fromBase64UrlPad = !HAS_NATIVE_SUPPORT ? fromBase64UrlPadPolyfill : fromBase64UrlPadNative;\nexport const toBase64UrlPad = !HAS_NATIVE_SUPPORT ? toBase64UrlPadPolyfill : toBase64UrlPadNative;\n// #endregion\n//# sourceMappingURL=base64-web.js.map","import { nanoid } from 'nanoid';\nimport { toBase64Url } from '@atcute/multibase';\nimport { encodeUtf8, toSha256 } from '@atcute/uint8array';\nexport const locks = typeof navigator !== 'undefined' ? navigator.locks : undefined;\nexport const stringToSha256 = async (input) => {\n const bytes = encodeUtf8(input);\n const digest = await toSha256(bytes);\n return toBase64Url(digest);\n};\nexport const generatePKCE = async () => {\n const verifier = nanoid(64);\n return {\n verifier: verifier,\n challenge: await stringToSha256(verifier),\n method: 'S256',\n };\n};\n//# sourceMappingURL=runtime.js.map","import { locks } from '../utils/runtime.js';\nconst parse = (raw) => {\n if (raw != null) {\n const parsed = JSON.parse(raw);\n if (parsed != null) {\n return parsed;\n }\n }\n return {};\n};\nexport const createOAuthDatabase = ({ name }) => {\n const controller = new AbortController();\n const signal = controller.signal;\n const createStore = (subname, expiresAt, persistUpdatedAt = false) => {\n let store;\n const storageKey = `${name}:${subname}`;\n const persist = () => store && localStorage.setItem(storageKey, JSON.stringify(store));\n const read = () => {\n if (signal.aborted) {\n throw new Error(`store closed`);\n }\n return (store ??= parse(localStorage.getItem(storageKey)));\n };\n {\n const listener = (ev) => {\n if (ev.key === storageKey) {\n store = undefined;\n }\n };\n globalThis.addEventListener('storage', listener, { signal });\n }\n {\n const cleanup = async (lock) => {\n if (!lock || signal.aborted) {\n return;\n }\n await new Promise((resolve) => setTimeout(resolve, 10_000));\n if (signal.aborted) {\n return;\n }\n let now = Date.now();\n let changed = false;\n read();\n for (const key in store) {\n const item = store[key];\n const expiresAt = item.expiresAt;\n if (expiresAt !== null && now > expiresAt) {\n changed = true;\n delete store[key];\n }\n }\n if (changed) {\n persist();\n }\n };\n if (locks) {\n locks.request(`${storageKey}:cleanup`, { ifAvailable: true }, cleanup);\n }\n else {\n cleanup(true);\n }\n }\n return {\n get(key) {\n read();\n const item = store[key];\n if (!item) {\n return;\n }\n const expiresAt = item.expiresAt;\n if (expiresAt !== null && Date.now() > expiresAt) {\n delete store[key];\n persist();\n return;\n }\n return item.value;\n },\n getWithLapsed(key) {\n read();\n const item = store[key];\n const now = Date.now();\n if (!item) {\n return [undefined, Infinity];\n }\n const updatedAt = item.updatedAt;\n if (updatedAt === undefined) {\n return [item.value, Infinity];\n }\n return [item.value, now - updatedAt];\n },\n set(key, value) {\n read();\n const item = {\n value: value,\n expiresAt: expiresAt(value),\n updatedAt: persistUpdatedAt ? Date.now() : undefined,\n };\n store[key] = item;\n persist();\n },\n delete(key) {\n read();\n if (store[key] !== undefined) {\n delete store[key];\n persist();\n }\n },\n keys() {\n read();\n return Object.keys(store);\n },\n };\n };\n return {\n dispose: () => {\n controller.abort();\n },\n sessions: createStore('sessions', ({ token }) => {\n if (token.refresh) {\n return null;\n }\n return token.expires_at ?? null;\n }),\n states: createStore('states', (_item) => Date.now() + 10 * 60 * 1_000), // 10 minutes\n // The reference PDS have nonces that expire after 3 minutes, while other\n // implementations can have varying expiration times.\n // Stored for 24 hours.\n dpopNonces: createStore('dpopNonces', (_item) => Date.now() + 24 * 60 * 60 * 1_000, true),\n inflightDpop: new Map(),\n };\n};\n//# sourceMappingURL=db.js.map","import { createOAuthDatabase } from './store/db.js';\nexport let CLIENT_ID;\nexport let REDIRECT_URI;\nexport let database;\nexport const configureOAuth = (options) => {\n ({ client_id: CLIENT_ID, redirect_uri: REDIRECT_URI } = options.metadata);\n database = createOAuthDatabase({ name: options.storageName ?? 'atcute-oauth' });\n};\n//# sourceMappingURL=environment.js.map","export class LoginError extends Error {\n name = 'LoginError';\n}\nexport class AuthorizationError extends Error {\n name = 'AuthorizationError';\n}\nexport class ResolverError extends Error {\n name = 'ResolverError';\n}\nexport class TokenRefreshError extends Error {\n sub;\n name = 'TokenRefreshError';\n constructor(sub, message, options) {\n super(message, options);\n this.sub = sub;\n }\n}\nexport class OAuthResponseError extends Error {\n response;\n data;\n name = 'OAuthResponseError';\n error;\n description;\n constructor(response, data) {\n const error = ifString(ifObject(data)?.['error']);\n const errorDescription = ifString(ifObject(data)?.['error_description']);\n const messageError = error ? `\"${error}\"` : 'unknown';\n const messageDesc = errorDescription ? `: ${errorDescription}` : '';\n const message = `OAuth ${messageError} error${messageDesc}`;\n super(message);\n this.response = response;\n this.data = data;\n this.error = error;\n this.description = errorDescription;\n }\n get status() {\n return this.response.status;\n }\n get headers() {\n return this.response.headers;\n }\n}\nexport class FetchResponseError extends Error {\n response;\n status;\n name = 'FetchResponseError';\n constructor(response, status, message) {\n super(message);\n this.response = response;\n this.status = status;\n }\n}\nconst ifString = (v) => {\n return typeof v === 'string' ? v : undefined;\n};\nconst ifObject = (v) => {\n return typeof v === 'object' && v !== null && !Array.isArray(v) ? v : undefined;\n};\n//# sourceMappingURL=errors.js.map","const DID_RE = /^did:([a-z]+):([a-zA-Z0-9._:%\\-]*[a-zA-Z0-9._\\-])$/;\n// #__NO_SIDE_EFFECTS__\nexport const isDid = (input) => {\n return typeof input === 'string' && input.length >= 7 && input.length <= 2048 && DID_RE.test(input);\n};\n//# sourceMappingURL=did.js.map","import { isHandle } from '@atcute/lexicons/syntax';\nimport * as t from './types.js';\nconst isUrlParseSupported = 'parse' in URL;\nexport const isAtprotoServiceEndpoint = (input) => {\n let url = null;\n if (isUrlParseSupported) {\n url = URL.parse(input);\n }\n else {\n try {\n url = new URL(input);\n }\n catch { }\n }\n return (url !== null &&\n (url.protocol === 'https:' || url.protocol === 'http:') &&\n url.pathname === '/' &&\n url.search === '' &&\n url.hash === '');\n};\nexport const getVerificationMaterial = (doc, id) => {\n const verificationMethods = doc.verificationMethod;\n if (!verificationMethods) {\n return;\n }\n const expectedId = `${doc.id}${id}`;\n for (let idx = 0, len = verificationMethods.length; idx < len; idx++) {\n const { id, type, publicKeyMultibase } = verificationMethods[idx];\n if (id !== expectedId) {\n continue;\n }\n if (publicKeyMultibase === undefined) {\n continue;\n }\n return { type, publicKeyMultibase };\n }\n};\nexport const getAtprotoVerificationMaterial = (doc) => {\n return getVerificationMaterial(doc, '#atproto');\n};\nexport const getAtprotoLabelerVerificationMaterial = (doc) => {\n return getVerificationMaterial(doc, '#atproto_label');\n};\nexport const getAtprotoHandle = (doc) => {\n const alsoKnownAs = doc.alsoKnownAs;\n if (!alsoKnownAs) {\n return null;\n }\n const PREFIX = 'at://';\n for (let idx = 0, len = alsoKnownAs.length; idx < len; idx++) {\n const aka = alsoKnownAs[idx];\n if (!aka.startsWith(PREFIX)) {\n continue;\n }\n const raw = aka.slice(PREFIX.length);\n if (!isHandle(raw)) {\n return undefined;\n }\n return raw;\n }\n return null;\n};\nexport const getAtprotoServiceEndpoint = (doc, predicate) => {\n const services = doc.service;\n if (!services) {\n return;\n }\n for (let idx = 0, len = services.length; idx < len; idx++) {\n const { id, type, serviceEndpoint } = services[idx];\n if (id !== predicate.id && id !== doc.id + predicate.id) {\n continue;\n }\n if (predicate.type !== undefined) {\n if (Array.isArray(type)) {\n if (!type.includes(predicate.type)) {\n continue;\n }\n }\n else {\n if (type !== predicate.type) {\n continue;\n }\n }\n }\n if (typeof serviceEndpoint !== 'string' || !isAtprotoServiceEndpoint(serviceEndpoint)) {\n continue;\n }\n return serviceEndpoint;\n }\n};\nexport const getPdsEndpoint = (doc) => {\n return getAtprotoServiceEndpoint(doc, {\n id: '#atproto_pds',\n type: 'AtprotoPersonalDataServer',\n });\n};\nexport const getLabelerEndpoint = (doc) => {\n return getAtprotoServiceEndpoint(doc, {\n id: '#atproto_labeler',\n type: 'AtprotoLabeler',\n });\n};\nexport const getBlueskyChatEndpoint = (doc) => {\n return getAtprotoServiceEndpoint(doc, {\n id: '#bsky_chat',\n type: 'BskyChatService',\n });\n};\nexport const getBlueskyFeedgenEndpoint = (doc) => {\n return getAtprotoServiceEndpoint(doc, {\n id: '#bsky_fg',\n type: 'BskyFeedGenerator',\n });\n};\nexport const getBlueskyNotificationEndpoint = (doc) => {\n return getAtprotoServiceEndpoint(doc, {\n id: '#bsky_notif',\n type: 'BskyNotificationService',\n });\n};\n//# sourceMappingURL=utils.js.map","export const DEFAULT_APPVIEW_URL = 'https://public.api.bsky.app';\n//# sourceMappingURL=constants.js.map","export const extractContentType = (headers) => {\n return headers.get('content-type')?.split(';')[0];\n};\n//# sourceMappingURL=response.js.map","const isUrlParseSupported = 'parse' in URL;\nexport const isValidUrl = (urlString) => {\n let url = null;\n if (isUrlParseSupported) {\n url = URL.parse(urlString);\n }\n else {\n try {\n url = new URL(urlString);\n }\n catch { }\n }\n if (url !== null) {\n return url.protocol === 'https:' || url.protocol === 'http:';\n }\n return false;\n};\n//# sourceMappingURL=strings.js.map","import { getPdsEndpoint } from '@atcute/identity';\nimport { isDid } from '@atcute/lexicons/syntax';\nimport { DEFAULT_APPVIEW_URL } from './constants.js';\nimport { ResolverError } from './errors.js';\nimport { extractContentType } from './utils/response.js';\nimport { isValidUrl } from './utils/strings.js';\nconst DID_WEB_RE = /^([a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*(?:\\.[a-zA-Z]{2,}))$/;\n/**\n * Resolves domain handles into DID identifiers, by requesting Bluesky's AppView\n * for identity resolution.\n * @param handle Domain handle to resolve\n * @returns DID identifier resolved from the domain handle\n */\nexport const resolveHandle = async (handle) => {\n const url = DEFAULT_APPVIEW_URL + `/xrpc/com.atproto.identity.resolveHandle` + `?handle=${handle}`;\n const response = await fetch(url);\n if (response.status === 400) {\n throw new ResolverError(`domain handle not found`);\n }\n else if (!response.ok) {\n throw new ResolverError(`directory is unreachable`);\n }\n const json = (await response.json());\n return json.did;\n};\n/**\n * Get DID documents of did:plc (via plc.directory) and did:web identifiers\n * @param did DID identifier we're seeking DID doc from\n * @returns Retrieved DID document\n */\nexport const getDidDocument = async (did) => {\n const colon_index = did.indexOf(':', 4);\n const type = did.slice(4, colon_index);\n const ident = did.slice(colon_index + 1);\n // 2. retrieve their DID documents\n let doc;\n if (type === 'plc') {\n const response = await fetch(`https://plc.directory/${did}`);\n if (response.status === 404) {\n throw new ResolverError(`did not found in directory`);\n }\n else if (!response.ok) {\n throw new ResolverError(`directory is unreachable`);\n }\n const json = await response.json();\n doc = json;\n }\n else if (type === 'web') {\n if (!DID_WEB_RE.test(ident)) {\n throw new ResolverError(`invalid identifier`);\n }\n const response = await fetch(`https://${ident}/.well-known/did.json`);\n if (!response.ok) {\n throw new ResolverError(`did document is unreachable`);\n }\n const json = await response.json();\n doc = json;\n }\n else {\n throw new ResolverError(`unsupported did method`);\n }\n return doc;\n};\n/**\n * Get OAuth protected resource metadata from a host\n * @param host URL of the host\n * @returns Retrieved protected resource metadata\n */\nexport const getProtectedResourceMetadata = async (host) => {\n const url = new URL(`/.well-known/oauth-protected-resource`, host);\n const response = await fetch(url, {\n redirect: 'manual',\n headers: {\n accept: 'application/json',\n },\n });\n if (response.status !== 200 || extractContentType(response.headers) !== 'application/json') {\n throw new ResolverError(`unexpected response`);\n }\n const metadata = (await response.json());\n if (metadata.resource !== url.origin) {\n throw new ResolverError(`unexpected issuer`);\n }\n return metadata;\n};\n/**\n * Get OAuth authorization server metadata from a host\n * @param host URL of the host\n * @returns Retrieved authorization server metadata\n */\nexport const getAuthorizationServerMetadata = async (host) => {\n const url = new URL(`/.well-known/oauth-authorization-server`, host);\n const response = await fetch(url, {\n redirect: 'manual',\n headers: {\n accept: 'application/json',\n },\n });\n if (response.status !== 200 || extractContentType(response.headers) !== 'application/json') {\n throw new ResolverError(`unexpected response`);\n }\n const metadata = (await response.json());\n if (metadata.issuer !== url.origin) {\n throw new ResolverError(`unexpected issuer`);\n }\n if (!isValidUrl(metadata.authorization_endpoint)) {\n throw new ResolverError(`authorization server provided incorrect authorization endpoint`);\n }\n if (!metadata.client_id_metadata_document_supported) {\n throw new ResolverError(`authorization server does not support 'client_id_metadata_document'`);\n }\n if (!metadata.pushed_authorization_request_endpoint) {\n throw new ResolverError(`authorization server does not support 'pushed_authorization request'`);\n }\n if (metadata.response_types_supported) {\n if (!metadata.response_types_supported.includes('code')) {\n throw new ResolverError(`authorization server does not support 'code' response type`);\n }\n }\n return metadata;\n};\n/**\n * Resolve handle domains or DID identifiers to get their PDS and its authorization server metadata\n * @param ident Handle domain or DID identifier to resolve\n * @returns Resolved PDS and authorization server metadata\n */\nexport const resolveFromIdentity = async (ident) => {\n let did;\n if (isDid(ident)) {\n did = ident;\n }\n else {\n const resolved = await resolveHandle(ident);\n did = resolved;\n }\n const doc = await getDidDocument(did);\n const pds = getPdsEndpoint(doc);\n if (!pds) {\n throw new ResolverError(`missing pds endpoint`);\n }\n return {\n identity: {\n id: did,\n raw: ident,\n pds: new URL(pds),\n },\n metadata: await getMetadataFromResourceServer(pds),\n };\n};\n/**\n * Request authorization server metadata from a PDS\n * @param host URL of the host\n * @returns Resolved authorization server metadata\n */\nexport const resolveFromService = async (host) => {\n try {\n const metadata = await getMetadataFromResourceServer(host);\n return { metadata };\n }\n catch (err) {\n if (err instanceof ResolverError) {\n try {\n const metadata = await getAuthorizationServerMetadata(host);\n return { metadata };\n }\n catch { }\n }\n throw err;\n }\n};\n/**\n * Request authorization server metadata from its protected resource metadata\n * @param input URL of the host whose authorization server is delegated\n * @returns Resolved authorization server metadata\n */\nexport const getMetadataFromResourceServer = async (input) => {\n const rs_metadata = await getProtectedResourceMetadata(input);\n if (rs_metadata.authorization_servers?.length !== 1) {\n throw new ResolverError(`expected exactly one authorization server in the listing`);\n }\n const issuer = rs_metadata.authorization_servers[0];\n const as_metadata = await getAuthorizationServerMetadata(issuer);\n if (as_metadata.protected_resources) {\n if (!as_metadata.protected_resources.includes(rs_metadata.resource)) {\n throw new ResolverError(`server is not in authorization server's jurisdiction`);\n }\n }\n return as_metadata;\n};\n//# sourceMappingURL=resolvers.js.map","import { fromBase64Url, toBase64Url } from '@atcute/multibase';\nimport { encodeUtf8 } from '@atcute/uint8array';\nimport { nanoid } from 'nanoid';\nimport { database } from './environment.js';\nimport { extractContentType } from './utils/response.js';\nimport { stringToSha256 } from './utils/runtime.js';\nconst ES256_ALG = { name: 'ECDSA', namedCurve: 'P-256' };\nexport const createES256Key = async () => {\n const pair = await crypto.subtle.generateKey(ES256_ALG, true, ['sign', 'verify']);\n const key = await crypto.subtle.exportKey('pkcs8', pair.privateKey);\n const { ext: _ext, key_ops: _key_opts, ...jwk } = await crypto.subtle.exportKey('jwk', pair.publicKey);\n return {\n typ: 'ES256',\n key: toBase64Url(new Uint8Array(key)),\n jwt: toBase64Url(encodeUtf8(JSON.stringify({ typ: 'dpop+jwt', alg: 'ES256', jwk: jwk }))),\n };\n};\nexport const createDPoPSignage = (dpopKey) => {\n const headerString = dpopKey.jwt;\n const keyPromise = crypto.subtle.importKey('pkcs8', fromBase64Url(dpopKey.key), ES256_ALG, true, ['sign']);\n const constructPayload = (htm, htu, nonce, ath) => {\n const payload = {\n ath: ath,\n htm: htm,\n htu: htu,\n iat: Math.floor(Date.now() / 1_000),\n jti: nanoid(24),\n nonce: nonce,\n };\n return toBase64Url(encodeUtf8(JSON.stringify(payload)));\n };\n return async (method, htu, nonce, ath) => {\n const payloadString = constructPayload(method, htu, nonce, ath);\n const signed = await crypto.subtle.sign({ name: 'ECDSA', hash: { name: 'SHA-256' } }, await keyPromise, encodeUtf8(headerString + '.' + payloadString));\n const signatureString = toBase64Url(new Uint8Array(signed));\n return headerString + '.' + payloadString + '.' + signatureString;\n };\n};\nexport const createDPoPFetch = (dpopKey, isAuthServer) => {\n const nonces = database.dpopNonces;\n const pending = database.inflightDpop;\n const sign = createDPoPSignage(dpopKey);\n return async (input, init) => {\n const request = new Request(input, init);\n const authorizationHeader = request.headers.get('authorization');\n const ath = authorizationHeader?.startsWith('DPoP ')\n ? await stringToSha256(authorizationHeader.slice(5))\n : undefined;\n const { method, url } = request;\n const { origin, pathname } = new URL(url);\n const htu = origin + pathname;\n // See if we have a pending promise for this origin, we'll await before\n // proceeding with this request, next comment describes what the promise\n // is meant to be.\n let deferred = pending.get(origin);\n if (deferred) {\n await deferred.promise;\n deferred = undefined;\n }\n // Get our persisted nonce value for this origin\n let initNonce;\n let expiredOrMissing = false;\n try {\n const [nonce, lapsed] = nonces.getWithLapsed(origin);\n initNonce = nonce;\n // The problem with DPoP nonces is that we don't have insight as to when\n // they'll expire, either we have a nonce value or we don't.\n //\n // Which is very unfortunate, if the client makes multiple requests at the\n // same time, there's a chance that all of them will fail due to the nonce\n // value having expired.\n //\n // To make this less painful, if it's been over 3 minutes since we last\n // had a nonce value, or we never had one to begin with, we'll let this\n // request through and defer everyone else until we get a possibly fresh\n // nonce value.\n //\n // 3 minutes being the DPoP nonce expiration time set by the reference PDS\n // implementation.\n expiredOrMissing = lapsed > 3 * 60 * 1_000;\n }\n catch {\n // Ignore read errors, we'll just act like we're missing a nonce.\n }\n if (expiredOrMissing) {\n // Defer everyone else until this request finishes.\n pending.set(origin, (deferred = Promise.withResolvers()));\n }\n let nextNonce;\n try {\n const initProof = await sign(method, htu, initNonce, ath);\n request.headers.set('dpop', initProof);\n const initResponse = await fetch(request);\n nextNonce = initResponse.headers.get('dpop-nonce');\n if (nextNonce === null || nextNonce === initNonce) {\n // No nonce was returned or it is the same as the one we sent. No need to\n // update the nonce store, or retry the request.\n return initResponse;\n }\n // Store the fresh nonce for future requests\n try {\n nonces.set(origin, nextNonce);\n }\n catch {\n // Ignore write errors\n }\n const shouldRetry = await isUseDpopNonceError(initResponse, isAuthServer);\n if (!shouldRetry) {\n // Not a \"use_dpop_nonce\" error, so there is no need to retry\n return initResponse;\n }\n if (input === request || init?.body instanceof ReadableStream) {\n // If the input stream was already consumed, we cannot retry the request. A\n // solution would be to clone() the request but that would bufferize the\n // entire stream in memory which can lead to memory starvation. Instead, we\n // will return the original response and let the calling code handle retries.\n return initResponse;\n }\n }\n finally {\n // Now everyone can have their turn.\n if (deferred) {\n pending.delete(origin);\n deferred.resolve();\n }\n }\n // We got here because we were asked to retry the request (due to missing\n // nonce value in the first request), let's do just that.\n {\n const nextProof = await sign(method, htu, nextNonce, ath);\n const nextRequest = new Request(input, init);\n nextRequest.headers.set('dpop', nextProof);\n const retryResponse = await fetch(nextRequest);\n // Check if the server returned another new nonce in the retry response\n const retryNonce = retryResponse.headers.get('dpop-nonce');\n if (retryNonce !== null && retryNonce !== nextNonce) {\n try {\n nonces.set(origin, retryNonce);\n }\n catch {\n // Ignore write errors\n }\n }\n return retryResponse;\n }\n };\n};\nconst isUseDpopNonceError = async (response, isAuthServer) => {\n // https://datatracker.ietf.org/doc/html/rfc6750#section-3\n // https://datatracker.ietf.org/doc/html/rfc9449#name-resource-server-provided-no\n if (isAuthServer === undefined || isAuthServer === false) {\n if (response.status === 401) {\n const wwwAuth = response.headers.get('www-authenticate');\n if (wwwAuth?.startsWith('DPoP')) {\n return wwwAuth.includes('error=\"use_dpop_nonce\"');\n }\n }\n }\n // https://datatracker.ietf.org/doc/html/rfc9449#name-authorization-server-provid\n if (isAuthServer === undefined || isAuthServer === true) {\n if (response.status === 400 && extractContentType(response.headers) === 'application/json') {\n try {\n const json = await response.clone().json();\n return typeof json === 'object' && json?.['error'] === 'use_dpop_nonce';\n }\n catch {\n // Response too big (to be \"use_dpop_nonce\" error) or invalid JSON\n return false;\n }\n }\n }\n return false;\n};\n//# sourceMappingURL=dpop.js.map","export const pick = (obj, keys) => {\n const cloned = {};\n for (let idx = 0, len = keys.length; idx < len; idx++) {\n const key = keys[idx];\n // @ts-expect-error\n cloned[key] = obj[key];\n }\n return cloned;\n};\n//# sourceMappingURL=misc.js.map","import { createDPoPFetch } from '../dpop.js';\nimport { CLIENT_ID, REDIRECT_URI } from '../environment.js';\nimport { FetchResponseError, OAuthResponseError, TokenRefreshError } from '../errors.js';\nimport { resolveFromIdentity } from '../resolvers.js';\nimport { pick } from '../utils/misc.js';\nimport { extractContentType } from '../utils/response.js';\nexport class OAuthServerAgent {\n #fetch;\n #metadata;\n constructor(metadata, dpopKey) {\n this.#metadata = metadata;\n this.#fetch = createDPoPFetch(dpopKey, true);\n }\n async request(endpoint, payload) {\n const url = this.#metadata[`${endpoint}_endpoint`];\n if (!url) {\n throw new Error(`no endpoint for ${endpoint}`);\n }\n const response = await this.#fetch(url, {\n method: 'post',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ ...payload, client_id: CLIENT_ID }),\n });\n if (extractContentType(response.headers) !== 'application/json') {\n throw new FetchResponseError(response, 2, `unexpected content-type`);\n }\n const json = await response.json();\n if (response.ok) {\n return json;\n }\n else {\n throw new OAuthResponseError(response, json);\n }\n }\n async revoke(token) {\n try {\n await this.request('revocation', { token: token });\n }\n catch { }\n }\n async exchangeCode(code, verifier) {\n const response = await this.request('token', {\n grant_type: 'authorization_code',\n redirect_uri: REDIRECT_URI,\n code: code,\n code_verifier: verifier,\n });\n try {\n return await this.#processExchangeResponse(response);\n }\n catch (err) {\n await this.revoke(response.access_token);\n throw err;\n }\n }\n async refresh({ sub, token }) {\n if (!token.refresh) {\n throw new TokenRefreshError(sub, 'no refresh token available');\n }\n const response = await this.request('token', {\n grant_type: 'refresh_token',\n refresh_token: token.refresh,\n });\n try {\n if (sub !== response.sub) {\n throw new TokenRefreshError(sub, `sub mismatch in token response; got ${response.sub}`);\n }\n return this.#processTokenResponse(response);\n }\n catch (err) {\n await this.revoke(response.access_token);\n throw err;\n }\n }\n #processTokenResponse(res) {\n if (!res.sub) {\n throw new TypeError(`missing sub field in token response`);\n }\n if (!res.scope) {\n throw new TypeError(`missing scope field in token response`);\n }\n if (res.token_type !== 'DPoP') {\n throw new TypeError(`token response returned a non-dpop token`);\n }\n return {\n scope: res.scope,\n refresh: res.refresh_token,\n access: res.access_token,\n type: res.token_type,\n expires_at: typeof res.expires_in === 'number' ? Date.now() + res.expires_in * 1_000 : undefined,\n };\n }\n async #processExchangeResponse(res) {\n const sub = res.sub;\n if (!sub) {\n throw new TypeError(`missing sub field in token response`);\n }\n const token = this.#processTokenResponse(res);\n const resolved = await resolveFromIdentity(sub);\n if (resolved.metadata.issuer !== this.#metadata.issuer) {\n throw new TypeError(`issuer mismatch; got ${resolved.metadata.issuer}`);\n }\n return {\n token: token,\n info: {\n sub: sub,\n aud: resolved.identity.pds.href,\n server: pick(resolved.metadata, [\n 'issuer',\n 'authorization_endpoint',\n 'introspection_endpoint',\n 'pushed_authorization_request_endpoint',\n 'revocation_endpoint',\n 'token_endpoint',\n ]),\n },\n };\n }\n}\n//# sourceMappingURL=server-agent.js.map","import { database } from '../environment.js';\nimport { OAuthResponseError, TokenRefreshError } from '../errors.js';\nimport { locks } from '../utils/runtime.js';\nimport { OAuthServerAgent } from './server-agent.js';\nconst pending = new Map();\nexport const getSession = async (sub, options) => {\n options?.signal?.throwIfAborted();\n let allowStored = isTokenUsable;\n if (options?.noCache) {\n allowStored = returnFalse;\n }\n else if (options?.allowStale) {\n allowStored = returnTrue;\n }\n // As long as concurrent requests are made for the same key, only one\n // request will be made to the cache & getter function at a time. This works\n // because there is no async operation between the while() loop and the\n // pending.set() call. Because of the \"single threaded\" nature of\n // JavaScript, the pending item will be set before the next iteration of the\n // while loop.\n let previousExecutionFlow;\n while ((previousExecutionFlow = pending.get(sub))) {\n try {\n const { isFresh, value } = await previousExecutionFlow;\n if (isFresh || allowStored(value)) {\n return value;\n }\n }\n catch {\n // Ignore errors from previous execution flows (they will have been\n // propagated by that flow).\n }\n options?.signal?.throwIfAborted();\n }\n const run = async () => {\n const storedSession = database.sessions.get(sub);\n if (storedSession && allowStored(storedSession)) {\n // Use the stored value as return value for the current execution\n // flow. Notify other concurrent execution flows (that should be\n // \"stuck\" in the loop before until this promise resolves) that we got\n // a value, but that it came from the store (isFresh = false).\n return { isFresh: false, value: storedSession };\n }\n const newSession = await refreshToken(sub, storedSession);\n await storeSession(sub, newSession);\n return { isFresh: true, value: newSession };\n };\n let promise;\n if (locks) {\n promise = locks.request(`atcute-oauth:${sub}`, run);\n }\n else {\n promise = run();\n }\n promise = promise.finally(() => pending.delete(sub));\n if (pending.has(sub)) {\n // This should never happen. Indeed, there must not be any 'await'\n // statement between this and the loop iteration check meaning that\n // this.pending.get returned undefined. It is there to catch bugs that\n // would occur in future changes to the code.\n throw new Error('concurrent request for the same key');\n }\n pending.set(sub, promise);\n const { value } = await promise;\n return value;\n};\nexport const storeSession = async (sub, newSession) => {\n try {\n database.sessions.set(sub, newSession);\n }\n catch (err) {\n await onRefreshError(newSession);\n throw err;\n }\n};\nexport const deleteStoredSession = (sub) => {\n database.sessions.delete(sub);\n};\nexport const listStoredSessions = () => {\n return database.sessions.keys();\n};\nconst returnTrue = () => true;\nconst returnFalse = () => false;\nconst refreshToken = async (sub, storedSession) => {\n if (storedSession === undefined) {\n throw new TokenRefreshError(sub, `session deleted by another tab`);\n }\n const { dpopKey, info, token } = storedSession;\n const server = new OAuthServerAgent(info.server, dpopKey);\n try {\n const newToken = await server.refresh({ sub: info.sub, token });\n return { dpopKey, info, token: newToken };\n }\n catch (cause) {\n if (cause instanceof OAuthResponseError && cause.status === 400 && cause.error === 'invalid_grant') {\n throw new TokenRefreshError(sub, `session was revoked`, { cause });\n }\n throw cause;\n }\n};\nconst onRefreshError = async ({ dpopKey, info, token }) => {\n // If the token data cannot be stored, let's revoke it\n const server = new OAuthServerAgent(info.server, dpopKey);\n await server.revoke(token.refresh ?? token.access);\n};\nconst isTokenUsable = ({ token }) => {\n const expires = token.expires_at;\n return expires == null || Date.now() + 60_000 <= expires;\n};\n//# sourceMappingURL=sessions.js.map","import { nanoid } from 'nanoid';\nimport { createES256Key } from '../dpop.js';\nimport { CLIENT_ID, database, REDIRECT_URI } from '../environment.js';\nimport { AuthorizationError, LoginError } from '../errors.js';\nimport { generatePKCE } from '../utils/runtime.js';\nimport { OAuthServerAgent } from './server-agent.js';\nimport { storeSession } from './sessions.js';\n/**\n * Create authentication URL for authorization\n * @param options\n * @returns URL to redirect the user for authorization\n */\nexport const createAuthorizationUrl = async ({ metadata, identity, scope, }) => {\n const state = nanoid(24);\n const pkce = await generatePKCE();\n const dpopKey = await createES256Key();\n const params = {\n redirect_uri: REDIRECT_URI,\n code_challenge: pkce.challenge,\n code_challenge_method: pkce.method,\n state: state,\n login_hint: identity?.raw,\n response_mode: 'fragment',\n response_type: 'code',\n display: 'page',\n // id_token_hint: undefined,\n // max_age: undefined,\n // prompt: undefined,\n scope: scope,\n // ui_locales: undefined,\n };\n database.states.set(state, {\n dpopKey: dpopKey,\n metadata: metadata,\n verifier: pkce.verifier,\n });\n const server = new OAuthServerAgent(metadata, dpopKey);\n const response = await server.request('pushed_authorization_request', params);\n const authUrl = new URL(metadata.authorization_endpoint);\n authUrl.searchParams.set('client_id', CLIENT_ID);\n authUrl.searchParams.set('request_uri', response.request_uri);\n return authUrl;\n};\n/**\n * Finalize authorization\n * @param params Search params\n * @returns Session object, which you can use to instantiate user agents\n */\nexport const finalizeAuthorization = async (params) => {\n const issuer = params.get('iss');\n const state = params.get('state');\n const code = params.get('code');\n const error = params.get('error');\n if (!state || !(code || error)) {\n throw new LoginError(`missing parameters`);\n }\n const stored = database.states.get(state);\n if (stored) {\n // Delete now that we've caught it\n database.states.delete(state);\n }\n else {\n throw new LoginError(`unknown state provided`);\n }\n const dpopKey = stored.dpopKey;\n const metadata = stored.metadata;\n if (error) {\n throw new AuthorizationError(params.get('error_description') || error);\n }\n if (!code) {\n throw new LoginError(`missing code parameter`);\n }\n if (issuer === null) {\n throw new LoginError(`missing issuer parameter`);\n }\n else if (issuer !== metadata.issuer) {\n throw new LoginError(`issuer mismatch`);\n }\n // Retrieve authentication tokens\n const server = new OAuthServerAgent(metadata, dpopKey);\n const { info, token } = await server.exchangeCode(code, stored.verifier);\n // We're finished!\n const sub = info.sub;\n const session = { dpopKey, info, token };\n await storeSession(sub, session);\n return session;\n};\n//# sourceMappingURL=exchange.js.map","import { createDPoPFetch } from '../dpop.js';\nimport { OAuthServerAgent } from './server-agent.js';\nimport { deleteStoredSession, getSession } from './sessions.js';\nexport class OAuthUserAgent {\n session;\n #fetch;\n #getSessionPromise;\n constructor(session) {\n this.session = session;\n this.#fetch = createDPoPFetch(session.dpopKey, false);\n }\n get sub() {\n return this.session.info.sub;\n }\n getSession(options) {\n const promise = getSession(this.session.info.sub, options);\n promise\n .then((session) => {\n this.session = session;\n })\n .finally(() => {\n this.#getSessionPromise = undefined;\n });\n return (this.#getSessionPromise = promise);\n }\n async signOut() {\n const sub = this.session.info.sub;\n try {\n const { dpopKey, info, token } = await getSession(sub, { allowStale: true });\n const server = new OAuthServerAgent(info.server, dpopKey);\n await server.revoke(token.refresh ?? token.access);\n }\n finally {\n deleteStoredSession(sub);\n }\n }\n async handle(pathname, init) {\n await this.#getSessionPromise;\n const headers = new Headers(init?.headers);\n let session = this.session;\n let url = new URL(pathname, session.info.aud);\n headers.set('authorization', `${session.token.type} ${session.token.access}`);\n let response = await this.#fetch(url, { ...init, headers });\n if (!isInvalidTokenResponse(response)) {\n return response;\n }\n try {\n if (this.#getSessionPromise) {\n session = await this.#getSessionPromise;\n }\n else {\n session = await this.getSession();\n }\n }\n catch {\n return response;\n }\n // Stream already consumed, can't retry.\n if (init?.body instanceof ReadableStream) {\n return response;\n }\n url = new URL(pathname, session.info.aud);\n headers.set('authorization', `${session.token.type} ${session.token.access}`);\n return await this.#fetch(url, { ...init, headers });\n }\n}\nconst isInvalidTokenResponse = (response) => {\n if (response.status !== 401) {\n return false;\n }\n const auth = response.headers.get('www-authenticate');\n return (auth != null &&\n (auth.startsWith('Bearer ') || auth.startsWith('DPoP ')) &&\n auth.includes('error=\"invalid_token\"'));\n};\n//# sourceMappingURL=user-agent.js.map","// Storage adapter that mimics browser.storage.local API but uses localStorage\n// This allows sharing code between extension and via-client\n\nexport const storage = {\n local: {\n async get(keys?: string | string[] | Record<string, any>): Promise<Record<string, any>> {\n if (!keys) {\n // Get all items\n const result: Record<string, any> = {};\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key) {\n try {\n result[key] = JSON.parse(localStorage.getItem(key) || 'null');\n } catch {\n result[key] = localStorage.getItem(key);\n }\n }\n }\n return result;\n }\n\n const result: Record<string, any> = {};\n \n if (typeof keys === 'string') {\n // Single key\n try {\n const value = localStorage.getItem(keys);\n result[keys] = value ? JSON.parse(value) : null;\n } catch {\n result[keys] = localStorage.getItem(keys);\n }\n } else if (Array.isArray(keys)) {\n // Array of keys\n keys.forEach(key => {\n try {\n const value = localStorage.getItem(key);\n result[key] = value ? JSON.parse(value) : null;\n } catch {\n result[key] = localStorage.getItem(key);\n }\n });\n } else {\n // Object with default values\n Object.keys(keys).forEach(key => {\n try {\n const value = localStorage.getItem(key);\n result[key] = value ? JSON.parse(value) : keys[key];\n } catch {\n result[key] = localStorage.getItem(key) || keys[key];\n }\n });\n }\n \n return result;\n },\n\n async set(items: Record<string, any>): Promise<void> {\n Object.entries(items).forEach(([key, value]) => {\n localStorage.setItem(key, JSON.stringify(value));\n });\n },\n\n async remove(keys: string | string[]): Promise<void> {\n const keysArray = Array.isArray(keys) ? keys : [keys];\n keysArray.forEach(key => localStorage.removeItem(key));\n },\n\n async clear(): Promise<void> {\n localStorage.clear();\n },\n },\n};\n","// Web-compatible OAuth implementation (for via-client)\n// Adapted from lib/oauth.ts to work without browser.* APIs\n\nimport {\n configureOAuth,\n createAuthorizationUrl,\n finalizeAuthorization,\n resolveFromIdentity,\n OAuthUserAgent,\n type OAuthSession,\n} from \"@atcute/oauth-browser-client\";\nimport { storage } from \"./storage-adapter\";\n\nconst OAUTH_SESSION_KEY = \"synthesis-oauth:session\";\n\nlet isOAuthInitialized = false;\n\nexport function initializeOAuth() {\n if (typeof window !== \"undefined\" && !isOAuthInitialized) {\n // Use web redirect URL for via proxy\n configureOAuth({\n metadata: {\n client_id: import.meta.env.VITE_OAUTH_CLIENT_ID || 'http://localhost:8081/static/client-metadata.json',\n redirect_uri: import.meta.env.VITE_OAUTH_REDIRECT_URI || 'http://localhost:8081/static/oauth-callback.html',\n },\n });\n isOAuthInitialized = true;\n }\n}\n\nexport async function startLoginProcess(handle: string): Promise<void> {\n console.log('[oauth-web] Starting login process for handle:', handle);\n initializeOAuth();\n \n console.log('[oauth-web] Resolving identity...');\n const { metadata } = await resolveFromIdentity(handle);\n console.log('[oauth-web] PDS metadata:', metadata);\n \n console.log('[oauth-web] Creating authorization URL...');\n const authUrl = await createAuthorizationUrl({\n metadata: metadata,\n scope: import.meta.env.VITE_OAUTH_SCOPE || 'atproto transition:generic',\n });\n console.log('[oauth-web] Auth URL:', authUrl.toString());\n\n // For web context, redirect to auth URL\n window.location.href = authUrl.toString();\n}\n\nexport async function handleOAuthCallback(): Promise<OAuthSession | null> {\n console.log('[oauth-web] Handling OAuth callback');\n \n // Parse OAuth response from URL (params can be in search or hash)\n const url = new URL(window.location.href);\n const paramString = url.search || url.hash.slice(1);\n const params = new URLSearchParams(paramString);\n \n console.log('[oauth-web] OAuth params:', Object.fromEntries(params));\n\n if (!params.has('code') && !params.has('error')) {\n console.log('[oauth-web] No OAuth params found');\n return null;\n }\n\n if (params.has('error')) {\n const error = params.get('error');\n const errorDesc = params.get('error_description');\n console.error('[oauth-web] OAuth error:', error, errorDesc);\n throw new Error(`OAuth error: ${error} - ${errorDesc}`);\n }\n\n // Finalize authorization with the params\n console.log('[oauth-web] Finalizing authorization...');\n const session = await finalizeAuthorization(params);\n console.log('[oauth-web] Authorization complete, session:', session);\n\n // Store session\n await saveSession(session);\n console.log('[oauth-web] Session saved successfully');\n\n return session;\n}\n\nexport async function saveSession(session: OAuthSession): Promise<void> {\n await storage.local.set({ [OAUTH_SESSION_KEY]: session });\n}\n\nexport async function loadSession(): Promise<OAuthSession | null> {\n const result = await storage.local.get(OAUTH_SESSION_KEY);\n return result[OAUTH_SESSION_KEY] || null;\n}\n\nexport async function clearSession(): Promise<void> {\n await storage.local.remove(OAUTH_SESSION_KEY);\n}\n\nexport async function getProfile(session: OAuthSession): Promise<any> {\n const agent = new OAuthUserAgent(session);\n const response = await agent.handle('/xrpc/app.bsky.actor.getProfile?actor=' + session.info.sub);\n return await response.json();\n}\n"],"names":["urlAlphabet","nanoid","size","id","bytes","scopedUrlAlphabet","textEncoder","subtle","alloc","allocUnsafe","encodeUtf8","str","toSha256","buffer","createRfc4648Encode","alphabet","bitsPerChar","pad","mask","bits","i","createRfc4648Decode","codes","end","written","value","fromBase64Url","toBase64Url","BASE64URL_CHARSET","HAS_NATIVE_SUPPORT","fromBase64UrlNative","fromBase64UrlPolyfill","toBase64UrlNative","toBase64UrlPolyfill","locks","stringToSha256","input","digest","generatePKCE","verifier","parse","raw","parsed","createOAuthDatabase","name","controller","signal","createStore","subname","expiresAt","persistUpdatedAt","store","storageKey","persist","read","listener","ev","cleanup","lock","resolve","now","changed","key","item","updatedAt","token","_item","CLIENT_ID","REDIRECT_URI","database","configureOAuth","options","LoginError","__publicField","AuthorizationError","ResolverError","TokenRefreshError","sub","message","OAuthResponseError","response","data","_a","_b","error","ifString","ifObject","errorDescription","messageError","messageDesc","FetchResponseError","status","v","DID_RE","isDid","isUrlParseSupported","isAtprotoServiceEndpoint","url","getAtprotoServiceEndpoint","doc","predicate","services","idx","len","type","serviceEndpoint","getPdsEndpoint","DEFAULT_APPVIEW_URL","extractContentType","headers","isValidUrl","urlString","DID_WEB_RE","resolveHandle","handle","getDidDocument","did","colon_index","ident","getProtectedResourceMetadata","host","metadata","getAuthorizationServerMetadata","resolveFromIdentity","pds","getMetadataFromResourceServer","rs_metadata","issuer","as_metadata","ES256_ALG","createES256Key","pair","_ext","_key_opts","jwk","createDPoPSignage","dpopKey","headerString","keyPromise","constructPayload","htm","htu","nonce","ath","payload","method","payloadString","signed","signatureString","createDPoPFetch","isAuthServer","nonces","pending","sign","init","request","authorizationHeader","origin","pathname","deferred","initNonce","expiredOrMissing","lapsed","nextNonce","initProof","initResponse","isUseDpopNonceError","nextProof","nextRequest","retryResponse","retryNonce","wwwAuth","json","pick","obj","keys","cloned","_fetch","_metadata","_OAuthServerAgent_instances","processTokenResponse_fn","processExchangeResponse_fn","OAuthServerAgent","__privateAdd","__privateSet","endpoint","__privateGet","code","__privateMethod","err","res","resolved","getSession","allowStored","isTokenUsable","returnFalse","returnTrue","previousExecutionFlow","isFresh","run","storedSession","newSession","refreshToken","storeSession","promise","onRefreshError","deleteStoredSession","info","server","newToken","cause","expires","createAuthorizationUrl","identity","scope","state","pkce","params","authUrl","finalizeAuthorization","stored","session","_getSessionPromise","OAuthUserAgent","isInvalidTokenResponse","auth","storage","result","items","OAUTH_SESSION_KEY","isOAuthInitialized","initializeOAuth","startLoginProcess","handleOAuthCallback","paramString","errorDesc","saveSession","loadSession","clearSession","getProfile"],"mappings":"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","x_google_ignoreList":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22]}
+1
pywb-test/static/assets/sidebar-BBEPW7gD.css
··· 1 + *{margin:0;padding:0;box-sizing:border-box}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,sans-serif;font-size:14px;line-height:1.5;color:#333}.sidebar{display:flex;flex-direction:column;height:100vh;background:#fff}.sidebar-header{padding:16px;border-bottom:1px solid #e0e0e0;background:#f5f5f5}.sidebar-header h1{font-size:20px;font-weight:600;margin-bottom:4px}.sidebar-header p{font-size:12px;color:#666}.sidebar-content{flex:1;overflow-y:auto;padding:16px}
+1
pywb-test/static/assets/sidebar-J3iG1W2k.css
··· 1 + *{margin:0;padding:0;box-sizing:border-box}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,sans-serif;font-size:14px;line-height:1.5;color:#333}.sidebar{display:flex;flex-direction:column;height:100vh;background:#fff}.sidebar-header{padding:16px;border-bottom:1px solid #e0e0e0;background:#f5f5f5}.sidebar-header h1{font-size:20px;font-weight:600;margin-bottom:4px}.sidebar-header p{font-size:12px;color:#666}.profile-info{display:flex;align-items:center;gap:8px;margin-top:8px}.profile-avatar{width:32px;height:32px;border-radius:50%}.profile-handle{font-size:14px;color:#333}.sidebar-content{flex:1;overflow-y:auto;padding:16px}.login-container{display:flex;flex-direction:column;gap:12px}.login-container h2{font-size:18px;font-weight:600;margin-bottom:8px}.input-wrapper{position:relative;display:flex;align-items:center}.at-symbol{position:absolute;left:12px;color:#666;font-size:14px;pointer-events:none}.handle-input{width:100%;padding:10px 12px 10px 28px;border:1px solid #ccc;border-radius:4px;font-size:14px;font-family:inherit}.handle-input:focus{outline:none;border-color:#0085ff}button{padding:10px 16px;background:#0085ff;color:#fff;border:none;border-radius:4px;font-size:14px;font-weight:500;cursor:pointer;font-family:inherit}button:hover{background:#0073e6}button:active{background:#0061c2}#auth-status{font-size:12px;color:#666;min-height:16px}#logout-btn{background:#e74c3c;margin-top:8px}#logout-btn:hover{background:#c0392b}
+2
pywb-test/static/assets/storage-web-DVEDyBzH.js
··· 1 + class o{constructor(e="seams-storage"){this.listeners=[],this.channel=new BroadcastChannel(e),this.channel.onmessage=s=>{console.log("[WebStorage] Received broadcast:",s.data),this.listeners.forEach(a=>a(s.data))}}async get(e){const s=localStorage.getItem(e);return s?JSON.parse(s):null}async set(e,s){const a=await this.get(e);localStorage.setItem(e,JSON.stringify(s));const t={key:e,newValue:s,oldValue:a};console.log("[WebStorage] Broadcasting change:",t),this.channel.postMessage(t)}onChange(e){this.listeners.push(e)}close(){this.channel.close()}}export{o as W}; 2 + //# sourceMappingURL=storage-web-DVEDyBzH.js.map
+1
pywb-test/static/assets/storage-web-DVEDyBzH.js.map
··· 1 + {"version":3,"file":"storage-web-DVEDyBzH.js","sources":["../../../lib/storage-web.ts"],"sourcesContent":["// Web storage adapter using localStorage + BroadcastChannel\n// Mimics browser.storage API for via proxy client\n\nexport interface StorageAdapter {\n get(key: string): Promise<any>;\n set(key: string, value: any): Promise<void>;\n onChange(callback: (changes: { key: string; newValue: any; oldValue?: any }) => void): void;\n}\n\nexport class WebStorage implements StorageAdapter {\n private channel: BroadcastChannel;\n private listeners: Array<(changes: { key: string; newValue: any; oldValue?: any }) => void> = [];\n\n constructor(channelName: string = 'seams-storage') {\n this.channel = new BroadcastChannel(channelName);\n \n // Listen for broadcasts from other contexts\n this.channel.onmessage = (event) => {\n console.log('[WebStorage] Received broadcast:', event.data);\n this.listeners.forEach(callback => callback(event.data));\n };\n }\n\n async get(key: string): Promise<any> {\n const value = localStorage.getItem(key);\n return value ? JSON.parse(value) : null;\n }\n\n async set(key: string, value: any): Promise<void> {\n const oldValue = await this.get(key);\n localStorage.setItem(key, JSON.stringify(value));\n \n // Broadcast change to other contexts (sidebar, content script, other tabs)\n const change = { key, newValue: value, oldValue };\n console.log('[WebStorage] Broadcasting change:', change);\n this.channel.postMessage(change);\n }\n\n onChange(callback: (changes: { key: string; newValue: any; oldValue?: any }) => void): void {\n this.listeners.push(callback);\n }\n\n close(): void {\n this.channel.close();\n }\n}\n"],"names":["WebStorage","channelName","event","callback","key","value","oldValue","change"],"mappings":"AASO,MAAMA,CAAqC,CAIhD,YAAYC,EAAsB,gBAAiB,CAFnD,KAAQ,UAAsF,CAAA,EAG5F,KAAK,QAAU,IAAI,iBAAiBA,CAAW,EAG/C,KAAK,QAAQ,UAAaC,GAAU,CAClC,QAAQ,IAAI,mCAAoCA,EAAM,IAAI,EAC1D,KAAK,UAAU,QAAQC,GAAYA,EAASD,EAAM,IAAI,CAAC,CACzD,CACF,CAEA,MAAM,IAAIE,EAA2B,CACnC,MAAMC,EAAQ,aAAa,QAAQD,CAAG,EACtC,OAAOC,EAAQ,KAAK,MAAMA,CAAK,EAAI,IACrC,CAEA,MAAM,IAAID,EAAaC,EAA2B,CAChD,MAAMC,EAAW,MAAM,KAAK,IAAIF,CAAG,EACnC,aAAa,QAAQA,EAAK,KAAK,UAAUC,CAAK,CAAC,EAG/C,MAAME,EAAS,CAAE,IAAAH,EAAK,SAAUC,EAAO,SAAAC,CAAA,EACvC,QAAQ,IAAI,oCAAqCC,CAAM,EACvD,KAAK,QAAQ,YAAYA,CAAM,CACjC,CAEA,SAASJ,EAAmF,CAC1F,KAAK,UAAU,KAAKA,CAAQ,CAC9B,CAEA,OAAc,CACZ,KAAK,QAAQ,MAAA,CACf,CACF"}
+14
pywb-test/static/client-metadata.json
··· 1 + { 2 + "client_id": "http://localhost:8081/static/client-metadata.json", 3 + "client_name": "Seams (via proxy - development)", 4 + "client_uri": "https://seams.so", 5 + "redirect_uris": [ 6 + "http://localhost:8081/static/oauth-callback.html" 7 + ], 8 + "scope": "atproto transition:generic", 9 + "grant_types": ["authorization_code", "refresh_token"], 10 + "response_types": ["code"], 11 + "application_type": "web", 12 + "token_endpoint_auth_method": "none", 13 + "dpop_bound_access_tokens": true 14 + }
+16
pywb-test/static/extension-callback.html
··· 1 + <!DOCTYPE html> 2 + <html> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <title>OAuth Callback</title> 6 + </head> 7 + <body> 8 + <p>Authenticated. This window will close automatically.</p> 9 + <script> 10 + // Log what we received to help debug 11 + console.log('[extension-callback] Full URL:', window.location.href); 12 + console.log('[extension-callback] Search:', window.location.search); 13 + console.log('[extension-callback] Hash:', window.location.hash); 14 + </script> 15 + </body> 16 + </html>
+64
pywb-test/static/fonts.css
··· 1 + /* fraunces-regular - latin */ 2 + @font-face { 3 + font-display: swap; 4 + font-family: 'Fraunces'; 5 + font-style: normal; 6 + font-weight: 400; 7 + src: url('fonts/fraunces-v38-latin-regular.eot'); /* IE9 Compat Modes */ 8 + src: url('fonts/fraunces-v38-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ 9 + url('fonts/fraunces-v38-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */ 10 + url('fonts/fraunces-v38-latin-regular.woff') format('woff'), /* Modern Browsers */ 11 + url('fonts/fraunces-v38-latin-regular.ttf') format('truetype'); /* Safari, Android, iOS */ 12 + } 13 + 14 + /* fraunces-600 - latin */ 15 + @font-face { 16 + font-display: swap; 17 + font-family: 'Fraunces'; 18 + font-style: normal; 19 + font-weight: 600; 20 + src: url('fonts/fraunces-v38-latin-600.eot'); /* IE9 Compat Modes */ 21 + src: url('fonts/fraunces-v38-latin-600.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ 22 + url('fonts/fraunces-v38-latin-600.woff2') format('woff2'), /* Super Modern Browsers */ 23 + url('fonts/fraunces-v38-latin-600.woff') format('woff'), /* Modern Browsers */ 24 + url('fonts/fraunces-v38-latin-600.ttf') format('truetype'); /* Safari, Android, iOS */ 25 + } 26 + 27 + /* fraunces-700 - latin */ 28 + @font-face { 29 + font-display: swap; 30 + font-family: 'Fraunces'; 31 + font-style: normal; 32 + font-weight: 700; 33 + src: url('fonts/fraunces-v38-latin-700.eot'); /* IE9 Compat Modes */ 34 + src: url('fonts/fraunces-v38-latin-700.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ 35 + url('fonts/fraunces-v38-latin-700.woff2') format('woff2'), /* Super Modern Browsers */ 36 + url('fonts/fraunces-v38-latin-700.woff') format('woff'), /* Modern Browsers */ 37 + url('fonts/fraunces-v38-latin-700.ttf') format('truetype'); /* Safari, Android, iOS */ 38 + } 39 + 40 + /* spectral-600 - latin */ 41 + @font-face { 42 + font-display: swap; 43 + font-family: 'Spectral'; 44 + font-style: normal; 45 + font-weight: 600; 46 + src: url('fonts/spectral-v15-latin-600.eot'); /* IE9 Compat Modes */ 47 + src: url('fonts/spectral-v15-latin-600.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ 48 + url('fonts/spectral-v15-latin-600.woff2') format('woff2'), /* Super Modern Browsers */ 49 + url('fonts/spectral-v15-latin-600.woff') format('woff'), /* Modern Browsers */ 50 + url('fonts/spectral-v15-latin-600.ttf') format('truetype'); /* Safari, Android, iOS */ 51 + } 52 + 53 + /* spectral-700 - latin */ 54 + @font-face { 55 + font-display: swap; 56 + font-family: 'Spectral'; 57 + font-style: normal; 58 + font-weight: 700; 59 + src: url('fonts/spectral-v15-latin-700.eot'); /* IE9 Compat Modes */ 60 + src: url('fonts/spectral-v15-latin-700.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ 61 + url('fonts/spectral-v15-latin-700.woff2') format('woff2'), /* Super Modern Browsers */ 62 + url('fonts/spectral-v15-latin-700.woff') format('woff'), /* Modern Browsers */ 63 + url('fonts/spectral-v15-latin-700.ttf') format('truetype'); /* Safari, Android, iOS */ 64 + }
pywb-test/static/fonts/fraunces-v38-latin-600.eot

This is a binary file and will not be displayed.

pywb-test/static/fonts/fraunces-v38-latin-600.ttf

This is a binary file and will not be displayed.

pywb-test/static/fonts/fraunces-v38-latin-600.woff

This is a binary file and will not be displayed.

pywb-test/static/fonts/fraunces-v38-latin-600.woff2

This is a binary file and will not be displayed.

pywb-test/static/fonts/fraunces-v38-latin-700.eot

This is a binary file and will not be displayed.

pywb-test/static/fonts/fraunces-v38-latin-700.ttf

This is a binary file and will not be displayed.

pywb-test/static/fonts/fraunces-v38-latin-700.woff

This is a binary file and will not be displayed.

pywb-test/static/fonts/fraunces-v38-latin-700.woff2

This is a binary file and will not be displayed.

pywb-test/static/fonts/fraunces-v38-latin-regular.eot

This is a binary file and will not be displayed.

pywb-test/static/fonts/fraunces-v38-latin-regular.ttf

This is a binary file and will not be displayed.

pywb-test/static/fonts/fraunces-v38-latin-regular.woff

This is a binary file and will not be displayed.

pywb-test/static/fonts/fraunces-v38-latin-regular.woff2

This is a binary file and will not be displayed.

pywb-test/static/fonts/spectral-v15-latin-600.eot

This is a binary file and will not be displayed.

+341
pywb-test/static/fonts/spectral-v15-latin-600.svg
··· 1 + <?xml version="1.0" standalone="no"?> 2 + <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> 3 + <svg xmlns="http://www.w3.org/2000/svg"> 4 + <defs > 5 + <font id="Spectral" horiz-adv-x="598" ><font-face 6 + font-family="Spectral SemiBold" 7 + units-per-em="1000" 8 + panose-1="0 0 0 0 0 0 0 0 0 0" 9 + ascent="1059" 10 + descent="-463" 11 + alphabetic="0" /> 12 + <glyph unicode=" " glyph-name="space" horiz-adv-x="250" /> 13 + <glyph unicode="!" glyph-name="exclam" horiz-adv-x="261" d="M117 211L62 660H199L144 211H117ZM130 -5Q102 -5 82 15T62 64Q62 92 82 112T130 133Q158 133 178 113T199 64Q199 36 179 16T130 -5Z" /> 14 + <glyph unicode="&quot;" glyph-name="quotedbl" horiz-adv-x="450" d="M114 490L76 740H174L136 490H114ZM314 490L276 740H374L336 490H314Z" /> 15 + <glyph unicode="#" glyph-name="numbersign" horiz-adv-x="536" d="M57 0L116 202H19V251H130L174 402H77V451H188L249 660H301L240 451H368L429 660H481L420 451H517V402H406L362 251H459V202H347L289 0H237L296 202H167L109 0H57ZM182 251H310L354 402H226L182 251Z" /> 16 + <glyph unicode="$" glyph-name="dollar" horiz-adv-x="536" d="M207 -80V-5Q133 8 94 48T54 134Q54 174 94 174H145V77Q169 56 207 46V296Q124 333 91 375T58 479Q58 518 78 553T131 616T207 658V740H244V667Q262 670 280 670Q292 670 304 669V740H341V664Q402 17 + 652 436 618T470 543Q470 522 461 513T431 504H385V588Q367 604 341 612V374Q397 347 429 320T475 260T489 185Q489 118 447 70T341 2V-80H304V-7Q283 -10 263 -10Q253 -10 244 -9V-80H207ZM282 619Q261 619 244 615V418Q257 411 272 404T304 390V618Q293 619 282 18 + 619ZM163 523Q163 498 172 479T207 441V600Q163 574 163 523ZM261 39Q284 39 304 44V251Q280 264 244 280V40Q252 40 261 39ZM388 141Q388 169 379 189T341 229V58Q388 85 388 141Z" /> 19 + <glyph unicode="%" glyph-name="percent" horiz-adv-x="775" d="M161 345Q91 345 51 400T10 548Q10 605 29 651T83 723T161 750Q231 750 271 695T312 547Q312 490 292 444T238 372T161 345ZM168 372Q185 372 195 396T209 456T214 531Q214 581 208 624T189 695T155 20 + 722Q138 722 128 699T113 640T108 564Q108 514 114 471T133 400T168 372ZM133 17L602 750L643 723L174 -10L133 17ZM615 -10Q545 -10 505 45T464 193Q464 250 483 296T537 368T615 395Q685 395 725 340T766 192Q766 135 746 89T692 17T615 -10ZM622 17Q639 17 649 21 + 41T663 101T668 176Q668 226 662 269T643 340T609 367Q592 367 582 344T567 285T562 209Q562 159 568 116T587 45T622 17Z" /> 22 + <glyph unicode="&amp;" glyph-name="ampersand" horiz-adv-x="736" d="M233 -10Q142 -10 86 35T30 160Q30 212 63 259T169 347Q138 380 121 405T95 453T87 507Q87 551 113 588T184 647T286 670Q370 670 418 633T467 537Q467 494 428 458T302 382Q311 374 320 365T338 23 + 346L491 192Q542 269 584 390L486 429V445H729V429L643 392Q613 326 583 269T519 164L631 52L714 16V0H514L441 73Q397 33 346 12T233 -10ZM202 540Q202 506 220 477T277 408Q313 437 332 469T352 541Q352 577 332 601T277 625Q243 625 223 601T202 540ZM147 194Q147 24 + 130 184 95T282 59Q351 59 411 104L255 260Q238 276 224 291T197 319Q166 289 157 261T147 194Z" /> 25 + <glyph unicode="&apos;" glyph-name="quotesingle" horiz-adv-x="250" d="M114 490L76 740H174L136 490H114Z" /> 26 + <glyph unicode="(" glyph-name="parenleft" horiz-adv-x="257" d="M224 -150Q139 -51 95 60T51 295Q51 419 95 530T224 740H247V737Q187 641 155 537T122 295Q122 204 136 128T179 -15T247 -147V-150H224Z" /> 27 + <glyph unicode=")" glyph-name="parenright" horiz-adv-x="257" d="M10 -150V-147Q51 -82 78 -15T120 128T135 295Q135 432 103 536T10 737V740H33Q118 641 162 530T206 295Q206 171 162 60T33 -150H10Z" /> 28 + <glyph unicode="*" glyph-name="asterisk" horiz-adv-x="379" d="M24 595Q22 616 27 633T48 665L178 599L153 741Q189 760 225 741L201 599L330 665Q345 651 351 634T355 595L209 574L314 470Q299 433 255 428L189 555L124 428Q80 433 65 470L169 574L24 595Z" /> 29 + <glyph unicode="+" glyph-name="plus" horiz-adv-x="536" d="M240 62V246H58V298H240V482H296V298H478V246H296V62H240Z" /> 30 + <glyph unicode="," glyph-name="comma" horiz-adv-x="261" d="M29 -197L87 108H211V91L50 -197H29Z" /> 31 + <glyph unicode="-" glyph-name="hyphen" horiz-adv-x="371" d="M48 222V292H323V222H48Z" /> 32 + <glyph unicode="." glyph-name="period" horiz-adv-x="261" d="M130 -5Q102 -5 82 15T62 64Q62 92 82 112T130 133Q158 133 178 113T199 64Q199 36 179 16T130 -5Z" /> 33 + <glyph unicode="/" glyph-name="slash" horiz-adv-x="250" d="M0 -120L200 750H250L50 -120H0Z" /> 34 + <glyph unicode="0" glyph-name="zero" horiz-adv-x="536" d="M268 -10Q194 -10 141 31T61 149T33 330Q33 428 62 504T145 625T268 670Q342 670 395 629T475 512T503 330Q503 233 473 156T390 35T268 -10ZM286 38Q315 38 334 71T364 164T374 299Q374 392 362 465T322 35 + 581T253 623Q223 623 203 590T173 499T162 363Q162 269 175 196T215 80T286 38Z" /> 36 + <glyph unicode="1" glyph-name="one" horiz-adv-x="536" d="M150 0V16L243 53V552L73 472V510L319 660H364V53L447 16V0H150Z" /> 37 + <glyph unicode="2" glyph-name="two" horiz-adv-x="536" d="M52 0V32Q135 125 189 188T275 294T322 369T341 431T345 500Q345 555 319 585T244 615Q188 615 146 583V467H73Q55 467 48 474T40 504Q40 532 59 561T111 615T188 655T279 670Q338 670 383 647T453 583T478 38 + 493Q478 461 471 434T444 378T391 312T303 224T173 100H427L482 198H498L488 0H52Z" /> 39 + <glyph unicode="3" glyph-name="three" horiz-adv-x="536" d="M258 -10Q194 -10 144 9T65 59T36 124Q36 146 43 153T68 161H142V64Q182 39 248 39Q307 39 340 76T373 172Q373 241 337 280T212 319H157V365H197Q258 365 298 402T339 508Q339 568 315 594T245 621Q226 40 + 621 204 616T159 596V485H85Q66 485 59 492T52 521Q52 558 82 592T164 648T277 670Q337 670 380 649T448 593T472 513Q472 462 430 419T310 359Q412 346 459 298T507 179Q507 130 475 87T386 17T258 -10Z" /> 41 + <glyph unicode="4" glyph-name="four" horiz-adv-x="536" d="M191 0V16L284 53V182H12V208L359 660H404V256H523V182H404V53L488 16V0H191ZM109 256H284V491L109 256Z" /> 42 + <glyph unicode="5" glyph-name="five" horiz-adv-x="536" d="M476 227Q476 156 442 103T349 20T212 -10Q135 -10 97 14T59 67Q59 85 75 96L124 132L189 35Q200 34 214 34Q279 34 323 73T367 186Q367 255 323 294T192 334Q167 334 142 331T88 320L100 660H444V569H151L144 43 + 381Q212 410 275 410Q338 410 383 386T452 320T476 227Z" /> 44 + <glyph unicode="6" glyph-name="six" horiz-adv-x="536" d="M271 -10Q201 -10 148 21T65 109T36 240Q36 348 85 434T227 580T445 670V637Q326 600 257 516T171 316Q198 352 237 372T328 392Q383 392 422 367T483 298T505 201Q505 147 479 99T401 20T271 -10ZM166 45 + 243Q166 140 196 89T275 37Q311 37 333 58T367 112T378 176Q378 244 352 282T271 320Q238 320 211 305T167 263Q166 253 166 243Z" /> 46 + <glyph unicode="7" glyph-name="seven" horiz-adv-x="536" d="M98 0Q124 60 151 115T211 233T290 372T396 553H55V660H486V629Q451 560 412 482T337 323T272 159T227 0H98Z" /> 47 + <glyph unicode="8" glyph-name="eight" horiz-adv-x="536" d="M266 -10Q205 -10 154 9T72 64T41 147Q41 176 52 205T91 262T174 320Q110 360 86 399T61 493Q61 541 91 581T169 646T274 670Q328 670 373 649T446 591T474 507Q474 466 439 432T347 367Q424 323 459 48 + 281T495 179Q495 121 463 79T378 13T266 -10ZM176 535Q176 509 185 487T221 442T304 390Q335 411 353 439T371 504Q371 565 343 594T268 624Q228 624 202 601T176 535ZM148 165Q148 104 181 69T267 34Q320 34 349 62T379 136Q379 172 362 196T308 243T212 298V298Q173 49 + 270 161 240T148 165Z" /> 50 + <glyph unicode="9" glyph-name="nine" horiz-adv-x="536" d="M91 -10V23Q210 60 279 144T365 344Q339 308 299 288T209 268Q154 268 114 293T53 362T31 459Q31 513 57 561T135 640T265 670Q336 670 389 639T471 551T501 420Q501 312 451 226T310 80T91 -10ZM159 51 + 484Q159 417 185 379T265 340Q298 340 325 355T370 398Q370 407 370 417Q370 520 340 571T262 623Q226 623 203 602T170 548T159 484Z" /> 52 + <glyph unicode=":" glyph-name="colon" horiz-adv-x="261" d="M130 -5Q102 -5 82 15T62 64Q62 92 82 112T130 133Q158 133 178 113T199 64Q199 36 179 16T130 -5ZM130 324Q102 324 82 344T62 393Q62 421 82 441T130 462Q158 462 178 442T199 393Q199 365 179 345T130 53 + 324Z" /> 54 + <glyph unicode=";" glyph-name="semicolon" horiz-adv-x="261" d="M130 324Q102 324 82 344T62 393Q62 421 82 441T130 462Q158 462 178 442T199 393Q199 365 179 345T130 324ZM268 -197L326 108H450V91L289 -197H268Z" /> 55 + <glyph unicode="&lt;" glyph-name="less" horiz-adv-x="536" d="M478 62L58 247V297L478 482V423L135 272L478 121V62Z" /> 56 + <glyph unicode="=" glyph-name="equal" horiz-adv-x="536" d="M58 348V400H478V348H58ZM58 144V196H478V144H58Z" /> 57 + <glyph unicode="&gt;" glyph-name="greater" horiz-adv-x="536" d="M58 62V121L401 272L58 423V482L478 297V247L58 62Z" /> 58 + <glyph unicode="?" glyph-name="question" horiz-adv-x="479" d="M197 211L168 364Q245 382 287 417T329 509Q329 565 299 592T223 619Q203 619 183 614T143 595V484H64Q47 484 41 492T34 523Q34 558 62 592T139 648T246 670Q303 670 345 647T411 583T435 493Q435 59 + 425 386 373T235 288L224 211H197ZM210 -5Q182 -5 162 15T141 64Q141 92 161 112T210 133Q237 133 258 113T279 64Q279 36 258 16T210 -5Z" /> 60 + <glyph unicode="@" glyph-name="at" horiz-adv-x="810" d="M372 -118Q280 -118 211 -79T103 28T64 182Q64 262 91 331T170 452T290 533T442 562Q540 562 607 520T708 409T742 257Q742 187 717 134T652 50T562 20Q507 20 479 50T452 131Q414 73 379 47T304 21Q269 61 + 21 246 50T223 136Q223 194 243 247T297 341T373 405T461 429Q484 429 504 424T538 408L576 426H597L546 143Q539 101 550 83T592 65Q623 65 649 89T692 157T708 258Q708 332 678 394T590 493T448 530Q348 530 277 487T169 370T132 200Q132 114 166 50T260 -49T400 62 + -84Q464 -84 514 -69T606 -25L619 -48Q565 -82 509 -100T372 -118ZM319 177Q319 129 329 110T362 91Q384 91 408 111T457 167L491 360Q480 374 467 383T433 392Q401 392 376 363T335 284T319 177Z" /> 63 + <glyph unicode="A" glyph-name="A" horiz-adv-x="764" d="M25 0V16L87 50L346 665H416L672 51L740 16V0H452V16L539 51L468 225H229L158 51L241 16V0H25ZM251 280H445L348 518L251 280Z" /> 64 + <glyph unicode="B" glyph-name="B" horiz-adv-x="686" d="M53 0V16L136 57V603L53 644V660H345Q414 660 469 644T555 595T587 512Q587 461 551 419T445 358Q539 345 592 302T646 192Q646 137 610 94T511 25T369 0H53ZM306 605H261V366H312Q380 366 418 397T457 65 + 483Q457 605 306 605ZM343 315H261V55H350Q425 55 468 92T511 186Q511 252 468 283T343 315Z" /> 66 + <glyph unicode="C" glyph-name="C" horiz-adv-x="717" d="M399 -10Q295 -10 219 32T100 148T57 315Q57 388 86 452T165 565T279 642T415 670Q466 670 501 664T564 647T620 622L629 440H611L530 581Q500 601 475 610T417 620Q352 620 300 584T218 483T188 333Q188 67 + 242 217 177T295 77T406 42Q451 42 483 54T543 83L621 206H638L625 41Q583 18 530 4T399 -10Z" /> 68 + <glyph unicode="D" glyph-name="D" horiz-adv-x="791" d="M53 0V16L136 57V603L53 644V660H374Q489 660 569 621T692 514T735 360Q735 282 708 217T631 103T514 27T367 0H53ZM340 605H261V55H355Q434 55 489 88T574 183T604 329Q604 455 537 530T340 605Z" /> 69 + <glyph unicode="E" glyph-name="E" horiz-adv-x="669" d="M53 0V16L136 57V603L53 644V660H575V487H559L490 605H261V368H422L476 453H492V230H476L422 313H261V55H515L587 195H603V0H53Z" /> 70 + <glyph unicode="F" glyph-name="F" horiz-adv-x="624" d="M53 0V16L136 57V603L53 644V660H565V487H549L483 605H261V366H412L466 451H482V232H466L412 315H261V57L359 16V0H53Z" /> 71 + <glyph unicode="G" glyph-name="G" horiz-adv-x="761" d="M398 -10Q326 -10 265 14T157 82T84 185T58 315Q58 391 87 456T168 569T285 643T426 670Q479 670 516 664T583 647T642 622L650 451H633L550 582Q517 603 490 612T428 621Q360 621 305 586T219 489T187 72 + 346Q187 252 219 185T305 82T427 46Q450 46 475 49T525 58V236L436 280V296H723V280L650 234V74Q620 49 578 30T488 1T398 -10Z" /> 73 + <glyph unicode="H" glyph-name="H" horiz-adv-x="825" d="M53 0V16L136 57V603L53 644V660H340V644L261 603V368H564V603L485 644V660H773V644L690 603V57L773 16V0H485V16L564 57V313H261V57L340 16V0H53Z" /> 74 + <glyph unicode="I" glyph-name="I" horiz-adv-x="404" d="M56 0V16L139 57V603L56 644V660H348V644L265 603V57L348 16V0H56Z" /> 75 + <glyph unicode="J" glyph-name="J" horiz-adv-x="415" d="M58 -174Q37 -174 25 -164T12 -139Q12 -134 15 -119L26 -76L115 -93Q134 -69 142 -36T150 50V603L67 644V660H359V644L276 603V243Q276 133 253 54T184 -81Q139 -136 112 -155T58 -174Z" /> 76 + <glyph unicode="K" glyph-name="K" horiz-adv-x="752" d="M53 0V16L136 57V603L53 644V660H345V644L261 603V57L345 16V0H53ZM262 335L537 609L465 644V660H690V644L625 617L380 380L673 44L740 16V0H550L262 335Z" /> 77 + <glyph unicode="L" glyph-name="L" horiz-adv-x="642" d="M53 0V16L136 57V603L53 644V660H355V644L261 603V55H491L567 235H583V0H53Z" /> 78 + <glyph unicode="M" glyph-name="M" horiz-adv-x="980" d="M49 0V16L132 57L152 603L66 644V660H288L501 167L713 660H926V644L841 610L858 57L931 16V0H658V16L736 56L726 538L494 -5H442L210 534L197 56L282 16V0H49Z" /> 79 + <glyph unicode="N" glyph-name="N" horiz-adv-x="809" d="M49 0V16L133 57V602L50 644V660H222L608 204V605L500 644V660H760V644L678 604V-5H626L203 493V56L311 16V0H49Z" /> 80 + <glyph unicode="O" glyph-name="O" horiz-adv-x="796" d="M398 -10Q325 -10 263 15T154 86T83 194T57 330Q57 404 82 466T154 573T262 644T398 670Q471 670 533 645T642 574T713 466T739 330Q739 257 714 195T642 87T534 16T398 -10ZM416 41Q472 41 514 73T581 81 + 163T605 300Q605 397 576 469T496 580T383 620Q327 620 284 589T216 499T191 362Q191 265 221 193T302 81T416 41Z" /> 82 + <glyph unicode="P" glyph-name="P" horiz-adv-x="665" d="M53 0V16L136 57V603L53 644V660H363Q481 660 552 618T624 482Q624 426 587 384T486 317T339 293H261V57L359 16V0H53ZM318 605H261V344H325Q404 344 449 380T494 473Q494 543 449 574T318 605Z" /> 83 + <glyph unicode="Q" glyph-name="Q" horiz-adv-x="796" d="M685 -153Q641 -153 594 -142T490 -99T357 -8Q270 1 203 47T96 166T57 330Q57 404 82 466T154 573T262 644T398 670Q471 670 533 645T642 574T713 466T739 330Q739 251 710 186T628 74T505 6Q561 -40 603 84 + -66T680 -104T745 -115Q767 -115 783 -112T810 -105V-126Q787 -137 756 -145T685 -153ZM416 41Q472 41 514 73T581 163T605 300Q605 397 576 469T496 580T383 620Q327 620 284 589T216 499T191 362Q191 265 221 193T302 81T416 41Z" /> 85 + <glyph unicode="R" glyph-name="R" horiz-adv-x="700" d="M53 0V16L136 57V603L53 644V660H360Q465 660 525 617T586 497Q586 441 549 400T450 335L637 55L707 16V0H520L331 305H261V58L336 16V0H53ZM312 605H261V356H318Q380 356 418 391T457 482Q457 540 419 86 + 572T312 605Z" /> 87 + <glyph unicode="S" glyph-name="S" horiz-adv-x="555" d="M268 -10Q204 -10 157 2T63 39L53 206H70L131 91Q193 40 261 40Q317 40 351 67T385 146Q385 184 368 207T314 250T221 296Q150 330 112 369T74 476Q74 513 92 548T143 610T215 654T296 670Q346 670 389 88 + 659T473 626L483 466H467L406 576Q377 598 352 609T294 620Q252 620 225 592T197 521Q197 476 227 449T327 388Q388 360 427 333T485 272T504 188Q504 140 484 104T429 42T352 3T268 -10Z" /> 89 + <glyph unicode="T" glyph-name="T" horiz-adv-x="690" d="M178 0V16L282 57V605H131L56 448H40V660H650V448H634L559 605H408V57L511 16V0H178Z" /> 90 + <glyph unicode="U" glyph-name="U" horiz-adv-x="826" d="M416 -10Q334 -10 271 18T172 108T136 272V603L53 644V660H345V644L262 603V278Q262 195 288 147T357 79T455 58Q499 58 536 76T597 135T620 243V605L524 644V660H773V644L690 604V272Q690 171 655 109T558 91 + 18T416 -10Z" /> 92 + <glyph unicode="V" glyph-name="V" horiz-adv-x="754" d="M356 -5L96 607L26 644V660H312V644L228 606L410 174H416L598 607L515 644V660H728V644L671 613L405 -5H356Z" /> 93 + <glyph unicode="W" glyph-name="W" horiz-adv-x="1017" d="M278 -5L89 605L13 644V660H303V644L218 606L338 202H343L516 665H566L741 202H746L865 606L780 644V660H1004V644L936 608L745 -5H696L516 458H510L327 -5H278Z" /> 94 + <glyph unicode="X" glyph-name="X" horiz-adv-x="764" d="M30 0V16L106 55L316 319L108 608L43 644V660H340V644L261 606V601L406 403L563 601V605L486 644V660H710V644L645 609L441 356L663 56L734 16V0H432V16L504 53V57L351 270L184 58L183 54L264 16V0H30Z" /> 95 + <glyph unicode="Y" glyph-name="Y" horiz-adv-x="739" d="M221 0V16L311 57V308L91 608L26 644V660H308V644L233 608V604L409 365L568 602V606L492 644V660H713V644L648 611L437 309V57L526 16V0H221Z" /> 96 + <glyph unicode="Z" glyph-name="Z" horiz-adv-x="678" d="M86 0V29L428 605H184L114 465H98V660H581V631L239 55H504L576 205H592V0H86Z" /> 97 + <glyph unicode="[" glyph-name="bracketleft" horiz-adv-x="257" d="M61 -150V740H245V715H129V-125H245V-150H61Z" /> 98 + <glyph unicode="\" glyph-name="backslash" horiz-adv-x="250" d="M200 -120L0 750H50L250 -120H200Z" /> 99 + <glyph unicode="]" glyph-name="bracketright" horiz-adv-x="257" d="M12 -150V-125H128V715H12V740H196V-150H12Z" /> 100 + <glyph unicode="^" glyph-name="asciicircum" horiz-adv-x="536" d="M58 250L243 670H293L478 250H419L268 593L116 250H58Z" /> 101 + <glyph unicode="_" glyph-name="underscore" horiz-adv-x="500" d="M25 -140V-90H475V-140H25Z" /> 102 + <glyph unicode="`" glyph-name="grave" horiz-adv-x="272" d="M227 530H210L29 689V707H149L227 530Z" /> 103 + <glyph unicode="a" glyph-name="a" horiz-adv-x="504" d="M153 -10Q102 -10 71 20T39 100Q39 152 73 185T197 241L294 265V331Q294 368 271 391T212 414Q198 414 184 411T153 400V302H80Q63 302 57 310T50 338Q50 367 77 396T148 444T246 464Q324 464 367 433T410 104 + 344V71Q417 64 429 60T461 55H494L497 52V43Q485 23 460 7T396 -10Q359 -10 336 7T301 55H294Q267 25 231 8T153 -10ZM149 126Q149 95 167 79T219 63Q254 63 294 80V227Q231 214 200 200T159 167T149 126Z" /> 105 + <glyph unicode="b" glyph-name="b" horiz-adv-x="573" d="M95 -6V636L33 685V695L201 750H211V403H216Q287 464 362 464Q409 464 447 438T509 363T532 247Q532 166 501 109T416 21T298 -10Q213 -10 151 24L105 -6H95ZM291 387Q251 387 211 363V77Q249 41 304 41Q333 106 + 41 359 57T401 113T417 223Q417 307 379 347T291 387Z" /> 107 + <glyph unicode="c" glyph-name="c" horiz-adv-x="482" d="M256 -10Q192 -10 144 17T70 92T43 206Q43 282 76 340T168 431T298 464Q346 464 378 449T426 415T443 382Q443 366 424 352L386 321L301 421Q294 422 288 422Q247 422 217 399T170 338T154 253Q154 173 108 + 195 129T312 84Q341 84 372 92T440 119H443V105Q419 57 371 24T256 -10Z" /> 109 + <glyph unicode="d" glyph-name="d" horiz-adv-x="573" d="M208 -10Q161 -10 122 15T60 86T37 194Q37 279 68 339T154 431T275 464Q323 464 359 455V636L292 686V695L465 750H475V45L545 16V0H369L360 50H354Q316 19 282 5T208 -10ZM153 222Q153 142 191 105T279 110 + 67Q319 67 359 91V377Q341 394 322 403T278 412Q244 412 216 395T170 337T153 222Z" /> 111 + <glyph unicode="e" glyph-name="e" horiz-adv-x="491" d="M260 -10Q159 -10 101 49T43 206Q43 282 74 340T159 431T276 464Q356 464 401 409T447 254L443 249H155Q155 205 171 168T222 107T318 84Q346 84 378 92T445 119H448V105Q424 56 375 23T260 -10ZM256 419Q216 112 + 419 191 387T158 294H350Q341 363 318 391T256 419Z" /> 113 + <glyph unicode="f" glyph-name="f" horiz-adv-x="359" d="M44 0V16L109 48V408H32V454H109V481Q109 522 131 568T190 656T271 723T361 749Q389 749 401 739T413 715Q413 711 412 706T410 694L399 647L274 679Q251 658 238 616T225 510V454H349V408H225V47L299 16V0H44Z" /> 114 + <glyph unicode="g" glyph-name="g" horiz-adv-x="543" d="M251 163Q224 163 198 170Q172 157 164 143T155 114Q155 96 171 85T222 74H344Q420 74 456 40T493 -60Q493 -105 465 -146T382 -213T251 -240Q161 -240 118 -201T75 -108Q75 -87 85 -65L148 -20V-18Q113 115 + -7 95 18T76 78Q76 92 79 105T88 133L164 183Q125 202 102 236T78 314Q78 356 100 390T162 443T251 463Q275 463 301 457T342 443L497 453V395L390 403L388 401Q404 385 414 362T425 314Q425 271 402 237T340 183T251 163ZM258 205Q290 205 307 234T325 311Q325 116 + 363 303 392T247 422Q215 422 197 394T178 316Q178 264 200 235T258 205ZM153 -86Q153 -138 185 -171T268 -205Q326 -205 361 -175T397 -102Q397 -66 378 -46T301 -26H205Q198 -26 193 -26Q172 -33 163 -49T153 -86Z" /> 117 + <glyph unicode="h" glyph-name="h" horiz-adv-x="603" d="M38 0V16L103 48V636L41 685V695L209 750H219V390H223Q260 426 294 445T373 464Q433 464 471 434T510 343V48L575 16V0H335V16L394 46V300Q394 338 371 358T311 379Q288 379 262 371T219 349V46L278 16V0H38Z" /> 118 + <glyph unicode="i" glyph-name="i" horiz-adv-x="325" d="M162 571Q133 571 114 590T95 639Q95 668 114 687T162 706Q191 706 210 687T230 639Q230 610 211 591T162 571ZM42 0V16L107 48V350L45 398V409L213 464H223V48L284 16V0H42Z" /> 119 + <glyph unicode="j" glyph-name="j" horiz-adv-x="324" d="M161 571Q132 571 113 590T94 639Q94 668 113 687T161 706Q190 706 209 687T229 639Q229 610 210 591T161 571ZM43 -230V-213Q68 -191 81 -169T100 -115T106 -37V350L44 398V409L212 464H222V14Q222 -42 120 + 206 -85T152 -163T43 -230Z" /> 121 + <glyph unicode="k" glyph-name="k" horiz-adv-x="584" d="M38 0V16L103 48V636L41 685V695L209 750H219V259L373 396V402L324 438V454H527V438L463 403L315 288L506 50L570 16V0H401L367 52L219 239V46L286 16V0H38Z" /> 122 + <glyph unicode="l" glyph-name="l" horiz-adv-x="313" d="M35 0V16L101 48V636L38 685V695L207 750H216V48L277 16V0H35Z" /> 123 + <glyph unicode="m" glyph-name="m" horiz-adv-x="884" d="M38 0V16L103 48V350L41 395V405L199 464H209L218 390H223Q259 426 292 445T369 464Q418 464 452 443T498 379H501Q541 420 576 442T659 464Q717 464 754 434T791 343V48L856 16V0H616V16L675 48V300Q675 124 + 338 653 358T596 379Q569 379 544 370T502 343V48L560 16V0H330V16L386 48V300Q386 338 364 358T307 379Q285 379 260 371T219 349V48L274 16V0H38Z" /> 125 + <glyph unicode="n" glyph-name="n" horiz-adv-x="603" d="M38 0V16L103 48V350L41 395V405L199 464H209L218 390H223Q260 426 294 445T373 464Q433 464 471 434T510 343V48L575 16V0H335V16L394 48V300Q394 338 371 358T311 379Q288 379 262 371T219 349V48L278 16V0H38Z" /> 126 + <glyph unicode="o" glyph-name="o" horiz-adv-x="544" d="M272 -10Q203 -10 151 21T70 107T41 227Q41 293 70 346T151 432T272 464Q341 464 393 432T474 347T503 227Q503 161 474 107T393 22T272 -10ZM284 37Q318 37 339 61T371 124T381 207Q381 261 369 309T331 127 + 387T262 418Q228 418 206 394T174 331T163 248Q163 194 175 146T215 67T284 37Z" /> 128 + <glyph unicode="p" glyph-name="p" horiz-adv-x="580" d="M38 -230V-214L103 -182V346L41 395V405L199 464H209L217 403H223Q261 435 295 449T370 464Q417 464 455 438T517 363T540 247Q540 166 509 109T425 21T310 -10Q258 -10 219 0V-182L294 -214V-230H38ZM299 129 + 387Q259 387 219 363V77Q258 41 319 41Q346 41 370 57T409 111T425 216Q425 304 387 345T299 387Z" /> 130 + <glyph unicode="q" glyph-name="q" horiz-adv-x="571" d="M286 -230V-214L361 -185V49H355Q319 18 284 4T210 -10Q163 -10 125 15T63 86T40 194Q40 279 72 339T159 431T283 464Q318 464 346 458T402 440L468 464H477V-183L542 -214V-230H286ZM156 222Q156 142 131 + 193 105T281 67Q299 67 319 72T361 91V340Q361 367 360 375T351 389Q335 402 316 407T274 412Q242 412 215 395T172 336T156 222Z" /> 132 + <glyph unicode="r" glyph-name="r" horiz-adv-x="413" d="M41 0V16L106 48V346L44 395V405L202 464H211L221 371H225Q260 421 290 441T350 462Q375 462 389 451T404 418Q404 412 400 397L388 350L290 372Q250 358 221 321V48L298 16V0H41Z" /> 133 + <glyph unicode="s" glyph-name="s" horiz-adv-x="435" d="M211 -10Q162 -10 127 -1T57 25V140H71L105 86Q139 31 207 31Q246 31 267 47T288 93Q288 117 276 132T239 159T174 188Q120 211 93 240T66 317Q66 358 87 391T145 444T225 464Q294 464 356 432V317H341L309 134 + 366Q291 395 271 408T225 421Q193 421 174 405T155 361Q155 332 177 315T254 275Q283 263 310 249T356 208T375 135Q375 89 351 57T288 7T211 -10Z" /> 135 + <glyph unicode="t" glyph-name="t" horiz-adv-x="373" d="M216 -10Q162 -10 126 16T89 89V408H13V418L103 454L191 564H205V454H341V408H205V111Q205 86 227 74T280 61Q299 61 314 63T343 68L346 67V53Q332 29 298 10T216 -10Z" /> 136 + <glyph unicode="u" glyph-name="u" horiz-adv-x="578" d="M221 -10Q161 -10 123 20T85 111V406L20 438V454H201V154Q201 115 224 95T284 75Q306 75 331 82T374 105V406L309 438V454H490V46L556 16V0H374V63H370Q333 27 299 9T221 -10Z" /> 137 + <glyph unicode="v" glyph-name="v" horiz-adv-x="478" d="M221 0L34 409L-30 438V454H234V438L160 408V404L274 158H278L379 399V404L312 438V454H508V438L436 403L252 0H221Z" /> 138 + <glyph unicode="w" glyph-name="w" horiz-adv-x="738" d="M195 0L34 409L-30 438V454H234V438L160 408V404L252 159H257L383 454H414L543 160H547L639 399V404L572 438V454H769V438L697 403L525 0H494L362 294L226 0H195Z" /> 139 + <glyph unicode="x" glyph-name="x" horiz-adv-x="527" d="M21 0V16L74 49L209 215L69 409L24 438V454H268V438L208 410V404L290 289L370 398V404L317 438V454H490V438L437 405L315 255L466 45L507 16V0H271V16L328 44V50L234 180L144 56V50L202 16V0H21Z" /> 140 + <glyph unicode="y" glyph-name="y" horiz-adv-x="478" d="M-31 454H230V438L160 408V404L280 149H285L381 399V404L315 438V454H508V438L436 404L221 -119Q201 -166 183 -192T142 -229T89 -240Q65 -240 57 -233T48 -199V-146H151Q168 -125 183 -96T222 -10L226 141 + 1L33 409L-31 438V454Z" /> 142 + <glyph unicode="z" glyph-name="z" horiz-adv-x="494" d="M60 0V19L279 408H127L76 302H59L70 464H75Q88 460 107 457T143 454H423V435L204 46H371L414 158H431V0H60Z" /> 143 + <glyph unicode="{" glyph-name="braceleft" horiz-adv-x="257" d="M222 -150Q139 -150 102 -113T64 -17Q64 25 76 61T100 131T112 199Q112 232 94 257T26 283H1V308H26Q75 308 93 333T112 391Q112 425 100 459T76 529T64 607Q64 666 101 703T222 740H245V715H218Q175 144 + 715 154 690T132 625Q132 586 143 551T164 482T175 413Q175 376 155 346T85 295Q135 274 155 244T175 177Q175 143 165 109T143 40T132 -35Q132 -75 153 -100T218 -125H245V-150H222Z" /> 145 + <glyph unicode="|" glyph-name="bar" horiz-adv-x="250" d="M100 -250V750H150V-250H100Z" /> 146 + <glyph unicode="}" glyph-name="braceright" horiz-adv-x="257" d="M35 -150H12V-125H39Q83 -125 104 -100T125 -35Q125 4 115 39T93 109T82 177Q82 214 102 244T172 295Q122 316 102 346T82 413Q82 448 93 481T114 550T125 625Q125 665 104 690T39 715H12V740H35Q118 147 + 740 155 703T193 607Q193 566 181 530T157 459T145 391Q145 358 163 333T231 308H256V283H231Q182 283 164 258T145 199Q145 165 157 131T181 61T193 -17Q193 -76 156 -113T35 -150Z" /> 148 + <glyph unicode="~" glyph-name="asciitilde" horiz-adv-x="536" d="M58 201Q58 269 89 306T172 343Q208 343 233 330T279 298T320 267T366 253Q396 253 410 275T424 343H478Q478 275 448 238T364 201Q328 201 302 214T256 246T215 277T170 291Q143 291 128 268T112 149 + 201H58Z" /> 150 + <glyph unicode="&#xa0;" glyph-name="uni00A0" horiz-adv-x="250" /> 151 + <glyph unicode="&#xa1;" glyph-name="exclamdown" horiz-adv-x="261" d="M131 326Q103 326 83 346T62 395Q62 422 82 443T131 464Q158 464 179 443T200 395Q200 367 179 347T131 326ZM62 -201L117 248H144L199 -201H62Z" /> 152 + <glyph unicode="&#xa2;" glyph-name="cent" horiz-adv-x="536" d="M183 26L202 105Q137 125 100 179T63 310Q63 386 96 444T187 534T316 567L333 635H373L356 565Q409 557 436 533T463 489Q463 478 459 471T445 455L406 423L341 507L266 202Q295 191 332 191Q360 153 + 191 392 201T460 231H463V202Q438 158 390 126T276 94Q258 94 240 96L223 26H183ZM174 357Q174 315 188 280T231 222L306 525Q265 525 236 502T190 441T174 357Z" /> 154 + <glyph unicode="&#xa3;" glyph-name="sterling" horiz-adv-x="536" d="M44 314V354H122V457Q122 499 142 537T197 605T273 652T360 669Q416 669 450 649T485 605Q485 592 473 579L434 532L341 624H337Q293 624 266 590T238 493V354H393V314H238V267Q238 235 226 155 + 209T176 141L139 100H393L465 197H483L444 0H41L35 30L90 107Q104 127 111 140T120 166T122 199V314H44Z" /> 156 + <glyph unicode="&#xa4;" glyph-name="currency" horiz-adv-x="536" d="M57 99L135 178Q121 198 113 222T105 272Q105 298 113 322T135 366L57 445L96 483L174 405Q194 419 218 427T268 435Q294 435 318 427T361 405L440 483L479 445L400 366Q414 346 422 322T431 157 + 272Q431 246 423 222T400 178L479 99L440 61L361 139Q341 125 318 117T268 109Q242 109 218 117T174 139L96 61L57 99ZM268 159Q314 159 345 191T376 272Q376 321 345 353T268 385Q222 385 191 353T160 272Q160 223 191 191T268 159Z" /> 158 + <glyph unicode="&#xa5;" glyph-name="yen" horiz-adv-x="536" d="M122 0V16L208 57V238H75V278H208V388H75V428H184L45 622L2 644V660H254V644L192 620V618L306 448L407 614V617L343 644V660H534V644L483 623L351 428H461V388H329V278H461V238H329V57L415 16V0H122Z" /> 159 + <glyph unicode="&#xa6;" glyph-name="brokenbar" horiz-adv-x="250" d="M100 350V750H150V350H100ZM100 -250V150H150V-250H100Z" /> 160 + <glyph unicode="&#xa7;" glyph-name="section" horiz-adv-x="520" d="M310 670Q348 670 378 660T427 634T445 603Q445 595 436 585L396 539L296 626Q251 625 221 600T191 540Q191 518 202 501T245 469T334 440Q422 418 454 383T487 296Q487 246 459 210T379 152Q404 161 + 133 414 108T425 49Q425 3 395 -35T315 -97T210 -121Q172 -121 140 -110T89 -83T69 -51Q69 -42 79 -32L119 14L222 -79Q270 -78 301 -54T333 4Q333 28 322 46T279 80T189 111Q100 132 66 169T31 261Q31 310 60 345T138 401Q117 419 108 443T99 499Q99 547 128 586T206 162 + 647T310 670ZM268 198Q308 189 336 177Q362 184 380 203T398 248Q398 272 387 290T344 323T255 350Q205 361 172 378Q147 367 136 349T124 305Q124 280 135 261T178 227T268 198Z" /> 163 + <glyph unicode="&#xa8;" glyph-name="dieresis" horiz-adv-x="390" d="M99 563Q75 563 60 578T44 617Q44 640 59 655T99 671Q122 671 137 656T153 617Q153 594 138 579T99 563ZM291 563Q267 563 252 578T237 617Q237 640 252 655T291 671Q315 671 330 656T345 164 + 617Q345 594 330 579T291 563Z" /> 165 + <glyph unicode="&#xa9;" glyph-name="copyright" horiz-adv-x="806" d="M403 -10Q331 -10 271 15T165 86T95 194T70 330Q70 404 95 466T165 573T270 644T403 670Q475 670 535 645T641 574T711 466T736 330Q736 257 711 195T641 87T536 16T403 -10ZM403 28Q487 166 + 28 553 67T657 175T696 330Q696 417 658 485T553 592T403 632Q319 632 253 593T149 485T110 330Q110 243 148 175T253 68T403 28ZM416 129Q319 129 264 183T209 324Q209 383 238 432T317 510T426 540Q471 540 498 532T552 509L556 394H527L488 474Q471 485 458 167 + 490T427 495Q370 495 334 452T297 341Q297 263 332 219T420 175Q442 175 459 180T492 193L534 266H563L555 162Q526 148 494 139T416 129Z" /> 168 + <glyph unicode="&#xaa;" glyph-name="ordfeminine" horiz-adv-x="408" d="M144 417Q119 417 101 433T82 477Q82 506 100 522T168 552L218 565V599Q218 640 180 640Q173 640 166 639T151 633V582H106Q91 582 91 598Q91 615 107 631T147 658T199 669Q245 669 270 169 + 652T295 602V464Q304 455 322 455H337L339 453V445Q333 434 319 426T280 417Q236 417 222 452H218Q204 437 185 427T144 417ZM156 492Q156 477 165 469T190 460Q204 460 218 466V543Q181 535 169 523T156 492ZM85 291V343H326V291H85Z" /> 170 + <glyph unicode="&#xab;" glyph-name="guillemotleft" horiz-adv-x="451" d="M225 70L24 247L225 424H241L132 247L241 70H225ZM412 70L211 247L412 424H428L319 247L428 70H412Z" /> 171 + <glyph unicode="&#xac;" glyph-name="logicalnot" horiz-adv-x="536" d="M424 62V246H58V298H478V62H424Z" /> 172 + <glyph unicode="&#xae;" glyph-name="registered" horiz-adv-x="547" d="M274 232Q212 232 163 260T86 338T58 451Q58 513 86 562T163 641T274 670Q336 670 384 641T461 563T489 451Q489 388 461 339T385 261T274 232ZM274 263Q327 263 367 288T430 355T453 451Q453 173 + 504 431 546T368 613T274 638Q220 638 180 614T117 547T94 451Q94 397 116 355T179 288T274 263ZM162 325V332L193 348V558L162 574V580H276Q316 580 339 564T363 517Q363 496 348 479T309 452L364 345L390 332V325H318L262 442H242V348L272 332V325H162ZM257 557H242V465H259Q280 174 + 465 295 477T311 512Q311 536 296 546T257 557Z" /> 175 + <glyph unicode="&#xaf;" glyph-name="overscore" horiz-adv-x="370" d="M45 589V645H325V589H45Z" /> 176 + <glyph unicode="&#xb0;" glyph-name="degree" horiz-adv-x="500" d="M250 410Q213 410 184 428T137 475T120 540Q120 575 137 604T183 652T250 670Q288 670 317 652T363 605T380 540Q380 505 363 476T317 428T250 410ZM250 452Q285 452 309 475T334 540Q334 581 177 + 310 605T250 629Q215 629 191 605T166 540Q166 499 190 476T250 452Z" /> 178 + <glyph unicode="&#xb1;" glyph-name="plusminus" horiz-adv-x="536" d="M240 152V286H58V338H240V482H296V338H478V286H296V152H240ZM58 61V113H478V61H58Z" /> 179 + <glyph unicode="&#xb2;" glyph-name="twosuperior" horiz-adv-x="331" d="M27 505V527Q82 588 115 630T167 702T191 756T198 803Q198 831 185 847T144 864Q118 864 94 850V767H41Q31 767 26 771T20 790Q20 815 41 840T97 883T173 900Q230 900 263 870T296 794Q296 180 + 771 289 751T262 707T202 650T98 566H266L296 625H311L305 505H27Z" /> 181 + <glyph unicode="&#xb3;" glyph-name="threesuperior" horiz-adv-x="333" d="M156 495Q114 495 84 507T37 539T20 577Q20 590 25 594T42 598H93V540Q114 528 146 528Q180 528 197 548T215 602Q215 643 195 665T123 687H90V717H104Q144 717 169 739T195 805Q195 182 + 864 143 864Q133 864 122 862T101 854V785H50Q39 785 34 789T28 810Q28 830 47 851T97 886T169 900Q224 900 258 876T293 808Q293 772 264 747T183 713Q313 700 313 610Q313 577 293 551T236 510T156 495Z" /> 183 + <glyph unicode="&#xb4;" glyph-name="acute" horiz-adv-x="272" d="M44 530L122 707H242V689L62 530H44Z" /> 184 + <glyph unicode="&#xb5;" glyph-name="micro" horiz-adv-x="561" d="M232 -10Q208 -10 186 -3T145 22L181 -210L78 -230H76V454H192V154Q192 115 215 95T274 75Q296 75 322 82T365 105V454H481V71Q488 64 500 60T532 55H564L568 52V43Q556 23 531 7T467 -10Q427 185 + -10 403 9T369 63H362Q330 28 301 9T232 -10Z" /> 186 + <glyph unicode="&#xb6;" glyph-name="paragraph" horiz-adv-x="507" d="M265 -110V359H223Q169 359 127 379T60 434T35 514Q35 590 82 625T207 660H320V-110H265ZM376 -110V660H514V644L431 603V-110H376Z" /> 187 + <glyph unicode="&#xb7;" glyph-name="middot" horiz-adv-x="250" d="M125 203Q97 203 77 223T56 271Q56 299 76 319T125 340Q153 340 173 320T194 271Q194 243 174 223T125 203Z" /> 188 + <glyph unicode="&#xb8;" glyph-name="cedilla" horiz-adv-x="500" d="M240 -240Q186 -240 158 -214V-198H162Q186 -209 221 -209Q249 -209 262 -199T276 -171Q276 -149 256 -135T176 -103L234 23H271L238 -48Q296 -65 319 -88T342 -151Q342 -189 315 -214T240 -240Z" /> 189 + <glyph unicode="&#xb9;" glyph-name="onesuperior" horiz-adv-x="283" d="M59 505V520L115 542V819L15 770V802L160 890H208V542L258 520V505H59Z" /> 190 + <glyph unicode="&#xba;" glyph-name="ordmasculine" horiz-adv-x="408" d="M204 417Q146 417 111 454T75 543Q75 595 110 632T204 669Q262 669 297 632T333 543Q333 491 297 454T204 417ZM210 443Q231 443 242 468T253 532Q253 579 239 611T199 643Q177 643 167 191 + 617T156 554Q156 506 169 475T210 443ZM85 291V343H326V291H85Z" /> 192 + <glyph unicode="&#xbb;" glyph-name="guillemotright" horiz-adv-x="451" d="M24 70L132 247L24 424H39L241 247L39 70H24ZM211 70L319 247L211 424H226L428 247L226 70H211Z" /> 193 + <glyph unicode="&#xbc;" glyph-name="onequarter" horiz-adv-x="775" d="M98 355V370L154 392V669L54 620V652L199 740H247V392L297 370V355H98ZM145 17L614 750L655 723L186 -10L145 17ZM513 0V15L569 40V101H405V119L597 385H662V147H732V101H662V40L712 15V0H513ZM467 194 + 147H569V294L467 147Z" /> 195 + <glyph unicode="&#xbd;" glyph-name="onehalf" horiz-adv-x="775" d="M92 355V370L148 392V669L48 620V652L193 740H241V392L291 370V355H92ZM116 17L585 750L626 723L157 -10L116 17ZM437 0V22Q492 83 525 125T577 197T601 251T608 298Q608 326 595 342T554 359Q528 196 + 359 504 345V262H451Q441 262 436 266T430 285Q430 310 451 335T507 378T583 395Q640 395 673 365T706 289Q706 266 699 246T672 202T612 145T508 61H676L706 120H721L715 0H437Z" /> 197 + <glyph unicode="&#xbe;" glyph-name="threequarters" horiz-adv-x="775" d="M169 345Q127 345 97 357T50 389T33 427Q33 440 38 444T55 448H106V390Q127 378 159 378Q193 378 210 398T228 452Q228 493 208 515T136 537H103V567H117Q157 567 182 589T208 655Q208 198 + 714 156 714Q146 714 135 712T114 704V635H63Q52 635 47 639T41 660Q41 680 60 701T110 736T182 750Q237 750 271 726T306 658Q306 622 277 597T196 563Q326 550 326 460Q326 427 306 401T249 360T169 345ZM166 17L635 750L676 723L207 -10L166 17ZM538 0V15L594 199 + 40V101H430V119L622 385H687V147H757V101H687V40L737 15V0H538ZM492 147H594V294L492 147Z" /> 200 + <glyph unicode="&#xbf;" glyph-name="questiondown" horiz-adv-x="479" d="M269 464Q297 464 317 443T338 395Q338 367 318 347T269 326Q242 326 222 346T201 395Q201 422 221 443T269 464ZM282 248L311 94Q234 76 192 41T150 -50Q150 -106 180 -133T256 -161Q276 201 + -161 296 -156T336 -137V-25H415Q432 -25 438 -33T445 -64Q445 -99 417 -133T340 -189T233 -211Q176 -211 134 -188T68 -124T44 -34Q44 33 93 85T244 171L255 248H282Z" /> 202 + <glyph unicode="&#xc0;" glyph-name="Agrave" horiz-adv-x="764" d="M25 0V16L87 50L346 665H416L672 51L740 16V0H452V16L539 51L468 225H229L158 51L241 16V0H25ZM251 280H445L348 518L251 280ZM390 710H371L198 842V862H315L390 710Z" /> 203 + <glyph unicode="&#xc1;" glyph-name="Aacute" horiz-adv-x="764" d="M25 0V16L87 50L346 665H416L672 51L740 16V0H452V16L539 51L468 225H229L158 51L241 16V0H25ZM251 280H445L348 518L251 280ZM373 710L448 862H565V842L393 710H373Z" /> 204 + <glyph unicode="&#xc2;" glyph-name="Acircumflex" horiz-adv-x="764" d="M25 0V16L87 50L346 665H416L672 51L740 16V0H452V16L539 51L468 225H229L158 51L241 16V0H25ZM251 280H445L348 518L251 280ZM228 710L362 862H402L537 710H519L382 779L245 710H228Z" /> 205 + <glyph unicode="&#xc3;" glyph-name="Atilde" horiz-adv-x="764" d="M25 0V16L87 50L346 665H416L672 51L740 16V0H452V16L539 51L468 225H229L158 51L241 16V0H25ZM251 280H445L348 518L251 280ZM223 724Q239 774 254 800T286 835T323 845Q351 845 374 832T420 206 + 805T464 792Q482 792 496 802T523 845H542Q526 794 511 768T478 733T441 724Q413 724 390 737T344 763T300 776Q282 776 269 767T241 724H223Z" /> 207 + <glyph unicode="&#xc4;" glyph-name="Adieresis" horiz-adv-x="764" d="M25 0V16L87 50L346 665H416L672 51L740 16V0H452V16L539 51L468 225H229L158 51L241 16V0H25ZM251 280H445L348 518L251 280ZM286 731Q262 731 247 747T231 786Q231 809 246 824T286 840Q309 208 + 840 324 825T340 786Q340 763 325 747T286 731ZM478 731Q454 731 439 747T424 786Q424 809 439 824T478 840Q502 840 517 825T532 786Q532 763 517 747T478 731Z" /> 209 + <glyph unicode="&#xc5;" glyph-name="Aring" horiz-adv-x="764" d="M25 0V16L87 50L346 665H416L672 51L740 16V0H452V16L539 51L468 225H229L158 51L241 16V0H25ZM251 280H445L348 518L251 280ZM382 710Q343 710 319 734T294 794Q294 830 318 854T382 878Q421 210 + 878 445 854T470 794Q470 758 446 734T382 710ZM387 734Q418 734 418 787Q418 854 378 854Q363 854 355 842T347 801Q347 734 387 734Z" /> 211 + <glyph unicode="&#xc6;" glyph-name="AE" horiz-adv-x="1010" d="M464 660H915V487H899L830 605H602V368H763L816 453H832V230H816L763 313H602V55H855L927 195H943V0H393V16L476 57V232H269L158 50L237 16V0H22V16L86 51L464 660ZM302 287H476V573L302 287Z" /> 212 + <glyph unicode="&#xc7;" glyph-name="Ccedilla" horiz-adv-x="717" d="M378 -209Q407 -209 420 -199T433 -171Q433 -149 413 -135T334 -103L377 -9Q279 -5 208 38T97 153T57 315Q57 388 86 452T165 565T279 642T415 670Q466 670 501 664T564 647T620 622L629 440H611L530 213 + 581Q500 601 475 610T417 620Q352 620 300 584T218 483T188 333Q188 242 217 177T295 77T406 42Q451 42 483 54T543 83L621 206H638L625 41Q585 19 535 5T413 -10L396 -48Q453 -65 476 -88T499 -151Q499 -189 472 -214T398 -240Q343 -240 316 -214V-198H319Q344 214 + -209 378 -209Z" /> 215 + <glyph unicode="&#xc8;" glyph-name="Egrave" horiz-adv-x="669" d="M53 0V16L136 57V603L53 644V660H575V487H559L490 605H261V368H422L476 453H492V230H476L422 313H261V55H515L587 195H603V0H53ZM354 710H335L162 842V862H279L354 710Z" /> 216 + <glyph unicode="&#xc9;" glyph-name="Eacute" horiz-adv-x="669" d="M53 0V16L136 57V603L53 644V660H575V487H559L490 605H261V368H422L476 453H492V230H476L422 313H261V55H515L587 195H603V0H53ZM337 710L412 862H529V842L357 710H337Z" /> 217 + <glyph unicode="&#xca;" glyph-name="Ecircumflex" horiz-adv-x="669" d="M53 0V16L136 57V603L53 644V660H575V487H559L490 605H261V368H422L476 453H492V230H476L422 313H261V55H515L587 195H603V0H53ZM192 710L326 862H366L501 710H483L346 779L209 710H192Z" /> 218 + <glyph unicode="&#xcb;" glyph-name="Edieresis" horiz-adv-x="669" d="M53 0V16L136 57V603L53 644V660H575V487H559L490 605H261V368H422L476 453H492V230H476L422 313H261V55H515L587 195H603V0H53ZM250 731Q226 731 211 747T195 786Q195 809 210 824T250 840Q273 219 + 840 288 825T304 786Q304 763 289 747T250 731ZM442 731Q418 731 403 747T388 786Q388 809 403 824T442 840Q466 840 481 825T496 786Q496 763 481 747T442 731Z" /> 220 + <glyph unicode="&#xcc;" glyph-name="Igrave" horiz-adv-x="404" d="M56 0V16L139 57V603L56 644V660H348V644L265 603V57L348 16V0H56ZM210 710H191L18 842V862H135L210 710Z" /> 221 + <glyph unicode="&#xcd;" glyph-name="Iacute" horiz-adv-x="404" d="M56 0V16L139 57V603L56 644V660H348V644L265 603V57L348 16V0H56ZM193 710L268 862H385V842L213 710H193Z" /> 222 + <glyph unicode="&#xce;" glyph-name="Icircumflex" horiz-adv-x="404" d="M56 0V16L139 57V603L56 644V660H348V644L265 603V57L348 16V0H56ZM48 710L182 862H222L357 710H339L202 779L65 710H48Z" /> 223 + <glyph unicode="&#xcf;" glyph-name="Idieresis" horiz-adv-x="404" d="M56 0V16L139 57V603L56 644V660H348V644L265 603V57L348 16V0H56ZM106 731Q82 731 67 747T51 786Q51 809 66 824T106 840Q129 840 144 825T160 786Q160 763 145 747T106 731ZM298 731Q274 224 + 731 259 747T244 786Q244 809 259 824T298 840Q322 840 337 825T352 786Q352 763 337 747T298 731Z" /> 225 + <glyph unicode="&#xd0;" glyph-name="Eth" horiz-adv-x="791" d="M53 0V16L136 57V316H53V368H136V603L53 644V660H374Q489 660 569 621T692 514T735 360Q735 282 708 217T631 103T514 27T367 0H53ZM423 316H261V55H355Q434 55 489 88T574 183T604 329Q604 455 226 + 537 530T340 605H261V368H423V316Z" /> 227 + <glyph unicode="&#xd1;" glyph-name="Ntilde" horiz-adv-x="809" d="M49 0V16L133 57V602L50 644V660H222L608 204V605L500 644V660H760V644L678 604V-5H626L203 493V56L311 16V0H49ZM253 724Q269 774 284 800T316 835T353 845Q381 845 404 832T450 805T494 792Q512 228 + 792 526 802T553 845H572Q556 794 541 768T508 733T471 724Q443 724 420 737T374 763T330 776Q312 776 299 767T271 724H253Z" /> 229 + <glyph unicode="&#xd2;" glyph-name="Ograve" horiz-adv-x="796" d="M398 -10Q325 -10 263 15T154 86T83 194T57 330Q57 404 82 466T154 573T262 644T398 670Q471 670 533 645T642 574T713 466T739 330Q739 257 714 195T642 87T534 16T398 -10ZM416 41Q472 41 230 + 514 73T581 163T605 300Q605 397 576 469T496 580T383 620Q327 620 284 589T216 499T191 362Q191 265 221 193T302 81T416 41ZM398 710H379L206 842V862H323L398 710Z" /> 231 + <glyph unicode="&#xd3;" glyph-name="Oacute" horiz-adv-x="796" d="M398 -10Q325 -10 263 15T154 86T83 194T57 330Q57 404 82 466T154 573T262 644T398 670Q471 670 533 645T642 574T713 466T739 330Q739 257 714 195T642 87T534 16T398 -10ZM416 41Q472 41 232 + 514 73T581 163T605 300Q605 397 576 469T496 580T383 620Q327 620 284 589T216 499T191 362Q191 265 221 193T302 81T416 41ZM382 710L457 862H574V842L402 710H382Z" /> 233 + <glyph unicode="&#xd4;" glyph-name="Ocircumflex" horiz-adv-x="796" d="M398 -10Q325 -10 263 15T154 86T83 194T57 330Q57 404 82 466T154 573T262 644T398 670Q471 670 533 645T642 574T713 466T739 330Q739 257 714 195T642 87T534 16T398 -10ZM416 41Q472 234 + 41 514 73T581 163T605 300Q605 397 576 469T496 580T383 620Q327 620 284 589T216 499T191 362Q191 265 221 193T302 81T416 41ZM236 710L370 862H410L545 710H527L390 779L253 710H236Z" /> 235 + <glyph unicode="&#xd5;" glyph-name="Otilde" horiz-adv-x="796" d="M398 -10Q325 -10 263 15T154 86T83 194T57 330Q57 404 82 466T154 573T262 644T398 670Q471 670 533 645T642 574T713 466T739 330Q739 257 714 195T642 87T534 16T398 -10ZM416 41Q472 41 236 + 514 73T581 163T605 300Q605 397 576 469T496 580T383 620Q327 620 284 589T216 499T191 362Q191 265 221 193T302 81T416 41ZM231 724Q247 774 262 800T294 835T331 845Q359 845 382 832T428 805T472 792Q490 792 504 802T531 845H550Q534 794 519 768T486 733T449 237 + 724Q421 724 398 737T352 763T308 776Q290 776 277 767T249 724H231Z" /> 238 + <glyph unicode="&#xd6;" glyph-name="Odieresis" horiz-adv-x="796" d="M398 -10Q325 -10 263 15T154 86T83 194T57 330Q57 404 82 466T154 573T262 644T398 670Q471 670 533 645T642 574T713 466T739 330Q739 257 714 195T642 87T534 16T398 -10ZM416 41Q472 239 + 41 514 73T581 163T605 300Q605 397 576 469T496 580T383 620Q327 620 284 589T216 499T191 362Q191 265 221 193T302 81T416 41ZM295 731Q271 731 256 747T240 786Q240 809 255 824T295 840Q318 840 333 825T349 786Q349 763 334 747T295 731ZM487 731Q463 731 240 + 448 747T433 786Q433 809 448 824T487 840Q511 840 526 825T541 786Q541 763 526 747T487 731Z" /> 241 + <glyph unicode="&#xd7;" glyph-name="multiply" horiz-adv-x="536" d="M121 87L82 126L229 272L82 419L121 458L268 311L415 458L453 419L307 272L453 126L415 87L268 233L121 87Z" /> 242 + <glyph unicode="&#xd8;" glyph-name="Oslash" horiz-adv-x="796" d="M58 -10L149 92Q106 137 82 198T57 330Q57 404 82 466T154 573T262 644T398 670Q458 670 511 653T607 604L666 670H739L647 568Q690 523 714 462T739 330Q739 257 714 195T642 87T534 16T398 243 + -10Q338 -10 285 7T190 56L130 -10H58ZM191 362Q191 309 200 263T227 180L542 531Q512 574 471 597T383 620Q327 620 284 589T216 499T191 362ZM416 41Q472 41 514 73T581 163T605 300Q605 352 596 398T570 481L256 130Q286 88 327 65T416 41Z" /> 244 + <glyph unicode="&#xd9;" glyph-name="Ugrave" horiz-adv-x="826" d="M416 -10Q334 -10 271 18T172 108T136 272V603L53 644V660H345V644L262 603V278Q262 195 288 147T357 79T455 58Q499 58 536 76T597 135T620 243V605L524 644V660H773V644L690 604V272Q690 171 245 + 655 109T558 18T416 -10ZM447 710H428L255 842V862H372L447 710Z" /> 246 + <glyph unicode="&#xda;" glyph-name="Uacute" horiz-adv-x="826" d="M416 -10Q334 -10 271 18T172 108T136 272V603L53 644V660H345V644L262 603V278Q262 195 288 147T357 79T455 58Q499 58 536 76T597 135T620 243V605L524 644V660H773V644L690 604V272Q690 171 247 + 655 109T558 18T416 -10ZM431 710L506 862H623V842L451 710H431Z" /> 248 + <glyph unicode="&#xdb;" glyph-name="Ucircumflex" horiz-adv-x="826" d="M416 -10Q334 -10 271 18T172 108T136 272V603L53 644V660H345V644L262 603V278Q262 195 288 147T357 79T455 58Q499 58 536 76T597 135T620 243V605L524 644V660H773V644L690 604V272Q690 249 + 171 655 109T558 18T416 -10ZM285 710L419 862H459L594 710H576L439 779L302 710H285Z" /> 250 + <glyph unicode="&#xdc;" glyph-name="Udieresis" horiz-adv-x="826" d="M416 -10Q334 -10 271 18T172 108T136 272V603L53 644V660H345V644L262 603V278Q262 195 288 147T357 79T455 58Q499 58 536 76T597 135T620 243V605L524 644V660H773V644L690 604V272Q690 251 + 171 655 109T558 18T416 -10ZM344 731Q320 731 305 747T289 786Q289 809 304 824T344 840Q367 840 382 825T398 786Q398 763 383 747T344 731ZM536 731Q512 731 497 747T482 786Q482 809 497 824T536 840Q560 840 575 825T590 786Q590 763 575 747T536 731Z" /> 252 + <glyph unicode="&#xdd;" glyph-name="Yacute" horiz-adv-x="739" d="M221 0V16L311 57V308L91 608L26 644V660H308V644L233 608V604L409 365L568 602V606L492 644V660H713V644L648 611L437 309V57L526 16V0H221ZM390 710L465 862H582V842L410 710H390Z" /> 253 + <glyph unicode="&#xde;" glyph-name="Thorn" horiz-adv-x="665" d="M53 0V16L136 57V603L53 644V660H345V644L261 603V535H363Q481 535 552 493T624 357Q624 301 587 259T486 192T339 168H261V57L345 16V0H53ZM318 480H261V219H325Q404 219 449 255T494 348Q494 254 + 418 449 449T318 480Z" /> 255 + <glyph unicode="&#xdf;" glyph-name="germandbls" horiz-adv-x="644" d="M52 0V16L115 48V407H37V454H115V480Q115 531 132 579T182 666T262 727T370 750Q460 750 506 712T553 620Q553 587 540 563T507 519T467 480T434 438T421 385Q421 362 434 346T472 314T529 256 + 273Q581 235 599 204T617 132Q617 92 595 60T535 9T454 -10Q405 -10 371 -1T302 25V140H317L351 86Q366 61 389 46T444 31Q479 31 501 49T523 100Q523 126 509 144T469 179T412 217Q378 240 355 266T331 341Q331 371 343 393T372 435T407 477T436 529T448 602Q448 257 + 646 423 676T348 706Q283 706 257 663T231 546V0H52Z" /> 258 + <glyph unicode="&#xe0;" glyph-name="agrave" horiz-adv-x="504" d="M153 -10Q102 -10 71 20T39 100Q39 152 73 185T197 241L294 265V331Q294 368 271 391T212 414Q198 414 184 411T153 400V302H80Q63 302 57 310T50 338Q50 367 77 396T148 444T246 464Q324 464 259 + 367 433T410 344V71Q417 64 429 60T461 55H494L497 52V43Q485 23 460 7T396 -10Q359 -10 336 7T301 55H294Q267 25 231 8T153 -10ZM149 126Q149 95 167 79T219 63Q254 63 294 80V227Q231 214 200 200T159 167T149 126ZM246 530H229L48 689V707H168L246 530Z" /> 260 + <glyph unicode="&#xe1;" glyph-name="aacute" horiz-adv-x="504" d="M153 -10Q102 -10 71 20T39 100Q39 152 73 185T197 241L294 265V331Q294 368 271 391T212 414Q198 414 184 411T153 400V302H80Q63 302 57 310T50 338Q50 367 77 396T148 444T246 464Q324 464 261 + 367 433T410 344V71Q417 64 429 60T461 55H494L497 52V43Q485 23 460 7T396 -10Q359 -10 336 7T301 55H294Q267 25 231 8T153 -10ZM149 126Q149 95 167 79T219 63Q254 63 294 80V227Q231 214 200 200T159 167T149 126ZM231 530L309 707H429V689L249 530H231Z" /> 262 + <glyph unicode="&#xe2;" glyph-name="acircumflex" horiz-adv-x="504" d="M153 -10Q102 -10 71 20T39 100Q39 152 73 185T197 241L294 265V331Q294 368 271 391T212 414Q198 414 184 411T153 400V302H80Q63 302 57 310T50 338Q50 367 77 396T148 444T246 464Q324 263 + 464 367 433T410 344V71Q417 64 429 60T461 55H494L497 52V43Q485 23 460 7T396 -10Q359 -10 336 7T301 55H294Q267 25 231 8T153 -10ZM149 126Q149 95 167 79T219 63Q254 63 294 80V227Q231 214 200 200T159 167T149 126ZM83 530L217 707H257L392 530H374L237 264 + 618L100 530H83Z" /> 265 + <glyph unicode="&#xe3;" glyph-name="atilde" horiz-adv-x="504" d="M153 -10Q102 -10 71 20T39 100Q39 152 73 185T197 241L294 265V331Q294 368 271 391T212 414Q198 414 184 411T153 400V302H80Q63 302 57 310T50 338Q50 367 77 396T148 444T246 464Q324 464 266 + 367 433T410 344V71Q417 64 429 60T461 55H494L497 52V43Q485 23 460 7T396 -10Q359 -10 336 7T301 55H294Q267 25 231 8T153 -10ZM149 126Q149 95 167 79T219 63Q254 63 294 80V227Q231 214 200 200T159 167T149 126ZM78 552Q96 610 111 638T143 675T179 684Q206 267 + 684 230 668T275 636T318 620Q336 620 349 632T378 684H397Q378 626 363 598T331 561T295 552Q268 552 244 568T199 600T156 616Q138 616 125 604T96 552H78Z" /> 268 + <glyph unicode="&#xe4;" glyph-name="adieresis" horiz-adv-x="504" d="M153 -10Q102 -10 71 20T39 100Q39 152 73 185T197 241L294 265V331Q294 368 271 391T212 414Q198 414 184 411T153 400V302H80Q63 302 57 310T50 338Q50 367 77 396T148 444T246 464Q324 269 + 464 367 433T410 344V71Q417 64 429 60T461 55H494L497 52V43Q485 23 460 7T396 -10Q359 -10 336 7T301 55H294Q267 25 231 8T153 -10ZM149 126Q149 95 167 79T219 63Q254 63 294 80V227Q231 214 200 200T159 167T149 126ZM141 563Q117 563 102 578T86 617Q86 640 270 + 101 655T141 671Q164 671 179 656T195 617Q195 594 180 579T141 563ZM333 563Q309 563 294 578T279 617Q279 640 294 655T333 671Q357 671 372 656T387 617Q387 594 372 579T333 563Z" /> 271 + <glyph unicode="&#xe5;" glyph-name="aring" horiz-adv-x="504" d="M153 -10Q102 -10 71 20T39 100Q39 152 73 185T197 241L294 265V331Q294 368 271 391T212 414Q198 414 184 411T153 400V302H80Q63 302 57 310T50 338Q50 367 77 396T148 444T246 464Q324 464 272 + 367 433T410 344V71Q417 64 429 60T461 55H494L497 52V43Q485 23 460 7T396 -10Q359 -10 336 7T301 55H294Q267 25 231 8T153 -10ZM149 126Q149 95 167 79T219 63Q254 63 294 80V227Q231 214 200 200T159 167T149 126ZM237 702Q276 702 300 678T325 618Q325 582 273 + 301 558T237 534Q198 534 174 557T149 618Q149 654 173 678T237 702ZM233 678Q218 678 210 666T202 625Q202 558 242 558Q273 558 273 611Q273 678 233 678Z" /> 274 + <glyph unicode="&#xe6;" glyph-name="ae" horiz-adv-x="743" d="M154 -10Q106 -10 73 19T40 101Q40 153 74 186T200 242L295 265V331Q295 368 272 391T212 414Q198 414 185 411T154 400V302H81Q63 302 57 310T51 338Q51 367 78 396T149 444T247 464Q347 464 386 275 + 411Q416 436 452 450T529 464Q609 464 654 409T700 254L695 249H408Q408 205 424 168T475 107T570 84Q599 84 630 92T698 119H701V105Q677 56 628 23T512 -10Q453 -10 408 12T334 73H327Q299 38 255 14T154 -10ZM508 419Q468 419 443 387T410 294H603Q594 363 570 276 + 391T508 419ZM150 126Q150 95 168 79T220 63Q247 63 271 71T321 93Q295 142 295 208V227Q233 215 202 201T160 168T150 126Z" /> 277 + <glyph unicode="&#xe7;" glyph-name="ccedilla" horiz-adv-x="482" d="M244 -209Q273 -209 286 -199T299 -171Q299 -149 279 -135T200 -103L242 -10Q152 -5 98 53T43 206Q43 282 76 340T168 431T298 464Q346 464 378 449T426 415T443 382Q443 366 424 352L386 278 + 321L301 421Q294 422 288 422Q247 422 217 399T170 338T154 253Q154 173 195 129T312 84Q341 84 372 92T440 119H443V105Q421 61 379 29T280 -9L262 -48Q319 -65 342 -88T365 -151Q365 -189 338 -214T264 -240Q209 -240 182 -214V-198H185Q210 -209 244 -209Z" 279 + /> 280 + <glyph unicode="&#xe8;" glyph-name="egrave" horiz-adv-x="491" d="M260 -10Q159 -10 101 49T43 206Q43 282 74 340T159 431T276 464Q356 464 401 409T447 254L443 249H155Q155 205 171 168T222 107T318 84Q346 84 378 92T445 119H448V105Q424 56 375 23T260 281 + -10ZM256 419Q216 419 191 387T158 294H350Q341 363 318 391T256 419ZM272 530H255L74 689V707H194L272 530Z" /> 282 + <glyph unicode="&#xe9;" glyph-name="eacute" horiz-adv-x="491" d="M260 -10Q159 -10 101 49T43 206Q43 282 74 340T159 431T276 464Q356 464 401 409T447 254L443 249H155Q155 205 171 168T222 107T318 84Q346 84 378 92T445 119H448V105Q424 56 375 23T260 283 + -10ZM256 419Q216 419 191 387T158 294H350Q341 363 318 391T256 419ZM258 530L336 707H456V689L276 530H258Z" /> 284 + <glyph unicode="&#xea;" glyph-name="ecircumflex" horiz-adv-x="491" d="M260 -10Q159 -10 101 49T43 206Q43 282 74 340T159 431T276 464Q356 464 401 409T447 254L443 249H155Q155 205 171 168T222 107T318 84Q346 84 378 92T445 119H448V105Q424 56 375 23T260 285 + -10ZM256 419Q216 419 191 387T158 294H350Q341 363 318 391T256 419ZM110 530L244 707H284L419 530H401L264 618L127 530H110Z" /> 286 + <glyph unicode="&#xeb;" glyph-name="edieresis" horiz-adv-x="491" d="M260 -10Q159 -10 101 49T43 206Q43 282 74 340T159 431T276 464Q356 464 401 409T447 254L443 249H155Q155 205 171 168T222 107T318 84Q346 84 378 92T445 119H448V105Q424 56 375 23T260 287 + -10ZM256 419Q216 419 191 387T158 294H350Q341 363 318 391T256 419ZM168 563Q144 563 129 578T113 617Q113 640 128 655T168 671Q191 671 206 656T222 617Q222 594 207 579T168 563ZM360 563Q336 563 321 578T306 617Q306 640 321 655T360 671Q384 671 399 656T414 288 + 617Q414 594 399 579T360 563Z" /> 289 + <glyph unicode="&#xec;" glyph-name="igrave" horiz-adv-x="325" d="M42 0V16L107 48V350L45 398V409L213 464H223V48L284 16V0H42ZM171 530H154L-27 689V707H93L171 530Z" /> 290 + <glyph unicode="&#xed;" glyph-name="iacute" horiz-adv-x="325" d="M42 0V16L107 48V350L45 398V409L213 464H223V48L284 16V0H42ZM157 530L235 707H355V689L175 530H157Z" /> 291 + <glyph unicode="&#xee;" glyph-name="icircumflex" horiz-adv-x="325" d="M42 0V16L107 48V350L45 398V409L213 464H223V48L284 16V0H42ZM9 530L143 707H183L318 530H300L163 618L26 530H9Z" /> 292 + <glyph unicode="&#xef;" glyph-name="idieresis" horiz-adv-x="325" d="M42 0V16L107 48V350L45 398V409L213 464H223V48L284 16V0H42ZM67 563Q43 563 28 578T12 617Q12 640 27 655T67 671Q90 671 105 656T121 617Q121 594 106 579T67 563ZM259 563Q235 563 220 293 + 578T205 617Q205 640 220 655T259 671Q283 671 298 656T313 617Q313 594 298 579T259 563Z" /> 294 + <glyph unicode="&#xf0;" glyph-name="eth" horiz-adv-x="549" d="M272 -10Q204 -10 152 21T71 104T41 220Q41 297 73 351T160 435T281 464Q303 464 320 462T353 456Q326 535 274 593L201 523H129L239 627Q178 680 94 710V749Q197 722 281 667L359 741H431L321 295 + 637Q409 567 458 470T508 261Q508 180 478 119T395 24T272 -10ZM163 229Q163 179 175 135T214 64T279 37Q325 37 353 91T382 266Q382 314 376 359Q354 384 331 398T276 413Q225 413 194 370T163 229Z" /> 296 + <glyph unicode="&#xf1;" glyph-name="ntilde" horiz-adv-x="603" d="M38 0V16L103 48V350L41 395V405L199 464H209L218 390H223Q260 426 294 445T373 464Q433 464 471 434T510 343V48L575 16V0H335V16L394 48V300Q394 338 371 358T311 379Q288 379 262 371T219 297 + 349V48L278 16V0H38ZM148 552Q166 610 181 638T213 675T249 684Q276 684 300 668T345 636T388 620Q406 620 419 632T448 684H467Q448 626 433 598T401 561T365 552Q338 552 314 568T269 600T226 616Q208 616 195 604T166 552H148Z" /> 298 + <glyph unicode="&#xf2;" glyph-name="ograve" horiz-adv-x="544" d="M272 -10Q203 -10 151 21T70 107T41 227Q41 293 70 346T151 432T272 464Q341 464 393 432T474 347T503 227Q503 161 474 107T393 22T272 -10ZM284 37Q318 37 339 61T371 124T381 207Q381 261 299 + 369 309T331 387T262 418Q228 418 206 394T174 331T163 248Q163 194 175 146T215 67T284 37ZM279 530H262L81 689V707H201L279 530Z" /> 300 + <glyph unicode="&#xf3;" glyph-name="oacute" horiz-adv-x="544" d="M272 -10Q203 -10 151 21T70 107T41 227Q41 293 70 346T151 432T272 464Q341 464 393 432T474 347T503 227Q503 161 474 107T393 22T272 -10ZM284 37Q318 37 339 61T371 124T381 207Q381 261 301 + 369 309T331 387T262 418Q228 418 206 394T174 331T163 248Q163 194 175 146T215 67T284 37ZM265 530L343 707H463V689L283 530H265Z" /> 302 + <glyph unicode="&#xf4;" glyph-name="ocircumflex" horiz-adv-x="544" d="M272 -10Q203 -10 151 21T70 107T41 227Q41 293 70 346T151 432T272 464Q341 464 393 432T474 347T503 227Q503 161 474 107T393 22T272 -10ZM284 37Q318 37 339 61T371 124T381 207Q381 303 + 261 369 309T331 387T262 418Q228 418 206 394T174 331T163 248Q163 194 175 146T215 67T284 37ZM116 530L250 707H290L425 530H407L270 618L133 530H116Z" /> 304 + <glyph unicode="&#xf5;" glyph-name="otilde" horiz-adv-x="544" d="M272 -10Q203 -10 151 21T70 107T41 227Q41 293 70 346T151 432T272 464Q341 464 393 432T474 347T503 227Q503 161 474 107T393 22T272 -10ZM284 37Q318 37 339 61T371 124T381 207Q381 261 305 + 369 309T331 387T262 418Q228 418 206 394T174 331T163 248Q163 194 175 146T215 67T284 37ZM111 552Q129 610 144 638T176 675T212 684Q239 684 263 668T308 636T351 620Q369 620 382 632T411 684H430Q411 626 396 598T364 561T328 552Q301 552 277 568T232 600T189 306 + 616Q171 616 158 604T129 552H111Z" /> 307 + <glyph unicode="&#xf6;" glyph-name="odieresis" horiz-adv-x="544" d="M272 -10Q203 -10 151 21T70 107T41 227Q41 293 70 346T151 432T272 464Q341 464 393 432T474 347T503 227Q503 161 474 107T393 22T272 -10ZM284 37Q318 37 339 61T371 124T381 207Q381 308 + 261 369 309T331 387T262 418Q228 418 206 394T174 331T163 248Q163 194 175 146T215 67T284 37ZM174 563Q150 563 135 578T119 617Q119 640 134 655T174 671Q197 671 212 656T228 617Q228 594 213 579T174 563ZM366 563Q342 563 327 578T312 617Q312 640 327 655T366 309 + 671Q390 671 405 656T420 617Q420 594 405 579T366 563Z" /> 310 + <glyph unicode="&#xf7;" glyph-name="divide" horiz-adv-x="536" d="M234 415V482H302V415H234ZM58 246V298H478V246H58ZM234 62V129H302V62H234Z" /> 311 + <glyph unicode="&#xf8;" glyph-name="oslash" horiz-adv-x="544" d="M41 -10L102 63Q73 94 57 136T41 227Q41 293 70 346T151 432T272 464Q310 464 343 454T403 425L435 464H503L443 391Q472 359 487 317T503 227Q503 161 474 107T393 22T272 -10Q234 -10 201 312 + 0T141 29L109 -10H41ZM163 248Q163 194 174 150L349 360Q335 387 314 402T262 418Q228 418 206 394T174 331T163 248ZM284 37Q318 37 339 61T371 124T381 207Q381 260 370 304L196 94Q211 68 233 53T284 37Z" /> 313 + <glyph unicode="&#xf9;" glyph-name="ugrave" horiz-adv-x="578" d="M221 -10Q161 -10 123 20T85 111V406L20 438V454H201V154Q201 115 224 95T284 75Q306 75 331 82T374 105V406L309 438V454H490V46L556 16V0H374V63H370Q333 27 299 9T221 -10ZM293 530H276L95 314 + 689V707H215L293 530Z" /> 315 + <glyph unicode="&#xfa;" glyph-name="uacute" horiz-adv-x="578" d="M221 -10Q161 -10 123 20T85 111V406L20 438V454H201V154Q201 115 224 95T284 75Q306 75 331 82T374 105V406L309 438V454H490V46L556 16V0H374V63H370Q333 27 299 9T221 -10ZM278 530L356 707H476V689L296 316 + 530H278Z" /> 317 + <glyph unicode="&#xfb;" glyph-name="ucircumflex" horiz-adv-x="578" d="M221 -10Q161 -10 123 20T85 111V406L20 438V454H201V154Q201 115 224 95T284 75Q306 75 331 82T374 105V406L309 438V454H490V46L556 16V0H374V63H370Q333 27 299 9T221 -10ZM130 530L264 318 + 707H304L439 530H421L284 618L147 530H130Z" /> 319 + <glyph unicode="&#xfc;" glyph-name="udieresis" horiz-adv-x="578" d="M221 -10Q161 -10 123 20T85 111V406L20 438V454H201V154Q201 115 224 95T284 75Q306 75 331 82T374 105V406L309 438V454H490V46L556 16V0H374V63H370Q333 27 299 9T221 -10ZM188 563Q164 320 + 563 149 578T133 617Q133 640 148 655T188 671Q211 671 226 656T242 617Q242 594 227 579T188 563ZM380 563Q356 563 341 578T326 617Q326 640 341 655T380 671Q404 671 419 656T434 617Q434 594 419 579T380 563Z" /> 321 + <glyph unicode="&#xfd;" glyph-name="yacute" horiz-adv-x="478" d="M-31 454H230V438L160 408V404L280 149H285L381 399V404L315 438V454H508V438L436 404L221 -119Q201 -166 183 -192T142 -229T89 -240Q65 -240 57 -233T48 -199V-146H151Q168 -125 183 -96T222 322 + -10L226 1L33 409L-31 438V454ZM268 530L346 707H466V689L286 530H268Z" /> 323 + <glyph unicode="&#xfe;" glyph-name="thorn" horiz-adv-x="578" d="M38 -230V-214L103 -182V636L41 685V695L203 750H219V404H225Q262 436 296 450T370 464Q417 464 455 438T517 363T540 247Q540 166 509 109T425 21T310 -10Q258 -10 219 0V-182L294 -214V-230H38ZM299 324 + 387Q259 387 219 363V77Q258 41 319 41Q346 41 370 57T409 111T425 216Q425 304 387 345T299 387Z" /> 325 + <glyph unicode="&#xff;" glyph-name="ydieresis" horiz-adv-x="478" d="M-31 454H230V438L160 408V404L280 149H285L381 399V404L315 438V454H508V438L436 404L221 -119Q201 -166 183 -192T142 -229T89 -240Q65 -240 57 -233T48 -199V-146H151Q168 -125 183 -96T222 326 + -10L226 1L33 409L-31 438V454ZM178 563Q154 563 139 578T123 617Q123 640 138 655T178 671Q201 671 216 656T232 617Q232 594 217 579T178 563ZM370 563Q346 563 331 578T316 617Q316 640 331 655T370 671Q394 671 409 656T424 617Q424 594 409 579T370 563Z" 327 + /> 328 + <glyph unicode="&#x2013;" glyph-name="endash" horiz-adv-x="500" d="M48 247V297H452V247H48Z" /> 329 + <glyph unicode="&#x2014;" glyph-name="emdash" horiz-adv-x="1000" d="M48 247V297H952V247H48Z" /> 330 + <glyph unicode="&#x2018;" glyph-name="quoteleft" horiz-adv-x="245" d="M224 744L145 489H17V512L200 744H224Z" /> 331 + <glyph unicode="&#x2019;" glyph-name="quoteright" horiz-adv-x="245" d="M22 486L100 740H228V718L46 486H22Z" /> 332 + <glyph unicode="&#x201a;" glyph-name="quotesinglbase" horiz-adv-x="245" d="M22 -186L100 68H228V46L46 -186H22Z" /> 333 + <glyph unicode="&#x201c;" glyph-name="quotedblleft" horiz-adv-x="442" d="M224 744L145 489H17V512L200 744H224ZM420 744L341 489H213V512L396 744H420Z" /> 334 + <glyph unicode="&#x201d;" glyph-name="quotedblright" horiz-adv-x="442" d="M22 486L100 740H228V718L46 486H22ZM218 486L296 740H424V718L242 486H218Z" /> 335 + <glyph unicode="&#x201e;" glyph-name="quotedblbase" horiz-adv-x="442" d="M22 -186L100 68H228V46L46 -186H22ZM218 -186L296 68H424V46L242 -186H218Z" /> 336 + <glyph unicode="&#x2022;" glyph-name="bullet" horiz-adv-x="500" d="M250 150Q197 150 163 185T128 272Q128 325 162 359T250 393Q301 393 336 359T371 272Q371 220 336 185T250 150Z" /> 337 + <glyph unicode="&#x2039;" glyph-name="guilsinglleft" horiz-adv-x="264" d="M225 70L24 247L225 424H241L132 247L241 70H225Z" /> 338 + <glyph unicode="&#x203a;" glyph-name="guilsinglright" horiz-adv-x="264" d="M24 70L132 247L24 424H39L241 247L39 70H24Z" /> 339 + </font> 340 + </defs> 341 + </svg>
pywb-test/static/fonts/spectral-v15-latin-600.ttf

This is a binary file and will not be displayed.

pywb-test/static/fonts/spectral-v15-latin-600.woff

This is a binary file and will not be displayed.

pywb-test/static/fonts/spectral-v15-latin-600.woff2

This is a binary file and will not be displayed.

pywb-test/static/fonts/spectral-v15-latin-700.eot

This is a binary file and will not be displayed.

pywb-test/static/fonts/spectral-v15-latin-700.ttf

This is a binary file and will not be displayed.

pywb-test/static/fonts/spectral-v15-latin-700.woff

This is a binary file and will not be displayed.

pywb-test/static/fonts/spectral-v15-latin-700.woff2

This is a binary file and will not be displayed.

+95
pywb-test/static/index.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 + <title>Seams - Wisdom is made Together</title> 7 + <link rel="stylesheet" href="landing.css"> 8 + </head> 9 + <body> 10 + <div class="layout"> 11 + <aside class="sidebar"> 12 + <div class="sidebar-content"> 13 + <header class="hero"> 14 + <div class="logo">Seams</div> 15 + <h1><span style="white-space: nowrap;">Wisdom is Made</span> <br>Together</h1> 16 + <p class="tagline">Annotations in the Atmosphere</p> 17 + <div class="cta-buttons"> 18 + <a href="https://addons.mozilla.org/en-US/firefox/addon/seams/" class="cta-primary" target="_blank">Install Extension</a> 19 + <a href="/about.html" class="cta-secondary">About</a> 20 + </div> 21 + </header> 22 + 23 + <footer class="footer desktop-footer"> 24 + <div class="footer-icons"> 25 + <a href="https://tangled.org/@sealight.xyz/seams.so" target="_blank" aria-label="Tangled"> 26 + <img src="https://semble.so/_next/static/media/tangled-icon.b95d4d65.svg" alt="Tangled" /> 27 + </a> 28 + <a href="https://bsky.app" target="_blank" aria-label="Bluesky"> 29 + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 360 320"> 30 + <path fill="currentColor" d="M180 142c-16.3-31.7-60.7-90.8-102-120C38.5-5.9 23.4-1 13.5 3.4 2.1 8.6 0 26.2 0 36.5c0 10.4 5.7 84.8 9.4 97.2 12.2 41 55.7 55 95.7 50.5-58.7 8.6-110.8 30-42.4 106.1 75.1 77.9 103-16.7 117.3-64.6 14.3 48 30.8 139 116 64.6 64-64.6 17.6-97.5-41.1-106.1 40 4.4 83.5-9.5 95.7-50.5 3.7-12.4 9.4-86.8 9.4-97.2 0-10.3-2-27.9-13.5-33C336.5-1 321.5-6 282 22c-41.3 29.2-85.7 88.3-102 120Z"/> 31 + </svg> 32 + </a> 33 + </div> 34 + </footer> 35 + </div> 36 + </aside> 37 + 38 + <main class="main-content"> 39 + <div class="feed-section" id="feed"> 40 + <div id="annotations-feed" class="annotations-feed"> 41 + <div class="loading">Tending the garden...</div> 42 + </div> 43 + <div id="load-more" class="load-more" style="display: none;"> 44 + <button id="load-more-btn">Nurture More</button> 45 + </div> 46 + </div> 47 + </main> 48 + 49 + <footer class="footer mobile-footer"> 50 + <div class="footer-icons"> 51 + <a href="https://tangled.org/@sealight.xyz/seams.so" target="_blank" aria-label="Tangled"> 52 + <img src="https://semble.so/_next/static/media/tangled-icon.b95d4d65.svg" alt="Tangled" /> 53 + </a> 54 + <a href="https://bsky.app" target="_blank" aria-label="Bluesky"> 55 + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 360 320"> 56 + <path fill="currentColor" d="M180 142c-16.3-31.7-60.7-90.8-102-120C38.5-5.9 23.4-1 13.5 3.4 2.1 8.6 0 26.2 0 36.5c0 10.4 5.7 84.8 9.4 97.2 12.2 41 55.7 55 95.7 50.5-58.7 8.6-110.8 30-42.4 106.1 75.1 77.9 103-16.7 117.3-64.6 14.3 48 30.8 139 116 64.6 64-64.6 17.6-97.5-41.1-106.1 40 4.4 83.5-9.5 95.7-50.5 3.7-12.4 9.4-86.8 9.4-97.2 0-10.3-2-27.9-13.5-33C336.5-1 321.5-6 282 22c-41.3 29.2-85.7 88.3-102 120Z"/> 57 + </svg> 58 + </a> 59 + </div> 60 + </footer> 61 + </div> 62 + 63 + <script> 64 + // Backend URL defaults to same origin (production) or can be overridden for development 65 + // window.BACKEND_URL = 'http://localhost:8080'; 66 + 67 + // Detect browser and set appropriate extension URL 68 + function getBrowserExtensionUrl() { 69 + const ua = navigator.userAgent; 70 + 71 + // Check Chrome (also works for Edge since it's Chromium-based) 72 + if (ua.includes('Chrome')) { 73 + return 'https://chromewebstore.google.com/detail/seams/dmkgcehijkfpalmplnallinblhimageb'; 74 + } 75 + 76 + // Firefox 77 + if (ua.includes('Firefox')) { 78 + return 'https://addons.mozilla.org/en-US/firefox/addon/seams/'; 79 + } 80 + 81 + // Default fallback to Firefox 82 + return 'https://addons.mozilla.org/en-US/firefox/addon/seams/'; 83 + } 84 + 85 + // Update button href when page loads 86 + document.addEventListener('DOMContentLoaded', function() { 87 + const installButton = document.querySelector('.cta-primary'); 88 + if (installButton) { 89 + installButton.href = getBrowserExtensionUrl(); 90 + } 91 + }); 92 + </script> 93 + <script type="module" src="landing.js"></script> 94 + </body> 95 + </html>
+65
pywb-test/static/introspect.html
··· 1 + <!DOCTYPE html> 2 + <html> 3 + <head> 4 + <title>GraphQL Introspection</title> 5 + </head> 6 + <body> 7 + <h1>GraphQL Schema Introspection</h1> 8 + <pre id="output">Loading...</pre> 9 + 10 + <script> 11 + const SLICE_ID = '3m3ugigrrz52k'; 12 + const GRAPHQL_ENDPOINT = `https://api.slices.network/graphql?slice=${SLICE_ID}`; 13 + 14 + const INTROSPECTION_QUERY = ` 15 + query IntrospectionQuery { 16 + __schema { 17 + queryType { 18 + fields { 19 + name 20 + description 21 + type { 22 + name 23 + kind 24 + } 25 + } 26 + } 27 + } 28 + } 29 + `; 30 + 31 + async function introspect() { 32 + try { 33 + const response = await fetch(GRAPHQL_ENDPOINT, { 34 + method: 'POST', 35 + headers: { 36 + 'Content-Type': 'application/json', 37 + }, 38 + body: JSON.stringify({ 39 + query: INTROSPECTION_QUERY 40 + }) 41 + }); 42 + 43 + const data = await response.json(); 44 + 45 + // Filter for annotation-related fields 46 + const queryFields = data.data.__schema.queryType.fields; 47 + const annotationFields = queryFields.filter(f => 48 + f.name.toLowerCase().includes('annotation') 49 + ); 50 + 51 + document.getElementById('output').textContent = 52 + 'All Query Fields:\n' + 53 + JSON.stringify(queryFields.map(f => f.name), null, 2) + 54 + '\n\nAnnotation-related fields:\n' + 55 + JSON.stringify(annotationFields, null, 2); 56 + } catch (error) { 57 + document.getElementById('output').textContent = 58 + 'Error: ' + error.message; 59 + } 60 + } 61 + 62 + introspect(); 63 + </script> 64 + </body> 65 + </html>
+413
pywb-test/static/landing.css
··· 1 + @import url('fonts.css'); 2 + 3 + * { 4 + box-sizing: border-box; 5 + margin: 0; 6 + padding: 0; 7 + } 8 + 9 + :root { 10 + --forest-green: #2d5016; 11 + --forest-green-light: #3d6b1f; 12 + --forest-green-dark: #1f3810; 13 + } 14 + 15 + body { 16 + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; 17 + background: #fafafa; 18 + color: #1a1a1a; 19 + position: relative; 20 + line-height: 1.6; 21 + margin: 0; 22 + padding: 0; 23 + } 24 + 25 + /* Blueprint grid background */ 26 + body::before { 27 + content: ''; 28 + position: fixed; 29 + inset: 0; 30 + pointer-events: none; 31 + background-image: 32 + radial-gradient(circle, rgba(45, 80, 22, 0.06) 1px, transparent 1px); 33 + background-size: 20px 20px; 34 + z-index: 0; 35 + } 36 + 37 + /* Layout - Sidebar + Main Content */ 38 + .layout { 39 + display: flex; 40 + min-height: 100vh; 41 + position: relative; 42 + z-index: 1; 43 + } 44 + 45 + /* Sidebar */ 46 + .sidebar { 47 + width: 420px; 48 + background: #fff; 49 + border-right: 2px dashed #d0d0d0; 50 + position: sticky; 51 + top: 0; 52 + height: 100vh; 53 + overflow-y: auto; 54 + flex-shrink: 0; 55 + } 56 + 57 + .sidebar-content { 58 + display: flex; 59 + flex-direction: column; 60 + justify-content: space-between; 61 + min-height: 100vh; 62 + padding: 60px 0; 63 + } 64 + 65 + /* Hero Section in Sidebar */ 66 + .hero { 67 + flex: 1; 68 + display: flex; 69 + flex-direction: column; 70 + justify-content: center; 71 + align-items: center; 72 + text-align: center; 73 + padding: 0 48px; 74 + } 75 + 76 + .logo { 77 + font-family: 'Spectral', serif; 78 + font-size: 16px; 79 + font-weight: 700; 80 + letter-spacing: 0.1em; 81 + text-transform: uppercase; 82 + color: #666; 83 + margin-bottom: 24px; 84 + } 85 + 86 + .hero h1 { 87 + font-family: 'Fraunces', serif; 88 + font-size: clamp(28px, 3vw, 40px); 89 + font-weight: 600; 90 + margin-bottom: 24px; 91 + color: #1a1a1a; 92 + line-height: 1.2; 93 + letter-spacing: -0.01em; 94 + } 95 + 96 + .tagline { 97 + font-size: 14px; 98 + color: #666; 99 + margin-bottom: 32px; 100 + font-weight: 400; 101 + line-height: 1.6; 102 + } 103 + 104 + /* CTA Buttons */ 105 + .cta-buttons { 106 + display: flex; 107 + gap: 12px; 108 + flex-direction: row; 109 + align-items: center; 110 + } 111 + 112 + .cta-primary, 113 + .cta-secondary { 114 + padding: 10px 20px; 115 + border-radius: 2px; 116 + font-size: 13px; 117 + font-weight: 500; 118 + text-decoration: none; 119 + transition: all 0.2s; 120 + border: 1px dashed; 121 + display: inline-block; 122 + } 123 + 124 + .cta-primary { 125 + background: var(--forest-green); 126 + color: #fff; 127 + border-color: var(--forest-green-dark); 128 + } 129 + 130 + .cta-primary:hover { 131 + background: var(--forest-green-dark); 132 + transform: translateY(-1px); 133 + } 134 + 135 + .cta-secondary { 136 + background: transparent; 137 + color: var(--forest-green); 138 + border-color: var(--forest-green); 139 + } 140 + 141 + .cta-secondary:hover { 142 + border-color: var(--forest-green-dark); 143 + background: rgba(45, 80, 22, 0.05); 144 + } 145 + 146 + /* Main Content Area */ 147 + .main-content { 148 + flex: 1; 149 + background: #fafafa; 150 + overflow-y: auto; 151 + } 152 + 153 + /* Feed Section */ 154 + .feed-section { 155 + padding: 60px 48px; 156 + max-width: 900px; 157 + } 158 + 159 + .annotations-feed { 160 + display: flex; 161 + flex-direction: column; 162 + gap: 24px; 163 + } 164 + 165 + /* Annotation Card */ 166 + .annotation-card { 167 + padding: 24px; 168 + border: 1px dashed #d0d0d0; 169 + border-radius: 2px; 170 + background: #fff; 171 + transition: all 0.2s; 172 + position: relative; 173 + } 174 + 175 + .annotation-card:hover { 176 + border-color: var(--forest-green); 177 + box-shadow: 0 2px 8px rgba(45, 80, 22, 0.08); 178 + } 179 + 180 + .annotation-quote { 181 + margin-bottom: 16px; 182 + padding: 12px 16px; 183 + background: #fafafa; 184 + border-left: 3px solid var(--forest-green); 185 + font-style: italic; 186 + color: #555; 187 + font-size: 15px; 188 + line-height: 1.6; 189 + } 190 + 191 + .annotation-body { 192 + margin-bottom: 16px; 193 + line-height: 1.6; 194 + color: #333; 195 + font-size: 15px; 196 + } 197 + 198 + .annotation-meta { 199 + display: flex; 200 + flex-wrap: wrap; 201 + gap: 12px; 202 + align-items: center; 203 + padding-top: 12px; 204 + border-top: 1px dashed #e0e0e0; 205 + font-size: 13px; 206 + color: #666; 207 + } 208 + 209 + .annotation-source { 210 + color: #1a1a1a; 211 + text-decoration: none; 212 + font-weight: 500; 213 + display: inline-flex; 214 + align-items: center; 215 + gap: 4px; 216 + border-bottom: 1px dashed transparent; 217 + transition: border-color 0.2s; 218 + } 219 + 220 + .annotation-source:hover { 221 + border-bottom-color: #1a1a1a; 222 + } 223 + 224 + .annotation-author { 225 + color: #666; 226 + display: inline-flex; 227 + align-items: center; 228 + gap: 8px; 229 + } 230 + 231 + .annotation-author .author-avatar { 232 + width: 24px; 233 + height: 24px; 234 + border-radius: 50%; 235 + object-fit: cover; 236 + border: 1px dashed #d0d0d0; 237 + } 238 + 239 + .annotation-author a { 240 + color: #1a1a1a; 241 + text-decoration: none; 242 + font-weight: 500; 243 + border-bottom: 1px dashed transparent; 244 + transition: border-color 0.2s; 245 + } 246 + 247 + .annotation-author a:hover { 248 + border-bottom-color: #1a1a1a; 249 + } 250 + 251 + .annotation-time { 252 + color: #999; 253 + } 254 + 255 + /* Loading & Empty States */ 256 + .loading, 257 + .empty, 258 + .error { 259 + text-align: center; 260 + padding: 60px 20px; 261 + color: #999; 262 + font-style: italic; 263 + } 264 + 265 + .error { 266 + color: #d93025; 267 + font-style: normal; 268 + } 269 + 270 + /* Load More */ 271 + .load-more { 272 + text-align: center; 273 + margin-top: 40px; 274 + } 275 + 276 + #load-more-btn { 277 + background: var(--forest-green); 278 + color: white; 279 + border: 1px dashed var(--forest-green-dark); 280 + padding: 12px 32px; 281 + border-radius: 2px; 282 + cursor: pointer; 283 + font-size: 14px; 284 + font-weight: 500; 285 + transition: all 0.2s; 286 + } 287 + 288 + #load-more-btn:hover { 289 + background: var(--forest-green-dark); 290 + transform: translateY(-1px); 291 + } 292 + 293 + #load-more-btn:disabled { 294 + background: #ccc; 295 + border-color: #aaa; 296 + cursor: not-allowed; 297 + transform: none; 298 + } 299 + 300 + /* Footer in Sidebar */ 301 + .footer { 302 + padding-top: 40px; 303 + color: #999; 304 + font-size: 13px; 305 + text-align: center; 306 + margin: 0; 307 + border-top: 2px dashed #d0d0d0; 308 + } 309 + 310 + .mobile-footer { 311 + display: none; 312 + } 313 + 314 + .desktop-footer { 315 + display: block; 316 + } 317 + 318 + .footer-icons { 319 + display: flex; 320 + gap: 20px; 321 + justify-content: center; 322 + align-items: center; 323 + } 324 + 325 + .footer-icons a { 326 + color: #1a1a1a; 327 + text-decoration: none; 328 + transition: all 0.2s; 329 + display: flex; 330 + align-items: center; 331 + justify-content: center; 332 + } 333 + 334 + .footer-icons a img, 335 + .footer-icons a svg { 336 + width: 32px; 337 + height: 32px; 338 + opacity: 0.6; 339 + transition: opacity 0.2s; 340 + } 341 + 342 + .footer-icons a:hover img, 343 + .footer-icons a:hover svg { 344 + opacity: 1; 345 + } 346 + 347 + /* Responsive */ 348 + @media (max-width: 1024px) { 349 + .layout { 350 + flex-direction: column; 351 + } 352 + 353 + .sidebar { 354 + width: 100%; 355 + height: auto; 356 + position: relative; 357 + border-right: none; 358 + border-bottom: 2px dashed #d0d0d0; 359 + } 360 + 361 + .sidebar-content { 362 + min-height: auto; 363 + padding: 40px 0; 364 + } 365 + 366 + .hero { 367 + justify-content: flex-start; 368 + padding: 0 32px; 369 + } 370 + 371 + .desktop-footer { 372 + display: none; 373 + } 374 + 375 + .mobile-footer { 376 + display: block; 377 + padding: 40px 32px; 378 + margin: 0; 379 + border-top: 2px dashed #d0d0d0; 380 + background: #fafafa; 381 + } 382 + 383 + .feed-section { 384 + padding: 48px 32px; 385 + } 386 + } 387 + 388 + @media (max-width: 640px) { 389 + .sidebar-content { 390 + padding: 32px 0; 391 + } 392 + 393 + .hero { 394 + padding: 0 24px; 395 + } 396 + 397 + .hero h1 { 398 + font-size: 28px; 399 + } 400 + 401 + .mobile-footer { 402 + padding: 32px 24px; 403 + font-size: 12px; 404 + } 405 + 406 + .feed-section { 407 + padding: 32px 24px; 408 + } 409 + 410 + .annotation-card { 411 + padding: 20px; 412 + } 413 + }
+209
pywb-test/static/landing.js
··· 1 + // Backend API endpoint - can be overridden by setting window.BACKEND_URL 2 + const BACKEND_URL = window.BACKEND_URL || ''; 3 + 4 + let isLoading = false; 5 + const avatarCache = new Map(); 6 + 7 + const feedContainer = document.getElementById('annotations-feed'); 8 + const loadMoreContainer = document.getElementById('load-more'); 9 + const loadMoreBtn = document.getElementById('load-more-btn'); 10 + 11 + // Fetch actor profile (avatar) from Bluesky 12 + async function fetchActorProfile(did) { 13 + if (avatarCache.has(did)) { 14 + return avatarCache.get(did); 15 + } 16 + 17 + try { 18 + const response = await fetch(`https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=${did}`); 19 + if (!response.ok) { 20 + throw new Error('Profile fetch failed'); 21 + } 22 + const profile = await response.json(); 23 + const avatarUrl = profile.avatar || null; 24 + avatarCache.set(did, avatarUrl); 25 + return avatarUrl; 26 + } catch (error) { 27 + console.error('Failed to fetch profile for', did, error); 28 + avatarCache.set(did, null); 29 + return null; 30 + } 31 + } 32 + 33 + // Fetch annotations from backend 34 + async function fetchAnnotations(limit = 20) { 35 + if (isLoading) return; 36 + isLoading = true; 37 + 38 + try { 39 + const response = await fetch(`${BACKEND_URL}/api/annotations?limit=${limit}`); 40 + 41 + if (!response.ok) { 42 + throw new Error(`HTTP error! status: ${response.status}`); 43 + } 44 + 45 + const data = await response.json(); 46 + return data.annotations || []; 47 + } catch (error) { 48 + console.error('Failed to fetch annotations:', error); 49 + throw error; 50 + } finally { 51 + isLoading = false; 52 + } 53 + } 54 + 55 + // Parse selectors from JSON string 56 + function parseSelectors(selectorsJSON) { 57 + try { 58 + return JSON.parse(selectorsJSON); 59 + } catch { 60 + return []; 61 + } 62 + } 63 + 64 + // Extract text quote selector from selectors 65 + function getTextQuoteSelector(selectorsJSON) { 66 + const selectors = parseSelectors(selectorsJSON); 67 + return selectors.find( 68 + s => s.$type === 'community.lexicon.annotation.annotation#textQuoteSelector' 69 + ) || null; 70 + } 71 + 72 + // Format relative time (e.g., "2 hours ago") 73 + function formatRelativeTime(dateString) { 74 + const date = new Date(dateString); 75 + const now = new Date(); 76 + const diffMs = now - date; 77 + const diffSecs = Math.floor(diffMs / 1000); 78 + const diffMins = Math.floor(diffSecs / 60); 79 + const diffHours = Math.floor(diffMins / 60); 80 + const diffDays = Math.floor(diffHours / 24); 81 + 82 + if (diffSecs < 60) return 'just now'; 83 + if (diffMins < 60) return `${diffMins}m ago`; 84 + if (diffHours < 24) return `${diffHours}h ago`; 85 + if (diffDays < 7) return `${diffDays}d ago`; 86 + 87 + return date.toLocaleDateString(); 88 + } 89 + 90 + // Get domain from URL 91 + function getDomain(url) { 92 + try { 93 + return new URL(url).hostname; 94 + } catch { 95 + return url; 96 + } 97 + } 98 + 99 + // Build text fragment URL from selector 100 + function buildTextFragmentUrl(sourceUrl, exactText) { 101 + if (!sourceUrl || !exactText) { 102 + return sourceUrl; 103 + } 104 + 105 + try { 106 + const url = new URL(sourceUrl); 107 + url.hash = `:~:text=${encodeURIComponent(exactText)}`; 108 + return url.toString(); 109 + } catch { 110 + return sourceUrl; 111 + } 112 + } 113 + 114 + // Render a single annotation card 115 + async function renderAnnotation(annotation) { 116 + const { targetUrl, body, createdAt, authorDid, uri, authorHandle, exactText, selectorsJson } = annotation; 117 + const textQuoteSelector = getTextQuoteSelector(selectorsJson); 118 + const quotedText = exactText || textQuoteSelector?.exact; 119 + const sourceUrl = targetUrl; 120 + const fragmentUrl = buildTextFragmentUrl(sourceUrl, quotedText); 121 + const domain = sourceUrl ? getDomain(sourceUrl) : ''; 122 + const did = authorDid; 123 + 124 + const card = document.createElement('article'); 125 + card.className = 'annotation-card'; 126 + 127 + let html = ''; 128 + 129 + // Quoted text 130 + if (quotedText) { 131 + html += `<blockquote class="annotation-quote">"${quotedText}"</blockquote>`; 132 + } 133 + 134 + // Annotation body 135 + if (body) { 136 + html += `<div class="annotation-body">${escapeHtml(body)}</div>`; 137 + } 138 + 139 + // Metadata 140 + const avatarUrl = await fetchActorProfile(did); 141 + const avatarSrc = avatarUrl || `https://api.dicebear.com/7.x/initials/svg?seed=${escapeHtml(authorHandle || did)}`; 142 + 143 + html += ` 144 + <div class="annotation-meta"> 145 + ${fragmentUrl ? ` 146 + <a href="${escapeHtml(fragmentUrl)}" target="_blank" rel="noopener noreferrer" class="annotation-source"> 147 + ${escapeHtml(domain)} ↗ 148 + </a> 149 + ` : ''} 150 + <span class="annotation-author"> 151 + <img class="author-avatar" src="${avatarSrc}" alt="avatar"> 152 + <a href="https://bsky.app/profile/${escapeHtml(authorHandle || did)}" target="_blank" rel="noopener noreferrer"> 153 + ${escapeHtml(authorHandle || did.split(':').pop().slice(0, 8) + '...')} 154 + </a> 155 + </span> 156 + <span class="annotation-time">${formatRelativeTime(createdAt)}</span> 157 + </div> 158 + `; 159 + 160 + card.innerHTML = html; 161 + return card; 162 + } 163 + 164 + // Escape HTML to prevent XSS 165 + function escapeHtml(text) { 166 + const div = document.createElement('div'); 167 + div.textContent = text; 168 + return div.innerHTML; 169 + } 170 + 171 + // Render annotations to the feed 172 + async function renderAnnotations(annotations, append = false) { 173 + if (!append) { 174 + feedContainer.innerHTML = ''; 175 + } 176 + 177 + if (annotations.length === 0 && !append) { 178 + feedContainer.innerHTML = '<div class="empty">No annotations yet. Install the browser extension to start annotating!</div>'; 179 + return; 180 + } 181 + 182 + const fragment = document.createDocumentFragment(); 183 + for (const annotation of annotations) { 184 + const card = await renderAnnotation(annotation); 185 + fragment.appendChild(card); 186 + } 187 + 188 + feedContainer.appendChild(fragment); 189 + } 190 + 191 + // Load initial annotations 192 + async function loadInitialAnnotations() { 193 + try { 194 + feedContainer.innerHTML = '<div class="loading">Tending the garden...</div>'; 195 + 196 + const annotations = await fetchAnnotations(20); 197 + 198 + await renderAnnotations(annotations); 199 + 200 + // Hide load more button for now (could implement pagination later) 201 + loadMoreContainer.style.display = 'none'; 202 + } catch (error) { 203 + feedContainer.innerHTML = `<div class="error">Failed to load annotations. Please try again later.</div>`; 204 + console.error('Error loading annotations:', error); 205 + } 206 + } 207 + 208 + // Initialize 209 + loadInitialAnnotations();
+47
pywb-test/static/oauth-callback.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 + <title>Seams OAuth Callback</title> 7 + <style> 8 + body { 9 + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; 10 + display: flex; 11 + align-items: center; 12 + justify-content: center; 13 + height: 100vh; 14 + margin: 0; 15 + background: #f5f5f5; 16 + } 17 + .message { 18 + text-align: center; 19 + padding: 32px; 20 + background: white; 21 + border-radius: 8px; 22 + box-shadow: 0 2px 8px rgba(0,0,0,0.1); 23 + } 24 + .spinner { 25 + border: 3px solid #f3f3f3; 26 + border-top: 3px solid #0085ff; 27 + border-radius: 50%; 28 + width: 40px; 29 + height: 40px; 30 + animation: spin 1s linear infinite; 31 + margin: 0 auto 16px; 32 + } 33 + @keyframes spin { 34 + 0% { transform: rotate(0deg); } 35 + 100% { transform: rotate(360deg); } 36 + } 37 + </style> 38 + </head> 39 + <body> 40 + <div class="message"> 41 + <div class="spinner"></div> 42 + <h2>Completing login...</h2> 43 + <p id="status">Processing OAuth response</p> 44 + </div> 45 + <script type="module" src="/static/seams-oauth-callback.js"></script> 46 + </body> 47 + </html>
+17
pywb-test/static/oauth/callback.html
··· 1 + <!DOCTYPE html> 2 + <html> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <title>OAuth Callback</title> 6 + </head> 7 + <body> 8 + <p>Redirecting...</p> 9 + <script> 10 + // Relay to Chromium extension callback 11 + // The extension ID is deterministic based on the manifest key 12 + const extensionId = 'kjdnjfgcikmlbloojphbkmknfpmfofio'; 13 + const extRedirect = `https://${extensionId}.chromiumapp.org/extension-callback.html`; 14 + window.location.href = extRedirect + window.location.search + window.location.hash; 15 + </script> 16 + </body> 17 + </html>
+24
pywb-test/static/oauth/client-metadata.json
··· 1 + { 2 + "client_id": "https://synthes-is.netlify.app/oauth/client-metadata.json", 3 + "client_uri": "https://synthes-is.netlify.app", 4 + "redirect_uris": [ 5 + "https://synthes-is.netlify.app", 6 + "https://synthes-is.netlify.app/oauth/callback", 7 + "https://synthes-is.netlify.app/oauth/ff/callback", 8 + "https://e7a0b7703dc3d21c8ef60539da3ed68d27b48e8c.extensions.allizom.org/", 9 + "https://synthesis@seams.so/oauth/ff/callback", 10 + "https://dmkgcehijkfpalmplnallinblhimageb.chromium.org/oauth/callback.html" 11 + ], 12 + "application_type": "web", 13 + "client_name": "Seams", 14 + "dpop_bound_access_tokens": true, 15 + "grant_types": [ 16 + "authorization_code", 17 + "refresh_token" 18 + ], 19 + "response_types": [ 20 + "code" 21 + ], 22 + "scope": "atproto transition:generic", 23 + "token_endpoint_auth_method": "none" 24 + }
+16
pywb-test/static/oauth/ff/callback.html
··· 1 + <!DOCTYPE html> 2 + <html> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <title>OAuth Callback</title> 6 + </head> 7 + <body> 8 + <p>Redirecting...</p> 9 + <script> 10 + // Relay to Firefox extension callback 11 + // The extension ID is synthesis@seams.so, which Firefox hashes to this subdomain 12 + const extRedirect = 'https://e7a0b7703dc3d21c8ef60539da3ed68d27b48e8c.extensions.allizom.org/'; 13 + window.location.href = extRedirect + window.location.search + window.location.hash; 14 + </script> 15 + </body> 16 + </html>
+13
pywb-test/static/seams-sidebar.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 + <title>Seams Sidebar</title> 7 + <link rel="stylesheet" href="/static/assets/sidebar-J3iG1W2k.css"> 8 + </head> 9 + <body> 10 + <div id="app"></div> 11 + <script type="module" src="/static/seams-sidebar.js"></script> 12 + </body> 13 + </html>
+1
pywb-test/static/sidebar-BBEPW7gD.css
··· 1 + *{margin:0;padding:0;box-sizing:border-box}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,sans-serif;font-size:14px;line-height:1.5;color:#333}.sidebar{display:flex;flex-direction:column;height:100vh;background:#fff}.sidebar-header{padding:16px;border-bottom:1px solid #e0e0e0;background:#f5f5f5}.sidebar-header h1{font-size:20px;font-weight:600;margin-bottom:4px}.sidebar-header p{font-size:12px;color:#666}.sidebar-content{flex:1;overflow-y:auto;padding:16px}
+28
pywb-test/static/test-client.js
··· 1 + // Minimal test client - just injects a visible banner 2 + (function() { 3 + console.log('🔬 Seams test client loaded!'); 4 + 5 + function injectBanner() { 6 + const banner = document.createElement('div'); 7 + banner.style.cssText = ` 8 + position: fixed; 9 + top: 0; 10 + left: 0; 11 + right: 0; 12 + background: #ff6b6b; 13 + color: white; 14 + padding: 10px; 15 + text-align: center; 16 + z-index: 999999; 17 + font-family: monospace; 18 + `; 19 + banner.textContent = '🔬 Seams via proxy - Client Injected!'; 20 + document.body.appendChild(banner); 21 + } 22 + 23 + if (document.body) { 24 + injectBanner(); 25 + } else { 26 + document.addEventListener('DOMContentLoaded', injectBanner); 27 + } 28 + })();
+190
pywb-test/static/via-landing.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 + <title>Via - Seams Web Annotation Proxy</title> 7 + <style> 8 + * { 9 + margin: 0; 10 + padding: 0; 11 + box-sizing: border-box; 12 + } 13 + 14 + body { 15 + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; 16 + line-height: 1.6; 17 + color: #333; 18 + background: #f5f5f5; 19 + min-height: 100vh; 20 + display: flex; 21 + align-items: center; 22 + justify-content: center; 23 + padding: 20px; 24 + } 25 + 26 + .container { 27 + background: white; 28 + border-radius: 8px; 29 + box-shadow: 0 2px 8px rgba(0,0,0,0.1); 30 + max-width: 700px; 31 + width: 100%; 32 + padding: 50px 40px; 33 + } 34 + 35 + h1 { 36 + font-size: 2rem; 37 + margin-bottom: 10px; 38 + color: #222; 39 + font-weight: 600; 40 + } 41 + 42 + .tagline { 43 + font-size: 1rem; 44 + color: #666; 45 + margin-bottom: 30px; 46 + } 47 + 48 + .url-form { 49 + display: flex; 50 + gap: 10px; 51 + margin-bottom: 30px; 52 + } 53 + 54 + input[type="url"] { 55 + flex: 1; 56 + padding: 14px 18px; 57 + border: 2px solid #ddd; 58 + border-radius: 4px; 59 + font-size: 16px; 60 + transition: border-color 0.2s; 61 + } 62 + 63 + input[type="url"]:focus { 64 + outline: none; 65 + border-color: #0085ff; 66 + } 67 + 68 + button { 69 + padding: 14px 32px; 70 + background: #0085ff; 71 + color: white; 72 + border: none; 73 + border-radius: 4px; 74 + font-size: 16px; 75 + font-weight: 600; 76 + cursor: pointer; 77 + transition: background 0.2s; 78 + white-space: nowrap; 79 + } 80 + 81 + button:hover { 82 + background: #0070d9; 83 + } 84 + 85 + .info { 86 + padding: 20px; 87 + background: #f8f9fa; 88 + border-radius: 4px; 89 + font-size: 14px; 90 + color: #555; 91 + line-height: 1.6; 92 + } 93 + 94 + .info p { 95 + margin-bottom: 10px; 96 + } 97 + 98 + .info p:last-child { 99 + margin-bottom: 0; 100 + } 101 + 102 + .info a { 103 + color: #0085ff; 104 + text-decoration: none; 105 + } 106 + 107 + .info a:hover { 108 + text-decoration: underline; 109 + } 110 + 111 + .example { 112 + margin-top: 15px; 113 + font-size: 13px; 114 + color: #777; 115 + } 116 + 117 + .example a { 118 + color: #0085ff; 119 + text-decoration: none; 120 + } 121 + 122 + .example a:hover { 123 + text-decoration: underline; 124 + } 125 + 126 + .footer { 127 + margin-top: 40px; 128 + text-align: center; 129 + font-size: 14px; 130 + color: #999; 131 + } 132 + 133 + .footer a { 134 + color: #0085ff; 135 + text-decoration: none; 136 + } 137 + </style> 138 + </head> 139 + <body> 140 + <div class="container"> 141 + <h1>Seams Via</h1> 142 + <p class="tagline">View and annotate any web page</p> 143 + 144 + <form class="url-form" id="via-form"> 145 + <input 146 + type="url" 147 + id="url-input" 148 + placeholder="Paste a link to annotate" 149 + required 150 + autocomplete="url" 151 + autofocus 152 + /> 153 + <button type="submit">Annotate</button> 154 + </form> 155 + 156 + <div class="info"> 157 + <p>Via is a web annotation proxy that lets you view and create annotations on any web page using Seams.</p> 158 + <p>Enter a URL above to view the page with all public annotations from the Seams community.</p> 159 + </div> 160 + 161 + <div class="example"> 162 + <strong>Try it:</strong> 163 + <a href="/proxy/https://newsletter.squishy.computer/p/places-to-intervene-in-a-system"> 164 + View example article with annotations 165 + </a> 166 + </div> 167 + 168 + <div class="footer"> 169 + Powered by <a href="https://seams.so" target="_blank">Seams</a> 170 + </div> 171 + </div> 172 + 173 + <script> 174 + document.getElementById('via-form').addEventListener('submit', (e) => { 175 + e.preventDefault(); 176 + let url = document.getElementById('url-input').value.trim(); 177 + 178 + if (!url) return; 179 + 180 + // Add protocol if missing 181 + if (!url.match(/^https?:\/\//i)) { 182 + url = 'https://' + url; 183 + } 184 + 185 + // Redirect to pywb proxy route 186 + window.location.href = `/proxy/${url}`; 187 + }); 188 + </script> 189 + </body> 190 + </html>
+51
scripts/start-via.sh
··· 1 + #!/usr/bin/env bash 2 + # Start via proxy development servers 3 + 4 + set -e 5 + 6 + echo "🔨 Building via client scripts (watch mode)..." 7 + pnpm dev:via & 8 + BUILD_PID=$! 9 + 10 + # Wait for initial build 11 + sleep 2 12 + 13 + echo "" 14 + echo "🚀 Starting via proxy servers..." 15 + echo "" 16 + 17 + # Kill any existing processes on these ports 18 + lsof -ti:8081 | xargs kill -9 2>/dev/null || true 19 + lsof -ti:8082 | xargs kill -9 2>/dev/null || true 20 + 21 + # Ensure LD_LIBRARY_PATH is set for pywb (needed for gevent/greenlet) 22 + # This should already be set by the nix shell, but we make sure here 23 + if [ -z "$LD_LIBRARY_PATH" ]; then 24 + echo "⚠️ LD_LIBRARY_PATH not set. Run this from nix shell!" 25 + exit 1 26 + fi 27 + 28 + # Start pywb in background (must run from pywb-test directory) 29 + echo "📦 Starting pywb on port 8081..." 30 + (cd pywb-test && LD_LIBRARY_PATH="$LD_LIBRARY_PATH" wayback) & 31 + PYWB_PID=$! 32 + 33 + # Wait for pywb to start 34 + sleep 2 35 + 36 + # Start Caddy in foreground 37 + echo "🌐 Starting Caddy on port 8082..." 38 + echo "" 39 + echo "✅ Via proxy ready at http://localhost:8082" 40 + echo "" 41 + echo "Press Ctrl+C to stop all services" 42 + echo "" 43 + 44 + # Trap to kill background processes on exit 45 + trap "kill $BUILD_PID $PYWB_PID 2>/dev/null || true" EXIT 46 + 47 + # Run Caddy (blocks) 48 + caddy run 49 + 50 + # Cleanup 51 + kill $BUILD_PID $PYWB_PID 2>/dev/null || true