diff --git a/.gitignore b/.gitignore index 832e80a1..0f0a986b 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,10 @@ build/ releases/ .claude/ coding-plans/ + +/worker/wrangler.toml + +/worker/.wrangler/ + +xcuserdata/ +*.xcuserdatad/ diff --git a/leanring-buddy.xcodeproj/project.pbxproj b/leanring-buddy.xcodeproj/project.pbxproj index 9d88d4b9..8ca76b97 100644 --- a/leanring-buddy.xcodeproj/project.pbxproj +++ b/leanring-buddy.xcodeproj/project.pbxproj @@ -411,7 +411,7 @@ CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 2UDAY4J48G; + DEVELOPMENT_TEAM = ZY7Q367B6W; ENABLE_APP_SANDBOX = NO; ENABLE_HARDENED_RUNTIME = YES; ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES; @@ -452,7 +452,7 @@ CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 2UDAY4J48G; + DEVELOPMENT_TEAM = ZY7Q367B6W; ENABLE_APP_SANDBOX = NO; ENABLE_HARDENED_RUNTIME = YES; ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES; diff --git a/leanring-buddy.xcodeproj/xcuserdata/julianalvarez.xcuserdatad/xcschemes/xcschememanagement.plist b/leanring-buddy.xcodeproj/xcuserdata/julianalvarez.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 6c13490a..00000000 --- a/leanring-buddy.xcodeproj/xcuserdata/julianalvarez.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,14 +0,0 @@ - - - - - SchemeUserState - - leanring-buddy.xcscheme_^#shared#^_ - - orderHint - 0 - - - - diff --git a/leanring-buddy.xcodeproj/xcuserdata/thorfinn.xcuserdatad/xcschemes/xcschememanagement.plist b/leanring-buddy.xcodeproj/xcuserdata/thorfinn.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 6c13490a..00000000 --- a/leanring-buddy.xcodeproj/xcuserdata/thorfinn.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,14 +0,0 @@ - - - - - SchemeUserState - - leanring-buddy.xcscheme_^#shared#^_ - - orderHint - 0 - - - - diff --git a/leanring-buddy/AppleSpeechTranscriptionProvider.swift b/leanring-buddy/AppleSpeechTranscriptionProvider.swift index 600594fa..51ed9713 100644 --- a/leanring-buddy/AppleSpeechTranscriptionProvider.swift +++ b/leanring-buddy/AppleSpeechTranscriptionProvider.swift @@ -24,12 +24,13 @@ final class AppleSpeechTranscriptionProvider: BuddyTranscriptionProvider { let unavailableExplanation: String? = nil func startStreamingSession( + languageCode: String, keyterms: [String], onTranscriptUpdate: @escaping (String) -> Void, onFinalTranscriptReady: @escaping (String) -> Void, onError: @escaping (Error) -> Void ) async throws -> any BuddyStreamingTranscriptionSession { - guard let speechRecognizer = Self.makeBestAvailableSpeechRecognizer() else { + guard let speechRecognizer = Self.makeBestAvailableSpeechRecognizer(languageCode: languageCode) else { throw AppleSpeechTranscriptionProviderError(message: "dictation is not available on this mac.") } @@ -41,8 +42,9 @@ final class AppleSpeechTranscriptionProvider: BuddyTranscriptionProvider { ) } - private static func makeBestAvailableSpeechRecognizer() -> SFSpeechRecognizer? { + private static func makeBestAvailableSpeechRecognizer(languageCode: String) -> SFSpeechRecognizer? { let preferredLocales = [ + Locale(identifier: languageCode), Locale.autoupdatingCurrent, Locale(identifier: "en-US") ] diff --git a/leanring-buddy/AssemblyAIStreamingTranscriptionProvider.swift b/leanring-buddy/AssemblyAIStreamingTranscriptionProvider.swift index d21286b6..a8809015 100644 --- a/leanring-buddy/AssemblyAIStreamingTranscriptionProvider.swift +++ b/leanring-buddy/AssemblyAIStreamingTranscriptionProvider.swift @@ -17,10 +17,6 @@ struct AssemblyAIStreamingTranscriptionProviderError: LocalizedError { } final class AssemblyAIStreamingTranscriptionProvider: BuddyTranscriptionProvider { - /// URL for the Cloudflare Worker endpoint that returns a short-lived - /// AssemblyAI streaming token. The real API key never leaves the server. - private static let tokenProxyURL = "https://your-worker-name.your-subdomain.workers.dev/transcribe-token" - let displayName = "AssemblyAI" let requiresSpeechRecognitionPermission = false @@ -34,6 +30,7 @@ final class AssemblyAIStreamingTranscriptionProvider: BuddyTranscriptionProvider private let sharedWebSocketURLSession = URLSession(configuration: .default) func startStreamingSession( + languageCode: String, keyterms: [String], onTranscriptUpdate: @escaping (String) -> Void, onFinalTranscriptReady: @escaping (String) -> Void, @@ -48,6 +45,7 @@ final class AssemblyAIStreamingTranscriptionProvider: BuddyTranscriptionProvider temporaryToken: temporaryToken, urlSession: sharedWebSocketURLSession, keyterms: keyterms, + languageCode: languageCode, onTranscriptUpdate: onTranscriptUpdate, onFinalTranscriptReady: onFinalTranscriptReady, onError: onError @@ -59,7 +57,8 @@ final class AssemblyAIStreamingTranscriptionProvider: BuddyTranscriptionProvider /// Calls the Cloudflare Worker to get a short-lived AssemblyAI token. private func fetchTemporaryToken() async throws -> String { - var request = URLRequest(url: URL(string: Self.tokenProxyURL)!) + let baseURL = await WorkerEnvironment.shared.getBaseURL() + var request = URLRequest(url: URL(string: "\(baseURL)/transcribe-token")!) request.httpMethod = "POST" let (data, response) = try await URLSession.shared.data(for: request) @@ -117,6 +116,7 @@ private final class AssemblyAIStreamingTranscriptionSession: NSObject, BuddyStre private let apiKey: String? private let temporaryToken: String? private let keyterms: [String] + private let languageCode: String private let onTranscriptUpdate: (String) -> Void private let onFinalTranscriptReady: (String) -> Void private let onError: (Error) -> Void @@ -142,6 +142,7 @@ private final class AssemblyAIStreamingTranscriptionSession: NSObject, BuddyStre temporaryToken: String?, urlSession: URLSession, keyterms: [String], + languageCode: String, onTranscriptUpdate: @escaping (String) -> Void, onFinalTranscriptReady: @escaping (String) -> Void, onError: @escaping (Error) -> Void @@ -150,6 +151,7 @@ private final class AssemblyAIStreamingTranscriptionSession: NSObject, BuddyStre self.temporaryToken = temporaryToken self.urlSession = urlSession self.keyterms = keyterms + self.languageCode = languageCode self.onTranscriptUpdate = onTranscriptUpdate self.onFinalTranscriptReady = onFinalTranscriptReady self.onError = onError @@ -158,7 +160,8 @@ private final class AssemblyAIStreamingTranscriptionSession: NSObject, BuddyStre func open() async throws { let websocketURL = try Self.makeWebsocketURL( temporaryToken: temporaryToken, - keyterms: keyterms + keyterms: keyterms, + languageCode: languageCode ) var websocketRequest = URLRequest(url: websocketURL) @@ -436,7 +439,8 @@ private final class AssemblyAIStreamingTranscriptionSession: NSObject, BuddyStre private static func makeWebsocketURL( temporaryToken: String?, - keyterms: [String] + keyterms: [String], + languageCode: String ) throws -> URL { guard var websocketURLComponents = URLComponents(string: websocketBaseURLString) else { throw AssemblyAIStreamingTranscriptionProviderError( @@ -451,6 +455,12 @@ private final class AssemblyAIStreamingTranscriptionSession: NSObject, BuddyStre URLQueryItem(name: "speech_model", value: "u3-rt-pro") ] + if languageCode != "en" { + // AssemblyAI supports multiple language codes. + queryItems.append(URLQueryItem(name: "language_code", value: languageCode)) + // u3-rt-pro generally supports multi lingual. If any errors happen, we might need a different speech_model or drop it. + } + let normalizedKeyterms = keyterms .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } .filter { !$0.isEmpty } diff --git a/leanring-buddy/BuddyDictationManager.swift b/leanring-buddy/BuddyDictationManager.swift index 5bca2677..dc1152ef 100644 --- a/leanring-buddy/BuddyDictationManager.swift +++ b/leanring-buddy/BuddyDictationManager.swift @@ -517,7 +517,9 @@ final class BuddyDictationManager: NSObject, ObservableObject { print("🎙️ BuddyDictationManager: opening transcription provider \(transcriptionProvider.displayName)") + let languageCode = UserDefaults.standard.string(forKey: "selectedLanguageCode") ?? "en" let activeTranscriptionSession = try await transcriptionProvider.startStreamingSession( + languageCode: languageCode, keyterms: buildTranscriptionKeyterms(), onTranscriptUpdate: { [weak self] transcriptText in Task { @MainActor in diff --git a/leanring-buddy/BuddyTranscriptionProvider.swift b/leanring-buddy/BuddyTranscriptionProvider.swift index 0a75715d..800f7657 100644 --- a/leanring-buddy/BuddyTranscriptionProvider.swift +++ b/leanring-buddy/BuddyTranscriptionProvider.swift @@ -22,6 +22,7 @@ protocol BuddyTranscriptionProvider { var unavailableExplanation: String? { get } func startStreamingSession( + languageCode: String, keyterms: [String], onTranscriptUpdate: @escaping (String) -> Void, onFinalTranscriptReady: @escaping (String) -> Void, diff --git a/leanring-buddy/ClaudeAPI.swift b/leanring-buddy/ClaudeAPI.swift index 0c7070b5..39153c60 100644 --- a/leanring-buddy/ClaudeAPI.swift +++ b/leanring-buddy/ClaudeAPI.swift @@ -10,12 +10,13 @@ class ClaudeAPI { private static let tlsWarmupLock = NSLock() private static var hasStartedTLSWarmup = false - private let apiURL: URL + var proxyURL: String + private var apiURL: URL { URL(string: proxyURL)! } var model: String private let session: URLSession init(proxyURL: String, model: String = "claude-sonnet-4-6") { - self.apiURL = URL(string: proxyURL)! + self.proxyURL = proxyURL self.model = model // Use .default instead of .ephemeral so TLS session tickets are cached. @@ -29,11 +30,6 @@ class ClaudeAPI { config.urlCache = nil config.httpCookieStorage = nil self.session = URLSession(configuration: config) - - // Fire a lightweight HEAD request in the background to pre-establish the TLS - // connection. This caches the TLS session ticket so the first real API call - // (which carries a large image payload) doesn't need a cold TLS handshake. - warmUpTLSConnectionIfNeeded() } private func makeAPIRequest() -> URLRequest { @@ -63,7 +59,7 @@ class ClaudeAPI { /// Sends a no-op HEAD request to the API host to establish and cache a TLS session. /// Failures are silently ignored — this is purely an optimization. - private func warmUpTLSConnectionIfNeeded() { + func warmUpTLSConnectionIfNeeded() { Self.tlsWarmupLock.lock() let shouldStartTLSWarmup = !Self.hasStartedTLSWarmup if shouldStartTLSWarmup { diff --git a/leanring-buddy/CompanionManager.swift b/leanring-buddy/CompanionManager.swift index 0234cf19..1bd45fb5 100644 --- a/leanring-buddy/CompanionManager.swift +++ b/leanring-buddy/CompanionManager.swift @@ -70,14 +70,18 @@ final class CompanionManager: ObservableObject { /// Base URL for the Cloudflare Worker proxy. All API requests route /// through this so keys never ship in the app binary. - private static let workerBaseURL = "https://your-worker-name.your-subdomain.workers.dev" - private lazy var claudeAPI: ClaudeAPI = { - return ClaudeAPI(proxyURL: "\(Self.workerBaseURL)/chat", model: selectedModel) + let defaultURL = "https://your-worker-name.your-subdomain.workers.dev" // Default placeholder, will be dynamically updated in start() + return ClaudeAPI(proxyURL: "\(defaultURL)/chat", model: selectedModel) + }() + + private lazy var openAIAPI: OpenAIAPI = { + return OpenAIAPI(apiKey: lmStudioAPIKey, model: selectedModel) }() private lazy var elevenLabsTTSClient: ElevenLabsTTSClient = { - return ElevenLabsTTSClient(proxyURL: "\(Self.workerBaseURL)/tts") + let defaultURL = "https://your-worker-name.your-subdomain.workers.dev" + return ElevenLabsTTSClient(proxyURL: "\(defaultURL)/tts") }() /// Conversation history so Claude remembers prior exchanges within a session. @@ -107,13 +111,65 @@ final class CompanionManager: ObservableObject { /// Used by the panel to show accurate status text ("Active" vs "Ready"). @Published private(set) var isOverlayVisible: Bool = false + /// The user's preferred spoken and transcribing language code (e.g. "en", "fr"). + @Published var selectedLanguageCode: String = UserDefaults.standard.string(forKey: "selectedLanguageCode") ?? "en" { + didSet { + UserDefaults.standard.set(selectedLanguageCode, forKey: "selectedLanguageCode") + } + } + /// The Claude model used for voice responses. Persisted to UserDefaults. - @Published var selectedModel: String = UserDefaults.standard.string(forKey: "selectedClaudeModel") ?? "claude-sonnet-4-6" + @Published var selectedModel: String = UserDefaults.standard.string(forKey: "selectedGemmaModel") ?? "" + + /// The API key for local LLM requests. Persisted to UserDefaults. + @Published var lmStudioAPIKey: String = UserDefaults.standard.string(forKey: "lmStudioAPIKey") ?? "" { + didSet { + UserDefaults.standard.set(lmStudioAPIKey, forKey: "lmStudioAPIKey") + openAIAPI.apiKey = lmStudioAPIKey + } + } + + /// List of available models fetched from LM Studio API + @Published var availableModels: [String] = [] + + func fetchAvailableModels() { + guard let url = URL(string: "http://127.0.0.1:1234/v1/models") else { return } + var request = URLRequest(url: url) + request.timeoutInterval = 3 + if !lmStudioAPIKey.isEmpty { + request.setValue("Bearer \(lmStudioAPIKey)", forHTTPHeaderField: "Authorization") + } + + Task { + do { + let (data, _) = try await URLSession.shared.data(for: request) + if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any], + let dataArr = json["data"] as? [[String: Any]] { + var models = dataArr.compactMap { $0["id"] as? String } + // Filter out embedding models or non-chat models if needed, + // usually all are fine but we just list them. + models.sort() + await MainActor.run { + self.availableModels = models + // If current selected isn't in there, don't force change it yet + // unless it's completely empty. It's safe to let them manually select it. + } + } + } catch { + print("Failed to fetch models from LM Studio: \\(error)") + } + } + } func setSelectedModel(_ model: String) { selectedModel = model - UserDefaults.standard.set(model, forKey: "selectedClaudeModel") - claudeAPI.model = model + UserDefaults.standard.set(model, forKey: "selectedGemmaModel") + + if model.lowercased().contains("claude") { + claudeAPI.model = model + } else { + openAIAPI.model = model + } } /// User preference for whether the Clicky cursor should be shown. @@ -173,15 +229,24 @@ final class CompanionManager: ObservableObject { } func start() { + Task { + let baseURL = await WorkerEnvironment.shared.getBaseURL() + self.claudeAPI.proxyURL = "\(baseURL)/chat" + self.elevenLabsTTSClient.proxyURL = "\(baseURL)/tts" + + // Eagerly touch APIs so their TLS warmup handshakes complete + // well before the onboarding demo fires at ~40s into the video. + self.claudeAPI.warmUpTLSConnectionIfNeeded() + _ = self.openAIAPI + } + + fetchAvailableModels() refreshAllPermissions() print("🔑 Clicky start — accessibility: \(hasAccessibilityPermission), screen: \(hasScreenRecordingPermission), mic: \(hasMicrophonePermission), screenContent: \(hasScreenContentPermission), onboarded: \(hasCompletedOnboarding)") startPermissionPolling() bindVoiceStateObservation() bindAudioPowerLevel() bindShortcutTransitions() - // Eagerly touch the Claude API so its TLS warmup handshake completes - // well before the onboarding demo fires at ~40s into the video. - _ = claudeAPI // If the user already completed onboarding AND all permissions are // still granted, show the cursor overlay immediately. If permissions @@ -610,15 +675,30 @@ final class CompanionManager: ObservableObject { (userPlaceholder: entry.userTranscript, assistantResponse: entry.assistantResponse) } - let (fullResponseText, _) = try await claudeAPI.analyzeImageStreaming( - images: labeledImages, - systemPrompt: Self.companionVoiceResponseSystemPrompt, - conversationHistory: historyForAPI, - userPrompt: transcript, - onTextChunk: { _ in - // No streaming text display — spinner stays until TTS plays - } - ) + let fullResponseText: String + let duration: TimeInterval + + if selectedModel.lowercased().contains("claude") { + (fullResponseText, duration) = try await claudeAPI.analyzeImageStreaming( + images: labeledImages, + systemPrompt: Self.companionVoiceResponseSystemPrompt + "\n\nCRITICAL: Respond EXCLUSIVELY in the following language code: \(selectedLanguageCode)", + conversationHistory: historyForAPI, + userPrompt: transcript, + onTextChunk: { _ in + // No streaming text display — spinner stays until TTS plays + } + ) + } else { + (fullResponseText, duration) = try await openAIAPI.analyzeImageStreaming( + images: labeledImages, + systemPrompt: Self.companionVoiceResponseSystemPrompt + "\n\nCRITICAL: Respond EXCLUSIVELY in the following language code: \(selectedLanguageCode)", + conversationHistory: historyForAPI, + userPrompt: transcript, + onTextChunk: { _ in + // No streaming text display — spinner stays until TTS plays + } + ) + } guard !Task.isCancelled else { return } @@ -982,12 +1062,24 @@ final class CompanionManager: ObservableObject { let dimensionInfo = " (image dimensions: \(cursorScreenCapture.screenshotWidthInPixels)x\(cursorScreenCapture.screenshotHeightInPixels) pixels)" let labeledImages = [(data: cursorScreenCapture.imageData, label: cursorScreenCapture.label + dimensionInfo)] - let (fullResponseText, _) = try await claudeAPI.analyzeImageStreaming( - images: labeledImages, - systemPrompt: Self.onboardingDemoSystemPrompt, - userPrompt: "look around my screen and find something interesting to point at", - onTextChunk: { _ in } - ) + let fullResponseText: String + let duration: TimeInterval + + if selectedModel.lowercased().contains("claude") { + (fullResponseText, duration) = try await claudeAPI.analyzeImageStreaming( + images: labeledImages, + systemPrompt: Self.onboardingDemoSystemPrompt + "\n\nCRITICAL: Respond EXCLUSIVELY in the following language code: \(selectedLanguageCode)", + userPrompt: "look around my screen and find something interesting to point at", + onTextChunk: { _ in } + ) + } else { + (fullResponseText, duration) = try await openAIAPI.analyzeImageStreaming( + images: labeledImages, + systemPrompt: Self.onboardingDemoSystemPrompt + "\n\nCRITICAL: Respond EXCLUSIVELY in the following language code: \(selectedLanguageCode)", + userPrompt: "look around my screen and find something interesting to point at", + onTextChunk: { _ in } + ) + } let parseResult = Self.parsePointingCoordinates(from: fullResponseText) diff --git a/leanring-buddy/CompanionPanelView.swift b/leanring-buddy/CompanionPanelView.swift index 76789b4c..a65b28d5 100644 --- a/leanring-buddy/CompanionPanelView.swift +++ b/leanring-buddy/CompanionPanelView.swift @@ -13,42 +13,119 @@ import SwiftUI struct CompanionPanelView: View { @ObservedObject var companionManager: CompanionManager @State private var emailInput: String = "" - + @State private var isAPIKeyVisible: Bool = false + @State private var isShowingSettings: Bool = false + var body: some View { VStack(alignment: .leading, spacing: 0) { panelHeader Divider() .background(DS.Colors.borderSubtle) .padding(.horizontal, 16) - - permissionsCopySection - .padding(.top, 16) + + if isShowingSettings { + settingsView + } else { + mainContentView + } + + Spacer() + .frame(height: 12) + + Divider() + .background(DS.Colors.borderSubtle) .padding(.horizontal, 16) + + footerSection // <-- This is now defined below + .padding(.horizontal, 16) + .padding(.vertical, 12) + } + .frame(width: 320) + .background(panelBackground) // <-- This is now defined below + } + + private var settingsView: some View { + VStack(alignment: .leading, spacing: 16) { + modelPickerRow + localAPIKeyRow + languagePickerRow + } + .padding(.horizontal, 16) + .padding(.top, 16) + } - if companionManager.hasCompletedOnboarding && companionManager.allPermissionsGranted { - Spacer() - .frame(height: 12) - - modelPickerRow - .padding(.horizontal, 16) + private var localAPIKeyRow: some View { + HStack { + HStack(spacing: 8) { + ZStack(alignment: .leading) { + if companionManager.lmStudioAPIKey.isEmpty { + Text("LM Studio API Key (leave empty if none)") + .font(.system(size: 11)) + .foregroundColor(.white.opacity(0.6)) + } + + if isAPIKeyVisible { + TextField("", text: Binding( + get: { companionManager.lmStudioAPIKey }, + set: { companionManager.lmStudioAPIKey = $0 } + )) + .textFieldStyle(PlainTextFieldStyle()) + .font(.system(size: 11)) + .foregroundColor(.white) + } else { + SecureField("", text: Binding( + get: { companionManager.lmStudioAPIKey }, + set: { companionManager.lmStudioAPIKey = $0 } + )) + .textFieldStyle(PlainTextFieldStyle()) + .font(.system(size: 11)) + .foregroundColor(.white) + } + } + + Button(action: { + isAPIKeyVisible.toggle() + }) { + Image(systemName: isAPIKeyVisible ? "eye.slash.fill" : "eye.fill") + .font(.system(size: 11)) + .foregroundColor(DS.Colors.textTertiary) + } + .buttonStyle(.plain) + .pointerCursor() } + .padding(.horizontal, 10) + .padding(.vertical, 6) + .background(Color.white.opacity(0.06)) + .cornerRadius(6) + .overlay( + RoundedRectangle(cornerRadius: 6, style: .continuous) + .stroke(DS.Colors.borderSubtle, lineWidth: 0.5) + ) + } + } + private var mainContentView: some View { + VStack(alignment: .leading, spacing: 0) { + permissionsCopySection + .padding(.top, 16) + .padding(.horizontal, 16) + if !companionManager.allPermissionsGranted { Spacer() .frame(height: 16) - + settingsSection .padding(.horizontal, 16) } - + if !companionManager.hasCompletedOnboarding && companionManager.allPermissionsGranted { Spacer() .frame(height: 16) - + startButton .padding(.horizontal, 16) } - + // Show Clicky toggle — hidden for now // if companionManager.hasCompletedOnboarding && companionManager.allPermissionsGranted { // Spacer() @@ -57,52 +134,58 @@ struct CompanionPanelView: View { // showClickyCursorToggleRow // .padding(.horizontal, 16) // } - + if companionManager.hasCompletedOnboarding && companionManager.allPermissionsGranted { Spacer() .frame(height: 16) - - dmFarzaButton + + dmFarzaButton // <-- This is now defined below .padding(.horizontal, 16) } - - Spacer() - .frame(height: 12) - - Divider() - .background(DS.Colors.borderSubtle) - .padding(.horizontal, 16) - - footerSection - .padding(.horizontal, 16) - .padding(.vertical, 12) } - .frame(width: 320) - .background(panelBackground) } - + // MARK: - Header - + private var panelHeader: some View { HStack { HStack(spacing: 8) { // Animated status dot Circle() - .fill(statusDotColor) + .fill(statusDotColor) // <-- This is now defined below .frame(width: 8, height: 8) .shadow(color: statusDotColor.opacity(0.6), radius: 4) - + Text("Clicky") .font(.system(size: 14, weight: .semibold)) .foregroundColor(DS.Colors.textPrimary) } - + Spacer() - - Text(statusText) + + Text(statusText) // <-- This is now defined below .font(.system(size: 12, weight: .medium)) .foregroundColor(DS.Colors.textTertiary) - + + if companionManager.hasCompletedOnboarding && companionManager.allPermissionsGranted { + Button(action: { + withAnimation(.spring(response: 0.3, dampingFraction: 0.7)) { + isShowingSettings.toggle() + } + }) { + Image(systemName: "gear") + .font(.system(size: 11, weight: .semibold)) + .foregroundColor(isShowingSettings ? DS.Colors.textPrimary : DS.Colors.textTertiary) + .frame(width: 20, height: 20) + .background( + Circle() + .fill(isShowingSettings ? Color.white.opacity(0.12) : Color.white.opacity(0.08)) + ) + } + .buttonStyle(.plain) + .pointerCursor() + } + Button(action: { NotificationCenter.default.post(name: .clickyDismissPanel, object: nil) }) { @@ -121,9 +204,9 @@ struct CompanionPanelView: View { .padding(.horizontal, 16) .padding(.vertical, 14) } - + // MARK: - Permissions Copy - + @ViewBuilder private var permissionsCopySection: some View { if companionManager.hasCompletedOnboarding && companionManager.allPermissionsGranted { @@ -152,7 +235,7 @@ struct CompanionPanelView: View { Text("Permissions needed") .font(.system(size: 12, weight: .bold)) .foregroundColor(DS.Colors.textSecondary) - + Text("Some permissions were revoked. Grant all four below to keep using Clicky.") .font(.system(size: 11)) .foregroundColor(DS.Colors.textTertiary) @@ -164,12 +247,12 @@ struct CompanionPanelView: View { Text("Hi, I'm Farza. This is Clicky.") .font(.system(size: 12, weight: .bold)) .foregroundColor(DS.Colors.textSecondary) - + Text("A side project I made for fun to help me learn stuff as I use my computer.") .font(.system(size: 11)) .foregroundColor(DS.Colors.textTertiary) .fixedSize(horizontal: false, vertical: true) - + Text("Nothing runs in the background. Clicky will only take a screenshot when you press the hot key. So, you can give that permission in peace. If you are still sus, eh, I can't do much there champ.") .font(.system(size: 11)) .foregroundColor(Color(red: 0.9, green: 0.4, blue: 0.4)) @@ -178,9 +261,9 @@ struct CompanionPanelView: View { .frame(maxWidth: .infinity, alignment: .leading) } } - + // MARK: - Email + Start Button - + @ViewBuilder private var startButton: some View { if !companionManager.hasCompletedOnboarding && companionManager.allPermissionsGranted { @@ -200,7 +283,7 @@ struct CompanionPanelView: View { RoundedRectangle(cornerRadius: DS.CornerRadius.medium, style: .continuous) .stroke(DS.Colors.borderSubtle, lineWidth: 0.5) ) - + Button(action: { companionManager.submitEmail(emailInput) }) { @@ -239,9 +322,9 @@ struct CompanionPanelView: View { } } } - + // MARK: - Permissions - + private var settingsSection: some View { VStack(spacing: 2) { Text("PERMISSIONS") @@ -249,20 +332,20 @@ struct CompanionPanelView: View { .foregroundColor(DS.Colors.textTertiary) .frame(maxWidth: .infinity, alignment: .leading) .padding(.bottom, 6) - + microphonePermissionRow - + accessibilityPermissionRow - + screenRecordingPermissionRow - + if companionManager.hasScreenRecordingPermission { screenContentPermissionRow } - + } } - + private var accessibilityPermissionRow: some View { let isGranted = companionManager.hasAccessibilityPermission return HStack { @@ -271,14 +354,14 @@ struct CompanionPanelView: View { .font(.system(size: 12, weight: .medium)) .foregroundColor(isGranted ? DS.Colors.textTertiary : DS.Colors.warning) .frame(width: 16) - + Text("Accessibility") .font(.system(size: 13, weight: .medium)) .foregroundColor(DS.Colors.textSecondary) } - + Spacer() - + if isGranted { HStack(spacing: 4) { Circle() @@ -307,7 +390,7 @@ struct CompanionPanelView: View { } .buttonStyle(.plain) .pointerCursor() - + Button(action: { // Reveals the app in Finder so the user can drag it into // the Accessibility list if it doesn't appear automatically @@ -332,7 +415,7 @@ struct CompanionPanelView: View { } .padding(.vertical, 6) } - + private var screenRecordingPermissionRow: some View { let isGranted = companionManager.hasScreenRecordingPermission return HStack { @@ -341,22 +424,22 @@ struct CompanionPanelView: View { .font(.system(size: 12, weight: .medium)) .foregroundColor(isGranted ? DS.Colors.textTertiary : DS.Colors.warning) .frame(width: 16) - + VStack(alignment: .leading, spacing: 1) { Text("Screen Recording") .font(.system(size: 13, weight: .medium)) .foregroundColor(DS.Colors.textSecondary) - + Text(isGranted ? "Only takes a screenshot when you use the hotkey" : "Quit and reopen after granting") - .font(.system(size: 10)) - .foregroundColor(DS.Colors.textTertiary) + .font(.system(size: 10)) + .foregroundColor(DS.Colors.textTertiary) } } - + Spacer() - + if isGranted { HStack(spacing: 4) { Circle() @@ -389,7 +472,7 @@ struct CompanionPanelView: View { } .padding(.vertical, 6) } - + private var screenContentPermissionRow: some View { let isGranted = companionManager.hasScreenContentPermission return HStack { @@ -398,14 +481,14 @@ struct CompanionPanelView: View { .font(.system(size: 12, weight: .medium)) .foregroundColor(isGranted ? DS.Colors.textTertiary : DS.Colors.warning) .frame(width: 16) - + Text("Screen Content") .font(.system(size: 13, weight: .medium)) .foregroundColor(DS.Colors.textSecondary) } - + Spacer() - + if isGranted { HStack(spacing: 4) { Circle() @@ -435,7 +518,7 @@ struct CompanionPanelView: View { } .padding(.vertical, 6) } - + private var microphonePermissionRow: some View { let isGranted = companionManager.hasMicrophonePermission return HStack { @@ -444,14 +527,14 @@ struct CompanionPanelView: View { .font(.system(size: 12, weight: .medium)) .foregroundColor(isGranted ? DS.Colors.textTertiary : DS.Colors.warning) .frame(width: 16) - + Text("Microphone") .font(.system(size: 13, weight: .medium)) .foregroundColor(DS.Colors.textSecondary) } - + Spacer() - + if isGranted { HStack(spacing: 4) { Circle() @@ -490,7 +573,7 @@ struct CompanionPanelView: View { } .padding(.vertical, 6) } - + private func permissionRow( label: String, iconName: String, @@ -503,14 +586,14 @@ struct CompanionPanelView: View { .font(.system(size: 12, weight: .medium)) .foregroundColor(isGranted ? DS.Colors.textTertiary : DS.Colors.warning) .frame(width: 16) - + Text(label) .font(.system(size: 13, weight: .medium)) .foregroundColor(DS.Colors.textSecondary) } - + Spacer() - + if isGranted { HStack(spacing: 4) { Circle() @@ -542,11 +625,11 @@ struct CompanionPanelView: View { } .padding(.vertical, 6) } - - - + + + // MARK: - Show Clicky Cursor Toggle - + private var showClickyCursorToggleRow: some View { HStack { HStack(spacing: 8) { @@ -554,14 +637,14 @@ struct CompanionPanelView: View { .font(.system(size: 12, weight: .medium)) .foregroundColor(DS.Colors.textTertiary) .frame(width: 16) - + Text("Show Clicky") .font(.system(size: 13, weight: .medium)) .foregroundColor(DS.Colors.textSecondary) } - + Spacer() - + Toggle("", isOn: Binding( get: { companionManager.isClickyCursorEnabled }, set: { companionManager.setClickyCursorEnabled($0) } @@ -573,7 +656,7 @@ struct CompanionPanelView: View { } .padding(.vertical, 4) } - + private var speechToTextProviderRow: some View { HStack { HStack(spacing: 8) { @@ -581,43 +664,174 @@ struct CompanionPanelView: View { .font(.system(size: 12, weight: .medium)) .foregroundColor(DS.Colors.textTertiary) .frame(width: 16) - + Text("Speech to Text") .font(.system(size: 13, weight: .medium)) .foregroundColor(DS.Colors.textSecondary) } - + Spacer() - + Text(companionManager.buddyDictationManager.transcriptionProviderDisplayName) .font(.system(size: 11, weight: .medium)) .foregroundColor(DS.Colors.textTertiary) } .padding(.vertical, 4) } - - // MARK: - Model Picker - - private var modelPickerRow: some View { + + // MARK: - Language Picker + + private var languagePickerRow: some View { HStack { - Text("Model") - .font(.system(size: 13, weight: .medium)) - .foregroundColor(DS.Colors.textSecondary) - + HStack(spacing: 8) { + Image(systemName: "globe") + .font(.system(size: 12, weight: .medium)) + .foregroundColor(DS.Colors.textTertiary) + .frame(width: 16) + + Text("Language") + .font(.system(size: 13, weight: .medium)) + .foregroundColor(DS.Colors.textSecondary) + } + Spacer() + + Menu { + // ElevenLabs supports up to 32 base languages automatically on flash 2.5 + Button("🇺🇸 English") { companionManager.selectedLanguageCode = "en" } + Button("🇫🇷 French") { companionManager.selectedLanguageCode = "fr" } + Button("🇪🇸 Spanish") { companionManager.selectedLanguageCode = "es" } + Button("🇩🇪 German") { companionManager.selectedLanguageCode = "de" } + Button("🇮🇹 Italian") { companionManager.selectedLanguageCode = "it" } + Button("🇯🇵 Japanese") { companionManager.selectedLanguageCode = "ja" } + Button("🇵🇹 Portuguese") { companionManager.selectedLanguageCode = "pt" } + Button("🇷🇺 Russian") { companionManager.selectedLanguageCode = "ru" } + // AssemblyAI supports these natively + } label: { + HStack(spacing: 4) { + Text(flagForLanguage(companionManager.selectedLanguageCode)) + .font(.system(size: 14)) + Image(systemName: "chevron.down") + .font(.system(size: 9)) + .foregroundColor(DS.Colors.textTertiary) + } + .padding(.horizontal, 10) + .padding(.vertical, 5) + .background( + RoundedRectangle(cornerRadius: 6, style: .continuous) + .fill(Color.white.opacity(0.06)) + ) + .overlay( + RoundedRectangle(cornerRadius: 6, style: .continuous) + .stroke(DS.Colors.borderSubtle, lineWidth: 0.5) + ) + } + .menuStyle(.borderlessButton) + .menuIndicator(.hidden) + .frame(maxWidth: 80) + } + .padding(.vertical, 4) + } - HStack(spacing: 0) { - modelOptionButton(label: "Sonnet", modelID: "claude-sonnet-4-6") - modelOptionButton(label: "Opus", modelID: "claude-opus-4-6") + private func flagForLanguage(_ code: String) -> String { + switch code { + case "fr": return "🇫🇷" + case "es": return "🇪🇸" + case "de": return "🇩🇪" + case "it": return "🇮🇹" + case "ja": return "🇯🇵" + case "pt": return "🇵🇹" + case "ru": return "🇷🇺" + default: return "🇺🇸" + } + } + + // MARK: - Model Picker + + private var modelPickerRow: some View { + VStack(spacing: 8) { + HStack { + Text("Model") + .font(.system(size: 13, weight: .medium)) + .foregroundColor(DS.Colors.textSecondary) + + Spacer() + + Menu { + // Section for Local LM Studio Models + Section(header: Text("LM Studio (Local)")) { + if companionManager.availableModels.isEmpty { + Text("No models found").disabled(true) + } else { + ForEach(companionManager.availableModels, id: \.self) { modelID in + Button(action: { + companionManager.setSelectedModel(modelID) + }) { + HStack { + Text(modelID) + if companionManager.selectedModel == modelID { + Image(systemName: "checkmark") + } + } + } + } + } + Button("Refresh LM Studio Models") { + companionManager.fetchAvailableModels() + } + } + + Divider() + + // Section for Cloud / Claude Models + Section(header: Text("Anthropic (Cloud)")) { + let claudeModels = [ + ("Claude 3.5 Sonnet", "claude-3-5-sonnet-20241022"), + ("Claude 3 Opus", "claude-3-opus-20240229"), + ("Claude 3 Haiku", "claude-3-haiku-20240307") + ] + + ForEach(claudeModels, id: \.1) { label, modelID in + Button(action: { + companionManager.setSelectedModel(modelID) + }) { + HStack { + Text(label) + if companionManager.selectedModel == modelID { + Image(systemName: "checkmark") + } + } + } + } + } + + } label: { + HStack(spacing: 4) { + Text(companionManager.selectedModel) + .font(.system(size: 11, weight: .medium)) + .foregroundColor(DS.Colors.textPrimary) + .lineLimit(1) + .truncationMode(.middle) + + Image(systemName: "chevron.down") + .font(.system(size: 9)) + .foregroundColor(DS.Colors.textTertiary) + } + .padding(.horizontal, 10) + .padding(.vertical, 5) + .background( + RoundedRectangle(cornerRadius: 6, style: .continuous) + .fill(Color.white.opacity(0.06)) + ) + .overlay( + RoundedRectangle(cornerRadius: 6, style: .continuous) + .stroke(DS.Colors.borderSubtle, lineWidth: 0.5) + ) } - .background( - RoundedRectangle(cornerRadius: 6, style: .continuous) - .fill(Color.white.opacity(0.06)) - ) - .overlay( - RoundedRectangle(cornerRadius: 6, style: .continuous) - .stroke(DS.Colors.borderSubtle, lineWidth: 0.5) - ) + .menuStyle(.borderlessButton) + .menuIndicator(.hidden) + .frame(maxWidth: 200) + } } .padding(.vertical, 4) } @@ -757,5 +971,4 @@ struct CompanionPanelView: View { return "Responding" } } - } diff --git a/leanring-buddy/ElevenLabsTTSClient.swift b/leanring-buddy/ElevenLabsTTSClient.swift index 35545c9d..f93752ea 100644 --- a/leanring-buddy/ElevenLabsTTSClient.swift +++ b/leanring-buddy/ElevenLabsTTSClient.swift @@ -12,7 +12,8 @@ import Foundation @MainActor final class ElevenLabsTTSClient { - private let proxyURL: URL + var proxyURL: String + private var apiURL: URL { URL(string: proxyURL)! } private let session: URLSession /// The audio player for the current TTS playback. Kept alive so the @@ -20,7 +21,7 @@ final class ElevenLabsTTSClient { private var audioPlayer: AVAudioPlayer? init(proxyURL: String) { - self.proxyURL = URL(string: proxyURL)! + self.proxyURL = proxyURL let configuration = URLSessionConfiguration.default configuration.timeoutIntervalForRequest = 30 @@ -31,7 +32,7 @@ final class ElevenLabsTTSClient { /// Sends `text` to ElevenLabs TTS and plays the resulting audio. /// Throws on network or decoding errors. Cancellation-safe. func speakText(_ text: String) async throws { - var request = URLRequest(url: proxyURL) + var request = URLRequest(url: apiURL) request.httpMethod = "POST" request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.setValue("audio/mpeg", forHTTPHeaderField: "Accept") diff --git a/leanring-buddy/OpenAIAPI.swift b/leanring-buddy/OpenAIAPI.swift index d0c3f2ae..951ca71e 100644 --- a/leanring-buddy/OpenAIAPI.swift +++ b/leanring-buddy/OpenAIAPI.swift @@ -7,14 +7,14 @@ import Foundation /// OpenAI API helper for vision analysis class OpenAIAPI { - private let apiKey: String + var apiKey: String private let apiURL: URL - private let model: String + var model: String private let session: URLSession - init(apiKey: String, model: String = "gpt-5.2-2025-12-11") { + init(apiKey: String = "", model: String = "") { self.apiKey = apiKey - self.apiURL = URL(string: "https://api.openai.com/v1/chat/completions")! + self.apiURL = URL(string: "http://127.0.0.1:1234/v1/chat/completions")! self.model = model // Use .default instead of .ephemeral so TLS session tickets are cached. @@ -32,7 +32,8 @@ class OpenAIAPI { // Fire a lightweight HEAD request in the background to pre-establish the TLS // connection. This caches the TLS session ticket so the first real API call // (which carries a large image payload) doesn't need a cold TLS handshake. - warmUpTLSConnection() + // Disabled for LM Studio as it causes "Unexpected endpoint or method" errors. + // warmUpTLSConnection() } /// Sends a no-op HEAD request to the API host to establish and cache a TLS session. @@ -139,4 +140,102 @@ class OpenAIAPI { let duration = Date().timeIntervalSince(startTime) return (text: text, duration: duration) } + + /// Send a request to OpenAI with one or more labeled images progressively, via SSE. + func analyzeImageStreaming( + images: [(data: Data, label: String)], + systemPrompt: String, + conversationHistory: [(userPlaceholder: String, assistantResponse: String)] = [], + userPrompt: String, + onTextChunk: @MainActor @Sendable (String) -> Void + ) async throws -> (text: String, duration: TimeInterval) { + let startTime = Date() + + // Build request + var request = URLRequest(url: apiURL) + request.httpMethod = "POST" + request.timeoutInterval = 120 + request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization") + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + + // Build messages array + var messages: [[String: Any]] = [] + + // Add system message first + messages.append([ + "role": "system", + "content": systemPrompt + ]) + + // Add conversation history + for (userPlaceholder, assistantResponse) in conversationHistory { + messages.append(["role": "user", "content": userPlaceholder]) + messages.append(["role": "assistant", "content": assistantResponse]) + } + + // Build current message with all labeled images + prompt + var contentBlocks: [[String: Any]] = [] + for image in images { + contentBlocks.append([ + "type": "text", + "text": image.label + ]) + contentBlocks.append([ + "type": "image_url", + "image_url": [ + "url": "data:image/jpeg;base64,\(image.data.base64EncodedString())" + ] + ]) + } + contentBlocks.append([ + "type": "text", + "text": userPrompt + ]) + messages.append(["role": "user", "content": contentBlocks]) + + // Build request body + let body: [String: Any] = [ + "model": model, + "max_tokens": 600, // some local models still require max_tokens instead of max_completion_tokens + "messages": messages, + "stream": true + ] + + let bodyData = try JSONSerialization.data(withJSONObject: body) + request.httpBody = bodyData + let payloadMB = Double(bodyData.count) / 1_048_576.0 + print("🌐 OpenAI streaming request: \(String(format: "%.1f", payloadMB))MB, \(images.count) image(s)") + + let (result, response) = try await session.bytes(for: request) + + guard let httpResponse = response as? HTTPURLResponse, + (200...299).contains(httpResponse.statusCode) else { + throw NSError( + domain: "OpenAIAPI", + code: (response as? HTTPURLResponse)?.statusCode ?? -1, + userInfo: [NSLocalizedDescriptionKey: "API Error: Invalid response"] + ) + } + + var fullText = "" + + for try await line in result.lines { + guard line.hasPrefix("data: ") else { continue } + let jsonString = line.dropFirst(6).trimmingCharacters(in: .whitespaces) + guard jsonString != "[DONE]" else { break } + guard let data = jsonString.data(using: .utf8) else { continue } + + if let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any], + let choices = json["choices"] as? [[String: Any]], + let firstChoice = choices.first, + let delta = firstChoice["delta"] as? [String: Any], + let contentChunk = delta["content"] as? String { + fullText += contentChunk + await onTextChunk(contentChunk) + } + } + + let duration = Date().timeIntervalSince(startTime) + return (text: fullText, duration: duration) + } } diff --git a/leanring-buddy/OpenAIAudioTranscriptionProvider.swift b/leanring-buddy/OpenAIAudioTranscriptionProvider.swift index 75092092..54dab211 100644 --- a/leanring-buddy/OpenAIAudioTranscriptionProvider.swift +++ b/leanring-buddy/OpenAIAudioTranscriptionProvider.swift @@ -34,6 +34,7 @@ final class OpenAIAudioTranscriptionProvider: BuddyTranscriptionProvider { } func startStreamingSession( + languageCode: String, keyterms: [String], onTranscriptUpdate: @escaping (String) -> Void, onFinalTranscriptReady: @escaping (String) -> Void, @@ -49,6 +50,7 @@ final class OpenAIAudioTranscriptionProvider: BuddyTranscriptionProvider { apiKey: apiKey, modelName: modelName, keyterms: keyterms, + languageCode: languageCode, onTranscriptUpdate: onTranscriptUpdate, onFinalTranscriptReady: onFinalTranscriptReady, onError: onError @@ -69,6 +71,7 @@ private final class OpenAIAudioTranscriptionSession: BuddyStreamingTranscription private let apiKey: String private let modelName: String private let keyterms: [String] + private let languageCode: String private let onTranscriptUpdate: (String) -> Void private let onFinalTranscriptReady: (String) -> Void private let onError: (Error) -> Void @@ -89,6 +92,7 @@ private final class OpenAIAudioTranscriptionSession: BuddyStreamingTranscription apiKey: String, modelName: String, keyterms: [String], + languageCode: String, onTranscriptUpdate: @escaping (String) -> Void, onFinalTranscriptReady: @escaping (String) -> Void, onError: @escaping (Error) -> Void @@ -96,6 +100,7 @@ private final class OpenAIAudioTranscriptionSession: BuddyStreamingTranscription self.apiKey = apiKey self.modelName = modelName self.keyterms = keyterms + self.languageCode = languageCode self.onTranscriptUpdate = onTranscriptUpdate self.onFinalTranscriptReady = onFinalTranscriptReady self.onError = onError @@ -234,7 +239,7 @@ private final class OpenAIAudioTranscriptionSession: BuddyStreamingTranscription ) requestBodyData.appendMultipartFormField( named: "language", - value: "en", + value: languageCode, usingBoundary: boundary ) requestBodyData.appendMultipartFormField( diff --git a/leanring-buddy/WorkerEnvironment.swift b/leanring-buddy/WorkerEnvironment.swift new file mode 100644 index 00000000..88fbaa91 --- /dev/null +++ b/leanring-buddy/WorkerEnvironment.swift @@ -0,0 +1,52 @@ +// +// WorkerEnvironment.swift +// leanring-buddy +// +// Automatically detects if the local development Cloudflare Worker is running. +// If yes, it connects to localhost out-of-the-box. Otherwise, it falls back to production. +// + +import Foundation + +actor WorkerEnvironment { + static let shared = WorkerEnvironment() + + private var cachedBaseURL: String? + + // Replace this with your actual production Worker URL if known + private let defaultProdURL = "https://your-worker-name.your-subdomain.workers.dev" + + func getBaseURL() async -> String { + if let cached = cachedBaseURL { + return cached + } + + if let saved = UserDefaults.standard.string(forKey: "workerBaseURL"), !saved.isEmpty { + cachedBaseURL = saved + return saved // Allow optional user override + } + + let localURL = "http://localhost:8787" + var request = URLRequest(url: URL(string: localURL)!) + request.httpMethod = "OPTIONS" // Quick lightweight pre-flight + request.timeoutInterval = 0.5 // Fail very fast if it's not running + + do { + let (_, response) = try await URLSession.shared.data(for: request) + if (response as? HTTPURLResponse) != nil { + print("🌐 Auto-detected local worker. Using \(localURL)") + cachedBaseURL = localURL + return localURL + } + } catch { + print("🌐 Local worker unreachable. Falling back to prod: \(defaultProdURL)") + } + + cachedBaseURL = defaultProdURL + return defaultProdURL + } + + func resetCache() { + cachedBaseURL = nil + } +} \ No newline at end of file diff --git a/worker/.gitignore b/worker/.gitignore new file mode 100644 index 00000000..74f12373 --- /dev/null +++ b/worker/.gitignore @@ -0,0 +1,2 @@ +wrangler.toml +.wrangler diff --git a/worker/package-lock.json b/worker/package-lock.json index c2383cc1..c3752f47 100644 --- a/worker/package-lock.json +++ b/worker/package-lock.json @@ -6,31 +6,28 @@ "": { "name": "clicky-proxy", "devDependencies": { - "wrangler": "^3.0.0" + "wrangler": "^4.81.0" } }, "node_modules/@cloudflare/kv-asset-handler": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.3.4.tgz", - "integrity": "sha512-YLPHc8yASwjNkmcDMQMY35yiWjoKAKnhUbPRszBRS0YgH+IXtsMp61j+yTcnCE3oO2DgP0U3iejLC8FTtKDC8Q==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.4.2.tgz", + "integrity": "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==", "dev": true, "license": "MIT OR Apache-2.0", - "dependencies": { - "mime": "^3.0.0" - }, "engines": { - "node": ">=16.13" + "node": ">=18.0.0" } }, "node_modules/@cloudflare/unenv-preset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.0.2.tgz", - "integrity": "sha512-nyzYnlZjjV5xT3LizahG1Iu6mnrCaxglJ04rZLpDwlDVDZ7v46lNsfxhV3A/xtfgQuSHmLnc6SVI+KwBpc3Lwg==", + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.16.0.tgz", + "integrity": "sha512-8ovsRpwzPoEqPUzoErAYVv8l3FMZNeBVQfJTvtzP4AgLSRGZISRfuChFxHWUQd3n6cnrwkuTGxT+2cGo8EsyYg==", "dev": true, "license": "MIT OR Apache-2.0", "peerDependencies": { - "unenv": "2.0.0-rc.14", - "workerd": "^1.20250124.0" + "unenv": "2.0.0-rc.24", + "workerd": "1.20260301.1 || ~1.20260302.1 || ~1.20260303.1 || ~1.20260304.1 || >1.20260305.0 <2.0.0-0" }, "peerDependenciesMeta": { "workerd": { @@ -39,9 +36,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-64": { - "version": "1.20250718.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20250718.0.tgz", - "integrity": "sha512-FHf4t7zbVN8yyXgQ/r/GqLPaYZSGUVzeR7RnL28Mwj2djyw2ZergvytVc7fdGcczl6PQh+VKGfZCfUqpJlbi9g==", + "version": "1.20260405.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260405.1.tgz", + "integrity": "sha512-EbmdBcmeIGogKG4V1odSWQe7z4rHssUD4iaXv0cXA22/MFrzH3iQT0R+FJFyhucGtih/9B9E+6j0QbSQD8xT3w==", "cpu": [ "x64" ], @@ -56,9 +53,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-arm64": { - "version": "1.20250718.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20250718.0.tgz", - "integrity": "sha512-fUiyUJYyqqp4NqJ0YgGtp4WJh/II/YZsUnEb6vVy5Oeas8lUOxnN+ZOJ8N/6/5LQCVAtYCChRiIrBbfhTn5Z8Q==", + "version": "1.20260405.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260405.1.tgz", + "integrity": "sha512-r44r418bOQtoP+Odu+L/BQM9q5cRSXRd1N167PgZQIo4MlqzTwHO4L0wwXhxbcV/PF46rrQre/uTFS8R0R+xSQ==", "cpu": [ "arm64" ], @@ -73,9 +70,9 @@ } }, "node_modules/@cloudflare/workerd-linux-64": { - "version": "1.20250718.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20250718.0.tgz", - "integrity": "sha512-5+eb3rtJMiEwp08Kryqzzu8d1rUcK+gdE442auo5eniMpT170Dz0QxBrqkg2Z48SFUPYbj+6uknuA5tzdRSUSg==", + "version": "1.20260405.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260405.1.tgz", + "integrity": "sha512-Aaq3RWnaTCzMBo77wC8fjOx+SFdO/rlcXa6HAf+PJs51LyMISFOBCJKqSlS6Irphen0WHHxFKPHUO9bjfj8g2g==", "cpu": [ "x64" ], @@ -90,9 +87,9 @@ } }, "node_modules/@cloudflare/workerd-linux-arm64": { - "version": "1.20250718.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20250718.0.tgz", - "integrity": "sha512-Aa2M/DVBEBQDdATMbn217zCSFKE+ud/teS+fFS+OQqKABLn0azO2qq6ANAHYOIE6Q3Sq4CxDIQr8lGdaJHwUog==", + "version": "1.20260405.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260405.1.tgz", + "integrity": "sha512-Lbp9Z2wiMzy3Sji3YwMHK5WDlejsH3jF4swAFEv7+jIf3NowZHga3GzwTypNRmcwnfz/XrqQ7Hc0Ul9OoU/lCw==", "cpu": [ "arm64" ], @@ -107,9 +104,9 @@ } }, "node_modules/@cloudflare/workerd-windows-64": { - "version": "1.20250718.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20250718.0.tgz", - "integrity": "sha512-dY16RXKffmugnc67LTbyjdDHZn5NoTF1yHEf2fN4+OaOnoGSp3N1x77QubTDwqZ9zECWxgQfDLjddcH8dWeFhg==", + "version": "1.20260405.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260405.1.tgz", + "integrity": "sha512-FhE0kt93kj5JnSPVqi4BAXpQQENyKnuSOoJLd35mkMMGhtPrwv5EsReJdck0S8hUocCBlb+U0RmP8ta6k41HjQ==", "cpu": [ "x64" ], @@ -147,34 +144,27 @@ "tslib": "^2.4.0" } }, - "node_modules/@esbuild-plugins/node-globals-polyfill": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-globals-polyfill/-/node-globals-polyfill-0.2.3.tgz", - "integrity": "sha512-r3MIryXDeXDOZh7ih1l/yE9ZLORCd5e8vWg02azWRGj5SPTuoh69A2AIyn0Z31V/kHBfZ4HgWJ+OK3GTTwLmnw==", - "dev": true, - "license": "ISC", - "peerDependencies": { - "esbuild": "*" - } - }, - "node_modules/@esbuild-plugins/node-modules-polyfill": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-modules-polyfill/-/node-modules-polyfill-0.2.2.tgz", - "integrity": "sha512-LXV7QsWJxRuMYvKbiznh+U1ilIop3g2TeKRzUxOG5X3YITc8JyyTa90BmLwqqv0YnX4v32CSlG+vsziZp9dMvA==", + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "escape-string-regexp": "^4.0.0", - "rollup-plugin-node-polyfills": "^0.2.1" - }, - "peerDependencies": { - "esbuild": "*" + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/android-arm": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", - "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", "cpu": [ "arm" ], @@ -185,13 +175,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", - "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", "cpu": [ "arm64" ], @@ -202,13 +192,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", - "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", "cpu": [ "x64" ], @@ -219,13 +209,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", - "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", "cpu": [ "arm64" ], @@ -236,13 +226,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", - "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", "cpu": [ "x64" ], @@ -253,13 +243,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", - "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", "cpu": [ "arm64" ], @@ -270,13 +260,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", - "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", "cpu": [ "x64" ], @@ -287,13 +277,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", - "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", "cpu": [ "arm" ], @@ -304,13 +294,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", - "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", "cpu": [ "arm64" ], @@ -321,13 +311,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", - "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", "cpu": [ "ia32" ], @@ -338,13 +328,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", - "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", "cpu": [ "loong64" ], @@ -355,13 +345,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", - "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", "cpu": [ "mips64el" ], @@ -372,13 +362,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", - "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", "cpu": [ "ppc64" ], @@ -389,13 +379,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", - "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", "cpu": [ "riscv64" ], @@ -406,13 +396,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", - "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", "cpu": [ "s390x" ], @@ -423,13 +413,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", - "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", "cpu": [ "x64" ], @@ -440,13 +430,30 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", - "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", "cpu": [ "x64" ], @@ -457,13 +464,30 @@ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", - "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", "cpu": [ "x64" ], @@ -474,13 +498,30 @@ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", - "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", "cpu": [ "x64" ], @@ -491,13 +532,13 @@ "sunos" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", - "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", "cpu": [ "arm64" ], @@ -508,13 +549,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", - "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", "cpu": [ "ia32" ], @@ -525,13 +566,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", - "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", "cpu": [ "x64" ], @@ -542,23 +583,23 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/@fastify/busboy": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", - "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", - "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", "cpu": [ "arm64" ], @@ -575,13 +616,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.0.4" + "@img/sharp-libvips-darwin-arm64": "1.2.4" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", - "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", "cpu": [ "x64" ], @@ -598,13 +639,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.0.4" + "@img/sharp-libvips-darwin-x64": "1.2.4" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", - "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", "cpu": [ "arm64" ], @@ -619,9 +660,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", - "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", "cpu": [ "x64" ], @@ -636,9 +677,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", - "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", "cpu": [ "arm" ], @@ -656,9 +697,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", - "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", "cpu": [ "arm64" ], @@ -675,10 +716,50 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", - "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", "cpu": [ "s390x" ], @@ -696,9 +777,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", - "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", "cpu": [ "x64" ], @@ -716,9 +797,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", - "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", "cpu": [ "arm64" ], @@ -736,9 +817,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", - "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", "cpu": [ "x64" ], @@ -756,9 +837,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", - "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", "cpu": [ "arm" ], @@ -778,13 +859,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.0.5" + "@img/sharp-libvips-linux-arm": "1.2.4" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", - "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", "cpu": [ "arm64" ], @@ -804,13 +885,65 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.0.4" + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", - "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", "cpu": [ "s390x" ], @@ -830,13 +963,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.0.4" + "@img/sharp-libvips-linux-s390x": "1.2.4" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", - "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", "cpu": [ "x64" ], @@ -856,13 +989,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.0.4" + "@img/sharp-libvips-linux-x64": "1.2.4" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", - "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", "cpu": [ "arm64" ], @@ -882,13 +1015,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", - "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", "cpu": [ "x64" ], @@ -908,13 +1041,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", - "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", "cpu": [ "wasm32" ], @@ -922,7 +1055,7 @@ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.2.0" + "@emnapi/runtime": "^1.7.0" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -931,10 +1064,30 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", - "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", "cpu": [ "ia32" ], @@ -952,9 +1105,9 @@ } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", - "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", "cpu": [ "x64" ], @@ -999,134 +1152,100 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", - "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "node_modules/@poppinss/colors": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.6.tgz", + "integrity": "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==", "dev": true, "license": "MIT", - "engines": { - "node": ">=0.4.0" + "dependencies": { + "kleur": "^4.1.5" } }, - "node_modules/as-table": { - "version": "1.0.55", - "resolved": "https://registry.npmjs.org/as-table/-/as-table-1.0.55.tgz", - "integrity": "sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==", + "node_modules/@poppinss/dumper": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@poppinss/dumper/-/dumper-0.6.5.tgz", + "integrity": "sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==", "dev": true, "license": "MIT", "dependencies": { - "printable-characters": "^1.0.42" + "@poppinss/colors": "^4.1.5", + "@sindresorhus/is": "^7.0.2", + "supports-color": "^10.0.0" } }, - "node_modules/blake3-wasm": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz", - "integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==", + "node_modules/@poppinss/exception": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.3.tgz", + "integrity": "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==", "dev": true, "license": "MIT" }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "node_modules/@sindresorhus/is": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.2.0.tgz", + "integrity": "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==", "dev": true, "license": "MIT", - "optional": true, - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, "engines": { - "node": ">=12.5.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "color-name": "~1.1.4" + "node": ">=18" }, - "engines": { - "node": ">=7.0.0" + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "node_modules/@speed-highlight/core": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.15.tgz", + "integrity": "sha512-BMq1K3DsElxDWawkX6eLg9+CKJrTVGCBAWVuHXVUV2u0s2711qiChLSId6ikYPfxhdYocLNt3wWwSvDiTvFabw==", "dev": true, - "license": "MIT", - "optional": true + "license": "CC0-1.0" }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "node_modules/blake3-wasm": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz", + "integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==", "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } + "license": "MIT" }, "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/data-uri-to-buffer": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-2.0.2.tgz", - "integrity": "sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==", - "dev": true, - "license": "MIT" - }, - "node_modules/defu": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.6.tgz", - "integrity": "sha512-f8mefEW4WIVg4LckePx3mALjQSPQgFlg9U8yaPdlsbdYcHQyj9n2zL2LJEA52smeYxOvmd/nB7TpMtHGMTHcug==", - "dev": true, - "license": "MIT" - }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true, "license": "Apache-2.0", - "optional": true, "engines": { "node": ">=8" } }, + "node_modules/error-stack-parser-es": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", + "integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/esbuild": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", - "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -1134,73 +1253,37 @@ "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/android-arm": "0.17.19", - "@esbuild/android-arm64": "0.17.19", - "@esbuild/android-x64": "0.17.19", - "@esbuild/darwin-arm64": "0.17.19", - "@esbuild/darwin-x64": "0.17.19", - "@esbuild/freebsd-arm64": "0.17.19", - "@esbuild/freebsd-x64": "0.17.19", - "@esbuild/linux-arm": "0.17.19", - "@esbuild/linux-arm64": "0.17.19", - "@esbuild/linux-ia32": "0.17.19", - "@esbuild/linux-loong64": "0.17.19", - "@esbuild/linux-mips64el": "0.17.19", - "@esbuild/linux-ppc64": "0.17.19", - "@esbuild/linux-riscv64": "0.17.19", - "@esbuild/linux-s390x": "0.17.19", - "@esbuild/linux-x64": "0.17.19", - "@esbuild/netbsd-x64": "0.17.19", - "@esbuild/openbsd-x64": "0.17.19", - "@esbuild/sunos-x64": "0.17.19", - "@esbuild/win32-arm64": "0.17.19", - "@esbuild/win32-ia32": "0.17.19", - "@esbuild/win32-x64": "0.17.19" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/estree-walker": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", - "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/exit-hook": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-2.2.1.tgz", - "integrity": "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" } }, - "node_modules/exsolve": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", - "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", - "dev": true, - "license": "MIT" - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1216,98 +1299,37 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/get-source": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/get-source/-/get-source-2.0.12.tgz", - "integrity": "sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==", - "dev": true, - "license": "Unlicense", - "dependencies": { - "data-uri-to-buffer": "^2.0.0", - "source-map": "^0.6.1" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/is-arrayish": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", - "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "sourcemap-codec": "^1.4.8" - } - }, - "node_modules/mime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", - "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", "dev": true, "license": "MIT", - "bin": { - "mime": "cli.js" - }, "engines": { - "node": ">=10.0.0" + "node": ">=6" } }, "node_modules/miniflare": { - "version": "3.20250718.3", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20250718.3.tgz", - "integrity": "sha512-JuPrDJhwLrNLEJiNLWO7ZzJrv/Vv9kZuwMYCfv0LskQDM6Eonw4OvywO3CH/wCGjgHzha/qyjUh8JQ068TjDgQ==", + "version": "4.20260405.0", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260405.0.tgz", + "integrity": "sha512-tpr4XdWMq7zFdsHH+CS0XS47nQzlRZH0rMJ1vobOZbkrs3cIj7qbD40ON616hDnzHxwqwB2qKHzmmuj6oRisSQ==", "dev": true, "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "0.8.1", - "acorn": "8.14.0", - "acorn-walk": "8.3.2", - "exit-hook": "2.2.1", - "glob-to-regexp": "0.4.1", - "stoppable": "1.1.0", - "undici": "^5.28.5", - "workerd": "1.20250718.0", + "sharp": "^0.34.5", + "undici": "7.24.4", + "workerd": "1.20260405.1", "ws": "8.18.0", - "youch": "3.3.4", - "zod": "3.22.3" + "youch": "4.1.0-beta.10" }, "bin": { "miniflare": "bootstrap.js" }, "engines": { - "node": ">=16.13" - } - }, - "node_modules/mustache": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", - "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", - "dev": true, - "license": "MIT", - "bin": { - "mustache": "bin/mustache" + "node": ">=18.0.0" } }, - "node_modules/ohash": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", - "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", - "dev": true, - "license": "MIT" - }, "node_modules/path-to-regexp": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", @@ -1322,53 +1344,12 @@ "dev": true, "license": "MIT" }, - "node_modules/printable-characters": { - "version": "1.0.42", - "resolved": "https://registry.npmjs.org/printable-characters/-/printable-characters-1.0.42.tgz", - "integrity": "sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==", - "dev": true, - "license": "Unlicense" - }, - "node_modules/rollup-plugin-inject": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rollup-plugin-inject/-/rollup-plugin-inject-3.0.2.tgz", - "integrity": "sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w==", - "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-inject.", - "dev": true, - "license": "MIT", - "dependencies": { - "estree-walker": "^0.6.1", - "magic-string": "^0.25.3", - "rollup-pluginutils": "^2.8.1" - } - }, - "node_modules/rollup-plugin-node-polyfills": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/rollup-plugin-node-polyfills/-/rollup-plugin-node-polyfills-0.2.1.tgz", - "integrity": "sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA==", - "dev": true, - "license": "MIT", - "dependencies": { - "rollup-plugin-inject": "^3.0.0" - } - }, - "node_modules/rollup-pluginutils": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", - "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "estree-walker": "^0.6.1" - } - }, "node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", - "optional": true, "bin": { "semver": "bin/semver.js" }, @@ -1377,17 +1358,16 @@ } }, "node_modules/sharp": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", - "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", - "optional": true, "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.3", - "semver": "^7.6.3" + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -1396,76 +1376,43 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.33.5", - "@img/sharp-darwin-x64": "0.33.5", - "@img/sharp-libvips-darwin-arm64": "1.0.4", - "@img/sharp-libvips-darwin-x64": "1.0.4", - "@img/sharp-libvips-linux-arm": "1.0.5", - "@img/sharp-libvips-linux-arm64": "1.0.4", - "@img/sharp-libvips-linux-s390x": "1.0.4", - "@img/sharp-libvips-linux-x64": "1.0.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", - "@img/sharp-libvips-linuxmusl-x64": "1.0.4", - "@img/sharp-linux-arm": "0.33.5", - "@img/sharp-linux-arm64": "0.33.5", - "@img/sharp-linux-s390x": "0.33.5", - "@img/sharp-linux-x64": "0.33.5", - "@img/sharp-linuxmusl-arm64": "0.33.5", - "@img/sharp-linuxmusl-x64": "0.33.5", - "@img/sharp-wasm32": "0.33.5", - "@img/sharp-win32-ia32": "0.33.5", - "@img/sharp-win32-x64": "0.33.5" - } - }, - "node_modules/simple-swizzle": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", - "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/supports-color": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", "dev": true, "license": "MIT", - "optional": true, - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "deprecated": "Please use @jridgewell/sourcemap-codec instead", - "dev": true, - "license": "MIT" - }, - "node_modules/stacktracey": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/stacktracey/-/stacktracey-2.2.0.tgz", - "integrity": "sha512-ETyQEz+CzXiLjEbyJqpbp+/T79RQD/6wqFucRBIlVNZfYq2Ay7wbretD4cxpbymZlaPWx58aIhPEY1Cr8DlVvg==", - "dev": true, - "license": "Unlicense", - "dependencies": { - "as-table": "^1.0.36", - "get-source": "^2.0.12" - } - }, - "node_modules/stoppable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", - "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4", - "npm": ">=6" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "node_modules/tslib": { @@ -1476,44 +1423,30 @@ "license": "0BSD", "optional": true }, - "node_modules/ufo": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", - "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", - "dev": true, - "license": "MIT" - }, "node_modules/undici": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", - "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.4.tgz", + "integrity": "sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==", "dev": true, "license": "MIT", - "dependencies": { - "@fastify/busboy": "^2.0.0" - }, "engines": { - "node": ">=14.0" + "node": ">=20.18.1" } }, "node_modules/unenv": { - "version": "2.0.0-rc.14", - "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.14.tgz", - "integrity": "sha512-od496pShMen7nOy5VmVJCnq8rptd45vh6Nx/r2iPbrba6pa6p+tS2ywuIHRZ/OBvSbQZB0kWvpO9XBNVFXHD3Q==", + "version": "2.0.0-rc.24", + "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz", + "integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==", "dev": true, "license": "MIT", "dependencies": { - "defu": "^6.1.4", - "exsolve": "^1.0.1", - "ohash": "^2.0.10", - "pathe": "^2.0.3", - "ufo": "^1.5.4" + "pathe": "^2.0.3" } }, "node_modules/workerd": { - "version": "1.20250718.0", - "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20250718.0.tgz", - "integrity": "sha512-kqkIJP/eOfDlUyBzU7joBg+tl8aB25gEAGqDap+nFWb+WHhnooxjGHgxPBy3ipw2hnShPFNOQt5lFRxbwALirg==", + "version": "1.20260405.1", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260405.1.tgz", + "integrity": "sha512-bSaRWCv9iO8/FWpgZRjHLGZLolX5s1AErRSYaTECMMHOZKuCbl2+ehnSyc+ZZ/70y+9owADmN6HoYEWvBlJdYw==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -1524,44 +1457,41 @@ "node": ">=16" }, "optionalDependencies": { - "@cloudflare/workerd-darwin-64": "1.20250718.0", - "@cloudflare/workerd-darwin-arm64": "1.20250718.0", - "@cloudflare/workerd-linux-64": "1.20250718.0", - "@cloudflare/workerd-linux-arm64": "1.20250718.0", - "@cloudflare/workerd-windows-64": "1.20250718.0" + "@cloudflare/workerd-darwin-64": "1.20260405.1", + "@cloudflare/workerd-darwin-arm64": "1.20260405.1", + "@cloudflare/workerd-linux-64": "1.20260405.1", + "@cloudflare/workerd-linux-arm64": "1.20260405.1", + "@cloudflare/workerd-windows-64": "1.20260405.1" } }, "node_modules/wrangler": { - "version": "3.114.17", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.114.17.tgz", - "integrity": "sha512-tAvf7ly+tB+zwwrmjsCyJ2pJnnc7SZhbnNwXbH+OIdVas3zTSmjcZOjmLKcGGptssAA3RyTKhcF9BvKZzMUycA==", + "version": "4.81.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.81.0.tgz", + "integrity": "sha512-9fLPDuDcb8Nu6iXrl5E3HGYt3TVhQr/UvqtTvWr9Nl1X7PlQrmWMwQCfSioqN8VHYyQCyESV5jQsoKg8Sx+sEA==", "dev": true, "license": "MIT OR Apache-2.0", "dependencies": { - "@cloudflare/kv-asset-handler": "0.3.4", - "@cloudflare/unenv-preset": "2.0.2", - "@esbuild-plugins/node-globals-polyfill": "0.2.3", - "@esbuild-plugins/node-modules-polyfill": "0.2.2", + "@cloudflare/kv-asset-handler": "0.4.2", + "@cloudflare/unenv-preset": "2.16.0", "blake3-wasm": "2.1.5", - "esbuild": "0.17.19", - "miniflare": "3.20250718.3", + "esbuild": "0.27.3", + "miniflare": "4.20260405.0", "path-to-regexp": "6.3.0", - "unenv": "2.0.0-rc.14", - "workerd": "1.20250718.0" + "unenv": "2.0.0-rc.24", + "workerd": "1.20260405.1" }, "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" }, "engines": { - "node": ">=16.17.0" + "node": ">=20.3.0" }, "optionalDependencies": { - "fsevents": "~2.3.2", - "sharp": "^0.33.5" + "fsevents": "~2.3.2" }, "peerDependencies": { - "@cloudflare/workers-types": "^4.20250408.0" + "@cloudflare/workers-types": "^4.20260405.1" }, "peerDependenciesMeta": { "@cloudflare/workers-types": { @@ -1592,25 +1522,28 @@ } }, "node_modules/youch": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/youch/-/youch-3.3.4.tgz", - "integrity": "sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg==", + "version": "4.1.0-beta.10", + "resolved": "https://registry.npmjs.org/youch/-/youch-4.1.0-beta.10.tgz", + "integrity": "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==", "dev": true, "license": "MIT", "dependencies": { - "cookie": "^0.7.1", - "mustache": "^4.2.0", - "stacktracey": "^2.1.8" + "@poppinss/colors": "^4.1.5", + "@poppinss/dumper": "^0.6.4", + "@speed-highlight/core": "^1.2.7", + "cookie": "^1.0.2", + "youch-core": "^0.3.3" } }, - "node_modules/zod": { - "version": "3.22.3", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.3.tgz", - "integrity": "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==", + "node_modules/youch-core": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/youch-core/-/youch-core-0.3.3.tgz", + "integrity": "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==", "dev": true, "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" + "dependencies": { + "@poppinss/exception": "^1.2.2", + "error-stack-parser-es": "^1.0.5" } } } diff --git a/worker/package.json b/worker/package.json index 445d90a6..857b744e 100644 --- a/worker/package.json +++ b/worker/package.json @@ -6,6 +6,6 @@ "deploy": "wrangler deploy" }, "devDependencies": { - "wrangler": "^3.0.0" + "wrangler": "^4.81.0" } } diff --git a/worker/wrangler.toml b/worker/wrangler.toml deleted file mode 100644 index b4bdbf38..00000000 --- a/worker/wrangler.toml +++ /dev/null @@ -1,6 +0,0 @@ -name = "clicky-proxy" -main = "src/index.ts" -compatibility_date = "2024-01-01" - -[vars] -ELEVENLABS_VOICE_ID = "kPzsL2i3teMYv0FxEYQ6"