From 8d7adedeedb14e85dd474f6e300b91ab2cbf41fa Mon Sep 17 00:00:00 2001 From: Tony Giorgio Date: Thu, 17 Apr 2025 16:42:30 -0500 Subject: [PATCH 1/6] Apple Auth start --- frontend/src-tauri/Cargo.lock | 13 ++++ frontend/src-tauri/Cargo.toml | 1 + frontend/src-tauri/apple-sign-in-info.md | 32 +++++++++ frontend/src-tauri/capabilities/default.json | 3 +- .../src-tauri/capabilities/mobile-ios.json | 25 +++++++ .../apple/maple_iOS/maple_iOS.entitlements | 4 ++ frontend/src-tauri/src/lib.rs | 3 + frontend/src/components/icons/Apple.tsx | 21 ++++++ frontend/src/routes/desktop-auth.tsx | 8 ++- frontend/src/routes/login.tsx | 66 ++++++++++++++++++- frontend/src/routes/signup.tsx | 66 ++++++++++++++++++- 11 files changed, 238 insertions(+), 4 deletions(-) create mode 100644 frontend/src-tauri/apple-sign-in-info.md create mode 100644 frontend/src-tauri/capabilities/mobile-ios.json create mode 100644 frontend/src/components/icons/Apple.tsx diff --git a/frontend/src-tauri/Cargo.lock b/frontend/src-tauri/Cargo.lock index 3922909a4..5635879b3 100644 --- a/frontend/src-tauri/Cargo.lock +++ b/frontend/src-tauri/Cargo.lock @@ -2244,6 +2244,7 @@ dependencies = [ "tauri-plugin-log", "tauri-plugin-opener", "tauri-plugin-os", + "tauri-plugin-sign-in-with-apple", "tauri-plugin-updater", "tokio", ] @@ -4408,6 +4409,18 @@ dependencies = [ "thiserror 2.0.11", ] +[[package]] +name = "tauri-plugin-sign-in-with-apple" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9402eb8a70fdff5127fccc1f1666ebb06b5efc10e0eb656d8a70c798e26142e" +dependencies = [ + "serde", + "tauri", + "tauri-plugin", + "thiserror 2.0.11", +] + [[package]] name = "tauri-plugin-updater" version = "2.7.0" diff --git a/frontend/src-tauri/Cargo.toml b/frontend/src-tauri/Cargo.toml index 6d5f6d359..bc0ab1417 100644 --- a/frontend/src-tauri/Cargo.toml +++ b/frontend/src-tauri/Cargo.toml @@ -29,5 +29,6 @@ tauri-plugin = "2.1.1" tauri-plugin-deep-link = "2" tauri-plugin-opener = "2" tauri-plugin-os = "2" +tauri-plugin-sign-in-with-apple = "1.0.0" tokio = { version = "1.0", features = ["time"] } once_cell = "1.18.0" diff --git a/frontend/src-tauri/apple-sign-in-info.md b/frontend/src-tauri/apple-sign-in-info.md new file mode 100644 index 000000000..7f0821de7 --- /dev/null +++ b/frontend/src-tauri/apple-sign-in-info.md @@ -0,0 +1,32 @@ +# Sign in with Apple Integration for iOS + +## Overview +This document provides information about the Sign in with Apple integration for Maple on iOS. + +## Configuration +1. The integration uses the tauri-plugin-sign-in-with-apple crate (version 1.0.0) +2. Required entitlements have been added in maple_iOS.entitlements +3. The capability is registered in capabilities/default.json and capabilities/mobile-ios.json + +## Frontend Implementation +1. Apple Sign In button is only shown on iOS devices +2. Platform detection via `@tauri-apps/plugin-os`'s `type()` function +3. Authentication is handled through the plugin's command: `plugin:sign-in-with-apple|get_apple_id_credential` + +## Future Backend Work +The frontend implementation for Apple Sign In is complete, but the backend integration is not yet implemented. +When the backend is ready, the response from the Apple Sign In should be sent to your backend to: + +1. Verify the identity token with Apple +2. Create or update the user account +3. Generate authentication tokens for your app + +## Debugging +If you experience issues with Sign in with Apple: +1. Check the debug logs (debug option is enabled) +2. Verify that the entitlements are properly set +3. Ensure your Apple Developer account is configured correctly for Sign in with Apple + +## Resources +- [Apple Developer Documentation](https://developer.apple.com/documentation/sign_in_with_apple) +- [tauri-plugin-sign-in-with-apple on crates.io](https://crates.io/crates/tauri-plugin-sign-in-with-apple) \ No newline at end of file diff --git a/frontend/src-tauri/capabilities/default.json b/frontend/src-tauri/capabilities/default.json index 12db43e64..1c519f1f5 100644 --- a/frontend/src-tauri/capabilities/default.json +++ b/frontend/src-tauri/capabilities/default.json @@ -18,6 +18,7 @@ "url": "https://trymaple.ai/*" } ] - } + }, + "sign-in-with-apple:default" ] } diff --git a/frontend/src-tauri/capabilities/mobile-ios.json b/frontend/src-tauri/capabilities/mobile-ios.json new file mode 100644 index 000000000..188ce5fa9 --- /dev/null +++ b/frontend/src-tauri/capabilities/mobile-ios.json @@ -0,0 +1,25 @@ +{ + "$schema": "../gen/schemas/mobile-schema.json", + "identifier": "mobile-ios", + "description": "Capabilities for iOS", + "windows": [ + "main" + ], + "platforms": ["iOS"], + "permissions": [ + "core:default", + "updater:default", + { + "identifier": "opener:allow-open-url", + "allow": [ + { + "url": "http://localhost:5173/*" + }, + { + "url": "https://trymaple.ai/*" + } + ] + }, + "sign-in-with-apple:default" + ] +} \ No newline at end of file diff --git a/frontend/src-tauri/gen/apple/maple_iOS/maple_iOS.entitlements b/frontend/src-tauri/gen/apple/maple_iOS/maple_iOS.entitlements index b026fc176..869918254 100644 --- a/frontend/src-tauri/gen/apple/maple_iOS/maple_iOS.entitlements +++ b/frontend/src-tauri/gen/apple/maple_iOS/maple_iOS.entitlements @@ -2,6 +2,10 @@ + com.apple.developer.applesignin + + Default + com.apple.developer.associated-domains applinks:trymaple.ai diff --git a/frontend/src-tauri/src/lib.rs b/frontend/src-tauri/src/lib.rs index 3655ec4a8..1cac2c392 100644 --- a/frontend/src-tauri/src/lib.rs +++ b/frontend/src-tauri/src/lib.rs @@ -1,6 +1,7 @@ use tauri::Emitter; use tauri_plugin_deep_link::DeepLinkExt; use tauri_plugin_opener; +use tauri_plugin_sign_in_with_apple; // This handles incoming deep links fn handle_deep_link_event(url: &str, app: &tauri::AppHandle) { @@ -21,6 +22,7 @@ pub fn run() { .plugin(tauri_plugin_deep_link::init()) .plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_os::init()) + .plugin(tauri_plugin_sign_in_with_apple::init()) .setup(|app| { // Set up the deep link handler // Use a cloned handle with 'static lifetime @@ -193,6 +195,7 @@ pub fn run() { .plugin(tauri_plugin_deep_link::init()) .plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_os::init()) + .plugin(tauri_plugin_sign_in_with_apple::init()) .setup(|app| { // Set up the deep link handler for mobile let app_handle = app.handle().clone(); diff --git a/frontend/src/components/icons/Apple.tsx b/frontend/src/components/icons/Apple.tsx new file mode 100644 index 000000000..949f0e28c --- /dev/null +++ b/frontend/src/components/icons/Apple.tsx @@ -0,0 +1,21 @@ +import React from "react"; + +export function Apple(props: React.SVGProps) { + return ( + + + + + ); +} diff --git a/frontend/src/routes/desktop-auth.tsx b/frontend/src/routes/desktop-auth.tsx index bbc113954..3cb1dd256 100644 --- a/frontend/src/routes/desktop-auth.tsx +++ b/frontend/src/routes/desktop-auth.tsx @@ -16,7 +16,7 @@ export const Route = createFileRoute("/desktop-auth")({ validateSearch: (search: Record): DesktopAuthSearchParams => { const provider = typeof search.provider === "string" ? search.provider : "github"; // Validate provider is supported - if (provider !== "github" && provider !== "google") { + if (provider !== "github" && provider !== "google" && provider !== "apple") { throw new Error(`Unsupported provider: ${provider}`); } return { @@ -52,6 +52,12 @@ function DesktopAuth() { } else if (provider === "google") { const result = await os.initiateGoogleAuth(""); auth_url = result.auth_url; + } else if (provider === "apple") { + // Apple sign-in is handled natively on iOS, not through browser OAuth + // For this demo, we'll just redirect to login page + console.log("[Apple Auth] Apple Sign In should be handled natively on iOS"); + navigate({ to: "/login" }); + return; } else { throw new Error("Unsupported provider"); } diff --git a/frontend/src/routes/login.tsx b/frontend/src/routes/login.tsx index ffab59628..9ade24403 100644 --- a/frontend/src/routes/login.tsx +++ b/frontend/src/routes/login.tsx @@ -7,8 +7,10 @@ import { Label } from "@/components/ui/label"; import { AlertDestructive } from "@/components/AlertDestructive"; import { Loader2, Github, Mail } from "lucide-react"; import { Google } from "@/components/icons/Google"; +import { Apple } from "@/components/icons/Apple"; import { AuthMain } from "@/components/AuthMain"; import { isTauri, invoke } from "@tauri-apps/api/core"; +import { type } from "@tauri-apps/plugin-os"; type LoginSearchParams = { next?: string; @@ -23,7 +25,7 @@ export const Route = createFileRoute("/login")({ }) }); -type LoginMethod = "email" | "github" | "google" | null; +type LoginMethod = "email" | "github" | "google" | "apple" | null; function LoginPage() { const navigate = useNavigate(); @@ -32,6 +34,22 @@ function LoginPage() { const [loginMethod, setLoginMethod] = useState(null); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(false); + const [isIOS, setIsIOS] = useState(false); + + // Check if running on iOS + useEffect(() => { + const checkPlatform = async () => { + try { + const platform = await type(); + setIsIOS(platform === "ios"); + } catch (error) { + console.error("Error checking platform:", error); + setIsIOS(false); + } + }; + + checkPlatform(); + }, []); // Redirect if already logged in useEffect(() => { @@ -150,9 +168,49 @@ function LoginPage() { } }; + const handleAppleLogin = async () => { + try { + // For Sign in with Apple, we use the Tauri plugin's command + console.log("[OAuth] Initiating Sign in with Apple"); + + try { + // Invoke the Apple Sign in plugin + // This will show the native Apple authentication UI + const result = await invoke("plugin:sign-in-with-apple|get_apple_id_credential", { + payload: { + scope: ["email", "fullName"], + state: "apple-signin-state", + // Add options to help with debugging + options: { + debug: true + } + } + }); + + console.log("[OAuth] Apple Sign-In result:", result); + + // In a real implementation, you would send this result to your backend + // For now we'll just show a message that this is coming soon + setError("Apple Sign In successful, but backend integration is coming soon."); + } catch (error) { + console.error("[OAuth] Failed to authenticate with Apple:", error); + // Be more specific about the error message + const errorMessage = + error instanceof Error + ? `Apple Sign In error: ${error.message}` + : "Failed to authenticate with Apple. Please try again."; + setError(errorMessage); + } + } catch (error) { + console.error("Failed to initiate Apple login:", error); + setError("Failed to initiate Apple login. Please try again."); + } + }; + if (!loginMethod) { return ( + {error && } + {isIOS && ( + + )}
Need an account?{" "} diff --git a/frontend/src/routes/signup.tsx b/frontend/src/routes/signup.tsx index 5c11c514d..dc15622ca 100644 --- a/frontend/src/routes/signup.tsx +++ b/frontend/src/routes/signup.tsx @@ -7,8 +7,10 @@ import { Label } from "@/components/ui/label"; import { AlertDestructive } from "@/components/AlertDestructive"; import { Loader2, Github, Mail } from "lucide-react"; import { Google } from "@/components/icons/Google"; +import { Apple } from "@/components/icons/Apple"; import { AuthMain } from "@/components/AuthMain"; import { isTauri, invoke } from "@tauri-apps/api/core"; +import { type } from "@tauri-apps/plugin-os"; type SignupSearchParams = { next?: string; @@ -23,7 +25,7 @@ export const Route = createFileRoute("/signup")({ }) }); -type SignUpMethod = "email" | "github" | "google" | null; +type SignUpMethod = "email" | "github" | "google" | "apple" | null; function SignupPage() { const navigate = useNavigate(); @@ -32,6 +34,22 @@ function SignupPage() { const [signUpMethod, setSignUpMethod] = useState(null); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(false); + const [isIOS, setIsIOS] = useState(false); + + // Check if running on iOS + useEffect(() => { + const checkPlatform = async () => { + try { + const platform = await type(); + setIsIOS(platform === "ios"); + } catch (error) { + console.error("Error checking platform:", error); + setIsIOS(false); + } + }; + + checkPlatform(); + }, []); // Redirect if already logged in useEffect(() => { @@ -150,9 +168,49 @@ function SignupPage() { } }; + const handleAppleSignup = async () => { + try { + // For Sign in with Apple, we use the Tauri plugin's command + console.log("[OAuth] Initiating Sign in with Apple"); + + try { + // Invoke the Apple Sign in plugin + // This will show the native Apple authentication UI + const result = await invoke("plugin:sign-in-with-apple|get_apple_id_credential", { + payload: { + scope: ["email", "fullName"], + state: "apple-signup-state", + // Add options to help with debugging + options: { + debug: true + } + } + }); + + console.log("[OAuth] Apple Sign-In result:", result); + + // In a real implementation, you would send this result to your backend + // For now we'll just show a message that this is coming soon + setError("Apple Sign In successful, but backend integration is coming soon."); + } catch (error) { + console.error("[OAuth] Failed to authenticate with Apple:", error); + // Be more specific about the error message + const errorMessage = + error instanceof Error + ? `Apple Sign In error: ${error.message}` + : "Failed to authenticate with Apple. Please try again."; + setError(errorMessage); + } + } catch (error) { + console.error("Failed to initiate Apple signup:", error); + setError("Failed to initiate Apple signup. Please try again."); + } + }; + if (!signUpMethod) { return ( + {error && } + {isIOS && ( + + )}
Already have an account?{" "} From 8f24af36470a26bfa0eab234d85ec41bb04907c6 Mon Sep 17 00:00:00 2001 From: Tony Giorgio Date: Wed, 23 Apr 2025 13:42:53 -0500 Subject: [PATCH 2/6] Attempt apple oauth --- frontend/bun.lock | 162 +++++++++++++++++- frontend/package.json | 2 +- frontend/src-tauri/apple-sign-in-info.md | 108 ++++++++++-- frontend/src/components/Marketing.tsx | 12 +- frontend/src/components/icons/Apple.tsx | 11 +- .../src/routes/auth.$provider.callback.tsx | 147 ++++++++++------ frontend/src/routes/desktop-auth.tsx | 16 +- frontend/src/routes/login.tsx | 141 ++++++++++----- frontend/src/routes/pricing.tsx | 12 +- frontend/src/routes/signup.tsx | 141 ++++++++++----- frontend/src/types/apple-sign-in.d.ts | 18 ++ 11 files changed, 581 insertions(+), 189 deletions(-) create mode 100644 frontend/src/types/apple-sign-in.d.ts diff --git a/frontend/bun.lock b/frontend/bun.lock index c193cedb9..576d84769 100644 --- a/frontend/bun.lock +++ b/frontend/bun.lock @@ -4,7 +4,7 @@ "": { "name": "maple", "dependencies": { - "@opensecret/react": "1.2.0", + "@opensecret/react": "../../OpenSecret-SDK/", "@radix-ui/react-alert-dialog": "^1.1.1", "@radix-ui/react-avatar": "^1.1.0", "@radix-ui/react-dialog": "^1.1.1", @@ -202,6 +202,14 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="], + "@microsoft/api-extractor": ["@microsoft/api-extractor@7.52.5", "", { "dependencies": { "@microsoft/api-extractor-model": "7.30.5", "@microsoft/tsdoc": "~0.15.1", "@microsoft/tsdoc-config": "~0.17.1", "@rushstack/node-core-library": "5.13.0", "@rushstack/rig-package": "0.5.3", "@rushstack/terminal": "0.15.2", "@rushstack/ts-command-line": "5.0.0", "lodash": "~4.17.15", "minimatch": "~3.0.3", "resolve": "~1.22.1", "semver": "~7.5.4", "source-map": "~0.6.1", "typescript": "5.8.2" }, "bin": { "api-extractor": "bin/api-extractor" } }, "sha512-6WWgjjg6FkoDWpF/O3sjB05OkszpI5wtKJqd8fUIR/JJUv8IqNCGr1lJUZJnc1HegcT9gAvyf98KfH0wFncU0w=="], + + "@microsoft/api-extractor-model": ["@microsoft/api-extractor-model@7.30.5", "", { "dependencies": { "@microsoft/tsdoc": "~0.15.1", "@microsoft/tsdoc-config": "~0.17.1", "@rushstack/node-core-library": "5.13.0" } }, "sha512-0ic4rcbcDZHz833RaTZWTGu+NpNgrxVNjVaor0ZDUymfDFzjA/Uuk8hYziIUIOEOSTfmIQqyzVwlzxZxPe7tOA=="], + + "@microsoft/tsdoc": ["@microsoft/tsdoc@0.15.1", "", {}, "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw=="], + + "@microsoft/tsdoc-config": ["@microsoft/tsdoc-config@0.17.1", "", { "dependencies": { "@microsoft/tsdoc": "0.15.1", "ajv": "~8.12.0", "jju": "~1.4.0", "resolve": "~1.22.2" } }, "sha512-UtjIFe0C6oYgTnad4q1QP4qXwLhe6tIpNTRStJ2RZEPIkqQPREAwE5spzVxsdn9UaEMUqhh0AqSx3X4nWAKXWw=="], + "@noble/curves": ["@noble/curves@1.8.1", "", { "dependencies": { "@noble/hashes": "1.7.1" } }, "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ=="], "@noble/hashes": ["@noble/hashes@1.7.1", "", {}, "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ=="], @@ -212,7 +220,7 @@ "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], - "@opensecret/react": ["@opensecret/react@1.2.0", "", { "dependencies": { "@peculiar/x509": "^1.12.2", "@stablelib/base64": "^2.0.0", "@stablelib/chacha20poly1305": "^2.0.0", "@stablelib/random": "^2.0.0", "cbor2": "^1.7.0", "tweetnacl": "^1.0.3", "zod": "^3.23.8" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-s2dKjznj2oamW6MQVED/g7bxBHdYpm7DGdMrJrC+R6YmwAtbwM3rRwkOM4pbCoiHUUo8NzrXFZWwm/yh8IIfeQ=="], + "@opensecret/react": ["@opensecret/react@file:../../OpenSecret-SDK", { "dependencies": { "@peculiar/x509": "^1.12.2", "@stablelib/base64": "^2.0.0", "@stablelib/chacha20poly1305": "^2.0.0", "@stablelib/random": "^2.0.0", "cbor2": "^1.7.0", "tweetnacl": "^1.0.3", "zod": "^3.23.8" }, "devDependencies": { "@eslint/js": "^9.13.0", "@noble/curves": "^1.6.0", "@noble/hashes": "^1.5.0", "@types/bun": "latest", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", "@vitejs/plugin-react": "^4.3.3", "eslint": "^9.13.0", "eslint-plugin-react-hooks": "^5.0.0", "eslint-plugin-react-refresh": "^0.4.14", "globals": "^15.11.0", "openai": "4.72.0", "prettier": "^3.3.3", "typescript": "~5.6.2", "typescript-eslint": "^8.11.0", "vite": "^5.4.12", "vite-plugin-dts": "^4.3.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }], "@peculiar/asn1-cms": ["@peculiar/asn1-cms@2.3.15", "", { "dependencies": { "@peculiar/asn1-schema": "^2.3.15", "@peculiar/asn1-x509": "^2.3.15", "@peculiar/asn1-x509-attr": "^2.3.15", "asn1js": "^3.0.5", "tslib": "^2.8.1" } }, "sha512-B+DoudF+TCrxoJSTjjcY8Mmu+lbv8e7pXGWrhNp2/EGJp9EEcpzjBCar7puU57sGifyzaRVM03oD5L7t7PghQg=="], @@ -310,6 +318,8 @@ "@radix-ui/rect": ["@radix-ui/rect@1.1.0", "", {}, "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg=="], + "@rollup/pluginutils": ["@rollup/pluginutils@5.1.4", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.34.8", "", { "os": "android", "cpu": "arm" }, "sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw=="], "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.34.8", "", { "os": "android", "cpu": "arm64" }, "sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q=="], @@ -348,6 +358,14 @@ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.34.8", "", { "os": "win32", "cpu": "x64" }, "sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g=="], + "@rushstack/node-core-library": ["@rushstack/node-core-library@5.13.0", "", { "dependencies": { "ajv": "~8.13.0", "ajv-draft-04": "~1.0.0", "ajv-formats": "~3.0.1", "fs-extra": "~11.3.0", "import-lazy": "~4.0.0", "jju": "~1.4.0", "resolve": "~1.22.1", "semver": "~7.5.4" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-IGVhy+JgUacAdCGXKUrRhwHMTzqhWwZUI+qEPcdzsb80heOw0QPbhhoVsoiMF7Klp8eYsp7hzpScMXmOa3Uhfg=="], + + "@rushstack/rig-package": ["@rushstack/rig-package@0.5.3", "", { "dependencies": { "resolve": "~1.22.1", "strip-json-comments": "~3.1.1" } }, "sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow=="], + + "@rushstack/terminal": ["@rushstack/terminal@0.15.2", "", { "dependencies": { "@rushstack/node-core-library": "5.13.0", "supports-color": "~8.1.1" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-7Hmc0ysK5077R/IkLS9hYu0QuNafm+TbZbtYVzCMbeOdMjaRboLKrhryjwZSRJGJzu+TV1ON7qZHeqf58XfLpA=="], + + "@rushstack/ts-command-line": ["@rushstack/ts-command-line@5.0.0", "", { "dependencies": { "@rushstack/terminal": "0.15.2", "@types/argparse": "1.0.38", "argparse": "~1.0.9", "string-argv": "~0.3.1" } }, "sha512-SW6nqZVxH26Rxz25+lJQRlnXI/YCrNH7NfDEWPPm9i0rwkSE6Rgtmzw96cuZgQjacOh0sw77d6V4SvgarAfr8g=="], + "@stablelib/aead": ["@stablelib/aead@2.0.0", "", {}, "sha512-U/RMANRxbT/ahIpYsPSiFwDFNjADHdnCFfmo09MO1ai2XmerPAOPtMl0qmX7XVvygnACC6ijKDyHBoT2rGyElg=="], "@stablelib/base64": ["@stablelib/base64@2.0.1", "", {}, "sha512-P2z89A7N1ETt6RxgpVdDT2xlg8cnm3n6td0lY9gyK7EiWK3wdq388yFX/hLknkCC0we05OZAD1rfxlQJUbl5VQ=="], @@ -422,6 +440,8 @@ "@tauri-apps/plugin-os": ["@tauri-apps/plugin-os@2.2.1", "", { "dependencies": { "@tauri-apps/api": "^2.0.0" } }, "sha512-cNYpNri2CCc6BaNeB6G/mOtLvg8dFyFQyCUdf2y0K8PIAKGEWdEcu8DECkydU2B+oj4OJihDPD2de5K6cbVl9A=="], + "@types/argparse": ["@types/argparse@1.0.38", "", {}, "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA=="], + "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], "@types/babel__generator": ["@types/babel__generator@7.6.8", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw=="], @@ -482,6 +502,22 @@ "@vitejs/plugin-react": ["@vitejs/plugin-react@4.3.4", "", { "dependencies": { "@babel/core": "^7.26.0", "@babel/plugin-transform-react-jsx-self": "^7.25.9", "@babel/plugin-transform-react-jsx-source": "^7.25.9", "@types/babel__core": "^7.20.5", "react-refresh": "^0.14.2" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" } }, "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug=="], + "@volar/language-core": ["@volar/language-core@2.4.12", "", { "dependencies": { "@volar/source-map": "2.4.12" } }, "sha512-RLrFdXEaQBWfSnYGVxvR2WrO6Bub0unkdHYIdC31HzIEqATIuuhRRzYu76iGPZ6OtA4Au1SnW0ZwIqPP217YhA=="], + + "@volar/source-map": ["@volar/source-map@2.4.12", "", {}, "sha512-bUFIKvn2U0AWojOaqf63ER0N/iHIBYZPpNGogfLPQ68F5Eet6FnLlyho7BS0y2HJ1jFhSif7AcuTx1TqsCzRzw=="], + + "@volar/typescript": ["@volar/typescript@2.4.12", "", { "dependencies": { "@volar/language-core": "2.4.12", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "sha512-HJB73OTJDgPc80K30wxi3if4fSsZZAOScbj2fcicMuOPoOkcf9NNAINb33o+DzhBdF9xTKC1gnPmIRDous5S0g=="], + + "@vue/compiler-core": ["@vue/compiler-core@3.5.13", "", { "dependencies": { "@babel/parser": "^7.25.3", "@vue/shared": "3.5.13", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" } }, "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q=="], + + "@vue/compiler-dom": ["@vue/compiler-dom@3.5.13", "", { "dependencies": { "@vue/compiler-core": "3.5.13", "@vue/shared": "3.5.13" } }, "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA=="], + + "@vue/compiler-vue2": ["@vue/compiler-vue2@2.7.16", "", { "dependencies": { "de-indent": "^1.0.2", "he": "^1.2.0" } }, "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A=="], + + "@vue/language-core": ["@vue/language-core@2.2.0", "", { "dependencies": { "@volar/language-core": "~2.4.11", "@vue/compiler-dom": "^3.5.0", "@vue/compiler-vue2": "^2.7.16", "@vue/shared": "^3.5.0", "alien-signals": "^0.4.9", "minimatch": "^9.0.3", "muggle-string": "^0.4.1", "path-browserify": "^1.0.1" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw=="], + + "@vue/shared": ["@vue/shared@3.5.13", "", {}, "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ=="], + "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], "acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="], @@ -492,6 +528,12 @@ "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], + "ajv-draft-04": ["ajv-draft-04@1.0.0", "", { "peerDependencies": { "ajv": "^8.5.0" }, "optionalPeers": ["ajv"] }, "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw=="], + + "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], + + "alien-signals": ["alien-signals@0.4.14", "", {}, "sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q=="], + "ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], @@ -568,8 +610,12 @@ "commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], + "compare-versions": ["compare-versions@6.1.1", "", {}, "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg=="], + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + "confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="], + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], @@ -578,6 +624,8 @@ "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + "de-indent": ["de-indent@1.0.2", "", {}, "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg=="], + "debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], "decode-named-character-reference": ["decode-named-character-reference@1.0.2", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg=="], @@ -642,10 +690,14 @@ "estree-util-is-identifier-name": ["estree-util-is-identifier-name@3.0.0", "", {}, "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg=="], + "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="], + "exsolve": ["exsolve@1.0.5", "", {}, "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg=="], + "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], @@ -678,6 +730,8 @@ "fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="], + "fs-extra": ["fs-extra@11.3.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew=="], + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], @@ -702,6 +756,8 @@ "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], @@ -732,6 +788,8 @@ "hastscript": ["hastscript@9.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^6.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-jzaLBGavEDKHrc5EfFImKN7nZKKBdSLIdGvCwDZ9TfzbF2ffXiov8CKE445L2Z1Ek2t/m4SKQ2j6Ipv7NyUolw=="], + "he": ["he@1.2.0", "", { "bin": { "he": "bin/he" } }, "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="], + "highlight.js": ["highlight.js@11.11.1", "", {}, "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="], "html-url-attributes": ["html-url-attributes@3.0.1", "", {}, "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ=="], @@ -742,6 +800,8 @@ "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + "import-lazy": ["import-lazy@4.0.0", "", {}, "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw=="], + "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], "inline-style-parser": ["inline-style-parser@0.2.4", "", {}, "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q=="], @@ -774,6 +834,8 @@ "jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="], + "jju": ["jju@1.4.0", "", {}, "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA=="], + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], @@ -788,18 +850,26 @@ "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + "jsonfile": ["jsonfile@6.1.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ=="], + "katex": ["katex@0.16.21", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-XvqR7FgOHtWupfMiigNzmh+MgUVmDGU2kXZm899ZkPfcuoPuFxyHmXsgATDpFZDAXCI8tvinaVcDo8PIIJSo4A=="], "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + "kolorist": ["kolorist@1.8.0", "", {}, "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ=="], + "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], + "local-pkg": ["local-pkg@1.1.1", "", { "dependencies": { "mlly": "^1.7.4", "pkg-types": "^2.0.1", "quansync": "^0.2.8" } }, "sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg=="], + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], @@ -812,6 +882,8 @@ "lucide-react": ["lucide-react@0.436.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" } }, "sha512-N292bIxoqm1aObAg0MzFtvhYwgQE6qnIOWx/GLj5ONgcTPH6N0fD9bVq/GfdeC9ZORBXozt/XeEKDpiB3x3vlQ=="], + "magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="], + "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], @@ -920,8 +992,12 @@ "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + "mlly": ["mlly@1.7.4", "", { "dependencies": { "acorn": "^8.14.0", "pathe": "^2.0.1", "pkg-types": "^1.3.0", "ufo": "^1.5.4" } }, "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "muggle-string": ["muggle-string@0.4.1", "", {}, "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ=="], + "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], "nanoid": ["nanoid@3.3.8", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="], @@ -958,6 +1034,8 @@ "parse5": ["parse5@7.2.1", "", { "dependencies": { "entities": "^4.5.0" } }, "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ=="], + "path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="], + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], @@ -966,6 +1044,8 @@ "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], @@ -974,6 +1054,8 @@ "pirates": ["pirates@4.0.6", "", {}, "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg=="], + "pkg-types": ["pkg-types@2.1.0", "", { "dependencies": { "confbox": "^0.2.1", "exsolve": "^1.0.1", "pathe": "^2.0.3" } }, "sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A=="], + "postcss": ["postcss@8.5.2", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA=="], "postcss-import": ["postcss-import@15.1.0", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="], @@ -1000,6 +1082,8 @@ "pvutils": ["pvutils@1.1.3", "", {}, "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ=="], + "quansync": ["quansync@0.2.10", "", {}, "sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A=="], + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], "react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="], @@ -1038,6 +1122,8 @@ "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="], + "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + "resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], @@ -1060,10 +1146,16 @@ "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], + "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], + + "string-argv": ["string-argv@0.3.2", "", {}, "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q=="], + "string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -1124,6 +1216,8 @@ "typescript-eslint": ["typescript-eslint@8.24.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.24.0", "@typescript-eslint/parser": "8.24.0", "@typescript-eslint/utils": "8.24.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-/lmv4366en/qbB32Vz5+kCNZEMf6xYHwh1z48suBwZvAtnXKbP+YhGe8OLE2BqC67LMqKkCNLtjejdwsdW6uOQ=="], + "ufo": ["ufo@1.6.1", "", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="], + "undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], @@ -1142,6 +1236,8 @@ "unist-util-visit-parents": ["unist-util-visit-parents@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw=="], + "universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="], + "unplugin": ["unplugin@2.2.0", "", { "dependencies": { "acorn": "^8.14.0", "webpack-virtual-modules": "^0.6.2" } }, "sha512-m1ekpSwuOT5hxkJeZGRxO7gXbXT3gF26NjQ7GdVHoLoF8/nopLcd/QfPigpCy7i51oFHiRJg/CyHhj4vs2+KGw=="], "update-browserslist-db": ["update-browserslist-db@1.1.2", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg=="], @@ -1164,6 +1260,10 @@ "vite": ["vite@5.4.14", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA=="], + "vite-plugin-dts": ["vite-plugin-dts@4.5.3", "", { "dependencies": { "@microsoft/api-extractor": "^7.50.1", "@rollup/pluginutils": "^5.1.4", "@volar/typescript": "^2.4.11", "@vue/language-core": "2.2.0", "compare-versions": "^6.1.1", "debug": "^4.4.0", "kolorist": "^1.8.0", "local-pkg": "^1.0.0", "magic-string": "^0.30.17" }, "peerDependencies": { "typescript": "*", "vite": "*" }, "optionalPeers": ["vite"] }, "sha512-P64VnD00dR+e8S26ESoFELqc17+w7pKkwlBpgXteOljFyT0zDwD8hH4zXp49M/kciy//7ZbVXIwQCekBJjfWzA=="], + + "vscode-uri": ["vscode-uri@3.1.0", "", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="], + "web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="], "web-streams-polyfill": ["web-streams-polyfill@4.0.0-beta.3", "", {}, "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug=="], @@ -1202,10 +1302,42 @@ "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="], + "@microsoft/api-extractor/minimatch": ["minimatch@3.0.8", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q=="], + + "@microsoft/api-extractor/semver": ["semver@7.5.4", "", { "dependencies": { "lru-cache": "^6.0.0" }, "bin": { "semver": "bin/semver.js" } }, "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA=="], + + "@microsoft/api-extractor/typescript": ["typescript@5.8.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ=="], + + "@microsoft/tsdoc-config/ajv": ["ajv@8.12.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA=="], + + "@opensecret/react/@types/bun": ["@types/bun@1.2.10", "", { "dependencies": { "bun-types": "1.2.10" } }, "sha512-eilv6WFM3M0c9ztJt7/g80BDusK98z/FrFwseZgT4bXCq2vPhXD4z8R3oddmAn+R/Nmz9vBn4kweJKmGTZj+lg=="], + + "@opensecret/react/@types/react": ["@types/react@19.1.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw=="], + + "@opensecret/react/@types/react-dom": ["@types/react-dom@19.1.2", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-XGJkWF41Qq305SKWEILa1O8vzhb3aOo3ogBlSmiqNko/WmRb6QIaweuZCXjKygVDXpzXb5wyxKTSOsmkuqj+Qw=="], + + "@opensecret/react/openai": ["openai@4.72.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" }, "peerDependencies": { "zod": "^3.23.8" }, "optionalPeers": ["zod"], "bin": { "openai": "bin/cli" } }, "sha512-hFqG9BWCs7L7ifrhJXw7mJXmUBr7d9N6If3J9563o0jfwVA4wFANFDDaOIWFdgDdwgCXg5emf0Q+LoLCGszQYA=="], + + "@opensecret/react/typescript": ["typescript@5.6.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw=="], + + "@rollup/pluginutils/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], + + "@rushstack/node-core-library/ajv": ["ajv@8.13.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.4.1" } }, "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA=="], + + "@rushstack/node-core-library/semver": ["semver@7.5.4", "", { "dependencies": { "lru-cache": "^6.0.0" }, "bin": { "semver": "bin/semver.js" } }, "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA=="], + + "@rushstack/terminal/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], + + "@rushstack/ts-command-line/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], "@typescript-eslint/typescript-estree/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], + "@vue/language-core/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "ajv-formats/ajv": ["ajv@8.13.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.4.1" } }, "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA=="], + "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], @@ -1216,6 +1348,8 @@ "mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + "mlly/pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], + "openai/@types/node": ["@types/node@18.19.76", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-yvR7Q9LdPz2vGpmpJX5LolrgRdWvB67MJKDPSgIIzpFbaf9a1j/f5DnLp5VDyHGMR0QZHlTr1afsD87QCXFHKw=="], "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], @@ -1238,10 +1372,28 @@ "wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "@microsoft/api-extractor/semver/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], + + "@microsoft/tsdoc-config/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "@opensecret/react/@types/bun/bun-types": ["bun-types@1.2.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-b5ITZMnVdf3m1gMvJHG+gIfeJHiQPJak0f7925Hxu6ZN5VKA8AGy4GZ4lM+Xkn6jtWxg5S3ldWvfmXdvnkp3GQ=="], + + "@opensecret/react/openai/@types/node": ["@types/node@18.19.76", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-yvR7Q9LdPz2vGpmpJX5LolrgRdWvB67MJKDPSgIIzpFbaf9a1j/f5DnLp5VDyHGMR0QZHlTr1afsD87QCXFHKw=="], + + "@rushstack/node-core-library/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "@rushstack/node-core-library/semver/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + "@vue/language-core/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + + "ajv-formats/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + "mlly/pkg-types/confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], + "openai/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], @@ -1295,5 +1447,11 @@ "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "@microsoft/api-extractor/semver/lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], + + "@opensecret/react/openai/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + + "@rushstack/node-core-library/semver/lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], } } diff --git a/frontend/package.json b/frontend/package.json index 3e9edc0fc..61ac01376 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -16,7 +16,7 @@ "mdast-util-gfm-autolink-literal": "2.0.0" }, "dependencies": { - "@opensecret/react": "1.2.0", + "@opensecret/react": "../../OpenSecret-SDK/", "@radix-ui/react-alert-dialog": "^1.1.1", "@radix-ui/react-avatar": "^1.1.0", "@radix-ui/react-dialog": "^1.1.1", diff --git a/frontend/src-tauri/apple-sign-in-info.md b/frontend/src-tauri/apple-sign-in-info.md index 7f0821de7..30fcc26d2 100644 --- a/frontend/src-tauri/apple-sign-in-info.md +++ b/frontend/src-tauri/apple-sign-in-info.md @@ -1,32 +1,102 @@ -# Sign in with Apple Integration for iOS +# Sign in with Apple Integration ## Overview -This document provides information about the Sign in with Apple integration for Maple on iOS. +This document provides a comprehensive guide to the Sign in with Apple integration for Maple, supporting both native iOS authentication and web-based OAuth. + +## Integration Types + +### 1. Native iOS Authentication +- Uses the iOS native Sign In with Apple dialog +- Implemented via `tauri-plugin-sign-in-with-apple` (version 1.0.0) +- Returns user credentials directly to the app +- Provides access to user identifiers, email, and name (only on first sign-in) + +### 2. Web-based OAuth Flow +- Similar to GitHub and Google OAuth flow +- Redirects users to Apple's authentication page +- Supports both web and desktop (non-iOS) platforms +- Handles callback with auth code and state verification ## Configuration -1. The integration uses the tauri-plugin-sign-in-with-apple crate (version 1.0.0) -2. Required entitlements have been added in maple_iOS.entitlements -3. The capability is registered in capabilities/default.json and capabilities/mobile-ios.json -## Frontend Implementation -1. Apple Sign In button is only shown on iOS devices -2. Platform detection via `@tauri-apps/plugin-os`'s `type()` function -3. Authentication is handled through the plugin's command: `plugin:sign-in-with-apple|get_apple_id_credential` +### iOS Native Auth +1. Required entitlements have been added in `maple_iOS.entitlements` +2. The capability is registered in `capabilities/default.json` and `capabilities/mobile-ios.json` +3. Plugin is configured in Cargo.toml and registered in the app + +### OAuth Configuration +1. Set up these parameters in your OpenSecret project settings: + - **Client ID**: Your Apple Services ID (e.g., com.example.web) + - **Client Secret**: The base64-encoded contents of your Apple private key (p8 file) + - **Redirect URI**: Configure as `https://api.opensecret.cloud/auth/apple/callback` + +## Implementation Details + +### Frontend Integration -## Future Backend Work -The frontend implementation for Apple Sign In is complete, but the backend integration is not yet implemented. -When the backend is ready, the response from the Apple Sign In should be sent to your backend to: +#### iOS Native Flow +```typescript +// iOS native authentication +const result = await invoke("plugin:sign-in-with-apple|get_apple_id_credential", { + payload: { + scope: ["email", "fullName"], + state: "apple-auth-state", + options: { debug: true } + } +}); -1. Verify the identity token with Apple -2. Create or update the user account -3. Generate authentication tokens for your app +// Format and send to backend +const appleUser = { + user_identifier: result.user, + identity_token: result.identityToken, + email: result.email, + given_name: result.fullName?.givenName, + family_name: result.fullName?.familyName +}; + +// Call OpenSecret SDK +await os.handleAppleNativeSignIn(appleUser, inviteCode); +``` + +#### Web OAuth Flow +```typescript +// Web OAuth authentication +const { auth_url } = await os.initiateAppleAuth(inviteCode); +window.location.href = auth_url; + +// Callback handling (in separate component) +await handleAppleCallback(code, state, inviteCode); +``` + +### Platform Detection +The app automatically determines the appropriate flow: +1. Checks if the app is running on iOS and uses native flow +2. Checks if running in a Tauri environment (desktop) and uses the desktop auth flow +3. Uses the web OAuth flow for all other cases + +## Callback Handling +For the web OAuth flow, callbacks are handled in `auth.$provider.callback.tsx`: +1. Extracts code and state from URL parameters +2. Verifies auth state to prevent CSRF attacks +3. Processes the authentication with the backend +4. Redirects to appropriate page after successful authentication ## Debugging If you experience issues with Sign in with Apple: -1. Check the debug logs (debug option is enabled) -2. Verify that the entitlements are properly set -3. Ensure your Apple Developer account is configured correctly for Sign in with Apple +1. Check debug logs (enabled by default in both flows) +2. Verify that iOS entitlements are properly configured (for native flow) +3. For OAuth flow, check if Apple Developer account is properly set up +4. Verify that the OpenSecret project settings are correctly configured + +## Apple Developer Setup +To use Sign in with Apple, you need: +1. An Apple Developer account +2. An App ID with "Sign In with Apple" capability +3. A Services ID for web authentication +4. A Bundle ID for iOS apps +5. A private key for JWT token signing ## Resources - [Apple Developer Documentation](https://developer.apple.com/documentation/sign_in_with_apple) -- [tauri-plugin-sign-in-with-apple on crates.io](https://crates.io/crates/tauri-plugin-sign-in-with-apple) \ No newline at end of file +- [OpenSecret Apple Auth API](https://docs.opensecret.cloud/docs/guides/authentication) +- [tauri-plugin-sign-in-with-apple](https://crates.io/crates/tauri-plugin-sign-in-with-apple) \ No newline at end of file diff --git a/frontend/src/components/Marketing.tsx b/frontend/src/components/Marketing.tsx index c060db583..63b0d8167 100644 --- a/frontend/src/components/Marketing.tsx +++ b/frontend/src/components/Marketing.tsx @@ -200,8 +200,16 @@ export function Marketing() { useEffect(() => { const checkPlatform = async () => { try { - const platform = await type(); - setIsIOS(platform === "ios"); + // First check if we're in a Tauri environment + const isTauriEnv = await import('@tauri-apps/api/core').then(m => m.isTauri()).catch(() => false); + + if (isTauriEnv) { + // Only check platform type if we're in a Tauri environment + const platform = await type(); + setIsIOS(platform === "ios"); + } else { + setIsIOS(false); + } } catch (error) { console.error("Error checking platform:", error); setIsIOS(false); diff --git a/frontend/src/components/icons/Apple.tsx b/frontend/src/components/icons/Apple.tsx index 949f0e28c..ac813c774 100644 --- a/frontend/src/components/icons/Apple.tsx +++ b/frontend/src/components/icons/Apple.tsx @@ -6,16 +6,11 @@ export function Apple(props: React.SVGProps) { xmlns="http://www.w3.org/2000/svg" width="24" height="24" - viewBox="0 0 24 24" - fill="none" - stroke="currentColor" - strokeWidth="2" - strokeLinecap="round" - strokeLinejoin="round" + viewBox="0 0 384 512" + fill="currentColor" {...props} > - - + ); } diff --git a/frontend/src/routes/auth.$provider.callback.tsx b/frontend/src/routes/auth.$provider.callback.tsx index ef043ccba..5b19e6419 100644 --- a/frontend/src/routes/auth.$provider.callback.tsx +++ b/frontend/src/routes/auth.$provider.callback.tsx @@ -17,6 +17,8 @@ function formatProviderName(provider: string): string { return "GitHub"; case "google": return "Google"; + case "apple": + return "Apple"; default: return provider.charAt(0).toUpperCase() + provider.slice(1); } @@ -26,8 +28,60 @@ function OAuthCallback() { const [isProcessing, setIsProcessing] = useState(true); const [error, setError] = useState(null); const navigate = useNavigate(); - const { handleGitHubCallback, handleGoogleCallback } = useOpenSecret(); + const { handleGitHubCallback, handleGoogleCallback, handleAppleCallback } = useOpenSecret(); const processedRef = useRef(false); + + // Helper functions for the callback process + const handleSuccessfulAuth = () => { + // Check if this is a Tauri app auth flow (desktop or mobile) + const isTauriAuth = localStorage.getItem("redirect-to-native") === "true"; + + // Clear the flag + localStorage.removeItem("redirect-to-native"); + + if (isTauriAuth) { + // Handle Tauri redirect + const accessToken = localStorage.getItem("access_token") || ""; + const refreshToken = localStorage.getItem("refresh_token"); + + let deepLinkUrl = `cloud.opensecret.maple://auth?access_token=${encodeURIComponent(accessToken)}`; + + if (refreshToken) { + deepLinkUrl += `&refresh_token=${encodeURIComponent(refreshToken)}`; + } + + setTimeout(() => { + window.location.href = deepLinkUrl; + }, 1000); + + return; + } + + // Handle web redirect + const selectedPlan = sessionStorage.getItem("selected_plan"); + sessionStorage.removeItem("selected_plan"); + + setTimeout(() => { + if (selectedPlan) { + navigate({ + to: "/pricing", + search: { selected_plan: selectedPlan } + }); + } else { + navigate({ to: "/" }); + } + }, 2000); + }; + + const handleAuthError = (error: unknown) => { + console.error(`Authentication callback error:`, error); + if (error instanceof Error) { + setError(error.message); + } else { + setError("Unknown error"); + } + setIsProcessing(false); + }; const { provider } = Route.useParams(); const formattedProvider = formatProviderName(provider); // Format the provider name @@ -36,69 +90,50 @@ function OAuthCallback() { const processCallback = async () => { if (processedRef.current) return; processedRef.current = true; - + + // Get URL parameters for all OAuth providers const urlParams = new URLSearchParams(window.location.search); const code = urlParams.get("code"); const state = urlParams.get("state"); + + // For Apple, also check for user data in the URL parameters + if (provider === "apple") { + console.log("[Apple Auth] Processing Apple callback"); + + // Apple may include user data in the URL parameters with response_mode=query + const userParam = urlParams.get("user"); + if (userParam) { + try { + const userData = JSON.parse(decodeURIComponent(userParam)); + console.log("[Apple Auth] User data from Apple:", userData); + // userData.name.firstName, userData.name.lastName, userData.email available on first sign-in + // Note: Apple only sends this data on the first sign-in + } catch (e) { + console.error("[Apple Auth] Failed to parse user data:", e); + } + } + } if (code && state) { try { - // Get the auth token from localStorage - await (provider === "github" - ? handleGitHubCallback(code, state, "") - : handleGoogleCallback(code, state, "")); - - // Check if this is a Tauri app auth flow (desktop or mobile) - const isTauriAuth = localStorage.getItem("redirect-to-native") === "true"; - - // Clear the flag - localStorage.removeItem("redirect-to-native"); - - if (isTauriAuth) { - // This is a Tauri app auth flow - redirect back to the app with tokens - // Get tokens from localStorage where they're stored after auth - const accessToken = localStorage.getItem("access_token") || ""; - const refreshToken = localStorage.getItem("refresh_token"); - - // Construct the deep link URL using the consistent token names - let deepLinkUrl = `cloud.opensecret.maple://auth?access_token=${encodeURIComponent(accessToken)}`; - - if (refreshToken) { - deepLinkUrl += `&refresh_token=${encodeURIComponent(refreshToken)}`; - } - - // Redirect to the deep link - setTimeout(() => { - window.location.href = deepLinkUrl; - }, 1000); - - return; // Stop further processing + // Get the auth token from localStorage based on the provider + if (provider === "github") { + await handleGitHubCallback(code, state, ""); + } else if (provider === "google") { + await handleGoogleCallback(code, state, ""); + } else if (provider === "apple") { + // Use the OpenSecret SDK to handle Apple OAuth callback + await handleAppleCallback(code, state, ""); + } else { + throw new Error(`Unsupported provider: ${provider}`); } - // Regular web flow - unchanged - const selectedPlan = sessionStorage.getItem("selected_plan"); - sessionStorage.removeItem("selected_plan"); - - // If successful, redirect after a short delay - setTimeout(() => { - if (selectedPlan) { - // If there was a selected plan, go to pricing - navigate({ - to: "/pricing", - search: { selected_plan: selectedPlan } - }); - } else { - // Otherwise go home (original behavior) - navigate({ to: "/" }); - } - }, 2000); + // Handle the successful authentication (redirect) + handleSuccessfulAuth(); + } catch (error) { - console.error(`${provider} callback error:`, error); - if (error instanceof Error) { - setError(error.message); - } else { - setError("Unknown error"); - } + // Handle authentication error + handleAuthError(error); } finally { setIsProcessing(false); } @@ -109,7 +144,7 @@ function OAuthCallback() { }; processCallback(); - }, [handleGitHubCallback, handleGoogleCallback, navigate, provider]); + }, [handleGitHubCallback, handleGoogleCallback, handleAppleCallback, navigate, provider]); // If this is a Tauri app auth flow (desktop or mobile), show a different UI if (localStorage.getItem("redirect-to-native") === "true") { diff --git a/frontend/src/routes/desktop-auth.tsx b/frontend/src/routes/desktop-auth.tsx index 3cb1dd256..3b878dc66 100644 --- a/frontend/src/routes/desktop-auth.tsx +++ b/frontend/src/routes/desktop-auth.tsx @@ -53,11 +53,17 @@ function DesktopAuth() { const result = await os.initiateGoogleAuth(""); auth_url = result.auth_url; } else if (provider === "apple") { - // Apple sign-in is handled natively on iOS, not through browser OAuth - // For this demo, we'll just redirect to login page - console.log("[Apple Auth] Apple Sign In should be handled natively on iOS"); - navigate({ to: "/login" }); - return; + // Apple sign-in can be handled through OAuth too for non-iOS platforms + console.log("[Apple Auth] Initiating Apple OAuth flow for desktop/web"); + try { + // Use response_mode=query to make Apple return URL parameters instead of a form POST + const options = { response_mode: "query" }; + const result = await os.initiateAppleAuth("", options); + auth_url = result.auth_url; + } catch (error) { + console.error("[Apple Auth] Failed to initiate Apple OAuth:", error); + throw new Error("Failed to initiate Apple authentication. Please try again."); + } } else { throw new Error("Unsupported provider"); } diff --git a/frontend/src/routes/login.tsx b/frontend/src/routes/login.tsx index 9ade24403..a7c9a5774 100644 --- a/frontend/src/routes/login.tsx +++ b/frontend/src/routes/login.tsx @@ -34,22 +34,8 @@ function LoginPage() { const [loginMethod, setLoginMethod] = useState(null); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(false); - const [isIOS, setIsIOS] = useState(false); - - // Check if running on iOS - useEffect(() => { - const checkPlatform = async () => { - try { - const platform = await type(); - setIsIOS(platform === "ios"); - } catch (error) { - console.error("Error checking platform:", error); - setIsIOS(false); - } - }; - - checkPlatform(); - }, []); + // No longer needed to check if it's iOS only for visibility + // We still check the platform inside the handlers to determine the flow // Redirect if already logged in useEffect(() => { @@ -170,36 +156,99 @@ function LoginPage() { const handleAppleLogin = async () => { try { - // For Sign in with Apple, we use the Tauri plugin's command - console.log("[OAuth] Initiating Sign in with Apple"); + const isTauriEnv = await isTauri(); + let isIOSDevice = false; + + // Only check platform type if we're in a Tauri environment + if (isTauriEnv) { + try { + const platform = await type(); + isIOSDevice = platform === "ios"; + } catch (error) { + console.error("Error checking platform type:", error); + } + } + + console.log("[OAuth] Using", isIOSDevice ? "iOS native" : isTauriEnv ? "Tauri" : "web", "flow"); - try { - // Invoke the Apple Sign in plugin - // This will show the native Apple authentication UI - const result = await invoke("plugin:sign-in-with-apple|get_apple_id_credential", { - payload: { - scope: ["email", "fullName"], - state: "apple-signin-state", - // Add options to help with debugging - options: { - debug: true + if (isIOSDevice) { + // For iOS, use the native Apple Sign In + console.log("[OAuth] Initiating native Sign in with Apple"); + + try { + // Invoke the Apple Sign in plugin + // This will show the native Apple authentication UI + const result = await invoke("plugin:sign-in-with-apple|get_apple_id_credential", { + payload: { + scope: ["email", "fullName"], + state: "apple-signin-state", + // Add options to help with debugging + options: { + debug: true + } + } + }); + + console.log("[OAuth] Apple Sign-In result:", result); + + // Format the response for the API + const appleUser = { + user_identifier: result.user, + identity_token: result.identityToken, + email: result.email, + given_name: result.fullName?.givenName, + family_name: result.fullName?.familyName + }; + + // Send to backend via SDK + try { + await os.handleAppleNativeSignIn(appleUser, ""); + // Redirect after successful login + if (selected_plan) { + navigate({ + to: "/pricing", + search: { selected_plan } + }); + } else { + navigate({ to: next || "/" }); } + } catch (backendError) { + console.error("[OAuth] Backend processing failed:", backendError); + setError(backendError instanceof Error ? backendError.message : "Failed to process Apple authentication"); } - }); + } catch (error) { + console.error("[OAuth] Failed to authenticate with Apple:", error); + const errorMessage = + error instanceof Error + ? `Apple Sign In error: ${error.message}` + : "Failed to authenticate with Apple. Please try again."; + setError(errorMessage); + } + } else if (isTauriEnv) { + // For Tauri (desktop), redirect to the web app's desktop-auth route + let desktopAuthUrl = "https://trymaple.ai/desktop-auth?provider=apple"; - console.log("[OAuth] Apple Sign-In result:", result); + // If there's a selected plan, add it to the URL + if (selected_plan) { + desktopAuthUrl += `&selected_plan=${encodeURIComponent(selected_plan)}`; + } - // In a real implementation, you would send this result to your backend - // For now we'll just show a message that this is coming soon - setError("Apple Sign In successful, but backend integration is coming soon."); - } catch (error) { - console.error("[OAuth] Failed to authenticate with Apple:", error); - // Be more specific about the error message - const errorMessage = - error instanceof Error - ? `Apple Sign In error: ${error.message}` - : "Failed to authenticate with Apple. Please try again."; - setError(errorMessage); + // Use the opener plugin by directly invoking the command + console.log("[OAuth] Opening URL in external browser:", desktopAuthUrl); + invoke("plugin:opener|open_url", { url: desktopAuthUrl }).catch((error: Error) => { + console.error("[OAuth] Failed to open external browser:", error); + setError("Failed to open authentication page in browser"); + }); + } else { + // Web flow for Apple OAuth with response_mode=query + // This makes Apple send the response as URL parameters instead of a form POST + const options = { response_mode: "query" }; + const { auth_url } = await os.initiateAppleAuth("", options); + + if (selected_plan) { + sessionStorage.setItem("selected_plan", selected_plan); + } + window.location.href = auth_url; } } catch (error) { console.error("Failed to initiate Apple login:", error); @@ -223,12 +272,10 @@ function LoginPage() { Log in with Google - {isIOS && ( - - )} +
Need an account?{" "} diff --git a/frontend/src/routes/pricing.tsx b/frontend/src/routes/pricing.tsx index 88142c8be..ee918d8de 100644 --- a/frontend/src/routes/pricing.tsx +++ b/frontend/src/routes/pricing.tsx @@ -152,8 +152,16 @@ function PricingPage() { useEffect(() => { const checkPlatform = async () => { try { - const platform = await type(); - setIsIOS(platform === "ios"); + // First check if we're in a Tauri environment + const isTauriEnv = await import('@tauri-apps/api/core').then(m => m.isTauri()).catch(() => false); + + if (isTauriEnv) { + // Only check platform type if we're in a Tauri environment + const platform = await type(); + setIsIOS(platform === "ios"); + } else { + setIsIOS(false); + } } catch (error) { console.error("Error checking platform:", error); setIsIOS(false); diff --git a/frontend/src/routes/signup.tsx b/frontend/src/routes/signup.tsx index dc15622ca..9d83c494f 100644 --- a/frontend/src/routes/signup.tsx +++ b/frontend/src/routes/signup.tsx @@ -34,22 +34,8 @@ function SignupPage() { const [signUpMethod, setSignUpMethod] = useState(null); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(false); - const [isIOS, setIsIOS] = useState(false); - - // Check if running on iOS - useEffect(() => { - const checkPlatform = async () => { - try { - const platform = await type(); - setIsIOS(platform === "ios"); - } catch (error) { - console.error("Error checking platform:", error); - setIsIOS(false); - } - }; - - checkPlatform(); - }, []); + // No longer needed to check if it's iOS only for visibility + // We still check the platform inside the handlers to determine the flow // Redirect if already logged in useEffect(() => { @@ -170,36 +156,99 @@ function SignupPage() { const handleAppleSignup = async () => { try { - // For Sign in with Apple, we use the Tauri plugin's command - console.log("[OAuth] Initiating Sign in with Apple"); + const isTauriEnv = await isTauri(); + let isIOSDevice = false; + + // Only check platform type if we're in a Tauri environment + if (isTauriEnv) { + try { + const platform = await type(); + isIOSDevice = platform === "ios"; + } catch (error) { + console.error("Error checking platform type:", error); + } + } + + console.log("[OAuth] Using", isIOSDevice ? "iOS native" : isTauriEnv ? "Tauri" : "web", "flow"); - try { - // Invoke the Apple Sign in plugin - // This will show the native Apple authentication UI - const result = await invoke("plugin:sign-in-with-apple|get_apple_id_credential", { - payload: { - scope: ["email", "fullName"], - state: "apple-signup-state", - // Add options to help with debugging - options: { - debug: true + if (isIOSDevice) { + // For iOS, use the native Apple Sign In + console.log("[OAuth] Initiating native Sign in with Apple"); + + try { + // Invoke the Apple Sign in plugin + // This will show the native Apple authentication UI + const result = await invoke("plugin:sign-in-with-apple|get_apple_id_credential", { + payload: { + scope: ["email", "fullName"], + state: "apple-signup-state", + // Add options to help with debugging + options: { + debug: true + } + } + }); + + console.log("[OAuth] Apple Sign-In result:", result); + + // Format the response for the API + const appleUser = { + user_identifier: result.user, + identity_token: result.identityToken, + email: result.email, + given_name: result.fullName?.givenName, + family_name: result.fullName?.familyName + }; + + // Send to backend via SDK + try { + await os.handleAppleNativeSignIn(appleUser, ""); + // Redirect after successful signup + if (selected_plan) { + navigate({ + to: "/pricing", + search: { selected_plan } + }); + } else { + navigate({ to: next || "/" }); } + } catch (backendError) { + console.error("[OAuth] Backend processing failed:", backendError); + setError(backendError instanceof Error ? backendError.message : "Failed to process Apple authentication"); } - }); + } catch (error) { + console.error("[OAuth] Failed to authenticate with Apple:", error); + const errorMessage = + error instanceof Error + ? `Apple Sign In error: ${error.message}` + : "Failed to authenticate with Apple. Please try again."; + setError(errorMessage); + } + } else if (isTauriEnv) { + // For Tauri (desktop), redirect to the web app's desktop-auth route + let desktopAuthUrl = "https://trymaple.ai/desktop-auth?provider=apple"; - console.log("[OAuth] Apple Sign-In result:", result); + // If there's a selected plan, add it to the URL + if (selected_plan) { + desktopAuthUrl += `&selected_plan=${encodeURIComponent(selected_plan)}`; + } - // In a real implementation, you would send this result to your backend - // For now we'll just show a message that this is coming soon - setError("Apple Sign In successful, but backend integration is coming soon."); - } catch (error) { - console.error("[OAuth] Failed to authenticate with Apple:", error); - // Be more specific about the error message - const errorMessage = - error instanceof Error - ? `Apple Sign In error: ${error.message}` - : "Failed to authenticate with Apple. Please try again."; - setError(errorMessage); + // Use the opener plugin by directly invoking the command + console.log("[OAuth] Opening URL in external browser:", desktopAuthUrl); + invoke("plugin:opener|open_url", { url: desktopAuthUrl }).catch((error: Error) => { + console.error("[OAuth] Failed to open external browser:", error); + setError("Failed to open authentication page in browser"); + }); + } else { + // Web flow for Apple OAuth with response_mode=query + // This makes Apple send the response as URL parameters instead of a form POST + const options = { response_mode: "query" }; + const { auth_url } = await os.initiateAppleAuth("", options); + + if (selected_plan) { + sessionStorage.setItem("selected_plan", selected_plan); + } + window.location.href = auth_url; } } catch (error) { console.error("Failed to initiate Apple signup:", error); @@ -223,12 +272,10 @@ function SignupPage() { Sign up with Google - {isIOS && ( - - )} +
Already have an account?{" "} diff --git a/frontend/src/types/apple-sign-in.d.ts b/frontend/src/types/apple-sign-in.d.ts new file mode 100644 index 000000000..31934ce97 --- /dev/null +++ b/frontend/src/types/apple-sign-in.d.ts @@ -0,0 +1,18 @@ +// Type definitions for Apple Sign In plugin and OAuth + +// For native Apple Sign In plugin +interface AppleCredential { + user: string; + identityToken: string; + email?: string; + fullName?: { + givenName?: string; + familyName?: string; + }; + state?: string; +} + +// For Apple OAuth options +interface AppleOAuthOptions { + response_mode?: "query" | "form_post"; +} \ No newline at end of file From 41922b834fd9ba1383991467b6c423af9f0c3306 Mon Sep 17 00:00:00 2001 From: Tony Giorgio Date: Fri, 25 Apr 2025 10:10:11 -0500 Subject: [PATCH 3/6] Remove apple non-native signin --- .../src-tauri/gen/apple/maple_iOS/Info.plist | 4 +- frontend/src/components/Marketing.tsx | 6 +- .../src/routes/auth.$provider.callback.tsx | 50 ++---- frontend/src/routes/desktop-auth.tsx | 14 +- frontend/src/routes/login.tsx | 166 +++++++++--------- frontend/src/routes/pricing.tsx | 6 +- frontend/src/routes/signup.tsx | 166 +++++++++--------- frontend/src/types/apple-sign-in.d.ts | 7 +- 8 files changed, 187 insertions(+), 232 deletions(-) diff --git a/frontend/src-tauri/gen/apple/maple_iOS/Info.plist b/frontend/src-tauri/gen/apple/maple_iOS/Info.plist index 2e8e14840..d38ce3269 100644 --- a/frontend/src-tauri/gen/apple/maple_iOS/Info.plist +++ b/frontend/src-tauri/gen/apple/maple_iOS/Info.plist @@ -15,9 +15,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0.4 + 1.0.5 CFBundleVersion - 1.0.4 + 1.0.5 LSRequiresIPhoneOS UILaunchStoryboardName diff --git a/frontend/src/components/Marketing.tsx b/frontend/src/components/Marketing.tsx index 63b0d8167..ad3c8ab25 100644 --- a/frontend/src/components/Marketing.tsx +++ b/frontend/src/components/Marketing.tsx @@ -201,8 +201,10 @@ export function Marketing() { const checkPlatform = async () => { try { // First check if we're in a Tauri environment - const isTauriEnv = await import('@tauri-apps/api/core').then(m => m.isTauri()).catch(() => false); - + const isTauriEnv = await import("@tauri-apps/api/core") + .then((m) => m.isTauri()) + .catch(() => false); + if (isTauriEnv) { // Only check platform type if we're in a Tauri environment const platform = await type(); diff --git a/frontend/src/routes/auth.$provider.callback.tsx b/frontend/src/routes/auth.$provider.callback.tsx index 5b19e6419..6ca4e2931 100644 --- a/frontend/src/routes/auth.$provider.callback.tsx +++ b/frontend/src/routes/auth.$provider.callback.tsx @@ -17,8 +17,6 @@ function formatProviderName(provider: string): string { return "GitHub"; case "google": return "Google"; - case "apple": - return "Apple"; default: return provider.charAt(0).toUpperCase() + provider.slice(1); } @@ -28,39 +26,39 @@ function OAuthCallback() { const [isProcessing, setIsProcessing] = useState(true); const [error, setError] = useState(null); const navigate = useNavigate(); - const { handleGitHubCallback, handleGoogleCallback, handleAppleCallback } = useOpenSecret(); + const { handleGitHubCallback, handleGoogleCallback } = useOpenSecret(); const processedRef = useRef(false); - + // Helper functions for the callback process const handleSuccessfulAuth = () => { // Check if this is a Tauri app auth flow (desktop or mobile) const isTauriAuth = localStorage.getItem("redirect-to-native") === "true"; - + // Clear the flag localStorage.removeItem("redirect-to-native"); - + if (isTauriAuth) { // Handle Tauri redirect const accessToken = localStorage.getItem("access_token") || ""; const refreshToken = localStorage.getItem("refresh_token"); - + let deepLinkUrl = `cloud.opensecret.maple://auth?access_token=${encodeURIComponent(accessToken)}`; - + if (refreshToken) { deepLinkUrl += `&refresh_token=${encodeURIComponent(refreshToken)}`; } - + setTimeout(() => { window.location.href = deepLinkUrl; }, 1000); - + return; } - + // Handle web redirect const selectedPlan = sessionStorage.getItem("selected_plan"); sessionStorage.removeItem("selected_plan"); - + setTimeout(() => { if (selectedPlan) { navigate({ @@ -72,7 +70,7 @@ function OAuthCallback() { } }, 2000); }; - + const handleAuthError = (error: unknown) => { console.error(`Authentication callback error:`, error); if (error instanceof Error) { @@ -90,29 +88,11 @@ function OAuthCallback() { const processCallback = async () => { if (processedRef.current) return; processedRef.current = true; - + // Get URL parameters for all OAuth providers const urlParams = new URLSearchParams(window.location.search); const code = urlParams.get("code"); const state = urlParams.get("state"); - - // For Apple, also check for user data in the URL parameters - if (provider === "apple") { - console.log("[Apple Auth] Processing Apple callback"); - - // Apple may include user data in the URL parameters with response_mode=query - const userParam = urlParams.get("user"); - if (userParam) { - try { - const userData = JSON.parse(decodeURIComponent(userParam)); - console.log("[Apple Auth] User data from Apple:", userData); - // userData.name.firstName, userData.name.lastName, userData.email available on first sign-in - // Note: Apple only sends this data on the first sign-in - } catch (e) { - console.error("[Apple Auth] Failed to parse user data:", e); - } - } - } if (code && state) { try { @@ -121,16 +101,12 @@ function OAuthCallback() { await handleGitHubCallback(code, state, ""); } else if (provider === "google") { await handleGoogleCallback(code, state, ""); - } else if (provider === "apple") { - // Use the OpenSecret SDK to handle Apple OAuth callback - await handleAppleCallback(code, state, ""); } else { throw new Error(`Unsupported provider: ${provider}`); } // Handle the successful authentication (redirect) handleSuccessfulAuth(); - } catch (error) { // Handle authentication error handleAuthError(error); @@ -144,7 +120,7 @@ function OAuthCallback() { }; processCallback(); - }, [handleGitHubCallback, handleGoogleCallback, handleAppleCallback, navigate, provider]); + }, [handleGitHubCallback, handleGoogleCallback, navigate, provider]); // If this is a Tauri app auth flow (desktop or mobile), show a different UI if (localStorage.getItem("redirect-to-native") === "true") { diff --git a/frontend/src/routes/desktop-auth.tsx b/frontend/src/routes/desktop-auth.tsx index 3b878dc66..bbc113954 100644 --- a/frontend/src/routes/desktop-auth.tsx +++ b/frontend/src/routes/desktop-auth.tsx @@ -16,7 +16,7 @@ export const Route = createFileRoute("/desktop-auth")({ validateSearch: (search: Record): DesktopAuthSearchParams => { const provider = typeof search.provider === "string" ? search.provider : "github"; // Validate provider is supported - if (provider !== "github" && provider !== "google" && provider !== "apple") { + if (provider !== "github" && provider !== "google") { throw new Error(`Unsupported provider: ${provider}`); } return { @@ -52,18 +52,6 @@ function DesktopAuth() { } else if (provider === "google") { const result = await os.initiateGoogleAuth(""); auth_url = result.auth_url; - } else if (provider === "apple") { - // Apple sign-in can be handled through OAuth too for non-iOS platforms - console.log("[Apple Auth] Initiating Apple OAuth flow for desktop/web"); - try { - // Use response_mode=query to make Apple return URL parameters instead of a form POST - const options = { response_mode: "query" }; - const result = await os.initiateAppleAuth("", options); - auth_url = result.auth_url; - } catch (error) { - console.error("[Apple Auth] Failed to initiate Apple OAuth:", error); - throw new Error("Failed to initiate Apple authentication. Please try again."); - } } else { throw new Error("Unsupported provider"); } diff --git a/frontend/src/routes/login.tsx b/frontend/src/routes/login.tsx index a7c9a5774..4dda32d22 100644 --- a/frontend/src/routes/login.tsx +++ b/frontend/src/routes/login.tsx @@ -34,8 +34,37 @@ function LoginPage() { const [loginMethod, setLoginMethod] = useState(null); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(false); - // No longer needed to check if it's iOS only for visibility - // We still check the platform inside the handlers to determine the flow + const [isIOS, setIsIOS] = useState(false); + + // Check if running on iOS + useEffect(() => { + const checkPlatform = async () => { + try { + // First check if we're in a Tauri environment + let isTauriEnv = false; + try { + isTauriEnv = await isTauri(); + } catch { + // Not in Tauri environment + isTauriEnv = false; + } + + // Only check platform type if we're in a Tauri environment + if (isTauriEnv) { + const platform = await type(); + setIsIOS(platform === "ios"); + } else { + // Not in Tauri environment, definitely not iOS + setIsIOS(false); + } + } catch (error) { + console.error("Error checking platform:", error); + setIsIOS(false); + } + }; + + checkPlatform(); + }, []); // Redirect if already logged in useEffect(() => { @@ -156,29 +185,15 @@ function LoginPage() { const handleAppleLogin = async () => { try { - const isTauriEnv = await isTauri(); - let isIOSDevice = false; - - // Only check platform type if we're in a Tauri environment - if (isTauriEnv) { - try { - const platform = await type(); - isIOSDevice = platform === "ios"; - } catch (error) { - console.error("Error checking platform type:", error); - } - } - - console.log("[OAuth] Using", isIOSDevice ? "iOS native" : isTauriEnv ? "Tauri" : "web", "flow"); - - if (isIOSDevice) { - // For iOS, use the native Apple Sign In - console.log("[OAuth] Initiating native Sign in with Apple"); + // Only iOS supports native Apple Sign In + console.log("[OAuth] Initiating native Sign in with Apple for iOS"); - try { - // Invoke the Apple Sign in plugin - // This will show the native Apple authentication UI - const result = await invoke("plugin:sign-in-with-apple|get_apple_id_credential", { + try { + // Invoke the Apple Sign in plugin + // This will show the native Apple authentication UI + const result = await invoke( + "plugin:sign-in-with-apple|get_apple_id_credential", + { payload: { scope: ["email", "fullName"], state: "apple-signin-state", @@ -187,68 +202,47 @@ function LoginPage() { debug: true } } - }); + } + ); - console.log("[OAuth] Apple Sign-In result:", result); + console.log("[OAuth] Apple Sign-In result:", result); - // Format the response for the API - const appleUser = { - user_identifier: result.user, - identity_token: result.identityToken, - email: result.email, - given_name: result.fullName?.givenName, - family_name: result.fullName?.familyName - }; + // Format the response for the API + const appleUser = { + user_identifier: result.user, + identity_token: result.identityToken, + email: result.email, + given_name: result.fullName?.givenName, + family_name: result.fullName?.familyName + }; - // Send to backend via SDK - try { - await os.handleAppleNativeSignIn(appleUser, ""); - // Redirect after successful login - if (selected_plan) { - navigate({ - to: "/pricing", - search: { selected_plan } - }); - } else { - navigate({ to: next || "/" }); - } - } catch (backendError) { - console.error("[OAuth] Backend processing failed:", backendError); - setError(backendError instanceof Error ? backendError.message : "Failed to process Apple authentication"); + // Send to backend via SDK + try { + await os.handleAppleNativeSignIn(appleUser, ""); + // Redirect after successful login + if (selected_plan) { + navigate({ + to: "/pricing", + search: { selected_plan } + }); + } else { + navigate({ to: next || "/" }); } - } catch (error) { - console.error("[OAuth] Failed to authenticate with Apple:", error); - const errorMessage = - error instanceof Error - ? `Apple Sign In error: ${error.message}` - : "Failed to authenticate with Apple. Please try again."; - setError(errorMessage); - } - } else if (isTauriEnv) { - // For Tauri (desktop), redirect to the web app's desktop-auth route - let desktopAuthUrl = "https://trymaple.ai/desktop-auth?provider=apple"; - - // If there's a selected plan, add it to the URL - if (selected_plan) { - desktopAuthUrl += `&selected_plan=${encodeURIComponent(selected_plan)}`; - } - - // Use the opener plugin by directly invoking the command - console.log("[OAuth] Opening URL in external browser:", desktopAuthUrl); - invoke("plugin:opener|open_url", { url: desktopAuthUrl }).catch((error: Error) => { - console.error("[OAuth] Failed to open external browser:", error); - setError("Failed to open authentication page in browser"); - }); - } else { - // Web flow for Apple OAuth with response_mode=query - // This makes Apple send the response as URL parameters instead of a form POST - const options = { response_mode: "query" }; - const { auth_url } = await os.initiateAppleAuth("", options); - - if (selected_plan) { - sessionStorage.setItem("selected_plan", selected_plan); + } catch (backendError) { + console.error("[OAuth] Backend processing failed:", backendError); + setError( + backendError instanceof Error + ? backendError.message + : "Failed to process Apple authentication" + ); } - window.location.href = auth_url; + } catch (error) { + console.error("[OAuth] Failed to authenticate with Apple:", error); + const errorMessage = + error instanceof Error + ? `Apple Sign In error: ${error.message}` + : "Failed to authenticate with Apple. Please try again."; + setError(errorMessage); } } catch (error) { console.error("Failed to initiate Apple login:", error); @@ -272,10 +266,12 @@ function LoginPage() { Log in with Google - + {isIOS && ( + + )}
Need an account?{" "} diff --git a/frontend/src/routes/pricing.tsx b/frontend/src/routes/pricing.tsx index ee918d8de..4702a76ac 100644 --- a/frontend/src/routes/pricing.tsx +++ b/frontend/src/routes/pricing.tsx @@ -153,8 +153,10 @@ function PricingPage() { const checkPlatform = async () => { try { // First check if we're in a Tauri environment - const isTauriEnv = await import('@tauri-apps/api/core').then(m => m.isTauri()).catch(() => false); - + const isTauriEnv = await import("@tauri-apps/api/core") + .then((m) => m.isTauri()) + .catch(() => false); + if (isTauriEnv) { // Only check platform type if we're in a Tauri environment const platform = await type(); diff --git a/frontend/src/routes/signup.tsx b/frontend/src/routes/signup.tsx index 9d83c494f..5a82362b4 100644 --- a/frontend/src/routes/signup.tsx +++ b/frontend/src/routes/signup.tsx @@ -34,8 +34,37 @@ function SignupPage() { const [signUpMethod, setSignUpMethod] = useState(null); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(false); - // No longer needed to check if it's iOS only for visibility - // We still check the platform inside the handlers to determine the flow + const [isIOS, setIsIOS] = useState(false); + + // Check if running on iOS + useEffect(() => { + const checkPlatform = async () => { + try { + // First check if we're in a Tauri environment + let isTauriEnv = false; + try { + isTauriEnv = await isTauri(); + } catch { + // Not in Tauri environment + isTauriEnv = false; + } + + // Only check platform type if we're in a Tauri environment + if (isTauriEnv) { + const platform = await type(); + setIsIOS(platform === "ios"); + } else { + // Not in Tauri environment, definitely not iOS + setIsIOS(false); + } + } catch (error) { + console.error("Error checking platform:", error); + setIsIOS(false); + } + }; + + checkPlatform(); + }, []); // Redirect if already logged in useEffect(() => { @@ -156,29 +185,15 @@ function SignupPage() { const handleAppleSignup = async () => { try { - const isTauriEnv = await isTauri(); - let isIOSDevice = false; - - // Only check platform type if we're in a Tauri environment - if (isTauriEnv) { - try { - const platform = await type(); - isIOSDevice = platform === "ios"; - } catch (error) { - console.error("Error checking platform type:", error); - } - } - - console.log("[OAuth] Using", isIOSDevice ? "iOS native" : isTauriEnv ? "Tauri" : "web", "flow"); - - if (isIOSDevice) { - // For iOS, use the native Apple Sign In - console.log("[OAuth] Initiating native Sign in with Apple"); + // Only iOS supports native Apple Sign In + console.log("[OAuth] Initiating native Sign in with Apple for iOS"); - try { - // Invoke the Apple Sign in plugin - // This will show the native Apple authentication UI - const result = await invoke("plugin:sign-in-with-apple|get_apple_id_credential", { + try { + // Invoke the Apple Sign in plugin + // This will show the native Apple authentication UI + const result = await invoke( + "plugin:sign-in-with-apple|get_apple_id_credential", + { payload: { scope: ["email", "fullName"], state: "apple-signup-state", @@ -187,68 +202,47 @@ function SignupPage() { debug: true } } - }); + } + ); - console.log("[OAuth] Apple Sign-In result:", result); + console.log("[OAuth] Apple Sign-In result:", result); - // Format the response for the API - const appleUser = { - user_identifier: result.user, - identity_token: result.identityToken, - email: result.email, - given_name: result.fullName?.givenName, - family_name: result.fullName?.familyName - }; + // Format the response for the API + const appleUser = { + user_identifier: result.user, + identity_token: result.identityToken, + email: result.email, + given_name: result.fullName?.givenName, + family_name: result.fullName?.familyName + }; - // Send to backend via SDK - try { - await os.handleAppleNativeSignIn(appleUser, ""); - // Redirect after successful signup - if (selected_plan) { - navigate({ - to: "/pricing", - search: { selected_plan } - }); - } else { - navigate({ to: next || "/" }); - } - } catch (backendError) { - console.error("[OAuth] Backend processing failed:", backendError); - setError(backendError instanceof Error ? backendError.message : "Failed to process Apple authentication"); + // Send to backend via SDK + try { + await os.handleAppleNativeSignIn(appleUser, ""); + // Redirect after successful signup + if (selected_plan) { + navigate({ + to: "/pricing", + search: { selected_plan } + }); + } else { + navigate({ to: next || "/" }); } - } catch (error) { - console.error("[OAuth] Failed to authenticate with Apple:", error); - const errorMessage = - error instanceof Error - ? `Apple Sign In error: ${error.message}` - : "Failed to authenticate with Apple. Please try again."; - setError(errorMessage); - } - } else if (isTauriEnv) { - // For Tauri (desktop), redirect to the web app's desktop-auth route - let desktopAuthUrl = "https://trymaple.ai/desktop-auth?provider=apple"; - - // If there's a selected plan, add it to the URL - if (selected_plan) { - desktopAuthUrl += `&selected_plan=${encodeURIComponent(selected_plan)}`; - } - - // Use the opener plugin by directly invoking the command - console.log("[OAuth] Opening URL in external browser:", desktopAuthUrl); - invoke("plugin:opener|open_url", { url: desktopAuthUrl }).catch((error: Error) => { - console.error("[OAuth] Failed to open external browser:", error); - setError("Failed to open authentication page in browser"); - }); - } else { - // Web flow for Apple OAuth with response_mode=query - // This makes Apple send the response as URL parameters instead of a form POST - const options = { response_mode: "query" }; - const { auth_url } = await os.initiateAppleAuth("", options); - - if (selected_plan) { - sessionStorage.setItem("selected_plan", selected_plan); + } catch (backendError) { + console.error("[OAuth] Backend processing failed:", backendError); + setError( + backendError instanceof Error + ? backendError.message + : "Failed to process Apple authentication" + ); } - window.location.href = auth_url; + } catch (error) { + console.error("[OAuth] Failed to authenticate with Apple:", error); + const errorMessage = + error instanceof Error + ? `Apple Sign In error: ${error.message}` + : "Failed to authenticate with Apple. Please try again."; + setError(errorMessage); } } catch (error) { console.error("Failed to initiate Apple signup:", error); @@ -272,10 +266,12 @@ function SignupPage() { Sign up with Google - + {isIOS && ( + + )}
Already have an account?{" "} diff --git a/frontend/src/types/apple-sign-in.d.ts b/frontend/src/types/apple-sign-in.d.ts index 31934ce97..086d1f8a7 100644 --- a/frontend/src/types/apple-sign-in.d.ts +++ b/frontend/src/types/apple-sign-in.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Apple Sign In plugin and OAuth +// Type definitions for native iOS Apple Sign In plugin // For native Apple Sign In plugin interface AppleCredential { @@ -11,8 +11,3 @@ interface AppleCredential { }; state?: string; } - -// For Apple OAuth options -interface AppleOAuthOptions { - response_mode?: "query" | "form_post"; -} \ No newline at end of file From 19f254fe8c9904c4f2ba5c95f1d472ce3267ee2e Mon Sep 17 00:00:00 2001 From: Tony Giorgio Date: Fri, 25 Apr 2025 12:02:21 -0500 Subject: [PATCH 4/6] Bump opensecret --- frontend/bun.lock | 162 +----------------------------------------- frontend/package.json | 2 +- 2 files changed, 3 insertions(+), 161 deletions(-) diff --git a/frontend/bun.lock b/frontend/bun.lock index 576d84769..6765c7811 100644 --- a/frontend/bun.lock +++ b/frontend/bun.lock @@ -4,7 +4,7 @@ "": { "name": "maple", "dependencies": { - "@opensecret/react": "../../OpenSecret-SDK/", + "@opensecret/react": "1.3.0", "@radix-ui/react-alert-dialog": "^1.1.1", "@radix-ui/react-avatar": "^1.1.0", "@radix-ui/react-dialog": "^1.1.1", @@ -202,14 +202,6 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="], - "@microsoft/api-extractor": ["@microsoft/api-extractor@7.52.5", "", { "dependencies": { "@microsoft/api-extractor-model": "7.30.5", "@microsoft/tsdoc": "~0.15.1", "@microsoft/tsdoc-config": "~0.17.1", "@rushstack/node-core-library": "5.13.0", "@rushstack/rig-package": "0.5.3", "@rushstack/terminal": "0.15.2", "@rushstack/ts-command-line": "5.0.0", "lodash": "~4.17.15", "minimatch": "~3.0.3", "resolve": "~1.22.1", "semver": "~7.5.4", "source-map": "~0.6.1", "typescript": "5.8.2" }, "bin": { "api-extractor": "bin/api-extractor" } }, "sha512-6WWgjjg6FkoDWpF/O3sjB05OkszpI5wtKJqd8fUIR/JJUv8IqNCGr1lJUZJnc1HegcT9gAvyf98KfH0wFncU0w=="], - - "@microsoft/api-extractor-model": ["@microsoft/api-extractor-model@7.30.5", "", { "dependencies": { "@microsoft/tsdoc": "~0.15.1", "@microsoft/tsdoc-config": "~0.17.1", "@rushstack/node-core-library": "5.13.0" } }, "sha512-0ic4rcbcDZHz833RaTZWTGu+NpNgrxVNjVaor0ZDUymfDFzjA/Uuk8hYziIUIOEOSTfmIQqyzVwlzxZxPe7tOA=="], - - "@microsoft/tsdoc": ["@microsoft/tsdoc@0.15.1", "", {}, "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw=="], - - "@microsoft/tsdoc-config": ["@microsoft/tsdoc-config@0.17.1", "", { "dependencies": { "@microsoft/tsdoc": "0.15.1", "ajv": "~8.12.0", "jju": "~1.4.0", "resolve": "~1.22.2" } }, "sha512-UtjIFe0C6oYgTnad4q1QP4qXwLhe6tIpNTRStJ2RZEPIkqQPREAwE5spzVxsdn9UaEMUqhh0AqSx3X4nWAKXWw=="], - "@noble/curves": ["@noble/curves@1.8.1", "", { "dependencies": { "@noble/hashes": "1.7.1" } }, "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ=="], "@noble/hashes": ["@noble/hashes@1.7.1", "", {}, "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ=="], @@ -220,7 +212,7 @@ "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], - "@opensecret/react": ["@opensecret/react@file:../../OpenSecret-SDK", { "dependencies": { "@peculiar/x509": "^1.12.2", "@stablelib/base64": "^2.0.0", "@stablelib/chacha20poly1305": "^2.0.0", "@stablelib/random": "^2.0.0", "cbor2": "^1.7.0", "tweetnacl": "^1.0.3", "zod": "^3.23.8" }, "devDependencies": { "@eslint/js": "^9.13.0", "@noble/curves": "^1.6.0", "@noble/hashes": "^1.5.0", "@types/bun": "latest", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", "@vitejs/plugin-react": "^4.3.3", "eslint": "^9.13.0", "eslint-plugin-react-hooks": "^5.0.0", "eslint-plugin-react-refresh": "^0.4.14", "globals": "^15.11.0", "openai": "4.72.0", "prettier": "^3.3.3", "typescript": "~5.6.2", "typescript-eslint": "^8.11.0", "vite": "^5.4.12", "vite-plugin-dts": "^4.3.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }], + "@opensecret/react": ["@opensecret/react@1.3.0", "", { "dependencies": { "@peculiar/x509": "^1.12.2", "@stablelib/base64": "^2.0.0", "@stablelib/chacha20poly1305": "^2.0.0", "@stablelib/random": "^2.0.0", "cbor2": "^1.7.0", "tweetnacl": "^1.0.3", "zod": "^3.23.8" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-vsYjgpvauakBsNqwPSZ4ZyI8C86+W1J56/swEaRJ6O4yIpPloBqg8XgypBsI6x+FFbQ2h65/MelZJfa42YhwLw=="], "@peculiar/asn1-cms": ["@peculiar/asn1-cms@2.3.15", "", { "dependencies": { "@peculiar/asn1-schema": "^2.3.15", "@peculiar/asn1-x509": "^2.3.15", "@peculiar/asn1-x509-attr": "^2.3.15", "asn1js": "^3.0.5", "tslib": "^2.8.1" } }, "sha512-B+DoudF+TCrxoJSTjjcY8Mmu+lbv8e7pXGWrhNp2/EGJp9EEcpzjBCar7puU57sGifyzaRVM03oD5L7t7PghQg=="], @@ -318,8 +310,6 @@ "@radix-ui/rect": ["@radix-ui/rect@1.1.0", "", {}, "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg=="], - "@rollup/pluginutils": ["@rollup/pluginutils@5.1.4", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ=="], - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.34.8", "", { "os": "android", "cpu": "arm" }, "sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw=="], "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.34.8", "", { "os": "android", "cpu": "arm64" }, "sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q=="], @@ -358,14 +348,6 @@ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.34.8", "", { "os": "win32", "cpu": "x64" }, "sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g=="], - "@rushstack/node-core-library": ["@rushstack/node-core-library@5.13.0", "", { "dependencies": { "ajv": "~8.13.0", "ajv-draft-04": "~1.0.0", "ajv-formats": "~3.0.1", "fs-extra": "~11.3.0", "import-lazy": "~4.0.0", "jju": "~1.4.0", "resolve": "~1.22.1", "semver": "~7.5.4" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-IGVhy+JgUacAdCGXKUrRhwHMTzqhWwZUI+qEPcdzsb80heOw0QPbhhoVsoiMF7Klp8eYsp7hzpScMXmOa3Uhfg=="], - - "@rushstack/rig-package": ["@rushstack/rig-package@0.5.3", "", { "dependencies": { "resolve": "~1.22.1", "strip-json-comments": "~3.1.1" } }, "sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow=="], - - "@rushstack/terminal": ["@rushstack/terminal@0.15.2", "", { "dependencies": { "@rushstack/node-core-library": "5.13.0", "supports-color": "~8.1.1" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-7Hmc0ysK5077R/IkLS9hYu0QuNafm+TbZbtYVzCMbeOdMjaRboLKrhryjwZSRJGJzu+TV1ON7qZHeqf58XfLpA=="], - - "@rushstack/ts-command-line": ["@rushstack/ts-command-line@5.0.0", "", { "dependencies": { "@rushstack/terminal": "0.15.2", "@types/argparse": "1.0.38", "argparse": "~1.0.9", "string-argv": "~0.3.1" } }, "sha512-SW6nqZVxH26Rxz25+lJQRlnXI/YCrNH7NfDEWPPm9i0rwkSE6Rgtmzw96cuZgQjacOh0sw77d6V4SvgarAfr8g=="], - "@stablelib/aead": ["@stablelib/aead@2.0.0", "", {}, "sha512-U/RMANRxbT/ahIpYsPSiFwDFNjADHdnCFfmo09MO1ai2XmerPAOPtMl0qmX7XVvygnACC6ijKDyHBoT2rGyElg=="], "@stablelib/base64": ["@stablelib/base64@2.0.1", "", {}, "sha512-P2z89A7N1ETt6RxgpVdDT2xlg8cnm3n6td0lY9gyK7EiWK3wdq388yFX/hLknkCC0we05OZAD1rfxlQJUbl5VQ=="], @@ -440,8 +422,6 @@ "@tauri-apps/plugin-os": ["@tauri-apps/plugin-os@2.2.1", "", { "dependencies": { "@tauri-apps/api": "^2.0.0" } }, "sha512-cNYpNri2CCc6BaNeB6G/mOtLvg8dFyFQyCUdf2y0K8PIAKGEWdEcu8DECkydU2B+oj4OJihDPD2de5K6cbVl9A=="], - "@types/argparse": ["@types/argparse@1.0.38", "", {}, "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA=="], - "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], "@types/babel__generator": ["@types/babel__generator@7.6.8", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw=="], @@ -502,22 +482,6 @@ "@vitejs/plugin-react": ["@vitejs/plugin-react@4.3.4", "", { "dependencies": { "@babel/core": "^7.26.0", "@babel/plugin-transform-react-jsx-self": "^7.25.9", "@babel/plugin-transform-react-jsx-source": "^7.25.9", "@types/babel__core": "^7.20.5", "react-refresh": "^0.14.2" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" } }, "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug=="], - "@volar/language-core": ["@volar/language-core@2.4.12", "", { "dependencies": { "@volar/source-map": "2.4.12" } }, "sha512-RLrFdXEaQBWfSnYGVxvR2WrO6Bub0unkdHYIdC31HzIEqATIuuhRRzYu76iGPZ6OtA4Au1SnW0ZwIqPP217YhA=="], - - "@volar/source-map": ["@volar/source-map@2.4.12", "", {}, "sha512-bUFIKvn2U0AWojOaqf63ER0N/iHIBYZPpNGogfLPQ68F5Eet6FnLlyho7BS0y2HJ1jFhSif7AcuTx1TqsCzRzw=="], - - "@volar/typescript": ["@volar/typescript@2.4.12", "", { "dependencies": { "@volar/language-core": "2.4.12", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "sha512-HJB73OTJDgPc80K30wxi3if4fSsZZAOScbj2fcicMuOPoOkcf9NNAINb33o+DzhBdF9xTKC1gnPmIRDous5S0g=="], - - "@vue/compiler-core": ["@vue/compiler-core@3.5.13", "", { "dependencies": { "@babel/parser": "^7.25.3", "@vue/shared": "3.5.13", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" } }, "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q=="], - - "@vue/compiler-dom": ["@vue/compiler-dom@3.5.13", "", { "dependencies": { "@vue/compiler-core": "3.5.13", "@vue/shared": "3.5.13" } }, "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA=="], - - "@vue/compiler-vue2": ["@vue/compiler-vue2@2.7.16", "", { "dependencies": { "de-indent": "^1.0.2", "he": "^1.2.0" } }, "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A=="], - - "@vue/language-core": ["@vue/language-core@2.2.0", "", { "dependencies": { "@volar/language-core": "~2.4.11", "@vue/compiler-dom": "^3.5.0", "@vue/compiler-vue2": "^2.7.16", "@vue/shared": "^3.5.0", "alien-signals": "^0.4.9", "minimatch": "^9.0.3", "muggle-string": "^0.4.1", "path-browserify": "^1.0.1" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw=="], - - "@vue/shared": ["@vue/shared@3.5.13", "", {}, "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ=="], - "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], "acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="], @@ -528,12 +492,6 @@ "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], - "ajv-draft-04": ["ajv-draft-04@1.0.0", "", { "peerDependencies": { "ajv": "^8.5.0" }, "optionalPeers": ["ajv"] }, "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw=="], - - "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], - - "alien-signals": ["alien-signals@0.4.14", "", {}, "sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q=="], - "ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], @@ -610,12 +568,8 @@ "commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], - "compare-versions": ["compare-versions@6.1.1", "", {}, "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg=="], - "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], - "confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="], - "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], @@ -624,8 +578,6 @@ "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], - "de-indent": ["de-indent@1.0.2", "", {}, "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg=="], - "debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], "decode-named-character-reference": ["decode-named-character-reference@1.0.2", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg=="], @@ -690,14 +642,10 @@ "estree-util-is-identifier-name": ["estree-util-is-identifier-name@3.0.0", "", {}, "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg=="], - "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], - "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="], - "exsolve": ["exsolve@1.0.5", "", {}, "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg=="], - "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], @@ -730,8 +678,6 @@ "fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="], - "fs-extra": ["fs-extra@11.3.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew=="], - "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], @@ -756,8 +702,6 @@ "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], - "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], - "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], @@ -788,8 +732,6 @@ "hastscript": ["hastscript@9.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^6.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-jzaLBGavEDKHrc5EfFImKN7nZKKBdSLIdGvCwDZ9TfzbF2ffXiov8CKE445L2Z1Ek2t/m4SKQ2j6Ipv7NyUolw=="], - "he": ["he@1.2.0", "", { "bin": { "he": "bin/he" } }, "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="], - "highlight.js": ["highlight.js@11.11.1", "", {}, "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="], "html-url-attributes": ["html-url-attributes@3.0.1", "", {}, "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ=="], @@ -800,8 +742,6 @@ "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], - "import-lazy": ["import-lazy@4.0.0", "", {}, "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw=="], - "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], "inline-style-parser": ["inline-style-parser@0.2.4", "", {}, "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q=="], @@ -834,8 +774,6 @@ "jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="], - "jju": ["jju@1.4.0", "", {}, "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA=="], - "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], @@ -850,26 +788,18 @@ "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], - "jsonfile": ["jsonfile@6.1.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ=="], - "katex": ["katex@0.16.21", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-XvqR7FgOHtWupfMiigNzmh+MgUVmDGU2kXZm899ZkPfcuoPuFxyHmXsgATDpFZDAXCI8tvinaVcDo8PIIJSo4A=="], "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], - "kolorist": ["kolorist@1.8.0", "", {}, "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ=="], - "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], - "local-pkg": ["local-pkg@1.1.1", "", { "dependencies": { "mlly": "^1.7.4", "pkg-types": "^2.0.1", "quansync": "^0.2.8" } }, "sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg=="], - "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], - "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], - "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], @@ -882,8 +812,6 @@ "lucide-react": ["lucide-react@0.436.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" } }, "sha512-N292bIxoqm1aObAg0MzFtvhYwgQE6qnIOWx/GLj5ONgcTPH6N0fD9bVq/GfdeC9ZORBXozt/XeEKDpiB3x3vlQ=="], - "magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="], - "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], @@ -992,12 +920,8 @@ "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], - "mlly": ["mlly@1.7.4", "", { "dependencies": { "acorn": "^8.14.0", "pathe": "^2.0.1", "pkg-types": "^1.3.0", "ufo": "^1.5.4" } }, "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw=="], - "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - "muggle-string": ["muggle-string@0.4.1", "", {}, "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ=="], - "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], "nanoid": ["nanoid@3.3.8", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="], @@ -1034,8 +958,6 @@ "parse5": ["parse5@7.2.1", "", { "dependencies": { "entities": "^4.5.0" } }, "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ=="], - "path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="], - "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], @@ -1044,8 +966,6 @@ "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], - "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], @@ -1054,8 +974,6 @@ "pirates": ["pirates@4.0.6", "", {}, "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg=="], - "pkg-types": ["pkg-types@2.1.0", "", { "dependencies": { "confbox": "^0.2.1", "exsolve": "^1.0.1", "pathe": "^2.0.3" } }, "sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A=="], - "postcss": ["postcss@8.5.2", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA=="], "postcss-import": ["postcss-import@15.1.0", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="], @@ -1082,8 +1000,6 @@ "pvutils": ["pvutils@1.1.3", "", {}, "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ=="], - "quansync": ["quansync@0.2.10", "", {}, "sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A=="], - "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], "react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="], @@ -1122,8 +1038,6 @@ "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="], - "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], - "resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], @@ -1146,16 +1060,10 @@ "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], - "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], - "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], - "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], - - "string-argv": ["string-argv@0.3.2", "", {}, "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q=="], - "string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -1216,8 +1124,6 @@ "typescript-eslint": ["typescript-eslint@8.24.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.24.0", "@typescript-eslint/parser": "8.24.0", "@typescript-eslint/utils": "8.24.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-/lmv4366en/qbB32Vz5+kCNZEMf6xYHwh1z48suBwZvAtnXKbP+YhGe8OLE2BqC67LMqKkCNLtjejdwsdW6uOQ=="], - "ufo": ["ufo@1.6.1", "", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="], - "undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], @@ -1236,8 +1142,6 @@ "unist-util-visit-parents": ["unist-util-visit-parents@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw=="], - "universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="], - "unplugin": ["unplugin@2.2.0", "", { "dependencies": { "acorn": "^8.14.0", "webpack-virtual-modules": "^0.6.2" } }, "sha512-m1ekpSwuOT5hxkJeZGRxO7gXbXT3gF26NjQ7GdVHoLoF8/nopLcd/QfPigpCy7i51oFHiRJg/CyHhj4vs2+KGw=="], "update-browserslist-db": ["update-browserslist-db@1.1.2", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg=="], @@ -1260,10 +1164,6 @@ "vite": ["vite@5.4.14", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA=="], - "vite-plugin-dts": ["vite-plugin-dts@4.5.3", "", { "dependencies": { "@microsoft/api-extractor": "^7.50.1", "@rollup/pluginutils": "^5.1.4", "@volar/typescript": "^2.4.11", "@vue/language-core": "2.2.0", "compare-versions": "^6.1.1", "debug": "^4.4.0", "kolorist": "^1.8.0", "local-pkg": "^1.0.0", "magic-string": "^0.30.17" }, "peerDependencies": { "typescript": "*", "vite": "*" }, "optionalPeers": ["vite"] }, "sha512-P64VnD00dR+e8S26ESoFELqc17+w7pKkwlBpgXteOljFyT0zDwD8hH4zXp49M/kciy//7ZbVXIwQCekBJjfWzA=="], - - "vscode-uri": ["vscode-uri@3.1.0", "", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="], - "web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="], "web-streams-polyfill": ["web-streams-polyfill@4.0.0-beta.3", "", {}, "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug=="], @@ -1302,42 +1202,10 @@ "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="], - "@microsoft/api-extractor/minimatch": ["minimatch@3.0.8", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q=="], - - "@microsoft/api-extractor/semver": ["semver@7.5.4", "", { "dependencies": { "lru-cache": "^6.0.0" }, "bin": { "semver": "bin/semver.js" } }, "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA=="], - - "@microsoft/api-extractor/typescript": ["typescript@5.8.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ=="], - - "@microsoft/tsdoc-config/ajv": ["ajv@8.12.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA=="], - - "@opensecret/react/@types/bun": ["@types/bun@1.2.10", "", { "dependencies": { "bun-types": "1.2.10" } }, "sha512-eilv6WFM3M0c9ztJt7/g80BDusK98z/FrFwseZgT4bXCq2vPhXD4z8R3oddmAn+R/Nmz9vBn4kweJKmGTZj+lg=="], - - "@opensecret/react/@types/react": ["@types/react@19.1.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw=="], - - "@opensecret/react/@types/react-dom": ["@types/react-dom@19.1.2", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-XGJkWF41Qq305SKWEILa1O8vzhb3aOo3ogBlSmiqNko/WmRb6QIaweuZCXjKygVDXpzXb5wyxKTSOsmkuqj+Qw=="], - - "@opensecret/react/openai": ["openai@4.72.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" }, "peerDependencies": { "zod": "^3.23.8" }, "optionalPeers": ["zod"], "bin": { "openai": "bin/cli" } }, "sha512-hFqG9BWCs7L7ifrhJXw7mJXmUBr7d9N6If3J9563o0jfwVA4wFANFDDaOIWFdgDdwgCXg5emf0Q+LoLCGszQYA=="], - - "@opensecret/react/typescript": ["typescript@5.6.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw=="], - - "@rollup/pluginutils/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], - - "@rushstack/node-core-library/ajv": ["ajv@8.13.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.4.1" } }, "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA=="], - - "@rushstack/node-core-library/semver": ["semver@7.5.4", "", { "dependencies": { "lru-cache": "^6.0.0" }, "bin": { "semver": "bin/semver.js" } }, "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA=="], - - "@rushstack/terminal/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], - - "@rushstack/ts-command-line/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], - "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], "@typescript-eslint/typescript-estree/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], - "@vue/language-core/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], - - "ajv-formats/ajv": ["ajv@8.13.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.4.1" } }, "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA=="], - "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], @@ -1348,8 +1216,6 @@ "mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], - "mlly/pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], - "openai/@types/node": ["@types/node@18.19.76", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-yvR7Q9LdPz2vGpmpJX5LolrgRdWvB67MJKDPSgIIzpFbaf9a1j/f5DnLp5VDyHGMR0QZHlTr1afsD87QCXFHKw=="], "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], @@ -1372,28 +1238,10 @@ "wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - "@microsoft/api-extractor/semver/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], - - "@microsoft/tsdoc-config/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], - - "@opensecret/react/@types/bun/bun-types": ["bun-types@1.2.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-b5ITZMnVdf3m1gMvJHG+gIfeJHiQPJak0f7925Hxu6ZN5VKA8AGy4GZ4lM+Xkn6jtWxg5S3ldWvfmXdvnkp3GQ=="], - - "@opensecret/react/openai/@types/node": ["@types/node@18.19.76", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-yvR7Q9LdPz2vGpmpJX5LolrgRdWvB67MJKDPSgIIzpFbaf9a1j/f5DnLp5VDyHGMR0QZHlTr1afsD87QCXFHKw=="], - - "@rushstack/node-core-library/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], - - "@rushstack/node-core-library/semver/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], - "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], - "@vue/language-core/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], - - "ajv-formats/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], - "glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], - "mlly/pkg-types/confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], - "openai/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], @@ -1447,11 +1295,5 @@ "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - - "@microsoft/api-extractor/semver/lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], - - "@opensecret/react/openai/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], - - "@rushstack/node-core-library/semver/lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], } } diff --git a/frontend/package.json b/frontend/package.json index 61ac01376..24176dbee 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -16,7 +16,7 @@ "mdast-util-gfm-autolink-literal": "2.0.0" }, "dependencies": { - "@opensecret/react": "../../OpenSecret-SDK/", + "@opensecret/react": "1.3.0", "@radix-ui/react-alert-dialog": "^1.1.1", "@radix-ui/react-avatar": "^1.1.0", "@radix-ui/react-dialog": "^1.1.1", From a77b8321e26a48a0ce5209e0809dae7f7f023754 Mon Sep 17 00:00:00 2001 From: Tony Giorgio Date: Thu, 1 May 2025 12:58:01 -0500 Subject: [PATCH 5/6] iOS auth cleanup --- .gitignore | 2 ++ frontend/bun.lock | 6 ++++++ frontend/package.json | 4 +++- frontend/src-tauri/Cargo.toml | 2 +- frontend/src-tauri/src/lib.rs | 15 +++++++++++---- frontend/src/routes/login.tsx | 13 ++++++++++--- frontend/src/routes/signup.tsx | 13 ++++++++++--- frontend/src/types/apple-sign-in.d.ts | 2 +- 8 files changed, 44 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index 40201a8d9..115cc540f 100644 --- a/.gitignore +++ b/.gitignore @@ -63,3 +63,5 @@ frontend/*.local # Build caching apparently? *.tsbuildinfo + +**/.claude/settings.local.json diff --git a/frontend/bun.lock b/frontend/bun.lock index 6765c7811..158a694a4 100644 --- a/frontend/bun.lock +++ b/frontend/bun.lock @@ -35,6 +35,7 @@ "remark-math": "^6.0.0", "tailwind-merge": "^2.6.0", "tailwindcss-animate": "^1.0.7", + "uuid": "^11.1.0", }, "devDependencies": { "@eslint/js": "^9.14.0", @@ -47,6 +48,7 @@ "@types/node": "^22.3.0", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", + "@types/uuid": "^10.0.0", "@vitejs/plugin-react": "^4.3.1", "autoprefixer": "^10.4.20", "eslint": "^9.8.0", @@ -460,6 +462,8 @@ "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + "@types/uuid": ["@types/uuid@10.0.0", "", {}, "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ=="], + "@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="], "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.24.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.24.0", "@typescript-eslint/type-utils": "8.24.0", "@typescript-eslint/utils": "8.24.0", "@typescript-eslint/visitor-keys": "8.24.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-aFcXEJJCI4gUdXgoo/j9udUYIHgF23MFkg09LFz2dzEmU0+1Plk4rQWv/IYKvPHAtlkkGoB3m5e6oUp+JPsNaQ=="], @@ -1156,6 +1160,8 @@ "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + "uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="], + "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], "vfile-location": ["vfile-location@5.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg=="], diff --git a/frontend/package.json b/frontend/package.json index 24176dbee..7623eae4c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -46,7 +46,8 @@ "remark-gfm": "^4.0.0", "remark-math": "^6.0.0", "tailwind-merge": "^2.6.0", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "uuid": "^11.1.0" }, "devDependencies": { "@eslint/js": "^9.14.0", @@ -59,6 +60,7 @@ "@types/node": "^22.3.0", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", + "@types/uuid": "^10.0.0", "@vitejs/plugin-react": "^4.3.1", "autoprefixer": "^10.4.20", "eslint": "^9.8.0", diff --git a/frontend/src-tauri/Cargo.toml b/frontend/src-tauri/Cargo.toml index bc0ab1417..58137c0d7 100644 --- a/frontend/src-tauri/Cargo.toml +++ b/frontend/src-tauri/Cargo.toml @@ -29,6 +29,6 @@ tauri-plugin = "2.1.1" tauri-plugin-deep-link = "2" tauri-plugin-opener = "2" tauri-plugin-os = "2" -tauri-plugin-sign-in-with-apple = "1.0.0" +tauri-plugin-sign-in-with-apple = "1.0.2" tokio = { version = "1.0", features = ["time"] } once_cell = "1.18.0" diff --git a/frontend/src-tauri/src/lib.rs b/frontend/src-tauri/src/lib.rs index 1cac2c392..52777924e 100644 --- a/frontend/src-tauri/src/lib.rs +++ b/frontend/src-tauri/src/lib.rs @@ -22,7 +22,6 @@ pub fn run() { .plugin(tauri_plugin_deep_link::init()) .plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_os::init()) - .plugin(tauri_plugin_sign_in_with_apple::init()) .setup(|app| { // Set up the deep link handler // Use a cloned handle with 'static lifetime @@ -186,7 +185,7 @@ pub fn run() { .plugin(tauri_plugin_updater::Builder::new().build()); #[cfg(not(desktop))] - let app = tauri::Builder::default() + let mut builder = tauri::Builder::default() .plugin( tauri_plugin_log::Builder::default() .level(log::LevelFilter::Info) @@ -194,8 +193,16 @@ pub fn run() { ) .plugin(tauri_plugin_deep_link::init()) .plugin(tauri_plugin_opener::init()) - .plugin(tauri_plugin_os::init()) - .plugin(tauri_plugin_sign_in_with_apple::init()) + .plugin(tauri_plugin_os::init()); + + // Only add the Apple Sign In plugin on iOS + #[cfg(all(not(desktop), target_os = "ios"))] + { + builder = builder.plugin(tauri_plugin_sign_in_with_apple::init()); + } + + #[cfg(not(desktop))] + let app = builder .setup(|app| { // Set up the deep link handler for mobile let app_handle = app.handle().clone(); diff --git a/frontend/src/routes/login.tsx b/frontend/src/routes/login.tsx index 4dda32d22..dbd498254 100644 --- a/frontend/src/routes/login.tsx +++ b/frontend/src/routes/login.tsx @@ -11,6 +11,8 @@ import { Apple } from "@/components/icons/Apple"; import { AuthMain } from "@/components/AuthMain"; import { isTauri, invoke } from "@tauri-apps/api/core"; import { type } from "@tauri-apps/plugin-os"; +import { v4 as uuidv4 } from "uuid"; +import type { AppleCredential } from "@/types/apple-sign-in"; type LoginSearchParams = { next?: string; @@ -189,6 +191,10 @@ function LoginPage() { console.log("[OAuth] Initiating native Sign in with Apple for iOS"); try { + // Generate random UUIDs for state and nonce + const state = uuidv4(); + const nonce = uuidv4(); + // Invoke the Apple Sign in plugin // This will show the native Apple authentication UI const result = await invoke( @@ -196,10 +202,11 @@ function LoginPage() { { payload: { scope: ["email", "fullName"], - state: "apple-signin-state", - // Add options to help with debugging + state, + nonce, + // Disable debug mode in production options: { - debug: true + debug: false } } } diff --git a/frontend/src/routes/signup.tsx b/frontend/src/routes/signup.tsx index 5a82362b4..be26c4c5a 100644 --- a/frontend/src/routes/signup.tsx +++ b/frontend/src/routes/signup.tsx @@ -11,6 +11,8 @@ import { Apple } from "@/components/icons/Apple"; import { AuthMain } from "@/components/AuthMain"; import { isTauri, invoke } from "@tauri-apps/api/core"; import { type } from "@tauri-apps/plugin-os"; +import { v4 as uuidv4 } from "uuid"; +import type { AppleCredential } from "@/types/apple-sign-in"; type SignupSearchParams = { next?: string; @@ -189,6 +191,10 @@ function SignupPage() { console.log("[OAuth] Initiating native Sign in with Apple for iOS"); try { + // Generate random UUIDs for state and nonce + const state = uuidv4(); + const nonce = uuidv4(); + // Invoke the Apple Sign in plugin // This will show the native Apple authentication UI const result = await invoke( @@ -196,10 +202,11 @@ function SignupPage() { { payload: { scope: ["email", "fullName"], - state: "apple-signup-state", - // Add options to help with debugging + state, + nonce, + // Disable debug mode in production options: { - debug: true + debug: false } } } diff --git a/frontend/src/types/apple-sign-in.d.ts b/frontend/src/types/apple-sign-in.d.ts index 086d1f8a7..406949156 100644 --- a/frontend/src/types/apple-sign-in.d.ts +++ b/frontend/src/types/apple-sign-in.d.ts @@ -1,7 +1,7 @@ // Type definitions for native iOS Apple Sign In plugin // For native Apple Sign In plugin -interface AppleCredential { +export interface AppleCredential { user: string; identityToken: string; email?: string; From d9ecddec2d62c049a8cd952346db98c0f1bff456 Mon Sep 17 00:00:00 2001 From: Tony Giorgio Date: Thu, 1 May 2025 17:52:27 -0500 Subject: [PATCH 6/6] Add apple nonce parameter --- frontend/bun.lock | 4 ++-- frontend/package.json | 2 +- frontend/src/routes/login.tsx | 14 +++++++++++--- frontend/src/routes/signup.tsx | 14 +++++++++++--- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/frontend/bun.lock b/frontend/bun.lock index 158a694a4..5ebd74298 100644 --- a/frontend/bun.lock +++ b/frontend/bun.lock @@ -4,7 +4,7 @@ "": { "name": "maple", "dependencies": { - "@opensecret/react": "1.3.0", + "@opensecret/react": "1.3.1", "@radix-ui/react-alert-dialog": "^1.1.1", "@radix-ui/react-avatar": "^1.1.0", "@radix-ui/react-dialog": "^1.1.1", @@ -214,7 +214,7 @@ "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], - "@opensecret/react": ["@opensecret/react@1.3.0", "", { "dependencies": { "@peculiar/x509": "^1.12.2", "@stablelib/base64": "^2.0.0", "@stablelib/chacha20poly1305": "^2.0.0", "@stablelib/random": "^2.0.0", "cbor2": "^1.7.0", "tweetnacl": "^1.0.3", "zod": "^3.23.8" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-vsYjgpvauakBsNqwPSZ4ZyI8C86+W1J56/swEaRJ6O4yIpPloBqg8XgypBsI6x+FFbQ2h65/MelZJfa42YhwLw=="], + "@opensecret/react": ["@opensecret/react@1.3.1", "", { "dependencies": { "@peculiar/x509": "^1.12.2", "@stablelib/base64": "^2.0.0", "@stablelib/chacha20poly1305": "^2.0.0", "@stablelib/random": "^2.0.0", "cbor2": "^1.7.0", "tweetnacl": "^1.0.3", "zod": "^3.23.8" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-C53cinMhkn2HNJ2LJxY5+xhVcyT2Y6p7z9nsAXCMkBQl677VteMGaDPifxsJpEIX1bBp5ZEsKIRX1md2zta3yw=="], "@peculiar/asn1-cms": ["@peculiar/asn1-cms@2.3.15", "", { "dependencies": { "@peculiar/asn1-schema": "^2.3.15", "@peculiar/asn1-x509": "^2.3.15", "@peculiar/asn1-x509-attr": "^2.3.15", "asn1js": "^3.0.5", "tslib": "^2.8.1" } }, "sha512-B+DoudF+TCrxoJSTjjcY8Mmu+lbv8e7pXGWrhNp2/EGJp9EEcpzjBCar7puU57sGifyzaRVM03oD5L7t7PghQg=="], diff --git a/frontend/package.json b/frontend/package.json index 7623eae4c..04b43d736 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -16,7 +16,7 @@ "mdast-util-gfm-autolink-literal": "2.0.0" }, "dependencies": { - "@opensecret/react": "1.3.0", + "@opensecret/react": "1.3.1", "@radix-ui/react-alert-dialog": "^1.1.1", "@radix-ui/react-avatar": "^1.1.0", "@radix-ui/react-dialog": "^1.1.1", diff --git a/frontend/src/routes/login.tsx b/frontend/src/routes/login.tsx index dbd498254..527fd2043 100644 --- a/frontend/src/routes/login.tsx +++ b/frontend/src/routes/login.tsx @@ -13,6 +13,8 @@ import { isTauri, invoke } from "@tauri-apps/api/core"; import { type } from "@tauri-apps/plugin-os"; import { v4 as uuidv4 } from "uuid"; import type { AppleCredential } from "@/types/apple-sign-in"; +import { sha256 } from "@noble/hashes/sha256"; +import { bytesToHex } from "@noble/hashes/utils"; type LoginSearchParams = { next?: string; @@ -193,7 +195,12 @@ function LoginPage() { try { // Generate random UUIDs for state and nonce const state = uuidv4(); - const nonce = uuidv4(); + const rawNonce = uuidv4(); + + // SHA-256 hash the nonce before sending to Apple + // Apple requires the nonce to be hashed with SHA-256 + const hashedNonce = bytesToHex(sha256(new TextEncoder().encode(rawNonce))); + // Invoke the Apple Sign in plugin // This will show the native Apple authentication UI @@ -203,7 +210,7 @@ function LoginPage() { payload: { scope: ["email", "fullName"], state, - nonce, + nonce: hashedNonce, // Send the hashed nonce to Apple // Disable debug mode in production options: { debug: false @@ -220,7 +227,8 @@ function LoginPage() { identity_token: result.identityToken, email: result.email, given_name: result.fullName?.givenName, - family_name: result.fullName?.familyName + family_name: result.fullName?.familyName, + nonce: rawNonce // Pass the original raw nonce to backend }; // Send to backend via SDK diff --git a/frontend/src/routes/signup.tsx b/frontend/src/routes/signup.tsx index be26c4c5a..26dfb6b61 100644 --- a/frontend/src/routes/signup.tsx +++ b/frontend/src/routes/signup.tsx @@ -13,6 +13,8 @@ import { isTauri, invoke } from "@tauri-apps/api/core"; import { type } from "@tauri-apps/plugin-os"; import { v4 as uuidv4 } from "uuid"; import type { AppleCredential } from "@/types/apple-sign-in"; +import { sha256 } from "@noble/hashes/sha256"; +import { bytesToHex } from "@noble/hashes/utils"; type SignupSearchParams = { next?: string; @@ -193,7 +195,12 @@ function SignupPage() { try { // Generate random UUIDs for state and nonce const state = uuidv4(); - const nonce = uuidv4(); + const rawNonce = uuidv4(); + + // SHA-256 hash the nonce before sending to Apple + // Apple requires the nonce to be hashed with SHA-256 + const hashedNonce = bytesToHex(sha256(new TextEncoder().encode(rawNonce))); + // Invoke the Apple Sign in plugin // This will show the native Apple authentication UI @@ -203,7 +210,7 @@ function SignupPage() { payload: { scope: ["email", "fullName"], state, - nonce, + nonce: hashedNonce, // Send the hashed nonce to Apple // Disable debug mode in production options: { debug: false @@ -220,7 +227,8 @@ function SignupPage() { identity_token: result.identityToken, email: result.email, given_name: result.fullName?.givenName, - family_name: result.fullName?.familyName + family_name: result.fullName?.familyName, + nonce: rawNonce // Pass the original raw nonce to backend }; // Send to backend via SDK