Auto-indexing service and GraphQL API for AT Protocol Records quickslice.slices.network/
atproto gleam graphql

feat: add support for request cancellations to the js client

authored by

Trezy and committed by tangled.org abcfb1a4 b28b31f4

+54 -34
+6 -3
quickslice-client-js/dist/client.d.ts
··· 8 8 export interface User { 9 9 did: string; 10 10 } 11 + export interface QueryOptions { 12 + signal?: AbortSignal; 13 + } 11 14 export declare class QuicksliceClient { 12 15 private server; 13 16 private clientId; ··· 56 59 /** 57 60 * Execute a GraphQL query (authenticated) 58 61 */ 59 - query<T = unknown>(query: string, variables?: Record<string, unknown>): Promise<T>; 62 + query<T = unknown>(query: string, variables?: Record<string, unknown>, options?: QueryOptions): Promise<T>; 60 63 /** 61 64 * Execute a GraphQL mutation (authenticated) 62 65 */ 63 - mutate<T = unknown>(mutation: string, variables?: Record<string, unknown>): Promise<T>; 66 + mutate<T = unknown>(mutation: string, variables?: Record<string, unknown>, options?: QueryOptions): Promise<T>; 64 67 /** 65 68 * Execute a public GraphQL query (no auth) 66 69 */ 67 - publicQuery<T = unknown>(query: string, variables?: Record<string, unknown>): Promise<T>; 70 + publicQuery<T = unknown>(query: string, variables?: Record<string, unknown>, options?: QueryOptions): Promise<T>; 68 71 }
+1 -1
quickslice-client-js/dist/graphql.d.ts
··· 9 9 /** 10 10 * Execute a GraphQL query or mutation 11 11 */ 12 - export declare function graphqlRequest<T = unknown>(storage: Storage, namespace: string, graphqlUrl: string, tokenUrl: string, query: string, variables?: Record<string, unknown>, requireAuth?: boolean): Promise<T>; 12 + export declare function graphqlRequest<T = unknown>(storage: Storage, namespace: string, graphqlUrl: string, tokenUrl: string, query: string, variables?: Record<string, unknown>, requireAuth?: boolean, signal?: AbortSignal): Promise<T>;
+1 -1
quickslice-client-js/dist/index.d.ts
··· 1 - export { QuicksliceClient, QuicksliceClientOptions, User } from './client'; 1 + export { QuicksliceClient, QuicksliceClientOptions, QueryOptions, User } from './client'; 2 2 export { QuicksliceError, LoginRequiredError, NetworkError, OAuthError, } from './errors'; 3 3 import { QuicksliceClient, QuicksliceClientOptions } from './client'; 4 4 /**
+11 -8
quickslice-client-js/dist/quickslice-client.esm.js
··· 411 411 } 412 412 413 413 // src/graphql.ts 414 - async function graphqlRequest(storage, namespace, graphqlUrl, tokenUrl, query, variables = {}, requireAuth = false) { 414 + async function graphqlRequest(storage, namespace, graphqlUrl, tokenUrl, query, variables = {}, requireAuth = false, signal) { 415 415 const headers = { 416 416 "Content-Type": "application/json" 417 417 }; ··· 427 427 const response = await fetch(graphqlUrl, { 428 428 method: "POST", 429 429 headers, 430 - body: JSON.stringify({ query, variables }) 430 + body: JSON.stringify({ query, variables }), 431 + signal 431 432 }); 432 433 if (!response.ok) { 433 434 throw new Error(`GraphQL request failed: ${response.statusText}`); ··· 528 529 /** 529 530 * Execute a GraphQL query (authenticated) 530 531 */ 531 - async query(query, variables = {}) { 532 + async query(query, variables = {}, options = {}) { 532 533 await this.init(); 533 534 return await graphqlRequest( 534 535 this.getStorage(), ··· 537 538 this.tokenUrl, 538 539 query, 539 540 variables, 540 - true 541 + true, 542 + options.signal 541 543 ); 542 544 } 543 545 /** 544 546 * Execute a GraphQL mutation (authenticated) 545 547 */ 546 - async mutate(mutation, variables = {}) { 547 - return this.query(mutation, variables); 548 + async mutate(mutation, variables = {}, options = {}) { 549 + return this.query(mutation, variables, options); 548 550 } 549 551 /** 550 552 * Execute a public GraphQL query (no auth) 551 553 */ 552 - async publicQuery(query, variables = {}) { 554 + async publicQuery(query, variables = {}, options = {}) { 553 555 await this.init(); 554 556 return await graphqlRequest( 555 557 this.getStorage(), ··· 558 560 this.tokenUrl, 559 561 query, 560 562 variables, 561 - false 563 + false, 564 + options.signal 562 565 ); 563 566 } 564 567 };
+2 -2
quickslice-client-js/dist/quickslice-client.esm.js.map
··· 1 1 { 2 2 "version": 3, 3 3 "sources": ["../src/storage/keys.ts", "../src/storage/storage.ts", "../src/utils/base64url.ts", "../src/utils/crypto.ts", "../src/auth/dpop.ts", "../src/auth/pkce.ts", "../src/storage/lock.ts", "../src/auth/tokens.ts", "../src/auth/oauth.ts", "../src/graphql.ts", "../src/client.ts", "../src/errors.ts", "../src/index.ts"], 4 - "sourcesContent": ["/**\n * Storage key factory - generates namespaced keys\n */\nexport interface StorageKeys {\n accessToken: string;\n refreshToken: string;\n tokenExpiresAt: string;\n clientId: string;\n userDid: string;\n codeVerifier: string;\n oauthState: string;\n redirectUri: string;\n}\n\nexport function createStorageKeys(namespace: string): StorageKeys {\n return {\n accessToken: `quickslice_${namespace}_access_token`,\n refreshToken: `quickslice_${namespace}_refresh_token`,\n tokenExpiresAt: `quickslice_${namespace}_token_expires_at`,\n clientId: `quickslice_${namespace}_client_id`,\n userDid: `quickslice_${namespace}_user_did`,\n codeVerifier: `quickslice_${namespace}_code_verifier`,\n oauthState: `quickslice_${namespace}_oauth_state`,\n redirectUri: `quickslice_${namespace}_redirect_uri`,\n };\n}\n\nexport type StorageKey = string;\n", "import { StorageKeys } from './keys';\n\n/**\n * Create a namespaced storage interface\n */\nexport function createStorage(keys: StorageKeys) {\n return {\n get(key: keyof StorageKeys): string | null {\n const storageKey = keys[key];\n // OAuth flow state stays in sessionStorage (per-tab)\n if (key === 'codeVerifier' || key === 'oauthState') {\n return sessionStorage.getItem(storageKey);\n }\n // Tokens go in localStorage (shared across tabs)\n return localStorage.getItem(storageKey);\n },\n\n set(key: keyof StorageKeys, value: string): void {\n const storageKey = keys[key];\n if (key === 'codeVerifier' || key === 'oauthState') {\n sessionStorage.setItem(storageKey, value);\n } else {\n localStorage.setItem(storageKey, value);\n }\n },\n\n remove(key: keyof StorageKeys): void {\n const storageKey = keys[key];\n sessionStorage.removeItem(storageKey);\n localStorage.removeItem(storageKey);\n },\n\n clear(): void {\n (Object.keys(keys) as Array<keyof StorageKeys>).forEach((key) => {\n const storageKey = keys[key];\n sessionStorage.removeItem(storageKey);\n localStorage.removeItem(storageKey);\n });\n },\n };\n}\n\nexport type Storage = ReturnType<typeof createStorage>;\n", "/**\n * Base64 URL encode a buffer (Uint8Array or ArrayBuffer)\n */\nexport function base64UrlEncode(buffer: ArrayBuffer | Uint8Array): string {\n const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary)\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n}\n\n/**\n * Generate a random base64url string\n */\nexport function generateRandomString(byteLength: number): string {\n const bytes = new Uint8Array(byteLength);\n crypto.getRandomValues(bytes);\n return base64UrlEncode(bytes);\n}\n", "import { base64UrlEncode } from './base64url';\n\n/**\n * SHA-256 hash, returned as base64url string\n */\nexport async function sha256Base64Url(data: string): Promise<string> {\n const encoder = new TextEncoder();\n const hash = await crypto.subtle.digest('SHA-256', encoder.encode(data));\n return base64UrlEncode(hash);\n}\n\n/**\n * Generate an 8-character namespace hash from clientId\n */\nexport async function generateNamespaceHash(clientId: string): Promise<string> {\n const encoder = new TextEncoder();\n const hash = await crypto.subtle.digest('SHA-256', encoder.encode(clientId));\n const hashArray = Array.from(new Uint8Array(hash));\n const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');\n return hashHex.substring(0, 8);\n}\n\n/**\n * Sign a JWT with an ECDSA P-256 private key\n */\nexport async function signJwt(\n header: Record<string, unknown>,\n payload: Record<string, unknown>,\n privateKey: CryptoKey\n): Promise<string> {\n const encoder = new TextEncoder();\n\n const headerB64 = base64UrlEncode(encoder.encode(JSON.stringify(header)));\n const payloadB64 = base64UrlEncode(encoder.encode(JSON.stringify(payload)));\n\n const signingInput = `${headerB64}.${payloadB64}`;\n\n const signature = await crypto.subtle.sign(\n { name: 'ECDSA', hash: 'SHA-256' },\n privateKey,\n encoder.encode(signingInput)\n );\n\n const signatureB64 = base64UrlEncode(signature);\n\n return `${signingInput}.${signatureB64}`;\n}\n", "import { generateRandomString } from '../utils/base64url';\nimport { sha256Base64Url, signJwt } from '../utils/crypto';\n\nconst DB_VERSION = 1;\nconst KEY_STORE = 'dpop-keys';\nconst KEY_ID = 'dpop-key';\n\ninterface DPoPKeyData {\n id: string;\n privateKey: CryptoKey;\n publicJwk: JsonWebKey;\n createdAt: number;\n}\n\n// Cache database connections per namespace\nconst dbPromises = new Map<string, Promise<IDBDatabase>>();\n\nfunction getDbName(namespace: string): string {\n return `quickslice-oauth-${namespace}`;\n}\n\nfunction openDatabase(namespace: string): Promise<IDBDatabase> {\n const existing = dbPromises.get(namespace);\n if (existing) return existing;\n\n const promise = new Promise<IDBDatabase>((resolve, reject) => {\n const request = indexedDB.open(getDbName(namespace), DB_VERSION);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result);\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n if (!db.objectStoreNames.contains(KEY_STORE)) {\n db.createObjectStore(KEY_STORE, { keyPath: 'id' });\n }\n };\n });\n\n dbPromises.set(namespace, promise);\n return promise;\n}\n\nasync function getDPoPKey(namespace: string): Promise<DPoPKeyData | null> {\n const db = await openDatabase(namespace);\n return new Promise((resolve, reject) => {\n const tx = db.transaction(KEY_STORE, 'readonly');\n const store = tx.objectStore(KEY_STORE);\n const request = store.get(KEY_ID);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result || null);\n });\n}\n\nasync function storeDPoPKey(\n namespace: string,\n privateKey: CryptoKey,\n publicJwk: JsonWebKey\n): Promise<void> {\n const db = await openDatabase(namespace);\n return new Promise((resolve, reject) => {\n const tx = db.transaction(KEY_STORE, 'readwrite');\n const store = tx.objectStore(KEY_STORE);\n const request = store.put({\n id: KEY_ID,\n privateKey,\n publicJwk,\n createdAt: Date.now(),\n });\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n}\n\nexport async function getOrCreateDPoPKey(namespace: string): Promise<DPoPKeyData> {\n const keyData = await getDPoPKey(namespace);\n\n if (keyData) {\n return keyData;\n }\n\n // Generate new P-256 key pair\n const keyPair = await crypto.subtle.generateKey(\n { name: 'ECDSA', namedCurve: 'P-256' },\n false, // NOT extractable - critical for security\n ['sign']\n );\n\n // Export public key as JWK\n const publicJwk = await crypto.subtle.exportKey('jwk', keyPair.publicKey);\n\n // Store in IndexedDB\n await storeDPoPKey(namespace, keyPair.privateKey, publicJwk);\n\n return {\n id: KEY_ID,\n privateKey: keyPair.privateKey,\n publicJwk,\n createdAt: Date.now(),\n };\n}\n\n/**\n * Create a DPoP proof JWT\n */\nexport async function createDPoPProof(\n namespace: string,\n method: string,\n url: string,\n accessToken: string | null = null\n): Promise<string> {\n const keyData = await getOrCreateDPoPKey(namespace);\n\n // Strip WebCrypto-specific fields from JWK for interoperability\n const { kty, crv, x, y } = keyData.publicJwk;\n const minimalJwk = { kty, crv, x, y };\n\n const header = {\n alg: 'ES256',\n typ: 'dpop+jwt',\n jwk: minimalJwk,\n };\n\n const payload: Record<string, unknown> = {\n jti: generateRandomString(16),\n htm: method,\n htu: url,\n iat: Math.floor(Date.now() / 1000),\n };\n\n // Add access token hash if provided (for resource requests)\n if (accessToken) {\n payload.ath = await sha256Base64Url(accessToken);\n }\n\n return await signJwt(header, payload, keyData.privateKey);\n}\n\n/**\n * Clear DPoP keys from IndexedDB\n */\nexport async function clearDPoPKeys(namespace: string): Promise<void> {\n const db = await openDatabase(namespace);\n return new Promise((resolve, reject) => {\n const tx = db.transaction(KEY_STORE, 'readwrite');\n const store = tx.objectStore(KEY_STORE);\n const request = store.clear();\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n}\n", "import { base64UrlEncode, generateRandomString } from '../utils/base64url';\n\n/**\n * Generate a PKCE code verifier (32 random bytes, base64url encoded)\n */\nexport function generateCodeVerifier(): string {\n return generateRandomString(32);\n}\n\n/**\n * Generate a PKCE code challenge from a verifier (SHA-256, base64url encoded)\n */\nexport async function generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const hash = await crypto.subtle.digest('SHA-256', data);\n return base64UrlEncode(hash);\n}\n\n/**\n * Generate a random state parameter for CSRF protection\n */\nexport function generateState(): string {\n return generateRandomString(16);\n}\n", "const LOCK_TIMEOUT = 5000; // 5 seconds\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction getLockKey(namespace: string, key: string): string {\n return `quickslice_${namespace}_lock_${key}`;\n}\n\n/**\n * Acquire a lock using localStorage for multi-tab coordination\n */\nexport async function acquireLock(\n namespace: string,\n key: string,\n timeout = LOCK_TIMEOUT\n): Promise<string | null> {\n const lockKey = getLockKey(namespace, key);\n const lockValue = `${Date.now()}_${Math.random()}`;\n const deadline = Date.now() + timeout;\n\n while (Date.now() < deadline) {\n const existing = localStorage.getItem(lockKey);\n\n if (existing) {\n // Check if lock is stale (older than timeout)\n const [timestamp] = existing.split('_');\n if (Date.now() - parseInt(timestamp) > LOCK_TIMEOUT) {\n // Lock is stale, remove it\n localStorage.removeItem(lockKey);\n } else {\n // Lock is held, wait and retry\n await sleep(50);\n continue;\n }\n }\n\n // Try to acquire\n localStorage.setItem(lockKey, lockValue);\n\n // Verify we got it (handle race condition)\n await sleep(10);\n if (localStorage.getItem(lockKey) === lockValue) {\n return lockValue; // Lock acquired\n }\n }\n\n return null; // Failed to acquire\n}\n\n/**\n * Release a lock\n */\nexport function releaseLock(namespace: string, key: string, lockValue: string): void {\n const lockKey = getLockKey(namespace, key);\n // Only release if we still hold it\n if (localStorage.getItem(lockKey) === lockValue) {\n localStorage.removeItem(lockKey);\n }\n}\n", "import { Storage } from '../storage/storage';\nimport { acquireLock, releaseLock } from '../storage/lock';\nimport { createDPoPProof } from './dpop';\n\nconst TOKEN_REFRESH_BUFFER_MS = 60000; // 60 seconds before expiry\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Refresh tokens using the refresh token\n */\nasync function refreshTokens(\n storage: Storage,\n namespace: string,\n tokenUrl: string\n): Promise<string> {\n const refreshToken = storage.get('refreshToken');\n const clientId = storage.get('clientId');\n\n if (!refreshToken || !clientId) {\n throw new Error('No refresh token available');\n }\n\n const dpopProof = await createDPoPProof(namespace, 'POST', tokenUrl);\n\n const response = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n DPoP: dpopProof,\n },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n client_id: clientId,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n throw new Error(\n `Token refresh failed: ${errorData.error_description || response.statusText}`\n );\n }\n\n const tokens = await response.json();\n\n // Store new tokens (rotation - new refresh token each time)\n storage.set('accessToken', tokens.access_token);\n if (tokens.refresh_token) {\n storage.set('refreshToken', tokens.refresh_token);\n }\n\n const expiresAt = Date.now() + tokens.expires_in * 1000;\n storage.set('tokenExpiresAt', expiresAt.toString());\n\n return tokens.access_token;\n}\n\n/**\n * Get a valid access token, refreshing if necessary.\n * Uses multi-tab locking to prevent duplicate refresh requests.\n */\nexport async function getValidAccessToken(\n storage: Storage,\n namespace: string,\n tokenUrl: string\n): Promise<string> {\n const accessToken = storage.get('accessToken');\n const expiresAt = parseInt(storage.get('tokenExpiresAt') || '0');\n\n // Check if token is still valid (with buffer)\n if (accessToken && Date.now() < expiresAt - TOKEN_REFRESH_BUFFER_MS) {\n return accessToken;\n }\n\n // Need to refresh - acquire lock first\n const lockKey = 'token_refresh';\n const lockValue = await acquireLock(namespace, lockKey);\n\n if (!lockValue) {\n // Failed to acquire lock, another tab is refreshing\n // Wait a bit and check cache again\n await sleep(100);\n const freshToken = storage.get('accessToken');\n const freshExpiry = parseInt(storage.get('tokenExpiresAt') || '0');\n if (freshToken && Date.now() < freshExpiry - TOKEN_REFRESH_BUFFER_MS) {\n return freshToken;\n }\n throw new Error('Failed to refresh token');\n }\n\n try {\n // Double-check after acquiring lock\n const freshToken = storage.get('accessToken');\n const freshExpiry = parseInt(storage.get('tokenExpiresAt') || '0');\n if (freshToken && Date.now() < freshExpiry - TOKEN_REFRESH_BUFFER_MS) {\n return freshToken;\n }\n\n // Actually refresh\n return await refreshTokens(storage, namespace, tokenUrl);\n } finally {\n releaseLock(namespace, lockKey, lockValue);\n }\n}\n\n/**\n * Store tokens from OAuth response\n */\nexport function storeTokens(\n storage: Storage,\n tokens: {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n sub?: string;\n }\n): void {\n storage.set('accessToken', tokens.access_token);\n if (tokens.refresh_token) {\n storage.set('refreshToken', tokens.refresh_token);\n }\n\n const expiresAt = Date.now() + tokens.expires_in * 1000;\n storage.set('tokenExpiresAt', expiresAt.toString());\n\n if (tokens.sub) {\n storage.set('userDid', tokens.sub);\n }\n}\n\n/**\n * Check if we have a valid session\n */\nexport function hasValidSession(storage: Storage): boolean {\n const accessToken = storage.get('accessToken');\n const refreshToken = storage.get('refreshToken');\n return !!(accessToken || refreshToken);\n}\n", "import { Storage } from '../storage/storage';\nimport { createDPoPProof, clearDPoPKeys } from './dpop';\nimport { generateCodeVerifier, generateCodeChallenge, generateState } from './pkce';\nimport { storeTokens } from './tokens';\n\nexport interface LoginOptions {\n handle?: string;\n redirectUri?: string;\n scope?: string;\n}\n\n/**\n * Initiate OAuth login flow with PKCE\n */\nexport async function initiateLogin(\n storage: Storage,\n authorizeUrl: string,\n clientId: string,\n options: LoginOptions = {}\n): Promise<void> {\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = await generateCodeChallenge(codeVerifier);\n const state = generateState();\n\n // Build redirect URI (use provided or derive from current page)\n const redirectUri = options.redirectUri || (window.location.origin + window.location.pathname);\n\n // Store for callback\n storage.set('codeVerifier', codeVerifier);\n storage.set('oauthState', state);\n storage.set('clientId', clientId);\n storage.set('redirectUri', redirectUri);\n\n // Build authorization URL\n const params = new URLSearchParams({\n client_id: clientId,\n redirect_uri: redirectUri,\n response_type: 'code',\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n state: state,\n });\n\n if (options.handle) {\n params.set('login_hint', options.handle);\n }\n\n if (options.scope) {\n params.set('scope', options.scope);\n }\n\n window.location.href = `${authorizeUrl}?${params.toString()}`;\n}\n\n/**\n * Handle OAuth callback - exchange code for tokens\n * Returns true if callback was handled, false if not a callback\n */\nexport async function handleOAuthCallback(\n storage: Storage,\n namespace: string,\n tokenUrl: string\n): Promise<boolean> {\n const params = new URLSearchParams(window.location.search);\n const code = params.get('code');\n const state = params.get('state');\n const error = params.get('error');\n\n if (error) {\n throw new Error(\n `OAuth error: ${error} - ${params.get('error_description') || ''}`\n );\n }\n\n if (!code || !state) {\n return false; // Not a callback\n }\n\n // Verify state\n const storedState = storage.get('oauthState');\n if (state !== storedState) {\n throw new Error('OAuth state mismatch - possible CSRF attack');\n }\n\n // Get stored values\n const codeVerifier = storage.get('codeVerifier');\n const clientId = storage.get('clientId');\n const redirectUri = storage.get('redirectUri');\n\n if (!codeVerifier || !clientId || !redirectUri) {\n throw new Error('Missing OAuth session data');\n }\n\n // Exchange code for tokens with DPoP\n const dpopProof = await createDPoPProof(namespace, 'POST', tokenUrl);\n\n const tokenResponse = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n DPoP: dpopProof,\n },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n code: code,\n redirect_uri: redirectUri,\n client_id: clientId,\n code_verifier: codeVerifier,\n }),\n });\n\n if (!tokenResponse.ok) {\n const errorData = await tokenResponse.json().catch(() => ({}));\n throw new Error(\n `Token exchange failed: ${errorData.error_description || tokenResponse.statusText}`\n );\n }\n\n const tokens = await tokenResponse.json();\n\n // Store tokens\n storeTokens(storage, tokens);\n\n // Clean up OAuth state\n storage.remove('codeVerifier');\n storage.remove('oauthState');\n storage.remove('redirectUri');\n\n // Clear URL params\n window.history.replaceState({}, document.title, window.location.pathname);\n\n return true;\n}\n\n/**\n * Logout - clear all stored data\n */\nexport async function logout(\n storage: Storage,\n namespace: string,\n options: { reload?: boolean } = {}\n): Promise<void> {\n storage.clear();\n await clearDPoPKeys(namespace);\n\n if (options.reload !== false) {\n window.location.reload();\n }\n}\n", "import { createDPoPProof } from './auth/dpop';\nimport { getValidAccessToken } from './auth/tokens';\nimport { Storage } from './storage/storage';\n\nexport interface GraphQLResponse<T = unknown> {\n data?: T;\n errors?: Array<{ message: string; path?: string[] }>;\n}\n\n/**\n * Execute a GraphQL query or mutation\n */\nexport async function graphqlRequest<T = unknown>(\n storage: Storage,\n namespace: string,\n graphqlUrl: string,\n tokenUrl: string,\n query: string,\n variables: Record<string, unknown> = {},\n requireAuth = false\n): Promise<T> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n if (requireAuth) {\n const token = await getValidAccessToken(storage, namespace, tokenUrl);\n if (!token) {\n throw new Error('Not authenticated');\n }\n\n // Create DPoP proof bound to this request\n const dpopProof = await createDPoPProof(namespace, 'POST', graphqlUrl, token);\n\n headers['Authorization'] = `DPoP ${token}`;\n headers['DPoP'] = dpopProof;\n }\n\n const response = await fetch(graphqlUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify({ query, variables }),\n });\n\n if (!response.ok) {\n throw new Error(`GraphQL request failed: ${response.statusText}`);\n }\n\n const result: GraphQLResponse<T> = await response.json();\n\n if (result.errors && result.errors.length > 0) {\n throw new Error(`GraphQL error: ${result.errors[0].message}`);\n }\n\n return result.data as T;\n}\n", "import { createStorageKeys } from './storage/keys';\nimport { createStorage, Storage } from './storage/storage';\nimport { getOrCreateDPoPKey } from './auth/dpop';\nimport { initiateLogin, handleOAuthCallback, logout as doLogout, LoginOptions } from './auth/oauth';\nimport { getValidAccessToken, hasValidSession } from './auth/tokens';\nimport { graphqlRequest } from './graphql';\nimport { generateNamespaceHash } from './utils/crypto';\n\nexport interface QuicksliceClientOptions {\n server: string;\n clientId: string;\n redirectUri?: string;\n scope?: string;\n}\n\nexport interface User {\n did: string;\n}\n\nexport class QuicksliceClient {\n private server: string;\n private clientId: string;\n private redirectUri?: string;\n private scope?: string;\n private graphqlUrl: string;\n private authorizeUrl: string;\n private tokenUrl: string;\n private initialized = false;\n private namespace: string = '';\n private storage: Storage | null = null;\n\n constructor(options: QuicksliceClientOptions) {\n this.server = options.server.replace(/\\/$/, ''); // Remove trailing slash\n this.clientId = options.clientId;\n this.redirectUri = options.redirectUri;\n this.scope = options.scope;\n\n this.graphqlUrl = `${this.server}/graphql`;\n this.authorizeUrl = `${this.server}/oauth/authorize`;\n this.tokenUrl = `${this.server}/oauth/token`;\n }\n\n /**\n * Initialize the client - must be called before other methods\n */\n async init(): Promise<void> {\n if (this.initialized) return;\n\n // Generate namespace from clientId\n this.namespace = await generateNamespaceHash(this.clientId);\n\n // Create namespaced storage\n const keys = createStorageKeys(this.namespace);\n this.storage = createStorage(keys);\n\n // Ensure DPoP key exists\n await getOrCreateDPoPKey(this.namespace);\n\n this.initialized = true;\n }\n\n private getStorage(): Storage {\n if (!this.storage) {\n throw new Error('Client not initialized. Call init() first.');\n }\n return this.storage;\n }\n\n /**\n * Start OAuth login flow\n */\n async loginWithRedirect(options: LoginOptions = {}): Promise<void> {\n await this.init();\n await initiateLogin(this.getStorage(), this.authorizeUrl, this.clientId, {\n ...options,\n redirectUri: options.redirectUri || this.redirectUri,\n scope: options.scope || this.scope,\n });\n }\n\n /**\n * Handle OAuth callback after redirect\n * Returns true if callback was handled\n */\n async handleRedirectCallback(): Promise<boolean> {\n await this.init();\n return await handleOAuthCallback(this.getStorage(), this.namespace, this.tokenUrl);\n }\n\n /**\n * Logout and clear all stored data\n */\n async logout(options: { reload?: boolean } = {}): Promise<void> {\n await this.init();\n await doLogout(this.getStorage(), this.namespace, options);\n }\n\n /**\n * Check if user is authenticated\n */\n async isAuthenticated(): Promise<boolean> {\n await this.init();\n return hasValidSession(this.getStorage());\n }\n\n /**\n * Get current user's DID (from stored token data)\n * For richer profile info, use client.query() with your own schema\n */\n async getUser(): Promise<User | null> {\n await this.init();\n if (!hasValidSession(this.getStorage())) {\n return null;\n }\n\n const did = this.getStorage().get('userDid');\n if (!did) {\n return null;\n }\n\n return { did };\n }\n\n /**\n * Get access token (auto-refreshes if needed)\n */\n async getAccessToken(): Promise<string> {\n await this.init();\n return await getValidAccessToken(this.getStorage(), this.namespace, this.tokenUrl);\n }\n\n /**\n * Execute a GraphQL query (authenticated)\n */\n async query<T = unknown>(\n query: string,\n variables: Record<string, unknown> = {}\n ): Promise<T> {\n await this.init();\n return await graphqlRequest<T>(\n this.getStorage(),\n this.namespace,\n this.graphqlUrl,\n this.tokenUrl,\n query,\n variables,\n true\n );\n }\n\n /**\n * Execute a GraphQL mutation (authenticated)\n */\n async mutate<T = unknown>(\n mutation: string,\n variables: Record<string, unknown> = {}\n ): Promise<T> {\n return this.query<T>(mutation, variables);\n }\n\n /**\n * Execute a public GraphQL query (no auth)\n */\n async publicQuery<T = unknown>(\n query: string,\n variables: Record<string, unknown> = {}\n ): Promise<T> {\n await this.init();\n return await graphqlRequest<T>(\n this.getStorage(),\n this.namespace,\n this.graphqlUrl,\n this.tokenUrl,\n query,\n variables,\n false\n );\n }\n}\n", "/**\n * Base error class for Quickslice client errors\n */\nexport class QuicksliceError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'QuicksliceError';\n }\n}\n\n/**\n * Thrown when authentication is required but user is not logged in\n */\nexport class LoginRequiredError extends QuicksliceError {\n constructor(message = 'Login required') {\n super(message);\n this.name = 'LoginRequiredError';\n }\n}\n\n/**\n * Thrown when network request fails\n */\nexport class NetworkError extends QuicksliceError {\n constructor(message: string) {\n super(message);\n this.name = 'NetworkError';\n }\n}\n\n/**\n * Thrown when OAuth flow fails\n */\nexport class OAuthError extends QuicksliceError {\n public code: string;\n public description?: string;\n\n constructor(code: string, description?: string) {\n super(`OAuth error: ${code}${description ? ` - ${description}` : ''}`);\n this.name = 'OAuthError';\n this.code = code;\n this.description = description;\n }\n}\n", "export { QuicksliceClient, QuicksliceClientOptions, User } from './client';\nexport {\n QuicksliceError,\n LoginRequiredError,\n NetworkError,\n OAuthError,\n} from './errors';\n\nimport { QuicksliceClient, QuicksliceClientOptions } from './client';\n\n/**\n * Create and initialize a Quickslice client\n */\nexport async function createQuicksliceClient(\n options: QuicksliceClientOptions\n): Promise<QuicksliceClient> {\n const client = new QuicksliceClient(options);\n await client.init();\n return client;\n}\n"], 5 - "mappings": "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", 4 + "sourcesContent": ["/**\n * Storage key factory - generates namespaced keys\n */\nexport interface StorageKeys {\n accessToken: string;\n refreshToken: string;\n tokenExpiresAt: string;\n clientId: string;\n userDid: string;\n codeVerifier: string;\n oauthState: string;\n redirectUri: string;\n}\n\nexport function createStorageKeys(namespace: string): StorageKeys {\n return {\n accessToken: `quickslice_${namespace}_access_token`,\n refreshToken: `quickslice_${namespace}_refresh_token`,\n tokenExpiresAt: `quickslice_${namespace}_token_expires_at`,\n clientId: `quickslice_${namespace}_client_id`,\n userDid: `quickslice_${namespace}_user_did`,\n codeVerifier: `quickslice_${namespace}_code_verifier`,\n oauthState: `quickslice_${namespace}_oauth_state`,\n redirectUri: `quickslice_${namespace}_redirect_uri`,\n };\n}\n\nexport type StorageKey = string;\n", "import { StorageKeys } from './keys';\n\n/**\n * Create a namespaced storage interface\n */\nexport function createStorage(keys: StorageKeys) {\n return {\n get(key: keyof StorageKeys): string | null {\n const storageKey = keys[key];\n // OAuth flow state stays in sessionStorage (per-tab)\n if (key === 'codeVerifier' || key === 'oauthState') {\n return sessionStorage.getItem(storageKey);\n }\n // Tokens go in localStorage (shared across tabs)\n return localStorage.getItem(storageKey);\n },\n\n set(key: keyof StorageKeys, value: string): void {\n const storageKey = keys[key];\n if (key === 'codeVerifier' || key === 'oauthState') {\n sessionStorage.setItem(storageKey, value);\n } else {\n localStorage.setItem(storageKey, value);\n }\n },\n\n remove(key: keyof StorageKeys): void {\n const storageKey = keys[key];\n sessionStorage.removeItem(storageKey);\n localStorage.removeItem(storageKey);\n },\n\n clear(): void {\n (Object.keys(keys) as Array<keyof StorageKeys>).forEach((key) => {\n const storageKey = keys[key];\n sessionStorage.removeItem(storageKey);\n localStorage.removeItem(storageKey);\n });\n },\n };\n}\n\nexport type Storage = ReturnType<typeof createStorage>;\n", "/**\n * Base64 URL encode a buffer (Uint8Array or ArrayBuffer)\n */\nexport function base64UrlEncode(buffer: ArrayBuffer | Uint8Array): string {\n const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary)\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n}\n\n/**\n * Generate a random base64url string\n */\nexport function generateRandomString(byteLength: number): string {\n const bytes = new Uint8Array(byteLength);\n crypto.getRandomValues(bytes);\n return base64UrlEncode(bytes);\n}\n", "import { base64UrlEncode } from './base64url';\n\n/**\n * SHA-256 hash, returned as base64url string\n */\nexport async function sha256Base64Url(data: string): Promise<string> {\n const encoder = new TextEncoder();\n const hash = await crypto.subtle.digest('SHA-256', encoder.encode(data));\n return base64UrlEncode(hash);\n}\n\n/**\n * Generate an 8-character namespace hash from clientId\n */\nexport async function generateNamespaceHash(clientId: string): Promise<string> {\n const encoder = new TextEncoder();\n const hash = await crypto.subtle.digest('SHA-256', encoder.encode(clientId));\n const hashArray = Array.from(new Uint8Array(hash));\n const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');\n return hashHex.substring(0, 8);\n}\n\n/**\n * Sign a JWT with an ECDSA P-256 private key\n */\nexport async function signJwt(\n header: Record<string, unknown>,\n payload: Record<string, unknown>,\n privateKey: CryptoKey\n): Promise<string> {\n const encoder = new TextEncoder();\n\n const headerB64 = base64UrlEncode(encoder.encode(JSON.stringify(header)));\n const payloadB64 = base64UrlEncode(encoder.encode(JSON.stringify(payload)));\n\n const signingInput = `${headerB64}.${payloadB64}`;\n\n const signature = await crypto.subtle.sign(\n { name: 'ECDSA', hash: 'SHA-256' },\n privateKey,\n encoder.encode(signingInput)\n );\n\n const signatureB64 = base64UrlEncode(signature);\n\n return `${signingInput}.${signatureB64}`;\n}\n", "import { generateRandomString } from '../utils/base64url';\nimport { sha256Base64Url, signJwt } from '../utils/crypto';\n\nconst DB_VERSION = 1;\nconst KEY_STORE = 'dpop-keys';\nconst KEY_ID = 'dpop-key';\n\ninterface DPoPKeyData {\n id: string;\n privateKey: CryptoKey;\n publicJwk: JsonWebKey;\n createdAt: number;\n}\n\n// Cache database connections per namespace\nconst dbPromises = new Map<string, Promise<IDBDatabase>>();\n\nfunction getDbName(namespace: string): string {\n return `quickslice-oauth-${namespace}`;\n}\n\nfunction openDatabase(namespace: string): Promise<IDBDatabase> {\n const existing = dbPromises.get(namespace);\n if (existing) return existing;\n\n const promise = new Promise<IDBDatabase>((resolve, reject) => {\n const request = indexedDB.open(getDbName(namespace), DB_VERSION);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result);\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n if (!db.objectStoreNames.contains(KEY_STORE)) {\n db.createObjectStore(KEY_STORE, { keyPath: 'id' });\n }\n };\n });\n\n dbPromises.set(namespace, promise);\n return promise;\n}\n\nasync function getDPoPKey(namespace: string): Promise<DPoPKeyData | null> {\n const db = await openDatabase(namespace);\n return new Promise((resolve, reject) => {\n const tx = db.transaction(KEY_STORE, 'readonly');\n const store = tx.objectStore(KEY_STORE);\n const request = store.get(KEY_ID);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result || null);\n });\n}\n\nasync function storeDPoPKey(\n namespace: string,\n privateKey: CryptoKey,\n publicJwk: JsonWebKey\n): Promise<void> {\n const db = await openDatabase(namespace);\n return new Promise((resolve, reject) => {\n const tx = db.transaction(KEY_STORE, 'readwrite');\n const store = tx.objectStore(KEY_STORE);\n const request = store.put({\n id: KEY_ID,\n privateKey,\n publicJwk,\n createdAt: Date.now(),\n });\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n}\n\nexport async function getOrCreateDPoPKey(namespace: string): Promise<DPoPKeyData> {\n const keyData = await getDPoPKey(namespace);\n\n if (keyData) {\n return keyData;\n }\n\n // Generate new P-256 key pair\n const keyPair = await crypto.subtle.generateKey(\n { name: 'ECDSA', namedCurve: 'P-256' },\n false, // NOT extractable - critical for security\n ['sign']\n );\n\n // Export public key as JWK\n const publicJwk = await crypto.subtle.exportKey('jwk', keyPair.publicKey);\n\n // Store in IndexedDB\n await storeDPoPKey(namespace, keyPair.privateKey, publicJwk);\n\n return {\n id: KEY_ID,\n privateKey: keyPair.privateKey,\n publicJwk,\n createdAt: Date.now(),\n };\n}\n\n/**\n * Create a DPoP proof JWT\n */\nexport async function createDPoPProof(\n namespace: string,\n method: string,\n url: string,\n accessToken: string | null = null\n): Promise<string> {\n const keyData = await getOrCreateDPoPKey(namespace);\n\n // Strip WebCrypto-specific fields from JWK for interoperability\n const { kty, crv, x, y } = keyData.publicJwk;\n const minimalJwk = { kty, crv, x, y };\n\n const header = {\n alg: 'ES256',\n typ: 'dpop+jwt',\n jwk: minimalJwk,\n };\n\n const payload: Record<string, unknown> = {\n jti: generateRandomString(16),\n htm: method,\n htu: url,\n iat: Math.floor(Date.now() / 1000),\n };\n\n // Add access token hash if provided (for resource requests)\n if (accessToken) {\n payload.ath = await sha256Base64Url(accessToken);\n }\n\n return await signJwt(header, payload, keyData.privateKey);\n}\n\n/**\n * Clear DPoP keys from IndexedDB\n */\nexport async function clearDPoPKeys(namespace: string): Promise<void> {\n const db = await openDatabase(namespace);\n return new Promise((resolve, reject) => {\n const tx = db.transaction(KEY_STORE, 'readwrite');\n const store = tx.objectStore(KEY_STORE);\n const request = store.clear();\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n}\n", "import { base64UrlEncode, generateRandomString } from '../utils/base64url';\n\n/**\n * Generate a PKCE code verifier (32 random bytes, base64url encoded)\n */\nexport function generateCodeVerifier(): string {\n return generateRandomString(32);\n}\n\n/**\n * Generate a PKCE code challenge from a verifier (SHA-256, base64url encoded)\n */\nexport async function generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const hash = await crypto.subtle.digest('SHA-256', data);\n return base64UrlEncode(hash);\n}\n\n/**\n * Generate a random state parameter for CSRF protection\n */\nexport function generateState(): string {\n return generateRandomString(16);\n}\n", "const LOCK_TIMEOUT = 5000; // 5 seconds\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction getLockKey(namespace: string, key: string): string {\n return `quickslice_${namespace}_lock_${key}`;\n}\n\n/**\n * Acquire a lock using localStorage for multi-tab coordination\n */\nexport async function acquireLock(\n namespace: string,\n key: string,\n timeout = LOCK_TIMEOUT\n): Promise<string | null> {\n const lockKey = getLockKey(namespace, key);\n const lockValue = `${Date.now()}_${Math.random()}`;\n const deadline = Date.now() + timeout;\n\n while (Date.now() < deadline) {\n const existing = localStorage.getItem(lockKey);\n\n if (existing) {\n // Check if lock is stale (older than timeout)\n const [timestamp] = existing.split('_');\n if (Date.now() - parseInt(timestamp) > LOCK_TIMEOUT) {\n // Lock is stale, remove it\n localStorage.removeItem(lockKey);\n } else {\n // Lock is held, wait and retry\n await sleep(50);\n continue;\n }\n }\n\n // Try to acquire\n localStorage.setItem(lockKey, lockValue);\n\n // Verify we got it (handle race condition)\n await sleep(10);\n if (localStorage.getItem(lockKey) === lockValue) {\n return lockValue; // Lock acquired\n }\n }\n\n return null; // Failed to acquire\n}\n\n/**\n * Release a lock\n */\nexport function releaseLock(namespace: string, key: string, lockValue: string): void {\n const lockKey = getLockKey(namespace, key);\n // Only release if we still hold it\n if (localStorage.getItem(lockKey) === lockValue) {\n localStorage.removeItem(lockKey);\n }\n}\n", "import { Storage } from '../storage/storage';\nimport { acquireLock, releaseLock } from '../storage/lock';\nimport { createDPoPProof } from './dpop';\n\nconst TOKEN_REFRESH_BUFFER_MS = 60000; // 60 seconds before expiry\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Refresh tokens using the refresh token\n */\nasync function refreshTokens(\n storage: Storage,\n namespace: string,\n tokenUrl: string\n): Promise<string> {\n const refreshToken = storage.get('refreshToken');\n const clientId = storage.get('clientId');\n\n if (!refreshToken || !clientId) {\n throw new Error('No refresh token available');\n }\n\n const dpopProof = await createDPoPProof(namespace, 'POST', tokenUrl);\n\n const response = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n DPoP: dpopProof,\n },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n client_id: clientId,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n throw new Error(\n `Token refresh failed: ${errorData.error_description || response.statusText}`\n );\n }\n\n const tokens = await response.json();\n\n // Store new tokens (rotation - new refresh token each time)\n storage.set('accessToken', tokens.access_token);\n if (tokens.refresh_token) {\n storage.set('refreshToken', tokens.refresh_token);\n }\n\n const expiresAt = Date.now() + tokens.expires_in * 1000;\n storage.set('tokenExpiresAt', expiresAt.toString());\n\n return tokens.access_token;\n}\n\n/**\n * Get a valid access token, refreshing if necessary.\n * Uses multi-tab locking to prevent duplicate refresh requests.\n */\nexport async function getValidAccessToken(\n storage: Storage,\n namespace: string,\n tokenUrl: string\n): Promise<string> {\n const accessToken = storage.get('accessToken');\n const expiresAt = parseInt(storage.get('tokenExpiresAt') || '0');\n\n // Check if token is still valid (with buffer)\n if (accessToken && Date.now() < expiresAt - TOKEN_REFRESH_BUFFER_MS) {\n return accessToken;\n }\n\n // Need to refresh - acquire lock first\n const lockKey = 'token_refresh';\n const lockValue = await acquireLock(namespace, lockKey);\n\n if (!lockValue) {\n // Failed to acquire lock, another tab is refreshing\n // Wait a bit and check cache again\n await sleep(100);\n const freshToken = storage.get('accessToken');\n const freshExpiry = parseInt(storage.get('tokenExpiresAt') || '0');\n if (freshToken && Date.now() < freshExpiry - TOKEN_REFRESH_BUFFER_MS) {\n return freshToken;\n }\n throw new Error('Failed to refresh token');\n }\n\n try {\n // Double-check after acquiring lock\n const freshToken = storage.get('accessToken');\n const freshExpiry = parseInt(storage.get('tokenExpiresAt') || '0');\n if (freshToken && Date.now() < freshExpiry - TOKEN_REFRESH_BUFFER_MS) {\n return freshToken;\n }\n\n // Actually refresh\n return await refreshTokens(storage, namespace, tokenUrl);\n } finally {\n releaseLock(namespace, lockKey, lockValue);\n }\n}\n\n/**\n * Store tokens from OAuth response\n */\nexport function storeTokens(\n storage: Storage,\n tokens: {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n sub?: string;\n }\n): void {\n storage.set('accessToken', tokens.access_token);\n if (tokens.refresh_token) {\n storage.set('refreshToken', tokens.refresh_token);\n }\n\n const expiresAt = Date.now() + tokens.expires_in * 1000;\n storage.set('tokenExpiresAt', expiresAt.toString());\n\n if (tokens.sub) {\n storage.set('userDid', tokens.sub);\n }\n}\n\n/**\n * Check if we have a valid session\n */\nexport function hasValidSession(storage: Storage): boolean {\n const accessToken = storage.get('accessToken');\n const refreshToken = storage.get('refreshToken');\n return !!(accessToken || refreshToken);\n}\n", "import { Storage } from '../storage/storage';\nimport { createDPoPProof, clearDPoPKeys } from './dpop';\nimport { generateCodeVerifier, generateCodeChallenge, generateState } from './pkce';\nimport { storeTokens } from './tokens';\n\nexport interface LoginOptions {\n handle?: string;\n redirectUri?: string;\n scope?: string;\n}\n\n/**\n * Initiate OAuth login flow with PKCE\n */\nexport async function initiateLogin(\n storage: Storage,\n authorizeUrl: string,\n clientId: string,\n options: LoginOptions = {}\n): Promise<void> {\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = await generateCodeChallenge(codeVerifier);\n const state = generateState();\n\n // Build redirect URI (use provided or derive from current page)\n const redirectUri = options.redirectUri || (window.location.origin + window.location.pathname);\n\n // Store for callback\n storage.set('codeVerifier', codeVerifier);\n storage.set('oauthState', state);\n storage.set('clientId', clientId);\n storage.set('redirectUri', redirectUri);\n\n // Build authorization URL\n const params = new URLSearchParams({\n client_id: clientId,\n redirect_uri: redirectUri,\n response_type: 'code',\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n state: state,\n });\n\n if (options.handle) {\n params.set('login_hint', options.handle);\n }\n\n if (options.scope) {\n params.set('scope', options.scope);\n }\n\n window.location.href = `${authorizeUrl}?${params.toString()}`;\n}\n\n/**\n * Handle OAuth callback - exchange code for tokens\n * Returns true if callback was handled, false if not a callback\n */\nexport async function handleOAuthCallback(\n storage: Storage,\n namespace: string,\n tokenUrl: string\n): Promise<boolean> {\n const params = new URLSearchParams(window.location.search);\n const code = params.get('code');\n const state = params.get('state');\n const error = params.get('error');\n\n if (error) {\n throw new Error(\n `OAuth error: ${error} - ${params.get('error_description') || ''}`\n );\n }\n\n if (!code || !state) {\n return false; // Not a callback\n }\n\n // Verify state\n const storedState = storage.get('oauthState');\n if (state !== storedState) {\n throw new Error('OAuth state mismatch - possible CSRF attack');\n }\n\n // Get stored values\n const codeVerifier = storage.get('codeVerifier');\n const clientId = storage.get('clientId');\n const redirectUri = storage.get('redirectUri');\n\n if (!codeVerifier || !clientId || !redirectUri) {\n throw new Error('Missing OAuth session data');\n }\n\n // Exchange code for tokens with DPoP\n const dpopProof = await createDPoPProof(namespace, 'POST', tokenUrl);\n\n const tokenResponse = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n DPoP: dpopProof,\n },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n code: code,\n redirect_uri: redirectUri,\n client_id: clientId,\n code_verifier: codeVerifier,\n }),\n });\n\n if (!tokenResponse.ok) {\n const errorData = await tokenResponse.json().catch(() => ({}));\n throw new Error(\n `Token exchange failed: ${errorData.error_description || tokenResponse.statusText}`\n );\n }\n\n const tokens = await tokenResponse.json();\n\n // Store tokens\n storeTokens(storage, tokens);\n\n // Clean up OAuth state\n storage.remove('codeVerifier');\n storage.remove('oauthState');\n storage.remove('redirectUri');\n\n // Clear URL params\n window.history.replaceState({}, document.title, window.location.pathname);\n\n return true;\n}\n\n/**\n * Logout - clear all stored data\n */\nexport async function logout(\n storage: Storage,\n namespace: string,\n options: { reload?: boolean } = {}\n): Promise<void> {\n storage.clear();\n await clearDPoPKeys(namespace);\n\n if (options.reload !== false) {\n window.location.reload();\n }\n}\n", "import { createDPoPProof } from './auth/dpop';\nimport { getValidAccessToken } from './auth/tokens';\nimport { Storage } from './storage/storage';\n\nexport interface GraphQLResponse<T = unknown> {\n data?: T;\n errors?: Array<{ message: string; path?: string[] }>;\n}\n\n/**\n * Execute a GraphQL query or mutation\n */\nexport async function graphqlRequest<T = unknown>(\n storage: Storage,\n namespace: string,\n graphqlUrl: string,\n tokenUrl: string,\n query: string,\n variables: Record<string, unknown> = {},\n requireAuth = false,\n signal?: AbortSignal\n): Promise<T> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n if (requireAuth) {\n const token = await getValidAccessToken(storage, namespace, tokenUrl);\n if (!token) {\n throw new Error('Not authenticated');\n }\n\n // Create DPoP proof bound to this request\n const dpopProof = await createDPoPProof(namespace, 'POST', graphqlUrl, token);\n\n headers['Authorization'] = `DPoP ${token}`;\n headers['DPoP'] = dpopProof;\n }\n\n const response = await fetch(graphqlUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify({ query, variables }),\n signal,\n });\n\n if (!response.ok) {\n throw new Error(`GraphQL request failed: ${response.statusText}`);\n }\n\n const result: GraphQLResponse<T> = await response.json();\n\n if (result.errors && result.errors.length > 0) {\n throw new Error(`GraphQL error: ${result.errors[0].message}`);\n }\n\n return result.data as T;\n}\n", "import { createStorageKeys } from './storage/keys';\nimport { createStorage, Storage } from './storage/storage';\nimport { getOrCreateDPoPKey } from './auth/dpop';\nimport { initiateLogin, handleOAuthCallback, logout as doLogout, LoginOptions } from './auth/oauth';\nimport { getValidAccessToken, hasValidSession } from './auth/tokens';\nimport { graphqlRequest } from './graphql';\nimport { generateNamespaceHash } from './utils/crypto';\n\nexport interface QuicksliceClientOptions {\n server: string;\n clientId: string;\n redirectUri?: string;\n scope?: string;\n}\n\nexport interface User {\n did: string;\n}\n\nexport interface QueryOptions {\n signal?: AbortSignal;\n}\n\nexport class QuicksliceClient {\n private server: string;\n private clientId: string;\n private redirectUri?: string;\n private scope?: string;\n private graphqlUrl: string;\n private authorizeUrl: string;\n private tokenUrl: string;\n private initialized = false;\n private namespace: string = '';\n private storage: Storage | null = null;\n\n constructor(options: QuicksliceClientOptions) {\n this.server = options.server.replace(/\\/$/, ''); // Remove trailing slash\n this.clientId = options.clientId;\n this.redirectUri = options.redirectUri;\n this.scope = options.scope;\n\n this.graphqlUrl = `${this.server}/graphql`;\n this.authorizeUrl = `${this.server}/oauth/authorize`;\n this.tokenUrl = `${this.server}/oauth/token`;\n }\n\n /**\n * Initialize the client - must be called before other methods\n */\n async init(): Promise<void> {\n if (this.initialized) return;\n\n // Generate namespace from clientId\n this.namespace = await generateNamespaceHash(this.clientId);\n\n // Create namespaced storage\n const keys = createStorageKeys(this.namespace);\n this.storage = createStorage(keys);\n\n // Ensure DPoP key exists\n await getOrCreateDPoPKey(this.namespace);\n\n this.initialized = true;\n }\n\n private getStorage(): Storage {\n if (!this.storage) {\n throw new Error('Client not initialized. Call init() first.');\n }\n return this.storage;\n }\n\n /**\n * Start OAuth login flow\n */\n async loginWithRedirect(options: LoginOptions = {}): Promise<void> {\n await this.init();\n await initiateLogin(this.getStorage(), this.authorizeUrl, this.clientId, {\n ...options,\n redirectUri: options.redirectUri || this.redirectUri,\n scope: options.scope || this.scope,\n });\n }\n\n /**\n * Handle OAuth callback after redirect\n * Returns true if callback was handled\n */\n async handleRedirectCallback(): Promise<boolean> {\n await this.init();\n return await handleOAuthCallback(this.getStorage(), this.namespace, this.tokenUrl);\n }\n\n /**\n * Logout and clear all stored data\n */\n async logout(options: { reload?: boolean } = {}): Promise<void> {\n await this.init();\n await doLogout(this.getStorage(), this.namespace, options);\n }\n\n /**\n * Check if user is authenticated\n */\n async isAuthenticated(): Promise<boolean> {\n await this.init();\n return hasValidSession(this.getStorage());\n }\n\n /**\n * Get current user's DID (from stored token data)\n * For richer profile info, use client.query() with your own schema\n */\n async getUser(): Promise<User | null> {\n await this.init();\n if (!hasValidSession(this.getStorage())) {\n return null;\n }\n\n const did = this.getStorage().get('userDid');\n if (!did) {\n return null;\n }\n\n return { did };\n }\n\n /**\n * Get access token (auto-refreshes if needed)\n */\n async getAccessToken(): Promise<string> {\n await this.init();\n return await getValidAccessToken(this.getStorage(), this.namespace, this.tokenUrl);\n }\n\n /**\n * Execute a GraphQL query (authenticated)\n */\n async query<T = unknown>(\n query: string,\n variables: Record<string, unknown> = {},\n options: QueryOptions = {}\n ): Promise<T> {\n await this.init();\n return await graphqlRequest<T>(\n this.getStorage(),\n this.namespace,\n this.graphqlUrl,\n this.tokenUrl,\n query,\n variables,\n true,\n options.signal\n );\n }\n\n /**\n * Execute a GraphQL mutation (authenticated)\n */\n async mutate<T = unknown>(\n mutation: string,\n variables: Record<string, unknown> = {},\n options: QueryOptions = {}\n ): Promise<T> {\n return this.query<T>(mutation, variables, options);\n }\n\n /**\n * Execute a public GraphQL query (no auth)\n */\n async publicQuery<T = unknown>(\n query: string,\n variables: Record<string, unknown> = {},\n options: QueryOptions = {}\n ): Promise<T> {\n await this.init();\n return await graphqlRequest<T>(\n this.getStorage(),\n this.namespace,\n this.graphqlUrl,\n this.tokenUrl,\n query,\n variables,\n false,\n options.signal\n );\n }\n}\n", "/**\n * Base error class for Quickslice client errors\n */\nexport class QuicksliceError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'QuicksliceError';\n }\n}\n\n/**\n * Thrown when authentication is required but user is not logged in\n */\nexport class LoginRequiredError extends QuicksliceError {\n constructor(message = 'Login required') {\n super(message);\n this.name = 'LoginRequiredError';\n }\n}\n\n/**\n * Thrown when network request fails\n */\nexport class NetworkError extends QuicksliceError {\n constructor(message: string) {\n super(message);\n this.name = 'NetworkError';\n }\n}\n\n/**\n * Thrown when OAuth flow fails\n */\nexport class OAuthError extends QuicksliceError {\n public code: string;\n public description?: string;\n\n constructor(code: string, description?: string) {\n super(`OAuth error: ${code}${description ? ` - ${description}` : ''}`);\n this.name = 'OAuthError';\n this.code = code;\n this.description = description;\n }\n}\n", "export { QuicksliceClient, QuicksliceClientOptions, QueryOptions, User } from './client';\nexport {\n QuicksliceError,\n LoginRequiredError,\n NetworkError,\n OAuthError,\n} from './errors';\n\nimport { QuicksliceClient, QuicksliceClientOptions } from './client';\n\n/**\n * Create and initialize a Quickslice client\n */\nexport async function createQuicksliceClient(\n options: QuicksliceClientOptions\n): Promise<QuicksliceClient> {\n const client = new QuicksliceClient(options);\n await client.init();\n return client;\n}\n"], 5 + "mappings": "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", 6 6 "names": ["sleep"] 7 7 }
+11 -8
quickslice-client-js/dist/quickslice-client.js
··· 442 442 } 443 443 444 444 // src/graphql.ts 445 - async function graphqlRequest(storage, namespace, graphqlUrl, tokenUrl, query, variables = {}, requireAuth = false) { 445 + async function graphqlRequest(storage, namespace, graphqlUrl, tokenUrl, query, variables = {}, requireAuth = false, signal) { 446 446 const headers = { 447 447 "Content-Type": "application/json" 448 448 }; ··· 458 458 const response = await fetch(graphqlUrl, { 459 459 method: "POST", 460 460 headers, 461 - body: JSON.stringify({ query, variables }) 461 + body: JSON.stringify({ query, variables }), 462 + signal 462 463 }); 463 464 if (!response.ok) { 464 465 throw new Error(`GraphQL request failed: ${response.statusText}`); ··· 559 560 /** 560 561 * Execute a GraphQL query (authenticated) 561 562 */ 562 - async query(query, variables = {}) { 563 + async query(query, variables = {}, options = {}) { 563 564 await this.init(); 564 565 return await graphqlRequest( 565 566 this.getStorage(), ··· 568 569 this.tokenUrl, 569 570 query, 570 571 variables, 571 - true 572 + true, 573 + options.signal 572 574 ); 573 575 } 574 576 /** 575 577 * Execute a GraphQL mutation (authenticated) 576 578 */ 577 - async mutate(mutation, variables = {}) { 578 - return this.query(mutation, variables); 579 + async mutate(mutation, variables = {}, options = {}) { 580 + return this.query(mutation, variables, options); 579 581 } 580 582 /** 581 583 * Execute a public GraphQL query (no auth) 582 584 */ 583 - async publicQuery(query, variables = {}) { 585 + async publicQuery(query, variables = {}, options = {}) { 584 586 await this.init(); 585 587 return await graphqlRequest( 586 588 this.getStorage(), ··· 589 591 this.tokenUrl, 590 592 query, 591 593 variables, 592 - false 594 + false, 595 + options.signal 593 596 ); 594 597 } 595 598 };
+2 -2
quickslice-client-js/dist/quickslice-client.js.map
··· 1 1 { 2 2 "version": 3, 3 3 "sources": ["../src/index.ts", "../src/storage/keys.ts", "../src/storage/storage.ts", "../src/utils/base64url.ts", "../src/utils/crypto.ts", "../src/auth/dpop.ts", "../src/auth/pkce.ts", "../src/storage/lock.ts", "../src/auth/tokens.ts", "../src/auth/oauth.ts", "../src/graphql.ts", "../src/client.ts", "../src/errors.ts"], 4 - "sourcesContent": ["export { QuicksliceClient, QuicksliceClientOptions, User } from './client';\nexport {\n QuicksliceError,\n LoginRequiredError,\n NetworkError,\n OAuthError,\n} from './errors';\n\nimport { QuicksliceClient, QuicksliceClientOptions } from './client';\n\n/**\n * Create and initialize a Quickslice client\n */\nexport async function createQuicksliceClient(\n options: QuicksliceClientOptions\n): Promise<QuicksliceClient> {\n const client = new QuicksliceClient(options);\n await client.init();\n return client;\n}\n", "/**\n * Storage key factory - generates namespaced keys\n */\nexport interface StorageKeys {\n accessToken: string;\n refreshToken: string;\n tokenExpiresAt: string;\n clientId: string;\n userDid: string;\n codeVerifier: string;\n oauthState: string;\n redirectUri: string;\n}\n\nexport function createStorageKeys(namespace: string): StorageKeys {\n return {\n accessToken: `quickslice_${namespace}_access_token`,\n refreshToken: `quickslice_${namespace}_refresh_token`,\n tokenExpiresAt: `quickslice_${namespace}_token_expires_at`,\n clientId: `quickslice_${namespace}_client_id`,\n userDid: `quickslice_${namespace}_user_did`,\n codeVerifier: `quickslice_${namespace}_code_verifier`,\n oauthState: `quickslice_${namespace}_oauth_state`,\n redirectUri: `quickslice_${namespace}_redirect_uri`,\n };\n}\n\nexport type StorageKey = string;\n", "import { StorageKeys } from './keys';\n\n/**\n * Create a namespaced storage interface\n */\nexport function createStorage(keys: StorageKeys) {\n return {\n get(key: keyof StorageKeys): string | null {\n const storageKey = keys[key];\n // OAuth flow state stays in sessionStorage (per-tab)\n if (key === 'codeVerifier' || key === 'oauthState') {\n return sessionStorage.getItem(storageKey);\n }\n // Tokens go in localStorage (shared across tabs)\n return localStorage.getItem(storageKey);\n },\n\n set(key: keyof StorageKeys, value: string): void {\n const storageKey = keys[key];\n if (key === 'codeVerifier' || key === 'oauthState') {\n sessionStorage.setItem(storageKey, value);\n } else {\n localStorage.setItem(storageKey, value);\n }\n },\n\n remove(key: keyof StorageKeys): void {\n const storageKey = keys[key];\n sessionStorage.removeItem(storageKey);\n localStorage.removeItem(storageKey);\n },\n\n clear(): void {\n (Object.keys(keys) as Array<keyof StorageKeys>).forEach((key) => {\n const storageKey = keys[key];\n sessionStorage.removeItem(storageKey);\n localStorage.removeItem(storageKey);\n });\n },\n };\n}\n\nexport type Storage = ReturnType<typeof createStorage>;\n", "/**\n * Base64 URL encode a buffer (Uint8Array or ArrayBuffer)\n */\nexport function base64UrlEncode(buffer: ArrayBuffer | Uint8Array): string {\n const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary)\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n}\n\n/**\n * Generate a random base64url string\n */\nexport function generateRandomString(byteLength: number): string {\n const bytes = new Uint8Array(byteLength);\n crypto.getRandomValues(bytes);\n return base64UrlEncode(bytes);\n}\n", "import { base64UrlEncode } from './base64url';\n\n/**\n * SHA-256 hash, returned as base64url string\n */\nexport async function sha256Base64Url(data: string): Promise<string> {\n const encoder = new TextEncoder();\n const hash = await crypto.subtle.digest('SHA-256', encoder.encode(data));\n return base64UrlEncode(hash);\n}\n\n/**\n * Generate an 8-character namespace hash from clientId\n */\nexport async function generateNamespaceHash(clientId: string): Promise<string> {\n const encoder = new TextEncoder();\n const hash = await crypto.subtle.digest('SHA-256', encoder.encode(clientId));\n const hashArray = Array.from(new Uint8Array(hash));\n const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');\n return hashHex.substring(0, 8);\n}\n\n/**\n * Sign a JWT with an ECDSA P-256 private key\n */\nexport async function signJwt(\n header: Record<string, unknown>,\n payload: Record<string, unknown>,\n privateKey: CryptoKey\n): Promise<string> {\n const encoder = new TextEncoder();\n\n const headerB64 = base64UrlEncode(encoder.encode(JSON.stringify(header)));\n const payloadB64 = base64UrlEncode(encoder.encode(JSON.stringify(payload)));\n\n const signingInput = `${headerB64}.${payloadB64}`;\n\n const signature = await crypto.subtle.sign(\n { name: 'ECDSA', hash: 'SHA-256' },\n privateKey,\n encoder.encode(signingInput)\n );\n\n const signatureB64 = base64UrlEncode(signature);\n\n return `${signingInput}.${signatureB64}`;\n}\n", "import { generateRandomString } from '../utils/base64url';\nimport { sha256Base64Url, signJwt } from '../utils/crypto';\n\nconst DB_VERSION = 1;\nconst KEY_STORE = 'dpop-keys';\nconst KEY_ID = 'dpop-key';\n\ninterface DPoPKeyData {\n id: string;\n privateKey: CryptoKey;\n publicJwk: JsonWebKey;\n createdAt: number;\n}\n\n// Cache database connections per namespace\nconst dbPromises = new Map<string, Promise<IDBDatabase>>();\n\nfunction getDbName(namespace: string): string {\n return `quickslice-oauth-${namespace}`;\n}\n\nfunction openDatabase(namespace: string): Promise<IDBDatabase> {\n const existing = dbPromises.get(namespace);\n if (existing) return existing;\n\n const promise = new Promise<IDBDatabase>((resolve, reject) => {\n const request = indexedDB.open(getDbName(namespace), DB_VERSION);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result);\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n if (!db.objectStoreNames.contains(KEY_STORE)) {\n db.createObjectStore(KEY_STORE, { keyPath: 'id' });\n }\n };\n });\n\n dbPromises.set(namespace, promise);\n return promise;\n}\n\nasync function getDPoPKey(namespace: string): Promise<DPoPKeyData | null> {\n const db = await openDatabase(namespace);\n return new Promise((resolve, reject) => {\n const tx = db.transaction(KEY_STORE, 'readonly');\n const store = tx.objectStore(KEY_STORE);\n const request = store.get(KEY_ID);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result || null);\n });\n}\n\nasync function storeDPoPKey(\n namespace: string,\n privateKey: CryptoKey,\n publicJwk: JsonWebKey\n): Promise<void> {\n const db = await openDatabase(namespace);\n return new Promise((resolve, reject) => {\n const tx = db.transaction(KEY_STORE, 'readwrite');\n const store = tx.objectStore(KEY_STORE);\n const request = store.put({\n id: KEY_ID,\n privateKey,\n publicJwk,\n createdAt: Date.now(),\n });\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n}\n\nexport async function getOrCreateDPoPKey(namespace: string): Promise<DPoPKeyData> {\n const keyData = await getDPoPKey(namespace);\n\n if (keyData) {\n return keyData;\n }\n\n // Generate new P-256 key pair\n const keyPair = await crypto.subtle.generateKey(\n { name: 'ECDSA', namedCurve: 'P-256' },\n false, // NOT extractable - critical for security\n ['sign']\n );\n\n // Export public key as JWK\n const publicJwk = await crypto.subtle.exportKey('jwk', keyPair.publicKey);\n\n // Store in IndexedDB\n await storeDPoPKey(namespace, keyPair.privateKey, publicJwk);\n\n return {\n id: KEY_ID,\n privateKey: keyPair.privateKey,\n publicJwk,\n createdAt: Date.now(),\n };\n}\n\n/**\n * Create a DPoP proof JWT\n */\nexport async function createDPoPProof(\n namespace: string,\n method: string,\n url: string,\n accessToken: string | null = null\n): Promise<string> {\n const keyData = await getOrCreateDPoPKey(namespace);\n\n // Strip WebCrypto-specific fields from JWK for interoperability\n const { kty, crv, x, y } = keyData.publicJwk;\n const minimalJwk = { kty, crv, x, y };\n\n const header = {\n alg: 'ES256',\n typ: 'dpop+jwt',\n jwk: minimalJwk,\n };\n\n const payload: Record<string, unknown> = {\n jti: generateRandomString(16),\n htm: method,\n htu: url,\n iat: Math.floor(Date.now() / 1000),\n };\n\n // Add access token hash if provided (for resource requests)\n if (accessToken) {\n payload.ath = await sha256Base64Url(accessToken);\n }\n\n return await signJwt(header, payload, keyData.privateKey);\n}\n\n/**\n * Clear DPoP keys from IndexedDB\n */\nexport async function clearDPoPKeys(namespace: string): Promise<void> {\n const db = await openDatabase(namespace);\n return new Promise((resolve, reject) => {\n const tx = db.transaction(KEY_STORE, 'readwrite');\n const store = tx.objectStore(KEY_STORE);\n const request = store.clear();\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n}\n", "import { base64UrlEncode, generateRandomString } from '../utils/base64url';\n\n/**\n * Generate a PKCE code verifier (32 random bytes, base64url encoded)\n */\nexport function generateCodeVerifier(): string {\n return generateRandomString(32);\n}\n\n/**\n * Generate a PKCE code challenge from a verifier (SHA-256, base64url encoded)\n */\nexport async function generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const hash = await crypto.subtle.digest('SHA-256', data);\n return base64UrlEncode(hash);\n}\n\n/**\n * Generate a random state parameter for CSRF protection\n */\nexport function generateState(): string {\n return generateRandomString(16);\n}\n", "const LOCK_TIMEOUT = 5000; // 5 seconds\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction getLockKey(namespace: string, key: string): string {\n return `quickslice_${namespace}_lock_${key}`;\n}\n\n/**\n * Acquire a lock using localStorage for multi-tab coordination\n */\nexport async function acquireLock(\n namespace: string,\n key: string,\n timeout = LOCK_TIMEOUT\n): Promise<string | null> {\n const lockKey = getLockKey(namespace, key);\n const lockValue = `${Date.now()}_${Math.random()}`;\n const deadline = Date.now() + timeout;\n\n while (Date.now() < deadline) {\n const existing = localStorage.getItem(lockKey);\n\n if (existing) {\n // Check if lock is stale (older than timeout)\n const [timestamp] = existing.split('_');\n if (Date.now() - parseInt(timestamp) > LOCK_TIMEOUT) {\n // Lock is stale, remove it\n localStorage.removeItem(lockKey);\n } else {\n // Lock is held, wait and retry\n await sleep(50);\n continue;\n }\n }\n\n // Try to acquire\n localStorage.setItem(lockKey, lockValue);\n\n // Verify we got it (handle race condition)\n await sleep(10);\n if (localStorage.getItem(lockKey) === lockValue) {\n return lockValue; // Lock acquired\n }\n }\n\n return null; // Failed to acquire\n}\n\n/**\n * Release a lock\n */\nexport function releaseLock(namespace: string, key: string, lockValue: string): void {\n const lockKey = getLockKey(namespace, key);\n // Only release if we still hold it\n if (localStorage.getItem(lockKey) === lockValue) {\n localStorage.removeItem(lockKey);\n }\n}\n", "import { Storage } from '../storage/storage';\nimport { acquireLock, releaseLock } from '../storage/lock';\nimport { createDPoPProof } from './dpop';\n\nconst TOKEN_REFRESH_BUFFER_MS = 60000; // 60 seconds before expiry\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Refresh tokens using the refresh token\n */\nasync function refreshTokens(\n storage: Storage,\n namespace: string,\n tokenUrl: string\n): Promise<string> {\n const refreshToken = storage.get('refreshToken');\n const clientId = storage.get('clientId');\n\n if (!refreshToken || !clientId) {\n throw new Error('No refresh token available');\n }\n\n const dpopProof = await createDPoPProof(namespace, 'POST', tokenUrl);\n\n const response = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n DPoP: dpopProof,\n },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n client_id: clientId,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n throw new Error(\n `Token refresh failed: ${errorData.error_description || response.statusText}`\n );\n }\n\n const tokens = await response.json();\n\n // Store new tokens (rotation - new refresh token each time)\n storage.set('accessToken', tokens.access_token);\n if (tokens.refresh_token) {\n storage.set('refreshToken', tokens.refresh_token);\n }\n\n const expiresAt = Date.now() + tokens.expires_in * 1000;\n storage.set('tokenExpiresAt', expiresAt.toString());\n\n return tokens.access_token;\n}\n\n/**\n * Get a valid access token, refreshing if necessary.\n * Uses multi-tab locking to prevent duplicate refresh requests.\n */\nexport async function getValidAccessToken(\n storage: Storage,\n namespace: string,\n tokenUrl: string\n): Promise<string> {\n const accessToken = storage.get('accessToken');\n const expiresAt = parseInt(storage.get('tokenExpiresAt') || '0');\n\n // Check if token is still valid (with buffer)\n if (accessToken && Date.now() < expiresAt - TOKEN_REFRESH_BUFFER_MS) {\n return accessToken;\n }\n\n // Need to refresh - acquire lock first\n const lockKey = 'token_refresh';\n const lockValue = await acquireLock(namespace, lockKey);\n\n if (!lockValue) {\n // Failed to acquire lock, another tab is refreshing\n // Wait a bit and check cache again\n await sleep(100);\n const freshToken = storage.get('accessToken');\n const freshExpiry = parseInt(storage.get('tokenExpiresAt') || '0');\n if (freshToken && Date.now() < freshExpiry - TOKEN_REFRESH_BUFFER_MS) {\n return freshToken;\n }\n throw new Error('Failed to refresh token');\n }\n\n try {\n // Double-check after acquiring lock\n const freshToken = storage.get('accessToken');\n const freshExpiry = parseInt(storage.get('tokenExpiresAt') || '0');\n if (freshToken && Date.now() < freshExpiry - TOKEN_REFRESH_BUFFER_MS) {\n return freshToken;\n }\n\n // Actually refresh\n return await refreshTokens(storage, namespace, tokenUrl);\n } finally {\n releaseLock(namespace, lockKey, lockValue);\n }\n}\n\n/**\n * Store tokens from OAuth response\n */\nexport function storeTokens(\n storage: Storage,\n tokens: {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n sub?: string;\n }\n): void {\n storage.set('accessToken', tokens.access_token);\n if (tokens.refresh_token) {\n storage.set('refreshToken', tokens.refresh_token);\n }\n\n const expiresAt = Date.now() + tokens.expires_in * 1000;\n storage.set('tokenExpiresAt', expiresAt.toString());\n\n if (tokens.sub) {\n storage.set('userDid', tokens.sub);\n }\n}\n\n/**\n * Check if we have a valid session\n */\nexport function hasValidSession(storage: Storage): boolean {\n const accessToken = storage.get('accessToken');\n const refreshToken = storage.get('refreshToken');\n return !!(accessToken || refreshToken);\n}\n", "import { Storage } from '../storage/storage';\nimport { createDPoPProof, clearDPoPKeys } from './dpop';\nimport { generateCodeVerifier, generateCodeChallenge, generateState } from './pkce';\nimport { storeTokens } from './tokens';\n\nexport interface LoginOptions {\n handle?: string;\n redirectUri?: string;\n scope?: string;\n}\n\n/**\n * Initiate OAuth login flow with PKCE\n */\nexport async function initiateLogin(\n storage: Storage,\n authorizeUrl: string,\n clientId: string,\n options: LoginOptions = {}\n): Promise<void> {\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = await generateCodeChallenge(codeVerifier);\n const state = generateState();\n\n // Build redirect URI (use provided or derive from current page)\n const redirectUri = options.redirectUri || (window.location.origin + window.location.pathname);\n\n // Store for callback\n storage.set('codeVerifier', codeVerifier);\n storage.set('oauthState', state);\n storage.set('clientId', clientId);\n storage.set('redirectUri', redirectUri);\n\n // Build authorization URL\n const params = new URLSearchParams({\n client_id: clientId,\n redirect_uri: redirectUri,\n response_type: 'code',\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n state: state,\n });\n\n if (options.handle) {\n params.set('login_hint', options.handle);\n }\n\n if (options.scope) {\n params.set('scope', options.scope);\n }\n\n window.location.href = `${authorizeUrl}?${params.toString()}`;\n}\n\n/**\n * Handle OAuth callback - exchange code for tokens\n * Returns true if callback was handled, false if not a callback\n */\nexport async function handleOAuthCallback(\n storage: Storage,\n namespace: string,\n tokenUrl: string\n): Promise<boolean> {\n const params = new URLSearchParams(window.location.search);\n const code = params.get('code');\n const state = params.get('state');\n const error = params.get('error');\n\n if (error) {\n throw new Error(\n `OAuth error: ${error} - ${params.get('error_description') || ''}`\n );\n }\n\n if (!code || !state) {\n return false; // Not a callback\n }\n\n // Verify state\n const storedState = storage.get('oauthState');\n if (state !== storedState) {\n throw new Error('OAuth state mismatch - possible CSRF attack');\n }\n\n // Get stored values\n const codeVerifier = storage.get('codeVerifier');\n const clientId = storage.get('clientId');\n const redirectUri = storage.get('redirectUri');\n\n if (!codeVerifier || !clientId || !redirectUri) {\n throw new Error('Missing OAuth session data');\n }\n\n // Exchange code for tokens with DPoP\n const dpopProof = await createDPoPProof(namespace, 'POST', tokenUrl);\n\n const tokenResponse = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n DPoP: dpopProof,\n },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n code: code,\n redirect_uri: redirectUri,\n client_id: clientId,\n code_verifier: codeVerifier,\n }),\n });\n\n if (!tokenResponse.ok) {\n const errorData = await tokenResponse.json().catch(() => ({}));\n throw new Error(\n `Token exchange failed: ${errorData.error_description || tokenResponse.statusText}`\n );\n }\n\n const tokens = await tokenResponse.json();\n\n // Store tokens\n storeTokens(storage, tokens);\n\n // Clean up OAuth state\n storage.remove('codeVerifier');\n storage.remove('oauthState');\n storage.remove('redirectUri');\n\n // Clear URL params\n window.history.replaceState({}, document.title, window.location.pathname);\n\n return true;\n}\n\n/**\n * Logout - clear all stored data\n */\nexport async function logout(\n storage: Storage,\n namespace: string,\n options: { reload?: boolean } = {}\n): Promise<void> {\n storage.clear();\n await clearDPoPKeys(namespace);\n\n if (options.reload !== false) {\n window.location.reload();\n }\n}\n", "import { createDPoPProof } from './auth/dpop';\nimport { getValidAccessToken } from './auth/tokens';\nimport { Storage } from './storage/storage';\n\nexport interface GraphQLResponse<T = unknown> {\n data?: T;\n errors?: Array<{ message: string; path?: string[] }>;\n}\n\n/**\n * Execute a GraphQL query or mutation\n */\nexport async function graphqlRequest<T = unknown>(\n storage: Storage,\n namespace: string,\n graphqlUrl: string,\n tokenUrl: string,\n query: string,\n variables: Record<string, unknown> = {},\n requireAuth = false\n): Promise<T> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n if (requireAuth) {\n const token = await getValidAccessToken(storage, namespace, tokenUrl);\n if (!token) {\n throw new Error('Not authenticated');\n }\n\n // Create DPoP proof bound to this request\n const dpopProof = await createDPoPProof(namespace, 'POST', graphqlUrl, token);\n\n headers['Authorization'] = `DPoP ${token}`;\n headers['DPoP'] = dpopProof;\n }\n\n const response = await fetch(graphqlUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify({ query, variables }),\n });\n\n if (!response.ok) {\n throw new Error(`GraphQL request failed: ${response.statusText}`);\n }\n\n const result: GraphQLResponse<T> = await response.json();\n\n if (result.errors && result.errors.length > 0) {\n throw new Error(`GraphQL error: ${result.errors[0].message}`);\n }\n\n return result.data as T;\n}\n", "import { createStorageKeys } from './storage/keys';\nimport { createStorage, Storage } from './storage/storage';\nimport { getOrCreateDPoPKey } from './auth/dpop';\nimport { initiateLogin, handleOAuthCallback, logout as doLogout, LoginOptions } from './auth/oauth';\nimport { getValidAccessToken, hasValidSession } from './auth/tokens';\nimport { graphqlRequest } from './graphql';\nimport { generateNamespaceHash } from './utils/crypto';\n\nexport interface QuicksliceClientOptions {\n server: string;\n clientId: string;\n redirectUri?: string;\n scope?: string;\n}\n\nexport interface User {\n did: string;\n}\n\nexport class QuicksliceClient {\n private server: string;\n private clientId: string;\n private redirectUri?: string;\n private scope?: string;\n private graphqlUrl: string;\n private authorizeUrl: string;\n private tokenUrl: string;\n private initialized = false;\n private namespace: string = '';\n private storage: Storage | null = null;\n\n constructor(options: QuicksliceClientOptions) {\n this.server = options.server.replace(/\\/$/, ''); // Remove trailing slash\n this.clientId = options.clientId;\n this.redirectUri = options.redirectUri;\n this.scope = options.scope;\n\n this.graphqlUrl = `${this.server}/graphql`;\n this.authorizeUrl = `${this.server}/oauth/authorize`;\n this.tokenUrl = `${this.server}/oauth/token`;\n }\n\n /**\n * Initialize the client - must be called before other methods\n */\n async init(): Promise<void> {\n if (this.initialized) return;\n\n // Generate namespace from clientId\n this.namespace = await generateNamespaceHash(this.clientId);\n\n // Create namespaced storage\n const keys = createStorageKeys(this.namespace);\n this.storage = createStorage(keys);\n\n // Ensure DPoP key exists\n await getOrCreateDPoPKey(this.namespace);\n\n this.initialized = true;\n }\n\n private getStorage(): Storage {\n if (!this.storage) {\n throw new Error('Client not initialized. Call init() first.');\n }\n return this.storage;\n }\n\n /**\n * Start OAuth login flow\n */\n async loginWithRedirect(options: LoginOptions = {}): Promise<void> {\n await this.init();\n await initiateLogin(this.getStorage(), this.authorizeUrl, this.clientId, {\n ...options,\n redirectUri: options.redirectUri || this.redirectUri,\n scope: options.scope || this.scope,\n });\n }\n\n /**\n * Handle OAuth callback after redirect\n * Returns true if callback was handled\n */\n async handleRedirectCallback(): Promise<boolean> {\n await this.init();\n return await handleOAuthCallback(this.getStorage(), this.namespace, this.tokenUrl);\n }\n\n /**\n * Logout and clear all stored data\n */\n async logout(options: { reload?: boolean } = {}): Promise<void> {\n await this.init();\n await doLogout(this.getStorage(), this.namespace, options);\n }\n\n /**\n * Check if user is authenticated\n */\n async isAuthenticated(): Promise<boolean> {\n await this.init();\n return hasValidSession(this.getStorage());\n }\n\n /**\n * Get current user's DID (from stored token data)\n * For richer profile info, use client.query() with your own schema\n */\n async getUser(): Promise<User | null> {\n await this.init();\n if (!hasValidSession(this.getStorage())) {\n return null;\n }\n\n const did = this.getStorage().get('userDid');\n if (!did) {\n return null;\n }\n\n return { did };\n }\n\n /**\n * Get access token (auto-refreshes if needed)\n */\n async getAccessToken(): Promise<string> {\n await this.init();\n return await getValidAccessToken(this.getStorage(), this.namespace, this.tokenUrl);\n }\n\n /**\n * Execute a GraphQL query (authenticated)\n */\n async query<T = unknown>(\n query: string,\n variables: Record<string, unknown> = {}\n ): Promise<T> {\n await this.init();\n return await graphqlRequest<T>(\n this.getStorage(),\n this.namespace,\n this.graphqlUrl,\n this.tokenUrl,\n query,\n variables,\n true\n );\n }\n\n /**\n * Execute a GraphQL mutation (authenticated)\n */\n async mutate<T = unknown>(\n mutation: string,\n variables: Record<string, unknown> = {}\n ): Promise<T> {\n return this.query<T>(mutation, variables);\n }\n\n /**\n * Execute a public GraphQL query (no auth)\n */\n async publicQuery<T = unknown>(\n query: string,\n variables: Record<string, unknown> = {}\n ): Promise<T> {\n await this.init();\n return await graphqlRequest<T>(\n this.getStorage(),\n this.namespace,\n this.graphqlUrl,\n this.tokenUrl,\n query,\n variables,\n false\n );\n }\n}\n", "/**\n * Base error class for Quickslice client errors\n */\nexport class QuicksliceError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'QuicksliceError';\n }\n}\n\n/**\n * Thrown when authentication is required but user is not logged in\n */\nexport class LoginRequiredError extends QuicksliceError {\n constructor(message = 'Login required') {\n super(message);\n this.name = 'LoginRequiredError';\n }\n}\n\n/**\n * Thrown when network request fails\n */\nexport class NetworkError extends QuicksliceError {\n constructor(message: string) {\n super(message);\n this.name = 'NetworkError';\n }\n}\n\n/**\n * Thrown when OAuth flow fails\n */\nexport class OAuthError extends QuicksliceError {\n public code: string;\n public description?: string;\n\n constructor(code: string, description?: string) {\n super(`OAuth error: ${code}${description ? ` - ${description}` : ''}`);\n this.name = 'OAuthError';\n this.code = code;\n this.description = description;\n }\n}\n"], 5 - "mappings": ";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACcO,WAAS,kBAAkB,WAAgC;AAChE,WAAO;AAAA,MACL,aAAa,cAAc,SAAS;AAAA,MACpC,cAAc,cAAc,SAAS;AAAA,MACrC,gBAAgB,cAAc,SAAS;AAAA,MACvC,UAAU,cAAc,SAAS;AAAA,MACjC,SAAS,cAAc,SAAS;AAAA,MAChC,cAAc,cAAc,SAAS;AAAA,MACrC,YAAY,cAAc,SAAS;AAAA,MACnC,aAAa,cAAc,SAAS;AAAA,IACtC;AAAA,EACF;;;ACpBO,WAAS,cAAc,MAAmB;AAC/C,WAAO;AAAA,MACL,IAAI,KAAuC;AACzC,cAAM,aAAa,KAAK,GAAG;AAE3B,YAAI,QAAQ,kBAAkB,QAAQ,cAAc;AAClD,iBAAO,eAAe,QAAQ,UAAU;AAAA,QAC1C;AAEA,eAAO,aAAa,QAAQ,UAAU;AAAA,MACxC;AAAA,MAEA,IAAI,KAAwB,OAAqB;AAC/C,cAAM,aAAa,KAAK,GAAG;AAC3B,YAAI,QAAQ,kBAAkB,QAAQ,cAAc;AAClD,yBAAe,QAAQ,YAAY,KAAK;AAAA,QAC1C,OAAO;AACL,uBAAa,QAAQ,YAAY,KAAK;AAAA,QACxC;AAAA,MACF;AAAA,MAEA,OAAO,KAA8B;AACnC,cAAM,aAAa,KAAK,GAAG;AAC3B,uBAAe,WAAW,UAAU;AACpC,qBAAa,WAAW,UAAU;AAAA,MACpC;AAAA,MAEA,QAAc;AACZ,QAAC,OAAO,KAAK,IAAI,EAA+B,QAAQ,CAAC,QAAQ;AAC/D,gBAAM,aAAa,KAAK,GAAG;AAC3B,yBAAe,WAAW,UAAU;AACpC,uBAAa,WAAW,UAAU;AAAA,QACpC,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;;;ACrCO,WAAS,gBAAgB,QAA0C;AACxE,UAAM,QAAQ,kBAAkB,aAAa,SAAS,IAAI,WAAW,MAAM;AAC3E,QAAI,SAAS;AACb,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,gBAAU,OAAO,aAAa,MAAM,CAAC,CAAC;AAAA,IACxC;AACA,WAAO,KAAK,MAAM,EACf,QAAQ,OAAO,GAAG,EAClB,QAAQ,OAAO,GAAG,EAClB,QAAQ,OAAO,EAAE;AAAA,EACtB;AAKO,WAAS,qBAAqB,YAA4B;AAC/D,UAAM,QAAQ,IAAI,WAAW,UAAU;AACvC,WAAO,gBAAgB,KAAK;AAC5B,WAAO,gBAAgB,KAAK;AAAA,EAC9B;;;ACjBA,iBAAsB,gBAAgB,MAA+B;AACnE,UAAM,UAAU,IAAI,YAAY;AAChC,UAAM,OAAO,MAAM,OAAO,OAAO,OAAO,WAAW,QAAQ,OAAO,IAAI,CAAC;AACvE,WAAO,gBAAgB,IAAI;AAAA,EAC7B;AAKA,iBAAsB,sBAAsB,UAAmC;AAC7E,UAAM,UAAU,IAAI,YAAY;AAChC,UAAM,OAAO,MAAM,OAAO,OAAO,OAAO,WAAW,QAAQ,OAAO,QAAQ,CAAC;AAC3E,UAAM,YAAY,MAAM,KAAK,IAAI,WAAW,IAAI,CAAC;AACjD,UAAM,UAAU,UAAU,IAAI,OAAK,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAC3E,WAAO,QAAQ,UAAU,GAAG,CAAC;AAAA,EAC/B;AAKA,iBAAsB,QACpB,QACA,SACA,YACiB;AACjB,UAAM,UAAU,IAAI,YAAY;AAEhC,UAAM,YAAY,gBAAgB,QAAQ,OAAO,KAAK,UAAU,MAAM,CAAC,CAAC;AACxE,UAAM,aAAa,gBAAgB,QAAQ,OAAO,KAAK,UAAU,OAAO,CAAC,CAAC;AAE1E,UAAM,eAAe,GAAG,SAAS,IAAI,UAAU;AAE/C,UAAM,YAAY,MAAM,OAAO,OAAO;AAAA,MACpC,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,MACjC;AAAA,MACA,QAAQ,OAAO,YAAY;AAAA,IAC7B;AAEA,UAAM,eAAe,gBAAgB,SAAS;AAE9C,WAAO,GAAG,YAAY,IAAI,YAAY;AAAA,EACxC;;;AC3CA,MAAM,aAAa;AACnB,MAAM,YAAY;AAClB,MAAM,SAAS;AAUf,MAAM,aAAa,oBAAI,IAAkC;AAEzD,WAAS,UAAU,WAA2B;AAC5C,WAAO,oBAAoB,SAAS;AAAA,EACtC;AAEA,WAAS,aAAa,WAAyC;AAC7D,UAAM,WAAW,WAAW,IAAI,SAAS;AACzC,QAAI,SAAU,QAAO;AAErB,UAAM,UAAU,IAAI,QAAqB,CAAC,SAAS,WAAW;AAC5D,YAAM,UAAU,UAAU,KAAK,UAAU,SAAS,GAAG,UAAU;AAE/D,cAAQ,UAAU,MAAM,OAAO,QAAQ,KAAK;AAC5C,cAAQ,YAAY,MAAM,QAAQ,QAAQ,MAAM;AAEhD,cAAQ,kBAAkB,CAAC,UAAU;AACnC,cAAM,KAAM,MAAM,OAA4B;AAC9C,YAAI,CAAC,GAAG,iBAAiB,SAAS,SAAS,GAAG;AAC5C,aAAG,kBAAkB,WAAW,EAAE,SAAS,KAAK,CAAC;AAAA,QACnD;AAAA,MACF;AAAA,IACF,CAAC;AAED,eAAW,IAAI,WAAW,OAAO;AACjC,WAAO;AAAA,EACT;AAEA,iBAAe,WAAW,WAAgD;AACxE,UAAM,KAAK,MAAM,aAAa,SAAS;AACvC,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,KAAK,GAAG,YAAY,WAAW,UAAU;AAC/C,YAAM,QAAQ,GAAG,YAAY,SAAS;AACtC,YAAM,UAAU,MAAM,IAAI,MAAM;AAEhC,cAAQ,UAAU,MAAM,OAAO,QAAQ,KAAK;AAC5C,cAAQ,YAAY,MAAM,QAAQ,QAAQ,UAAU,IAAI;AAAA,IAC1D,CAAC;AAAA,EACH;AAEA,iBAAe,aACb,WACA,YACA,WACe;AACf,UAAM,KAAK,MAAM,aAAa,SAAS;AACvC,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,KAAK,GAAG,YAAY,WAAW,WAAW;AAChD,YAAM,QAAQ,GAAG,YAAY,SAAS;AACtC,YAAM,UAAU,MAAM,IAAI;AAAA,QACxB,IAAI;AAAA,QACJ;AAAA,QACA;AAAA,QACA,WAAW,KAAK,IAAI;AAAA,MACtB,CAAC;AAED,cAAQ,UAAU,MAAM,OAAO,QAAQ,KAAK;AAC5C,cAAQ,YAAY,MAAM,QAAQ;AAAA,IACpC,CAAC;AAAA,EACH;AAEA,iBAAsB,mBAAmB,WAAyC;AAChF,UAAM,UAAU,MAAM,WAAW,SAAS;AAE1C,QAAI,SAAS;AACX,aAAO;AAAA,IACT;AAGA,UAAM,UAAU,MAAM,OAAO,OAAO;AAAA,MAClC,EAAE,MAAM,SAAS,YAAY,QAAQ;AAAA,MACrC;AAAA;AAAA,MACA,CAAC,MAAM;AAAA,IACT;AAGA,UAAM,YAAY,MAAM,OAAO,OAAO,UAAU,OAAO,QAAQ,SAAS;AAGxE,UAAM,aAAa,WAAW,QAAQ,YAAY,SAAS;AAE3D,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,YAAY,QAAQ;AAAA,MACpB;AAAA,MACA,WAAW,KAAK,IAAI;AAAA,IACtB;AAAA,EACF;AAKA,iBAAsB,gBACpB,WACA,QACA,KACA,cAA6B,MACZ;AACjB,UAAM,UAAU,MAAM,mBAAmB,SAAS;AAGlD,UAAM,EAAE,KAAK,KAAK,GAAG,EAAE,IAAI,QAAQ;AACnC,UAAM,aAAa,EAAE,KAAK,KAAK,GAAG,EAAE;AAEpC,UAAM,SAAS;AAAA,MACb,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,IACP;AAEA,UAAM,UAAmC;AAAA,MACvC,KAAK,qBAAqB,EAAE;AAAA,MAC5B,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,IACnC;AAGA,QAAI,aAAa;AACf,cAAQ,MAAM,MAAM,gBAAgB,WAAW;AAAA,IACjD;AAEA,WAAO,MAAM,QAAQ,QAAQ,SAAS,QAAQ,UAAU;AAAA,EAC1D;AAKA,iBAAsB,cAAc,WAAkC;AACpE,UAAM,KAAK,MAAM,aAAa,SAAS;AACvC,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,KAAK,GAAG,YAAY,WAAW,WAAW;AAChD,YAAM,QAAQ,GAAG,YAAY,SAAS;AACtC,YAAM,UAAU,MAAM,MAAM;AAE5B,cAAQ,UAAU,MAAM,OAAO,QAAQ,KAAK;AAC5C,cAAQ,YAAY,MAAM,QAAQ;AAAA,IACpC,CAAC;AAAA,EACH;;;ACpJO,WAAS,uBAA+B;AAC7C,WAAO,qBAAqB,EAAE;AAAA,EAChC;AAKA,iBAAsB,sBAAsB,UAAmC;AAC7E,UAAM,UAAU,IAAI,YAAY;AAChC,UAAM,OAAO,QAAQ,OAAO,QAAQ;AACpC,UAAM,OAAO,MAAM,OAAO,OAAO,OAAO,WAAW,IAAI;AACvD,WAAO,gBAAgB,IAAI;AAAA,EAC7B;AAKO,WAAS,gBAAwB;AACtC,WAAO,qBAAqB,EAAE;AAAA,EAChC;;;ACxBA,MAAM,eAAe;AAErB,WAAS,MAAM,IAA2B;AACxC,WAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AAAA,EACzD;AAEA,WAAS,WAAW,WAAmB,KAAqB;AAC1D,WAAO,cAAc,SAAS,SAAS,GAAG;AAAA,EAC5C;AAKA,iBAAsB,YACpB,WACA,KACA,UAAU,cACc;AACxB,UAAM,UAAU,WAAW,WAAW,GAAG;AACzC,UAAM,YAAY,GAAG,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC;AAChD,UAAM,WAAW,KAAK,IAAI,IAAI;AAE9B,WAAO,KAAK,IAAI,IAAI,UAAU;AAC5B,YAAM,WAAW,aAAa,QAAQ,OAAO;AAE7C,UAAI,UAAU;AAEZ,cAAM,CAAC,SAAS,IAAI,SAAS,MAAM,GAAG;AACtC,YAAI,KAAK,IAAI,IAAI,SAAS,SAAS,IAAI,cAAc;AAEnD,uBAAa,WAAW,OAAO;AAAA,QACjC,OAAO;AAEL,gBAAM,MAAM,EAAE;AACd;AAAA,QACF;AAAA,MACF;AAGA,mBAAa,QAAQ,SAAS,SAAS;AAGvC,YAAM,MAAM,EAAE;AACd,UAAI,aAAa,QAAQ,OAAO,MAAM,WAAW;AAC/C,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAKO,WAAS,YAAY,WAAmB,KAAa,WAAyB;AACnF,UAAM,UAAU,WAAW,WAAW,GAAG;AAEzC,QAAI,aAAa,QAAQ,OAAO,MAAM,WAAW;AAC/C,mBAAa,WAAW,OAAO;AAAA,IACjC;AAAA,EACF;;;ACxDA,MAAM,0BAA0B;AAEhC,WAASA,OAAM,IAA2B;AACxC,WAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AAAA,EACzD;AAKA,iBAAe,cACb,SACA,WACA,UACiB;AACjB,UAAM,eAAe,QAAQ,IAAI,cAAc;AAC/C,UAAM,WAAW,QAAQ,IAAI,UAAU;AAEvC,QAAI,CAAC,gBAAgB,CAAC,UAAU;AAC9B,YAAM,IAAI,MAAM,4BAA4B;AAAA,IAC9C;AAEA,UAAM,YAAY,MAAM,gBAAgB,WAAW,QAAQ,QAAQ;AAEnE,UAAM,WAAW,MAAM,MAAM,UAAU;AAAA,MACrC,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,MAAM;AAAA,MACR;AAAA,MACA,MAAM,IAAI,gBAAgB;AAAA,QACxB,YAAY;AAAA,QACZ,eAAe;AAAA,QACf,WAAW;AAAA,MACb,CAAC;AAAA,IACH,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,YAAY,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AACxD,YAAM,IAAI;AAAA,QACR,yBAAyB,UAAU,qBAAqB,SAAS,UAAU;AAAA,MAC7E;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,SAAS,KAAK;AAGnC,YAAQ,IAAI,eAAe,OAAO,YAAY;AAC9C,QAAI,OAAO,eAAe;AACxB,cAAQ,IAAI,gBAAgB,OAAO,aAAa;AAAA,IAClD;AAEA,UAAM,YAAY,KAAK,IAAI,IAAI,OAAO,aAAa;AACnD,YAAQ,IAAI,kBAAkB,UAAU,SAAS,CAAC;AAElD,WAAO,OAAO;AAAA,EAChB;AAMA,iBAAsB,oBACpB,SACA,WACA,UACiB;AACjB,UAAM,cAAc,QAAQ,IAAI,aAAa;AAC7C,UAAM,YAAY,SAAS,QAAQ,IAAI,gBAAgB,KAAK,GAAG;AAG/D,QAAI,eAAe,KAAK,IAAI,IAAI,YAAY,yBAAyB;AACnE,aAAO;AAAA,IACT;AAGA,UAAM,UAAU;AAChB,UAAM,YAAY,MAAM,YAAY,WAAW,OAAO;AAEtD,QAAI,CAAC,WAAW;AAGd,YAAMA,OAAM,GAAG;AACf,YAAM,aAAa,QAAQ,IAAI,aAAa;AAC5C,YAAM,cAAc,SAAS,QAAQ,IAAI,gBAAgB,KAAK,GAAG;AACjE,UAAI,cAAc,KAAK,IAAI,IAAI,cAAc,yBAAyB;AACpE,eAAO;AAAA,MACT;AACA,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AAEA,QAAI;AAEF,YAAM,aAAa,QAAQ,IAAI,aAAa;AAC5C,YAAM,cAAc,SAAS,QAAQ,IAAI,gBAAgB,KAAK,GAAG;AACjE,UAAI,cAAc,KAAK,IAAI,IAAI,cAAc,yBAAyB;AACpE,eAAO;AAAA,MACT;AAGA,aAAO,MAAM,cAAc,SAAS,WAAW,QAAQ;AAAA,IACzD,UAAE;AACA,kBAAY,WAAW,SAAS,SAAS;AAAA,IAC3C;AAAA,EACF;AAKO,WAAS,YACd,SACA,QAMM;AACN,YAAQ,IAAI,eAAe,OAAO,YAAY;AAC9C,QAAI,OAAO,eAAe;AACxB,cAAQ,IAAI,gBAAgB,OAAO,aAAa;AAAA,IAClD;AAEA,UAAM,YAAY,KAAK,IAAI,IAAI,OAAO,aAAa;AACnD,YAAQ,IAAI,kBAAkB,UAAU,SAAS,CAAC;AAElD,QAAI,OAAO,KAAK;AACd,cAAQ,IAAI,WAAW,OAAO,GAAG;AAAA,IACnC;AAAA,EACF;AAKO,WAAS,gBAAgB,SAA2B;AACzD,UAAM,cAAc,QAAQ,IAAI,aAAa;AAC7C,UAAM,eAAe,QAAQ,IAAI,cAAc;AAC/C,WAAO,CAAC,EAAE,eAAe;AAAA,EAC3B;;;AC/HA,iBAAsB,cACpB,SACA,cACA,UACA,UAAwB,CAAC,GACV;AACf,UAAM,eAAe,qBAAqB;AAC1C,UAAM,gBAAgB,MAAM,sBAAsB,YAAY;AAC9D,UAAM,QAAQ,cAAc;AAG5B,UAAM,cAAc,QAAQ,eAAgB,OAAO,SAAS,SAAS,OAAO,SAAS;AAGrF,YAAQ,IAAI,gBAAgB,YAAY;AACxC,YAAQ,IAAI,cAAc,KAAK;AAC/B,YAAQ,IAAI,YAAY,QAAQ;AAChC,YAAQ,IAAI,eAAe,WAAW;AAGtC,UAAM,SAAS,IAAI,gBAAgB;AAAA,MACjC,WAAW;AAAA,MACX,cAAc;AAAA,MACd,eAAe;AAAA,MACf,gBAAgB;AAAA,MAChB,uBAAuB;AAAA,MACvB;AAAA,IACF,CAAC;AAED,QAAI,QAAQ,QAAQ;AAClB,aAAO,IAAI,cAAc,QAAQ,MAAM;AAAA,IACzC;AAEA,QAAI,QAAQ,OAAO;AACjB,aAAO,IAAI,SAAS,QAAQ,KAAK;AAAA,IACnC;AAEA,WAAO,SAAS,OAAO,GAAG,YAAY,IAAI,OAAO,SAAS,CAAC;AAAA,EAC7D;AAMA,iBAAsB,oBACpB,SACA,WACA,UACkB;AAClB,UAAM,SAAS,IAAI,gBAAgB,OAAO,SAAS,MAAM;AACzD,UAAM,OAAO,OAAO,IAAI,MAAM;AAC9B,UAAM,QAAQ,OAAO,IAAI,OAAO;AAChC,UAAM,QAAQ,OAAO,IAAI,OAAO;AAEhC,QAAI,OAAO;AACT,YAAM,IAAI;AAAA,QACR,gBAAgB,KAAK,MAAM,OAAO,IAAI,mBAAmB,KAAK,EAAE;AAAA,MAClE;AAAA,IACF;AAEA,QAAI,CAAC,QAAQ,CAAC,OAAO;AACnB,aAAO;AAAA,IACT;AAGA,UAAM,cAAc,QAAQ,IAAI,YAAY;AAC5C,QAAI,UAAU,aAAa;AACzB,YAAM,IAAI,MAAM,6CAA6C;AAAA,IAC/D;AAGA,UAAM,eAAe,QAAQ,IAAI,cAAc;AAC/C,UAAM,WAAW,QAAQ,IAAI,UAAU;AACvC,UAAM,cAAc,QAAQ,IAAI,aAAa;AAE7C,QAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,aAAa;AAC9C,YAAM,IAAI,MAAM,4BAA4B;AAAA,IAC9C;AAGA,UAAM,YAAY,MAAM,gBAAgB,WAAW,QAAQ,QAAQ;AAEnE,UAAM,gBAAgB,MAAM,MAAM,UAAU;AAAA,MAC1C,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,MAAM;AAAA,MACR;AAAA,MACA,MAAM,IAAI,gBAAgB;AAAA,QACxB,YAAY;AAAA,QACZ;AAAA,QACA,cAAc;AAAA,QACd,WAAW;AAAA,QACX,eAAe;AAAA,MACjB,CAAC;AAAA,IACH,CAAC;AAED,QAAI,CAAC,cAAc,IAAI;AACrB,YAAM,YAAY,MAAM,cAAc,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC7D,YAAM,IAAI;AAAA,QACR,0BAA0B,UAAU,qBAAqB,cAAc,UAAU;AAAA,MACnF;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,cAAc,KAAK;AAGxC,gBAAY,SAAS,MAAM;AAG3B,YAAQ,OAAO,cAAc;AAC7B,YAAQ,OAAO,YAAY;AAC3B,YAAQ,OAAO,aAAa;AAG5B,WAAO,QAAQ,aAAa,CAAC,GAAG,SAAS,OAAO,OAAO,SAAS,QAAQ;AAExE,WAAO;AAAA,EACT;AAKA,iBAAsB,OACpB,SACA,WACA,UAAgC,CAAC,GAClB;AACf,YAAQ,MAAM;AACd,UAAM,cAAc,SAAS;AAE7B,QAAI,QAAQ,WAAW,OAAO;AAC5B,aAAO,SAAS,OAAO;AAAA,IACzB;AAAA,EACF;;;ACxIA,iBAAsB,eACpB,SACA,WACA,YACA,UACA,OACA,YAAqC,CAAC,GACtC,cAAc,OACF;AACZ,UAAM,UAAkC;AAAA,MACtC,gBAAgB;AAAA,IAClB;AAEA,QAAI,aAAa;AACf,YAAM,QAAQ,MAAM,oBAAoB,SAAS,WAAW,QAAQ;AACpE,UAAI,CAAC,OAAO;AACV,cAAM,IAAI,MAAM,mBAAmB;AAAA,MACrC;AAGA,YAAM,YAAY,MAAM,gBAAgB,WAAW,QAAQ,YAAY,KAAK;AAE5E,cAAQ,eAAe,IAAI,QAAQ,KAAK;AACxC,cAAQ,MAAM,IAAI;AAAA,IACpB;AAEA,UAAM,WAAW,MAAM,MAAM,YAAY;AAAA,MACvC,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,OAAO,UAAU,CAAC;AAAA,IAC3C,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,2BAA2B,SAAS,UAAU,EAAE;AAAA,IAClE;AAEA,UAAM,SAA6B,MAAM,SAAS,KAAK;AAEvD,QAAI,OAAO,UAAU,OAAO,OAAO,SAAS,GAAG;AAC7C,YAAM,IAAI,MAAM,kBAAkB,OAAO,OAAO,CAAC,EAAE,OAAO,EAAE;AAAA,IAC9D;AAEA,WAAO,OAAO;AAAA,EAChB;;;ACpCO,MAAM,mBAAN,MAAuB;AAAA,IAY5B,YAAY,SAAkC;AAJ9C,WAAQ,cAAc;AACtB,WAAQ,YAAoB;AAC5B,WAAQ,UAA0B;AAGhC,WAAK,SAAS,QAAQ,OAAO,QAAQ,OAAO,EAAE;AAC9C,WAAK,WAAW,QAAQ;AACxB,WAAK,cAAc,QAAQ;AAC3B,WAAK,QAAQ,QAAQ;AAErB,WAAK,aAAa,GAAG,KAAK,MAAM;AAChC,WAAK,eAAe,GAAG,KAAK,MAAM;AAClC,WAAK,WAAW,GAAG,KAAK,MAAM;AAAA,IAChC;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,OAAsB;AAC1B,UAAI,KAAK,YAAa;AAGtB,WAAK,YAAY,MAAM,sBAAsB,KAAK,QAAQ;AAG1D,YAAM,OAAO,kBAAkB,KAAK,SAAS;AAC7C,WAAK,UAAU,cAAc,IAAI;AAGjC,YAAM,mBAAmB,KAAK,SAAS;AAEvC,WAAK,cAAc;AAAA,IACrB;AAAA,IAEQ,aAAsB;AAC5B,UAAI,CAAC,KAAK,SAAS;AACjB,cAAM,IAAI,MAAM,4CAA4C;AAAA,MAC9D;AACA,aAAO,KAAK;AAAA,IACd;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,kBAAkB,UAAwB,CAAC,GAAkB;AACjE,YAAM,KAAK,KAAK;AAChB,YAAM,cAAc,KAAK,WAAW,GAAG,KAAK,cAAc,KAAK,UAAU;AAAA,QACvE,GAAG;AAAA,QACH,aAAa,QAAQ,eAAe,KAAK;AAAA,QACzC,OAAO,QAAQ,SAAS,KAAK;AAAA,MAC/B,CAAC;AAAA,IACH;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,MAAM,yBAA2C;AAC/C,YAAM,KAAK,KAAK;AAChB,aAAO,MAAM,oBAAoB,KAAK,WAAW,GAAG,KAAK,WAAW,KAAK,QAAQ;AAAA,IACnF;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,OAAO,UAAgC,CAAC,GAAkB;AAC9D,YAAM,KAAK,KAAK;AAChB,YAAM,OAAS,KAAK,WAAW,GAAG,KAAK,WAAW,OAAO;AAAA,IAC3D;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,kBAAoC;AACxC,YAAM,KAAK,KAAK;AAChB,aAAO,gBAAgB,KAAK,WAAW,CAAC;AAAA,IAC1C;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,MAAM,UAAgC;AACpC,YAAM,KAAK,KAAK;AAChB,UAAI,CAAC,gBAAgB,KAAK,WAAW,CAAC,GAAG;AACvC,eAAO;AAAA,MACT;AAEA,YAAM,MAAM,KAAK,WAAW,EAAE,IAAI,SAAS;AAC3C,UAAI,CAAC,KAAK;AACR,eAAO;AAAA,MACT;AAEA,aAAO,EAAE,IAAI;AAAA,IACf;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,iBAAkC;AACtC,YAAM,KAAK,KAAK;AAChB,aAAO,MAAM,oBAAoB,KAAK,WAAW,GAAG,KAAK,WAAW,KAAK,QAAQ;AAAA,IACnF;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,MACJ,OACA,YAAqC,CAAC,GAC1B;AACZ,YAAM,KAAK,KAAK;AAChB,aAAO,MAAM;AAAA,QACX,KAAK,WAAW;AAAA,QAChB,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,OACJ,UACA,YAAqC,CAAC,GAC1B;AACZ,aAAO,KAAK,MAAS,UAAU,SAAS;AAAA,IAC1C;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,YACJ,OACA,YAAqC,CAAC,GAC1B;AACZ,YAAM,KAAK,KAAK;AAChB,aAAO,MAAM;AAAA,QACX,KAAK,WAAW;AAAA,QAChB,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;;;AC/KO,MAAM,kBAAN,cAA8B,MAAM;AAAA,IACzC,YAAY,SAAiB;AAC3B,YAAM,OAAO;AACb,WAAK,OAAO;AAAA,IACd;AAAA,EACF;AAKO,MAAM,qBAAN,cAAiC,gBAAgB;AAAA,IACtD,YAAY,UAAU,kBAAkB;AACtC,YAAM,OAAO;AACb,WAAK,OAAO;AAAA,IACd;AAAA,EACF;AAKO,MAAM,eAAN,cAA2B,gBAAgB;AAAA,IAChD,YAAY,SAAiB;AAC3B,YAAM,OAAO;AACb,WAAK,OAAO;AAAA,IACd;AAAA,EACF;AAKO,MAAM,aAAN,cAAyB,gBAAgB;AAAA,IAI9C,YAAY,MAAc,aAAsB;AAC9C,YAAM,gBAAgB,IAAI,GAAG,cAAc,MAAM,WAAW,KAAK,EAAE,EAAE;AACrE,WAAK,OAAO;AACZ,WAAK,OAAO;AACZ,WAAK,cAAc;AAAA,IACrB;AAAA,EACF;;;AZ9BA,iBAAsB,uBACpB,SAC2B;AAC3B,UAAM,SAAS,IAAI,iBAAiB,OAAO;AAC3C,UAAM,OAAO,KAAK;AAClB,WAAO;AAAA,EACT;", 4 + "sourcesContent": ["export { QuicksliceClient, QuicksliceClientOptions, QueryOptions, User } from './client';\nexport {\n QuicksliceError,\n LoginRequiredError,\n NetworkError,\n OAuthError,\n} from './errors';\n\nimport { QuicksliceClient, QuicksliceClientOptions } from './client';\n\n/**\n * Create and initialize a Quickslice client\n */\nexport async function createQuicksliceClient(\n options: QuicksliceClientOptions\n): Promise<QuicksliceClient> {\n const client = new QuicksliceClient(options);\n await client.init();\n return client;\n}\n", "/**\n * Storage key factory - generates namespaced keys\n */\nexport interface StorageKeys {\n accessToken: string;\n refreshToken: string;\n tokenExpiresAt: string;\n clientId: string;\n userDid: string;\n codeVerifier: string;\n oauthState: string;\n redirectUri: string;\n}\n\nexport function createStorageKeys(namespace: string): StorageKeys {\n return {\n accessToken: `quickslice_${namespace}_access_token`,\n refreshToken: `quickslice_${namespace}_refresh_token`,\n tokenExpiresAt: `quickslice_${namespace}_token_expires_at`,\n clientId: `quickslice_${namespace}_client_id`,\n userDid: `quickslice_${namespace}_user_did`,\n codeVerifier: `quickslice_${namespace}_code_verifier`,\n oauthState: `quickslice_${namespace}_oauth_state`,\n redirectUri: `quickslice_${namespace}_redirect_uri`,\n };\n}\n\nexport type StorageKey = string;\n", "import { StorageKeys } from './keys';\n\n/**\n * Create a namespaced storage interface\n */\nexport function createStorage(keys: StorageKeys) {\n return {\n get(key: keyof StorageKeys): string | null {\n const storageKey = keys[key];\n // OAuth flow state stays in sessionStorage (per-tab)\n if (key === 'codeVerifier' || key === 'oauthState') {\n return sessionStorage.getItem(storageKey);\n }\n // Tokens go in localStorage (shared across tabs)\n return localStorage.getItem(storageKey);\n },\n\n set(key: keyof StorageKeys, value: string): void {\n const storageKey = keys[key];\n if (key === 'codeVerifier' || key === 'oauthState') {\n sessionStorage.setItem(storageKey, value);\n } else {\n localStorage.setItem(storageKey, value);\n }\n },\n\n remove(key: keyof StorageKeys): void {\n const storageKey = keys[key];\n sessionStorage.removeItem(storageKey);\n localStorage.removeItem(storageKey);\n },\n\n clear(): void {\n (Object.keys(keys) as Array<keyof StorageKeys>).forEach((key) => {\n const storageKey = keys[key];\n sessionStorage.removeItem(storageKey);\n localStorage.removeItem(storageKey);\n });\n },\n };\n}\n\nexport type Storage = ReturnType<typeof createStorage>;\n", "/**\n * Base64 URL encode a buffer (Uint8Array or ArrayBuffer)\n */\nexport function base64UrlEncode(buffer: ArrayBuffer | Uint8Array): string {\n const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary)\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n}\n\n/**\n * Generate a random base64url string\n */\nexport function generateRandomString(byteLength: number): string {\n const bytes = new Uint8Array(byteLength);\n crypto.getRandomValues(bytes);\n return base64UrlEncode(bytes);\n}\n", "import { base64UrlEncode } from './base64url';\n\n/**\n * SHA-256 hash, returned as base64url string\n */\nexport async function sha256Base64Url(data: string): Promise<string> {\n const encoder = new TextEncoder();\n const hash = await crypto.subtle.digest('SHA-256', encoder.encode(data));\n return base64UrlEncode(hash);\n}\n\n/**\n * Generate an 8-character namespace hash from clientId\n */\nexport async function generateNamespaceHash(clientId: string): Promise<string> {\n const encoder = new TextEncoder();\n const hash = await crypto.subtle.digest('SHA-256', encoder.encode(clientId));\n const hashArray = Array.from(new Uint8Array(hash));\n const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');\n return hashHex.substring(0, 8);\n}\n\n/**\n * Sign a JWT with an ECDSA P-256 private key\n */\nexport async function signJwt(\n header: Record<string, unknown>,\n payload: Record<string, unknown>,\n privateKey: CryptoKey\n): Promise<string> {\n const encoder = new TextEncoder();\n\n const headerB64 = base64UrlEncode(encoder.encode(JSON.stringify(header)));\n const payloadB64 = base64UrlEncode(encoder.encode(JSON.stringify(payload)));\n\n const signingInput = `${headerB64}.${payloadB64}`;\n\n const signature = await crypto.subtle.sign(\n { name: 'ECDSA', hash: 'SHA-256' },\n privateKey,\n encoder.encode(signingInput)\n );\n\n const signatureB64 = base64UrlEncode(signature);\n\n return `${signingInput}.${signatureB64}`;\n}\n", "import { generateRandomString } from '../utils/base64url';\nimport { sha256Base64Url, signJwt } from '../utils/crypto';\n\nconst DB_VERSION = 1;\nconst KEY_STORE = 'dpop-keys';\nconst KEY_ID = 'dpop-key';\n\ninterface DPoPKeyData {\n id: string;\n privateKey: CryptoKey;\n publicJwk: JsonWebKey;\n createdAt: number;\n}\n\n// Cache database connections per namespace\nconst dbPromises = new Map<string, Promise<IDBDatabase>>();\n\nfunction getDbName(namespace: string): string {\n return `quickslice-oauth-${namespace}`;\n}\n\nfunction openDatabase(namespace: string): Promise<IDBDatabase> {\n const existing = dbPromises.get(namespace);\n if (existing) return existing;\n\n const promise = new Promise<IDBDatabase>((resolve, reject) => {\n const request = indexedDB.open(getDbName(namespace), DB_VERSION);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result);\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n if (!db.objectStoreNames.contains(KEY_STORE)) {\n db.createObjectStore(KEY_STORE, { keyPath: 'id' });\n }\n };\n });\n\n dbPromises.set(namespace, promise);\n return promise;\n}\n\nasync function getDPoPKey(namespace: string): Promise<DPoPKeyData | null> {\n const db = await openDatabase(namespace);\n return new Promise((resolve, reject) => {\n const tx = db.transaction(KEY_STORE, 'readonly');\n const store = tx.objectStore(KEY_STORE);\n const request = store.get(KEY_ID);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result || null);\n });\n}\n\nasync function storeDPoPKey(\n namespace: string,\n privateKey: CryptoKey,\n publicJwk: JsonWebKey\n): Promise<void> {\n const db = await openDatabase(namespace);\n return new Promise((resolve, reject) => {\n const tx = db.transaction(KEY_STORE, 'readwrite');\n const store = tx.objectStore(KEY_STORE);\n const request = store.put({\n id: KEY_ID,\n privateKey,\n publicJwk,\n createdAt: Date.now(),\n });\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n}\n\nexport async function getOrCreateDPoPKey(namespace: string): Promise<DPoPKeyData> {\n const keyData = await getDPoPKey(namespace);\n\n if (keyData) {\n return keyData;\n }\n\n // Generate new P-256 key pair\n const keyPair = await crypto.subtle.generateKey(\n { name: 'ECDSA', namedCurve: 'P-256' },\n false, // NOT extractable - critical for security\n ['sign']\n );\n\n // Export public key as JWK\n const publicJwk = await crypto.subtle.exportKey('jwk', keyPair.publicKey);\n\n // Store in IndexedDB\n await storeDPoPKey(namespace, keyPair.privateKey, publicJwk);\n\n return {\n id: KEY_ID,\n privateKey: keyPair.privateKey,\n publicJwk,\n createdAt: Date.now(),\n };\n}\n\n/**\n * Create a DPoP proof JWT\n */\nexport async function createDPoPProof(\n namespace: string,\n method: string,\n url: string,\n accessToken: string | null = null\n): Promise<string> {\n const keyData = await getOrCreateDPoPKey(namespace);\n\n // Strip WebCrypto-specific fields from JWK for interoperability\n const { kty, crv, x, y } = keyData.publicJwk;\n const minimalJwk = { kty, crv, x, y };\n\n const header = {\n alg: 'ES256',\n typ: 'dpop+jwt',\n jwk: minimalJwk,\n };\n\n const payload: Record<string, unknown> = {\n jti: generateRandomString(16),\n htm: method,\n htu: url,\n iat: Math.floor(Date.now() / 1000),\n };\n\n // Add access token hash if provided (for resource requests)\n if (accessToken) {\n payload.ath = await sha256Base64Url(accessToken);\n }\n\n return await signJwt(header, payload, keyData.privateKey);\n}\n\n/**\n * Clear DPoP keys from IndexedDB\n */\nexport async function clearDPoPKeys(namespace: string): Promise<void> {\n const db = await openDatabase(namespace);\n return new Promise((resolve, reject) => {\n const tx = db.transaction(KEY_STORE, 'readwrite');\n const store = tx.objectStore(KEY_STORE);\n const request = store.clear();\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve();\n });\n}\n", "import { base64UrlEncode, generateRandomString } from '../utils/base64url';\n\n/**\n * Generate a PKCE code verifier (32 random bytes, base64url encoded)\n */\nexport function generateCodeVerifier(): string {\n return generateRandomString(32);\n}\n\n/**\n * Generate a PKCE code challenge from a verifier (SHA-256, base64url encoded)\n */\nexport async function generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const hash = await crypto.subtle.digest('SHA-256', data);\n return base64UrlEncode(hash);\n}\n\n/**\n * Generate a random state parameter for CSRF protection\n */\nexport function generateState(): string {\n return generateRandomString(16);\n}\n", "const LOCK_TIMEOUT = 5000; // 5 seconds\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction getLockKey(namespace: string, key: string): string {\n return `quickslice_${namespace}_lock_${key}`;\n}\n\n/**\n * Acquire a lock using localStorage for multi-tab coordination\n */\nexport async function acquireLock(\n namespace: string,\n key: string,\n timeout = LOCK_TIMEOUT\n): Promise<string | null> {\n const lockKey = getLockKey(namespace, key);\n const lockValue = `${Date.now()}_${Math.random()}`;\n const deadline = Date.now() + timeout;\n\n while (Date.now() < deadline) {\n const existing = localStorage.getItem(lockKey);\n\n if (existing) {\n // Check if lock is stale (older than timeout)\n const [timestamp] = existing.split('_');\n if (Date.now() - parseInt(timestamp) > LOCK_TIMEOUT) {\n // Lock is stale, remove it\n localStorage.removeItem(lockKey);\n } else {\n // Lock is held, wait and retry\n await sleep(50);\n continue;\n }\n }\n\n // Try to acquire\n localStorage.setItem(lockKey, lockValue);\n\n // Verify we got it (handle race condition)\n await sleep(10);\n if (localStorage.getItem(lockKey) === lockValue) {\n return lockValue; // Lock acquired\n }\n }\n\n return null; // Failed to acquire\n}\n\n/**\n * Release a lock\n */\nexport function releaseLock(namespace: string, key: string, lockValue: string): void {\n const lockKey = getLockKey(namespace, key);\n // Only release if we still hold it\n if (localStorage.getItem(lockKey) === lockValue) {\n localStorage.removeItem(lockKey);\n }\n}\n", "import { Storage } from '../storage/storage';\nimport { acquireLock, releaseLock } from '../storage/lock';\nimport { createDPoPProof } from './dpop';\n\nconst TOKEN_REFRESH_BUFFER_MS = 60000; // 60 seconds before expiry\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Refresh tokens using the refresh token\n */\nasync function refreshTokens(\n storage: Storage,\n namespace: string,\n tokenUrl: string\n): Promise<string> {\n const refreshToken = storage.get('refreshToken');\n const clientId = storage.get('clientId');\n\n if (!refreshToken || !clientId) {\n throw new Error('No refresh token available');\n }\n\n const dpopProof = await createDPoPProof(namespace, 'POST', tokenUrl);\n\n const response = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n DPoP: dpopProof,\n },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n client_id: clientId,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n throw new Error(\n `Token refresh failed: ${errorData.error_description || response.statusText}`\n );\n }\n\n const tokens = await response.json();\n\n // Store new tokens (rotation - new refresh token each time)\n storage.set('accessToken', tokens.access_token);\n if (tokens.refresh_token) {\n storage.set('refreshToken', tokens.refresh_token);\n }\n\n const expiresAt = Date.now() + tokens.expires_in * 1000;\n storage.set('tokenExpiresAt', expiresAt.toString());\n\n return tokens.access_token;\n}\n\n/**\n * Get a valid access token, refreshing if necessary.\n * Uses multi-tab locking to prevent duplicate refresh requests.\n */\nexport async function getValidAccessToken(\n storage: Storage,\n namespace: string,\n tokenUrl: string\n): Promise<string> {\n const accessToken = storage.get('accessToken');\n const expiresAt = parseInt(storage.get('tokenExpiresAt') || '0');\n\n // Check if token is still valid (with buffer)\n if (accessToken && Date.now() < expiresAt - TOKEN_REFRESH_BUFFER_MS) {\n return accessToken;\n }\n\n // Need to refresh - acquire lock first\n const lockKey = 'token_refresh';\n const lockValue = await acquireLock(namespace, lockKey);\n\n if (!lockValue) {\n // Failed to acquire lock, another tab is refreshing\n // Wait a bit and check cache again\n await sleep(100);\n const freshToken = storage.get('accessToken');\n const freshExpiry = parseInt(storage.get('tokenExpiresAt') || '0');\n if (freshToken && Date.now() < freshExpiry - TOKEN_REFRESH_BUFFER_MS) {\n return freshToken;\n }\n throw new Error('Failed to refresh token');\n }\n\n try {\n // Double-check after acquiring lock\n const freshToken = storage.get('accessToken');\n const freshExpiry = parseInt(storage.get('tokenExpiresAt') || '0');\n if (freshToken && Date.now() < freshExpiry - TOKEN_REFRESH_BUFFER_MS) {\n return freshToken;\n }\n\n // Actually refresh\n return await refreshTokens(storage, namespace, tokenUrl);\n } finally {\n releaseLock(namespace, lockKey, lockValue);\n }\n}\n\n/**\n * Store tokens from OAuth response\n */\nexport function storeTokens(\n storage: Storage,\n tokens: {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n sub?: string;\n }\n): void {\n storage.set('accessToken', tokens.access_token);\n if (tokens.refresh_token) {\n storage.set('refreshToken', tokens.refresh_token);\n }\n\n const expiresAt = Date.now() + tokens.expires_in * 1000;\n storage.set('tokenExpiresAt', expiresAt.toString());\n\n if (tokens.sub) {\n storage.set('userDid', tokens.sub);\n }\n}\n\n/**\n * Check if we have a valid session\n */\nexport function hasValidSession(storage: Storage): boolean {\n const accessToken = storage.get('accessToken');\n const refreshToken = storage.get('refreshToken');\n return !!(accessToken || refreshToken);\n}\n", "import { Storage } from '../storage/storage';\nimport { createDPoPProof, clearDPoPKeys } from './dpop';\nimport { generateCodeVerifier, generateCodeChallenge, generateState } from './pkce';\nimport { storeTokens } from './tokens';\n\nexport interface LoginOptions {\n handle?: string;\n redirectUri?: string;\n scope?: string;\n}\n\n/**\n * Initiate OAuth login flow with PKCE\n */\nexport async function initiateLogin(\n storage: Storage,\n authorizeUrl: string,\n clientId: string,\n options: LoginOptions = {}\n): Promise<void> {\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = await generateCodeChallenge(codeVerifier);\n const state = generateState();\n\n // Build redirect URI (use provided or derive from current page)\n const redirectUri = options.redirectUri || (window.location.origin + window.location.pathname);\n\n // Store for callback\n storage.set('codeVerifier', codeVerifier);\n storage.set('oauthState', state);\n storage.set('clientId', clientId);\n storage.set('redirectUri', redirectUri);\n\n // Build authorization URL\n const params = new URLSearchParams({\n client_id: clientId,\n redirect_uri: redirectUri,\n response_type: 'code',\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n state: state,\n });\n\n if (options.handle) {\n params.set('login_hint', options.handle);\n }\n\n if (options.scope) {\n params.set('scope', options.scope);\n }\n\n window.location.href = `${authorizeUrl}?${params.toString()}`;\n}\n\n/**\n * Handle OAuth callback - exchange code for tokens\n * Returns true if callback was handled, false if not a callback\n */\nexport async function handleOAuthCallback(\n storage: Storage,\n namespace: string,\n tokenUrl: string\n): Promise<boolean> {\n const params = new URLSearchParams(window.location.search);\n const code = params.get('code');\n const state = params.get('state');\n const error = params.get('error');\n\n if (error) {\n throw new Error(\n `OAuth error: ${error} - ${params.get('error_description') || ''}`\n );\n }\n\n if (!code || !state) {\n return false; // Not a callback\n }\n\n // Verify state\n const storedState = storage.get('oauthState');\n if (state !== storedState) {\n throw new Error('OAuth state mismatch - possible CSRF attack');\n }\n\n // Get stored values\n const codeVerifier = storage.get('codeVerifier');\n const clientId = storage.get('clientId');\n const redirectUri = storage.get('redirectUri');\n\n if (!codeVerifier || !clientId || !redirectUri) {\n throw new Error('Missing OAuth session data');\n }\n\n // Exchange code for tokens with DPoP\n const dpopProof = await createDPoPProof(namespace, 'POST', tokenUrl);\n\n const tokenResponse = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n DPoP: dpopProof,\n },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n code: code,\n redirect_uri: redirectUri,\n client_id: clientId,\n code_verifier: codeVerifier,\n }),\n });\n\n if (!tokenResponse.ok) {\n const errorData = await tokenResponse.json().catch(() => ({}));\n throw new Error(\n `Token exchange failed: ${errorData.error_description || tokenResponse.statusText}`\n );\n }\n\n const tokens = await tokenResponse.json();\n\n // Store tokens\n storeTokens(storage, tokens);\n\n // Clean up OAuth state\n storage.remove('codeVerifier');\n storage.remove('oauthState');\n storage.remove('redirectUri');\n\n // Clear URL params\n window.history.replaceState({}, document.title, window.location.pathname);\n\n return true;\n}\n\n/**\n * Logout - clear all stored data\n */\nexport async function logout(\n storage: Storage,\n namespace: string,\n options: { reload?: boolean } = {}\n): Promise<void> {\n storage.clear();\n await clearDPoPKeys(namespace);\n\n if (options.reload !== false) {\n window.location.reload();\n }\n}\n", "import { createDPoPProof } from './auth/dpop';\nimport { getValidAccessToken } from './auth/tokens';\nimport { Storage } from './storage/storage';\n\nexport interface GraphQLResponse<T = unknown> {\n data?: T;\n errors?: Array<{ message: string; path?: string[] }>;\n}\n\n/**\n * Execute a GraphQL query or mutation\n */\nexport async function graphqlRequest<T = unknown>(\n storage: Storage,\n namespace: string,\n graphqlUrl: string,\n tokenUrl: string,\n query: string,\n variables: Record<string, unknown> = {},\n requireAuth = false,\n signal?: AbortSignal\n): Promise<T> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n if (requireAuth) {\n const token = await getValidAccessToken(storage, namespace, tokenUrl);\n if (!token) {\n throw new Error('Not authenticated');\n }\n\n // Create DPoP proof bound to this request\n const dpopProof = await createDPoPProof(namespace, 'POST', graphqlUrl, token);\n\n headers['Authorization'] = `DPoP ${token}`;\n headers['DPoP'] = dpopProof;\n }\n\n const response = await fetch(graphqlUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify({ query, variables }),\n signal,\n });\n\n if (!response.ok) {\n throw new Error(`GraphQL request failed: ${response.statusText}`);\n }\n\n const result: GraphQLResponse<T> = await response.json();\n\n if (result.errors && result.errors.length > 0) {\n throw new Error(`GraphQL error: ${result.errors[0].message}`);\n }\n\n return result.data as T;\n}\n", "import { createStorageKeys } from './storage/keys';\nimport { createStorage, Storage } from './storage/storage';\nimport { getOrCreateDPoPKey } from './auth/dpop';\nimport { initiateLogin, handleOAuthCallback, logout as doLogout, LoginOptions } from './auth/oauth';\nimport { getValidAccessToken, hasValidSession } from './auth/tokens';\nimport { graphqlRequest } from './graphql';\nimport { generateNamespaceHash } from './utils/crypto';\n\nexport interface QuicksliceClientOptions {\n server: string;\n clientId: string;\n redirectUri?: string;\n scope?: string;\n}\n\nexport interface User {\n did: string;\n}\n\nexport interface QueryOptions {\n signal?: AbortSignal;\n}\n\nexport class QuicksliceClient {\n private server: string;\n private clientId: string;\n private redirectUri?: string;\n private scope?: string;\n private graphqlUrl: string;\n private authorizeUrl: string;\n private tokenUrl: string;\n private initialized = false;\n private namespace: string = '';\n private storage: Storage | null = null;\n\n constructor(options: QuicksliceClientOptions) {\n this.server = options.server.replace(/\\/$/, ''); // Remove trailing slash\n this.clientId = options.clientId;\n this.redirectUri = options.redirectUri;\n this.scope = options.scope;\n\n this.graphqlUrl = `${this.server}/graphql`;\n this.authorizeUrl = `${this.server}/oauth/authorize`;\n this.tokenUrl = `${this.server}/oauth/token`;\n }\n\n /**\n * Initialize the client - must be called before other methods\n */\n async init(): Promise<void> {\n if (this.initialized) return;\n\n // Generate namespace from clientId\n this.namespace = await generateNamespaceHash(this.clientId);\n\n // Create namespaced storage\n const keys = createStorageKeys(this.namespace);\n this.storage = createStorage(keys);\n\n // Ensure DPoP key exists\n await getOrCreateDPoPKey(this.namespace);\n\n this.initialized = true;\n }\n\n private getStorage(): Storage {\n if (!this.storage) {\n throw new Error('Client not initialized. Call init() first.');\n }\n return this.storage;\n }\n\n /**\n * Start OAuth login flow\n */\n async loginWithRedirect(options: LoginOptions = {}): Promise<void> {\n await this.init();\n await initiateLogin(this.getStorage(), this.authorizeUrl, this.clientId, {\n ...options,\n redirectUri: options.redirectUri || this.redirectUri,\n scope: options.scope || this.scope,\n });\n }\n\n /**\n * Handle OAuth callback after redirect\n * Returns true if callback was handled\n */\n async handleRedirectCallback(): Promise<boolean> {\n await this.init();\n return await handleOAuthCallback(this.getStorage(), this.namespace, this.tokenUrl);\n }\n\n /**\n * Logout and clear all stored data\n */\n async logout(options: { reload?: boolean } = {}): Promise<void> {\n await this.init();\n await doLogout(this.getStorage(), this.namespace, options);\n }\n\n /**\n * Check if user is authenticated\n */\n async isAuthenticated(): Promise<boolean> {\n await this.init();\n return hasValidSession(this.getStorage());\n }\n\n /**\n * Get current user's DID (from stored token data)\n * For richer profile info, use client.query() with your own schema\n */\n async getUser(): Promise<User | null> {\n await this.init();\n if (!hasValidSession(this.getStorage())) {\n return null;\n }\n\n const did = this.getStorage().get('userDid');\n if (!did) {\n return null;\n }\n\n return { did };\n }\n\n /**\n * Get access token (auto-refreshes if needed)\n */\n async getAccessToken(): Promise<string> {\n await this.init();\n return await getValidAccessToken(this.getStorage(), this.namespace, this.tokenUrl);\n }\n\n /**\n * Execute a GraphQL query (authenticated)\n */\n async query<T = unknown>(\n query: string,\n variables: Record<string, unknown> = {},\n options: QueryOptions = {}\n ): Promise<T> {\n await this.init();\n return await graphqlRequest<T>(\n this.getStorage(),\n this.namespace,\n this.graphqlUrl,\n this.tokenUrl,\n query,\n variables,\n true,\n options.signal\n );\n }\n\n /**\n * Execute a GraphQL mutation (authenticated)\n */\n async mutate<T = unknown>(\n mutation: string,\n variables: Record<string, unknown> = {},\n options: QueryOptions = {}\n ): Promise<T> {\n return this.query<T>(mutation, variables, options);\n }\n\n /**\n * Execute a public GraphQL query (no auth)\n */\n async publicQuery<T = unknown>(\n query: string,\n variables: Record<string, unknown> = {},\n options: QueryOptions = {}\n ): Promise<T> {\n await this.init();\n return await graphqlRequest<T>(\n this.getStorage(),\n this.namespace,\n this.graphqlUrl,\n this.tokenUrl,\n query,\n variables,\n false,\n options.signal\n );\n }\n}\n", "/**\n * Base error class for Quickslice client errors\n */\nexport class QuicksliceError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'QuicksliceError';\n }\n}\n\n/**\n * Thrown when authentication is required but user is not logged in\n */\nexport class LoginRequiredError extends QuicksliceError {\n constructor(message = 'Login required') {\n super(message);\n this.name = 'LoginRequiredError';\n }\n}\n\n/**\n * Thrown when network request fails\n */\nexport class NetworkError extends QuicksliceError {\n constructor(message: string) {\n super(message);\n this.name = 'NetworkError';\n }\n}\n\n/**\n * Thrown when OAuth flow fails\n */\nexport class OAuthError extends QuicksliceError {\n public code: string;\n public description?: string;\n\n constructor(code: string, description?: string) {\n super(`OAuth error: ${code}${description ? ` - ${description}` : ''}`);\n this.name = 'OAuthError';\n this.code = code;\n this.description = description;\n }\n}\n"], 5 + "mappings": "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", 6 6 "names": ["sleep"] 7 7 }
+1 -1
quickslice-client-js/dist/quickslice-client.min.js
··· 1 - "use strict";var QuicksliceClient=(()=>{var _=Object.defineProperty;var W=Object.getOwnPropertyDescriptor;var Y=Object.getOwnPropertyNames;var X=Object.prototype.hasOwnProperty;var Z=(t,e)=>{for(var r in e)_(t,r,{get:e[r],enumerable:!0})},ee=(t,e,r,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Y(e))!X.call(t,i)&&i!==r&&_(t,i,{get:()=>e[i],enumerable:!(o=W(e,i))||o.enumerable});return t};var te=t=>ee(_({},"__esModule",{value:!0}),t);var ge={};Z(ge,{LoginRequiredError:()=>S,NetworkError:()=>P,OAuthError:()=>x,QuicksliceClient:()=>m,QuicksliceError:()=>h,createQuicksliceClient:()=>ce});function K(t){return{accessToken:`quickslice_${t}_access_token`,refreshToken:`quickslice_${t}_refresh_token`,tokenExpiresAt:`quickslice_${t}_token_expires_at`,clientId:`quickslice_${t}_client_id`,userDid:`quickslice_${t}_user_did`,codeVerifier:`quickslice_${t}_code_verifier`,oauthState:`quickslice_${t}_oauth_state`,redirectUri:`quickslice_${t}_redirect_uri`}}function A(t){return{get(e){let r=t[e];return e==="codeVerifier"||e==="oauthState"?sessionStorage.getItem(r):localStorage.getItem(r)},set(e,r){let o=t[e];e==="codeVerifier"||e==="oauthState"?sessionStorage.setItem(o,r):localStorage.setItem(o,r)},remove(e){let r=t[e];sessionStorage.removeItem(r),localStorage.removeItem(r)},clear(){Object.keys(t).forEach(e=>{let r=t[e];sessionStorage.removeItem(r),localStorage.removeItem(r)})}}}function u(t){let e=t instanceof Uint8Array?t:new Uint8Array(t),r="";for(let o=0;o<e.length;o++)r+=String.fromCharCode(e[o]);return btoa(r).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function w(t){let e=new Uint8Array(t);return crypto.getRandomValues(e),u(e)}async function I(t){let e=new TextEncoder,r=await crypto.subtle.digest("SHA-256",e.encode(t));return u(r)}async function O(t){let e=new TextEncoder,r=await crypto.subtle.digest("SHA-256",e.encode(t));return Array.from(new Uint8Array(r)).map(a=>a.toString(16).padStart(2,"0")).join("").substring(0,8)}async function C(t,e,r){let o=new TextEncoder,i=u(o.encode(JSON.stringify(t))),a=u(o.encode(JSON.stringify(e))),n=`${i}.${a}`,s=await crypto.subtle.sign({name:"ECDSA",hash:"SHA-256"},r,o.encode(n)),c=u(s);return`${n}.${c}`}var re=1,d="dpop-keys",T="dpop-key",$=new Map;function oe(t){return`quickslice-oauth-${t}`}function b(t){let e=$.get(t);if(e)return e;let r=new Promise((o,i)=>{let a=indexedDB.open(oe(t),re);a.onerror=()=>i(a.error),a.onsuccess=()=>o(a.result),a.onupgradeneeded=n=>{let s=n.target.result;s.objectStoreNames.contains(d)||s.createObjectStore(d,{keyPath:"id"})}});return $.set(t,r),r}async function ne(t){let e=await b(t);return new Promise((r,o)=>{let n=e.transaction(d,"readonly").objectStore(d).get(T);n.onerror=()=>o(n.error),n.onsuccess=()=>r(n.result||null)})}async function ie(t,e,r){let o=await b(t);return new Promise((i,a)=>{let c=o.transaction(d,"readwrite").objectStore(d).put({id:T,privateKey:e,publicJwk:r,createdAt:Date.now()});c.onerror=()=>a(c.error),c.onsuccess=()=>i()})}async function D(t){let e=await ne(t);if(e)return e;let r=await crypto.subtle.generateKey({name:"ECDSA",namedCurve:"P-256"},!1,["sign"]),o=await crypto.subtle.exportKey("jwk",r.publicKey);return await ie(t,r.privateKey,o),{id:T,privateKey:r.privateKey,publicJwk:o,createdAt:Date.now()}}async function f(t,e,r,o=null){let i=await D(t),{kty:a,crv:n,x:s,y:c}=i.publicJwk,l={alg:"ES256",typ:"dpop+jwt",jwk:{kty:a,crv:n,x:s,y:c}},p={jti:w(16),htm:e,htu:r,iat:Math.floor(Date.now()/1e3)};return o&&(p.ath=await I(o)),await C(l,p,i.privateKey)}async function R(t){let e=await b(t);return new Promise((r,o)=>{let n=e.transaction(d,"readwrite").objectStore(d).clear();n.onerror=()=>o(n.error),n.onsuccess=()=>r()})}function q(){return w(32)}async function L(t){let r=new TextEncoder().encode(t),o=await crypto.subtle.digest("SHA-256",r);return u(o)}function V(){return w(16)}function j(t){return new Promise(e=>setTimeout(e,t))}function B(t,e){return`quickslice_${t}_lock_${e}`}async function Q(t,e,r=5e3){let o=B(t,e),i=`${Date.now()}_${Math.random()}`,a=Date.now()+r;for(;Date.now()<a;){let n=localStorage.getItem(o);if(n){let[s]=n.split("_");if(Date.now()-parseInt(s)>5e3)localStorage.removeItem(o);else{await j(50);continue}}if(localStorage.setItem(o,i),await j(10),localStorage.getItem(o)===i)return i}return null}function J(t,e,r){let o=B(t,e);localStorage.getItem(o)===r&&localStorage.removeItem(o)}var v=6e4;function se(t){return new Promise(e=>setTimeout(e,t))}async function ae(t,e,r){let o=t.get("refreshToken"),i=t.get("clientId");if(!o||!i)throw new Error("No refresh token available");let a=await f(e,"POST",r),n=await fetch(r,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",DPoP:a},body:new URLSearchParams({grant_type:"refresh_token",refresh_token:o,client_id:i})});if(!n.ok){let g=await n.json().catch(()=>({}));throw new Error(`Token refresh failed: ${g.error_description||n.statusText}`)}let s=await n.json();t.set("accessToken",s.access_token),s.refresh_token&&t.set("refreshToken",s.refresh_token);let c=Date.now()+s.expires_in*1e3;return t.set("tokenExpiresAt",c.toString()),s.access_token}async function k(t,e,r){let o=t.get("accessToken"),i=parseInt(t.get("tokenExpiresAt")||"0");if(o&&Date.now()<i-v)return o;let a="token_refresh",n=await Q(e,a);if(!n){await se(100);let s=t.get("accessToken"),c=parseInt(t.get("tokenExpiresAt")||"0");if(s&&Date.now()<c-v)return s;throw new Error("Failed to refresh token")}try{let s=t.get("accessToken"),c=parseInt(t.get("tokenExpiresAt")||"0");return s&&Date.now()<c-v?s:await ae(t,e,r)}finally{J(e,a,n)}}function N(t,e){t.set("accessToken",e.access_token),e.refresh_token&&t.set("refreshToken",e.refresh_token);let r=Date.now()+e.expires_in*1e3;t.set("tokenExpiresAt",r.toString()),e.sub&&t.set("userDid",e.sub)}function U(t){let e=t.get("accessToken"),r=t.get("refreshToken");return!!(e||r)}async function z(t,e,r,o={}){let i=q(),a=await L(i),n=V(),s=o.redirectUri||window.location.origin+window.location.pathname;t.set("codeVerifier",i),t.set("oauthState",n),t.set("clientId",r),t.set("redirectUri",s);let c=new URLSearchParams({client_id:r,redirect_uri:s,response_type:"code",code_challenge:a,code_challenge_method:"S256",state:n});o.handle&&c.set("login_hint",o.handle),o.scope&&c.set("scope",o.scope),window.location.href=`${e}?${c.toString()}`}async function H(t,e,r){let o=new URLSearchParams(window.location.search),i=o.get("code"),a=o.get("state"),n=o.get("error");if(n)throw new Error(`OAuth error: ${n} - ${o.get("error_description")||""}`);if(!i||!a)return!1;let s=t.get("oauthState");if(a!==s)throw new Error("OAuth state mismatch - possible CSRF attack");let c=t.get("codeVerifier"),g=t.get("clientId"),l=t.get("redirectUri");if(!c||!g||!l)throw new Error("Missing OAuth session data");let p=await f(e,"POST",r),y=await fetch(r,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",DPoP:p},body:new URLSearchParams({grant_type:"authorization_code",code:i,redirect_uri:l,client_id:g,code_verifier:c})});if(!y.ok){let G=await y.json().catch(()=>({}));throw new Error(`Token exchange failed: ${G.error_description||y.statusText}`)}let F=await y.json();return N(t,F),t.remove("codeVerifier"),t.remove("oauthState"),t.remove("redirectUri"),window.history.replaceState({},document.title,window.location.pathname),!0}async function M(t,e,r={}){t.clear(),await R(e),r.reload!==!1&&window.location.reload()}async function E(t,e,r,o,i,a={},n=!1){let s={"Content-Type":"application/json"};if(n){let l=await k(t,e,o);if(!l)throw new Error("Not authenticated");let p=await f(e,"POST",r,l);s.Authorization=`DPoP ${l}`,s.DPoP=p}let c=await fetch(r,{method:"POST",headers:s,body:JSON.stringify({query:i,variables:a})});if(!c.ok)throw new Error(`GraphQL request failed: ${c.statusText}`);let g=await c.json();if(g.errors&&g.errors.length>0)throw new Error(`GraphQL error: ${g.errors[0].message}`);return g.data}var m=class{constructor(e){this.initialized=!1;this.namespace="";this.storage=null;this.server=e.server.replace(/\/$/,""),this.clientId=e.clientId,this.redirectUri=e.redirectUri,this.scope=e.scope,this.graphqlUrl=`${this.server}/graphql`,this.authorizeUrl=`${this.server}/oauth/authorize`,this.tokenUrl=`${this.server}/oauth/token`}async init(){if(this.initialized)return;this.namespace=await O(this.clientId);let e=K(this.namespace);this.storage=A(e),await D(this.namespace),this.initialized=!0}getStorage(){if(!this.storage)throw new Error("Client not initialized. Call init() first.");return this.storage}async loginWithRedirect(e={}){await this.init(),await z(this.getStorage(),this.authorizeUrl,this.clientId,{...e,redirectUri:e.redirectUri||this.redirectUri,scope:e.scope||this.scope})}async handleRedirectCallback(){return await this.init(),await H(this.getStorage(),this.namespace,this.tokenUrl)}async logout(e={}){await this.init(),await M(this.getStorage(),this.namespace,e)}async isAuthenticated(){return await this.init(),U(this.getStorage())}async getUser(){if(await this.init(),!U(this.getStorage()))return null;let e=this.getStorage().get("userDid");return e?{did:e}:null}async getAccessToken(){return await this.init(),await k(this.getStorage(),this.namespace,this.tokenUrl)}async query(e,r={}){return await this.init(),await E(this.getStorage(),this.namespace,this.graphqlUrl,this.tokenUrl,e,r,!0)}async mutate(e,r={}){return this.query(e,r)}async publicQuery(e,r={}){return await this.init(),await E(this.getStorage(),this.namespace,this.graphqlUrl,this.tokenUrl,e,r,!1)}};var h=class extends Error{constructor(e){super(e),this.name="QuicksliceError"}},S=class extends h{constructor(e="Login required"){super(e),this.name="LoginRequiredError"}},P=class extends h{constructor(e){super(e),this.name="NetworkError"}},x=class extends h{constructor(e,r){super(`OAuth error: ${e}${r?` - ${r}`:""}`),this.name="OAuthError",this.code=e,this.description=r}};async function ce(t){let e=new m(t);return await e.init(),e}return te(ge);})(); 1 + "use strict";var QuicksliceClient=(()=>{var _=Object.defineProperty;var W=Object.getOwnPropertyDescriptor;var Y=Object.getOwnPropertyNames;var X=Object.prototype.hasOwnProperty;var Z=(t,e)=>{for(var r in e)_(t,r,{get:e[r],enumerable:!0})},ee=(t,e,r,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Y(e))!X.call(t,i)&&i!==r&&_(t,i,{get:()=>e[i],enumerable:!(o=W(e,i))||o.enumerable});return t};var te=t=>ee(_({},"__esModule",{value:!0}),t);var ge={};Z(ge,{LoginRequiredError:()=>S,NetworkError:()=>P,OAuthError:()=>x,QuicksliceClient:()=>w,QuicksliceError:()=>h,createQuicksliceClient:()=>ce});function K(t){return{accessToken:`quickslice_${t}_access_token`,refreshToken:`quickslice_${t}_refresh_token`,tokenExpiresAt:`quickslice_${t}_token_expires_at`,clientId:`quickslice_${t}_client_id`,userDid:`quickslice_${t}_user_did`,codeVerifier:`quickslice_${t}_code_verifier`,oauthState:`quickslice_${t}_oauth_state`,redirectUri:`quickslice_${t}_redirect_uri`}}function A(t){return{get(e){let r=t[e];return e==="codeVerifier"||e==="oauthState"?sessionStorage.getItem(r):localStorage.getItem(r)},set(e,r){let o=t[e];e==="codeVerifier"||e==="oauthState"?sessionStorage.setItem(o,r):localStorage.setItem(o,r)},remove(e){let r=t[e];sessionStorage.removeItem(r),localStorage.removeItem(r)},clear(){Object.keys(t).forEach(e=>{let r=t[e];sessionStorage.removeItem(r),localStorage.removeItem(r)})}}}function d(t){let e=t instanceof Uint8Array?t:new Uint8Array(t),r="";for(let o=0;o<e.length;o++)r+=String.fromCharCode(e[o]);return btoa(r).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function y(t){let e=new Uint8Array(t);return crypto.getRandomValues(e),d(e)}async function O(t){let e=new TextEncoder,r=await crypto.subtle.digest("SHA-256",e.encode(t));return d(r)}async function I(t){let e=new TextEncoder,r=await crypto.subtle.digest("SHA-256",e.encode(t));return Array.from(new Uint8Array(r)).map(s=>s.toString(16).padStart(2,"0")).join("").substring(0,8)}async function C(t,e,r){let o=new TextEncoder,i=d(o.encode(JSON.stringify(t))),s=d(o.encode(JSON.stringify(e))),n=`${i}.${s}`,a=await crypto.subtle.sign({name:"ECDSA",hash:"SHA-256"},r,o.encode(n)),c=d(a);return`${n}.${c}`}var re=1,p="dpop-keys",T="dpop-key",$=new Map;function oe(t){return`quickslice-oauth-${t}`}function b(t){let e=$.get(t);if(e)return e;let r=new Promise((o,i)=>{let s=indexedDB.open(oe(t),re);s.onerror=()=>i(s.error),s.onsuccess=()=>o(s.result),s.onupgradeneeded=n=>{let a=n.target.result;a.objectStoreNames.contains(p)||a.createObjectStore(p,{keyPath:"id"})}});return $.set(t,r),r}async function ne(t){let e=await b(t);return new Promise((r,o)=>{let n=e.transaction(p,"readonly").objectStore(p).get(T);n.onerror=()=>o(n.error),n.onsuccess=()=>r(n.result||null)})}async function ie(t,e,r){let o=await b(t);return new Promise((i,s)=>{let c=o.transaction(p,"readwrite").objectStore(p).put({id:T,privateKey:e,publicJwk:r,createdAt:Date.now()});c.onerror=()=>s(c.error),c.onsuccess=()=>i()})}async function D(t){let e=await ne(t);if(e)return e;let r=await crypto.subtle.generateKey({name:"ECDSA",namedCurve:"P-256"},!1,["sign"]),o=await crypto.subtle.exportKey("jwk",r.publicKey);return await ie(t,r.privateKey,o),{id:T,privateKey:r.privateKey,publicJwk:o,createdAt:Date.now()}}async function m(t,e,r,o=null){let i=await D(t),{kty:s,crv:n,x:a,y:c}=i.publicJwk,l={alg:"ES256",typ:"dpop+jwt",jwk:{kty:s,crv:n,x:a,y:c}},u={jti:y(16),htm:e,htu:r,iat:Math.floor(Date.now()/1e3)};return o&&(u.ath=await O(o)),await C(l,u,i.privateKey)}async function R(t){let e=await b(t);return new Promise((r,o)=>{let n=e.transaction(p,"readwrite").objectStore(p).clear();n.onerror=()=>o(n.error),n.onsuccess=()=>r()})}function q(){return y(32)}async function L(t){let r=new TextEncoder().encode(t),o=await crypto.subtle.digest("SHA-256",r);return d(o)}function V(){return y(16)}function Q(t){return new Promise(e=>setTimeout(e,t))}function j(t,e){return`quickslice_${t}_lock_${e}`}async function B(t,e,r=5e3){let o=j(t,e),i=`${Date.now()}_${Math.random()}`,s=Date.now()+r;for(;Date.now()<s;){let n=localStorage.getItem(o);if(n){let[a]=n.split("_");if(Date.now()-parseInt(a)>5e3)localStorage.removeItem(o);else{await Q(50);continue}}if(localStorage.setItem(o,i),await Q(10),localStorage.getItem(o)===i)return i}return null}function J(t,e,r){let o=j(t,e);localStorage.getItem(o)===r&&localStorage.removeItem(o)}var v=6e4;function se(t){return new Promise(e=>setTimeout(e,t))}async function ae(t,e,r){let o=t.get("refreshToken"),i=t.get("clientId");if(!o||!i)throw new Error("No refresh token available");let s=await m(e,"POST",r),n=await fetch(r,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",DPoP:s},body:new URLSearchParams({grant_type:"refresh_token",refresh_token:o,client_id:i})});if(!n.ok){let g=await n.json().catch(()=>({}));throw new Error(`Token refresh failed: ${g.error_description||n.statusText}`)}let a=await n.json();t.set("accessToken",a.access_token),a.refresh_token&&t.set("refreshToken",a.refresh_token);let c=Date.now()+a.expires_in*1e3;return t.set("tokenExpiresAt",c.toString()),a.access_token}async function k(t,e,r){let o=t.get("accessToken"),i=parseInt(t.get("tokenExpiresAt")||"0");if(o&&Date.now()<i-v)return o;let s="token_refresh",n=await B(e,s);if(!n){await se(100);let a=t.get("accessToken"),c=parseInt(t.get("tokenExpiresAt")||"0");if(a&&Date.now()<c-v)return a;throw new Error("Failed to refresh token")}try{let a=t.get("accessToken"),c=parseInt(t.get("tokenExpiresAt")||"0");return a&&Date.now()<c-v?a:await ae(t,e,r)}finally{J(e,s,n)}}function N(t,e){t.set("accessToken",e.access_token),e.refresh_token&&t.set("refreshToken",e.refresh_token);let r=Date.now()+e.expires_in*1e3;t.set("tokenExpiresAt",r.toString()),e.sub&&t.set("userDid",e.sub)}function U(t){let e=t.get("accessToken"),r=t.get("refreshToken");return!!(e||r)}async function z(t,e,r,o={}){let i=q(),s=await L(i),n=V(),a=o.redirectUri||window.location.origin+window.location.pathname;t.set("codeVerifier",i),t.set("oauthState",n),t.set("clientId",r),t.set("redirectUri",a);let c=new URLSearchParams({client_id:r,redirect_uri:a,response_type:"code",code_challenge:s,code_challenge_method:"S256",state:n});o.handle&&c.set("login_hint",o.handle),o.scope&&c.set("scope",o.scope),window.location.href=`${e}?${c.toString()}`}async function H(t,e,r){let o=new URLSearchParams(window.location.search),i=o.get("code"),s=o.get("state"),n=o.get("error");if(n)throw new Error(`OAuth error: ${n} - ${o.get("error_description")||""}`);if(!i||!s)return!1;let a=t.get("oauthState");if(s!==a)throw new Error("OAuth state mismatch - possible CSRF attack");let c=t.get("codeVerifier"),g=t.get("clientId"),l=t.get("redirectUri");if(!c||!g||!l)throw new Error("Missing OAuth session data");let u=await m(e,"POST",r),f=await fetch(r,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",DPoP:u},body:new URLSearchParams({grant_type:"authorization_code",code:i,redirect_uri:l,client_id:g,code_verifier:c})});if(!f.ok){let G=await f.json().catch(()=>({}));throw new Error(`Token exchange failed: ${G.error_description||f.statusText}`)}let F=await f.json();return N(t,F),t.remove("codeVerifier"),t.remove("oauthState"),t.remove("redirectUri"),window.history.replaceState({},document.title,window.location.pathname),!0}async function M(t,e,r={}){t.clear(),await R(e),r.reload!==!1&&window.location.reload()}async function E(t,e,r,o,i,s={},n=!1,a){let c={"Content-Type":"application/json"};if(n){let u=await k(t,e,o);if(!u)throw new Error("Not authenticated");let f=await m(e,"POST",r,u);c.Authorization=`DPoP ${u}`,c.DPoP=f}let g=await fetch(r,{method:"POST",headers:c,body:JSON.stringify({query:i,variables:s}),signal:a});if(!g.ok)throw new Error(`GraphQL request failed: ${g.statusText}`);let l=await g.json();if(l.errors&&l.errors.length>0)throw new Error(`GraphQL error: ${l.errors[0].message}`);return l.data}var w=class{constructor(e){this.initialized=!1;this.namespace="";this.storage=null;this.server=e.server.replace(/\/$/,""),this.clientId=e.clientId,this.redirectUri=e.redirectUri,this.scope=e.scope,this.graphqlUrl=`${this.server}/graphql`,this.authorizeUrl=`${this.server}/oauth/authorize`,this.tokenUrl=`${this.server}/oauth/token`}async init(){if(this.initialized)return;this.namespace=await I(this.clientId);let e=K(this.namespace);this.storage=A(e),await D(this.namespace),this.initialized=!0}getStorage(){if(!this.storage)throw new Error("Client not initialized. Call init() first.");return this.storage}async loginWithRedirect(e={}){await this.init(),await z(this.getStorage(),this.authorizeUrl,this.clientId,{...e,redirectUri:e.redirectUri||this.redirectUri,scope:e.scope||this.scope})}async handleRedirectCallback(){return await this.init(),await H(this.getStorage(),this.namespace,this.tokenUrl)}async logout(e={}){await this.init(),await M(this.getStorage(),this.namespace,e)}async isAuthenticated(){return await this.init(),U(this.getStorage())}async getUser(){if(await this.init(),!U(this.getStorage()))return null;let e=this.getStorage().get("userDid");return e?{did:e}:null}async getAccessToken(){return await this.init(),await k(this.getStorage(),this.namespace,this.tokenUrl)}async query(e,r={},o={}){return await this.init(),await E(this.getStorage(),this.namespace,this.graphqlUrl,this.tokenUrl,e,r,!0,o.signal)}async mutate(e,r={},o={}){return this.query(e,r,o)}async publicQuery(e,r={},o={}){return await this.init(),await E(this.getStorage(),this.namespace,this.graphqlUrl,this.tokenUrl,e,r,!1,o.signal)}};var h=class extends Error{constructor(e){super(e),this.name="QuicksliceError"}},S=class extends h{constructor(e="Login required"){super(e),this.name="LoginRequiredError"}},P=class extends h{constructor(e){super(e),this.name="NetworkError"}},x=class extends h{constructor(e,r){super(`OAuth error: ${e}${r?` - ${r}`:""}`),this.name="OAuthError",this.code=e,this.description=r}};async function ce(t){let e=new w(t);return await e.init(),e}return te(ge);})();
+15 -6
quickslice-client-js/src/client.ts
··· 17 17 did: string; 18 18 } 19 19 20 + export interface QueryOptions { 21 + signal?: AbortSignal; 22 + } 23 + 20 24 export class QuicksliceClient { 21 25 private server: string; 22 26 private clientId: string; ··· 134 138 */ 135 139 async query<T = unknown>( 136 140 query: string, 137 - variables: Record<string, unknown> = {} 141 + variables: Record<string, unknown> = {}, 142 + options: QueryOptions = {} 138 143 ): Promise<T> { 139 144 await this.init(); 140 145 return await graphqlRequest<T>( ··· 144 149 this.tokenUrl, 145 150 query, 146 151 variables, 147 - true 152 + true, 153 + options.signal 148 154 ); 149 155 } 150 156 ··· 153 159 */ 154 160 async mutate<T = unknown>( 155 161 mutation: string, 156 - variables: Record<string, unknown> = {} 162 + variables: Record<string, unknown> = {}, 163 + options: QueryOptions = {} 157 164 ): Promise<T> { 158 - return this.query<T>(mutation, variables); 165 + return this.query<T>(mutation, variables, options); 159 166 } 160 167 161 168 /** ··· 163 170 */ 164 171 async publicQuery<T = unknown>( 165 172 query: string, 166 - variables: Record<string, unknown> = {} 173 + variables: Record<string, unknown> = {}, 174 + options: QueryOptions = {} 167 175 ): Promise<T> { 168 176 await this.init(); 169 177 return await graphqlRequest<T>( ··· 173 181 this.tokenUrl, 174 182 query, 175 183 variables, 176 - false 184 + false, 185 + options.signal 177 186 ); 178 187 } 179 188 }
+3 -1
quickslice-client-js/src/graphql.ts
··· 17 17 tokenUrl: string, 18 18 query: string, 19 19 variables: Record<string, unknown> = {}, 20 - requireAuth = false 20 + requireAuth = false, 21 + signal?: AbortSignal 21 22 ): Promise<T> { 22 23 const headers: Record<string, string> = { 23 24 'Content-Type': 'application/json', ··· 40 41 method: 'POST', 41 42 headers, 42 43 body: JSON.stringify({ query, variables }), 44 + signal, 43 45 }); 44 46 45 47 if (!response.ok) {
+1 -1
quickslice-client-js/src/index.ts
··· 1 - export { QuicksliceClient, QuicksliceClientOptions, User } from './client'; 1 + export { QuicksliceClient, QuicksliceClientOptions, QueryOptions, User } from './client'; 2 2 export { 3 3 QuicksliceError, 4 4 LoginRequiredError,