From 9b9df56f36c24d270df416f33625e19946d8f5de Mon Sep 17 00:00:00 2001 From: Spencer Chang Date: Fri, 22 Aug 2025 15:12:14 -0700 Subject: [PATCH 1/3] checkpoint --- docs/auth.md | 799 ++++++++++++++++++ packages/common/src/auth.ts | 120 +++ packages/common/src/index.ts | 3 + packages/playhtml/src/auth.ts | 516 +++++++++++ packages/playhtml/src/crypto.ts | 258 ++++++ packages/playhtml/src/index.ts | 222 ++++- packages/react/src/elements.tsx | 26 +- .../react/src/hooks/usePlayHTMLPermissions.ts | 88 ++ packages/react/src/index.tsx | 50 +- partykit/party.ts | 267 +++++- website/examples/auth-example.html | 489 +++++++++++ 11 files changed, 2805 insertions(+), 33 deletions(-) create mode 100644 docs/auth.md create mode 100644 packages/common/src/auth.ts create mode 100644 packages/playhtml/src/auth.ts create mode 100644 packages/playhtml/src/crypto.ts create mode 100644 packages/react/src/hooks/usePlayHTMLPermissions.ts create mode 100644 website/examples/auth-example.html diff --git a/docs/auth.md b/docs/auth.md new file mode 100644 index 0000000..6a3f846 --- /dev/null +++ b/docs/auth.md @@ -0,0 +1,799 @@ +8.# PlayHTML Authentication & Identity System Design + +## Overview + +A **lightweight, decentralized identity system** that works seamlessly between the browser extension and core PlayHTML library. Like MetaMask for social web interactions, providing permissioned access to PlayHTML elements across the internet without requiring traditional login systems. + +## Core Architecture + +### 1. Identity Generation & Storage + +**Hosted Web Interface** (`auth.playhtml.fun`): + +```typescript +interface PlayHTMLIdentity { + privateKey: string; // Ed25519 private key for signing + publicKey: string; // Public identity (like MetaMask address) + displayName?: string; // Human-readable name + avatar?: string; // Custom avatar/cursor style + createdAt: number; // Identity creation timestamp + version: number; // For future migrations +} + +// Key generation using Web Crypto API +async function generateIdentity( + displayName?: string +): Promise { + const keyPair = await crypto.subtle.generateKey( + { name: "Ed25519", namedCurve: "Ed25519" }, + true, + ["sign", "verify"] + ); + + const privateKey = await crypto.subtle.exportKey("pkcs8", keyPair.privateKey); + const publicKey = await crypto.subtle.exportKey("spki", keyPair.publicKey); + + return { + privateKey: arrayBufferToBase64(privateKey), + publicKey: arrayBufferToBase64(publicKey), + displayName, + createdAt: Date.now(), + version: 1, + }; +} +``` + +**Export/Import for Portability**: + +```typescript +// Export identity as encrypted JSON file +function exportIdentity(identity: PlayHTMLIdentity, password?: string): string { + const data = password ? encrypt(identity, password) : identity; + return JSON.stringify(data, null, 2); +} + +// Import from file with password recovery +function importIdentity(jsonData: string, password?: string): PlayHTMLIdentity { + const parsed = JSON.parse(jsonData); + return password ? decrypt(parsed, password) : parsed; +} +``` + +### 2. Extension Integration + +**Global Identity Injection**: + +```typescript +// Extension injects identity into page context +interface PlayHTMLAuth { + identity?: PlayHTMLIdentity; + isAuthenticated: boolean; + sign: (message: string) => Promise; + verify: ( + message: string, + signature: string, + publicKey: string + ) => Promise; +} + +// Global object available to all PlayHTML instances +declare global { + interface Window { + playhtmlAuth?: PlayHTMLAuth; + } +} + +// Content script automatically injects authenticated identity +function injectAuth() { + const identity = getStoredIdentity(); // From extension storage + + window.playhtmlAuth = { + identity, + isAuthenticated: !!identity, + sign: async (message) => signMessage(message, identity.privateKey), + verify: async (message, signature, publicKey) => + verifySignature(message, signature, publicKey), + }; + + // Dispatch event so PlayHTML can react to auth changes + window.dispatchEvent( + new CustomEvent("playhtmlAuthReady", { detail: window.playhtmlAuth }) + ); +} +``` + +### 3. Clean HTML Permission System + +**HTML Attributes - Keep It Simple**: + +```html + +
+ + +
+ + +
+ + +
+``` + +**React Component Props**: + +```tsx +// React components use props instead of HTML attributes +function MyGuestbook() { + return ( + + {/* guestbook content */} + + ); +} + +// Alternative object syntax for complex permissions +function ComplexElement() { + return ( + + {/* element content */} + + ); +} +``` + +### 4. JavaScript Configuration System + +**Global PlayHTML Configuration**: + +### 5. Automatic Signing & Server Validation + +**Client-Side Automatic Signing**: + +```typescript +// Enhanced setData with automatic authentication +async function setData( + elementId: string, + newData: any, + options: { action?: string } = {} +) { + const action = options.action || "write"; // Default action + + if (window.playhtmlAuth?.identity) { + try { + // Check permissions first + const hasPermission = await checkPermission( + elementId, + action, + window.playhtmlAuth.identity + ); + + if (!hasPermission) { + throw new Error("Permission denied"); + } + + // Automatically sign the data change + const signedChange = await createSignedAction( + action, + elementId, + newData, + window.playhtmlAuth.identity + ); + + // Apply to CRDT with temporary auth data + applyDataChange({ + type: "crdt_update", + elementId, + data: { + ...newData, + _temp_auth: signedChange, + }, + }); + } catch (error) { + console.error("Failed to perform action:", error); + showUserFeedback(`Unable to ${action}: ${error.message}`); + } + } else { + // No identity - check if action is allowed for "everyone" + const hasPermission = await checkPermission(elementId, action); + + if (hasPermission) { + // Apply change without authentication + applyDataChange({ + type: "crdt_update", + elementId, + data: newData, + }); + } else { + showUserFeedback("Please connect your PlayHTML identity to interact"); + } + } +} + +async function createSignedAction( + action: string, + elementId: string, + data: any, + identity: PlayHTMLIdentity +): Promise { + const payload = { + action, + elementId, + data, + timestamp: Date.now(), + nonce: crypto.randomUUID(), + }; + + const message = JSON.stringify(payload); + const signature = await signMessage(message, identity.privateKey); + + return { + ...payload, + signature, + publicKey: identity.publicKey, + }; +} +``` + +**Server-Side Session & Renewal Management**: + +```typescript +// Enhanced PartyKit server with session renewal support +export default class SessionValidatedPlayHTML implements PartyKitServer { + private validSessions = new Map(); + private pendingChallenges = new Map(); + private usedNonces = new Set(); + + // Session establishment with renewal support + async handleSessionEstablishment(request: Request): Response { + const { challenge, signature, publicKey } = await request.json(); + + // Validate challenge exists and signature is correct (ONLY crypto verification) + const storedChallenge = this.pendingChallenges.get(challenge.challenge); + if (!storedChallenge || storedChallenge.expiresAt < Date.now()) { + return new Response('Invalid or expired challenge', { status: 400 }); + } + + const isValidSignature = await verifySignature( + JSON.stringify(challenge), + signature, + publicKey + ); + + if (!isValidSignature) { + return new Response('Invalid signature', { status: 400 }); + } + + // Check if this is a renewal (user already has active session) + const existingSession = this.findExistingSession(publicKey); + + if (existingSession) { + // Extend existing session instead of creating new one + existingSession.expiresAt = Date.now() + (24 * 60 * 60 * 1000); + + console.log(`🔄 Renewed session for ${publicKey}`); + + return new Response(JSON.stringify({ + sessionId: existingSession.sessionId, // Keep same session ID + publicKey: existingSession.publicKey, + expiresAt: existingSession.expiresAt, + renewed: true + })); + } else { + // Create new session + const session: ValidatedSession = { + sessionId: crypto.randomUUID(), + publicKey, + domain: challenge.domain, + establishedAt: Date.now(), + expiresAt: Date.now() + (24 * 60 * 60 * 1000), // 24 hours + permissions: await this.getUserPermissions(publicKey, challenge.domain) + }; + + this.validSessions.set(session.sessionId, session); + this.pendingChallenges.delete(challenge.challenge); + + console.log(`✅ New session established for ${publicKey}`); + + return new Response(JSON.stringify({ + sessionId: session.sessionId, + publicKey: session.publicKey, + expiresAt: session.expiresAt, + renewed: false + })); + } + } + + private findExistingSession(publicKey: string): ValidatedSession | null { + for (const session of this.validSessions.values()) { + if (session.publicKey === publicKey && session.expiresAt > Date.now()) { + return session; + } + } + return null; + } + + // Message handling with simplified session validation + async onMessage(message: string, sender: Party.Connection) { + try { + const parsed = JSON.parse(message); + + if (parsed.type === "session_action") { + await this.handleSessionAction(parsed.action, sender); + } else { + await this.handleAnonymousAction(parsed, sender); + } + } catch (error) { + console.error("Message processing error:", error); + sender.send(JSON.stringify({ + type: "action_rejected", + reason: error.message + })); + } + } + + private async handleSessionAction(action: SimpleAction, sender: Party.Connection) { + // 1. Validate session exists and is not expired + const session = this.validSessions.get(action.sessionId); + if (!session || session.expiresAt < Date.now()) { + throw new Error('Invalid or expired session'); + } + + // 2. Basic action validation (no signature verification needed) + if (!this.isValidAction(action)) { + throw new Error('Invalid action format'); + } + + // 3. Check nonce uniqueness (prevent replay attacks) + const nonceKey = `${action.sessionId}:${action.nonce}`; + if (this.usedNonces.has(nonceKey)) { + throw new Error('Duplicate action detected'); + } + + // 4. Check user permissions + const hasPermission = await this.checkUserPermission( + session.publicKey, + action.elementId, + action.action + ); + + if (!hasPermission) { + throw new Error('Permission denied'); + } + + // 5. Apply action to CRDT + await this.executeAction(action); + + // 6. Track nonce and broadcast to other clients + this.usedNonces.add(nonceKey); + this.room.broadcast(JSON.stringify({ + type: 'action_applied', + action: { + elementId: action.elementId, + data: action.data, + appliedBy: session.publicKey, + appliedAt: Date.now() + } + })); + + // Clean up old nonces (5 minute window) + setTimeout(() => this.usedNonces.delete(nonceKey), 5 * 60 * 1000); + } + + // Periodic cleanup of expired sessions + private cleanupExpiredSessions(): void { + const now = Date.now(); + for (const [sessionId, session] of this.validSessions.entries()) { + if (session.expiresAt < now) { + this.validSessions.delete(sessionId); + console.log(`🗑️ Cleaned up expired session: ${sessionId}`); + } + } + } + + constructor() { + // Run cleanup every hour + setInterval(() => this.cleanupExpiredSessions(), 60 * 60 * 1000); + } +} + + private async checkUserPermission( + domain: string, + publicKey: string, + elementId: string, + action: string + ): Promise { + const roles = this.globalRoles.get(domain); + if (!roles) return false; + + // Check if user has required role for action + const userRoles = await this.getUserRoles(domain, publicKey); + const elementConfig = this.elementConfigs.get(elementId); + + if (!elementConfig) return false; + + const requiredRole = elementConfig.permissions[action]; + if (!requiredRole || requiredRole === "everyone") return true; + + return userRoles.includes(requiredRole); + } +} +``` + +### 7. React Integration + +**Advanced Security: Cryptographic Element Verification** + +For elements requiring maximum security, PlayHTML will support cryptographic signatures that prevent HTML configuration tampering: + +```html + +
+``` + +**How Element Signatures Work:** + +1. **Configuration Signing**: Website owners create a canonical message containing element ID, domain, owner, and permissions, then sign it with their private key +2. **Tamper Detection**: PlayHTML verifies the signature matches the current HTML attributes before processing any actions +3. **Authentication Chain**: Combined with session authentication, this creates end-to-end cryptographic verification + +**Security Benefits:** + +- **Prevents HTML tampering**: Can't modify `playhtml-owner` or `playhtml-permissions` without invalidating signature +- **Cryptographic proof**: Mathematical guarantee that configuration comes from legitimate owner +- **Decentralized**: No server-side configuration registry required + +**Developer Experience Improvements for Signature Generation:** + +- **React Development Hook**: `usePlayHTMLDev()` hook for automatic signature generation during development + + ```typescript + const { signElementConfig } = usePlayHTMLDev(); + // Auto-generates signatures for development with console output + ``` + +- **Build Tool Integration**: Webpack/Vite plugins and CLI tools for automated signature injection + + ```bash + npx playhtml sign-elements --input src/ --output dist/ + # Scans components and auto-injects signatures during build + ``` + +- **Browser Extension Dev Mode**: PlayHTML extension automatically detects development domains and generates signatures + + ```typescript + // Extension auto-signs elements on localhost, *.dev, staging domains + // Shows signature status in developer tools + ``` + +- **Zero-Config Experience**: Progressive complexity from automatic dev signatures to production signing workflows + ```typescript + + // Signature auto-generated in development, build process handles production + ``` + +**Implementation Status:** Element signatures are planned for Phase 3 after core session authentication is established. They provide optional enhanced security for sensitive elements while maintaining PlayHTML's ease-of-use for creative applications. + +```tsx +// usePlayHTMLPermissions hook +function usePlayHTMLPermissions(elementId: string) { + const [permissions, setPermissions] = useState({ + canRead: true, + canWrite: true, + canDelete: false, + canModerate: false, + }); + + useEffect(() => { + const updatePermissions = async () => { + const identity = window.playhtmlAuth?.identity; + + const newPermissions = { + canRead: await checkPermission(elementId, "read", identity), + canWrite: await checkPermission(elementId, "write", identity), + canDelete: await checkPermission(elementId, "delete", identity), + canModerate: await checkPermission(elementId, "moderate", identity), + }; + + setPermissions(newPermissions); + }; + + updatePermissions(); + + // Listen for auth changes + window.addEventListener("playhtmlAuthReady", updatePermissions); + return () => + window.removeEventListener("playhtmlAuthReady", updatePermissions); + }, [elementId]); + + return permissions; +} + +// Example usage in components +function GuestbookComponent({ id }: { id: string }) { + const { data, setData } = usePlayHTML(id); + const permissions = usePlayHTMLPermissions(id); + + const addEntry = async (text: string) => { + try { + await setData( + id, + { + entries: [ + ...data.entries, + { id: crypto.randomUUID(), text, timestamp: Date.now() }, + ], + }, + { action: "write" } + ); + } catch (error) { + alert("Unable to add entry - permission denied"); + } + }; + + const deleteEntry = async (entryId: string) => { + try { + await setData( + id, + { + entries: data.entries.filter((e) => e.id !== entryId), + }, + { action: "delete" } + ); + } catch (error) { + alert("Unable to delete entry - permission denied"); + } + }; + + return ( +
+ {data.entries?.map((entry) => ( +
+ {entry.text} + {permissions.canDelete && ( + + )} +
+ ))} + + {permissions.canWrite ? ( + + ) : ( +

You need permission to add entries to this guestbook.

+ )} +
+ ); +} +``` + +## Implementation Phases + +### **Phase 1: Core Permission System (Immediate)** + +1. **Clean HTML API**: Simple `playhtml-permissions="delete:owner"` syntax +2. **JavaScript role configuration**: Global role definitions in `initPlayHTML()` +3. **Automatic extension signing**: Domain-based auto-signing without user prompts +4. **Temp auth validation**: Server validates `_temp_auth` data and strips before broadcast +5. **Basic permission checking**: Owner, role-based, and "everyone" permission model + +### **Phase 2: Enhanced Permissions (Next)** + +1. **Custom permission conditions**: JavaScript functions for time-based, visit-based, etc. +2. **React permission hooks**: Easy access to permission state in components +3. **Advanced role assignments**: Conditional roles based on user behavior +4. **Audit logging**: Track all permission checks and violations +5. **Permission inheritance**: Element-level permission inheritance + +### **Phase 3: Advanced Features (Future)** + +1. **UCAN-style capabilities**: Delegatable, time-limited permission tokens +2. **Cross-domain permissions**: Trusted domain verification via DNS +3. **Admin dashboard**: GUI for permission management at `admin.playhtml.fun` +4. **Enterprise integration**: OAuth/SAML support for organizational identity + +### 8. Session Management & User Experience + +**Automatic Session Renewal Benefits:** + +**✅ Seamless User Experience**: Users authenticate once and then interact freely for 24 hours without interruption + +**✅ Transparent Renewal**: Sessions automatically renew in the background when 1 hour remains, invisible to users + +**✅ Graceful Failure**: If renewal fails, users get clear instructions to refresh rather than cryptic errors + +**✅ Multi-Tab Support**: Session renewal works across browser tabs since sessions are identified by public key + +**✅ Development Friendly**: Optional session status component for debugging and monitoring + +**Session Lifecycle:** + +1. **Initial Authentication**: User signs challenge on first interaction +2. **Active Period**: 23 hours of seamless interaction +3. **Auto-Renewal**: Transparent renewal when 1 hour remains +4. **Expiration Handling**: Clear messaging and refresh instruction if renewal fails + +**Events for UI Integration:** + +```typescript +// Listen for session events in your application +window.addEventListener("playhtmlSessionEstablished", (e) => { + console.log("✅ Session established:", e.detail.sessionId); +}); + +window.addEventListener("playhtmlSessionRenewed", (e) => { + console.log("🔄 Session renewed:", e.detail.sessionId); +}); + +window.addEventListener("playhtmlSessionExpired", (e) => { + console.log("❌ Session expired:", e.detail.error); + showRefreshPrompt(); +}); +``` + +## Security Benefits of Session-Only Authentication + +### **What Session Authentication Provides:** + +**✅ Identity Verification**: Users must cryptographically prove they control their private key during session establishment + +**✅ Prevents Identity Spoofing**: Can't fake being another user without their actual private key + +**✅ Session Management**: Controlled expiry, automatic renewal, domain binding, and revocation capabilities + +**✅ Replay Protection**: Nonce tracking prevents duplicate actions within session lifetime + +**✅ Performance**: One-time crypto operation per page load instead of per action + +**✅ Multi-Tab Consistency**: Session renewal works across browser tabs for seamless experience + +### **Security Comparison:** + +| Attack Vector | Session Auth Protection | Per-Action Signing | Anonymous Mode | +| ----------------------- | -------------------------------------- | --------------------------------------- | --------------------------------- | +| **Identity Spoofing** | ✅ Cryptographically prevented | ✅ Cryptographically prevented | ❌ No identity verification | +| **Action Tampering** | ✅ Server validates all actions | ✅ Each action cryptographically signed | ⚠️ Basic permission checking only | +| **Replay Attacks** | ✅ Nonce tracking + session expiry | ✅ Individual action nonces | ❌ No replay protection | +| **Client Modification** | ✅ Actions validated server-side | ✅ Signatures validated server-side | ⚠️ Relies on client-side checking | +| **Session Hijacking** | ✅ Domain binding + auto-renewal | ✅ No session concept | N/A | +| **Performance Impact** | ✅ Fast (one-time auth + auto-renewal) | ❌ Slow (crypto per action) | ✅ Fastest (no crypto) | +| **User Experience** | ✅ Seamless after initial auth | ❌ Approval prompts per action | ✅ No auth friction | +| **Multi-Tab Support** | ✅ Works across tabs | ⚠️ Each tab needs individual setup | ✅ No setup needed | + +### **When Session Auth is Sufficient:** + +- ✅ Creative collaboration (art, writing, games) +- ✅ Social interactions (guestbooks, comments, reactions) +- ✅ Community moderation (content curation, basic access control) +- ✅ Educational tools (shared whiteboards, polls) +- ✅ Most PlayHTML use cases + +### **When Per-Action Signing Might Be Needed:** + +- ⚠️ Financial transactions or payment processing +- ⚠️ Legal document modifications +- ⚠️ Medical record updates +- ⚠️ High-stakes administrative changes + +For PlayHTML's creative and social focus, **session authentication with automatic renewal provides the optimal balance** of strong security with excellent user experience. + +## Security Model & Limitations + +### **Trust Model** + +PlayHTML uses a **"session-based cryptographic authentication"** model that balances security with usability: + +- **Session Establishment**: Users prove identity through challenge-response cryptography +- **Action Validation**: Server validates each action against established session permissions +- **Optimistic Updates**: Actions appear immediately but can be rolled back if server rejects +- **Domain Binding**: Sessions are tied to specific domains for additional security + +### **Known Security Limitations** + +**1. Session-Based Trust Window** + +- **Risk**: All actions within a valid session are trusted without individual signing +- **Impact**: Compromised client could perform multiple actions before session expires +- **Mitigation**: Short session lifetime (24 hours), nonce tracking, permission validation +- **Acceptable for**: Creative, social platforms where consequences are reversible + +**2. Browser Extension Dependency** + +- **Risk**: Users rely on browser extension for identity and session establishment +- **Impact**: Malicious extensions could auto-establish sessions for unauthorized domains +- **Mitigation**: Extension permission system limits scope to trusted domains +- **User responsibility**: Only install PlayHTML extensions from trusted sources + +**3. Optimistic Update Window** + +- **Risk**: Invalid actions appear briefly for the user before server validation +- **Impact**: Temporary "split brain" state until server responds +- **Mitigation**: Only affects the user attempting invalid actions +- **Behavior**: Invalid changes disappear on server response or page refresh + +**4. Element Configuration Trust** + +- **Risk**: HTML-based permissions can be modified in browser dev tools +- **Impact**: Users could claim false ownership or permissions locally +- **Mitigation**: Server validates against authoritative configuration (Element Signatures in Phase 3) +- **Current state**: Relies on social trust and basic validation + +### **Recommended Use Cases** + +**✅ Good fit:** + +- Creative collaboration (art, writing, games) +- Social interactions (guestbooks, comments, reactions) +- Community moderation (content curation, basic access control) +- Educational tools (shared whiteboards, polls) + +**❌ Not recommended:** + +- Financial transactions or payment processing +- Medical records or sensitive personal information +- Legal documents or contracts +- High-stakes data where corruption has serious consequences + +## Open Questions for Discussion + +1. **Cross-domain role inheritance**: Should roles defined on one domain work on related subdomains? + +2. **Permission condition caching**: How long should we cache results of expensive permission conditions (like geolocation)? + +3. **Role delegation**: Should users be able to delegate their roles to others temporarily? + +4. **Permission debugging**: What developer tools should we provide for debugging permission issues? + +5. **Rate limiting**: Should permission checks themselves be rate limited to prevent abuse? + +6. **Graceful degradation**: How should elements behave when session establishment fails due to network issues? + +7. **Element-level vs global roles**: Should we support element-specific role overrides in addition to global roles? + +8. **Session renewal notifications**: Should we show users when their session is being renewed, or keep it completely transparent? + +9. **Multi-device sessions**: Should a user be able to have multiple active sessions across different devices simultaneously? + +10. **Session analytics**: What session-related metrics should we track for debugging and optimization? + +11. **Migration strategy**: How do we handle permission changes on existing elements with historical data? + +12. **Offline session handling**: How should the system behave when users go offline during an active session? diff --git a/packages/common/src/auth.ts b/packages/common/src/auth.ts new file mode 100644 index 0000000..ef7c4cb --- /dev/null +++ b/packages/common/src/auth.ts @@ -0,0 +1,120 @@ +// PlayHTML Authentication & Identity Types +// Based on the design in docs/auth.md + +export interface PlayHTMLIdentity { + privateKey: string; // Private key for signing (base64 encoded) + publicKey: string; // Public identity (base64 encoded) + displayName?: string; // Human-readable name + avatar?: string; // Custom avatar/cursor style + createdAt: number; // Identity creation timestamp + version: number; // For future migrations + verifiedDomains?: string[]; // DNS-verified domain aliases + algorithm?: string; // Crypto algorithm used (Ed25519 or RSA-PSS) +} + +export interface PlayHTMLAuth { + identity?: PlayHTMLIdentity; + isAuthenticated: boolean; + sign: (message: string) => Promise; + verify: (message: string, signature: string, publicKey: string) => Promise; +} + +// Simplified permission model +export interface GlobalRoleDefinition { + [roleName: string]: string[] | { condition: string }; // Public keys array or conditional assignment +} + +export interface PermissionConfig { + [action: string]: string; // action -> required role mapping +} + +export interface GlobalPlayHTMLConfig { + roles?: GlobalRoleDefinition; + permissionConditions?: Record; +} + +export interface PermissionContext { + user?: PlayHTMLIdentity; + element: HTMLElement; + domain: string; + visitCount: number; + timeOfDay: number; + userLocation?: { lat: number; lng: number }; + siteLocation?: { lat: number; lng: number }; + customData: Record; // Site-specific context +} + +export interface SignedAction { + action: string; // What action is being performed + elementId: string; // Target element + data: any; // Action payload + timestamp: number; // When action was created + nonce: string; // Unique nonce to prevent replay + signature: string; // Ed25519 signature + publicKey: string; // Actor's public key +} + +export interface AuthenticatedMessage { + type: string; + data: any; + timestamp: number; + nonce: string; + signature: string; + publicKey: string; +} + +export interface UserSession { + publicKey: string; + connectedAt: number; + lastSeen: number; + verifiedDomains: string[]; +} + +// Session-based authentication types +export interface SessionChallenge { + challenge: string; + domain: string; + timestamp: number; + expiresAt: number; +} + +export interface ValidatedSession { + sessionId: string; + publicKey: string; + domain: string; + establishedAt: number; + expiresAt: number; + permissions?: any; // Cached permissions for this session +} + +export interface SessionAction { + sessionId: string; + action: string; + elementId: string; + data: any; + timestamp: number; + nonce: string; +} + +export interface SessionEstablishmentRequest { + challenge: SessionChallenge; + signature: string; + publicKey: string; +} + +export interface SessionEstablishmentResponse { + sessionId: string; + publicKey: string; + expiresAt: number; + renewed: boolean; +} + +// Permission function type for extensible custom logic +export type PermissionFunction = (context: PermissionContext) => Promise; + +// Declare global auth object for extension integration +declare global { + interface Window { + playhtmlAuth?: PlayHTMLAuth; + } +} \ No newline at end of file diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index 9c7eeb8..dc6fbb7 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -735,6 +735,9 @@ function constructInitialState(element: HTMLElement | Text): ElementState { return state; } +// Export authentication types and interfaces +export * from "./auth"; + function updateElementFromState( element: HTMLElement | Text, newState: ElementState diff --git a/packages/playhtml/src/auth.ts b/packages/playhtml/src/auth.ts new file mode 100644 index 0000000..9a5452a --- /dev/null +++ b/packages/playhtml/src/auth.ts @@ -0,0 +1,516 @@ +// PlayHTML Authentication System +// Implements identity management and global auth object + +import type { + PlayHTMLIdentity, + PlayHTMLAuth, + PermissionContext, + GlobalRoleDefinition, + PermissionConfig, + PermissionFunction, + SessionChallenge, + ValidatedSession, + SessionAction, +} from "@playhtml/common"; +import { + generateIdentity, + signMessage, + verifySignature, + importIdentity, + exportIdentity, +} from "./crypto"; + +// Re-export crypto functions for external use +export { + generateIdentity, + signMessage, + verifySignature, + importIdentity, + exportIdentity, + createSignedAction, + createAuthenticatedMessage, +} from "./crypto"; + +// Global auth state +let currentIdentity: PlayHTMLIdentity | undefined; +let authReadyCallbacks: Array<(auth: PlayHTMLAuth) => void> = []; + +// Global permission configuration +let globalRoles: GlobalRoleDefinition = {}; +let permissionConditions: Record = {}; + +// Session management state +let currentSession: ValidatedSession | null = null; +let sessionRenewalTimer: number | null = null; + +// No built-in permission functions - users define their own custom functions + +// Create PlayHTMLAuth object from identity +function createAuthFromIdentity(identity?: PlayHTMLIdentity): PlayHTMLAuth { + return { + identity, + isAuthenticated: !!identity, + sign: async (message: string) => { + if (!identity) throw new Error("No identity available for signing"); + return await signMessage( + message, + identity.privateKey, + identity.algorithm + ); + }, + verify: async ( + message: string, + signature: string, + publicKey: string, + algorithm?: string + ) => { + return await verifySignature(message, signature, publicKey, algorithm); + }, + }; +} + +// Initialize authentication system +export async function initializeAuth(): Promise { + // Check if extension has already injected auth + if (window.playhtmlAuth) { + console.log("PlayHTML Auth: Using extension-provided identity"); + currentIdentity = window.playhtmlAuth.identity; + return window.playhtmlAuth; + } + + // Check for stored identity in localStorage (fallback) + const storedIdentity = getStoredIdentity(); + if (storedIdentity) { + console.log("PlayHTML Auth: Using stored identity"); + currentIdentity = storedIdentity; + const auth = createAuthFromIdentity(storedIdentity); + injectGlobalAuth(auth); + return auth; + } + + // No identity found - running in read-only mode + console.log("PlayHTML Auth: No identity found, running in read-only mode"); + const auth = createAuthFromIdentity(); + injectGlobalAuth(auth); + return auth; +} + +// Get stored identity from localStorage +function getStoredIdentity(): PlayHTMLIdentity | undefined { + try { + const stored = localStorage.getItem("playhtml_identity"); + if (stored) { + return importIdentity(stored); + } + } catch (error) { + console.error("Failed to load stored identity:", error); + } + return undefined; +} + +// Store identity in localStorage +export function storeIdentity(identity: PlayHTMLIdentity): void { + try { + const exported = exportIdentity(identity); + localStorage.setItem("playhtml_identity", exported); + currentIdentity = identity; + + // Update global auth object + const auth = createAuthFromIdentity(identity); + injectGlobalAuth(auth); + + console.log("Identity stored successfully"); + } catch (error) { + console.error("Failed to store identity:", error); + throw error; + } +} + +// Clear stored identity +export function clearIdentity(): void { + localStorage.removeItem("playhtml_identity"); + currentIdentity = undefined; + + // Update global auth object + const auth = createAuthFromIdentity(); + injectGlobalAuth(auth); + + console.log("Identity cleared"); +} + +// Inject auth object into global scope +function injectGlobalAuth(auth: PlayHTMLAuth): void { + window.playhtmlAuth = auth; + + // Dispatch event for PlayHTML to react to auth changes + window.dispatchEvent(new CustomEvent("playhtmlAuthReady", { detail: auth })); + + // Notify any waiting callbacks + authReadyCallbacks.forEach((callback) => callback(auth)); + authReadyCallbacks = []; +} + +// Wait for auth to be ready +export function onAuthReady(callback: (auth: PlayHTMLAuth) => void): void { + if (window.playhtmlAuth) { + callback(window.playhtmlAuth); + } else { + authReadyCallbacks.push(callback); + } +} + +// Generate and store a new identity +export async function createNewIdentity( + displayName?: string +): Promise { + const identity = await generateIdentity(displayName); + storeIdentity(identity); + return identity; +} + +// Check if user has permission for a specific action using new simplified model +export async function checkPermission( + elementId: string, + action: string, + userIdentity?: PlayHTMLIdentity +): Promise { + const element = document.getElementById(elementId); + if (!element) return false; + + const permissionsAttr = element.getAttribute("playhtml-permissions"); + if (!permissionsAttr) { + // No restrictions = everyone can do everything + return true; + } + + // Parse permissions like "write:contributors, delete:moderators" + const permissions = parsePermissions(permissionsAttr); + const requiredRole = permissions[action]; + + if (!requiredRole) { + // Action not restricted + return true; + } + + if (requiredRole === "everyone") { + return true; + } + + if (!userIdentity) { + // No identity, can only access "everyone" permissions + return false; + } + + // Check if user has required role + const userRoles = await getUserRolesForElement(elementId, userIdentity); + return userRoles.includes(requiredRole); +} + +// Helper functions +function getVisitCount(domain: string): number { + try { + const stored = localStorage.getItem(`playhtml_visits_${domain}`); + const count = stored ? parseInt(stored) : 0; + localStorage.setItem(`playhtml_visits_${domain}`, (count + 1).toString()); + return count + 1; + } catch { + return 0; + } +} + +function getElementCustomData(element: HTMLElement): Record { + try { + const customData = element.getAttribute("playhtml-custom-data"); + return customData ? JSON.parse(customData) : {}; + } catch { + return {}; + } +} + +// Configure global roles and permission conditions +export function configureGlobalPermissions( + roles: GlobalRoleDefinition, + conditions: Record = {} +): void { + globalRoles = roles; + permissionConditions = conditions; + console.log("[PLAYHTML AUTH]: Configured global permissions", { + roles, + conditions, + }); +} + +// Parse simple permission string like "write:owner, delete:moderators" +export function parsePermissions(permissionsAttr: string): PermissionConfig { + const permissions: PermissionConfig = {}; + + permissionsAttr.split(",").forEach((perm) => { + const [action, role] = perm.trim().split(":"); + if (action && role) { + permissions[action.trim()] = role.trim(); + } + }); + + return permissions; +} + +// Get user's roles based on global configuration +export async function getUserRolesForElement( + elementId: string, + userIdentity?: PlayHTMLIdentity +): Promise { + const roles: string[] = []; + + if (!userIdentity) { + return ["everyone"]; // Default role for unauthenticated users + } + + const context = await buildPermissionContext(userIdentity, elementId); + + for (const [roleName, roleDefinition] of Object.entries(globalRoles)) { + if (Array.isArray(roleDefinition)) { + // Explicit public key list + if (roleDefinition.includes(userIdentity.publicKey)) { + roles.push(roleName); + } + } else if (roleDefinition.condition) { + // Conditional role assignment + const conditionFn = permissionConditions[roleDefinition.condition]; + if (conditionFn) { + try { + if (await conditionFn(context)) { + roles.push(roleName); + } + } catch (error) { + console.error( + `Failed to evaluate role condition '${roleDefinition.condition}':`, + error + ); + } + } else { + console.warn( + `Permission condition '${roleDefinition.condition}' not found in custom conditions` + ); + } + } + } + + return roles.length > 0 ? roles : ["visitor"]; // Default to visitor if no roles matched +} + +// Build permission context for evaluation +async function buildPermissionContext( + userIdentity: PlayHTMLIdentity | undefined, + elementId: string +): Promise { + const element = document.getElementById(elementId); + if (!element) { + throw new Error(`Element ${elementId} not found`); + } + + return { + user: userIdentity, + element, + domain: window.location.hostname, + visitCount: getVisitCount(window.location.hostname), + timeOfDay: new Date().getHours(), + customData: getElementCustomData(element), + }; +} + +// Get current authenticated identity +export function getCurrentIdentity(): PlayHTMLIdentity | undefined { + return currentIdentity || window.playhtmlAuth?.identity; +} + +// Session Management Functions + +// Generate a challenge for session establishment +export function generateSessionChallenge(): SessionChallenge { + const challenge = crypto.randomUUID(); + const domain = window.location.hostname; + const timestamp = Date.now(); + const expiresAt = timestamp + 5 * 60 * 1000; // 5 minutes + + return { + challenge, + domain, + timestamp, + expiresAt, + }; +} + +// Internal session establishment with WebSocket (called from main.ts) +export async function establishSessionWithWS( + identity: PlayHTMLIdentity, + ws: WebSocket +): Promise { + return new Promise((resolve, reject) => { + const challenge = generateSessionChallenge(); + + // Sign the challenge to prove identity ownership + signMessage( + JSON.stringify(challenge), + identity.privateKey, + identity.algorithm + ) + .then((signature) => { + const request = { + type: "session_establish", + challenge, + signature, + publicKey: identity.publicKey, + algorithm: identity.algorithm || "Ed25519", + }; + + // Set up one-time listener for session response + const handleMessage = (event: MessageEvent) => { + try { + const data = JSON.parse(event.data); + if ( + data.type === "session_established" || + data.type === "session_renewed" + ) { + ws.removeEventListener("message", handleMessage); + + const session: ValidatedSession = { + sessionId: data.sessionId, + publicKey: data.publicKey, + domain: window.location.hostname, + establishedAt: Date.now(), + expiresAt: data.expiresAt, + }; + + // Store session and set up auto-renewal + currentSession = session; + scheduleSessionRenewal(session); + + // Dispatch session events + const eventType = + data.type === "session_renewed" + ? "playhtmlSessionRenewed" + : "playhtmlSessionEstablished"; + window.dispatchEvent( + new CustomEvent(eventType, { detail: session }) + ); + + console.log( + `✅ Session ${ + data.type === "session_renewed" ? "renewed" : "established" + }: ${session.sessionId}` + ); + + resolve(session); + } else if (data.type === "session_error") { + ws.removeEventListener("message", handleMessage); + reject(new Error(data.message || "Session establishment failed")); + } + } catch (error) { + // Ignore non-JSON messages + } + }; + + ws.addEventListener("message", handleMessage); + + // Send session establishment request + ws.send(JSON.stringify(request)); + + // Set timeout for session establishment + setTimeout(() => { + ws.removeEventListener("message", handleMessage); + reject(new Error("Session establishment timeout")); + }, 10000); // 10 second timeout + }) + .catch(reject); + }); +} + +// Public session establishment function (delegates to main.ts) +export async function establishSession( + identity?: PlayHTMLIdentity +): Promise { + const actualIdentity = identity || getCurrentIdentity(); + if (!actualIdentity) { + throw new Error("No identity available for session establishment"); + } + + // This will be called by the main module with the actual WebSocket + throw new Error( + "Session establishment must be called through PlayHTML main module" + ); +} + +// Schedule automatic session renewal +function scheduleSessionRenewal(session: ValidatedSession): void { + if (sessionRenewalTimer) { + clearTimeout(sessionRenewalTimer); + } + + // Renew when 1 hour remains (23 hours after establishment) + const renewalTime = session.expiresAt - 60 * 60 * 1000; // 1 hour before expiry + const timeUntilRenewal = renewalTime - Date.now(); + + if (timeUntilRenewal > 0) { + sessionRenewalTimer = window.setTimeout(async () => { + try { + const identity = getCurrentIdentity(); + if (identity) { + await establishSession(identity); // This will extend the existing session + } + } catch (error) { + console.error("Session renewal failed:", error); + window.dispatchEvent( + new CustomEvent("playhtmlSessionExpired", { + detail: { + error: error instanceof Error ? error.message : String(error), + }, + }) + ); + } + }, timeUntilRenewal); + + console.log( + `🔄 Session renewal scheduled in ${Math.round( + timeUntilRenewal / 1000 / 60 + )} minutes` + ); + } +} + +// Get current session +export function getCurrentSession(): ValidatedSession | null { + return currentSession; +} + +// Create a session action (replaces signed actions for better performance) +export function createSessionAction( + action: string, + elementId: string, + data: any +): SessionAction { + if (!currentSession) { + throw new Error("No active session for creating actions"); + } + + return { + sessionId: currentSession.sessionId, + action, + elementId, + data, + timestamp: Date.now(), + nonce: crypto.randomUUID(), + }; +} + +// Initialize session authentication on page load (called from main.ts with WebSocket) +export async function initializeSessionAuth(ws?: WebSocket): Promise { + const identity = getCurrentIdentity(); + if (identity && ws) { + try { + await establishSessionWithWS(identity, ws); + } catch (error) { + console.warn("Failed to establish session on page load:", error); + // Continue without session - user can still interact with public elements + } + } +} diff --git a/packages/playhtml/src/crypto.ts b/packages/playhtml/src/crypto.ts new file mode 100644 index 0000000..4cb806b --- /dev/null +++ b/packages/playhtml/src/crypto.ts @@ -0,0 +1,258 @@ +// PlayHTML Cryptographic Utilities +// Implements Ed25519 key generation, signing, and verification + +import type { PlayHTMLIdentity, SignedAction, AuthenticatedMessage } from "@playhtml/common"; + +// Utility functions for base64 encoding/decoding +export function arrayBufferToBase64(buffer: ArrayBuffer): string { + const bytes = new Uint8Array(buffer); + let binary = ''; + for (let i = 0; i < bytes.byteLength; i++) { + binary += String.fromCharCode(bytes[i]); + } + return btoa(binary); +} + +export function base64ToArrayBuffer(base64: string): ArrayBuffer { + const binary = atob(base64); + const bytes = new Uint8Array(binary.length); + for (let i = 0; i < binary.length; i++) { + bytes[i] = binary.charCodeAt(i); + } + return bytes.buffer; +} + +// Generate a new PlayHTML identity with RSA-PSS key pair (better browser support than Ed25519) +export async function generateIdentity(displayName?: string): Promise { + try { + // Try Ed25519 first (preferred), fallback to RSA-PSS + let keyPair; + let algorithm; + + try { + // Try Ed25519 first + keyPair = await crypto.subtle.generateKey( + { name: "Ed25519" }, + true, + ["sign", "verify"] + ); + algorithm = "Ed25519"; + } catch { + // Fallback to RSA-PSS for better browser support + keyPair = await crypto.subtle.generateKey( + { + name: "RSA-PSS", + modulusLength: 2048, + publicExponent: new Uint8Array([1, 0, 1]), + hash: "SHA-256", + }, + true, + ["sign", "verify"] + ); + algorithm = "RSA-PSS"; + } + + // Export keys + const privateKeyBuffer = await crypto.subtle.exportKey("pkcs8", keyPair.privateKey); + const publicKeyBuffer = await crypto.subtle.exportKey("spki", keyPair.publicKey); + + return { + privateKey: arrayBufferToBase64(privateKeyBuffer), + publicKey: arrayBufferToBase64(publicKeyBuffer), + displayName, + createdAt: Date.now(), + version: 1, + algorithm, // Store which algorithm was used + }; + } catch (error) { + console.error("Failed to generate identity:", error); + throw new Error("Identity generation failed. This browser may not support the required crypto APIs."); + } +} + +// Import private key from base64 string for signing +export async function importPrivateKey(privateKeyBase64: string, algorithm: string = "Ed25519"): Promise { + const privateKeyBuffer = base64ToArrayBuffer(privateKeyBase64); + + const keyAlgorithm = algorithm === "RSA-PSS" + ? { name: "RSA-PSS", hash: "SHA-256" } + : { name: "Ed25519" }; + + return await crypto.subtle.importKey( + "pkcs8", + privateKeyBuffer, + keyAlgorithm, + false, // not extractable + ["sign"] + ); +} + +// Import public key from base64 string for verification +export async function importPublicKey(publicKeyBase64: string, algorithm: string = "Ed25519"): Promise { + const publicKeyBuffer = base64ToArrayBuffer(publicKeyBase64); + + const keyAlgorithm = algorithm === "RSA-PSS" + ? { name: "RSA-PSS", hash: "SHA-256" } + : { name: "Ed25519" }; + + return await crypto.subtle.importKey( + "spki", + publicKeyBuffer, + keyAlgorithm, + false, // not extractable + ["verify"] + ); +} + +// Sign a message using a private key +export async function signMessage(message: string, privateKeyBase64: string, algorithm: string = "Ed25519"): Promise { + try { + const privateKey = await importPrivateKey(privateKeyBase64, algorithm); + const messageBuffer = new TextEncoder().encode(message); + + const signAlgorithm = algorithm === "RSA-PSS" + ? { name: "RSA-PSS", saltLength: 32 } + : "Ed25519"; + + const signature = await crypto.subtle.sign( + signAlgorithm, + privateKey, + messageBuffer + ); + + return arrayBufferToBase64(signature); + } catch (error) { + console.error("Failed to sign message:", error); + throw new Error("Message signing failed"); + } +} + +// Verify a signature using a public key +export async function verifySignature( + message: string, + signatureBase64: string, + publicKeyBase64: string, + algorithm: string = "Ed25519" +): Promise { + try { + const publicKey = await importPublicKey(publicKeyBase64, algorithm); + const messageBuffer = new TextEncoder().encode(message); + const signatureBuffer = base64ToArrayBuffer(signatureBase64); + + const verifyAlgorithm = algorithm === "RSA-PSS" + ? { name: "RSA-PSS", saltLength: 32 } + : "Ed25519"; + + return await crypto.subtle.verify( + verifyAlgorithm, + publicKey, + signatureBuffer, + messageBuffer + ); + } catch (error) { + console.error("Failed to verify signature:", error); + return false; + } +} + +// Create a signed action for authenticated operations +export async function createSignedAction( + action: string, + elementId: string, + data: any, + identity: PlayHTMLIdentity +): Promise { + const timestamp = Date.now(); + const nonce = crypto.randomUUID(); + + const payload = { action, elementId, data, timestamp, nonce }; + const message = JSON.stringify(payload); + const signature = await signMessage(message, identity.privateKey, identity.algorithm); + + return { + ...payload, + signature, + publicKey: identity.publicKey, + }; +} + +// Verify a signed action +export async function verifySignedAction(signedAction: SignedAction): Promise { + const { signature, publicKey, ...payload } = signedAction; + const message = JSON.stringify(payload); + + // Check timestamp freshness (5 minute window) + const age = Date.now() - signedAction.timestamp; + if (age > 5 * 60 * 1000 || age < -60 * 1000) { + console.error("Signed action timestamp outside acceptable window"); + return false; + } + + return await verifySignature(message, signature, publicKey); +} + +// Create an authenticated message for server communication +export async function createAuthenticatedMessage( + type: string, + data: any, + identity: PlayHTMLIdentity +): Promise { + const timestamp = Date.now(); + const nonce = crypto.randomUUID(); + + const payload = { type, data, timestamp, nonce }; + const message = JSON.stringify(payload); + const signature = await signMessage(message, identity.privateKey, identity.algorithm); + + return { + ...payload, + signature, + publicKey: identity.publicKey, + }; +} + +// Verify an authenticated message +export async function verifyAuthenticatedMessage( + message: AuthenticatedMessage +): Promise { + const { signature, publicKey, ...payload } = message; + const messageToVerify = JSON.stringify(payload); + + // Check timestamp freshness + const age = Date.now() - message.timestamp; + if (age > 5 * 60 * 1000 || age < -60 * 1000) { + return false; + } + + return await verifySignature(messageToVerify, signature, publicKey); +} + +// Export/import identity with optional password encryption +export function exportIdentity(identity: PlayHTMLIdentity, password?: string): string { + // For now, export as plain JSON. TODO: Add encryption support + if (password) { + console.warn("Password encryption not yet implemented, exporting as plain text"); + } + + return JSON.stringify(identity, null, 2); +} + +export function importIdentity(jsonData: string, password?: string): PlayHTMLIdentity { + // For now, import from plain JSON. TODO: Add decryption support + if (password) { + console.warn("Password decryption not yet implemented, importing as plain text"); + } + + try { + const parsed = JSON.parse(jsonData); + + // Validate required fields + if (!parsed.privateKey || !parsed.publicKey || !parsed.createdAt) { + throw new Error("Invalid identity format"); + } + + return parsed as PlayHTMLIdentity; + } catch (error) { + throw new Error("Failed to import identity: " + (error instanceof Error ? error.message : String(error))); + } +} \ No newline at end of file diff --git a/packages/playhtml/src/index.ts b/packages/playhtml/src/index.ts index 3234987..382dfac 100644 --- a/packages/playhtml/src/index.ts +++ b/packages/playhtml/src/index.ts @@ -13,12 +13,29 @@ import { RegisteredPlayEvent, generatePersistentPlayerIdentity, deepReplaceIntoProxy, + PlayHTMLIdentity, + GlobalRoleDefinition, + PermissionFunction, + ValidatedSession, } from "@playhtml/common"; import * as Y from "yjs"; import { syncedStore, getYjsDoc, getYjsValue } from "@syncedstore/core"; import { ElementHandler } from "./elements"; import { hashElement } from "./utils"; import { CursorClientAwareness } from "./cursors/cursor-client"; +import { + initializeAuth, + onAuthReady, + getCurrentIdentity, + checkPermission, + createNewIdentity, + configureGlobalPermissions, + initializeSessionAuth, + getCurrentSession, + createSessionAction, + establishSessionWithWS, +} from "./auth"; +import { createSignedAction, createAuthenticatedMessage } from "./crypto"; import { setupDevUI } from "./development"; import { findSharedElementsOnPage, @@ -410,6 +427,17 @@ export interface InitOptions { * Cursor tracking and proximity detection configuration */ cursors?: CursorOptions; + + /** + * Define global roles for the entire site. Maps role names to arrays of public keys + * or conditional role assignments. + */ + roles?: GlobalRoleDefinition; + + /** + * Custom permission condition functions for dynamic role assignments. + */ + permissionConditions?: Record; } let capabilitiesToInitializer: Record = @@ -499,6 +527,8 @@ async function initPlayHTML({ onError, developmentMode = false, cursors = {}, + roles = {}, + permissionConditions = {}, }: InitOptions = {}) { if (!firstSetup || "playhtml" in window) { console.error("playhtml already set up! ignoring"); @@ -508,6 +538,14 @@ async function initPlayHTML({ // @ts-ignore window.playhtml = playhtml; + // Configure global permissions if provided + if ( + Object.keys(roles).length > 0 || + Object.keys(permissionConditions).length > 0 + ) { + configureGlobalPermissions(roles, permissionConditions); + } + // TODO: change to md5 hash if room ID length becomes problem / if some other analytic for telling who is connecting const room = encodeURIComponent(window.location.host + "-" + inputRoom); @@ -615,6 +653,39 @@ async function initPlayHTML({ migrateAllDataFromYMapToSyncedStore(); + // Initialize authentication system + initializeAuth() + .then(async (auth) => { + if (auth.isAuthenticated) { + console.log( + "[PLAYHTML AUTH]: Authenticated as", + auth.identity?.displayName || + auth.identity?.publicKey?.slice(0, 8) + "..." + ); + + // Initialize session-based authentication + try { + await initializeSessionAuth(yprovider.ws!); + console.log( + "[PLAYHTML SESSION]: Session authentication initialized" + ); + } catch (error) { + console.warn( + "[PLAYHTML SESSION]: Failed to establish session:", + error + ); + } + + // Auto-register owned elements with authentication server + await autoRegisterAuthenticatedElements(auth.identity!); + } else { + console.log("[PLAYHTML AUTH]: Running in read-only mode"); + } + }) + .catch((error) => { + console.error("[PLAYHTML AUTH]: Failed to initialize auth:", error); + }); + setupElements(); // Mark all elements as ready after sync completes and elements are set up @@ -637,6 +708,33 @@ async function initPlayHTML({ return yprovider; } +// Auto-register elements with authentication attributes +async function autoRegisterAuthenticatedElements( + identity: PlayHTMLIdentity +): Promise { + // Find all elements this user owns + const ownedElements = document.querySelectorAll( + `[playhtml-owner="${identity.publicKey}"]` + ); + + console.log( + `[PLAYHTML AUTH]: Found ${ownedElements.length} owned elements to register` + ); + + // For the simplified system, we just notify the server of owned elements + // The server will validate permissions based on the simple string format + for (const element of Array.from(ownedElements)) { + if (!(element instanceof HTMLElement) || !element.id) continue; + + const permissions = element.getAttribute("playhtml-permissions"); + if (permissions) { + console.log( + `[PLAYHTML AUTH]: Element ${element.id} has permissions: ${permissions}` + ); + } + } +} + function getElementAwareness(tagType: TagType, elementId: string) { const awareness = yprovider.awareness.getLocalState(); const elementAwareness = awareness?.[tagType] ?? {}; @@ -745,23 +843,88 @@ function createPlayElementData( ? [tagInfo.myDefaultAwareness] : undefined, element, - onChange: (newData) => { + onChange: async (newData) => { // Prevent writes for read-only shared consumer elements const elementIdFromAttr = getIdForElement(element); if (isSharedReadOnly(element, elementIdFromAttr)) { return; } - if (typeof (newData as any) === "function") { - // Mutator form support: onChange can accept function(draft) - // Batch all nested mutations into a single Yjs transaction to coalesce events - doc.transact(() => { - (newData as any)(dataProxy); - }); + // Check permissions if element has authentication attributes + const identity = getCurrentIdentity(); + const session = getCurrentSession(); + + if (element.hasAttribute("playhtml-owner")) { + const hasPermission = await checkPermission( + elementId, + "write", + identity + ); + if (!hasPermission) { + console.warn( + `[PLAYHTML AUTH]: Permission denied for write action on element ${elementId}` + ); + return; + } + } + + // If we have a session, use session-based actions for server validation + if (session && identity) { + try { + // Create session action for server validation + const sessionAction = createSessionAction( + "write", + elementId, + newData + ); + + // Send to server for validation (optimistic update - apply locally first) + if (typeof (newData as any) === "function") { + doc.transact(() => { + (newData as any)(dataProxy); + }); + } else { + doc.transact(() => { + deepReplaceIntoProxy(dataProxy, newData); + }); + } + + // Send session action to server for validation + yprovider.ws?.send( + JSON.stringify({ + type: "session_action", + action: sessionAction, + }) + ); + } catch (error) { + console.error( + "[PLAYHTML SESSION]: Failed to create session action:", + error + ); + // Fall back to direct update for better UX + if (typeof (newData as any) === "function") { + doc.transact(() => { + (newData as any)(dataProxy); + }); + } else { + doc.transact(() => { + deepReplaceIntoProxy(dataProxy, newData); + }); + } + } } else { - // Value form: replace snapshot semantics - doc.transact(() => { - deepReplaceIntoProxy(dataProxy, newData); - }); + // No session or identity - direct CRDT update + if (typeof (newData as any) === "function") { + // Mutator form support: onChange can accept function(draft) + // Batch all nested mutations into a single Yjs transaction to coalesce events + doc.transact(() => { + (newData as any)(dataProxy); + }); + } else { + // Value form: replace snapshot semantics + doc.transact(() => { + deepReplaceIntoProxy(dataProxy, newData); + }); + } } }, onAwarenessChange: (elementAwarenessData) => { @@ -949,6 +1112,21 @@ export interface PlayHTMLComponents { registerPlayEventListener: typeof registerPlayEventListener; removePlayEventListener: typeof removePlayEventListener; cursorClient: CursorClientAwareness | null; + // Authentication + auth: { + getCurrentIdentity: typeof getCurrentIdentity; + checkPermission: typeof checkPermission; + onAuthReady: typeof onAuthReady; + createSignedAction: typeof createSignedAction; + createAuthenticatedMessage: typeof createAuthenticatedMessage; + createNewIdentity: typeof createNewIdentity; + configureGlobalPermissions: typeof configureGlobalPermissions; + // Session functions + getCurrentSession: typeof getCurrentSession; + establishSession: ( + identity?: PlayHTMLIdentity + ) => Promise; + }; } // Expose big variables to the window object for debugging purposes. @@ -969,6 +1147,28 @@ export const playhtml: PlayHTMLComponents = { get cursorClient() { return cursorClient; }, + // Authentication + auth: { + getCurrentIdentity, + checkPermission, + onAuthReady, + createSignedAction, + createAuthenticatedMessage, + createNewIdentity, + configureGlobalPermissions, + // Session functions + getCurrentSession, + establishSession: async (identity?: PlayHTMLIdentity) => { + const actualIdentity = identity || getCurrentIdentity(); + if (!actualIdentity) { + throw new Error("No identity available for session establishment"); + } + if (!yprovider?.ws) { + throw new Error("No WebSocket connection available"); + } + return await establishSessionWithWS(actualIdentity, yprovider.ws); + }, + }, }; /** diff --git a/packages/react/src/elements.tsx b/packages/react/src/elements.tsx index 886961b..6b1726a 100644 --- a/packages/react/src/elements.tsx +++ b/packages/react/src/elements.tsx @@ -1,18 +1,22 @@ import { TagTypeToElement, TagType } from "@playhtml/common"; import classNames from "classnames"; import React, { useState } from "react"; -import { CanPlayElement, SharedBindingProps } from "."; +import { CanPlayElement, PermissionProps, SharedBindingProps } from "."; import playhtml from "./playhtml-singleton"; import { SingleChildOrPlayable, renderSingleChildOrPlayable } from "./utils"; export function CanMoveElement({ children, + owner, + permissions, dataSource, shared, -}: { children: SingleChildOrPlayable } & SharedBindingProps) { +}: { children: SingleChildOrPlayable } & SharedBindingProps & PermissionProps) { return ( (() => { + const initial: PermissionState = { + canRead: true, + canWrite: true, + canDelete: false, + canModerate: false, + canAdmin: false, + }; + + // Add custom actions to initial state + customActions.forEach(action => { + initial[`can${action.charAt(0).toUpperCase() + action.slice(1)}`] = false; + }); + + return initial; + }); + + useEffect(() => { + const updatePermissions = async () => { + // @ts-ignore - auth may not be typed in the React package yet + if (!playhtml.auth?.getCurrentIdentity || !playhtml.auth?.checkPermission) { + console.warn('[usePlayHTMLPermissions] PlayHTML auth not available'); + return; + } + + // @ts-ignore - auth may not be typed in the React package yet + const identity = playhtml.auth.getCurrentIdentity(); + + const newPermissions: PermissionState = { + canRead: true, + canWrite: true, + canDelete: false, + canModerate: false, + canAdmin: false, + }; + + try { + for (const action of allActions) { + // @ts-ignore - auth may not be typed in the React package yet + const hasPermission = await playhtml.auth.checkPermission(elementId, action, identity); + const camelCaseAction = `can${action.charAt(0).toUpperCase() + action.slice(1)}`; + newPermissions[camelCaseAction] = hasPermission; + } + } catch (error) { + console.error('[usePlayHTMLPermissions] Error checking permissions:', error); + } + + setPermissions(newPermissions); + }; + + updatePermissions(); + + // Listen for auth changes + const handleAuthReady = () => updatePermissions(); + window.addEventListener('playhtmlAuthReady', handleAuthReady); + + return () => { + window.removeEventListener('playhtmlAuthReady', handleAuthReady); + }; + }, [elementId, allActions.join(',')]); + + return permissions; +} \ No newline at end of file diff --git a/packages/react/src/index.tsx b/packages/react/src/index.tsx index b5d478a..04dc324 100644 --- a/packages/react/src/index.tsx +++ b/packages/react/src/index.tsx @@ -1,7 +1,11 @@ // TODO: idk why but this is not getting registered otherwise?? import React from "react"; import { useContext, useEffect, useRef, useState } from "react"; -import { ElementInitializer, TagType } from "@playhtml/common"; +import { + ElementInitializer, + TagType, + PermissionConfig, +} from "@playhtml/common"; import playhtml from "./playhtml-singleton"; import { cloneThroughFragments, @@ -21,6 +25,12 @@ export interface LoadingOptions { style?: "breathing" | "pulse" | "fade" | "none"; } +// Permission-related props for React components +export interface PermissionProps { + owner?: string; // Owner public key or domain + permissions?: PermissionConfig | string; // Object or string like "write:owner, delete:moderators" +} + // Shared binding props for both generic and tag-specific React elements export type SharedBindingProps = { dataSource?: string; @@ -40,20 +50,20 @@ export type WithPlayProps = loading?: LoadingOptions; dataSource?: SharedBindingProps["dataSource"]; shared?: SharedBindingProps["shared"]; - }); + } & PermissionProps); // Add standalone and loading to the ReactElementInitializer type export type ReactElementInitializerWithStandalone = | (ReactElementInitializer & { standalone?: boolean; loading?: LoadingOptions; - }) + } & PermissionProps) | (Omit, "defaultData"> & { defaultData: undefined; tagInfo?: Partial<{ [k in TagType]: string }> | TagType[]; standalone?: boolean; loading?: LoadingOptions; - }); + } & PermissionProps); // TODO: make the mapping to for TagType -> ReactElementInitializer // TODO: semantically, it should not be `can-play` for all of the pre-defined ones.. @@ -100,7 +110,12 @@ export function CanPlayElement({ } }, [standalone, playContext.isProviderMissing]); - const { tagInfo = { "can-play": "" }, ...elementProps } = { + const { + tagInfo = { "can-play": "" }, + owner, + permissions, + ...elementProps + } = { tagInfo: undefined, ...restProps, }; @@ -154,13 +169,13 @@ export function CanPlayElement({ ref.current.updateElement = updateElement; // @ts-ignore ref.current.updateElementAwareness = updateElement; - + // Setup the element, which will handle data-source discovery if needed try { playhtml.setupPlayElement(ref.current, { ignoreIfAlreadySetup: true }); } catch (error) { console.warn("[@playhtml/react] Failed to setup play element:", error); - + // If playhtml isn't initialized yet, log a helpful message if (!playhtml.elementHandlers) { console.warn( @@ -215,6 +230,25 @@ export function CanPlayElement({ ); } + // Convert permission props to HTML attributes + const permissionAttrs: Record = {}; + + if (owner) { + permissionAttrs["playhtml-owner"] = owner; + } + + if (permissions) { + if (typeof permissions === "string") { + permissionAttrs["playhtml-permissions"] = permissions; + } else { + // Convert object to string format + const permissionString = Object.entries(permissions) + .map(([action, role]) => `${action}:${role}`) + .join(", "); + permissionAttrs["playhtml-permissions"] = permissionString; + } + } + return cloneThroughFragments( React.Children.only(renderedChildren), { @@ -222,6 +256,7 @@ export function CanPlayElement({ ref, ...computedTagInfo, ...loadingAttributes, + ...permissionAttrs, ...(dataSource ? { "data-source": dataSource } : {}), ...(shared ? typeof shared === "string" @@ -410,3 +445,4 @@ export { CanDuplicateElement, CanHoverElement, } from "./elements"; +export { usePlayHTMLPermissions } from "./hooks/usePlayHTMLPermissions"; diff --git a/partykit/party.ts b/partykit/party.ts index 4c8740c..21ebefe 100644 --- a/partykit/party.ts +++ b/partykit/party.ts @@ -2,18 +2,16 @@ import type * as Party from "partykit/server"; import { onConnect, unstable_getYDoc } from "y-partykit"; import { createClient } from "@supabase/supabase-js"; import { syncedStore, getYjsValue } from "@syncedstore/core"; -import { deepReplaceIntoProxy } from "@playhtml/common"; +import { + deepReplaceIntoProxy, + SessionAction, + SessionChallenge, + ValidatedSession, +} from "@playhtml/common"; import { Buffer } from "node:buffer"; import * as Y from "yjs"; import { deriveRoomId, normalizePath } from "@playhtml/common"; -// Create a single supabase client for interacting with your database -const supabase = createClient( - process.env.SUPABASE_URL as string, - process.env.SUPABASE_KEY as string, - { auth: { persistSession: false } } -); - // Storage key constants for consistency const STORAGE_KEYS = { subscribers: "subscribers", @@ -23,11 +21,74 @@ const STORAGE_KEYS = { const ORIGIN_S2C = "__bridge_s2c__"; const ORIGIN_C2S = "__bridge_c2s__"; +// Create a single supabase client for interacting with your database +const supabase = createClient( + process.env.SUPABASE_URL as string, + process.env.SUPABASE_KEY as string, + { auth: { persistSession: false } } +); + +// Crypto utilities +// TODO: merge with one in common +async function verifySignature( + message: string, + signatureBase64: string, + publicKeyBase64: string, + algorithm: string = "Ed25519" +): Promise { + try { + console.log(`[PartyKit] Verifying signature with algorithm: ${algorithm}`); + console.log(`[PartyKit] Public key length: ${publicKeyBase64.length}`); + console.log(`[PartyKit] Signature length: ${signatureBase64.length}`); + + const publicKeyBuffer = Buffer.from(publicKeyBase64, "base64"); + + const keyAlgorithm = + algorithm === "RSA-PSS" + ? { name: "RSA-PSS", hash: "SHA-256" } + : { name: "Ed25519" }; + + const publicKey = await crypto.subtle.importKey( + "spki", + publicKeyBuffer, + keyAlgorithm, + false, + ["verify"] + ); + + const messageBuffer = new TextEncoder().encode(message); + const signatureBuffer = Buffer.from(signatureBase64, "base64"); + + const verifyAlgorithm = + algorithm === "RSA-PSS" ? { name: "RSA-PSS", saltLength: 32 } : "Ed25519"; + + const result = await crypto.subtle.verify( + verifyAlgorithm, + publicKey, + signatureBuffer, + messageBuffer + ); + + console.log(`[PartyKit] Signature verification result: ${result}`); + return result; + } catch (error) { + console.error("Signature verification failed:", error); + return false; + } +} + export default class implements Party.Server { - constructor(public room: Party.Room) {} // Reuse the exact same options for all Y.Doc access private providerOptions: import("y-partykit").YPartyKitOptions | undefined; private observersAttached = false; + private validSessions = new Map(); + private pendingChallenges = new Map(); + private usedNonces = new Set(); + + constructor(public room: Party.Room) { + // Cleanup expired sessions every hour + setInterval(() => this.cleanupExpiredSessions(), 60 * 60 * 1000); + } // --- Helper: normalize path used in room id derivation normalizePath(path: string): string { @@ -233,7 +294,7 @@ export default class implements Party.Server { if (typeof message === "string") { try { const parsed = JSON.parse(message); - + console.log(`[PartyKit] Received message type: ${parsed.type}`); if (parsed.type === "add-shared-reference") { // Handle dynamic addition of shared reference await this.handleAddSharedReference(parsed.reference, sender); @@ -243,6 +304,19 @@ export default class implements Party.Server { } else if (parsed.type === "register-shared-element") { // Handle dynamic registration of shared source element await this.handleRegisterSharedElement(parsed.element, sender); + } else if (parsed.type === "session_establish") { + console.log( + `[PartyKit] Handling session establishment for ${parsed.publicKey?.slice( + 0, + 8 + )}...` + ); + await this.handleSessionEstablishmentWS(parsed, sender); + } else if (parsed.type === "session_action") { + console.log( + `[PartyKit] Handling session action: ${parsed.action?.action}` + ); + await this.handleSessionAction(parsed.action, sender); } else { // Broadcast other messages normally this.room.broadcast(message); @@ -619,4 +693,177 @@ export default class implements Party.Server { this.observersAttached = true; } + + // WebSocket-based session establishment + private async handleSessionEstablishmentWS( + request: any, + sender: Party.Connection + ) { + try { + const { challenge, signature, publicKey, algorithm } = request; + + console.log( + `[PartyKit] Session request - algorithm: ${algorithm}, publicKey: ${publicKey?.slice( + 0, + 16 + )}...` + ); + + // Validate signature first - use algorithm if provided, default to Ed25519 + const isValidSignature = await verifySignature( + JSON.stringify(challenge), + signature, + publicKey, + algorithm || "Ed25519" + ); + + if (!isValidSignature) { + sender.send( + JSON.stringify({ + type: "session_error", + message: "Invalid signature", + }) + ); + return; + } + + // Check if this is a renewal (user already has active session) + const existingSession = this.findExistingSession(publicKey); + + if (existingSession) { + // Extend existing session + existingSession.expiresAt = Date.now() + 24 * 60 * 60 * 1000; + + console.log(`🔄 Renewed session for ${publicKey.slice(0, 8)}...`); + + sender.send( + JSON.stringify({ + type: "session_renewed", + sessionId: existingSession.sessionId, + publicKey: existingSession.publicKey, + expiresAt: existingSession.expiresAt, + }) + ); + } else { + // Create new session + const session: ValidatedSession = { + sessionId: crypto.randomUUID(), + publicKey, + domain: challenge.domain || "localhost", + establishedAt: Date.now(), + expiresAt: Date.now() + 24 * 60 * 60 * 1000, // 24 hours + }; + + this.validSessions.set(session.sessionId, session); + + console.log( + `✅ New session established for ${publicKey.slice(0, 8)}...` + ); + + sender.send( + JSON.stringify({ + type: "session_established", + sessionId: session.sessionId, + publicKey: session.publicKey, + expiresAt: session.expiresAt, + }) + ); + } + } catch (error) { + console.error("Session establishment error:", error); + sender.send( + JSON.stringify({ + type: "session_error", + message: "Session establishment failed", + }) + ); + } + } + + private findExistingSession(publicKey: string): ValidatedSession | null { + for (const session of this.validSessions.values()) { + if (session.publicKey === publicKey && session.expiresAt > Date.now()) { + return session; + } + } + return null; + } + + // Handle session-based actions + private async handleSessionAction( + action: SessionAction, + sender: Party.Connection + ) { + try { + // Validate session exists and is not expired + const session = this.validSessions.get(action.sessionId); + if (!session || session.expiresAt < Date.now()) { + throw new Error("Invalid or expired session"); + } + + // Basic action validation + if (!this.isValidAction(action)) { + throw new Error("Invalid action format"); + } + + // Check nonce uniqueness (prevent replay attacks) + const nonceKey = `${action.sessionId}:${action.nonce}`; + if (this.usedNonces.has(nonceKey)) { + throw new Error("Duplicate action detected"); + } + + // Mark action as processed and broadcast validation + this.usedNonces.add(nonceKey); + + this.room.broadcast( + JSON.stringify({ + type: "session_action_validated", + action: { + elementId: action.elementId, + action: action.action, + appliedBy: session.publicKey, + appliedAt: Date.now(), + }, + }) + ); + + console.log( + `✅ Session action validated: ${action.action} on ${action.elementId}` + ); + + // Clean up old nonces (5 minute window) + setTimeout(() => this.usedNonces.delete(nonceKey), 5 * 60 * 1000); + } catch (error) { + console.error("Session action error:", error); + sender.send( + JSON.stringify({ + type: "action_rejected", + reason: error.message, + }) + ); + } + } + + private isValidAction(action: SessionAction): boolean { + return !!( + action.sessionId && + action.action && + action.elementId && + action.timestamp && + action.nonce && + // Timestamp should be recent (within 5 minutes) + Date.now() - action.timestamp < 5 * 60 * 1000 + ); + } + + // Cleanup expired sessions + private cleanupExpiredSessions(): void { + const now = Date.now(); + for (const [sessionId, session] of this.validSessions.entries()) { + if (session.expiresAt < now) { + this.validSessions.delete(sessionId); + console.log(`🗑️ Cleaned up expired session: ${sessionId}`); + } + } + } } diff --git a/website/examples/auth-example.html b/website/examples/auth-example.html new file mode 100644 index 0000000..a43f268 --- /dev/null +++ b/website/examples/auth-example.html @@ -0,0 +1,489 @@ + + + + + + PlayHTML Authentication Example + + + +

PlayHTML Authentication Example

+ +
+

Authentication Status

+
Loading...
+
+ + + + +
+ +
+

Public Element (No Authentication Required)

+

Anyone can interact with this element:

+
+ Public +
+
+ +
+

Authenticated Element (Owner Only)

+

+ Only the owner can modify this element (using new simplified syntax): +

+
+ Owner Only +
+
+ +
+

Role-Based Element (Simple Permissions)

+

Different actions require different roles:

+
+ Role-Based +
+
+ +
+

Conditional Access Element

+

Access based on visit count and time of day:

+
+ Conditional +
+
+ +
+ + + + From 3bb1fcd215dc2f0a6f56d7fe8bc5220234b7e9ff Mon Sep 17 00:00:00 2001 From: Spencer Chang Date: Fri, 12 Sep 2025 10:55:10 -0700 Subject: [PATCH 2/3] checkpoint identity + tests --- bun.lock | 448 +++++++---------- packages/playhtml/src/__tests__/auth.test.ts | 374 ++++++++++++++ packages/playhtml/src/auth.ts | 28 +- partykit/__tests__/auth.test.ts | 503 +++++++++++++++++++ partykit/package.json | 17 + partykit/party.ts | 31 +- website/examples/auth-example.html | 59 +-- 7 files changed, 1165 insertions(+), 295 deletions(-) create mode 100644 packages/playhtml/src/__tests__/auth.test.ts create mode 100644 partykit/__tests__/auth.test.ts create mode 100644 partykit/package.json diff --git a/bun.lock b/bun.lock index d18f54e..ec21b28 100644 --- a/bun.lock +++ b/bun.lock @@ -32,7 +32,7 @@ }, "packages/common": { "name": "@playhtml/common", - "version": "0.2.0", + "version": "0.2.1", "devDependencies": { "typescript": "^5.0.2", "vite": "^7.1.2", @@ -41,7 +41,7 @@ }, "packages/extension": { "name": "@playhtml/extension", - "version": "0.1.2", + "version": "0.1.3", "dependencies": { "@playhtml/common": "workspace:*", "@playhtml/react": "workspace:*", @@ -65,9 +65,9 @@ }, "packages/playhtml": { "name": "playhtml", - "version": "2.4.0", + "version": "2.4.1", "dependencies": { - "@playhtml/common": "0.2.0", + "@playhtml/common": "0.2.1", "@syncedstore/core": "^0.6.0", "y-partykit": "^0.0.31", "yjs": "13.6.18", @@ -83,11 +83,11 @@ }, "packages/react": { "name": "@playhtml/react", - "version": "0.6.0", + "version": "0.6.1", "dependencies": { - "@playhtml/common": "^0.2.0", + "@playhtml/common": "^0.2.1", "classnames": "^2.3.2", - "playhtml": "^2.4.0", + "playhtml": "^2.4.1", "react": "^16.8.0 || ^17.0.0 || ^18.2.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.2.0 || ^19.0.0", }, @@ -117,15 +117,13 @@ "@aklinker1/rollup-plugin-visualizer": ["@aklinker1/rollup-plugin-visualizer@5.12.0", "", { "dependencies": { "open": "^8.4.0", "picomatch": "^2.3.1", "source-map": "^0.7.4", "yargs": "^17.5.1" }, "peerDependencies": { "rollup": "2.x || 3.x || 4.x" }, "optionalPeers": ["rollup"], "bin": { "rollup-plugin-visualizer": "dist/bin/cli.js" } }, "sha512-X24LvEGw6UFmy0lpGJDmXsMyBD58XmX1bbwsaMLhNoM+UMQfQ3b2RtC+nz4b/NoRK5r6QJSKJHBNVeUdwqybaQ=="], - "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="], - "@asamuzakjp/css-color": ["@asamuzakjp/css-color@3.2.0", "", { "dependencies": { "@csstools/css-calc": "^2.1.3", "@csstools/css-color-parser": "^3.0.9", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "lru-cache": "^10.4.3" } }, "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw=="], "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], - "@babel/compat-data": ["@babel/compat-data@7.28.0", "", {}, "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw=="], + "@babel/compat-data": ["@babel/compat-data@7.28.4", "", {}, "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw=="], - "@babel/core": ["@babel/core@7.28.3", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.3", "@babel/parser": "^7.28.3", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.3", "@babel/types": "^7.28.2", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ=="], + "@babel/core": ["@babel/core@7.28.4", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.4", "@babel/types": "^7.28.4", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA=="], "@babel/generator": ["@babel/generator@7.28.3", "", { "dependencies": { "@babel/parser": "^7.28.3", "@babel/types": "^7.28.2", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw=="], @@ -145,29 +143,29 @@ "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], - "@babel/helpers": ["@babel/helpers@7.28.3", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.2" } }, "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw=="], + "@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="], - "@babel/parser": ["@babel/parser@7.28.3", "", { "dependencies": { "@babel/types": "^7.28.2" }, "bin": "./bin/babel-parser.js" }, "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA=="], + "@babel/parser": ["@babel/parser@7.28.4", "", { "dependencies": { "@babel/types": "^7.28.4" }, "bin": "./bin/babel-parser.js" }, "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg=="], "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="], "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="], - "@babel/runtime": ["@babel/runtime@7.28.3", "", {}, "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA=="], + "@babel/runtime": ["@babel/runtime@7.28.4", "", {}, "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ=="], "@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="], - "@babel/traverse": ["@babel/traverse@7.28.3", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.3", "@babel/template": "^7.27.2", "@babel/types": "^7.28.2", "debug": "^4.3.1" } }, "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ=="], + "@babel/traverse": ["@babel/traverse@7.28.4", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", "@babel/types": "^7.28.4", "debug": "^4.3.1" } }, "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ=="], - "@babel/types": ["@babel/types@7.28.2", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ=="], + "@babel/types": ["@babel/types@7.28.4", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q=="], - "@changesets/apply-release-plan": ["@changesets/apply-release-plan@7.0.12", "", { "dependencies": { "@changesets/config": "^3.1.1", "@changesets/get-version-range-type": "^0.4.0", "@changesets/git": "^3.0.4", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "detect-indent": "^6.0.0", "fs-extra": "^7.0.1", "lodash.startcase": "^4.4.0", "outdent": "^0.5.0", "prettier": "^2.7.1", "resolve-from": "^5.0.0", "semver": "^7.5.3" } }, "sha512-EaET7As5CeuhTzvXTQCRZeBUcisoYPDDcXvgTE/2jmmypKp0RC7LxKj/yzqeh/1qFTZI7oDGFcL1PHRuQuketQ=="], + "@changesets/apply-release-plan": ["@changesets/apply-release-plan@7.0.13", "", { "dependencies": { "@changesets/config": "^3.1.1", "@changesets/get-version-range-type": "^0.4.0", "@changesets/git": "^3.0.4", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "detect-indent": "^6.0.0", "fs-extra": "^7.0.1", "lodash.startcase": "^4.4.0", "outdent": "^0.5.0", "prettier": "^2.7.1", "resolve-from": "^5.0.0", "semver": "^7.5.3" } }, "sha512-BIW7bofD2yAWoE8H4V40FikC+1nNFEKBisMECccS16W1rt6qqhNTBDmIw5HaqmMgtLNz9e7oiALiEUuKrQ4oHg=="], "@changesets/assemble-release-plan": ["@changesets/assemble-release-plan@6.0.9", "", { "dependencies": { "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.3", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "semver": "^7.5.3" } }, "sha512-tPgeeqCHIwNo8sypKlS3gOPmsS3wP0zHt67JDuL20P4QcXiw/O4Hl7oXiuLnP9yg+rXLQ2sScdV1Kkzde61iSQ=="], "@changesets/changelog-git": ["@changesets/changelog-git@0.2.1", "", { "dependencies": { "@changesets/types": "^6.1.0" } }, "sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q=="], - "@changesets/cli": ["@changesets/cli@2.29.6", "", { "dependencies": { "@changesets/apply-release-plan": "^7.0.12", "@changesets/assemble-release-plan": "^6.0.9", "@changesets/changelog-git": "^0.2.1", "@changesets/config": "^3.1.1", "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.3", "@changesets/get-release-plan": "^4.0.13", "@changesets/git": "^3.0.4", "@changesets/logger": "^0.1.1", "@changesets/pre": "^2.0.2", "@changesets/read": "^0.6.5", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@changesets/write": "^0.4.0", "@inquirer/external-editor": "^1.0.0", "@manypkg/get-packages": "^1.1.3", "ansi-colors": "^4.1.3", "ci-info": "^3.7.0", "enquirer": "^2.4.1", "fs-extra": "^7.0.1", "mri": "^1.2.0", "p-limit": "^2.2.0", "package-manager-detector": "^0.2.0", "picocolors": "^1.1.0", "resolve-from": "^5.0.0", "semver": "^7.5.3", "spawndamnit": "^3.0.1", "term-size": "^2.1.0" }, "bin": { "changeset": "bin.js" } }, "sha512-6qCcVsIG1KQLhpQ5zE8N0PckIx4+9QlHK3z6/lwKnw7Tir71Bjw8BeOZaxA/4Jt00pcgCnCSWZnyuZf5Il05QQ=="], + "@changesets/cli": ["@changesets/cli@2.29.7", "", { "dependencies": { "@changesets/apply-release-plan": "^7.0.13", "@changesets/assemble-release-plan": "^6.0.9", "@changesets/changelog-git": "^0.2.1", "@changesets/config": "^3.1.1", "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.3", "@changesets/get-release-plan": "^4.0.13", "@changesets/git": "^3.0.4", "@changesets/logger": "^0.1.1", "@changesets/pre": "^2.0.2", "@changesets/read": "^0.6.5", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@changesets/write": "^0.4.0", "@inquirer/external-editor": "^1.0.0", "@manypkg/get-packages": "^1.1.3", "ansi-colors": "^4.1.3", "ci-info": "^3.7.0", "enquirer": "^2.4.1", "fs-extra": "^7.0.1", "mri": "^1.2.0", "p-limit": "^2.2.0", "package-manager-detector": "^0.2.0", "picocolors": "^1.1.0", "resolve-from": "^5.0.0", "semver": "^7.5.3", "spawndamnit": "^3.0.1", "term-size": "^2.1.0" }, "bin": { "changeset": "bin.js" } }, "sha512-R7RqWoaksyyKXbKXBTbT4REdy22yH81mcFK6sWtqSanxUCbUi9Uf+6aqxZtDQouIqPdem2W56CdxXgsxdq7FLQ=="], "@changesets/config": ["@changesets/config@3.1.1", "", { "dependencies": { "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.3", "@changesets/logger": "^0.1.1", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "fs-extra": "^7.0.1", "micromatch": "^4.0.8" } }, "sha512-bd+3Ap2TKXxljCggI0mKPfzCQKeV/TU4yO2h2C6vAihIo8tzseAn2e7klSuiyYYXvgu53zMN1OeYMIQkaQoWnA=="], @@ -205,15 +203,15 @@ "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20240718.0", "", { "os": "win32", "cpu": "x64" }, "sha512-YpCRvvT47XanFum7C3SedOZKK6BfVhqmwdAAVAQFyc4gsCdegZo0JkUkdloC/jwuWlbCACOG2HTADHOqyeolzQ=="], - "@cloudflare/workers-types": ["@cloudflare/workers-types@4.20250819.0", "", {}, "sha512-TGghrtaQzT2k71gdiUOxwwt3owni1rWX3+BBTk1K/H0vmsinwEJsGmEJRdvIuQO72Davtfz9u6LBbR1uNoE1Qw=="], + "@cloudflare/workers-types": ["@cloudflare/workers-types@4.20250922.0", "", {}, "sha512-BaqlKnVc0Xzqm9xt3TC4v0yB9EHy5vVqpiWz+DAsbEmdcpUbqdBschvI9502p6FgFbZElD7XcxTEeViXLsoO0A=="], "@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="], - "@csstools/color-helpers": ["@csstools/color-helpers@5.0.2", "", {}, "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA=="], + "@csstools/color-helpers": ["@csstools/color-helpers@5.1.0", "", {}, "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA=="], "@csstools/css-calc": ["@csstools/css-calc@2.1.4", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ=="], - "@csstools/css-color-parser": ["@csstools/css-color-parser@3.0.10", "", { "dependencies": { "@csstools/color-helpers": "^5.0.2", "@csstools/css-calc": "^2.1.4" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg=="], + "@csstools/css-color-parser": ["@csstools/css-color-parser@3.1.0", "", { "dependencies": { "@csstools/color-helpers": "^5.1.0", "@csstools/css-calc": "^2.1.4" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA=="], "@csstools/css-parser-algorithms": ["@csstools/css-parser-algorithms@3.0.5", "", { "peerDependencies": { "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ=="], @@ -225,61 +223,61 @@ "@devicefarmer/adbkit-monkey": ["@devicefarmer/adbkit-monkey@1.2.1", "", {}, "sha512-ZzZY/b66W2Jd6NHbAhLyDWOEIBWC11VizGFk7Wx7M61JZRz7HR9Cq5P+65RKWUU7u6wgsE8Lmh9nE4Mz+U2eTg=="], - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.9", "", { "os": "aix", "cpu": "ppc64" }, "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.10", "", { "os": "aix", "cpu": "ppc64" }, "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw=="], - "@esbuild/android-arm": ["@esbuild/android-arm@0.25.9", "", { "os": "android", "cpu": "arm" }, "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ=="], + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.10", "", { "os": "android", "cpu": "arm" }, "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w=="], - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.9", "", { "os": "android", "cpu": "arm64" }, "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg=="], + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.10", "", { "os": "android", "cpu": "arm64" }, "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg=="], - "@esbuild/android-x64": ["@esbuild/android-x64@0.25.9", "", { "os": "android", "cpu": "x64" }, "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw=="], + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.10", "", { "os": "android", "cpu": "x64" }, "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg=="], - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.9", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg=="], + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA=="], - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.9", "", { "os": "darwin", "cpu": "x64" }, "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ=="], + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg=="], - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.9", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q=="], + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.10", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg=="], - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.9", "", { "os": "freebsd", "cpu": "x64" }, "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg=="], + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.10", "", { "os": "freebsd", "cpu": "x64" }, "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA=="], - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.9", "", { "os": "linux", "cpu": "arm" }, "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw=="], + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.10", "", { "os": "linux", "cpu": "arm" }, "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg=="], - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw=="], + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ=="], - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.9", "", { "os": "linux", "cpu": "ia32" }, "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A=="], + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.10", "", { "os": "linux", "cpu": "ia32" }, "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ=="], - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.9", "", { "os": "linux", "cpu": "none" }, "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ=="], + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg=="], - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.9", "", { "os": "linux", "cpu": "none" }, "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA=="], + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA=="], - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.9", "", { "os": "linux", "cpu": "ppc64" }, "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w=="], + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.10", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA=="], - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.9", "", { "os": "linux", "cpu": "none" }, "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg=="], + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA=="], - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.9", "", { "os": "linux", "cpu": "s390x" }, "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA=="], + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.10", "", { "os": "linux", "cpu": "s390x" }, "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew=="], - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.9", "", { "os": "linux", "cpu": "x64" }, "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg=="], + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.10", "", { "os": "linux", "cpu": "x64" }, "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA=="], - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.9", "", { "os": "none", "cpu": "arm64" }, "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q=="], + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.10", "", { "os": "none", "cpu": "arm64" }, "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A=="], - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.9", "", { "os": "none", "cpu": "x64" }, "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g=="], + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.10", "", { "os": "none", "cpu": "x64" }, "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig=="], - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.9", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ=="], + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.10", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw=="], - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.9", "", { "os": "openbsd", "cpu": "x64" }, "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA=="], + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.10", "", { "os": "openbsd", "cpu": "x64" }, "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw=="], - "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.9", "", { "os": "none", "cpu": "arm64" }, "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg=="], + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.10", "", { "os": "none", "cpu": "arm64" }, "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag=="], - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.9", "", { "os": "sunos", "cpu": "x64" }, "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw=="], + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.10", "", { "os": "sunos", "cpu": "x64" }, "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ=="], - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.9", "", { "os": "win32", "cpu": "arm64" }, "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ=="], + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw=="], - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.9", "", { "os": "win32", "cpu": "ia32" }, "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww=="], + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.10", "", { "os": "win32", "cpu": "ia32" }, "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw=="], - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.9", "", { "os": "win32", "cpu": "x64" }, "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ=="], + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.10", "", { "os": "win32", "cpu": "x64" }, "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw=="], "@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="], - "@inquirer/external-editor": ["@inquirer/external-editor@1.0.1", "", { "dependencies": { "chardet": "^2.1.0", "iconv-lite": "^0.6.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-Oau4yL24d2B5IL4ma4UpbQigkVhzPDXLoqy1ggK4gnHg/stmkffJE4oOXHXF3uz0UEpywG68KcyXsyYpA1Re/Q=="], + "@inquirer/external-editor": ["@inquirer/external-editor@1.0.2", "", { "dependencies": { "chardet": "^2.1.0", "iconv-lite": "^0.7.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-yy9cOoBnx58TlsPrIxauKIFQTiyH+0MK4e97y4sV9ERbI+zDxw7i2hxHLCIEGIE/8PPvDxGhgzIOTSOWcs6/MQ=="], "@isaacs/balanced-match": ["@isaacs/balanced-match@4.0.1", "", {}, "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ=="], @@ -295,7 +293,7 @@ "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], - "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.30", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q=="], + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], "@manypkg/find-root": ["@manypkg/find-root@1.1.0", "", { "dependencies": { "@babel/runtime": "^7.5.5", "@types/node": "^12.7.1", "find-up": "^4.1.0", "fs-extra": "^8.1.0" } }, "sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA=="], @@ -361,47 +359,51 @@ "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="], - "@rollup/pluginutils": ["@rollup/pluginutils@5.2.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw=="], + "@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="], + + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.0", "", { "os": "android", "cpu": "arm" }, "sha512-VxDYCDqOaR7NXzAtvRx7G1u54d2kEHopb28YH/pKzY6y0qmogP3gG7CSiWsq9WvDFxOQMpNEyjVAHZFXfH3o/A=="], - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.46.3", "", { "os": "android", "cpu": "arm" }, "sha512-UmTdvXnLlqQNOCJnyksjPs1G4GqXNGW1LrzCe8+8QoaLhhDeTXYBgJ3k6x61WIhlHX2U+VzEJ55TtIjR/HTySA=="], + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.52.0", "", { "os": "android", "cpu": "arm64" }, "sha512-pqDirm8koABIKvzL59YI9W9DWbRlTX7RWhN+auR8HXJxo89m4mjqbah7nJZjeKNTNYopqL+yGg+0mhCpf3xZtQ=="], - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.46.3", "", { "os": "android", "cpu": "arm64" }, "sha512-8NoxqLpXm7VyeI0ocidh335D6OKT0UJ6fHdnIxf3+6oOerZZc+O7r+UhvROji6OspyPm+rrIdb1gTXtVIqn+Sg=="], + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.52.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-YCdWlY/8ltN6H78HnMsRHYlPiKvqKagBP1r+D7SSylxX+HnsgXGCmLiV3Y4nSyY9hW8qr8U9LDUx/Lo7M6MfmQ=="], - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.46.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-csnNavqZVs1+7/hUKtgjMECsNG2cdB8F7XBHP6FfQjqhjF8rzMzb3SLyy/1BG7YSfQ+bG75Ph7DyedbUqwq1rA=="], + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.52.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-z4nw6y1j+OOSGzuVbSWdIp1IUks9qNw4dc7z7lWuWDKojY38VMWBlEN7F9jk5UXOkUcp97vA1N213DF+Lz8BRg=="], - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.46.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-r2MXNjbuYabSIX5yQqnT8SGSQ26XQc8fmp6UhlYJd95PZJkQD1u82fWP7HqvGUf33IsOC6qsiV+vcuD4SDP6iw=="], + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.52.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-Q/dv9Yvyr5rKlK8WQJZVrp5g2SOYeZUs9u/t2f9cQ2E0gJjYB/BWoedXfUT0EcDJefi2zzVfhcOj8drWCzTviw=="], - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.46.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-uluObTmgPJDuJh9xqxyr7MV61Imq+0IvVsAlWyvxAaBSNzCcmZlhfYcRhCdMaCsy46ccZa7vtDDripgs9Jkqsw=="], + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.52.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kdBsLs4Uile/fbjZVvCRcKB4q64R+1mUq0Yd7oU1CMm1Av336ajIFqNFovByipciuUQjBCPMxwJhCgfG2re3rg=="], - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.46.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-AVJXEq9RVHQnejdbFvh1eWEoobohUYN3nqJIPI4mNTMpsyYN01VvcAClxflyk2HIxvLpRcRggpX1m9hkXkpC/A=="], + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.52.0", "", { "os": "linux", "cpu": "arm" }, "sha512-aL6hRwu0k7MTUESgkg7QHY6CoqPgr6gdQXRJI1/VbFlUMwsSzPGSR7sG5d+MCbYnJmJwThc2ol3nixj1fvI/zQ=="], - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.46.3", "", { "os": "linux", "cpu": "arm" }, "sha512-byyflM+huiwHlKi7VHLAYTKr67X199+V+mt1iRgJenAI594vcmGGddWlu6eHujmcdl6TqSNnvqaXJqZdnEWRGA=="], + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.52.0", "", { "os": "linux", "cpu": "arm" }, "sha512-BTs0M5s1EJejgIBJhCeiFo7GZZ2IXWkFGcyZhxX4+8usnIo5Mti57108vjXFIQmmJaRyDwmV59Tw64Ap1dkwMw=="], - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.46.3", "", { "os": "linux", "cpu": "arm" }, "sha512-aLm3NMIjr4Y9LklrH5cu7yybBqoVCdr4Nvnm8WB7PKCn34fMCGypVNpGK0JQWdPAzR/FnoEoFtlRqZbBBLhVoQ=="], + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.52.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-uj672IVOU9m08DBGvoPKPi/J8jlVgjh12C9GmjjBxCTQc3XtVmRkRKyeHSmIKQpvJ7fIm1EJieBUcnGSzDVFyw=="], - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.46.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-VtilE6eznJRDIoFOzaagQodUksTEfLIsvXymS+UdJiSXrPW7Ai+WG4uapAc3F7Hgs791TwdGh4xyOzbuzIZrnw=="], + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.52.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-/+IVbeDMDCtB/HP/wiWsSzduD10SEGzIZX2945KSgZRNi4TSkjHqRJtNTVtVb8IRwhJ65ssI56krlLik+zFWkw=="], - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.46.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-dG3JuS6+cRAL0GQ925Vppafi0qwZnkHdPeuZIxIPXqkCLP02l7ka+OCyBoDEv8S+nKHxfjvjW4OZ7hTdHkx8/w=="], + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.52.0", "", { "os": "linux", "cpu": "none" }, "sha512-U1vVzvSWtSMWKKrGoROPBXMh3Vwn93TA9V35PldokHGqiUbF6erSzox/5qrSMKp6SzakvyjcPiVF8yB1xKr9Pg=="], - "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.46.3", "", { "os": "linux", "cpu": "none" }, "sha512-iU8DxnxEKJptf8Vcx4XvAUdpkZfaz0KWfRrnIRrOndL0SvzEte+MTM7nDH4A2Now4FvTZ01yFAgj6TX/mZl8hQ=="], + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.52.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-X/4WfuBAdQRH8cK3DYl8zC00XEE6aM472W+QCycpQJeLWVnHfkv7RyBFVaTqNUMsTgIX8ihMjCvFF9OUgeABzw=="], - "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.46.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-VrQZp9tkk0yozJoQvQcqlWiqaPnLM6uY1qPYXvukKePb0fqaiQtOdMJSxNFUZFsGw5oA5vvVokjHrx8a9Qsz2A=="], + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.52.0", "", { "os": "linux", "cpu": "none" }, "sha512-xIRYc58HfWDBZoLmWfWXg2Sq8VCa2iJ32B7mqfWnkx5mekekl0tMe7FHpY8I72RXEcUkaWawRvl3qA55og+cwQ=="], - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.46.3", "", { "os": "linux", "cpu": "none" }, "sha512-uf2eucWSUb+M7b0poZ/08LsbcRgaDYL8NCGjUeFMwCWFwOuFcZ8D9ayPl25P3pl+D2FH45EbHdfyUesQ2Lt9wA=="], + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.52.0", "", { "os": "linux", "cpu": "none" }, "sha512-mbsoUey05WJIOz8U1WzNdf+6UMYGwE3fZZnQqsM22FZ3wh1N887HT6jAOjXs6CNEK3Ntu2OBsyQDXfIjouI4dw=="], - "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.46.3", "", { "os": "linux", "cpu": "none" }, "sha512-7tnUcDvN8DHm/9ra+/nF7lLzYHDeODKKKrh6JmZejbh1FnCNZS8zMkZY5J4sEipy2OW1d1Ncc4gNHUd0DLqkSg=="], + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.52.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-qP6aP970bucEi5KKKR4AuPFd8aTx9EF6BvutvYxmZuWLJHmnq4LvBfp0U+yFDMGwJ+AIJEH5sIP+SNypauMWzg=="], - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.46.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-MUpAOallJim8CsJK+4Lc9tQzlfPbHxWDrGXZm2z6biaadNpvh3a5ewcdat478W+tXDoUiHwErX/dOql7ETcLqg=="], + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.52.0", "", { "os": "linux", "cpu": "x64" }, "sha512-nmSVN+F2i1yKZ7rJNKO3G7ZzmxJgoQBQZ/6c4MuS553Grmr7WqR7LLDcYG53Z2m9409z3JLt4sCOhLdbKQ3HmA=="], - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.46.3", "", { "os": "linux", "cpu": "x64" }, "sha512-F42IgZI4JicE2vM2PWCe0N5mR5vR0gIdORPqhGQ32/u1S1v3kLtbZ0C/mi9FFk7C5T0PgdeyWEPajPjaUpyoKg=="], + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.52.0", "", { "os": "linux", "cpu": "x64" }, "sha512-2d0qRo33G6TfQVjaMR71P+yJVGODrt5V6+T0BDYH4EMfGgdC/2HWDVjSSFw888GSzAZUwuska3+zxNUCDco6rQ=="], - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.46.3", "", { "os": "linux", "cpu": "x64" }, "sha512-oLc+JrwwvbimJUInzx56Q3ujL3Kkhxehg7O1gWAYzm8hImCd5ld1F2Gry5YDjR21MNb5WCKhC9hXgU7rRlyegQ=="], + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.52.0", "", { "os": "none", "cpu": "arm64" }, "sha512-A1JalX4MOaFAAyGgpO7XP5khquv/7xKzLIyLmhNrbiCxWpMlnsTYr8dnsWM7sEeotNmxvSOEL7F65j0HXFcFsw=="], - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.46.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-lOrQ+BVRstruD1fkWg9yjmumhowR0oLAAzavB7yFSaGltY8klttmZtCLvOXCmGE9mLIn8IBV/IFrQOWz5xbFPg=="], + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.52.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-YQugafP/rH0eOOHGjmNgDURrpYHrIX0yuojOI8bwCyXwxC9ZdTd3vYkmddPX0oHONLXu9Rb1dDmT0VNpjkzGGw=="], - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.46.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-vvrVKPRS4GduGR7VMH8EylCBqsDcw6U+/0nPDuIjXQRbHJc6xOBj+frx8ksfZAh6+Fptw5wHrN7etlMmQnPQVg=="], + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.52.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-zYdUYhi3Qe2fndujBqL5FjAFzvNeLxtIqfzNEVKD1I7C37/chv1VxhscWSQHTNfjPCrBFQMnynwA3kpZpZ8w4A=="], - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.46.3", "", { "os": "win32", "cpu": "x64" }, "sha512-fi3cPxCnu3ZeM3EwKZPgXbWoGzm2XHgB/WShKI81uj8wG0+laobmqy5wbgEwzstlbLu4MyO8C19FyhhWseYKNQ=="], + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.52.0", "", { "os": "win32", "cpu": "x64" }, "sha512-fGk03kQylNaCOQ96HDMeT7E2n91EqvCDd3RwvT5k+xNdFCeMGnj5b5hEgTGrQuyidqSsD3zJDQ21QIaxXqTBJw=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.0", "", { "os": "win32", "cpu": "x64" }, "sha512-6iKDCVSIUQ8jPMoIV0OytRKniaYyy5EbY/RRydmLW8ZR3cEBhxbWl5ro0rkUNe0ef6sScvhbY79HrjRm8i3vDQ=="], "@rushstack/node-core-library": ["@rushstack/node-core-library@4.0.2", "", { "dependencies": { "fs-extra": "~7.0.1", "import-lazy": "~4.0.0", "jju": "~1.4.0", "resolve": "~1.22.1", "semver": "~7.5.4", "z-schema": "~5.0.2" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-hyES82QVpkfQMeBMteQUnrhASL/KHPhd7iJ8euduwNJG4mu2GSOKybf0rOEjOm1Wz7CwJEUm9y0yD7jg2C1bfg=="], @@ -431,7 +433,7 @@ "@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="], - "@testing-library/jest-dom": ["@testing-library/jest-dom@6.7.0", "", { "dependencies": { "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.6.3", "picocolors": "^1.1.1", "redent": "^3.0.0" } }, "sha512-RI2e97YZ7MRa+vxP4UUnMuMFL2buSsf0ollxUbTgrbPLKhMn8KVTx7raS6DYjC7v1NDVrioOvaShxsguLNISCA=="], + "@testing-library/jest-dom": ["@testing-library/jest-dom@6.8.0", "", { "dependencies": { "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.6.3", "picocolors": "^1.1.1", "redent": "^3.0.0" } }, "sha512-WgXcWzVM6idy5JaftTVC8Vs83NKRmGJz4Hqs4oyOuO2J4r/y79vvKZsb+CaGyCSEbUPI6OsewfPd0G1A0/TUZQ=="], "@testing-library/react": ["@testing-library/react@16.3.0", "", { "dependencies": { "@babel/runtime": "^7.12.5" }, "peerDependencies": { "@testing-library/dom": "^10.0.0", "@types/react": "^18.0.0 || ^19.0.0", "@types/react-dom": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw=="], @@ -465,7 +467,7 @@ "@types/minimatch": ["@types/minimatch@3.0.5", "", {}, "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ=="], - "@types/node": ["@types/node@20.19.11", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-uug3FEEGv0r+jrecvUUpbY8lLisvIjg6AAic6a2bSP5OEOLeJsDSnvhCDov7ipFFMXS3orMpzlmi0ZcuGkBbow=="], + "@types/node": ["@types/node@20.19.17", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-gfehUI8N1z92kygssiuWvLiwcbOB3IRktR6hTDgJlXMYh5OvkPSRmgfoBUmfZt+vhwJtX7v1Yw4KvvAf7c5QKQ=="], "@types/phoenix": ["@types/phoenix@1.6.6", "", {}, "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A=="], @@ -473,9 +475,9 @@ "@types/randomcolor": ["@types/randomcolor@0.5.9", "", {}, "sha512-k58cfpkK15AKn1m+oRd9nh5BnuiowhbyvBBdAzcddtARMr3xRzP0VlFaAKovSG6N6Knx08EicjPlOMzDejerrQ=="], - "@types/react": ["@types/react@18.3.23", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w=="], + "@types/react": ["@types/react@18.3.24", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, "sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A=="], - "@types/react-dom": ["@types/react-dom@19.1.7", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw=="], + "@types/react-dom": ["@types/react-dom@19.1.9", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ=="], "@types/react-is": ["@types/react-is@18.3.1", "", { "dependencies": { "@types/react": "^18" } }, "sha512-zts4lhQn5ia0cF/y2+3V6Riu0MAfez9/LJYavdM8TvcVl+S91A/7VWxyBT8hbRuWspmuCaiGI0F41OJYGrKhRA=="], @@ -507,13 +509,13 @@ "@volar/typescript": ["@volar/typescript@1.11.1", "", { "dependencies": { "@volar/language-core": "1.11.1", "path-browserify": "^1.0.1" } }, "sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ=="], - "@vue/compiler-core": ["@vue/compiler-core@3.5.18", "", { "dependencies": { "@babel/parser": "^7.28.0", "@vue/shared": "3.5.18", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw=="], + "@vue/compiler-core": ["@vue/compiler-core@3.5.21", "", { "dependencies": { "@babel/parser": "^7.28.3", "@vue/shared": "3.5.21", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw=="], - "@vue/compiler-dom": ["@vue/compiler-dom@3.5.18", "", { "dependencies": { "@vue/compiler-core": "3.5.18", "@vue/shared": "3.5.18" } }, "sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A=="], + "@vue/compiler-dom": ["@vue/compiler-dom@3.5.21", "", { "dependencies": { "@vue/compiler-core": "3.5.21", "@vue/shared": "3.5.21" } }, "sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ=="], "@vue/language-core": ["@vue/language-core@1.8.27", "", { "dependencies": { "@volar/language-core": "~1.11.1", "@volar/source-map": "~1.11.1", "@vue/compiler-dom": "^3.3.0", "@vue/shared": "^3.3.0", "computeds": "^0.0.1", "minimatch": "^9.0.3", "muggle-string": "^0.3.1", "path-browserify": "^1.0.1", "vue-template-compiler": "^2.7.14" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA=="], - "@vue/shared": ["@vue/shared@3.5.18", "", {}, "sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA=="], + "@vue/shared": ["@vue/shared@3.5.21", "", {}, "sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw=="], "@webext-core/fake-browser": ["@webext-core/fake-browser@1.3.2", "", { "dependencies": { "lodash.merge": "^4.6.2" } }, "sha512-jFyPWWz+VkHAC9DRIiIPOyu6X/KlC8dYqSKweHz6tsDb86QawtVgZSpYcM+GOQBlZc5DHFo92jJ7cIq4uBnU0A=="], @@ -523,9 +525,9 @@ "@wxt-dev/browser": ["@wxt-dev/browser@0.1.4", "", { "dependencies": { "@types/filesystem": "*", "@types/har-format": "*" } }, "sha512-9x03I15i79XU8qYwjv4le0K2HdMl/Yga2wUBSoUbcrCnamv8P3nvuYxREQ9C5QY/qPAfeEVdAtaTrS3KWak71g=="], - "@wxt-dev/module-react": ["@wxt-dev/module-react@1.1.3", "", { "dependencies": { "@vitejs/plugin-react": "^4.3.4" }, "peerDependencies": { "wxt": ">=0.19.16" } }, "sha512-ede2FLS3sdJwtyI61jvY1UiF194ouv3wxm+fCYjfP4FfvoXQbif8UuusYBC0KSa/L2AL9Cfa/lEvsdNYrKFUaA=="], + "@wxt-dev/module-react": ["@wxt-dev/module-react@1.1.5", "", { "dependencies": { "@vitejs/plugin-react": "^4.4.1 || ^5.0.0" }, "peerDependencies": { "wxt": ">=0.19.16" } }, "sha512-KgsUrsgH5rBT8MwiipnDEOHBXmLvTIdFICrI7KjngqSf9DpVRn92HsKmToxY0AYpkP19hHWta2oNYFTzmmm++g=="], - "@wxt-dev/storage": ["@wxt-dev/storage@1.1.1", "", { "dependencies": { "async-mutex": "^0.5.0", "dequal": "^2.0.3" } }, "sha512-H1vYWeoWz03INV4r+sLYDFil88b3rgMMfgGp/EXy3bLbveJeiMiFs/G0bsBN2Ra87Iqlf2oVYRb/ABQpAugbew=="], + "@wxt-dev/storage": ["@wxt-dev/storage@1.2.0", "", { "dependencies": { "@wxt-dev/browser": "^0.1.4", "async-mutex": "^0.5.0", "dequal": "^2.0.3" } }, "sha512-4A44zCpwl5GZdmUdSJvUWJ6ekZZ+Fz5ttYqTGPIRJSsyosKX8X8Yl7D2Loy1ZlqIg6oJHysaiFXALtTE+pFjpw=="], "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], @@ -541,7 +543,7 @@ "ansi-colors": ["ansi-colors@4.1.3", "", {}, "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="], - "ansi-escapes": ["ansi-escapes@7.0.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw=="], + "ansi-escapes": ["ansi-escapes@7.1.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-YdhtCd19sKRKfAAUsrcC1wzm4JuzJoiX4pOJqIoW2qmKj5WzG/dL8uUJ0361zaXtHqK7gEhOwtAtz7t3Yq3X5g=="], "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], @@ -573,9 +575,9 @@ "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], - "better-path-resolve": ["better-path-resolve@1.0.0", "", { "dependencies": { "is-windows": "^1.0.0" } }, "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.8.6", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw=="], - "big-integer": ["big-integer@1.6.52", "", {}, "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg=="], + "better-path-resolve": ["better-path-resolve@1.0.0", "", { "dependencies": { "is-windows": "^1.0.0" } }, "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g=="], "bl": ["bl@5.1.0", "", { "dependencies": { "buffer": "^6.0.3", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ=="], @@ -585,13 +587,11 @@ "boxen": ["boxen@8.0.1", "", { "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^8.0.0", "chalk": "^5.3.0", "cli-boxes": "^3.0.0", "string-width": "^7.2.0", "type-fest": "^4.21.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0" } }, "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw=="], - "bplist-parser": ["bplist-parser@0.2.0", "", { "dependencies": { "big-integer": "^1.6.44" } }, "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw=="], - "brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], - "browserslist": ["browserslist@4.25.3", "", { "dependencies": { "caniuse-lite": "^1.0.30001735", "electron-to-chromium": "^1.5.204", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ=="], + "browserslist": ["browserslist@4.26.2", "", { "dependencies": { "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001741", "electron-to-chromium": "^1.5.218", "node-releases": "^2.0.21", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A=="], "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], @@ -601,21 +601,21 @@ "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="], - "c12": ["c12@3.2.0", "", { "dependencies": { "chokidar": "^4.0.3", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^17.2.1", "exsolve": "^1.0.7", "giget": "^2.0.0", "jiti": "^2.5.1", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^1.0.0", "pkg-types": "^2.2.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "^0.3.5" }, "optionalPeers": ["magicast"] }, "sha512-ixkEtbYafL56E6HiFuonMm1ZjoKtIo7TH68/uiEq4DAwv9NcUX2nJ95F8TrbMeNjqIkZpruo3ojXQJ+MGG5gcQ=="], + "c12": ["c12@3.3.0", "", { "dependencies": { "chokidar": "^4.0.3", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^17.2.2", "exsolve": "^1.0.7", "giget": "^2.0.0", "jiti": "^2.5.1", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^2.0.0", "pkg-types": "^2.3.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "^0.3.5" }, "optionalPeers": ["magicast"] }, "sha512-K9ZkuyeJQeqLEyqldbYLG3wjqwpw4BVaAqvmxq3GYKK0b1A/yYQdIcJxkzAOWcNVWhJpRXAPfZFueekiY/L8Dw=="], "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], "camelcase": ["camelcase@8.0.0", "", {}, "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA=="], - "caniuse-lite": ["caniuse-lite@1.0.30001735", "", {}, "sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w=="], + "caniuse-lite": ["caniuse-lite@1.0.30001743", "", {}, "sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw=="], "canvas-confetti": ["canvas-confetti@1.9.3", "", {}, "sha512-rFfTURMvmVEX1gyXFgn5QMn81bYk70qa0HLzcIOSVEyl57n6o9ItHeBtUSWdvKAPY0xlvBHno4/v3QPrT83q9g=="], "capnp-ts": ["capnp-ts@0.7.0", "", { "dependencies": { "debug": "^4.3.1", "tslib": "^2.2.0" } }, "sha512-XKxXAC3HVPv7r674zP0VC3RTXz+/JKhfyw94ljvF80yynK6VkTnqE3jMuN8b3dUVmmc43TjyxjW4KTsmB3c86g=="], - "chai": ["chai@5.3.1", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-48af6xm9gQK8rhIcOxWwdGzIervm8BVTin+yRp9HEvU20BtVZ2lBywlIJBzwaDtvo0FvjeL7QdCADoUoqIbV3A=="], + "chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], - "chalk": ["chalk@5.6.0", "", {}, "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ=="], + "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], "chardet": ["chardet@2.1.0", "", {}, "sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA=="], @@ -623,7 +623,7 @@ "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], - "chrome-launcher": ["chrome-launcher@1.1.2", "", { "dependencies": { "@types/node": "*", "escape-string-regexp": "^4.0.0", "is-wsl": "^2.2.0", "lighthouse-logger": "^2.0.1" }, "bin": { "print-chrome-path": "bin/print-chrome-path.js" } }, "sha512-YclTJey34KUm5jB1aEJCq807bSievi7Nb/TU4Gu504fUYi3jw3KCIaH6L7nFWQhdEgH3V+wCh+kKD1P5cXnfxw=="], + "chrome-launcher": ["chrome-launcher@1.2.0", "", { "dependencies": { "@types/node": "*", "escape-string-regexp": "^4.0.0", "is-wsl": "^2.2.0", "lighthouse-logger": "^2.0.1" }, "bin": { "print-chrome-path": "bin/print-chrome-path.cjs" } }, "sha512-JbuGuBNss258bvGil7FT4HKdC3SC2K7UAEUqiPy3ACS3Yxo3hAW6bvFpCu2HsIJLgTqxgEX6BkujvzZfLpUD0Q=="], "ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], @@ -667,7 +667,7 @@ "config-chain": ["config-chain@1.1.13", "", { "dependencies": { "ini": "^1.3.4", "proto-list": "~1.2.1" } }, "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ=="], - "configstore": ["configstore@7.0.0", "", { "dependencies": { "atomically": "^2.0.3", "dot-prop": "^9.0.0", "graceful-fs": "^4.2.11", "xdg-basedir": "^5.1.0" } }, "sha512-yk7/5PN5im4qwz0WFZW3PXnzHgPu9mX29Y8uZ3aefe2lBPC1FYttWZRcaW9fKkT0pBCJyuQ2HfbmPVaODi9jcQ=="], + "configstore": ["configstore@7.1.0", "", { "dependencies": { "atomically": "^2.0.3", "dot-prop": "^9.0.0", "graceful-fs": "^4.2.11", "xdg-basedir": "^5.1.0" } }, "sha512-N4oog6YJWbR9kGyXvS7jEykLDXIE2C0ILYqNBZBp9iwiJpoCBWYsuAdW6PPFn6w06jjnC+3JstVvWHO4cZqvRg=="], "connect-history-api-fallback": ["connect-history-api-fallback@1.6.0", "", {}, "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg=="], @@ -701,7 +701,7 @@ "debounce": ["debounce@1.2.1", "", {}, "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="], - "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], "decimal.js": ["decimal.js@10.6.0", "", {}, "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="], @@ -741,13 +741,13 @@ "dot-prop": ["dot-prop@9.0.0", "", { "dependencies": { "type-fest": "^4.18.2" } }, "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ=="], - "dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], + "dotenv": ["dotenv@17.2.2", "", {}, "sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q=="], - "dotenv-expand": ["dotenv-expand@12.0.2", "", { "dependencies": { "dotenv": "^16.4.5" } }, "sha512-lXpXz2ZE1cea1gL4sz2Ipj8y4PiVjytYr3Ij0SWoms1PGxIv7m2CRKuRuCRtHdVuvM/hNJPMxt5PbhboNC4dPQ=="], + "dotenv-expand": ["dotenv-expand@12.0.3", "", { "dependencies": { "dotenv": "^16.4.5" } }, "sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA=="], "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], - "electron-to-chromium": ["electron-to-chromium@1.5.207", "", {}, "sha512-mryFrrL/GXDTmAtIVMVf+eIXM09BBPlO5IQ7lUyKmK8d+A4VpRGG+M3ofoVef6qyF8s60rJei8ymlJxjUA8Faw=="], + "electron-to-chromium": ["electron-to-chromium@1.5.222", "", {}, "sha512-gA7psSwSwQRE60CEoLz6JBCQPIxNeuzB2nL8vE03GK/OHxlvykbLyeiumQy1iH5C2f3YbRAZpGCMT12a/9ih9w=="], "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], @@ -759,13 +759,13 @@ "environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="], - "error-ex": ["error-ex@1.3.2", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="], + "error-ex": ["error-ex@1.3.4", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ=="], "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], "es6-error": ["es6-error@4.1.1", "", {}, "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg=="], - "esbuild": ["esbuild@0.25.9", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.9", "@esbuild/android-arm": "0.25.9", "@esbuild/android-arm64": "0.25.9", "@esbuild/android-x64": "0.25.9", "@esbuild/darwin-arm64": "0.25.9", "@esbuild/darwin-x64": "0.25.9", "@esbuild/freebsd-arm64": "0.25.9", "@esbuild/freebsd-x64": "0.25.9", "@esbuild/linux-arm": "0.25.9", "@esbuild/linux-arm64": "0.25.9", "@esbuild/linux-ia32": "0.25.9", "@esbuild/linux-loong64": "0.25.9", "@esbuild/linux-mips64el": "0.25.9", "@esbuild/linux-ppc64": "0.25.9", "@esbuild/linux-riscv64": "0.25.9", "@esbuild/linux-s390x": "0.25.9", "@esbuild/linux-x64": "0.25.9", "@esbuild/netbsd-arm64": "0.25.9", "@esbuild/netbsd-x64": "0.25.9", "@esbuild/openbsd-arm64": "0.25.9", "@esbuild/openbsd-x64": "0.25.9", "@esbuild/openharmony-arm64": "0.25.9", "@esbuild/sunos-x64": "0.25.9", "@esbuild/win32-arm64": "0.25.9", "@esbuild/win32-ia32": "0.25.9", "@esbuild/win32-x64": "0.25.9" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g=="], + "esbuild": ["esbuild@0.25.10", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.10", "@esbuild/android-arm": "0.25.10", "@esbuild/android-arm64": "0.25.10", "@esbuild/android-x64": "0.25.10", "@esbuild/darwin-arm64": "0.25.10", "@esbuild/darwin-x64": "0.25.10", "@esbuild/freebsd-arm64": "0.25.10", "@esbuild/freebsd-x64": "0.25.10", "@esbuild/linux-arm": "0.25.10", "@esbuild/linux-arm64": "0.25.10", "@esbuild/linux-ia32": "0.25.10", "@esbuild/linux-loong64": "0.25.10", "@esbuild/linux-mips64el": "0.25.10", "@esbuild/linux-ppc64": "0.25.10", "@esbuild/linux-riscv64": "0.25.10", "@esbuild/linux-s390x": "0.25.10", "@esbuild/linux-x64": "0.25.10", "@esbuild/netbsd-arm64": "0.25.10", "@esbuild/netbsd-x64": "0.25.10", "@esbuild/openbsd-arm64": "0.25.10", "@esbuild/openbsd-x64": "0.25.10", "@esbuild/openharmony-arm64": "0.25.10", "@esbuild/sunos-x64": "0.25.10", "@esbuild/win32-arm64": "0.25.10", "@esbuild/win32-ia32": "0.25.10", "@esbuild/win32-x64": "0.25.10" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ=="], "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], @@ -805,7 +805,7 @@ "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], - "filesize": ["filesize@10.1.6", "", {}, "sha512-sJslQKU2uM33qH5nqewAwVB2QgR6w1aMNsYUp3aN5rMRyXEwJGmZvaWzeJFNTOXWlHQyBFCWrdj3fV/fsTOX8w=="], + "filesize": ["filesize@11.0.13", "", {}, "sha512-mYJ/qXKvREuO0uH8LTQJ6v7GsUvVOguqxg2VTwQUkyTPXXRRWPdjuUPVqdBrJQhvci48OHlNGRnux+Slr2Rnvw=="], "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], @@ -831,7 +831,7 @@ "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], - "get-east-asian-width": ["get-east-asian-width@1.3.0", "", {}, "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ=="], + "get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="], "get-port-please": ["get-port-please@3.2.0", "", {}, "sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A=="], @@ -881,7 +881,7 @@ "human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="], - "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + "iconv-lite": ["iconv-lite@0.7.0", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ=="], "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], @@ -893,7 +893,7 @@ "import-lazy": ["import-lazy@4.0.0", "", {}, "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw=="], - "import-meta-resolve": ["import-meta-resolve@4.1.0", "", {}, "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw=="], + "import-meta-resolve": ["import-meta-resolve@4.2.0", "", {}, "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg=="], "indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="], @@ -927,7 +927,7 @@ "is-interactive": ["is-interactive@2.0.0", "", {}, "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ=="], - "is-npm": ["is-npm@6.0.0", "", {}, "sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ=="], + "is-npm": ["is-npm@6.1.0", "", {}, "sha512-O2z4/kNgyjhQwVR1Wpkbfc19JIhggF97NZNCpWTnjH7kVcZMUrnut9XSN7txI7VdyIYk5ZatOq3zvSuWpU8hoA=="], "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], @@ -989,7 +989,7 @@ "kolorist": ["kolorist@1.8.0", "", {}, "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ=="], - "ky": ["ky@1.9.0", "", {}, "sha512-NgBeR/cu7kuC4BAeF1rnXhfoI2uQ9RBe8zl5vo87ASsf1iIQoCeOxyt6Io6K4Ki++5ItCavXAtbEWWCGFciQ6g=="], + "ky": ["ky@1.10.0", "", {}, "sha512-YRPCzHEWZffbfvmRrfwa+5nwBHwZuYiTrfDX0wuhGBPV0pA/zCqcOq93MDssON/baIkpYbvehIX5aLpMxrRhaA=="], "latest-version": ["latest-version@9.0.0", "", { "dependencies": { "package-json": "^10.0.0" } }, "sha512-7W0vV3rqv5tokqkBAFV1LbR7HPOWzXQDpDgEuib/aJ1jsZZx6x3c2mBI+TJhJzOhkGeaLbCKEHXEXLfirtG2JA=="], @@ -1033,13 +1033,13 @@ "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], - "loupe": ["loupe@3.2.0", "", {}, "sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw=="], + "loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], - "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + "lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], - "magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="], + "magic-string": ["magic-string@0.30.19", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw=="], "magicast": ["magicast@0.3.5", "", { "dependencies": { "@babel/parser": "^7.25.4", "@babel/types": "^7.25.4", "source-map-js": "^1.2.0" } }, "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ=="], @@ -1083,7 +1083,7 @@ "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], - "nano-spawn": ["nano-spawn@0.2.1", "", {}, "sha512-/pULofvsF8mOVcl/nUeVXL/GYOEvc7eJWSIxa+K4OYUolvXa5zwSgevsn4eoHs1xvh/BO3vx/PZiD9+Ow2ZVuw=="], + "nano-spawn": ["nano-spawn@1.0.3", "", {}, "sha512-jtpsQDetTnvS2Ts1fiRdci5rx0VYws5jGyC+4IYOTnIQ/wwdf6JdomlHBwqC3bJYOvaKu0C2GSZ1A60anrYpaA=="], "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], @@ -1095,7 +1095,7 @@ "node-notifier": ["node-notifier@10.0.1", "", { "dependencies": { "growly": "^1.3.0", "is-wsl": "^2.2.0", "semver": "^7.3.5", "shellwords": "^0.1.1", "uuid": "^8.3.2", "which": "^2.0.2" } }, "sha512-YX7TSyDukOZ0g+gmzjB6abKu+hTGvO8+8+gIFDsRCU2t8fLV/P2unmt+LGFaIa4y64aX98Qksa97rgz4vMNeLQ=="], - "node-releases": ["node-releases@2.0.19", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="], + "node-releases": ["node-releases@2.0.21", "", {}, "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw=="], "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], @@ -1103,9 +1103,9 @@ "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], - "nwsapi": ["nwsapi@2.2.21", "", {}, "sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA=="], + "nwsapi": ["nwsapi@2.2.22", "", {}, "sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ=="], - "nypm": ["nypm@0.6.1", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.2", "pathe": "^2.0.3", "pkg-types": "^2.2.0", "tinyexec": "^1.0.1" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-hlacBiRiv1k9hZFiphPUkfSQ/ZfQzZDzC+8z0wL3lvDAOUu/2NnChkKuMoMjNur/9OpKuz2QsIeiPVN0xM5Q0w=="], + "nypm": ["nypm@0.6.2", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.2", "pathe": "^2.0.3", "pkg-types": "^2.3.0", "tinyexec": "^1.0.1" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g=="], "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], @@ -1173,7 +1173,7 @@ "pend": ["pend@1.2.0", "", {}, "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="], - "perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="], + "perfect-debounce": ["perfect-debounce@2.0.0", "", {}, "sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], @@ -1181,7 +1181,7 @@ "pify": ["pify@4.0.1", "", {}, "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g=="], - "pino": ["pino@9.6.0", "", { "dependencies": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.1.1", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^2.0.0", "pino-std-serializers": "^7.0.0", "process-warning": "^4.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^4.0.1", "thread-stream": "^3.0.0" }, "bin": { "pino": "bin.js" } }, "sha512-i85pKRCt4qMjZ1+L7sy2Ag4t1atFcdbEt76+7iRJn1g2BvsnRMGu9p8pivl9fs63M2kF/A0OacFZhTub+m/qMg=="], + "pino": ["pino@9.7.0", "", { "dependencies": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.1.1", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^2.0.0", "pino-std-serializers": "^7.0.0", "process-warning": "^5.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^4.0.1", "thread-stream": "^3.0.0" }, "bin": { "pino": "bin.js" } }, "sha512-vnMCM6xZTb1WDmLvtG2lE/2p+t9hDEIvTWJsu6FejkE62vB7gDhvzrpFR4Cw2to+9JNQxVnkAKVPA1KPB98vWg=="], "pino-abstract-transport": ["pino-abstract-transport@2.0.0", "", { "dependencies": { "split2": "^4.0.0" } }, "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw=="], @@ -1201,7 +1201,7 @@ "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], - "process-warning": ["process-warning@4.0.1", "", {}, "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q=="], + "process-warning": ["process-warning@5.0.0", "", {}, "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA=="], "profane-words": ["profane-words@1.6.0", "", {}, "sha512-BJa9m5yfTCjV+m7KxqGlK49lugCnpbEQ9MJkp/SCpVwKWq4Wiw5G0EaWcQgzY5xPtheRmM1MjF95/uqGV3oQlQ=="], @@ -1213,13 +1213,13 @@ "proto-list": ["proto-list@1.2.4", "", {}, "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA=="], - "publish-browser-extension": ["publish-browser-extension@3.0.1", "", { "dependencies": { "cac": "^6.7.14", "cli-highlight": "^2.1.11", "consola": "^3.2.3", "dotenv": "^16.3.1", "extract-zip": "^2.0.1", "formdata-node": "^6.0.3", "listr2": "^8.0.1", "lodash.camelcase": "^4.3.0", "lodash.kebabcase": "^4.1.1", "lodash.snakecase": "^4.1.1", "ofetch": "^1.3.3", "open": "^9.1.0", "ora": "^6.3.1", "prompts": "^2.4.2", "zod": "^3.22.4" }, "bin": { "publish-extension": "bin/publish-extension.cjs" } }, "sha512-k0Ljop/AIGlX0M+hrYjjL/fCaFy0TmkqQGcK3uHN3ZbxYWzivF412nGco6tRawb6Nxe/fPxWh3OaewaH+l03VA=="], + "publish-browser-extension": ["publish-browser-extension@3.0.2", "", { "dependencies": { "cac": "^6.7.14", "cli-highlight": "^2.1.11", "consola": "^3.4.2", "dotenv": "^16.6.1", "extract-zip": "^2.0.1", "formdata-node": "^6.0.3", "listr2": "^8.3.3", "lodash.camelcase": "^4.3.0", "lodash.kebabcase": "^4.1.1", "lodash.snakecase": "^4.1.1", "ofetch": "^1.4.1", "open": "^10.2.0", "ora": "^6.3.1", "prompts": "^2.4.2", "zod": "^3.25.76" }, "bin": { "publish-extension": "bin/publish-extension.cjs" } }, "sha512-yZLPF/WyyaKYUHmurDcSMYpgZLqpUkx/4482bLpelHyRlyghjo3951pJXw/KunMnO6pdwWEZGr0AJnvlls2H8g=="], "pump": ["pump@3.0.3", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA=="], "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], - "pupa": ["pupa@3.1.0", "", { "dependencies": { "escape-goat": "^4.0.0" } }, "sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug=="], + "pupa": ["pupa@3.3.0", "", { "dependencies": { "escape-goat": "^4.0.0" } }, "sha512-LjgDO2zPtoXP2wJpDjZrGdojii1uqO0cnwKoIoUzkfS98HDmbeiGmYiXo3lXeFlq2xvne1QFQhwYXSUCLKtEuA=="], "quansync": ["quansync@0.2.11", "", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="], @@ -1257,8 +1257,6 @@ "redent": ["redent@3.0.0", "", { "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" } }, "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg=="], - "regenerator-runtime": ["regenerator-runtime@0.14.1", "", {}, "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="], - "registry-auth-token": ["registry-auth-token@5.1.0", "", { "dependencies": { "@pnpm/npm-conf": "^2.1.0" } }, "sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw=="], "registry-url": ["registry-url@6.0.1", "", { "dependencies": { "rc": "1.2.8" } }, "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q=="], @@ -1275,11 +1273,11 @@ "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], - "rollup": ["rollup@4.46.3", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.46.3", "@rollup/rollup-android-arm64": "4.46.3", "@rollup/rollup-darwin-arm64": "4.46.3", "@rollup/rollup-darwin-x64": "4.46.3", "@rollup/rollup-freebsd-arm64": "4.46.3", "@rollup/rollup-freebsd-x64": "4.46.3", "@rollup/rollup-linux-arm-gnueabihf": "4.46.3", "@rollup/rollup-linux-arm-musleabihf": "4.46.3", "@rollup/rollup-linux-arm64-gnu": "4.46.3", "@rollup/rollup-linux-arm64-musl": "4.46.3", "@rollup/rollup-linux-loongarch64-gnu": "4.46.3", "@rollup/rollup-linux-ppc64-gnu": "4.46.3", "@rollup/rollup-linux-riscv64-gnu": "4.46.3", "@rollup/rollup-linux-riscv64-musl": "4.46.3", "@rollup/rollup-linux-s390x-gnu": "4.46.3", "@rollup/rollup-linux-x64-gnu": "4.46.3", "@rollup/rollup-linux-x64-musl": "4.46.3", "@rollup/rollup-win32-arm64-msvc": "4.46.3", "@rollup/rollup-win32-ia32-msvc": "4.46.3", "@rollup/rollup-win32-x64-msvc": "4.46.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-RZn2XTjXb8t5g13f5YclGoilU/kwT696DIkY3sywjdZidNSi3+vseaQov7D7BZXVJCPv3pDWUN69C78GGbXsKw=="], + "rollup": ["rollup@4.52.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.0", "@rollup/rollup-android-arm64": "4.52.0", "@rollup/rollup-darwin-arm64": "4.52.0", "@rollup/rollup-darwin-x64": "4.52.0", "@rollup/rollup-freebsd-arm64": "4.52.0", "@rollup/rollup-freebsd-x64": "4.52.0", "@rollup/rollup-linux-arm-gnueabihf": "4.52.0", "@rollup/rollup-linux-arm-musleabihf": "4.52.0", "@rollup/rollup-linux-arm64-gnu": "4.52.0", "@rollup/rollup-linux-arm64-musl": "4.52.0", "@rollup/rollup-linux-loong64-gnu": "4.52.0", "@rollup/rollup-linux-ppc64-gnu": "4.52.0", "@rollup/rollup-linux-riscv64-gnu": "4.52.0", "@rollup/rollup-linux-riscv64-musl": "4.52.0", "@rollup/rollup-linux-s390x-gnu": "4.52.0", "@rollup/rollup-linux-x64-gnu": "4.52.0", "@rollup/rollup-linux-x64-musl": "4.52.0", "@rollup/rollup-openharmony-arm64": "4.52.0", "@rollup/rollup-win32-arm64-msvc": "4.52.0", "@rollup/rollup-win32-ia32-msvc": "4.52.0", "@rollup/rollup-win32-x64-gnu": "4.52.0", "@rollup/rollup-win32-x64-msvc": "4.52.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-+IuescNkTJQgX7AkIDtITipZdIGcWF0pnVvZTWStiazUmcGA2ag8dfg0urest2XlXUi9kuhfQ+qmdc5Stc3z7g=="], "rrweb-cssom": ["rrweb-cssom@0.8.0", "", {}, "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw=="], - "run-applescript": ["run-applescript@7.0.0", "", {}, "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A=="], + "run-applescript": ["run-applescript@7.1.0", "", {}, "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q=="], "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], @@ -1289,7 +1287,7 @@ "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], - "sass": ["sass@1.90.0", "", { "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" }, "bin": { "sass": "sass.js" } }, "sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q=="], + "sass": ["sass@1.93.0", "", { "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" }, "bin": { "sass": "sass.js" } }, "sha512-CQi5/AzCwiubU3dSqRDJ93RfOfg/hhpW1l6wCIvolmehfwgCI35R/0QDs1+R+Ygrl8jFawwwIojE2w47/mf94A=="], "sax": ["sax@1.4.1", "", {}, "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg=="], @@ -1299,7 +1297,7 @@ "scule": ["scule@1.3.0", "", {}, "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g=="], - "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + "semver": ["semver@7.5.4", "", { "dependencies": { "lru-cache": "^6.0.0" }, "bin": { "semver": "bin/semver.js" } }, "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA=="], "set-value": ["set-value@4.1.0", "", { "dependencies": { "is-plain-object": "^2.0.4", "is-primitive": "^3.0.1" } }, "sha512-zTEg4HL0RwVrqcWs3ztF+x1vkxfm0lP+MQQFPiMJTKVceBwEV0A569Ou8l9IYQG8jOZdMVI1hGsc0tmeD2o/Lw=="], @@ -1371,7 +1369,7 @@ "strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="], - "strip-json-comments": ["strip-json-comments@5.0.1", "", {}, "sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw=="], + "strip-json-comments": ["strip-json-comments@5.0.2", "", {}, "sha512-4X2FR3UwhNUE9G49aIsJW5hRRR3GXGTBTZRMfv568O60ojM8HcWjV/VxAxCDW3SUND33O6ZY66ZuRcdkj73q2g=="], "strip-literal": ["strip-literal@3.0.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA=="], @@ -1399,7 +1397,7 @@ "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], - "tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="], + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], "tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], @@ -1407,13 +1405,11 @@ "tinyspy": ["tinyspy@3.0.2", "", {}, "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q=="], - "titleize": ["titleize@3.0.0", "", {}, "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ=="], - "tldts": ["tldts@6.1.86", "", { "dependencies": { "tldts-core": "^6.1.86" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ=="], "tldts-core": ["tldts-core@6.1.86", "", {}, "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA=="], - "tmp": ["tmp@0.2.3", "", {}, "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w=="], + "tmp": ["tmp@0.2.5", "", {}, "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow=="], "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], @@ -1437,15 +1433,13 @@ "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "unimport": ["unimport@5.2.0", "", { "dependencies": { "acorn": "^8.15.0", "escape-string-regexp": "^5.0.0", "estree-walker": "^3.0.3", "local-pkg": "^1.1.1", "magic-string": "^0.30.17", "mlly": "^1.7.4", "pathe": "^2.0.3", "picomatch": "^4.0.3", "pkg-types": "^2.2.0", "scule": "^1.3.0", "strip-literal": "^3.0.0", "tinyglobby": "^0.2.14", "unplugin": "^2.3.5", "unplugin-utils": "^0.2.4" } }, "sha512-bTuAMMOOqIAyjV4i4UH7P07pO+EsVxmhOzQ2YJ290J6mkLUdozNhb5I/YoOEheeNADC03ent3Qj07X0fWfUpmw=="], + "unimport": ["unimport@5.3.0", "", { "dependencies": { "acorn": "^8.15.0", "escape-string-regexp": "^5.0.0", "estree-walker": "^3.0.3", "local-pkg": "^1.1.2", "magic-string": "^0.30.19", "mlly": "^1.8.0", "pathe": "^2.0.3", "picomatch": "^4.0.3", "pkg-types": "^2.3.0", "scule": "^1.3.0", "strip-literal": "^3.0.0", "tinyglobby": "^0.2.15", "unplugin": "^2.3.10", "unplugin-utils": "^0.3.0" } }, "sha512-cty7t1DESgm0OPfCy9oyn5u9B5t0tMW6tH6bXTjAGIO3SkJsbg/DXYHjrPrUKqultqbAAoltAfYsuu/FEDocjg=="], "universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], - "unplugin": ["unplugin@2.3.8", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-lkaSIlxceytPyt9yfb1h7L9jDFqwMqvUZeGsKB7Z8QrvAO3xZv2S+xMQQYzxk0AGJHcQhbcvhKEstrMy99jnuQ=="], - - "unplugin-utils": ["unplugin-utils@0.2.5", "", { "dependencies": { "pathe": "^2.0.3", "picomatch": "^4.0.3" } }, "sha512-gwXJnPRewT4rT7sBi/IvxKTjsms7jX7QIDLOClApuZwR49SXbrB1z2NLUZ+vDHyqCj/n58OzRRqaW+B8OZi8vg=="], + "unplugin": ["unplugin@2.3.10", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw=="], - "untildify": ["untildify@4.0.0", "", {}, "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw=="], + "unplugin-utils": ["unplugin-utils@0.3.0", "", { "dependencies": { "pathe": "^2.0.3", "picomatch": "^4.0.3" } }, "sha512-JLoggz+PvLVMJo+jZt97hdIIIZ2yTzGgft9e9q8iMrC4ewufl62ekeW7mixBghonn2gVb/ICjyvlmOCUBnJLQg=="], "update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="], @@ -1459,7 +1453,7 @@ "validator": ["validator@13.15.15", "", {}, "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A=="], - "vite": ["vite@7.1.3", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.14" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-OOUi5zjkDxYrKhTV3V7iKsoS37VUM7v40+HuwEmcrsf11Cdx9y3DIr2Px6liIcZFwt3XSRpQvFpL3WVy7ApkGw=="], + "vite": ["vite@7.1.6", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-SRYIB8t/isTwNn8vMB3MR6E+EQZM/WG1aKmmIUCfDXfVvKfc20ZpamngWHKzAmmu9ppsgxsg4b2I7c90JZudIQ=="], "vite-node": ["vite-node@2.1.9", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.3.7", "es-module-lexer": "^1.5.4", "pathe": "^1.1.2", "vite": "^5.0.0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA=="], @@ -1475,11 +1469,11 @@ "w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="], - "watchpack": ["watchpack@2.4.2", "", { "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" } }, "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw=="], + "watchpack": ["watchpack@2.4.4", "", { "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" } }, "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA=="], "wcwidth": ["wcwidth@1.0.1", "", { "dependencies": { "defaults": "^1.0.3" } }, "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg=="], - "web-ext-run": ["web-ext-run@0.2.3", "", { "dependencies": { "@babel/runtime": "7.27.0", "@devicefarmer/adbkit": "3.3.8", "chrome-launcher": "1.1.2", "debounce": "1.2.1", "es6-error": "4.1.1", "firefox-profile": "4.7.0", "fx-runner": "1.4.0", "multimatch": "6.0.0", "node-notifier": "10.0.1", "parse-json": "7.1.1", "pino": "9.6.0", "promise-toolbox": "0.21.0", "set-value": "4.1.0", "source-map-support": "0.5.21", "strip-bom": "5.0.0", "strip-json-comments": "5.0.1", "tmp": "0.2.3", "update-notifier": "7.3.1", "watchpack": "2.4.2", "ws": "8.18.1", "zip-dir": "2.0.0" } }, "sha512-u/IiZaZ7dHFqTM1MLF27rBy8mS9fEEsqoOKL0u+kQdOLmEioA/0Szp67ADd3WAJZLd8/hO8cFST1IC/YMXKIjQ=="], + "web-ext-run": ["web-ext-run@0.2.4", "", { "dependencies": { "@babel/runtime": "7.28.2", "@devicefarmer/adbkit": "3.3.8", "chrome-launcher": "1.2.0", "debounce": "1.2.1", "es6-error": "4.1.1", "firefox-profile": "4.7.0", "fx-runner": "1.4.0", "multimatch": "6.0.0", "node-notifier": "10.0.1", "parse-json": "7.1.1", "pino": "9.7.0", "promise-toolbox": "0.21.0", "set-value": "4.1.0", "source-map-support": "0.5.21", "strip-bom": "5.0.0", "strip-json-comments": "5.0.2", "tmp": "0.2.5", "update-notifier": "7.3.1", "watchpack": "2.4.4", "zip-dir": "2.0.0" } }, "sha512-rQicL7OwuqWdQWI33JkSXKcp7cuv1mJG8u3jRQwx/8aDsmhbTHs9ZRmNYOL+LX0wX8edIEQX8jj4bB60GoXtKA=="], "webextension-polyfill": ["webextension-polyfill@0.12.0", "", {}, "sha512-97TBmpoWJEE+3nFBQ4VocyCdLKfw54rFaJ6EVQYLBCXqCIpLSZkwGgASpv4oPt9gdKCJ80RJlcmNzNn008Ag6Q=="], @@ -1517,7 +1511,7 @@ "wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="], - "wxt": ["wxt@0.20.8", "", { "dependencies": { "@1natsu/wait-element": "^4.1.2", "@aklinker1/rollup-plugin-visualizer": "5.12.0", "@webext-core/fake-browser": "^1.3.2", "@webext-core/isolated-element": "^1.1.2", "@webext-core/match-patterns": "^1.0.3", "@wxt-dev/browser": "^0.1.1", "@wxt-dev/storage": "^1.0.0", "async-mutex": "^0.5.0", "c12": "^3.0.3", "cac": "^6.7.14", "chokidar": "^4.0.3", "ci-info": "^4.2.0", "consola": "^3.4.2", "defu": "^6.1.4", "dotenv": "^16.5.0", "dotenv-expand": "^12.0.2", "esbuild": "^0.25.0", "fast-glob": "^3.3.3", "filesize": "^10.1.6", "fs-extra": "^11.3.0", "get-port-please": "^3.1.2", "giget": "^1.2.3 || ^2.0.0", "hookable": "^5.5.3", "import-meta-resolve": "^4.1.0", "is-wsl": "^3.1.0", "json5": "^2.2.3", "jszip": "^3.10.1", "linkedom": "^0.18.10", "magicast": "^0.3.5", "minimatch": "^10.0.1", "nano-spawn": "^0.2.0", "normalize-path": "^3.0.0", "nypm": "^0.6.0", "ohash": "^2.0.11", "open": "^10.1.2", "ora": "^8.2.0", "perfect-debounce": "^1.0.0", "picocolors": "^1.1.1", "prompts": "^2.4.2", "publish-browser-extension": "^2.3.0 || ^3.0.0", "scule": "^1.3.0", "unimport": "^3.13.1 || ^4.0.0 || ^5.0.0", "vite": "^5.4.19 || ^6.3.4 || ^7.0.0", "vite-node": "^2.1.4 || ^3.1.2", "web-ext-run": "^0.2.3" }, "bin": { "wxt": "bin/wxt.mjs", "wxt-publish-extension": "bin/wxt-publish-extension.cjs" } }, "sha512-aP6SY7oUK1+uqG5fVdFaBeFTXJ+Av/CRTYM47ljZnSSd18zJhevwU+JU7wFKAJwYomEK1eHE+F9YVAKmA82EbA=="], + "wxt": ["wxt@0.20.11", "", { "dependencies": { "@1natsu/wait-element": "^4.1.2", "@aklinker1/rollup-plugin-visualizer": "5.12.0", "@webext-core/fake-browser": "^1.3.2", "@webext-core/isolated-element": "^1.1.2", "@webext-core/match-patterns": "^1.0.3", "@wxt-dev/browser": "^0.1.4", "@wxt-dev/storage": "^1.0.0", "async-mutex": "^0.5.0", "c12": "^3.2.0", "cac": "^6.7.14", "chokidar": "^4.0.3", "ci-info": "^4.3.0", "consola": "^3.4.2", "defu": "^6.1.4", "dotenv": "^17.2.2", "dotenv-expand": "^12.0.3", "esbuild": "^0.25.0", "fast-glob": "^3.3.3", "filesize": "^11.0.2", "fs-extra": "^11.3.1", "get-port-please": "^3.2.0", "giget": "^1.2.3 || ^2.0.0", "hookable": "^5.5.3", "import-meta-resolve": "^4.2.0", "is-wsl": "^3.1.0", "json5": "^2.2.3", "jszip": "^3.10.1", "linkedom": "^0.18.12", "magicast": "^0.3.5", "minimatch": "^10.0.3", "nano-spawn": "^1.0.2", "normalize-path": "^3.0.0", "nypm": "^0.6.1", "ohash": "^2.0.11", "open": "^10.2.0", "ora": "^8.2.0", "perfect-debounce": "^2.0.0", "picocolors": "^1.1.1", "prompts": "^2.4.2", "publish-browser-extension": "^2.3.0 || ^3.0.2", "scule": "^1.3.0", "unimport": "^3.13.1 || ^4.0.0 || ^5.0.0", "vite": "^5.4.19 || ^6.3.4 || ^7.0.0", "vite-node": "^2.1.4 || ^3.1.2", "web-ext-run": "^0.2.4" }, "bin": { "wxt": "bin/wxt.mjs", "wxt-publish-extension": "bin/wxt-publish-extension.cjs" } }, "sha512-DqqHc/5COs8GR21ii99bANXf/mu6zuDpiXFV1YKNsqO5/PvkrCx5arY0aVPL5IATsuacAnNzdj4eMc1qbzS53Q=="], "xdg-basedir": ["xdg-basedir@5.1.0", "", {}, "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ=="], @@ -1535,9 +1529,7 @@ "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], - "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], - - "yaml": ["yaml@2.8.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw=="], + "yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], "yargs": ["yargs@16.2.0", "", { "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } }, "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw=="], @@ -1565,19 +1557,27 @@ "@aklinker1/rollup-plugin-visualizer/yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], + "@asamuzakjp/css-color/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "@changesets/apply-release-plan/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + + "@changesets/assemble-release-plan/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + + "@changesets/get-dependents-graph/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + "@cspotcode/source-map-support/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="], "@devicefarmer/adbkit/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], - "@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + "@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], "@manypkg/find-root/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], @@ -1589,19 +1589,17 @@ "@microsoft/api-extractor/minimatch": ["minimatch@3.0.8", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q=="], - "@microsoft/api-extractor/semver": ["semver@7.5.4", "", { "dependencies": { "lru-cache": "^6.0.0" }, "bin": { "semver": "bin/semver.js" } }, "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA=="], - "@microsoft/api-extractor/typescript": ["typescript@5.4.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ=="], "@microsoft/tsdoc-config/resolve": ["resolve@1.19.0", "", { "dependencies": { "is-core-module": "^2.1.0", "path-parse": "^1.0.6" } }, "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg=="], - "@playhtml/extension/@types/react": ["@types/react@19.1.11", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-lr3jdBw/BGj49Eps7EvqlUaoeA0xpj3pc0RoJkHpYaCHkVK7i28dKyImLQb3JVlqs3aYSXf7qYuWOW/fgZnTXQ=="], + "@playhtml/extension/@types/react": ["@types/react@19.1.13", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ=="], "@playhtml/extension/react": ["react@19.1.1", "", {}, "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ=="], "@playhtml/extension/react-dom": ["react-dom@19.1.1", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.1" } }, "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw=="], - "@playhtml/react/@types/react": ["@types/react@19.1.11", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-lr3jdBw/BGj49Eps7EvqlUaoeA0xpj3pc0RoJkHpYaCHkVK7i28dKyImLQb3JVlqs3aYSXf7qYuWOW/fgZnTXQ=="], + "@playhtml/react/@types/react": ["@types/react@19.1.13", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ=="], "@playhtml/react/react": ["react@19.1.1", "", {}, "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ=="], @@ -1611,14 +1609,10 @@ "@pnpm/network.ca-file/graceful-fs": ["graceful-fs@4.2.10", "", {}, "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="], - "@rushstack/node-core-library/semver": ["semver@7.5.4", "", { "dependencies": { "lru-cache": "^6.0.0" }, "bin": { "semver": "bin/semver.js" } }, "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA=="], - "@rushstack/rig-package/strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], "@supabase/node-fetch/whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], - "@testing-library/jest-dom/aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], - "@testing-library/jest-dom/dom-accessibility-api": ["dom-accessibility-api@0.6.3", "", {}, "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w=="], "@vitest/mocker/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], @@ -1631,9 +1625,7 @@ "boxen/type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], - "boxen/wrap-ansi": ["wrap-ansi@9.0.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q=="], - - "c12/dotenv": ["dotenv@17.2.1", "", {}, "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ=="], + "boxen/wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], "c12/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], @@ -1655,9 +1647,11 @@ "dot-prop/type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], + "dotenv-expand/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], + "extract-zip/get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="], - "firefox-profile/fs-extra": ["fs-extra@11.3.1", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g=="], + "firefox-profile/fs-extra": ["fs-extra@11.3.2", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A=="], "fx-runner/commander": ["commander@2.9.0", "", { "dependencies": { "graceful-readlink": ">= 1.0.0" } }, "sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A=="], @@ -1669,15 +1663,15 @@ "is-inside-container/is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="], - "listr2/wrap-ansi": ["wrap-ansi@9.0.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q=="], + "listr2/wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], "log-symbols/is-unicode-supported": ["is-unicode-supported@1.3.0", "", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="], - "log-update/slice-ansi": ["slice-ansi@7.1.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg=="], + "log-update/slice-ansi": ["slice-ansi@7.1.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w=="], - "log-update/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + "log-update/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], - "log-update/wrap-ansi": ["wrap-ansi@9.0.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q=="], + "log-update/wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], @@ -1691,8 +1685,6 @@ "node-notifier/is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="], - "node-notifier/semver": ["semver@7.5.4", "", { "dependencies": { "lru-cache": "^6.0.0" }, "bin": { "semver": "bin/semver.js" } }, "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA=="], - "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], "nypm/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], @@ -1701,7 +1693,9 @@ "ora/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], - "ora/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + "ora/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], + + "package-json/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], "parse5-htmlparser2-tree-adapter/parse5": ["parse5@6.0.1", "", {}, "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="], @@ -1709,13 +1703,15 @@ "partykit/esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], + "path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + "pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], "playhtml/vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], "prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], - "publish-browser-extension/open": ["open@9.1.0", "", { "dependencies": { "default-browser": "^4.0.0", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "is-wsl": "^2.2.0" } }, "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg=="], + "publish-browser-extension/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], "publish-browser-extension/ora": ["ora@6.3.1", "", { "dependencies": { "chalk": "^5.0.0", "cli-cursor": "^4.0.0", "cli-spinners": "^2.6.1", "is-interactive": "^2.0.0", "is-unicode-supported": "^1.1.0", "log-symbols": "^5.1.0", "stdin-discarder": "^0.1.0", "strip-ansi": "^7.0.1", "wcwidth": "^1.0.1" } }, "sha512-ERAyNnZOfqM+Ao3RAvIXkYh5joP220yf59gVe2X/cI6SiCxIdi4c9HZKZD8R6q/RDXEje1THBju6iExiSsgJaQ=="], @@ -1729,7 +1725,7 @@ "shelljs/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], - "slice-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], + "slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], "slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="], @@ -1741,34 +1737,32 @@ "unplugin-utils/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - "vite-node/vite": ["vite@5.4.19", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA=="], + "update-notifier/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], - "vitest/vite": ["vite@5.4.19", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA=="], + "vite-node/vite": ["vite@5.4.20", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g=="], - "vue-tsc/semver": ["semver@7.5.4", "", { "dependencies": { "lru-cache": "^6.0.0" }, "bin": { "semver": "bin/semver.js" } }, "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA=="], + "vitest/vite": ["vite@5.4.20", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g=="], - "web-ext-run/@babel/runtime": ["@babel/runtime@7.27.0", "", { "dependencies": { "regenerator-runtime": "^0.14.0" } }, "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw=="], + "web-ext-run/@babel/runtime": ["@babel/runtime@7.28.2", "", {}, "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA=="], - "web-ext-run/ws": ["ws@8.18.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w=="], + "whatwg-encoding/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], "widest-line/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], - "wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], + "wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], "wrap-ansi/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], - "wrap-ansi/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + "wrap-ansi/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], "wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "wxt/ci-info": ["ci-info@4.3.0", "", {}, "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ=="], - "wxt/fs-extra": ["fs-extra@11.3.1", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g=="], + "wxt/fs-extra": ["fs-extra@11.3.2", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A=="], "wxt/minimatch": ["minimatch@10.0.3", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw=="], - "wxt/vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], - "z-schema/commander": ["commander@10.0.1", "", {}, "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug=="], "@aklinker1/rollup-plugin-visualizer/open/define-lazy-prop": ["define-lazy-prop@2.0.0", "", {}, "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og=="], @@ -1779,14 +1773,14 @@ "@aklinker1/rollup-plugin-visualizer/yargs/yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + "@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], - "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.0", "", {}, "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg=="], + "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], "@microsoft/api-extractor/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], - "@microsoft/api-extractor/semver/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], - "@playhtml/extension/react-dom/scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="], "@playhtml/react/react-dom/scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="], @@ -1811,27 +1805,25 @@ "@playhtml/react/vitest/vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], - "@rushstack/node-core-library/semver/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], - "@supabase/node-fetch/whatwg-url/tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], "@supabase/node-fetch/whatwg-url/webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], - "boxen/string-width/emoji-regex": ["emoji-regex@10.4.0", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="], + "boxen/string-width/emoji-regex": ["emoji-regex@10.5.0", "", {}, "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg=="], - "boxen/string-width/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + "boxen/string-width/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], - "boxen/wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], + "boxen/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], - "boxen/wrap-ansi/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + "boxen/wrap-ansi/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], "cli-highlight/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "cli-highlight/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "cli-truncate/string-width/emoji-regex": ["emoji-regex@10.4.0", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="], + "cli-truncate/string-width/emoji-regex": ["emoji-regex@10.5.0", "", {}, "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg=="], - "cli-truncate/string-width/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + "cli-truncate/string-width/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], "cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], @@ -1841,19 +1833,19 @@ "fx-runner/which/isexe": ["isexe@1.1.2", "", {}, "sha512-d2eJzK691yZwPHcv1LbeAOa91yMJ9QmfTgSO1oXB65ezVhXQsxBac2vEB4bMVms9cGzaA99n6V2viHMq82VLDw=="], - "listr2/wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], + "listr2/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], "listr2/wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], - "listr2/wrap-ansi/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + "listr2/wrap-ansi/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], - "log-update/slice-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], + "log-update/slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], - "log-update/slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.0.0", "", { "dependencies": { "get-east-asian-width": "^1.0.0" } }, "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA=="], + "log-update/slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="], - "log-update/strip-ansi/ansi-regex": ["ansi-regex@6.2.0", "", {}, "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg=="], + "log-update/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], - "log-update/wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], + "log-update/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], "log-update/wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], @@ -1861,11 +1853,9 @@ "multimatch/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], - "node-notifier/semver/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], + "ora/string-width/emoji-regex": ["emoji-regex@10.5.0", "", {}, "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg=="], - "ora/string-width/emoji-regex": ["emoji-regex@10.4.0", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="], - - "ora/strip-ansi/ansi-regex": ["ansi-regex@6.2.0", "", {}, "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg=="], + "ora/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], "partykit/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], @@ -1933,10 +1923,6 @@ "playhtml/vitest/vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], - "publish-browser-extension/open/default-browser": ["default-browser@4.0.0", "", { "dependencies": { "bundle-name": "^3.0.0", "default-browser-id": "^3.0.0", "execa": "^7.1.1", "titleize": "^3.0.0" } }, "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA=="], - - "publish-browser-extension/open/is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="], - "publish-browser-extension/ora/cli-cursor": ["cli-cursor@4.0.0", "", { "dependencies": { "restore-cursor": "^4.0.0" } }, "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg=="], "publish-browser-extension/ora/is-unicode-supported": ["is-unicode-supported@1.3.0", "", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="], @@ -1945,7 +1931,7 @@ "publish-browser-extension/ora/stdin-discarder": ["stdin-discarder@0.1.0", "", { "dependencies": { "bl": "^5.0.0" } }, "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ=="], - "publish-browser-extension/ora/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + "publish-browser-extension/ora/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], "shelljs/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], @@ -1953,59 +1939,43 @@ "vitest/vite/esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], - "vue-tsc/semver/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], - - "widest-line/string-width/emoji-regex": ["emoji-regex@10.4.0", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="], + "widest-line/string-width/emoji-regex": ["emoji-regex@10.5.0", "", {}, "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg=="], - "widest-line/string-width/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + "widest-line/string-width/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], "wrap-ansi/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], - "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.2.0", "", {}, "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg=="], + "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], "wxt/fs-extra/jsonfile": ["jsonfile@6.2.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg=="], "wxt/fs-extra/universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="], - "wxt/vite-node/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - "@aklinker1/rollup-plugin-visualizer/yargs/cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], - "@microsoft/api-extractor/semver/lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], - "@playhtml/react/vitest/@vitest/mocker/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], - "@playhtml/react/vitest/@vitest/spy/tinyspy": ["tinyspy@4.0.3", "", {}, "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A=="], - - "@rushstack/node-core-library/semver/lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], + "@playhtml/react/vitest/@vitest/spy/tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], - "boxen/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.0", "", {}, "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg=="], + "boxen/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], - "boxen/wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.2.0", "", {}, "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg=="], + "boxen/wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], - "cli-truncate/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.0", "", {}, "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg=="], + "cli-truncate/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], - "listr2/wrap-ansi/string-width/emoji-regex": ["emoji-regex@10.4.0", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="], + "listr2/wrap-ansi/string-width/emoji-regex": ["emoji-regex@10.5.0", "", {}, "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg=="], - "listr2/wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.2.0", "", {}, "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg=="], + "listr2/wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], - "log-update/wrap-ansi/string-width/emoji-regex": ["emoji-regex@10.4.0", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="], - - "node-notifier/semver/lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], + "log-update/wrap-ansi/string-width/emoji-regex": ["emoji-regex@10.5.0", "", {}, "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg=="], "playhtml/vitest/@vitest/mocker/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], - "playhtml/vitest/@vitest/spy/tinyspy": ["tinyspy@4.0.3", "", {}, "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A=="], - - "publish-browser-extension/open/default-browser/bundle-name": ["bundle-name@3.0.0", "", { "dependencies": { "run-applescript": "^5.0.0" } }, "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw=="], - - "publish-browser-extension/open/default-browser/default-browser-id": ["default-browser-id@3.0.0", "", { "dependencies": { "bplist-parser": "^0.2.0", "untildify": "^4.0.0" } }, "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA=="], - - "publish-browser-extension/open/default-browser/execa": ["execa@7.2.0", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.1", "human-signals": "^4.3.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^3.0.7", "strip-final-newline": "^3.0.0" } }, "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA=="], + "playhtml/vitest/@vitest/spy/tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], "publish-browser-extension/ora/cli-cursor/restore-cursor": ["restore-cursor@4.0.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg=="], - "publish-browser-extension/ora/strip-ansi/ansi-regex": ["ansi-regex@6.2.0", "", {}, "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg=="], + "publish-browser-extension/ora/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], "shelljs/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], @@ -2101,42 +2071,14 @@ "vitest/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], - "vue-tsc/semver/lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], - - "widest-line/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.0", "", {}, "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg=="], + "widest-line/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], "@aklinker1/rollup-plugin-visualizer/yargs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - "publish-browser-extension/open/default-browser/bundle-name/run-applescript": ["run-applescript@5.0.0", "", { "dependencies": { "execa": "^5.0.0" } }, "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg=="], - - "publish-browser-extension/open/default-browser/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], - - "publish-browser-extension/open/default-browser/execa/human-signals": ["human-signals@4.3.1", "", {}, "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ=="], - - "publish-browser-extension/open/default-browser/execa/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], - "publish-browser-extension/ora/cli-cursor/restore-cursor/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], "publish-browser-extension/ora/cli-cursor/restore-cursor/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], - "publish-browser-extension/open/default-browser/bundle-name/run-applescript/execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], - "publish-browser-extension/ora/cli-cursor/restore-cursor/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], - - "publish-browser-extension/open/default-browser/bundle-name/run-applescript/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], - - "publish-browser-extension/open/default-browser/bundle-name/run-applescript/execa/human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], - - "publish-browser-extension/open/default-browser/bundle-name/run-applescript/execa/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], - - "publish-browser-extension/open/default-browser/bundle-name/run-applescript/execa/npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="], - - "publish-browser-extension/open/default-browser/bundle-name/run-applescript/execa/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], - - "publish-browser-extension/open/default-browser/bundle-name/run-applescript/execa/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], - - "publish-browser-extension/open/default-browser/bundle-name/run-applescript/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], - - "publish-browser-extension/open/default-browser/bundle-name/run-applescript/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], } } diff --git a/packages/playhtml/src/__tests__/auth.test.ts b/packages/playhtml/src/__tests__/auth.test.ts new file mode 100644 index 0000000..2b3f7d0 --- /dev/null +++ b/packages/playhtml/src/__tests__/auth.test.ts @@ -0,0 +1,374 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { + generateIdentity, + signMessage, + verifySignature, + generateSessionChallenge, + createSessionAction, + getCurrentSession, +} from "../auth"; +import type { PlayHTMLIdentity, SessionChallenge } from "@playhtml/common"; + +// Mock global crypto for testing +const mockCrypto = { + randomUUID: vi.fn(() => 'test-uuid-12345'), + subtle: { + generateKey: vi.fn(), + sign: vi.fn(), + verify: vi.fn(), + importKey: vi.fn(), + exportKey: vi.fn(), + } +}; + +Object.defineProperty(globalThis, 'crypto', { + value: mockCrypto, + writable: true +}); + +// Mock window object for session utilities +Object.defineProperty(globalThis, 'window', { + value: { + location: { + hostname: 'localhost' + } + }, + writable: true +}); + +describe("Authentication - Identity Generation", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("should generate a valid Ed25519 identity", async () => { + // Mock successful Ed25519 key generation + const mockKeyPair = { + privateKey: { type: 'private' }, + publicKey: { type: 'public' } + }; + + mockCrypto.subtle.generateKey.mockResolvedValue(mockKeyPair as any); + mockCrypto.subtle.exportKey + .mockResolvedValueOnce(new ArrayBuffer(32)) // private key + .mockResolvedValueOnce(new ArrayBuffer(44)); // public key + + const identity = await generateIdentity("Test User"); + + expect(identity).toHaveProperty('privateKey'); + expect(identity).toHaveProperty('publicKey'); + expect(identity).toHaveProperty('displayName', 'Test User'); + expect(identity).toHaveProperty('algorithm', 'Ed25519'); + expect(identity).toHaveProperty('createdAt'); + expect(identity).toHaveProperty('version', 1); + + expect(crypto.subtle.generateKey).toHaveBeenCalledWith( + { name: "Ed25519" }, + true, + ["sign", "verify"] + ); + }); + + it("should fallback to RSA-PSS when Ed25519 fails", async () => { + // Mock Ed25519 failure, RSA-PSS success + mockCrypto.subtle.generateKey + .mockRejectedValueOnce(new Error("Ed25519 not supported")) + .mockResolvedValueOnce({ + privateKey: { type: 'private' }, + publicKey: { type: 'public' } + } as any); + + mockCrypto.subtle.exportKey + .mockResolvedValueOnce(new ArrayBuffer(256)) // RSA private key + .mockResolvedValueOnce(new ArrayBuffer(270)); // RSA public key + + const identity = await generateIdentity("Test User"); + + expect(identity.algorithm).toBe('RSA-PSS'); + expect(crypto.subtle.generateKey).toHaveBeenCalledTimes(2); + expect(crypto.subtle.generateKey).toHaveBeenLastCalledWith({ + name: "RSA-PSS", + modulusLength: 2048, + publicExponent: new Uint8Array([1, 0, 1]), + hash: "SHA-256", + }, true, ["sign", "verify"]); + }); + + it("should create identity with correct structure", async () => { + const mockKeyPair = { privateKey: {}, publicKey: {} }; + mockCrypto.subtle.generateKey.mockResolvedValue(mockKeyPair as any); + mockCrypto.subtle.exportKey + .mockResolvedValue(new ArrayBuffer(32)); + + const identity = await generateIdentity(); + + expect(identity).toEqual(expect.objectContaining({ + privateKey: expect.any(String), + publicKey: expect.any(String), + createdAt: expect.any(Number), + version: 1, + algorithm: expect.stringMatching(/^(Ed25519|RSA-PSS)$/), + })); + }); +}); + +describe("Authentication - Message Signing", () => { + let testIdentity: PlayHTMLIdentity; + + beforeEach(async () => { + vi.clearAllMocks(); + + // Create a test identity + testIdentity = { + privateKey: "dGVzdC1wcml2YXRlLWtleQ==", // base64: "test-private-key" + publicKey: "dGVzdC1wdWJsaWMta2V5", // base64: "test-public-key" + algorithm: "Ed25519", + displayName: "Test User", + createdAt: Date.now(), + version: 1, + }; + }); + + it("should sign a message with Ed25519", async () => { + const mockSignature = new ArrayBuffer(64); + mockCrypto.subtle.importKey.mockResolvedValue({} as any); + mockCrypto.subtle.sign.mockResolvedValue(mockSignature); + + const message = "test message"; + const signature = await signMessage(message, testIdentity.privateKey, "Ed25519"); + + expect(mockCrypto.subtle.importKey).toHaveBeenCalledWith( + "pkcs8", + expect.anything(), + { name: "Ed25519" }, + false, + ["sign"] + ); + expect(mockCrypto.subtle.sign).toHaveBeenCalledWith( + "Ed25519", + expect.anything(), + expect.anything() + ); + expect(signature).toEqual(expect.any(String)); + }); + + it("should sign a message with RSA-PSS", async () => { + const mockSignature = new ArrayBuffer(256); + mockCrypto.subtle.importKey.mockResolvedValue({} as any); + mockCrypto.subtle.sign.mockResolvedValue(mockSignature); + + const message = "test message"; + const signature = await signMessage(message, testIdentity.privateKey, "RSA-PSS"); + + expect(mockCrypto.subtle.importKey).toHaveBeenCalledWith( + "pkcs8", + expect.anything(), + { name: "RSA-PSS", hash: "SHA-256" }, + false, + ["sign"] + ); + expect(mockCrypto.subtle.sign).toHaveBeenCalledWith( + { name: "RSA-PSS", saltLength: 32 }, + expect.anything(), + expect.anything() + ); + }); + + it("should produce deterministic signatures", async () => { + const mockSignature = new ArrayBuffer(64); + mockCrypto.subtle.importKey.mockResolvedValue({} as any); + mockCrypto.subtle.sign.mockResolvedValue(mockSignature); + + const message = "consistent message"; + const signature1 = await signMessage(message, testIdentity.privateKey, "Ed25519"); + const signature2 = await signMessage(message, testIdentity.privateKey, "Ed25519"); + + expect(signature1).toBe(signature2); + }); + + it("should handle signing errors gracefully", async () => { + mockCrypto.subtle.importKey.mockRejectedValue(new Error("Import failed")); + + await expect(signMessage("test", testIdentity.privateKey, "Ed25519")) + .rejects.toThrow("Message signing failed"); + }); +}); + +describe("Authentication - Signature Verification", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("should verify valid Ed25519 signature", async () => { + mockCrypto.subtle.importKey.mockResolvedValue({} as any); + mockCrypto.subtle.verify.mockResolvedValue(true); + + const result = await verifySignature( + "test message", + "dGVzdC1zaWduYXR1cmU=", // base64: "test-signature" + "dGVzdC1wdWJsaWMta2V5", // base64: "test-public-key" + "Ed25519" + ); + + expect(result).toBe(true); + expect(mockCrypto.subtle.importKey).toHaveBeenCalledWith( + "spki", + expect.anything(), + { name: "Ed25519" }, + false, + ["verify"] + ); + expect(mockCrypto.subtle.verify).toHaveBeenCalledWith( + "Ed25519", + expect.anything(), + expect.anything(), + expect.any(ArrayBuffer) + ); + }); + + it("should verify valid RSA-PSS signature", async () => { + mockCrypto.subtle.importKey.mockResolvedValue({} as any); + mockCrypto.subtle.verify.mockResolvedValue(true); + + const result = await verifySignature( + "test message", + "dGVzdC1zaWduYXR1cmU=", + "dGVzdC1wdWJsaWMta2V5", + "RSA-PSS" + ); + + expect(result).toBe(true); + expect(mockCrypto.subtle.importKey).toHaveBeenCalledWith( + "spki", + expect.anything(), + { name: "RSA-PSS", hash: "SHA-256" }, + false, + ["verify"] + ); + expect(mockCrypto.subtle.verify).toHaveBeenCalledWith( + { name: "RSA-PSS", saltLength: 32 }, + expect.anything(), + expect.anything(), + expect.any(ArrayBuffer) + ); + }); + + it("should return false for invalid signature", async () => { + mockCrypto.subtle.importKey.mockResolvedValue({} as any); + mockCrypto.subtle.verify.mockResolvedValue(false); + + const result = await verifySignature( + "test message", + "invalid-signature", + "dGVzdC1wdWJsaWMta2V5", + "Ed25519" + ); + + expect(result).toBe(false); + }); + + it("should return false on crypto errors", async () => { + mockCrypto.subtle.importKey.mockRejectedValue(new Error("Key import failed")); + + const result = await verifySignature( + "test message", + "dGVzdC1zaWduYXR1cmU=", + "invalid-key", + "Ed25519" + ); + + expect(result).toBe(false); + }); +}); + +describe("Authentication - Session Challenges", () => { + beforeEach(() => { + vi.clearAllMocks(); + mockCrypto.randomUUID.mockReturnValue('test-challenge-uuid'); + + // Mock window.location + Object.defineProperty(window, 'location', { + value: { hostname: 'localhost' }, + writable: true + }); + }); + + it("should generate valid session challenge", () => { + const challenge = generateSessionChallenge(); + + expect(challenge).toEqual({ + challenge: 'test-challenge-uuid', + domain: 'localhost', + timestamp: expect.any(Number), + expiresAt: expect.any(Number), + }); + + // Should expire in 5 minutes (300,000ms) + expect(challenge.expiresAt - challenge.timestamp).toBe(5 * 60 * 1000); + }); + + it("should create unique challenges", () => { + mockCrypto.randomUUID + .mockReturnValueOnce('challenge-1') + .mockReturnValueOnce('challenge-2'); + + const challenge1 = generateSessionChallenge(); + const challenge2 = generateSessionChallenge(); + + expect(challenge1.challenge).not.toBe(challenge2.challenge); + }); + + it("should use correct domain from window.location", () => { + Object.defineProperty(window, 'location', { + value: { hostname: 'example.com' }, + writable: true + }); + + const challenge = generateSessionChallenge(); + expect(challenge.domain).toBe('example.com'); + }); +}); + +describe("Authentication - Session Actions", () => { + beforeEach(() => { + vi.clearAllMocks(); + mockCrypto.randomUUID.mockReturnValue('action-nonce-123'); + }); + + it("should create valid session action", () => { + // Mock current session + const mockSession = { + sessionId: 'test-session-123', + publicKey: 'test-key', + domain: 'localhost', + establishedAt: Date.now(), + expiresAt: Date.now() + 24 * 60 * 60 * 1000, + }; + + vi.doMock('../auth', () => ({ + getCurrentSession: () => mockSession, + })); + + const action = createSessionAction('write', 'element-123', { x: 10, y: 20 }); + + expect(action).toEqual({ + sessionId: 'test-session-123', + action: 'write', + elementId: 'element-123', + data: { x: 10, y: 20 }, + timestamp: expect.any(Number), + nonce: 'action-nonce-123', + }); + }); + + it("should throw error if no current session", () => { + vi.doMock('../auth', () => ({ + getCurrentSession: () => null, + })); + + expect(() => { + createSessionAction('write', 'element-123', {}); + }).toThrow('No active session for creating actions'); + }); +}); + diff --git a/packages/playhtml/src/auth.ts b/packages/playhtml/src/auth.ts index 9a5452a..c36a6d0 100644 --- a/packages/playhtml/src/auth.ts +++ b/packages/playhtml/src/auth.ts @@ -365,12 +365,16 @@ export async function establishSessionWithWS( // Set up one-time listener for session response const handleMessage = (event: MessageEvent) => { + console.log('[PLAYHTML] Received WebSocket message:', event.data); try { const data = JSON.parse(event.data); + console.log('[PLAYHTML] Parsed message type:', data.type); + if ( data.type === "session_established" || data.type === "session_renewed" ) { + console.log('[PLAYHTML] Session response received:', data); ws.removeEventListener("message", handleMessage); const session: ValidatedSession = { @@ -402,18 +406,24 @@ export async function establishSessionWithWS( resolve(session); } else if (data.type === "session_error") { + console.log('[PLAYHTML] Session error received:', data); ws.removeEventListener("message", handleMessage); reject(new Error(data.message || "Session establishment failed")); + } else { + console.log('[PLAYHTML] Ignoring message type:', data.type); } } catch (error) { - // Ignore non-JSON messages + console.log('[PLAYHTML] Non-JSON message received:', event.data); } }; ws.addEventListener("message", handleMessage); // Send session establishment request + console.log('[PLAYHTML] Sending session establishment request:', request); + console.log('[PLAYHTML] WebSocket readyState:', ws.readyState); ws.send(JSON.stringify(request)); + console.log('[PLAYHTML] Session request sent, waiting for response...'); // Set timeout for session establishment setTimeout(() => { @@ -506,7 +516,23 @@ export function createSessionAction( export async function initializeSessionAuth(ws?: WebSocket): Promise { const identity = getCurrentIdentity(); if (identity && ws) { + // Wait a bit for WebSocket to be fully ready + if (ws.readyState !== WebSocket.OPEN) { + console.log('[PLAYHTML SESSION]: WebSocket not ready, waiting...'); + await new Promise((resolve) => { + const checkReady = () => { + if (ws.readyState === WebSocket.OPEN) { + resolve(true); + } else { + setTimeout(checkReady, 100); + } + }; + checkReady(); + }); + } + try { + console.log('[PLAYHTML SESSION]: Attempting to establish session...'); await establishSessionWithWS(identity, ws); } catch (error) { console.warn("Failed to establish session on page load:", error); diff --git a/partykit/__tests__/auth.test.ts b/partykit/__tests__/auth.test.ts new file mode 100644 index 0000000..90170cc --- /dev/null +++ b/partykit/__tests__/auth.test.ts @@ -0,0 +1,503 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { Buffer } from "node:buffer"; + +// Mock crypto for Node.js environment +const mockCrypto = { + randomUUID: vi.fn(() => 'test-session-uuid'), + subtle: { + importKey: vi.fn(), + verify: vi.fn(), + } +}; + +// Setup global crypto mock +globalThis.crypto = mockCrypto as any; + +// Test data - these should match real Ed25519/RSA-PSS key formats +const TEST_KEYS = { + ed25519: { + publicKey: "MCowBQYDK2VwAyEAMndQFsLmugJTh0yVF0somtpb9FVY91mTTmMXUB+Bzfc=", // Real Ed25519 public key + signature: "ZYVDrvh6Buc6ce4Znle3+efFC1ZZ7aOe9uX++bUmdpAdwfj78/IZbDYRL1U9k+BDW6jr3VNk9MQTqIPB1O2VBw==", // 64 bytes + algorithm: "Ed25519" + }, + rsaPss: { + publicKey: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA", // Truncated RSA public key for test + signature: "dGVzdC1yc2Etc2lnbmF0dXJlLWxvbmctZW5vdWdoLWZvci1yc2EtcHNzLXNpZ25hdHVyZS10ZXN0aW5nLXB1cnBvc2VzLW9ubHk=", // 256 bytes when decoded + algorithm: "RSA-PSS" + } +}; + +// Import the functions we need to test - we'll need to extract these from party.ts +// For now, let's recreate the key functions to test + +async function verifySignature( + message: string, + signatureBase64: string, + publicKeyBase64: string, + algorithm: string = "Ed25519" +): Promise { + try { + console.log(`[PartyKit] Verifying signature with algorithm: ${algorithm}`); + console.log(`[PartyKit] Public key length: ${publicKeyBase64.length}`); + console.log(`[PartyKit] Signature length: ${signatureBase64.length}`); + + // Early return for empty inputs + if (!signatureBase64 || !publicKeyBase64) { + console.log(`[PartyKit] Signature verification result: false (empty inputs)`); + return false; + } + + let publicKeyBuffer: Buffer; + let signatureBuffer: Buffer; + + try { + // Check if inputs are valid base64 + if (!/^[A-Za-z0-9+/]*={0,2}$/.test(publicKeyBase64) || !/^[A-Za-z0-9+/]*={0,2}$/.test(signatureBase64)) { + console.log(`[PartyKit] Signature verification result: false (invalid base64)`); + return false; + } + + publicKeyBuffer = Buffer.from(publicKeyBase64, "base64"); + signatureBuffer = Buffer.from(signatureBase64, "base64"); + } catch (error) { + console.log(`[PartyKit] Signature verification result: false (invalid base64)`); + return false; + } + + const keyAlgorithm = algorithm === "RSA-PSS" + ? { name: "RSA-PSS", hash: "SHA-256" } + : { name: "Ed25519" }; + + const publicKey = await crypto.subtle.importKey( + "spki", + publicKeyBuffer, + keyAlgorithm, + false, + ["verify"] + ); + + const messageBuffer = new TextEncoder().encode(message); + + const verifyAlgorithm = algorithm === "RSA-PSS" + ? { name: "RSA-PSS", saltLength: 32 } + : "Ed25519"; + + const result = await crypto.subtle.verify( + verifyAlgorithm, + publicKey, + signatureBuffer, + messageBuffer + ); + + console.log(`[PartyKit] Signature verification result: ${result}`); + return result; + } catch (error) { + console.error("Signature verification failed:", error); + return false; + } +} + +interface ValidatedSession { + sessionId: string; + publicKey: string; + domain: string; + establishedAt: number; + expiresAt: number; +} + +interface SessionAction { + sessionId: string; + action: string; + elementId: string; + data: any; + timestamp: number; + nonce: string; +} + +class MockSessionManager { + private validSessions = new Map(); + private usedNonces = new Set(); + + createSession(publicKey: string, domain: string = "localhost"): ValidatedSession { + const session: ValidatedSession = { + sessionId: crypto.randomUUID(), + publicKey, + domain, + establishedAt: Date.now(), + expiresAt: Date.now() + (24 * 60 * 60 * 1000), // 24 hours + }; + + this.validSessions.set(session.sessionId, session); + return session; + } + + findExistingSession(publicKey: string): ValidatedSession | null { + for (const session of this.validSessions.values()) { + if (session.publicKey === publicKey && session.expiresAt > Date.now()) { + return session; + } + } + return null; + } + + validateSessionAction(action: SessionAction): boolean { + // Check if session exists and is not expired + const session = this.validSessions.get(action.sessionId); + if (!session || session.expiresAt < Date.now()) { + return false; + } + + // Check nonce uniqueness + const nonceKey = `${action.sessionId}:${action.nonce}`; + if (this.usedNonces.has(nonceKey)) { + return false; + } + + // Basic action validation + const isValidFormat = !!( + action.sessionId && + action.action && + action.elementId && + action.timestamp && + action.nonce && + (Date.now() - action.timestamp) < 5 * 60 * 1000 // Within 5 minutes + ); + + if (isValidFormat) { + this.usedNonces.add(nonceKey); + } + + return isValidFormat; + } + + cleanupExpiredSessions(): number { + const now = Date.now(); + let cleaned = 0; + + for (const [sessionId, session] of this.validSessions.entries()) { + if (session.expiresAt < now) { + this.validSessions.delete(sessionId); + cleaned++; + } + } + + return cleaned; + } + + getSessionCount(): number { + return this.validSessions.size; + } +} + +describe("Server-Side Authentication - Signature Verification", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("should verify valid Ed25519 signature", async () => { + mockCrypto.subtle.importKey.mockResolvedValue({} as any); + mockCrypto.subtle.verify.mockResolvedValue(true); + + const result = await verifySignature( + "test message", + TEST_KEYS.ed25519.signature, + TEST_KEYS.ed25519.publicKey, + "Ed25519" + ); + + expect(result).toBe(true); + expect(mockCrypto.subtle.importKey).toHaveBeenCalledWith( + "spki", + expect.anything(), + { name: "Ed25519" }, + false, + ["verify"] + ); + }); + + it("should verify valid RSA-PSS signature", async () => { + mockCrypto.subtle.importKey.mockResolvedValue({} as any); + mockCrypto.subtle.verify.mockResolvedValue(true); + + const result = await verifySignature( + "test message", + TEST_KEYS.rsaPss.signature, + TEST_KEYS.rsaPss.publicKey, + "RSA-PSS" + ); + + expect(result).toBe(true); + expect(mockCrypto.subtle.importKey).toHaveBeenCalledWith( + "spki", + expect.anything(), + { name: "RSA-PSS", hash: "SHA-256" }, + false, + ["verify"] + ); + }); + + it("should reject invalid signatures", async () => { + mockCrypto.subtle.importKey.mockResolvedValue({} as any); + mockCrypto.subtle.verify.mockResolvedValue(false); + + const result = await verifySignature( + "test message", + "invalid-signature", + TEST_KEYS.ed25519.publicKey, + "Ed25519" + ); + + expect(result).toBe(false); + }); + + it("should handle malformed public keys gracefully", async () => { + mockCrypto.subtle.importKey.mockRejectedValue(new Error("Invalid key format")); + + const result = await verifySignature( + "test message", + TEST_KEYS.ed25519.signature, + "malformed-key!!!", + "Ed25519" + ); + + expect(result).toBe(false); + }); + + it("should handle crypto operation failures", async () => { + mockCrypto.subtle.importKey.mockResolvedValue({} as any); + mockCrypto.subtle.verify.mockRejectedValue(new Error("Crypto operation failed")); + + const result = await verifySignature( + "test message", + TEST_KEYS.ed25519.signature, + TEST_KEYS.ed25519.publicKey, + "Ed25519" + ); + + expect(result).toBe(false); + }); + + it("should log verification details", async () => { + const consoleSpy = vi.spyOn(console, 'log'); + mockCrypto.subtle.importKey.mockResolvedValue({} as any); + mockCrypto.subtle.verify.mockResolvedValue(true); + + await verifySignature( + "test message", + TEST_KEYS.ed25519.signature, + TEST_KEYS.ed25519.publicKey, + "Ed25519" + ); + + expect(consoleSpy).toHaveBeenCalledWith('[PartyKit] Verifying signature with algorithm: Ed25519'); + expect(consoleSpy).toHaveBeenCalledWith(`[PartyKit] Public key length: ${TEST_KEYS.ed25519.publicKey.length}`); + expect(consoleSpy).toHaveBeenCalledWith(`[PartyKit] Signature length: ${TEST_KEYS.ed25519.signature.length}`); + expect(consoleSpy).toHaveBeenCalledWith('[PartyKit] Signature verification result: true'); + }); +}); + +describe("Server-Side Authentication - Session Management", () => { + let sessionManager: MockSessionManager; + + beforeEach(() => { + vi.clearAllMocks(); + sessionManager = new MockSessionManager(); + mockCrypto.randomUUID.mockReturnValue('test-session-123'); + }); + + it("should create new session", () => { + const session = sessionManager.createSession(TEST_KEYS.ed25519.publicKey); + + expect(session).toEqual({ + sessionId: 'test-session-123', + publicKey: TEST_KEYS.ed25519.publicKey, + domain: 'localhost', + establishedAt: expect.any(Number), + expiresAt: expect.any(Number), + }); + + // Should expire in 24 hours + expect(session.expiresAt - session.establishedAt).toBe(24 * 60 * 60 * 1000); + }); + + it("should find existing session by public key", () => { + const session = sessionManager.createSession(TEST_KEYS.ed25519.publicKey); + const found = sessionManager.findExistingSession(TEST_KEYS.ed25519.publicKey); + + expect(found).toEqual(session); + }); + + it("should return null for non-existent session", () => { + const found = sessionManager.findExistingSession("non-existent-key"); + expect(found).toBeNull(); + }); + + it("should not find expired sessions", () => { + const session = sessionManager.createSession(TEST_KEYS.ed25519.publicKey); + + // Manually expire the session + session.expiresAt = Date.now() - 1000; + + const found = sessionManager.findExistingSession(TEST_KEYS.ed25519.publicKey); + expect(found).toBeNull(); + }); + + it("should clean up expired sessions", () => { + mockCrypto.randomUUID + .mockReturnValueOnce('session-1') + .mockReturnValueOnce('session-2'); + + const session1 = sessionManager.createSession(TEST_KEYS.ed25519.publicKey, "domain1"); + const session2 = sessionManager.createSession(TEST_KEYS.rsaPss.publicKey, "domain2"); + + // Expire first session + session1.expiresAt = Date.now() - 1000; + + expect(sessionManager.getSessionCount()).toBe(2); + + const cleaned = sessionManager.cleanupExpiredSessions(); + + expect(cleaned).toBe(1); + expect(sessionManager.getSessionCount()).toBe(1); + }); +}); + +describe("Server-Side Authentication - Session Actions", () => { + let sessionManager: MockSessionManager; + let validSession: ValidatedSession; + + beforeEach(() => { + vi.clearAllMocks(); + sessionManager = new MockSessionManager(); + validSession = sessionManager.createSession(TEST_KEYS.ed25519.publicKey); + mockCrypto.randomUUID.mockReturnValue('action-nonce-456'); + }); + + it("should validate valid session action", () => { + const action: SessionAction = { + sessionId: validSession.sessionId, + action: 'write', + elementId: 'test-element', + data: { x: 10, y: 20 }, + timestamp: Date.now(), + nonce: 'unique-nonce-123', + }; + + const isValid = sessionManager.validateSessionAction(action); + expect(isValid).toBe(true); + }); + + it("should reject action with invalid session", () => { + const action: SessionAction = { + sessionId: 'invalid-session-id', + action: 'write', + elementId: 'test-element', + data: { x: 10, y: 20 }, + timestamp: Date.now(), + nonce: 'unique-nonce-123', + }; + + const isValid = sessionManager.validateSessionAction(action); + expect(isValid).toBe(false); + }); + + it("should reject action with expired session", () => { + // Expire the session + validSession.expiresAt = Date.now() - 1000; + + const action: SessionAction = { + sessionId: validSession.sessionId, + action: 'write', + elementId: 'test-element', + data: { x: 10, y: 20 }, + timestamp: Date.now(), + nonce: 'unique-nonce-123', + }; + + const isValid = sessionManager.validateSessionAction(action); + expect(isValid).toBe(false); + }); + + it("should reject duplicate nonce", () => { + const action: SessionAction = { + sessionId: validSession.sessionId, + action: 'write', + elementId: 'test-element', + data: { x: 10, y: 20 }, + timestamp: Date.now(), + nonce: 'duplicate-nonce', + }; + + // First action should succeed + expect(sessionManager.validateSessionAction(action)).toBe(true); + + // Second action with same nonce should fail + expect(sessionManager.validateSessionAction(action)).toBe(false); + }); + + it("should reject old timestamp", () => { + const action: SessionAction = { + sessionId: validSession.sessionId, + action: 'write', + elementId: 'test-element', + data: { x: 10, y: 20 }, + timestamp: Date.now() - (10 * 60 * 1000), // 10 minutes ago + nonce: 'unique-nonce-123', + }; + + const isValid = sessionManager.validateSessionAction(action); + expect(isValid).toBe(false); + }); + + it("should reject incomplete action data", () => { + const incompleteAction = { + sessionId: validSession.sessionId, + action: 'write', + // missing elementId, data, timestamp, nonce + } as any; + + const isValid = sessionManager.validateSessionAction(incompleteAction); + expect(isValid).toBe(false); + }); +}); + +describe("Server-Side Authentication - Error Handling", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("should handle empty signature", async () => { + const result = await verifySignature("test", "", TEST_KEYS.ed25519.publicKey, "Ed25519"); + expect(result).toBe(false); + }); + + it("should handle empty public key", async () => { + const result = await verifySignature("test", TEST_KEYS.ed25519.signature, "", "Ed25519"); + expect(result).toBe(false); + }); + + it("should handle invalid base64 in signature", async () => { + const result = await verifySignature("test", "not-base64!", TEST_KEYS.ed25519.publicKey, "Ed25519"); + expect(result).toBe(false); + }); + + it("should handle invalid base64 in public key", async () => { + const result = await verifySignature("test", TEST_KEYS.ed25519.signature, "not-base64!", "Ed25519"); + expect(result).toBe(false); + }); + + it("should default to Ed25519 when no algorithm specified", async () => { + mockCrypto.subtle.importKey.mockResolvedValue({} as any); + mockCrypto.subtle.verify.mockResolvedValue(true); + + await verifySignature("test", TEST_KEYS.ed25519.signature, TEST_KEYS.ed25519.publicKey); + + expect(mockCrypto.subtle.importKey).toHaveBeenCalledWith( + "spki", + expect.anything(), + { name: "Ed25519" }, + false, + ["verify"] + ); + }); +}); \ No newline at end of file diff --git a/partykit/package.json b/partykit/package.json new file mode 100644 index 0000000..220d3bc --- /dev/null +++ b/partykit/package.json @@ -0,0 +1,17 @@ +{ + "name": "playhtml-partykit-tests", + "private": true, + "type": "module", + "scripts": { + "test": "vitest", + "test:run": "vitest run" + }, + "devDependencies": { + "vitest": "^1.0.0", + "@types/node": "^20.3.3" + }, + "dependencies": { + "y-partykit": "^0.0.120", + "@supabase/supabase-js": "^2.45.4" + } +} \ No newline at end of file diff --git a/partykit/party.ts b/partykit/party.ts index 21ebefe..977189c 100644 --- a/partykit/party.ts +++ b/partykit/party.ts @@ -305,19 +305,21 @@ export default class implements Party.Server { // Handle dynamic registration of shared source element await this.handleRegisterSharedElement(parsed.element, sender); } else if (parsed.type === "session_establish") { - console.log( - `[PartyKit] Handling session establishment for ${parsed.publicKey?.slice( - 0, - 8 - )}...` - ); - await this.handleSessionEstablishmentWS(parsed, sender); - } else if (parsed.type === "session_action") { - console.log( - `[PartyKit] Handling session action: ${parsed.action?.action}` - ); - await this.handleSessionAction(parsed.action, sender); - } else { + console.log( + `[PartyKit] Handling session establishment for ${parsed.publicKey?.slice( + 0, + 8 + )}...` + ); + await this.handleSessionEstablishmentWS(parsed, sender); + return; // Don't broadcast session messages + } else if (parsed.type === "session_action") { + console.log( + `[PartyKit] Handling session action: ${parsed.action?.action}` + ); + await this.handleSessionAction(parsed.action, sender); + return; // Don't broadcast session actions + } else { // Broadcast other messages normally this.room.broadcast(message); } @@ -325,6 +327,8 @@ export default class implements Party.Server { // If not valid JSON, broadcast as-is (existing behavior) this.room.broadcast(message); } + } catch (error) { + console.error(`[PartyKit] Message handling error:`, error); } } @@ -384,6 +388,7 @@ export default class implements Party.Server { async onConnect(connection: Party.Connection, ctx: Party.ConnectionContext) { const room = this.room; + console.log(`[PartyKit] New connection established: ${connection.id}`); // Parse shared references from the connecting client (for consumer rooms) // Parse from the WebSocket request URL diff --git a/website/examples/auth-example.html b/website/examples/auth-example.html index a43f268..72e573e 100644 --- a/website/examples/auth-example.html +++ b/website/examples/auth-example.html @@ -64,11 +64,21 @@

PlayHTML Authentication Example

Authentication Status

Loading...
-
+
- +
@@ -93,7 +103,7 @@

Authenticated Element (Owner Only)

id="authenticated-element" class="element" can-move - playhtml-owner="replace_with_your_public_key" + playhtml-owner="MCowBQYDK2VwAyEAMndQFsLmugJTh0yVF0somtpb9FVY91mTTmMXUB+Bzfc=" playhtml-permissions="write:owner, delete:owner, admin:owner" > Owner Only @@ -101,13 +111,13 @@

Authenticated Element (Owner Only)

-

Role-Based Element (Simple Permissions)

+

Role-Based Element (Contributor (auto-added))

Different actions require different roles:

Role-Based @@ -131,7 +141,7 @@

Conditional Access Element