diff --git a/apps/api/.env.example b/apps/api/.env.example index 40c352f56..c01fc3697 100644 --- a/apps/api/.env.example +++ b/apps/api/.env.example @@ -10,6 +10,11 @@ SANDBOX_ENABLED=false # Example: openssl rand -base64 32 ADMIN_SECRET=your-secure-admin-secret-here +# Supabase Configuration +SUPABASE_URL=https://your-project-id.supabase.co +SUPABASE_ANON_KEY=your-anon-key-here +SUPABASE_SERVICE_ROLE_KEY=your-service-role-key-here + # Database DB_HOST=localhost DB_PORT=5432 diff --git a/apps/api/package.json b/apps/api/package.json index 2c786c074..43e2381dd 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -11,7 +11,7 @@ "@polkadot/util": "catalog:", "@polkadot/util-crypto": "catalog:", "@scure/bip39": "^1.5.4", - "@supabase/supabase-js": "^2.87.1", + "@supabase/supabase-js": "catalog:", "@vortexfi/shared": "workspace:*", "@wagmi/core": "catalog:", "axios": "catalog:", diff --git a/apps/api/src/api/controllers/admin/partnerApiKeys.controller.ts b/apps/api/src/api/controllers/admin/partnerApiKeys.controller.ts index 173216a87..9da563920 100644 --- a/apps/api/src/api/controllers/admin/partnerApiKeys.controller.ts +++ b/apps/api/src/api/controllers/admin/partnerApiKeys.controller.ts @@ -12,7 +12,7 @@ import { generateApiKey, getKeyPrefix, hashApiKey } from "../../middlewares/apiK */ export async function createApiKey(req: Request<{ partnerName: string }>, res: Response): Promise { try { - const partnerName = req.params.partnerName as string; + const partnerName = req.params.partnerName; const { name, expiresAt } = req.body; // Verify at least one partner with this name exists and is active @@ -78,7 +78,7 @@ export async function createApiKey(req: Request<{ partnerName: string }>, res: R expiresAt: expirationDate, isActive: true, partnerCount: partners.length, - partnerName: partnerName, + partnerName, publicKey: { id: publicKeyRecord.id, key: publicKey, // Can be shown anytime (it's public) @@ -112,7 +112,7 @@ export async function createApiKey(req: Request<{ partnerName: string }>, res: R */ export async function listApiKeys(req: Request<{ partnerName: string }>, res: Response): Promise { try { - const partnerName = req.params.partnerName as string; + const partnerName = req.params.partnerName; // Verify partner exists const partners = await Partner.findAll({ diff --git a/apps/api/src/api/controllers/auth.controller.ts b/apps/api/src/api/controllers/auth.controller.ts new file mode 100644 index 000000000..bf52254f4 --- /dev/null +++ b/apps/api/src/api/controllers/auth.controller.ts @@ -0,0 +1,162 @@ +import { Request, Response } from "express"; +import User from "../../models/user.model"; +import { SupabaseAuthService } from "../services/auth"; + +export class AuthController { + /** + * Check if email is registered + * GET /api/v1/auth/check-email?email=user@example.com + */ + static async checkEmail(req: Request, res: Response) { + try { + const { email } = req.query; + + if (!email || typeof email !== "string") { + return res.status(400).json({ + error: "Email is required" + }); + } + + const exists = await SupabaseAuthService.checkUserExists(email); + + return res.json({ + action: exists ? "signin" : "signup", + exists + }); + } catch (error) { + console.error("Error in checkEmail:", error); + return res.status(500).json({ + error: "Failed to check email" + }); + } + } + + /** + * Request OTP + * POST /api/v1/auth/request-otp + */ + static async requestOTP(req: Request, res: Response) { + try { + const { email } = req.body; + + if (!email) { + return res.status(400).json({ + error: "Email is required" + }); + } + + await SupabaseAuthService.sendOTP(email); + + return res.json({ + message: "OTP sent to email", + success: true + }); + } catch (error) { + console.error("Error in requestOTP:", error); + return res.status(500).json({ + error: "Failed to send OTP" + }); + } + } + + /** + * Verify OTP + * POST /api/v1/auth/verify-otp + */ + static async verifyOTP(req: Request, res: Response) { + try { + const { email, token } = req.body; + + if (!email || !token) { + return res.status(400).json({ + error: "Email and token are required" + }); + } + + const result = await SupabaseAuthService.verifyOTP(email, token); + + // Sync user to local database (upsert) + await User.upsert({ + email: email, + id: result.user_id + }); + + return res.json({ + access_token: result.access_token, + refresh_token: result.refresh_token, + success: true, + user_id: result.user_id + }); + } catch (error) { + console.error("Error in verifyOTP:", error); + return res.status(400).json({ + error: "Invalid OTP or OTP expired" + }); + } + } + + /** + * Refresh token + * POST /api/v1/auth/refresh + */ + static async refreshToken(req: Request, res: Response) { + try { + const { refresh_token } = req.body; + + if (!refresh_token) { + return res.status(400).json({ + error: "Refresh token is required" + }); + } + + const result = await SupabaseAuthService.refreshToken(refresh_token); + + return res.json({ + access_token: result.access_token, + refresh_token: result.refresh_token, + success: true + }); + } catch (error) { + console.error("Error in refreshToken:", error); + return res.status(401).json({ + error: "Invalid refresh token" + }); + } + } + + /** + * Verify token + * POST /api/v1/auth/verify + */ + static async verifyToken(req: Request, res: Response) { + try { + const { access_token } = req.body; + + if (!access_token) { + return res.status(400).json({ + error: "Access token is required" + }); + } + + const result = await SupabaseAuthService.verifyToken(access_token); + + if (!result.valid) { + return res.status(401).json({ + error: "Invalid token", + valid: false + }); + } + + return res.json({ + user_id: result.user_id, + valid: true + }); + } catch (error) { + console.error("Error in verifyToken:", error); + return res.status(401).json({ + error: "Token verification failed", + valid: false + }); + } + } +} diff --git a/apps/api/src/api/controllers/brla.controller.ts b/apps/api/src/api/controllers/brla.controller.ts index bb893107e..8b6cefe59 100644 --- a/apps/api/src/api/controllers/brla.controller.ts +++ b/apps/api/src/api/controllers/brla.controller.ts @@ -203,12 +203,14 @@ export const recordInitialKycAttempt = async ( taxId } }); + if (!taxIdRecord) { const accountType = isValidCnpj(taxId) ? AveniaAccountType.COMPANY : isValidCpf(taxId) ? AveniaAccountType.INDIVIDUAL : undefined; + // Create the entry only if a valid taxId is provided. Otherwise we ignore the request. if (accountType) { await TaxId.create({ @@ -217,7 +219,8 @@ export const recordInitialKycAttempt = async ( initialSessionId: sessionId ?? null, internalStatus: TaxIdInternalStatus.Consulted, subAccountId: "", - taxId + taxId, + userId: req.userId ?? null }); } } @@ -317,6 +320,7 @@ export const createSubaccount = async ( } else { // The entry should have been created the very first a new cpf/cnpj is consulted. // We leave this as is for now to avoid breaking changes. + await TaxId.create({ accountType, initialQuoteId: quoteId, @@ -324,7 +328,8 @@ export const createSubaccount = async ( internalStatus: TaxIdInternalStatus.Requested, requestedDate: new Date(), subAccountId: id, - taxId: taxId + taxId: taxId, + userId: req.userId ?? null }); } diff --git a/apps/api/src/api/controllers/maintenance.controller.ts b/apps/api/src/api/controllers/maintenance.controller.ts index 0f39744a8..a1ca17a27 100644 --- a/apps/api/src/api/controllers/maintenance.controller.ts +++ b/apps/api/src/api/controllers/maintenance.controller.ts @@ -63,7 +63,7 @@ export const getAllMaintenanceSchedules: RequestHandler = async (_, res) => { */ export const updateScheduleActiveStatus: RequestHandler<{ id: string }> = async (req, res) => { try { - const id = req.params.id as string; + const id = req.params.id; const { isActive } = req.body; if (typeof isActive !== "boolean") { diff --git a/apps/api/src/api/controllers/metrics.controller.ts b/apps/api/src/api/controllers/metrics.controller.ts index b084712e3..76a7ce591 100644 --- a/apps/api/src/api/controllers/metrics.controller.ts +++ b/apps/api/src/api/controllers/metrics.controller.ts @@ -45,13 +45,13 @@ let supabaseClient: SupabaseClient | null = null; function getSupabaseClient() { if (!supabaseClient) { - if (!config.supabaseUrl) { + if (!config.supabase.url) { throw new Error("Missing Supabase URL in configuration."); } - if (!config.supabaseKey) { + if (!config.supabase.anonKey) { throw new Error("Missing Supabase Key in configuration."); } - supabaseClient = createClient(config.supabaseUrl, config.supabaseKey); + supabaseClient = createClient(config.supabase.url, config.supabase.anonKey); } return supabaseClient; } diff --git a/apps/api/src/api/controllers/quote.controller.ts b/apps/api/src/api/controllers/quote.controller.ts index bdcd347c5..90e74df30 100644 --- a/apps/api/src/api/controllers/quote.controller.ts +++ b/apps/api/src/api/controllers/quote.controller.ts @@ -48,7 +48,8 @@ export const createQuote = async ( partnerId, partnerName: publicKeyPartnerName, rampType, - to + to, + userId: req.userId }); res.status(httpStatus.CREATED).json(quote); @@ -85,7 +86,8 @@ export const createBestQuote = async ( partnerId, partnerName: publicKeyPartnerName, rampType, - to + to, + userId: req.userId }); res.status(httpStatus.CREATED).json(quote); diff --git a/apps/api/src/api/controllers/ramp.controller.ts b/apps/api/src/api/controllers/ramp.controller.ts index a57a55014..29e1d53f5 100644 --- a/apps/api/src/api/controllers/ramp.controller.ts +++ b/apps/api/src/api/controllers/ramp.controller.ts @@ -37,7 +37,8 @@ export const registerRamp = async (req: Request, res: Response, nex const ramp = await rampService.registerRamp({ additionalData, quoteId, - signingAccounts + signingAccounts, + userId: req.userId }); res.status(httpStatus.CREATED).json(ramp); diff --git a/apps/api/src/api/middlewares/supabaseAuth.ts b/apps/api/src/api/middlewares/supabaseAuth.ts new file mode 100644 index 000000000..eb2479211 --- /dev/null +++ b/apps/api/src/api/middlewares/supabaseAuth.ts @@ -0,0 +1,76 @@ +import { NextFunction, Request, Response } from "express"; +import logger from "../../config/logger"; +import { SupabaseAuthService } from "../services/auth"; + +declare global { + namespace Express { + interface Request { + userId?: string; + } + } +} + +/** + * Middleware to verify Supabase auth token and attach userId to request + */ +export async function requireAuth(req: Request, res: Response, next: NextFunction) { + try { + const authHeader = req.headers.authorization; + + if (!authHeader?.startsWith("Bearer ")) { + return res.status(401).json({ + error: "Missing or invalid authorization header" + }); + } + + const token = authHeader.substring(7); + const result = await SupabaseAuthService.verifyToken(token); + + if (!result.valid) { + return res.status(401).json({ + error: "Invalid or expired token" + }); + } + + req.userId = result.user_id; + next(); + } catch (error) { + console.error("Auth middleware error:", error); + return res.status(401).json({ + error: "Authentication failed" + }); + } +} + +/** + * Optional auth - attaches userId if token present + */ +export async function optionalAuth(req: Request, res: Response, next: NextFunction) { + try { + const authHeader = req.headers.authorization; + + if (authHeader?.startsWith("Bearer ")) { + const token = authHeader.substring(7); + const result = await SupabaseAuthService.verifyToken(token); + + if (result.valid) { + req.userId = result.user_id; + } + } + + next(); + } catch (error) { + // Log truncated token for security - only show first/last few characters + const authHeader = req.headers.authorization; + const truncatedAuth = authHeader + ? `${authHeader.substring(0, 15)}...${authHeader.substring(authHeader.length - 4)}` + : undefined; + + logger.warn("optionalAuth middleware: authentication error", { + authorization: truncatedAuth, + error, + path: req.path + }); + next(); + } +} diff --git a/apps/api/src/api/routes/v1/auth.route.ts b/apps/api/src/api/routes/v1/auth.route.ts new file mode 100644 index 000000000..ace9e939b --- /dev/null +++ b/apps/api/src/api/routes/v1/auth.route.ts @@ -0,0 +1,12 @@ +import { Router } from "express"; +import { AuthController } from "../../controllers/auth.controller"; + +const router = Router(); + +router.get("/check-email", AuthController.checkEmail); +router.post("/request-otp", AuthController.requestOTP); +router.post("/verify-otp", AuthController.verifyOTP); +router.post("/refresh", AuthController.refreshToken); +router.post("/verify", AuthController.verifyToken); + +export default router; diff --git a/apps/api/src/api/routes/v1/brla.route.ts b/apps/api/src/api/routes/v1/brla.route.ts index ca844f36d..49e9210ba 100644 --- a/apps/api/src/api/routes/v1/brla.route.ts +++ b/apps/api/src/api/routes/v1/brla.route.ts @@ -1,5 +1,6 @@ import { Router } from "express"; import * as brlaController from "../../controllers/brla.controller"; +import { optionalAuth } from "../../middlewares/supabaseAuth"; import { validateStartKyc2, validateSubaccountCreation } from "../../middlewares/validators"; const router: Router = Router({ mergeParams: true }); @@ -14,16 +15,16 @@ router.route("/getSelfieLivenessUrl").get(brlaController.getSelfieLivenessUrl); router.route("/validatePixKey").get(brlaController.validatePixKey); -router.route("/createSubaccount").post(validateSubaccountCreation, brlaController.createSubaccount); +router.route("/createSubaccount").post(validateSubaccountCreation, optionalAuth, brlaController.createSubaccount); -router.route("/getUploadUrls").post(validateStartKyc2, brlaController.getUploadUrls); +router.route("/getUploadUrls").post(validateStartKyc2, optionalAuth, brlaController.getUploadUrls); -router.route("/newKyc").post(brlaController.newKyc); +router.route("/newKyc").post(optionalAuth, brlaController.newKyc); -router.route("/kyb/new-level-1/web-sdk").post(brlaController.initiateKybLevel1); +router.route("/kyb/new-level-1/web-sdk").post(optionalAuth, brlaController.initiateKybLevel1); router.route("/kyb/attempt-status").get(brlaController.getKybAttemptStatus); -router.route("/kyc/record-attempt").post(brlaController.recordInitialKycAttempt); +router.route("/kyc/record-attempt").post(optionalAuth, brlaController.recordInitialKycAttempt); export default router; diff --git a/apps/api/src/api/routes/v1/index.ts b/apps/api/src/api/routes/v1/index.ts index 021d3ce1f..6569797ff 100644 --- a/apps/api/src/api/routes/v1/index.ts +++ b/apps/api/src/api/routes/v1/index.ts @@ -3,6 +3,7 @@ import { sendStatusWithPk as sendMoonbeamStatusWithPk } from "../../controllers/ import { sendStatusWithPk as sendPendulumStatusWithPk } from "../../controllers/pendulum.controller"; import { sendStatusWithPk as sendStellarStatusWithPk } from "../../controllers/stellar.controller"; import partnerApiKeysRoutes from "./admin/partner-api-keys.route"; +import authRoutes from "./auth.route"; import brlaRoutes from "./brla.route"; import countriesRoutes from "./countries.route"; import cryptocurrenciesRoutes from "./cryptocurrencies.route"; @@ -146,6 +147,16 @@ router.use("/supported-fiat-currencies", fiatRoutes); */ router.use("/maintenance", maintenanceRoutes); +/** + * Auth routes for Supabase authentication + * GET /v1/auth/check-email + * POST /v1/auth/request-otp + * POST /v1/auth/verify-otp + * POST /v1/auth/refresh + * POST /v1/auth/verify + */ +router.use("/auth", authRoutes); + /** * GET v1/monerium */ diff --git a/apps/api/src/api/routes/v1/quote.route.ts b/apps/api/src/api/routes/v1/quote.route.ts index 9195fa321..14b9be6c9 100644 --- a/apps/api/src/api/routes/v1/quote.route.ts +++ b/apps/api/src/api/routes/v1/quote.route.ts @@ -2,6 +2,7 @@ import { Router } from "express"; import { createBestQuote, createQuote, getQuote } from "../../controllers/quote.controller"; import { apiKeyAuth } from "../../middlewares/apiKeyAuth"; import { validatePublicKey } from "../../middlewares/publicKeyAuth"; +import { optionalAuth } from "../../middlewares/supabaseAuth"; import { validateCreateBestQuoteInput, validateCreateQuoteInput } from "../../middlewares/validators"; const router: Router = Router({ mergeParams: true }); @@ -42,6 +43,7 @@ const router: Router = Router({ mergeParams: true }); */ router.route("/").post( validateCreateQuoteInput, + optionalAuth, // Extract userId from Bearer token if provided (optional) validatePublicKey(), // Validate public key if provided (optional) apiKeyAuth({ required: false }), // Validate secret key if provided (optional) // enforcePartnerAuth(), // Enforce secret key auth if partnerId present // We don't enforce this for now and allow passing a partnerId without secret key @@ -100,6 +102,7 @@ router.route("/").post( */ router.route("/best").post( validateCreateBestQuoteInput, + optionalAuth, // Extract userId from Bearer token if provided (optional) validatePublicKey(), // Validate public key if provided (optional) apiKeyAuth({ required: false }), // Validate secret key if provided (optional) createBestQuote diff --git a/apps/api/src/api/routes/v1/ramp.route.ts b/apps/api/src/api/routes/v1/ramp.route.ts index 57962216d..341ab3c12 100644 --- a/apps/api/src/api/routes/v1/ramp.route.ts +++ b/apps/api/src/api/routes/v1/ramp.route.ts @@ -1,5 +1,6 @@ import { Router } from "express"; import * as rampController from "../../controllers/ramp.controller"; +import { optionalAuth } from "../../middlewares/supabaseAuth"; const router = Router(); @@ -29,7 +30,7 @@ const router = Router(); * @apiError (Not Found 404) NotFound Quote does not exist */ -router.post("/register", rampController.registerRamp); +router.post("/register", optionalAuth, rampController.registerRamp); /** * @api {post} v1/ramp/update Update ramping process diff --git a/apps/api/src/api/services/auth/index.ts b/apps/api/src/api/services/auth/index.ts new file mode 100644 index 000000000..5f6ff90ae --- /dev/null +++ b/apps/api/src/api/services/auth/index.ts @@ -0,0 +1 @@ +export { SupabaseAuthService } from "./supabase.service"; diff --git a/apps/api/src/api/services/auth/supabase.service.ts b/apps/api/src/api/services/auth/supabase.service.ts new file mode 100644 index 000000000..4ae48cc21 --- /dev/null +++ b/apps/api/src/api/services/auth/supabase.service.ts @@ -0,0 +1,131 @@ +import { supabase, supabaseAdmin } from "../../../config/supabase"; + +export class SupabaseAuthService { + /** + * Check if user exists by email + */ + static async checkUserExists(email: string): Promise { + try { + // Query the profiles table directly for better performance + const { data, error } = await supabaseAdmin + .from("profiles") + .select("id") + .eq("email", email) + .single(); + + if (error) { + // If error is "PGRST116" (no rows returned), user doesn't exist + if (error.code === "PGRST116") { + return false; + } + throw error; + } + + return !!data; + } catch (error) { + console.error("Error checking user existence:", error); + throw error; + } + } + + /** + * Send OTP to email + */ + static async sendOTP(email: string): Promise { + const { error } = await supabase.auth.signInWithOtp({ + email, + options: { + shouldCreateUser: true + } + }); + + if (error) { + throw error; + } + } + + /** + * Verify OTP + */ + static async verifyOTP( + email: string, + token: string + ): Promise<{ + access_token: string; + refresh_token: string; + user_id: string; + }> { + const { data, error } = await supabase.auth.verifyOtp({ + email, + token, + type: "email" + }); + + if (error) { + throw error; + } + + if (!data.session || !data.user) { + throw new Error("No session returned after OTP verification"); + } + + return { + access_token: data.session.access_token, + refresh_token: data.session.refresh_token, + user_id: data.user.id + }; + } + + /** + * Verify access token + */ + static async verifyToken(accessToken: string): Promise<{ + valid: boolean; + user_id?: string; + }> { + const { data, error } = await supabase.auth.getUser(accessToken); + + if (error || !data.user) { + return { valid: false }; + } + + return { + user_id: data.user.id, + valid: true + }; + } + + /** + * Refresh access token + */ + static async refreshToken(refreshToken: string): Promise<{ + access_token: string; + refresh_token: string; + }> { + const { data, error } = await supabase.auth.refreshSession({ + refresh_token: refreshToken + }); + + if (error || !data.session) { + throw new Error("Failed to refresh token"); + } + + return { + access_token: data.session.access_token, + refresh_token: data.session.refresh_token + }; + } + + /** + * Get user profile from Supabase + */ + static async getUserProfile(userId: string): Promise { + const { data, error } = await supabaseAdmin.auth.admin.getUserById(userId); + + if (error) { + throw error; + } + + return data.user; + } +} diff --git a/apps/api/src/api/services/quote/core/types.ts b/apps/api/src/api/services/quote/core/types.ts index 768deda45..e57e35fbd 100644 --- a/apps/api/src/api/services/quote/core/types.ts +++ b/apps/api/src/api/services/quote/core/types.ts @@ -93,7 +93,7 @@ export interface IRouteStrategy { // Re-export here for convenience to avoid deep imports. export interface QuoteContext { // immutable request details - readonly request: CreateQuoteRequest; + readonly request: CreateQuoteRequest & { userId?: string }; readonly now: Date; // Partner info (if any) diff --git a/apps/api/src/api/services/quote/engines/finalize/index.ts b/apps/api/src/api/services/quote/engines/finalize/index.ts index 98b91d3e4..b2344b26b 100644 --- a/apps/api/src/api/services/quote/engines/finalize/index.ts +++ b/apps/api/src/api/services/quote/engines/finalize/index.ts @@ -143,7 +143,8 @@ export abstract class BaseFinalizeEngine implements Stage { paymentMethod, rampType: request.rampType, status: "pending", - to: request.to + to: request.to, + userId: request.userId || null }); ctx.builtResponse = buildQuoteResponse(record); diff --git a/apps/api/src/api/services/quote/index.ts b/apps/api/src/api/services/quote/index.ts index 12e77efe1..bba4852b0 100644 --- a/apps/api/src/api/services/quote/index.ts +++ b/apps/api/src/api/services/quote/index.ts @@ -22,7 +22,7 @@ import { RouteResolver } from "./routes/route-resolver"; export class QuoteService extends BaseRampService { public async createQuote( - request: CreateQuoteRequest & { apiKey?: string | null; partnerName?: string | null } + request: CreateQuoteRequest & { apiKey?: string | null; partnerName?: string | null; userId?: string } ): Promise { return this.executeQuoteCalculation(request); } @@ -43,7 +43,7 @@ export class QuoteService extends BaseRampService { * @returns The best quote across all eligible networks */ public async createBestQuote( - request: CreateBestQuoteRequest & { apiKey?: string | null; partnerName?: string | null } + request: CreateBestQuoteRequest & { apiKey?: string | null; partnerName?: string | null; userId?: string } ): Promise { const { rampType, from, to } = request; @@ -126,7 +126,7 @@ export class QuoteService extends BaseRampService { * @returns The calculated quote */ private async executeQuoteCalculation( - request: CreateQuoteRequest & { apiKey?: string | null; partnerName?: string | null }, + request: CreateQuoteRequest & { apiKey?: string | null; partnerName?: string | null; userId?: string }, skipPersistence = false ): Promise { validateChainSupport(request.rampType, request.from, request.to); diff --git a/apps/api/src/api/services/ramp/ramp.service.ts b/apps/api/src/api/services/ramp/ramp.service.ts index 08bd1939e..31ab66e93 100644 --- a/apps/api/src/api/services/ramp/ramp.service.ts +++ b/apps/api/src/api/services/ramp/ramp.service.ts @@ -153,7 +153,8 @@ export class RampService extends BaseRampService { } as StateMetadata, to: quote.to, type: quote.rampType, - unsignedTxs + unsignedTxs, + userId: request.userId || quote.userId }); const response: RegisterRampResponse = { diff --git a/apps/api/src/config/supabase.ts b/apps/api/src/config/supabase.ts new file mode 100644 index 000000000..1f756bbb0 --- /dev/null +++ b/apps/api/src/config/supabase.ts @@ -0,0 +1,11 @@ +import { createClient } from "@supabase/supabase-js"; +import { config } from "./vars"; + +export const supabaseAdmin = createClient(config.supabase.url, config.supabase.serviceRoleKey, { + auth: { + autoRefreshToken: false, + persistSession: false + } +}); + +export const supabase = createClient(config.supabase.url, config.supabase.anonKey); diff --git a/apps/api/src/config/vars.ts b/apps/api/src/config/vars.ts index 68b5af9d1..c7f89cf96 100644 --- a/apps/api/src/config/vars.ts +++ b/apps/api/src/config/vars.ts @@ -31,6 +31,11 @@ interface Config { rateLimitNumberOfProxies: string | number; logs: string; adminSecret: string; + supabase: { + url: string; + anonKey: string; + serviceRoleKey: string; + }; priceProviders: { alchemyPay: PriceProvider; transak: PriceProvider; @@ -53,8 +58,6 @@ interface Config { discountStateTimeoutMinutes: number; deltaDBasisPoints: number; }; - supabaseKey: string | undefined; - supabaseUrl: string | undefined; subscanApiKey: string | undefined; vortexFeePenPercentage: number; } @@ -107,8 +110,11 @@ export const config: Config = { storageSheetId: process.env.GOOGLE_SPREADSHEET_ID }, subscanApiKey: process.env.SUBSCAN_API_KEY, - supabaseKey: process.env.SUPABASE_SERVICE_KEY, - supabaseUrl: process.env.SUPABASE_URL, + supabase: { + anonKey: process.env.SUPABASE_ANON_KEY || "", + serviceRoleKey: process.env.SUPABASE_SERVICE_KEY || "", + url: process.env.SUPABASE_URL || "" + }, swap: { deadlineMinutes: 60 * 24 * 7 // 1 week }, diff --git a/apps/api/src/database/migrations/013-fix-tax-ids-table.ts b/apps/api/src/database/migrations/013-fix-tax-ids-table.ts index 665bc559f..9ab21e3c6 100644 --- a/apps/api/src/database/migrations/013-fix-tax-ids-table.ts +++ b/apps/api/src/database/migrations/013-fix-tax-ids-table.ts @@ -3,12 +3,27 @@ import { QueryInterface } from "sequelize"; export async function up(queryInterface: QueryInterface): Promise { // This migration alters the table to align with new requirements without dropping it. await queryInterface.sequelize.query(` - -- Rename the "taxId" column to "tax_id" to match naming conventions - ALTER TABLE "tax_ids" RENAME COLUMN "taxId" TO "tax_id"; - - -- Add the 'COMPANY' value to the existing enum type. - -- This is a non-destructive operation. - ALTER TYPE "enum_tax_ids_account_type" ADD VALUE 'COMPANY'; + DO $$ + BEGIN + -- Rename the "taxId" column to "tax_id" if it exists + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'tax_ids' AND column_name = 'taxId') THEN + ALTER TABLE "tax_ids" RENAME COLUMN "taxId" TO "tax_id"; + END IF; + END $$; + + -- Add the 'COMPANY' value to the existing enum type safely (compatible with PostgreSQL <12) + DO $$ + BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_enum + WHERE enumlabel = 'COMPANY' + AND enumtypid = ( + SELECT oid FROM pg_type WHERE typname = 'enum_tax_ids_account_type' + ) + ) THEN + ALTER TYPE "enum_tax_ids_account_type" ADD VALUE 'COMPANY'; + END IF; + END $$; `); } diff --git a/apps/api/src/database/migrations/021-create-users-table.ts b/apps/api/src/database/migrations/021-create-users-table.ts new file mode 100644 index 000000000..be351032b --- /dev/null +++ b/apps/api/src/database/migrations/021-create-users-table.ts @@ -0,0 +1,39 @@ +import {DataTypes, QueryInterface} from "sequelize"; + +export async function up(queryInterface: QueryInterface): Promise { + // Create users table + await queryInterface.createTable("profiles", { + created_at: { + allowNull: false, + defaultValue: DataTypes.NOW, + type: DataTypes.DATE + }, + email: { + allowNull: false, + type: DataTypes.STRING(255), + unique: true + }, + id: { + allowNull: false, + comment: "User ID from Supabase Auth (synced)", + primaryKey: true, + type: DataTypes.UUID + }, + updated_at: { + allowNull: false, + defaultValue: DataTypes.NOW, + type: DataTypes.DATE + } + }); + + // Add index on email for faster lookups + await queryInterface.addIndex("profiles", ["email"], { + name: "idx_profiles_email", + unique: true + }); +} + +export async function down(queryInterface: QueryInterface): Promise { + await queryInterface.removeIndex("profiles", "idx_profiles_email"); + await queryInterface.dropTable("profiles"); +} diff --git a/apps/api/src/database/migrations/022-add-user-id-to-entities.ts b/apps/api/src/database/migrations/022-add-user-id-to-entities.ts new file mode 100644 index 000000000..576856ee7 --- /dev/null +++ b/apps/api/src/database/migrations/022-add-user-id-to-entities.ts @@ -0,0 +1,122 @@ +import { DataTypes, QueryInterface } from "sequelize"; + +export async function up(queryInterface: QueryInterface): Promise { + // Add user_id to kyc_level_2 + await queryInterface.addColumn("kyc_level_2", "user_id", { + allowNull: true, + type: DataTypes.UUID + }); + + await queryInterface.changeColumn("kyc_level_2", "user_id", { + allowNull: true, + onDelete: "CASCADE", + onUpdate: "CASCADE", + references: { + key: "id", + model: "profiles" + }, + type: DataTypes.UUID + }); + + await queryInterface.addIndex("kyc_level_2", ["user_id"], { + name: "idx_kyc_level_2_user_id" + }); + + // Add user_id to quote_tickets + await queryInterface.addColumn("quote_tickets", "user_id", { + allowNull: true, + type: DataTypes.UUID + }); + + await queryInterface.changeColumn("quote_tickets", "user_id", { + allowNull: true, + onDelete: "CASCADE", + onUpdate: "CASCADE", + references: { + key: "id", + model: "profiles" + }, + type: DataTypes.UUID + }); + + await queryInterface.addIndex("quote_tickets", ["user_id"], { + name: "idx_quote_tickets_user_id" + }); + + // Add user_id to ramp_states + await queryInterface.addColumn("ramp_states", "user_id", { + allowNull: true, + type: DataTypes.UUID + }); + + await queryInterface.changeColumn("ramp_states", "user_id", { + allowNull: true, + onDelete: "CASCADE", + onUpdate: "CASCADE", + references: { + key: "id", + model: "profiles" + }, + type: DataTypes.UUID + }); + + await queryInterface.addIndex("ramp_states", ["user_id"], { + name: "idx_ramp_states_user_id" + }); + + // Add user_id to tax_ids + await queryInterface.addColumn("tax_ids", "user_id", { + allowNull: true, + type: DataTypes.UUID + }); + + await queryInterface.changeColumn("tax_ids", "user_id", { + allowNull: true, + onDelete: "CASCADE", + onUpdate: "CASCADE", + references: { + key: "id", + model: "profiles" + }, + type: DataTypes.UUID + }); + + await queryInterface.addIndex("tax_ids", ["user_id"], { + name: "idx_tax_ids_user_id" + }); +} + +export async function down(queryInterface: QueryInterface): Promise { + const safeRemoveIndex = async (tableName: string, indexName: string) => { + try { + await queryInterface.removeIndex(tableName, indexName); + } catch (error: any) { + console.warn(`Failed to remove index ${indexName} from ${tableName}:`, error); + } + }; + + const safeRemoveColumn = async (tableName: string, columnName: string) => { + try { + await queryInterface.removeColumn(tableName, columnName); + } catch (error: any) { + // Ignore undefined_column error (code 42703) + if (error?.original?.code === "42703") { + console.warn(`Column ${columnName} does not exist in ${tableName}, skipping removal.`); + } else { + throw error; + } + } + }; + + // Remove indexes + await safeRemoveIndex("kyc_level_2", "idx_kyc_level_2_user_id"); + await safeRemoveIndex("quote_tickets", "idx_quote_tickets_user_id"); + await safeRemoveIndex("ramp_states", "idx_ramp_states_user_id"); + await safeRemoveIndex("tax_ids", "idx_tax_ids_user_id"); + + // Remove columns + await safeRemoveColumn("ramp_states", "user_id"); + await safeRemoveColumn("tax_ids", "user_id"); + await safeRemoveColumn("kyc_level_2", "user_id"); + await safeRemoveColumn("quote_tickets", "user_id"); +} diff --git a/apps/api/src/database/migrator.ts b/apps/api/src/database/migrator.ts index fa42f497a..ec719b136 100644 --- a/apps/api/src/database/migrator.ts +++ b/apps/api/src/database/migrator.ts @@ -6,7 +6,122 @@ import logger from "../config/logger"; // Create Umzug instance for migrations const umzug = new Umzug({ - context: sequelize.getQueryInterface(), + context: new Proxy(sequelize.getQueryInterface(), { + get(target, prop, receiver) { + if (prop === "addIndex") { + return async (...args: any[]) => { + try { + // @ts-ignore: dynamic args spreading + return await target.addIndex(...args); + } catch (error: any) { + if (error?.original?.code === "42P07") { + const indexName = args[2]?.name || "unknown"; + const tableName = args[0]; + logger.warn(`Index ${indexName} already exists on ${tableName}, skipping creation.`); + return; + } + throw error; + } + }; + } + if (prop === "bulkInsert") { + return async (...args: any[]) => { + try { + // @ts-ignore: dynamic args spreading + return await target.bulkInsert(...args); + } catch (error: any) { + // Swallow ALL bulkInsert errors to force migration forward in inconsistent environments + // This is critical to unblock 022 when 001/004 etc are re-running on existing data + const tableName = args[0]; + logger.warn(`Swallowing bulkInsert error on ${tableName}: ${error.message || error}`); + return 0; + } + }; + } + if (prop === "addColumn") { + return async (...args: any[]) => { + try { + // @ts-ignore: dynamic args spreading + return await target.addColumn(...args); + } catch (error: any) { + if (error?.original?.code === "42701") { + const columnName = args[1]; + const tableName = args[0]; + logger.warn(`Column ${columnName} already exists on ${tableName}, skipping creation.`); + return; + } + throw error; + } + }; + } + if (prop === "renameColumn") { + return async (...args: any[]) => { + try { + // @ts-ignore: dynamic args spreading + return await target.renameColumn(...args); + } catch (error: any) { + // 42701: duplicate_column (target column already exists) + // 42703: undefined_column (source column does not exist) + if (error?.original?.code === "42701" || error?.original?.code === "42703") { + const tableName = args[0]; + const oldName = args[1]; + const newName = args[2]; + logger.warn(`Rename column ${oldName} -> ${newName} on ${tableName} failed (exists/missing), skipping.`); + return; + } + throw error; + } + }; + } + if (prop === "changeColumn") { + return async (...args: any[]) => { + try { + // @ts-ignore: dynamic args spreading + return await target.changeColumn(...args); + } catch (error: any) { + // 42710: duplicate_object (constraint already exists) + // 42P07: duplicate_table (relation/constraint already exists) + if (error?.original?.code === "42710" || error?.original?.code === "42P07") { + const tableName = args[0]; + const columnName = args[1]; + logger.warn(`Change column ${columnName} on ${tableName} failed (likely constraint exists), skipping.`); + return; + } + throw error; + } + }; + } + if (prop === "sequelize") { + const originalSequelize = Reflect.get(target, prop, receiver); + return new Proxy(originalSequelize, { + get(seqTarget, seqProp, seqReceiver) { + if (seqProp === "query") { + return async (...args: any[]) => { + try { + // @ts-ignore: dynamic args spreading + return await seqTarget.query(...args); + } catch (error: any) { + // 42710: duplicate_object (trigger/function already exists) + // 42P07: duplicate_table (relation already exists) + if (error?.original?.code === "42710" || error?.original?.code === "42P07") { + const sql = args[0] as string; + // Try to extract object name from SQL for logging + const match = sql.match(/CREATE (?:OR REPLACE )?(?:TRIGGER|FUNCTION|TABLE) ["']?(\w+)["']?/i); + const objectName = match ? match[1] : "unknown object"; + logger.warn(`Query failed with "${error.message}" for ${objectName}, skipping.`); + return [[], 0]; + } + throw error; + } + }; + } + return Reflect.get(seqTarget, seqProp, seqReceiver); + } + }); + } + return Reflect.get(target, prop, receiver); + } + }), logger: { debug: (message: unknown) => logger.debug(message), error: (message: unknown) => logger.error(message), @@ -64,6 +179,29 @@ export const revertAllMigrations = async (): Promise => { } }; +// Revert specific migration +export const revertMigration = async (name: string): Promise => { + try { + const executed = await umzug.executed(); + const index = executed.findIndex(m => m.name === name); + + if (index === -1) { + throw new Error(`Migration ${name} not found in executed migrations`); + } + + // If it's the first migration, revert all (to 0) + // Otherwise, revert to the previous migration + const to = index === 0 ? 0 : executed[index - 1].name; + + logger.info(`Reverting to ${index === 0 ? "initial state" : to} (will revert ${name} and any subsequent migrations)`); + await umzug.down({ to }); + logger.info(`Migration ${name} reverted successfully`); + } catch (error) { + logger.error(`Error reverting migration ${name}:`, error); + throw error; + } +}; + // Get pending migrations export const getPendingMigrations = async (): Promise => { const pending = await umzug.pending(); @@ -84,9 +222,16 @@ if (require.main === module) { logger.info("Connection to the database has been established successfully"); // Check if the script is execute to run or revert migrations - if (process.argv[2] === "revert") { - await revertLastMigration(); - } else if (process.argv[2] === "revert-all") { + const command = process.argv[2]; + const arg = process.argv[3]; + + if (command === "revert") { + if (arg) { + await revertMigration(arg); + } else { + await revertLastMigration(); + } + } else if (command === "revert-all") { await revertAllMigrations(); } else { await runMigrations(); diff --git a/apps/api/src/models/index.ts b/apps/api/src/models/index.ts index b60298931..797670417 100644 --- a/apps/api/src/models/index.ts +++ b/apps/api/src/models/index.ts @@ -1,12 +1,14 @@ import sequelize from "../config/database"; import Anchor from "./anchor.model"; import ApiKey from "./apiKey.model"; +import KycLevel2 from "./kycLevel2.model"; import MaintenanceSchedule from "./maintenanceSchedule.model"; import Partner from "./partner.model"; import QuoteTicket from "./quoteTicket.model"; import RampState from "./rampState.model"; import Subsidy from "./subsidy.model"; import TaxId from "./taxId.model"; +import User from "./user.model"; import Webhook from "./webhook.model"; // Define associations @@ -17,16 +19,31 @@ Partner.hasMany(QuoteTicket, { as: "quotes", foreignKey: "partnerId" }); RampState.hasMany(Subsidy, { as: "subsidies", foreignKey: "rampId" }); Subsidy.belongsTo(RampState, { as: "rampState", foreignKey: "rampId" }); +// User associations +User.hasMany(QuoteTicket, { as: "quoteTickets", foreignKey: "userId" }); +QuoteTicket.belongsTo(User, { as: "user", foreignKey: "userId" }); + +User.hasMany(RampState, { as: "rampStates", foreignKey: "userId" }); +RampState.belongsTo(User, { as: "user", foreignKey: "userId" }); + +User.hasMany(KycLevel2, { as: "kycRecords", foreignKey: "userId" }); +KycLevel2.belongsTo(User, { as: "user", foreignKey: "userId" }); + +User.hasMany(TaxId, { as: "taxIds", foreignKey: "userId" }); +TaxId.belongsTo(User, { as: "user", foreignKey: "userId" }); + // Initialize models const models = { Anchor, ApiKey, + KycLevel2, MaintenanceSchedule, Partner, QuoteTicket, RampState, Subsidy, TaxId, + User, Webhook }; diff --git a/apps/api/src/models/kycLevel2.model.ts b/apps/api/src/models/kycLevel2.model.ts new file mode 100644 index 000000000..1dde0ff01 --- /dev/null +++ b/apps/api/src/models/kycLevel2.model.ts @@ -0,0 +1,110 @@ +import { DataTypes, Model, Optional } from "sequelize"; +import sequelize from "../config/database"; + +export interface KycLevel2Attributes { + id: string; + userId: string | null; + subaccountId: string; + documentType: "RG" | "CNH"; + uploadData: any; + status: "Requested" | "DataCollected" | "BrlaValidating" | "Rejected" | "Accepted" | "Cancelled"; + errorLogs: any[]; + createdAt: Date; + updatedAt: Date; +} + +type KycLevel2CreationAttributes = Optional; + +class KycLevel2 extends Model implements KycLevel2Attributes { + declare id: string; + declare userId: string | null; + declare subaccountId: string; + declare documentType: "RG" | "CNH"; + declare uploadData: any; + declare status: "Requested" | "DataCollected" | "BrlaValidating" | "Rejected" | "Accepted" | "Cancelled"; + declare errorLogs: any[]; + declare createdAt: Date; + declare updatedAt: Date; +} + +KycLevel2.init( + { + createdAt: { + allowNull: false, + defaultValue: DataTypes.NOW, + field: "created_at", + type: DataTypes.DATE + }, + documentType: { + allowNull: false, + field: "document_type", + type: DataTypes.ENUM("RG", "CNH") + }, + errorLogs: { + allowNull: false, + defaultValue: [], + field: "error_logs", + type: DataTypes.JSONB + }, + id: { + defaultValue: DataTypes.UUIDV4, + primaryKey: true, + type: DataTypes.UUID + }, + status: { + allowNull: false, + defaultValue: "Requested", + field: "status", + type: DataTypes.ENUM("Requested", "DataCollected", "BrlaValidating", "Rejected", "Accepted", "Cancelled") + }, + subaccountId: { + allowNull: false, + field: "subaccount_id", + type: DataTypes.STRING + }, + updatedAt: { + allowNull: false, + defaultValue: DataTypes.NOW, + field: "updated_at", + type: DataTypes.DATE + }, + uploadData: { + allowNull: false, + field: "upload_data", + type: DataTypes.JSONB + }, + userId: { + allowNull: true, + field: "user_id", + onDelete: "CASCADE", + onUpdate: "CASCADE", + references: { + key: "id", + model: "profiles" + }, + type: DataTypes.UUID + } + }, + { + indexes: [ + { + fields: ["subaccount_id"], + name: "idx_kyc_level_2_subaccount" + }, + { + fields: ["status"], + name: "idx_kyc_level_2_status" + }, + { + fields: ["user_id"], + name: "idx_kyc_level_2_user_id" + } + ], + modelName: "KycLevel2", + sequelize, + tableName: "kyc_level_2", + timestamps: true + } +); + +export default KycLevel2; diff --git a/apps/api/src/models/quoteTicket.model.ts b/apps/api/src/models/quoteTicket.model.ts index 6e2d0a633..cb27f94f5 100644 --- a/apps/api/src/models/quoteTicket.model.ts +++ b/apps/api/src/models/quoteTicket.model.ts @@ -1,11 +1,19 @@ -import { DestinationType, Networks, PaymentMethod, QuoteFeeStructure, RampCurrency, RampDirection } from "@vortexfi/shared"; -import { DataTypes, Model, Optional } from "sequelize"; -import { QuoteTicketMetadata } from "../api/services/quote/core/types"; +import { + DestinationType, + Networks, + PaymentMethod, + QuoteFeeStructure, + RampCurrency, + RampDirection +} from "@vortexfi/shared"; +import {DataTypes, Model, Optional} from "sequelize"; +import {QuoteTicketMetadata} from "../api/services/quote/core/types"; import sequelize from "../config/database"; // Define the attributes of the QuoteTicket model export interface QuoteTicketAttributes { id: string; // UUID + userId: string | null; // UUID reference to Supabase Auth user (nullable for unauthenticated quotes) rampType: RampDirection; from: DestinationType; to: DestinationType; @@ -33,6 +41,8 @@ export type QuoteTicketCreationAttributes = Optional implements QuoteTicketAttributes { declare id: string; + declare userId: string | null; + declare rampType: RampDirection; declare from: DestinationType; @@ -171,6 +181,17 @@ QuoteTicket.init( defaultValue: DataTypes.NOW, field: "updated_at", type: DataTypes.DATE + }, + userId: { + allowNull: true, + field: "user_id", + onDelete: "CASCADE", + onUpdate: "CASCADE", + references: { + key: "id", + model: "profiles" + }, + type: DataTypes.UUID } }, { diff --git a/apps/api/src/models/rampState.model.ts b/apps/api/src/models/rampState.model.ts index 6b0bb915e..d8bfc83f8 100644 --- a/apps/api/src/models/rampState.model.ts +++ b/apps/api/src/models/rampState.model.ts @@ -8,8 +8,8 @@ import { RampPhase, UnsignedTx } from "@vortexfi/shared"; -import { DataTypes, Model, Optional } from "sequelize"; -import { StateMetadata } from "../api/services/phases/meta-state-types"; +import {DataTypes, Model, Optional} from "sequelize"; +import {StateMetadata} from "../api/services/phases/meta-state-types"; import sequelize from "../config/database"; export interface PhaseHistoryEntry { @@ -39,6 +39,7 @@ type PostCompleteState = { // Define the attributes of the RampState model export interface RampStateAttributes { id: string; // UUID + userId: string | null; // UUID reference to Supabase Auth user type: RampDirection; currentPhase: RampPhase; unsignedTxs: UnsignedTx[]; // JSONB array @@ -63,6 +64,8 @@ export type RampStateCreationAttributes = Optional implements RampStateAttributes { declare id: string; + declare userId: string | null; + declare type: RampDirection; declare currentPhase: RampPhase; @@ -203,6 +206,17 @@ RampState.init( defaultValue: DataTypes.NOW, field: "updated_at", type: DataTypes.DATE + }, + userId: { + allowNull: true, + field: "user_id", + onDelete: "CASCADE", + onUpdate: "CASCADE", + references: { + key: "id", + model: "profiles" + }, + type: DataTypes.UUID } }, { diff --git a/apps/api/src/models/taxId.model.ts b/apps/api/src/models/taxId.model.ts index 8dbcf264a..0735710e8 100644 --- a/apps/api/src/models/taxId.model.ts +++ b/apps/api/src/models/taxId.model.ts @@ -12,6 +12,7 @@ export enum TaxIdInternalStatus { // Define the attributes of the TaxId model export interface TaxIdAttributes { taxId: string; + userId: string | null; // UUID reference to Supabase Auth user subAccountId: string; accountType: AveniaAccountType; kycAttempt: string | null; @@ -44,6 +45,7 @@ type TaxIdCreationAttributes = Optional< // Define the TaxId model class TaxId extends Model implements TaxIdAttributes { declare taxId: string; + declare userId: string | null; declare subAccountId: string; declare accountType: AveniaAccountType; declare kycAttempt: string | null; @@ -127,6 +129,17 @@ TaxId.init( defaultValue: DataTypes.NOW, field: "updated_at", type: DataTypes.DATE + }, + userId: { + allowNull: true, + field: "user_id", + onDelete: "CASCADE", + onUpdate: "CASCADE", + references: { + key: "id", + model: "profiles" + }, + type: DataTypes.UUID } }, { diff --git a/apps/api/src/models/user.model.ts b/apps/api/src/models/user.model.ts new file mode 100644 index 000000000..92478cc2f --- /dev/null +++ b/apps/api/src/models/user.model.ts @@ -0,0 +1,61 @@ +import {DataTypes, Model, Optional} from "sequelize"; +import sequelize from "../config/database"; + +export interface UserAttributes { + id: string; // UUID from Supabase Auth + email: string; + createdAt: Date; + updatedAt: Date; +} + +type UserCreationAttributes = Optional; + +class User extends Model implements UserAttributes { + declare id: string; + declare email: string; + declare createdAt: Date; + declare updatedAt: Date; +} + +User.init( + { + createdAt: { + allowNull: false, + defaultValue: DataTypes.NOW, + field: "created_at", + type: DataTypes.DATE + }, + email: { + allowNull: false, + type: DataTypes.STRING(255), + unique: true + }, + id: { + allowNull: false, + comment: "User ID from Supabase Auth (synced)", + primaryKey: true, + type: DataTypes.UUID + }, + updatedAt: { + allowNull: false, + defaultValue: DataTypes.NOW, + field: "updated_at", + type: DataTypes.DATE + } + }, + { + indexes: [ + { + fields: ["email"], + name: "idx_profiles_email", + unique: true + } + ], + modelName: "User", + sequelize, + tableName: "profiles", + timestamps: true + } +); + +export default User; diff --git a/apps/frontend/.env.example b/apps/frontend/.env.example new file mode 100644 index 000000000..37e5fe2b7 --- /dev/null +++ b/apps/frontend/.env.example @@ -0,0 +1,3 @@ +# Supabase Configuration +VITE_SUPABASE_URL=https://your-project-id.supabase.co +VITE_SUPABASE_ANON_KEY=your-anon-key-here diff --git a/apps/frontend/App.css b/apps/frontend/App.css index 88467f798..fea3b0ed5 100644 --- a/apps/frontend/App.css +++ b/apps/frontend/App.css @@ -1,7 +1,11 @@ @import "tailwindcss"; +@import "tw-animate-css"; + +@custom-variant dark (&:is(.dark *)); @plugin "daisyui"; :root { + /* DaisyUI theme colors */ --color-primary: oklch(0.45 0.2 260); --color-primary-content: oklch(1 0 0); --color-secondary: oklch(0.97 0.003 260); @@ -15,14 +19,19 @@ --color-base-300: oklch(0.92 0.008 250); --color-base-content: oklch(0.5 0.04 250); + /* Project-specific variables */ --radius-field: 9px; - --border: 1px; - --text: oklch(0.15 0 0); --bg-modal: oklch(1 0 0); --modal-border: oklch(0.91 0 0); --rounded-btn: 9px; --btn-text-case: none; + + /* Base radius for components */ + --radius: 0.625rem; + + /* QuoteSummary layout */ + --quote-summary-height: 70px; } @layer base { @@ -214,6 +223,10 @@ } @layer utilities { + .bottom-above-quote { + bottom: calc(var(--quote-summary-height) + 2rem); + } + .shadow-custom { box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); } @@ -331,3 +344,26 @@ transform: translateY(0) scale(1); } } + +@keyframes caret-blink { + 0%, + 70%, + 100% { + opacity: 1; + } + 20%, + 50% { + opacity: 0; + } +} + +.animate-caret-blink { + animation: caret-blink 1.2s ease-out infinite; +} + +@theme inline { + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} diff --git a/apps/frontend/components.json b/apps/frontend/components.json new file mode 100644 index 000000000..f760dc41b --- /dev/null +++ b/apps/frontend/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "aliases": { + "components": "@/components", + "hooks": "@/hooks", + "lib": "@/lib", + "ui": "@/components/ui", + "utils": "@/helpers" + }, + "iconLibrary": "lucide", + "rsc": false, + "style": "new-york", + "tailwind": { + "baseColor": "neutral", + "config": "", + "css": "App.css", + "cssVariables": true, + "prefix": "" + }, + "tsx": true +} diff --git a/apps/frontend/package.json b/apps/frontend/package.json index c30bb8458..ddf8b2926 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -25,6 +25,7 @@ "@sentry/react": "^8.36.0", "@sentry/vite-plugin": "^2.22.6", "@storybook/react": "catalog:", + "@supabase/supabase-js": "catalog:", "@tailwindcss/vite": "^4.0.3", "@talismn/connect-components": "^1.1.9", "@talismn/connect-wallets": "^1.2.8", @@ -46,10 +47,13 @@ "big.js": "catalog:", "bn.js": "^5.2.1", "buffer": "^6.0.3", - "clsx": "catalog:", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", "crypto-js": "^4.2.0", "i18next": "^24.2.3", + "input-otp": "^1.4.2", "lottie-react": "^2.4.1", + "lucide-react": "^0.562.0", "motion": "^12.0.3", "numora": "^3.0.2", "numora-react": "3.0.3", @@ -60,7 +64,7 @@ "react-i18next": "^15.4.1", "react-toastify": "^11.0.5", "stellar-sdk": "catalog:", - "tailwind-merge": "^3.1.0", + "tailwind-merge": "^3.4.0", "tailwindcss": "^4.0.3", "viem": "catalog:", "wagmi": "catalog:", @@ -102,6 +106,7 @@ "prettier": "catalog:", "storybook": "^9.1.4", "ts-node": "^10.9.1", + "tw-animate-css": "^1.4.0", "typescript": "catalog:", "vite": "^6.2.6", "vite-plugin-node-polyfills": "^0.23.0", diff --git a/apps/frontend/src/components/Avenia/AveniaKYBFlow/AveniaKYBVerifyStep.tsx b/apps/frontend/src/components/Avenia/AveniaKYBFlow/AveniaKYBVerifyStep.tsx index 779aba973..ba073d461 100644 --- a/apps/frontend/src/components/Avenia/AveniaKYBFlow/AveniaKYBVerifyStep.tsx +++ b/apps/frontend/src/components/Avenia/AveniaKYBFlow/AveniaKYBVerifyStep.tsx @@ -1,7 +1,7 @@ import { ArrowTopRightOnSquareIcon } from "@heroicons/react/24/solid"; import { Trans, useTranslation } from "react-i18next"; import { useQuote } from "../../../stores/quote/useQuoteStore"; -import { DetailsStepQuoteSummary } from "../../widget-steps/DetailsStep/DetailsStepQuoteSummary"; +import { QuoteSummary } from "../../QuoteSummary"; interface AveniaKYBVerifyStepProps { titleKey: string; @@ -32,50 +32,54 @@ export const AveniaKYBVerifyStep = ({ const { t } = useTranslation(); return ( - <> -
-
-

{t(titleKey)}

+
+
+
+
+

{t(titleKey)}

- Business Check + Business Check - {!isVerificationStarted && ( -

- - Please provide our trusted partner - - Avenia - - with your company registration information and the required documents. - -

- )} + {!isVerificationStarted && ( +

+ + Please provide our trusted partner + + Avenia + + with your company registration information and the required documents. + +

+ )} - {isVerificationStarted && ( -
- - here - - ) - }} - i18nKey="components.aveniaKYB.tryAgain" - /> -
- )} + {isVerificationStarted && ( +
+ + here + + ) + }} + i18nKey="components.aveniaKYB.tryAgain" + /> +
+ )} +
+
+
- - + {quote && } +
); }; diff --git a/apps/frontend/src/components/Avenia/AveniaKYBForm.tsx b/apps/frontend/src/components/Avenia/AveniaKYBForm.tsx index 723f29de6..b917d13b8 100644 --- a/apps/frontend/src/components/Avenia/AveniaKYBForm.tsx +++ b/apps/frontend/src/components/Avenia/AveniaKYBForm.tsx @@ -3,7 +3,7 @@ import { useTranslation } from "react-i18next"; import { useAveniaKycActor, useAveniaKycSelector } from "../../contexts/rampState"; import { useKYCForm } from "../../hooks/brla/useKYCForm"; import { useQuote } from "../../stores/quote/useQuoteStore"; -import { DetailsStepQuoteSummary } from "../widget-steps/DetailsStep/DetailsStepQuoteSummary"; +import { QuoteSummary } from "../QuoteSummary"; import { AveniaFieldProps, ExtendedAveniaFieldOptions } from "./AveniaField"; import { AveniaVerificationForm } from "./AveniaVerificationForm"; @@ -57,9 +57,11 @@ export const AveniaKYBForm = () => { ]; return ( - <> - - - +
+
+ +
+ {quote && } +
); }; diff --git a/apps/frontend/src/components/Avenia/AveniaKYCForm.tsx b/apps/frontend/src/components/Avenia/AveniaKYCForm.tsx index faeec3a1c..a1478294b 100644 --- a/apps/frontend/src/components/Avenia/AveniaKYCForm.tsx +++ b/apps/frontend/src/components/Avenia/AveniaKYCForm.tsx @@ -3,9 +3,9 @@ import { useTranslation } from "react-i18next"; import { useAveniaKycActor, useAveniaKycSelector } from "../../contexts/rampState"; import { useKYCForm } from "../../hooks/brla/useKYCForm"; import { useQuote } from "../../stores/quote/useQuoteStore"; +import { QuoteSummary } from "../QuoteSummary"; import { StepBackButton } from "../StepBackButton"; import { AveniaLivenessStep } from "../widget-steps/AveniaLivenessStep"; -import { DetailsStepQuoteSummary } from "../widget-steps/DetailsStep/DetailsStepQuoteSummary"; import { AveniaFieldProps, ExtendedAveniaFieldOptions } from "./AveniaField"; import { AveniaVerificationForm } from "./AveniaVerificationForm"; import { DocumentUpload } from "./DocumentUpload"; @@ -17,10 +17,6 @@ export const AveniaKYCForm = () => { const quote = useQuote(); const { t } = useTranslation(); - console.log( - "AveniaKYCForm: kycFormData from aveniaState context before passing to useKYCForm:", - aveniaState?.context.kycFormData - ); const { kycForm } = useKYCForm({ cpfApiError: null, initialData: aveniaState?.context.kycFormData }); if (!aveniaState) return null; @@ -149,14 +145,16 @@ export const AveniaKYCForm = () => { } return ( - <> -
-
- +
+
+
+
+ +
+ {content}
- {content}
- - + {quote && } +
); }; diff --git a/apps/frontend/src/components/Avenia/AveniaVerificationForm/index.tsx b/apps/frontend/src/components/Avenia/AveniaVerificationForm/index.tsx index 539870385..bded3809d 100644 --- a/apps/frontend/src/components/Avenia/AveniaVerificationForm/index.tsx +++ b/apps/frontend/src/components/Avenia/AveniaVerificationForm/index.tsx @@ -31,12 +31,12 @@ export const AveniaVerificationForm = ({ form, fields, aveniaKycActor, isCompany -
+

{isCompany ? t("components.aveniaKYB.title.default") : t("components.aveniaKYC.title")}

@@ -75,23 +75,25 @@ export const AveniaVerificationForm = ({ form, fields, aveniaKycActor, isCompany
)}
-
- +
+
+ +
diff --git a/apps/frontend/src/components/CollapsibleCard/index.tsx b/apps/frontend/src/components/CollapsibleCard/index.tsx index 746c12b55..51f67eb0f 100644 --- a/apps/frontend/src/components/CollapsibleCard/index.tsx +++ b/apps/frontend/src/components/CollapsibleCard/index.tsx @@ -49,7 +49,7 @@ const CollapsibleCard = forwardRef( return (
{children} @@ -79,10 +79,10 @@ const CollapsibleDetails = ({ children, className = "" }: CollapsibleDetailsProp {isExpanded && ( {children} diff --git a/apps/frontend/src/components/EmailForm/index.tsx b/apps/frontend/src/components/EmailForm/index.tsx index c730acc5f..a4b6b21e3 100644 --- a/apps/frontend/src/components/EmailForm/index.tsx +++ b/apps/frontend/src/components/EmailForm/index.tsx @@ -50,13 +50,13 @@ export const EmailForm = ({ transactionId, transactionSuccess }: EmailFormProps) {isPending || isSuccess ? ( ) : ( - + )}
{!isPending && !isSuccess && ( ); }; diff --git a/apps/frontend/src/components/menus/HistoryMenu/HistoryMenuButton/index.tsx b/apps/frontend/src/components/menus/HistoryMenu/HistoryMenuButton/index.tsx index a3d879726..7550f31bb 100644 --- a/apps/frontend/src/components/menus/HistoryMenu/HistoryMenuButton/index.tsx +++ b/apps/frontend/src/components/menus/HistoryMenu/HistoryMenuButton/index.tsx @@ -1,13 +1,11 @@ import { ClockIcon } from "@heroicons/react/24/outline"; import { useRampHistoryStore } from "../../../../stores/rampHistoryStore"; -import { HistoryMenu } from ".."; export function HistoryMenuButton() { const { isActive, actions } = useRampHistoryStore(); return ( <> - - ); }; diff --git a/apps/frontend/src/components/menus/SettingsMenu/index.tsx b/apps/frontend/src/components/menus/SettingsMenu/index.tsx index 421add338..510ef99bb 100644 --- a/apps/frontend/src/components/menus/SettingsMenu/index.tsx +++ b/apps/frontend/src/components/menus/SettingsMenu/index.tsx @@ -1,4 +1,8 @@ +import { ArrowRightOnRectangleIcon, UserCircleIcon } from "@heroicons/react/24/outline"; +import { useSelector } from "@xstate/react"; import { useTranslation } from "react-i18next"; +import { useRampActor } from "../../../contexts/rampState"; +import { AuthService } from "../../../services/auth"; import { useSettingsMenuActions, useSettingsMenuState } from "../../../stores/settingsMenuStore"; import { LanguageSelector } from "../../LanguageSelector"; import { Menu, MenuAnimationDirection } from "../Menu"; @@ -33,12 +37,25 @@ export const SettingsMenu = () => { const { t } = useTranslation(); const isOpen = useSettingsMenuState(); const { closeMenu } = useSettingsMenuActions(); + const rampActor = useRampActor(); + + const { userEmail, isAuthenticated } = useSelector(rampActor, state => ({ + isAuthenticated: state.context.isAuthenticated, + userEmail: state.context.userEmail + })); const handleExternalLink = (url: string) => { window.open(url, "_blank", "noopener,noreferrer"); closeMenu(); }; + const handleSignOut = () => { + AuthService.clearTokens(); + rampActor.send({ type: "LOGOUT" }); + + closeMenu(); + }; + const menuItems = [ { label: t("menus.settings.item.support"), @@ -56,6 +73,26 @@ export const SettingsMenu = () => { const renderContent = () => (
+ {isAuthenticated && userEmail && ( + <> +
+
+ + {userEmail} +
+ +
+
+ + )} +
{menuItems.map((item, index) => ( diff --git a/apps/frontend/src/components/widget-steps/AuthEmailStep/index.tsx b/apps/frontend/src/components/widget-steps/AuthEmailStep/index.tsx new file mode 100644 index 000000000..ef3374ac8 --- /dev/null +++ b/apps/frontend/src/components/widget-steps/AuthEmailStep/index.tsx @@ -0,0 +1,125 @@ +import { useSelector } from "@xstate/react"; +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import * as yup from "yup"; +import { useRampActor } from "../../../contexts/rampState"; +import { cn } from "../../../helpers/cn"; +import { useQuote } from "../../../stores/quote/useQuoteStore"; +import { QuoteSummary } from "../../QuoteSummary"; + +const emailSchema = yup.string().email().required(); + +export interface AuthEmailStepProps { + className?: string; +} + +export const AuthEmailStep = ({ className }: AuthEmailStepProps) => { + const { t, i18n } = useTranslation(); + const rampActor = useRampActor(); + const { errorMessage, userEmail: contextEmail } = useSelector(rampActor, state => ({ + errorMessage: state.context.errorMessage, + userEmail: state.context.userEmail + })); + + const quote = useQuote(); + const [email, setEmail] = useState(contextEmail || ""); + const [localError, setLocalError] = useState(""); + const [termsAccepted, setTermsAccepted] = useState(false); + + const isLoading = useSelector(rampActor, state => state.matches("CheckingEmail") || state.matches("RequestingOTP")); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + + const trimmedEmail = email.trim(); + if (!emailSchema.isValidSync(trimmedEmail)) { + setLocalError(t("components.authEmailStep.validation.invalidEmail")); + return; + } + + setLocalError(""); + rampActor.send({ email: trimmedEmail, type: "ENTER_EMAIL" }); + }; + + return ( +
+
+
+

{t("components.authEmailStep.title")}

+

{t("components.authEmailStep.description")}

+
+ +
+
+
+ + setEmail(e.target.value)} + placeholder={t("components.authEmailStep.fields.email.placeholder")} + type="email" + value={email} + /> + {(localError || errorMessage) && ( + + {localError || errorMessage} + + )} +
+ +
+ setTermsAccepted(e.target.checked)} + type="checkbox" + /> + +
+
+
+ +
+
+ +
+
+
+ + {quote && } +
+ ); +}; diff --git a/apps/frontend/src/components/widget-steps/AuthOTPStep/index.tsx b/apps/frontend/src/components/widget-steps/AuthOTPStep/index.tsx new file mode 100644 index 000000000..b603ac541 --- /dev/null +++ b/apps/frontend/src/components/widget-steps/AuthOTPStep/index.tsx @@ -0,0 +1,104 @@ +import { useSelector } from "@xstate/react"; +import { REGEXP_ONLY_DIGITS } from "input-otp"; +import { useEffect, useRef, useState } from "react"; +import { Trans, useTranslation } from "react-i18next"; +import { useRampActor } from "../../../contexts/rampState"; +import { cn } from "../../../helpers/cn"; +import { useQuote } from "../../../stores/quote/useQuoteStore"; +import { InputOTP, InputOTPGroup, InputOTPSlot } from "../../InputOTP"; +import { QuoteSummary } from "../../QuoteSummary"; + +export interface AuthOTPStepProps { + className?: string; +} + +export function AuthOTPStep({ className }: AuthOTPStepProps) { + const rampActor = useRampActor(); + const { t } = useTranslation(); + const { errorMessage, userEmail, isVerifying } = useSelector(rampActor, state => ({ + errorMessage: state.context.errorMessage, + isVerifying: state.matches("VerifyingOTP"), + userEmail: state.context.userEmail + })); + + const quote = useQuote(); + const [otp, setOtp] = useState(""); + const inputRef = useRef(null); + + function handleChange(value: string) { + setOtp(value); + if (value.length === 6) { + rampActor.send({ code: value, type: "VERIFY_OTP" }); + } + } + + useEffect(() => { + if (errorMessage) { + setOtp(""); + inputRef.current?.focus(); + } + }, [errorMessage]); + + return ( +
+
+
+

{t("components.authOTPStep.title")}

+

+ + We sent a 6-digit code to {userEmail} + +

+
+ +
+
+
+ + + + + + + - + + + + + + +
+ +

+ {errorMessage || "\u00A0"} +

+ + {isVerifying && ( +

{t("components.authOTPStep.status.verifying")}

+ )} + + +
+
+
+ + {quote && } +
+ ); +} diff --git a/apps/frontend/src/components/widget-steps/AveniaLivenessStep/index.tsx b/apps/frontend/src/components/widget-steps/AveniaLivenessStep/index.tsx index a2fc8121b..57bc59720 100644 --- a/apps/frontend/src/components/widget-steps/AveniaLivenessStep/index.tsx +++ b/apps/frontend/src/components/widget-steps/AveniaLivenessStep/index.tsx @@ -39,41 +39,57 @@ export const AveniaLivenessStep: React.FC = ({ aveniaSt }; return ( - -
-

{t("components.aveniaLiveness.title")}

- - {t("components.aveniaLiveness.description")} - -
+
+ +
+

{t("components.aveniaLiveness.title")}

+ + {t("components.aveniaLiveness.description")} + +
-
- - - - {t("components.aveniaLiveness.cameraWarning")} -
+
+ + + + {t("components.aveniaLiveness.cameraWarning")} +
+ + Liveness Check - Liveness Check + {livenessCheckOpened && ( +
+

+ <> + {t("components.aveniaLiveness.troubleTextPart1")} + + {t("components.aveniaLiveness.troubleTextPart2")} + +

+
+ )} +
-
- {livenessCheckOpened ? ( -
+
+
+ {livenessCheckOpened ? ( = ({ aveniaSt > {t("components.aveniaLiveness.livenessDone")} -
-

- <> - {t("components.aveniaLiveness.troubleTextPart1")} - - {t("components.aveniaLiveness.troubleTextPart2")} - -

-
-
- ) : ( - - {t("components.aveniaLiveness.openLivenessCheck")} - - - )} + ) : ( + + {t("components.aveniaLiveness.openLivenessCheck")} + + + )} +
- +
); }; diff --git a/apps/frontend/src/components/widget-steps/DetailsStep/DetailsStepActions.tsx b/apps/frontend/src/components/widget-steps/DetailsStep/DetailsStepActions.tsx index 5c9cb283a..df6b49784 100644 --- a/apps/frontend/src/components/widget-steps/DetailsStep/DetailsStepActions.tsx +++ b/apps/frontend/src/components/widget-steps/DetailsStep/DetailsStepActions.tsx @@ -56,7 +56,7 @@ export const DetailsStepActions = ({ return (
{requiresConnection && } - {displayRampSubmitButton && } + {displayRampSubmitButton && }
); }; diff --git a/apps/frontend/src/components/widget-steps/DetailsStep/DetailsStepQuoteSummary.tsx b/apps/frontend/src/components/widget-steps/DetailsStep/DetailsStepQuoteSummary.tsx deleted file mode 100644 index 7cb526b11..000000000 --- a/apps/frontend/src/components/widget-steps/DetailsStep/DetailsStepQuoteSummary.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { QuoteResponse } from "@vortexfi/shared"; -import { QuoteSummary } from "../../QuoteSummary"; - -export interface DetailsStepQuoteSummaryProps { - quote: QuoteResponse | undefined; - className?: string; -} - -export const DetailsStepQuoteSummary = ({ quote, className }: DetailsStepQuoteSummaryProps) => { - if (!quote) return null; - - return ( -
- -
- ); -}; diff --git a/apps/frontend/src/components/widget-steps/DetailsStep/index.tsx b/apps/frontend/src/components/widget-steps/DetailsStep/index.tsx index a2291a00a..6e8ecbb42 100644 --- a/apps/frontend/src/components/widget-steps/DetailsStep/index.tsx +++ b/apps/frontend/src/components/widget-steps/DetailsStep/index.tsx @@ -5,6 +5,7 @@ import { useEffect, useRef } from "react"; import { FormProvider } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useRampActor } from "../../../contexts/rampState"; +import { cn } from "../../../helpers/cn"; import { RampFormValues } from "../../../hooks/ramp/schema"; import { useRampForm } from "../../../hooks/ramp/useRampForm"; import { useRampSubmission } from "../../../hooks/ramp/useRampSubmission"; @@ -12,10 +13,10 @@ import { useSigningBoxState } from "../../../hooks/useSigningBoxState"; import { useVortexAccount } from "../../../hooks/useVortexAccount"; import { usePixId, useTaxId } from "../../../stores/quote/useQuoteFormStore"; import { useQuote } from "../../../stores/quote/useQuoteStore"; +import { QUOTE_SUMMARY_COLLAPSED_HEIGHT, QuoteSummary } from "../../QuoteSummary"; import { DetailsStepActions } from "./DetailsStepActions"; import { DetailsStepForm } from "./DetailsStepForm"; import { DetailsStepHeader } from "./DetailsStepHeader"; -import { DetailsStepQuoteSummary } from "./DetailsStepQuoteSummary"; export interface DetailsStepProps { className?: string; @@ -111,30 +112,42 @@ export const DetailsStep = ({ className }: DetailsStepProps) => { return ( -
- - - {isSep24Redo && ( -
-
- -

{t("pages.widget.details.quoteChangedWarning")}

-
+
+ +
+ + + {isSep24Redo && ( +
+
+ +

{t("pages.widget.details.quoteChangedWarning")}

+
+
+ )}
- )} - - - +
+ +
+ + {quote && } +
); }; diff --git a/apps/frontend/src/components/widget-steps/MoneriumRedirectStep/index.tsx b/apps/frontend/src/components/widget-steps/MoneriumRedirectStep/index.tsx index ff95203c0..276f29a88 100644 --- a/apps/frontend/src/components/widget-steps/MoneriumRedirectStep/index.tsx +++ b/apps/frontend/src/components/widget-steps/MoneriumRedirectStep/index.tsx @@ -2,7 +2,7 @@ import { useParams, useRouter } from "@tanstack/react-router"; import { useTranslation } from "react-i18next"; import { useMoneriumKycActor, useRampActor } from "../../../contexts/rampState"; import { cn } from "../../../helpers/cn"; -import { navigateToCleanOrigin } from "../../../utils/navigation"; +import { navigateToCleanOrigin } from "../../../lib/navigation"; interface MoneriumRedirectStepProps { className?: string; diff --git a/apps/frontend/src/config/supabase.ts b/apps/frontend/src/config/supabase.ts new file mode 100644 index 000000000..7bfa762c6 --- /dev/null +++ b/apps/frontend/src/config/supabase.ts @@ -0,0 +1,16 @@ +import { createClient } from "@supabase/supabase-js"; + +const supabaseUrl = import.meta.env.VITE_SUPABASE_URL; +const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY; + +if (!supabaseUrl || !supabaseAnonKey) { + throw new Error("Missing Supabase environment variables"); +} + +export const supabase = createClient(supabaseUrl, supabaseAnonKey, { + auth: { + autoRefreshToken: true, + detectSessionInUrl: true, + persistSession: true + } +}); diff --git a/apps/frontend/src/contexts/rampState.tsx b/apps/frontend/src/contexts/rampState.tsx index 9cd1a803d..52bdcb08d 100644 --- a/apps/frontend/src/contexts/rampState.tsx +++ b/apps/frontend/src/contexts/rampState.tsx @@ -45,8 +45,9 @@ const PersistenceEffect = () => { | AveniaKycActorRef | undefined; - const { rampContext, rampState, isQuoteExpired } = useSelector(rampActor, state => ({ + const { rampContext, rampState, isQuoteExpired, quote } = useSelector(rampActor, state => ({ isQuoteExpired: state?.context.isQuoteExpired, + quote: state?.context.quote, rampContext: state?.context, rampState: state?.value })); @@ -67,8 +68,8 @@ const PersistenceEffect = () => { useEffect(() => { const persistedSnapshot = rampActor.getPersistedSnapshot(); localStorage.setItem("rampState", JSON.stringify(persistedSnapshot)); - // It's important to have `isQuoteExpired` here in the deps array to persist it when it changes - }, [rampContext, rampState, moneriumState, stellarState, aveniaState, isQuoteExpired, rampActor.getPersistedSnapshot]); + // It's important to have `isQuoteExpired` and `quote` here in the deps array to persist them when they change + }, [rampContext, rampState, moneriumState, stellarState, aveniaState, isQuoteExpired, quote, rampActor.getPersistedSnapshot]); return null; }; diff --git a/apps/frontend/src/hooks/ramp/useRampSubmission.ts b/apps/frontend/src/hooks/ramp/useRampSubmission.ts index cb7a4f432..1ab592026 100644 --- a/apps/frontend/src/hooks/ramp/useRampSubmission.ts +++ b/apps/frontend/src/hooks/ramp/useRampSubmission.ts @@ -12,6 +12,7 @@ import { useEventsContext } from "../../contexts/events"; import { useRampActor } from "../../contexts/rampState"; import { usePreRampCheck } from "../../services/initialChecks"; import { useQuoteFormStore, useQuoteFormStoreActions } from "../../stores/quote/useQuoteFormStore"; +import { useQuote } from "../../stores/quote/useQuoteStore"; import { useRampDirectionStore } from "../../stores/rampDirectionStore"; import { RampExecutionInput } from "../../types/phases"; @@ -35,11 +36,14 @@ export const useRampSubmission = () => { const { setTaxId, setPixId } = useQuoteFormStoreActions(); - const { connectedWalletAddress, quote } = useSelector(rampActor, state => ({ + const { connectedWalletAddress, quote: contextQuote } = useSelector(rampActor, state => ({ connectedWalletAddress: state.context.connectedWalletAddress, quote: state.context.quote })); + const storeQuote = useQuote(); + const quote = contextQuote || storeQuote; + const { inputAmount, fiatToken, onChainToken } = useQuoteFormStore(); const network = quote ? ((Object.values(Networks).includes(quote.to as Networks) ? quote.to : quote.from) as Networks) diff --git a/apps/frontend/src/hooks/useAuthTokens.ts b/apps/frontend/src/hooks/useAuthTokens.ts new file mode 100644 index 000000000..f09b8db23 --- /dev/null +++ b/apps/frontend/src/hooks/useAuthTokens.ts @@ -0,0 +1,89 @@ +import { useSelector } from "@xstate/react"; +import { useCallback, useEffect, useRef } from "react"; +import type { ActorRefFrom } from "xstate"; +import { supabase } from "../config/supabase"; +import type { rampMachine } from "../machines/ramp.machine"; +import { AuthService } from "../services/auth"; + +export function useAuthTokens(actorRef: ActorRefFrom) { + const { isAuthenticated, userId, userEmail } = useSelector(actorRef, state => ({ + isAuthenticated: state.context.isAuthenticated, + userEmail: state.context.userEmail, + userId: state.context.userId + })); + + // Track if we've already restored the session to avoid running multiple times + const hasRestoredSession = useRef(false); + + // Check for tokens in URL on mount (magic link callback) + useEffect(() => { + const urlTokens = AuthService.handleUrlTokens(); + if (urlTokens) { + // Use the URL tokens to set session with Supabase, then get full user details + supabase.auth + .setSession({ + access_token: urlTokens.accessToken, + refresh_token: urlTokens.refreshToken + }) + .then(({ data, error }) => { + if (error) { + console.error("Failed to set session from URL tokens:", error); + return; + } + + if (data.session) { + const tokens = { + accessToken: data.session.access_token, + refreshToken: data.session.refresh_token, + userEmail: data.session.user.email, + userId: data.session.user.id + }; + + AuthService.storeTokens(tokens); + actorRef.send({ tokens, type: "AUTH_SUCCESS" }); + + // Clean URL + window.history.replaceState({}, "", window.location.pathname); + } + }); + } + }, [actorRef]); + + // Setup auto-refresh on mount + useEffect(() => { + const cleanup = AuthService.setupAutoRefresh(); + return cleanup; + }, []); + + // Restore session from localStorage on mount + useEffect(() => { + // Only restore once on initial mount to avoid infinite loops + if (!hasRestoredSession.current && !isAuthenticated) { + const tokens = AuthService.getTokens(); + if (tokens) { + hasRestoredSession.current = true; + actorRef.send({ + tokens: { + accessToken: tokens.accessToken, + refreshToken: tokens.refreshToken, + userEmail: tokens.userEmail, + userId: tokens.userId + }, + type: "AUTH_SUCCESS" + }); + } + } + }, [actorRef, isAuthenticated]); + + const signOut = useCallback(async () => { + await AuthService.signOut(); + actorRef.send({ type: "RESET_RAMP" }); + }, [actorRef]); + + return { + isAuthenticated, + signOut, + userEmail, + userId + }; +} diff --git a/apps/frontend/src/hooks/useStepper.ts b/apps/frontend/src/hooks/useStepper.ts index 827e7b85b..11a58af23 100644 --- a/apps/frontend/src/hooks/useStepper.ts +++ b/apps/frontend/src/hooks/useStepper.ts @@ -1,6 +1,7 @@ import { CheckCircleIcon as ConfirmIcon, DocumentTextIcon as DetailsIcon, + UserCircleIcon as LoginIcon, DocumentCheckIcon as VerificationIcon } from "@heroicons/react/24/outline"; import { useSelector } from "@xstate/react"; @@ -14,6 +15,7 @@ export const useStepper = () => { const rampActor = useRampActor(); const { + isAuthenticated, isKycActive, isKycComplete, isKycFailure, @@ -25,6 +27,7 @@ export const useStepper = () => { redirectCallback, isError } = useSelector(rampActor, state => ({ + isAuthenticated: state.context.isAuthenticated, isError: state.matches("Error"), isKycActive: state.matches("KYC"), isKycComplete: state.matches("KycComplete"), @@ -37,32 +40,56 @@ export const useStepper = () => { redirectCallback: state.matches("RedirectCallback") })); - const secondStepActive = isKycComplete || isKycActive || isKycFailure; - const secondStepComplete = + // Step 3: Verification - active during KYC, complete when done + const verificationStepActive = isKycActive || isKycFailure; + const verificationStepComplete = rampFollowUp || redirectCallback || isKycComplete || isRegister || isUpdate || rampPaymentConfirmed; - const thirdStepActive = secondStepComplete && rampSummaryVisible; - const thirdStepComplete = rampFollowUp || redirectCallback || rampPaymentConfirmed || isRegister; + // Step 4: Confirm - active when verification complete, complete when payment confirmed + const confirmStepActive = verificationStepComplete && (rampSummaryVisible || isRegister || isUpdate); + const confirmStepComplete = rampFollowUp || redirectCallback || rampPaymentConfirmed; + + // Step 2: Details - active after login, complete when KYC starts + const detailsStepActive = isAuthenticated && !isKycActive && !isKycComplete && !isKycFailure; + const detailsStepComplete = verificationStepComplete || isKycComplete || isKycActive || isKycFailure; + + // Step 1: Login - complete when authenticated + const loginStepComplete = isAuthenticated; const steps = useMemo((): Step[] => { return [ + { + Icon: LoginIcon, + status: loginStepComplete ? "complete" : "active", + title: t("components.stepper.login", "Login") + }, { Icon: DetailsIcon, - status: isError ? "error" : secondStepActive || secondStepComplete ? "complete" : "active", + status: isError ? "error" : detailsStepComplete ? "complete" : detailsStepActive ? "active" : "incomplete", title: t("components.stepper.details", "Details") }, { Icon: VerificationIcon, - status: isError ? "error" : secondStepComplete ? "complete" : secondStepActive ? "active" : "incomplete", + status: isError ? "error" : verificationStepComplete ? "complete" : verificationStepActive ? "active" : "incomplete", title: t("components.stepper.verification", "Verification") }, { Icon: ConfirmIcon, - status: isError ? "error" : thirdStepComplete ? "complete" : thirdStepActive ? "active" : "incomplete", + status: isError ? "error" : confirmStepComplete ? "complete" : confirmStepActive ? "active" : "incomplete", title: t("components.stepper.confirm", "Confirm") } ]; - }, [t, secondStepActive, secondStepComplete, thirdStepActive, thirdStepComplete, isError]); + }, [ + confirmStepActive, + confirmStepComplete, + detailsStepActive, + detailsStepComplete, + isError, + loginStepComplete, + t, + verificationStepActive, + verificationStepComplete + ]); const currentStep = useMemo(() => { return steps.findIndex(step => step.status === "active"); diff --git a/apps/frontend/src/utils/navigation.ts b/apps/frontend/src/lib/navigation.ts similarity index 100% rename from apps/frontend/src/utils/navigation.ts rename to apps/frontend/src/lib/navigation.ts diff --git a/apps/frontend/src/machines/actors/auth.actor.ts b/apps/frontend/src/machines/actors/auth.actor.ts new file mode 100644 index 000000000..91587dda5 --- /dev/null +++ b/apps/frontend/src/machines/actors/auth.actor.ts @@ -0,0 +1,25 @@ +import { AuthAPI } from "../../services/api/auth.api"; +import { RampContext } from "../types"; + +export const checkEmailActor = async ({ input }: { input: { context: RampContext } }) => { + if (!input.context.userEmail) { + throw new Error("Email is required"); + } + + const result = await AuthAPI.checkEmail(input.context.userEmail); + return result; +}; + +export const requestOTPActor = async ({ input }: { input: { context: RampContext } }) => { + if (!input.context.userEmail) { + throw new Error("Email is required"); + } + + await AuthAPI.requestOTP(input.context.userEmail); + return { success: true }; +}; + +export const verifyOTPActor = async ({ input }: { input: { email: string; code: string } }) => { + const result = await AuthAPI.verifyOTP(input.email, input.code); + return result; +}; diff --git a/apps/frontend/src/machines/actors/register.actor.ts b/apps/frontend/src/machines/actors/register.actor.ts index f005b3128..6dcf4ae5b 100644 --- a/apps/frontend/src/machines/actors/register.actor.ts +++ b/apps/frontend/src/machines/actors/register.actor.ts @@ -27,7 +27,7 @@ export class RegisterRampError extends Error { } export const registerRampActor = async ({ input }: { input: RampContext }): Promise => { - const { executionInput, chainId, connectedWalletAddress, authToken, paymentData, quote } = input; + const { executionInput, chainId, connectedWalletAddress, authToken, paymentData, quote, userId } = input; // TODO there should be a way to assert types in states, given transitions should ensure the type. if (!executionInput || !quote) { @@ -97,7 +97,7 @@ export const registerRampActor = async ({ input }: { input: RampContext }): Prom }; } - const rampProcess = await RampService.registerRamp(quoteId, signingAccounts, additionalData); + const rampProcess = await RampService.registerRamp(quoteId, signingAccounts, additionalData, userId); const ephemeralTxs = (rampProcess.unsignedTxs || []).filter(tx => { if (!connectedWalletAddress) { diff --git a/apps/frontend/src/machines/ramp.machine.ts b/apps/frontend/src/machines/ramp.machine.ts index b5b0b1a0b..68643f006 100644 --- a/apps/frontend/src/machines/ramp.machine.ts +++ b/apps/frontend/src/machines/ramp.machine.ts @@ -4,7 +4,9 @@ import { assign, emit, fromCallback, fromPromise, setup } from "xstate"; import { ToastMessage } from "../helpers/notifications"; import { KYCFormData } from "../hooks/brla/useKYCForm"; import { QuoteService } from "../services/api"; +import { AuthService } from "../services/auth"; import { RampExecutionInput, RampSigningPhase } from "../types/phases"; +import { checkEmailActor, requestOTPActor, verifyOTPActor } from "./actors/auth.actor"; import { registerRampActor } from "./actors/register.actor"; import { SignRampError, SignRampErrorType, signTransactionsActor } from "./actors/sign.actor"; import { startRampActor } from "./actors/start.actor"; @@ -19,6 +21,28 @@ const QUOTE_EXPIRY_THRESHOLD_SECONDS = 120; // 2 minutes export const SUCCESS_CALLBACK_DELAY_MS = 5000; // 5 seconds +// Restore session from localStorage if available +const getInitialAuthState = () => { + if (typeof window === "undefined") { + return { isAuthenticated: false, userEmail: undefined, userId: undefined }; + } + + const tokens = AuthService.getTokens(); + + if (tokens) { + const authState = { + isAuthenticated: true, + userEmail: tokens.userEmail, + userId: tokens.userId + }; + return authState; + } + + return { isAuthenticated: false, userEmail: undefined, userId: undefined }; +}; + +const authState = getInitialAuthState(); + const initialRampContext: RampContext = { apiKey: undefined, authToken: undefined, @@ -31,6 +55,7 @@ const initialRampContext: RampContext = { externalSessionId: undefined, getMessageSignature: undefined, initializeFailedMessage: undefined, + isAuthenticated: authState.isAuthenticated, isQuoteExpired: false, isSep24Redo: false, partnerId: undefined, @@ -43,6 +68,9 @@ const initialRampContext: RampContext = { rampSigningPhase: undefined, rampState: undefined, substrateWalletAccount: undefined, + // Auth fields - restore from localStorage if available + userEmail: authState.userEmail, + userId: authState.userId, walletLocked: undefined }; @@ -113,7 +141,16 @@ export type RampMachineEvents = | { type: "SET_INITIALIZE_FAILED_MESSAGE"; message: string | undefined } | { type: "EXPIRE_QUOTE" } | { type: "REFRESH_FAILED" } - | { type: "GO_BACK" }; + | { type: "GO_BACK" } + // Auth events + | { type: "ENTER_EMAIL"; email: string } + | { type: "EMAIL_VERIFIED" } + | { type: "OTP_SENT" } + | { type: "VERIFY_OTP"; code: string } + | { type: "AUTH_SUCCESS"; tokens: { accessToken: string; refreshToken: string; userId: string; userEmail?: string } } + | { type: "AUTH_ERROR"; error: string } + | { type: "CHANGE_EMAIL" } + | { type: "LOGOUT" }; export const rampMachine = setup({ actions: { @@ -148,6 +185,7 @@ export const rampMachine = setup({ }, actors: { aveniaKyc: aveniaKycMachine, + checkEmail: fromPromise(checkEmailActor), loadQuote: fromPromise(async ({ input }: { input: { quoteId: string } }) => { if (!input.quoteId) { throw new Error("Quote ID is required to load quote."); @@ -175,6 +213,7 @@ export const rampMachine = setup({ return () => clearInterval(timer); }), registerRamp: fromPromise(registerRampActor), + requestOTP: fromPromise(requestOTPActor), signTransactions: fromPromise(signTransactionsActor), startRamp: fromPromise(startRampActor), stellarKyc: stellarKycMachine, @@ -190,7 +229,8 @@ export const rampMachine = setup({ }, 1); }) ), - validateKyc: fromPromise(validateKycActor) + validateKyc: fromPromise(validateKycActor), + verifyOTP: fromPromise(verifyOTPActor) }, types: { context: {} as RampContext, @@ -198,16 +238,31 @@ export const rampMachine = setup({ events: {} as RampMachineEvents } }).createMachine({ - /** @xstate-layout N4IgpgJg5mDOIC5QCcCGBbADgYgMoFEAVAfVwEkB1fYgYQHkA5Q-ADUIG0AGAXUVEwD2sAJYAXYQIB2fEAA9EARgBMAdgUA6TgFZOKg3pU6VAGhABPRAEZNAnYt36zVq0BfGvWboM2PAIIAItEASvi4uFy8SCCCIuJSMvIIyu7qbgpunCVavgrlela2CEql6i6ceg7F3oZGbkEhGDgEJADiEQCyibiRQ6RkAwyRhACqCSkyGWIS0mm5ykou6krFZkp++i7ldjWKnGaFWnaVut7GCsrdIKF9EZEACmS0dMNfRj4JjJHgrIRrbKbRRKVzqcxaBTOEouQ5mao2GGvd7qMgQAA2YGwAGMpAAzYTIdDLNKrLIbUBbBzqHTeK4qLR6FpuFRuC4INwuJTqMwqfTlXnNNx6MzY3rqADSAE0aNgIFIwOphJIAG4CADWmpxypoCG1euJqHpKRp-Ah9JyiBcCl2xk5xR5Yu8Cn5LV2Kh2dnqLS0KhcKjlWHUcV6cTAAEcAK5wUSQNUarW6g1G+UxrBxpMpyBmrOW608W3pe3rR0CvQaEp6WEtTiw558zEC06NMPGXk7MynWXBN652MJ5OwVMQdOSTXm7PqHF5zAFyfTksWq3rG0KVJ2zI16F1hvNZsONvFX12f2B4POMMRkfL8eFqdpsDIZACZDqTD4q0yR-dAlzHfMJyLCBNwEMsdwrMFaWrKFGUQaVTybTkL0RK9O3MYUeRcOwVDsMwij0VEFEjTBozAKBhHfZAV1necs0NUCozjOiGJXaDYKkG0EIPSEGTkRQeS0EVTA9F03DsWSO1qHk7BFfxWy8QizlMKiaK41NGN6bBP2-X9-0A4D2Oozj6L0niFz4yQBP3KtD2Q0S8nEyS1DcIp3DkuTfUFdQ7lhcxeX0SotG03BRFQZBRCY9U50zPU2JxaLYvi3peO3fj4KcukjxQuoiM4TQjiItx0URThnH5SrhU5J5BQ8fwumfeV0ripijJ-P8ANEICqQs9ROsyrBsvLbhKwK1zciOFRStbY5KoMLxas7W5FowpozCRW4FECdqo3mTAICtMAEozBdUvlE6ztTWzSxyhy8vBFyRK2CpChlNQ7maAxQzq4iVL0NTOA03wtNeSQBAgOAZHeN7hNrABaH1OxItwRWUUGlDMRxSg8bSADFUGEfFE2QMAkYdY9YVklkIrUIVeXrALrmCwxOeaJFtLxQkacKtyeX5RE9CCrQfO0OSkUI7STUF2bEA5CT6juLxylcIpfUqPYdlhW4-TDQ6eg43piYEfF8QEAB3E7FY+xB6eUxEtFDF1VDQurdpZJsAd0bDOW0lc10gh3ayUQKikbBRzAcMw3ZUOqhRFSXVC5IiFF2lxg9o6zPxXcPjxx4UdhlbzY4DNnOxW32ngW+tymMKKYq63oi6K-ZeUKFxe7cPGzwOrQgeFYKWmUWS1A5bS7vOwvEPe2sDtMQoz3B-ZKpcAwR6CnHgsn2PIqCAIgA */ + /** @xstate-layout N4IgpgJg5mDOIC5QCcCGBbADgYgMoFEAVAfVwEkB1fYgYQHkA5Q-ADUIG0AGAXUVEwD2sAJYAXYQIB2fEAA9EARgBMAdgUA6TgFZOKg3pU6VAGhABPRAEZNAnYt36zVq0BfGvWboM2PAIIAItEASvi4uFy8SCCCIuJSMvIIyu7qbgpunCVavgrlela2CEql6i6ceg7F3oZGbkEhGDgEJADiEQCyibiRQ6RkAwyRhACqCSkyGWIS0mm5ykou6krFZkp++i7ldjWKnGaFWnaRut7GCsrdIKF9EZEACmS0dMNfRj4JjJHgrIRrbKbRRKVzqcxaBTOEouQ5mao2GGvd7qMgQAA2YGwAGMpAAzYTIdDLNKrLIbUBbBzqHTeK4qLR6FpuFRuC4INwuJTqMwqfTlXnNNx6MzY3rqADSAE0aNgIFIwOphJIAG4CADWmpxypoCG1euJqHpKRp-Ah9JyiBcCl2xk5xR5Yu8Cn5LV2Kh2dnqLS0KhcKjlWHUcV6cTAAEcAK5wUSYNUarW6g1G+UxrBxpMpyBmrOWs08W3pe3rR0CvQaEp6WEtTiw558zEC06NMPGXk7MynWXBN652MJ5OwVMQdOSTXm7PqHF5zAFyfTksWq3rG0KVJ2zI16F1hvNZsONvFX12f2B4POMMRkfL8eFqdpsDIZACZDqTD4q0yR-dAlzHfMJyLCBNwEMsdwrMFaWrKFGUQaVTybTkL0RK9O3MYUeRcOwVDsMwij0VEFEjTBozAKBhHfZAV1necs0NUCozjOiGJXaDYKkG0EIPSEGTkRQeS0EVTA9F03DsWSO1qHk7BFfxWy8QizlMKiaK41NGN6bBP2-X9-0A4D2Oozj6L0niFz4yQBP3KtD2Q0S8nEyS1DcIp3DkuTfUFdQ7lhcxeX0SotG03BRFQZBRCY1U50zPU2JxaLYvi3peO3fj4KcukjxQuoiM4TQjiItx0URThnH5SrhU5J5BQ8fwumfeV0ripijJ-P8ANEICqQs9ROsyrBsvLbhKwK1zciOFRStbY5KoMLxas7W5FowpozCRW4FECdqo3mTAICtMAEozBdUvlE6ztTWzSxyhy8vBFyRK2CpChlNQ7maAxQzq4iVL0NTOA03wtNeSQBAgOAZHeN7hNrABaH1OxItwRWUUGlDMRxSg8bSADFUGEfFE2QMAkYdY9YVklkIrUIVeXrALrmGwxOeaJFtLxQkacKtyeX5RE9CCrQfO0OSkUI7STUF2bEA5CT6juLxylcIpfUqPYdlhW4-TDQ6eg43piYEfF8QEAB3E7FY+xB6eUxEtFDF1VDQurdpZJsAd0bDOW0lc10gh3ayUQKikbBRjAcMw3ZUOqhRFSXVC5IiFF2lxg9o6zPxXcPjxx4UdhlbzY4DNzOxW32ngW+tymMKKYq63oi6K-ZeUKFxe7cPGzwOrQgeFYKWmUWS1A5bS7vOwvEPe2sDtMQoz3B-ZKpcAwR6CnHgsn2PIqCAIgA */ context: initialRampContext, id: "ramp", initial: "Idle", on: { + AUTH_SUCCESS: { + actions: assign({ + isAuthenticated: true, + userEmail: ({ event }) => event.tokens.userEmail, + userId: ({ event }) => event.tokens.userId + }) + }, EXPIRE_QUOTE: { actions: assign({ isQuoteExpired: true }) }, + LOGOUT: { + actions: assign({ + isAuthenticated: false, + userEmail: undefined, + userId: undefined + }), + target: "#ramp.EnterEmail" + }, RESET_RAMP: [ { actions: [{ type: "resetRamp" }], @@ -280,6 +335,78 @@ export const rampMachine = setup({ } }, states: { + CheckAuth: { + always: [ + { + guard: ({ context }) => context.isAuthenticated, + target: "QuoteReady" + }, + { + target: "EnterEmail" + } + ] + }, + CheckingEmail: { + invoke: { + input: ({ context }) => ({ context }), + onDone: { + target: "RequestingOTP" + }, + onError: { + actions: assign({ + errorMessage: "Failed to check email. Please try again." + }), + target: "EnterEmail" + }, + src: "checkEmail" + } + }, + EnterEmail: { + on: { + ENTER_EMAIL: { + actions: assign({ + userEmail: ({ event }) => event.email + }), + target: "CheckingEmail" + }, + SET_QUOTE: { + actions: assign({ + quoteId: ({ event }) => event.quoteId, + quoteLocked: ({ event }) => event.lock + }) + }, + SET_QUOTE_PARAMS: { + actions: assign({ + apiKey: ({ event }) => event.apiKey, + callbackUrl: ({ event }) => event.callbackUrl, + partnerId: ({ event }) => event.partnerId, + walletLocked: ({ event }) => event.walletLocked + }) + } + } + }, + EnterOTP: { + on: { + CHANGE_EMAIL: { + actions: assign({ + errorMessage: undefined, + userEmail: undefined + }), + target: "EnterEmail" + }, + ENTER_EMAIL: { + actions: assign({ + errorMessage: undefined, + userEmail: ({ event }) => event.email + }), + target: "CheckingEmail" + }, + VERIFY_OTP: { + actions: assign({ errorMessage: undefined }), + target: "VerifyingOTP" + } + } + }, Error: { entry: assign(({ context }) => ({ ...context, @@ -304,6 +431,12 @@ export const rampMachine = setup({ } }, Idle: { + always: [ + { + guard: ({ context }) => !context.isAuthenticated, + target: "CheckAuth" + } + ], on: { INITIAL_QUOTE_FETCH_FAILED: { target: "InitialFetchFailed" @@ -335,7 +468,7 @@ export const rampMachine = setup({ }, on: { GO_BACK: { - target: "KYC" + target: "QuoteReady" }, PROCEED_TO_REGISTRATION: { target: "RegisterRamp" @@ -377,7 +510,9 @@ export const rampMachine = setup({ LoadingQuote: { invoke: { id: "loadQuote", - input: ({ event }) => ({ quoteId: (event as Extract).quoteId }), + input: ({ event, context }) => ({ + quoteId: (event as Extract).quoteId || context.quoteId! + }), onDone: { actions: assign({ isQuoteExpired: ({ event }) => event.output.isExpired, @@ -396,6 +531,12 @@ export const rampMachine = setup({ } }, QuoteReady: { + always: [ + { + guard: ({ context }) => context.quoteId !== undefined && context.quote === undefined, + target: "LoadingQuote" + } + ], entry: assign({ rampSigningPhase: undefined }), on: { // This is the main confirm button. @@ -405,9 +546,12 @@ export const rampMachine = setup({ executionInput: ({ event }) => event.input.executionInput, // Also reset any error from a previous attempt initializeFailedMessage: undefined, + // Restore quote and quoteId if missing + quote: ({ context, event }) => context.quote || event.input.executionInput.quote, + quoteId: ({ context, event }) => context.quoteId || event.input.executionInput.quote.id, rampDirection: ({ event }) => event.input.rampDirection }), - guard: ({ context }) => context.quoteId !== undefined, + guard: ({ context, event }) => context.quoteId !== undefined || event.input.executionInput.quote !== undefined, target: "RampRequested" }, GO_BACK: { @@ -483,6 +627,21 @@ export const rampMachine = setup({ src: "registerRamp" } }, + RequestingOTP: { + invoke: { + input: ({ context }) => ({ context }), + onDone: { + target: "EnterOTP" + }, + onError: { + actions: assign({ + errorMessage: "Failed to send OTP. Please try again." + }), + target: "EnterEmail" + }, + src: "requestOTP" + } + }, Resetting: { entry: "resetRamp", invoke: { @@ -553,6 +712,40 @@ export const rampMachine = setup({ target: "StartRamp" } } + }, + VerifyingOTP: { + invoke: { + input: ({ context, event }) => ({ + code: (event as any).code, + email: context.userEmail! + }), + onDone: { + actions: [ + assign({ + errorMessage: undefined, + isAuthenticated: true, + userId: ({ event }) => event.output.userId + }), + ({ event, context }) => { + // Store tokens in localStorage for session persistence + AuthService.storeTokens({ + accessToken: event.output.accessToken, + refreshToken: event.output.refreshToken, + userEmail: context.userEmail, + userId: event.output.userId + }); + } + ], + target: "QuoteReady" + }, + onError: { + actions: assign({ + errorMessage: "Invalid OTP code. Please try again." + }), + target: "EnterOTP" + }, + src: "verifyOTP" + } } } }); diff --git a/apps/frontend/src/machines/types.ts b/apps/frontend/src/machines/types.ts index 950ca48d0..1048c8f31 100644 --- a/apps/frontend/src/machines/types.ts +++ b/apps/frontend/src/machines/types.ts @@ -37,6 +37,10 @@ export interface RampContext { errorMessage?: string; kycFormData?: KYCFormData; enteredViaForm?: boolean; // True if user navigated from the Quote form, false if entered via direct URL + // Auth-related fields + userEmail?: string; + userId?: string; + isAuthenticated: boolean; } export type RampMachineEvents = @@ -57,12 +61,22 @@ export type RampMachineEvents = | { type: "PROCEED_TO_REGISTRATION" } | { type: "SET_QUOTE"; quoteId: string; lock: boolean; enteredViaForm?: boolean } | { type: "UPDATE_QUOTE"; quote: QuoteResponse } - | { type: "SET_QUOTE_PARAMS"; partnerId?: string; walletLocked?: string; callbackUrl?: string } + | { type: "SET_QUOTE_PARAMS"; apiKey?: string; partnerId?: string; walletLocked?: string; callbackUrl?: string } | { type: "SET_EXTERNAL_ID"; externalSessionId: string | undefined } | { type: "INITIAL_QUOTE_FETCH_FAILED" } | { type: "SET_INITIALIZE_FAILED_MESSAGE"; message: string | undefined } | { type: "EXPIRE_QUOTE" } - | { type: "REFRESH_FAILED" }; + | { type: "REFRESH_FAILED" } + // Auth events + | { type: "ENTER_EMAIL"; email: string } + | { type: "CHANGE_EMAIL" } + | { type: "EMAIL_VERIFIED" } + | { type: "OTP_SENT" } + | { type: "VERIFY_OTP"; code: string } + | { type: "AUTH_SUCCESS"; tokens: { accessToken: string; refreshToken: string; userId: string; userEmail?: string } } + | { type: "AUTH_ERROR"; error: string } + | { type: "LOGOUT" } + | { type: "GO_BACK" }; export type RampMachineActor = ActorRef; export type RampMachineSnapshot = SnapshotFrom; diff --git a/apps/frontend/src/pages/ramp/index.tsx b/apps/frontend/src/pages/ramp/index.tsx index be0db10ae..73866cfe6 100644 --- a/apps/frontend/src/pages/ramp/index.tsx +++ b/apps/frontend/src/pages/ramp/index.tsx @@ -4,6 +4,7 @@ import { useRampActor, useStellarKycActor } from "../../contexts/rampState"; import { useToastMessage } from "../../helpers/notifications"; import { useMoneriumFlow } from "../../hooks/monerium/useMoneriumFlow"; import { useRampNavigation } from "../../hooks/ramp/useRampNavigation"; +import { useAuthTokens } from "../../hooks/useAuthTokens"; import { useSiweSignature } from "../../hooks/useSignChallenge"; import { useQuote, useQuoteActions } from "../../stores/quote/useQuoteStore"; import { FailurePage } from "../failure"; @@ -20,6 +21,7 @@ export const Ramp = () => { const { forceSetQuote } = useQuoteActions(); useMoneriumFlow(); useSiweSignature(stellarKycActor); + useAuthTokens(rampActor); const { showToast } = useToastMessage(); @@ -41,6 +43,12 @@ export const Ramp = () => { } }, [quote, quoteFromState, forceSetQuote]); + useEffect(() => { + if (!quoteFromState && quote && state === "QuoteReady") { + rampActor.send({ quote, type: "UPDATE_QUOTE" }); + } + }, [quote, quoteFromState, state, rampActor]); + console.log("Debug: Current Ramp State:", state); return getCurrentComponent(); }; diff --git a/apps/frontend/src/pages/widget/index.tsx b/apps/frontend/src/pages/widget/index.tsx index 58c19293b..0745275f1 100644 --- a/apps/frontend/src/pages/widget/index.tsx +++ b/apps/frontend/src/pages/widget/index.tsx @@ -4,6 +4,10 @@ import { motion } from "motion/react"; import { AveniaKYBFlow } from "../../components/Avenia/AveniaKYBFlow"; import { AveniaKYBForm } from "../../components/Avenia/AveniaKYBForm"; import { AveniaKYCForm } from "../../components/Avenia/AveniaKYCForm"; +import { HistoryMenu } from "../../components/menus/HistoryMenu"; +import { SettingsMenu } from "../../components/menus/SettingsMenu"; +import { AuthEmailStep } from "../../components/widget-steps/AuthEmailStep"; +import { AuthOTPStep } from "../../components/widget-steps/AuthOTPStep"; import { DetailsStep } from "../../components/widget-steps/DetailsStep"; import { ErrorStep } from "../../components/widget-steps/ErrorStep"; import { InitialQuoteFailedStep } from "../../components/widget-steps/InitialQuoteFailedStep"; @@ -12,6 +16,7 @@ import { RampFollowUpRedirectStep } from "../../components/widget-steps/RampFoll import { SummaryStep } from "../../components/widget-steps/SummaryStep"; import { useAveniaKycActor, useAveniaKycSelector, useMoneriumKycActor, useRampActor } from "../../contexts/rampState"; import { cn } from "../../helpers/cn"; +import { useAuthTokens } from "../../hooks/useAuthTokens"; export interface WidgetProps { className?: string; @@ -28,6 +33,8 @@ export const Widget = ({ className }: WidgetProps) => ( transition={{ duration: 0.3 }} > + + ); @@ -37,6 +44,9 @@ const WidgetContent = () => { const moneriumKycActor = useMoneriumKycActor(); const aveniaState = useAveniaKycSelector(); + // Enable session persistence and auto-refresh + useAuthTokens(rampActor); + const { rampState, isRedirectCallback, isError } = useSelector(rampActor, state => ({ isError: state.matches("Error"), isRedirectCallback: state.matches("RedirectCallback"), @@ -55,6 +65,17 @@ const WidgetContent = () => { const isInitialQuoteFailed = useSelector(rampActor, state => state.matches("InitialFetchFailed")); + const isAuthEmail = useSelector( + rampActor, + state => + state.matches("CheckAuth") || + state.matches("EnterEmail") || + state.matches("CheckingEmail") || + state.matches("RequestingOTP") + ); + + const isAuthOTP = useSelector(rampActor, state => state.matches("EnterOTP") || state.matches("VerifyingOTP")); + if (isError) { return ; } @@ -63,6 +84,14 @@ const WidgetContent = () => { return ; } + if (isAuthEmail) { + return ; + } + + if (isAuthOTP) { + return ; + } + if (isMoneriumRedirect) { return ; } diff --git a/apps/frontend/src/services/api/api-client.ts b/apps/frontend/src/services/api/api-client.ts index 750810ce8..025fcfd29 100644 --- a/apps/frontend/src/services/api/api-client.ts +++ b/apps/frontend/src/services/api/api-client.ts @@ -1,5 +1,6 @@ import axios, { AxiosError, AxiosInstance, AxiosRequestConfig } from "axios"; import { SIGNING_SERVICE_URL } from "../../constants/constants"; +import { AuthService } from "../auth"; // TODO: CONSIDER REACT TANSTACK QUERY @@ -14,10 +15,14 @@ export const apiClient: AxiosInstance = axios.create({ timeout: 30000 }); -// Add request interceptor for common headers +// Add request interceptor for common headers and auth token apiClient.interceptors.request.use( config => { - // Add any common headers here + // Add Authorization header if user is authenticated + const tokens = AuthService.getTokens(); + if (tokens?.accessToken) { + config.headers.Authorization = `Bearer ${tokens.accessToken}`; + } return config; }, error => { diff --git a/apps/frontend/src/services/api/auth.api.ts b/apps/frontend/src/services/api/auth.api.ts new file mode 100644 index 000000000..a7a1cf6ac --- /dev/null +++ b/apps/frontend/src/services/api/auth.api.ts @@ -0,0 +1,71 @@ +import { apiClient } from "./api-client"; + +export interface CheckEmailResponse { + exists: boolean; + action: "signin" | "signup"; +} + +export interface VerifyOTPResponse { + success: boolean; + accessToken: string; + refreshToken: string; + userId: string; +} + +export class AuthAPI { + /** + * Check if email exists + */ + static async checkEmail(email: string): Promise { + const response = await apiClient.get("/auth/check-email", { + params: { email } + }); + return response.data; + } + + /** + * Request OTP + */ + static async requestOTP(email: string): Promise { + await apiClient.post("/auth/request-otp", { + email + }); + } + + /** + * Verify OTP + */ + static async verifyOTP(email: string, token: string): Promise { + const response = await apiClient.post<{ success: boolean; access_token: string; refresh_token: string; user_id: string }>( + "/auth/verify-otp", + { + email, + token + } + ); + return { + accessToken: response.data.access_token, + refreshToken: response.data.refresh_token, + success: response.data.success, + userId: response.data.user_id + }; + } + + /** + * Refresh token + */ + static async refreshToken(refreshToken: string): Promise { + const response = await apiClient.post<{ success: boolean; access_token: string; refresh_token: string; user_id: string }>( + "/auth/refresh", + { + refresh_token: refreshToken + } + ); + return { + accessToken: response.data.access_token, + refreshToken: response.data.refresh_token, + success: response.data.success, + userId: response.data.user_id + }; + } +} diff --git a/apps/frontend/src/services/api/ramp.service.ts b/apps/frontend/src/services/api/ramp.service.ts index e2375a937..bdc8feefc 100644 --- a/apps/frontend/src/services/api/ramp.service.ts +++ b/apps/frontend/src/services/api/ramp.service.ts @@ -29,12 +29,14 @@ export class RampService { static async registerRamp( quoteId: string, signingAccounts: AccountMeta[], - additionalData?: RegisterRampRequest["additionalData"] + additionalData?: RegisterRampRequest["additionalData"], + userId?: string ): Promise { - const request: RegisterRampRequest = { + const request: RegisterRampRequest & { userId?: string } = { additionalData, quoteId, - signingAccounts + signingAccounts, + userId }; return apiRequest("post", `${this.BASE_PATH}/register`, request); } diff --git a/apps/frontend/src/services/auth.ts b/apps/frontend/src/services/auth.ts new file mode 100644 index 000000000..cfd4f89fc --- /dev/null +++ b/apps/frontend/src/services/auth.ts @@ -0,0 +1,148 @@ +import { supabase } from "../config/supabase"; + +export interface AuthTokens { + accessToken: string; + refreshToken: string; + userId: string; + userEmail?: string; +} + +export class AuthService { + private static readonly ACCESS_TOKEN_KEY = "vortex_access_token"; + private static readonly REFRESH_TOKEN_KEY = "vortex_refresh_token"; + private static readonly USER_ID_KEY = "vortex_user_id"; + private static readonly USER_EMAIL_KEY = "vortex_user_email"; + + /** + * Store tokens in localStorage + * + * Security Note: Storing tokens in localStorage makes them vulnerable to XSS attacks. + * For production applications, consider using httpOnly cookies or implementing additional + * security measures such as Content Security Policy headers and token encryption. + * The current implementation prioritizes user experience and ease of integration. + */ + static storeTokens(tokens: AuthTokens): void { + localStorage.setItem(this.ACCESS_TOKEN_KEY, tokens.accessToken); + localStorage.setItem(this.REFRESH_TOKEN_KEY, tokens.refreshToken); + localStorage.setItem(this.USER_ID_KEY, tokens.userId); + if (tokens.userEmail) { + localStorage.setItem(this.USER_EMAIL_KEY, tokens.userEmail); + } + } + + /** + * Get tokens from localStorage + */ + static getTokens(): AuthTokens | null { + const access_token = localStorage.getItem(this.ACCESS_TOKEN_KEY); + const refresh_token = localStorage.getItem(this.REFRESH_TOKEN_KEY); + const user_id = localStorage.getItem(this.USER_ID_KEY); + const user_email = localStorage.getItem(this.USER_EMAIL_KEY); + + if (!access_token || !refresh_token || !user_id) { + return null; + } + + return { accessToken: access_token, refreshToken: refresh_token, userEmail: user_email || undefined, userId: user_id }; + } + + /** + * Clear tokens from localStorage + */ + static clearTokens(): void { + localStorage.removeItem(this.ACCESS_TOKEN_KEY); + localStorage.removeItem(this.REFRESH_TOKEN_KEY); + localStorage.removeItem(this.USER_ID_KEY); + localStorage.removeItem(this.USER_EMAIL_KEY); + } + + /** + * Check if user is authenticated + */ + static isAuthenticated(): boolean { + return this.getTokens() !== null; + } + + /** + * Get user ID + */ + static getUserId(): string | null { + return localStorage.getItem(this.USER_ID_KEY); + } + + /** + * Handle tokens from URL (for magic link callback) + * Returns the tokens from the URL hash if present, otherwise null. + * Note: These are raw URL tokens; the caller should use them to set up + * the Supabase session and get the full user details. + */ + static handleUrlTokens(): { accessToken: string; refreshToken: string } | null { + const params = new URLSearchParams(window.location.hash.substring(1)); + const access_token = params.get("access_token"); + const refresh_token = params.get("refresh_token"); + + if (access_token && refresh_token) { + return { accessToken: access_token, refreshToken: refresh_token }; + } + + return null; + } + + /** + * Refresh access token + */ + static async refreshAccessToken(): Promise { + const tokens = this.getTokens(); + if (!tokens) { + return null; + } + + try { + const { data, error } = await supabase.auth.refreshSession({ + refresh_token: tokens.refreshToken + }); + + if (error || !data.session || !data.user) { + this.clearTokens(); + return null; + } + + const newTokens: AuthTokens = { + accessToken: data.session.access_token, + refreshToken: data.session.refresh_token, + userEmail: data.user.email, + userId: data.user.id + }; + + this.storeTokens(newTokens); + return newTokens; + } catch (error) { + console.error("Token refresh failed:", error); + this.clearTokens(); + return null; + } + } + + /** + * Setup auto-refresh (refresh 5 minutes before expiry) + */ + static setupAutoRefresh(): () => void { + const REFRESH_INTERVAL = 55 * 60 * 1000; // 55 minutes + + const intervalId = setInterval(async () => { + if (this.isAuthenticated()) { + await this.refreshAccessToken(); + } + }, REFRESH_INTERVAL); + + return () => clearInterval(intervalId); + } + + /** + * Sign out + */ + static async signOut(): Promise { + await supabase.auth.signOut(); + this.clearTokens(); + } +} diff --git a/apps/frontend/src/stories/Stepper.stories.tsx b/apps/frontend/src/stories/Stepper.stories.tsx index d07620331..d3f2656b1 100644 --- a/apps/frontend/src/stories/Stepper.stories.tsx +++ b/apps/frontend/src/stories/Stepper.stories.tsx @@ -42,8 +42,9 @@ const generateSteps = ( allIncomplete = false, longTitles = false ): Step[] => { - const shortTitles = ["Setup", "Details", "Review", "Payment", "Complete"]; + const shortTitles = ["Login", "Setup", "Details", "Review", "Payment", "Complete"]; const longTitles_array = [ + "Login & Authentication", "Initial Setup & Configuration", "Personal Details & Information & Personal Details & Information", "Review & Verification", @@ -289,7 +290,7 @@ type Story = StoryObj; export const Default: Story = { args: { currentStep: 1, - stepCount: 3 + stepCount: 4 }, render: StepperWrapper }; diff --git a/apps/frontend/src/stories/TermsAndConditions.stories.tsx b/apps/frontend/src/stories/TermsAndConditions.stories.tsx deleted file mode 100644 index c09f2948d..000000000 --- a/apps/frontend/src/stories/TermsAndConditions.stories.tsx +++ /dev/null @@ -1,289 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react"; -import { useState } from "react"; -import { TermsAndConditions } from "../components/TermsAndConditions"; - -interface StoryArgs { - termsChecked?: boolean; - termsAccepted?: boolean; - termsError?: boolean; -} - -const TermsAndConditionsWrapper = ({ termsChecked = false, termsAccepted = false, termsError = false }: StoryArgs) => { - const [checked, setChecked] = useState(termsChecked); - const [error, setError] = useState(termsError); - const [accepted, setAccepted] = useState(termsAccepted); - - return ( -
- setChecked(!checked)} - /> - {!accepted && ( -
- - {!checked && ( - - )} -
- )} -
- ); -}; - -const InteractiveDemo = () => { - const [checked, setChecked] = useState(false); - const [error, setError] = useState(false); - const [accepted, setAccepted] = useState(false); - - const handleContinue = () => { - if (!checked) { - setError(true); - return; - } - setAccepted(true); - }; - - const handleReset = () => { - setChecked(false); - setError(false); - setAccepted(false); - }; - - return ( -
-
-

- State: {accepted ? "Accepted" : checked ? "Checked" : error ? "Error" : "Unchecked"} -

-
- - { - setChecked(!checked); - setError(false); - }} - /> - - {accepted ? ( -
-

Terms Accepted!

- -
- ) : ( - - )} -
- ); -}; - -const CheckoutFlowDemo = () => { - const [checked, setChecked] = useState(false); - const [error, setError] = useState(false); - const [accepted, setAccepted] = useState(false); - - return ( -
-

Complete Your Order

- -
-
- Amount - 100 USDC -
-
- Fee - 0.50 USDC -
-
- Total - 100.50 USDC -
-
- - { - setChecked(!checked); - setError(false); - }} - /> - - {accepted ? ( -
-

Order Confirmed!

-
- ) : ( - - )} -
- ); -}; - -const meta: Meta = { - argTypes: { - termsAccepted: { - control: "boolean", - description: "Whether the terms have been accepted (hides the checkbox)" - }, - termsChecked: { - control: "boolean", - description: "Whether the checkbox is checked" - }, - termsError: { - control: "boolean", - description: "Whether to show the error state" - } - }, - component: TermsAndConditionsWrapper, - parameters: { - docs: { - description: { - component: - "A terms and conditions checkbox component with animated error states and fade-out on acceptance. Features a subtle scale animation on error and supports reduced motion preferences." - } - }, - layout: "centered" - }, - tags: ["autodocs"], - title: "Components/TermsAndConditions" -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - termsAccepted: false, - termsChecked: false, - termsError: false - }, - parameters: { - docs: { - description: { - story: "Default unchecked state. Check the box before continuing." - } - } - }, - render: TermsAndConditionsWrapper -}; - -export const Checked: Story = { - args: { - termsAccepted: false, - termsChecked: true, - termsError: false - }, - parameters: { - docs: { - description: { - story: "Checkbox in checked state, ready to continue." - } - } - }, - render: TermsAndConditionsWrapper -}; - -export const Error: Story = { - args: { - termsAccepted: false, - termsChecked: false, - termsError: true - }, - parameters: { - docs: { - description: { - story: "Error state shown when user tries to continue without accepting terms." - } - } - }, - render: TermsAndConditionsWrapper -}; - -export const Accepted: Story = { - args: { - termsAccepted: true, - termsChecked: true, - termsError: false - }, - parameters: { - docs: { - description: { - story: "Accepted state - the checkbox fades out with a scale animation." - } - } - }, - render: TermsAndConditionsWrapper -}; - -export const Interactive: Story = { - parameters: { - docs: { - description: { - story: "Interactive demo showing the full flow: unchecked -> error -> checked -> accepted." - } - } - }, - render: InteractiveDemo -}; - -export const CheckoutFlow: Story = { - parameters: { - docs: { - description: { - story: "Real-world example showing the terms checkbox in a checkout/confirmation flow." - } - } - }, - render: CheckoutFlowDemo -}; - -export const ReducedMotion: Story = { - args: { - termsAccepted: false, - termsChecked: false, - termsError: false - }, - parameters: { - docs: { - description: { - story: - "Test reduced motion support. Enable 'prefers-reduced-motion: reduce' to see instant transitions instead of animations." - } - } - }, - render: TermsAndConditionsWrapper -}; diff --git a/apps/frontend/src/translations/en.json b/apps/frontend/src/translations/en.json index 8f3c31ff4..b42290e67 100644 --- a/apps/frontend/src/translations/en.json +++ b/apps/frontend/src/translations/en.json @@ -11,6 +11,39 @@ }, "title": "Special offer until 27 May" }, + "authEmailStep": { + "buttons": { + "continue": "Continue", + "sending": "Sending..." + }, + "description": "We'll send you a one-time code to verify your identity", + "fields": { + "email": { + "label": "Email Address", + "placeholder": "you@example.com" + } + }, + "termsCheckbox": { + "and": "and", + "prefix": "I agree to the", + "privacyPolicy": "Privacy Policy", + "termsAndConditions": "Terms and Conditions" + }, + "title": "Enter Your Email", + "validation": { + "invalidEmail": "Please enter a valid email address" + } + }, + "authOTPStep": { + "buttons": { + "useDifferentEmail": "Use a different email" + }, + "description": "We sent a 6-digit code to <1>{{email}}", + "status": { + "verifying": "Verifying..." + }, + "title": "Enter Verification Code" + }, "aveniaKYB": { "aveniaKYBVerifyCompanyRepresentative": { "instructions": "As the individual legally authorized to act on behalf of the company, please provide your personal information and required documents." @@ -435,6 +468,7 @@ "stepper": { "confirm": "Confirm", "details": "Details", + "login": "Login", "verification": "Verification" }, "swap": { @@ -528,6 +562,7 @@ "support": "Support", "termsAndConditions": "Terms & Conditions" }, + "signOut": "Sign Out", "title": "Settings" } }, diff --git a/apps/frontend/src/translations/pt.json b/apps/frontend/src/translations/pt.json index b8029d8f2..9dd728c43 100644 --- a/apps/frontend/src/translations/pt.json +++ b/apps/frontend/src/translations/pt.json @@ -11,6 +11,39 @@ }, "title": "Oferta especial até 27 Maio" }, + "authEmailStep": { + "buttons": { + "continue": "Continuar", + "sending": "Enviando..." + }, + "description": "Enviaremos um código único para verificar sua identidade", + "fields": { + "email": { + "label": "Endereço de e-mail", + "placeholder": "voce@exemplo.com" + } + }, + "termsCheckbox": { + "and": "e", + "prefix": "Eu concordo com os", + "privacyPolicy": "Política de Privacidade", + "termsAndConditions": "Termos e Condições" + }, + "title": "Digite seu e-mail", + "validation": { + "invalidEmail": "Por favor, insira um endereço de e-mail válido" + } + }, + "authOTPStep": { + "buttons": { + "useDifferentEmail": "Usar um e-mail diferente" + }, + "description": "Enviamos um código de 6 dígitos para <1>{{email}}", + "status": { + "verifying": "Verificando..." + }, + "title": "Digite o código de verificação" + }, "aveniaKYB": { "aveniaKYBVerifyCompanyRepresentative": { "instructions": "Como indivíduo legalmente autorizado a agir em nome da empresa, forneça suas informações pessoais e os documentos necessários." @@ -435,6 +468,7 @@ "stepper": { "confirm": "Confirmar", "details": "Detalhes", + "login": "Login", "verification": "Verificação" }, "swap": { @@ -528,6 +562,7 @@ "support": "Apoiar", "termsAndConditions": "Termos e Condições" }, + "signOut": "Sair", "title": "Configurações" } }, diff --git a/apps/rebalancer/package.json b/apps/rebalancer/package.json index 03d219a2c..bc19634f4 100644 --- a/apps/rebalancer/package.json +++ b/apps/rebalancer/package.json @@ -6,7 +6,7 @@ "@polkadot/api-contract": "catalog:", "@polkadot/util": "catalog:", "@polkadot/util-crypto": "catalog:", - "@supabase/supabase-js": "^2.80.0", + "@supabase/supabase-js": "catalog:", "@types/big.js": "catalog:", "@vortexfi/shared": "workspace:*", "big.js": "catalog:", diff --git a/bun.lock b/bun.lock index ace8a13a1..b21883d30 100644 --- a/bun.lock +++ b/bun.lock @@ -30,7 +30,7 @@ "@polkadot/util": "catalog:", "@polkadot/util-crypto": "catalog:", "@scure/bip39": "^1.5.4", - "@supabase/supabase-js": "^2.87.1", + "@supabase/supabase-js": "catalog:", "@vortexfi/shared": "workspace:*", "@wagmi/core": "catalog:", "axios": "catalog:", @@ -127,6 +127,7 @@ "@sentry/react": "^8.36.0", "@sentry/vite-plugin": "^2.22.6", "@storybook/react": "catalog:", + "@supabase/supabase-js": "catalog:", "@tailwindcss/vite": "^4.0.3", "@talismn/connect-components": "^1.1.9", "@talismn/connect-wallets": "^1.2.8", @@ -148,10 +149,13 @@ "big.js": "catalog:", "bn.js": "^5.2.1", "buffer": "^6.0.3", - "clsx": "catalog:", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", "crypto-js": "^4.2.0", "i18next": "^24.2.3", + "input-otp": "^1.4.2", "lottie-react": "^2.4.1", + "lucide-react": "^0.562.0", "motion": "^12.0.3", "numora": "^3.0.2", "numora-react": "3.0.3", @@ -162,7 +166,7 @@ "react-i18next": "^15.4.1", "react-toastify": "^11.0.5", "stellar-sdk": "catalog:", - "tailwind-merge": "^3.1.0", + "tailwind-merge": "^3.4.0", "tailwindcss": "^4.0.3", "viem": "catalog:", "wagmi": "catalog:", @@ -204,6 +208,7 @@ "prettier": "catalog:", "storybook": "^9.1.4", "ts-node": "^10.9.1", + "tw-animate-css": "^1.4.0", "typescript": "catalog:", "vite": "^6.2.6", "vite-plugin-node-polyfills": "^0.23.0", @@ -219,7 +224,7 @@ "@polkadot/api-contract": "catalog:", "@polkadot/util": "catalog:", "@polkadot/util-crypto": "catalog:", - "@supabase/supabase-js": "^2.80.0", + "@supabase/supabase-js": "catalog:", "@types/big.js": "catalog:", "@vortexfi/shared": "workspace:*", "big.js": "catalog:", @@ -335,6 +340,7 @@ "@polkadot/util-crypto": "^13.5.6", "@scure/bip39": "2.0.1", "@storybook/react": "^9.1.16", + "@supabase/supabase-js": "^2.80.0", "@types/big.js": "^6.0.2", "@types/node": "^22.7.5", "@vortexfi/shared": "workspace:*", @@ -370,31 +376,31 @@ "@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="], - "@aws-sdk/client-cognito-identity": ["@aws-sdk/client-cognito-identity@3.971.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.970.0", "@aws-sdk/credential-provider-node": "3.971.0", "@aws-sdk/middleware-host-header": "3.969.0", "@aws-sdk/middleware-logger": "3.969.0", "@aws-sdk/middleware-recursion-detection": "3.969.0", "@aws-sdk/middleware-user-agent": "3.970.0", "@aws-sdk/region-config-resolver": "3.969.0", "@aws-sdk/types": "3.969.0", "@aws-sdk/util-endpoints": "3.970.0", "@aws-sdk/util-user-agent-browser": "3.969.0", "@aws-sdk/util-user-agent-node": "3.971.0", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.20.6", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.7", "@smithy/middleware-retry": "^4.4.23", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.8", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.10.8", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.22", "@smithy/util-defaults-mode-node": "^4.2.25", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-JK1H3EXpm+z2qrfaM56ohx6wfibuF9+Kis4nGxQgitZjKhtZRysIkV7EBoeRGXXutESAumSV+DYSuFHU9n4Pzw=="], + "@aws-sdk/client-cognito-identity": ["@aws-sdk/client-cognito-identity@3.969.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.969.0", "@aws-sdk/credential-provider-node": "3.969.0", "@aws-sdk/middleware-host-header": "3.969.0", "@aws-sdk/middleware-logger": "3.969.0", "@aws-sdk/middleware-recursion-detection": "3.969.0", "@aws-sdk/middleware-user-agent": "3.969.0", "@aws-sdk/region-config-resolver": "3.969.0", "@aws-sdk/types": "3.969.0", "@aws-sdk/util-endpoints": "3.969.0", "@aws-sdk/util-user-agent-browser": "3.969.0", "@aws-sdk/util-user-agent-node": "3.969.0", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.20.5", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.6", "@smithy/middleware-retry": "^4.4.22", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.8", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.10.7", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.21", "@smithy/util-defaults-mode-node": "^4.2.24", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Ck202yziyzEu8O8XUZ9fyQOrjXAOFCOk3iAS2aqz517Pyb6ys81pf8DqOKEoYrtcKPY3Yn7Jb5zcLi0FVUMgTQ=="], - "@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.971.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.970.0", "@aws-sdk/middleware-host-header": "3.969.0", "@aws-sdk/middleware-logger": "3.969.0", "@aws-sdk/middleware-recursion-detection": "3.969.0", "@aws-sdk/middleware-user-agent": "3.970.0", "@aws-sdk/region-config-resolver": "3.969.0", "@aws-sdk/types": "3.969.0", "@aws-sdk/util-endpoints": "3.970.0", "@aws-sdk/util-user-agent-browser": "3.969.0", "@aws-sdk/util-user-agent-node": "3.971.0", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.20.6", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.7", "@smithy/middleware-retry": "^4.4.23", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.8", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.10.8", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.22", "@smithy/util-defaults-mode-node": "^4.2.25", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Xx+w6DQqJxDdymYyIxyKJnRzPvVJ4e/Aw0czO7aC9L/iraaV7AG8QtRe93OGW6aoHSh72CIiinnpJJfLsQqP4g=="], + "@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.969.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.969.0", "@aws-sdk/middleware-host-header": "3.969.0", "@aws-sdk/middleware-logger": "3.969.0", "@aws-sdk/middleware-recursion-detection": "3.969.0", "@aws-sdk/middleware-user-agent": "3.969.0", "@aws-sdk/region-config-resolver": "3.969.0", "@aws-sdk/types": "3.969.0", "@aws-sdk/util-endpoints": "3.969.0", "@aws-sdk/util-user-agent-browser": "3.969.0", "@aws-sdk/util-user-agent-node": "3.969.0", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.20.5", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.6", "@smithy/middleware-retry": "^4.4.22", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.8", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.10.7", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.21", "@smithy/util-defaults-mode-node": "^4.2.24", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Qn0Uz6o15q2S+1E6OpwRKmaAMoT4LktEn+Oibk28qb2Mne+emaDawhZXahOJb/wFw5lN2FEH7XoiSNenNNUmCw=="], - "@aws-sdk/core": ["@aws-sdk/core@3.970.0", "", { "dependencies": { "@aws-sdk/types": "3.969.0", "@aws-sdk/xml-builder": "3.969.0", "@smithy/core": "^3.20.6", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/smithy-client": "^4.10.8", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-klpzObldOq8HXzDjDlY6K8rMhYZU6mXRz6P9F9N+tWnjoYFfeBMra8wYApydElTUYQKP1O7RLHwH1OKFfKcqIA=="], + "@aws-sdk/core": ["@aws-sdk/core@3.969.0", "", { "dependencies": { "@aws-sdk/types": "3.969.0", "@aws-sdk/xml-builder": "3.969.0", "@smithy/core": "^3.20.5", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@smithy/smithy-client": "^4.10.7", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-qqmQt4z5rEK1OYVkVkboWgy/58CC5QaQ7oy0tvLe3iri/mfZbgJkA+pkwQyRP827DfCBZ3W7Ki9iwSa+B2U7uQ=="], - "@aws-sdk/credential-provider-cognito-identity": ["@aws-sdk/credential-provider-cognito-identity@3.971.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.971.0", "@aws-sdk/types": "3.969.0", "@smithy/property-provider": "^4.2.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-NsFmrFoBPqLUYCmLoRpi1MUB5Jw4txk9+eV8xn4NArGB89WiZXqrupvHOPra0uLEDVwuwvg7eWOvdex03aV3nA=="], + "@aws-sdk/credential-provider-cognito-identity": ["@aws-sdk/credential-provider-cognito-identity@3.969.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.969.0", "@aws-sdk/types": "3.969.0", "@smithy/property-provider": "^4.2.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-Fqt+qa9/yEn5wr2fnpocOB4BBGkatxVfXn9WKXwItkgbhjtIz3DEwMCOBFiOiOf1WZn3+E/JupH4oBP3QkGlOw=="], - "@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.970.0", "", { "dependencies": { "@aws-sdk/core": "3.970.0", "@aws-sdk/types": "3.969.0", "@smithy/property-provider": "^4.2.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-rtVzXzEtAfZBfh+lq3DAvRar4c3jyptweOAJR2DweyXx71QSMY+O879hjpMwES7jl07a3O1zlnFIDo4KP/96kQ=="], + "@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.969.0", "", { "dependencies": { "@aws-sdk/core": "3.969.0", "@aws-sdk/types": "3.969.0", "@smithy/property-provider": "^4.2.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-yS96heH5XDUqS3qQNcdObKKMOqZaivuNInMVRpRli48aXW8fX1M3fY67K/Onlqa3Wxu6WfDc3ZGF52SywdLvbg=="], - "@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.970.0", "", { "dependencies": { "@aws-sdk/core": "3.970.0", "@aws-sdk/types": "3.969.0", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/node-http-handler": "^4.4.8", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.10.8", "@smithy/types": "^4.12.0", "@smithy/util-stream": "^4.5.10", "tslib": "^2.6.2" } }, "sha512-CjDbWL7JxjLc9ZxQilMusWSw05yRvUJKRpz59IxDpWUnSMHC9JMMUUkOy5Izk8UAtzi6gupRWArp4NG4labt9Q=="], + "@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.969.0", "", { "dependencies": { "@aws-sdk/core": "3.969.0", "@aws-sdk/types": "3.969.0", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/node-http-handler": "^4.4.8", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.10.7", "@smithy/types": "^4.12.0", "@smithy/util-stream": "^4.5.10", "tslib": "^2.6.2" } }, "sha512-QCEFxBiUYFUW5VG6k8jKhT4luZndpC7uUY4u1olwt+OnJrl3N2yC7oS34isVBa3ioXZ4A0YagbXTa/3mXUhlAA=="], - "@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.971.0", "", { "dependencies": { "@aws-sdk/core": "3.970.0", "@aws-sdk/credential-provider-env": "3.970.0", "@aws-sdk/credential-provider-http": "3.970.0", "@aws-sdk/credential-provider-login": "3.971.0", "@aws-sdk/credential-provider-process": "3.970.0", "@aws-sdk/credential-provider-sso": "3.971.0", "@aws-sdk/credential-provider-web-identity": "3.971.0", "@aws-sdk/nested-clients": "3.971.0", "@aws-sdk/types": "3.969.0", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-c0TGJG4xyfTZz3SInXfGU8i5iOFRrLmy4Bo7lMyH+IpngohYMYGYl61omXqf2zdwMbDv+YJ9AviQTcCaEUKi8w=="], + "@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.969.0", "", { "dependencies": { "@aws-sdk/core": "3.969.0", "@aws-sdk/credential-provider-env": "3.969.0", "@aws-sdk/credential-provider-http": "3.969.0", "@aws-sdk/credential-provider-login": "3.969.0", "@aws-sdk/credential-provider-process": "3.969.0", "@aws-sdk/credential-provider-sso": "3.969.0", "@aws-sdk/credential-provider-web-identity": "3.969.0", "@aws-sdk/nested-clients": "3.969.0", "@aws-sdk/types": "3.969.0", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-lsXyTDkUrZPxjr0XruZrqdcHY9zHcIuoY3TOCQEm23VTc8Np2BenTtjGAIexkL3ar69K4u3FVLQroLpmFxeXqA=="], - "@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.971.0", "", { "dependencies": { "@aws-sdk/core": "3.970.0", "@aws-sdk/nested-clients": "3.971.0", "@aws-sdk/types": "3.969.0", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-yhbzmDOsk0RXD3rTPhZra4AWVnVAC4nFWbTp+sUty1hrOPurUmhuz8bjpLqYTHGnlMbJp+UqkQONhS2+2LzW2g=="], + "@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.969.0", "", { "dependencies": { "@aws-sdk/core": "3.969.0", "@aws-sdk/nested-clients": "3.969.0", "@aws-sdk/types": "3.969.0", "@smithy/property-provider": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-bIRFDf54qIUFFLTZNYt40d6EseNeK9w80dHEs7BVEAWoS23c9+MSqkdg/LJBBK9Kgy01vRmjiedfBZN+jGypLw=="], - "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.971.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.970.0", "@aws-sdk/credential-provider-http": "3.970.0", "@aws-sdk/credential-provider-ini": "3.971.0", "@aws-sdk/credential-provider-process": "3.970.0", "@aws-sdk/credential-provider-sso": "3.971.0", "@aws-sdk/credential-provider-web-identity": "3.971.0", "@aws-sdk/types": "3.969.0", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-epUJBAKivtJqalnEBRsYIULKYV063o/5mXNJshZfyvkAgNIzc27CmmKRXTN4zaNOZg8g/UprFp25BGsi19x3nQ=="], + "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.969.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.969.0", "@aws-sdk/credential-provider-http": "3.969.0", "@aws-sdk/credential-provider-ini": "3.969.0", "@aws-sdk/credential-provider-process": "3.969.0", "@aws-sdk/credential-provider-sso": "3.969.0", "@aws-sdk/credential-provider-web-identity": "3.969.0", "@aws-sdk/types": "3.969.0", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-lImMjcy/5SGDIBk7PFJCqFO4rFuapKCvo1z2PidD3Cbz2D7wsJnyqUNQIp5Ix0Xc3/uAYG9zXI9kgaMf1dspIQ=="], - "@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.970.0", "", { "dependencies": { "@aws-sdk/core": "3.970.0", "@aws-sdk/types": "3.969.0", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-0XeT8OaT9iMA62DFV9+m6mZfJhrD0WNKf4IvsIpj2Z7XbaYfz3CoDDvNoALf3rPY9NzyMHgDxOspmqdvXP00mw=="], + "@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.969.0", "", { "dependencies": { "@aws-sdk/core": "3.969.0", "@aws-sdk/types": "3.969.0", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-2qQkM0rwd8Hl9nIHtUaqT8Z/djrulovqx/wBHsbRKaISwc2fiT3De1Lk1jx34Jzrz/dTHAMJJi+cML1N4Lk3kw=="], - "@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.971.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.971.0", "@aws-sdk/core": "3.970.0", "@aws-sdk/token-providers": "3.971.0", "@aws-sdk/types": "3.969.0", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-dY0hMQ7dLVPQNJ8GyqXADxa9w5wNfmukgQniLxGVn+dMRx3YLViMp5ZpTSQpFhCWNF0oKQrYAI5cHhUJU1hETw=="], + "@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.969.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.969.0", "@aws-sdk/core": "3.969.0", "@aws-sdk/token-providers": "3.969.0", "@aws-sdk/types": "3.969.0", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-JHqXw9Ct3dtZB86/zGFJYWyodr961GyIrqTBhV0brrZFPvcinM9abDSK58jt6GNBM2lqfMCvXL6I4ahNsMdkrg=="], - "@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.971.0", "", { "dependencies": { "@aws-sdk/core": "3.970.0", "@aws-sdk/nested-clients": "3.971.0", "@aws-sdk/types": "3.969.0", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-F1AwfNLr7H52T640LNON/h34YDiMuIqW/ZreGzhRR6vnFGaSPtNSKAKB2ssAMkLM8EVg8MjEAYD3NCUiEo+t/w=="], + "@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.969.0", "", { "dependencies": { "@aws-sdk/core": "3.969.0", "@aws-sdk/nested-clients": "3.969.0", "@aws-sdk/types": "3.969.0", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-mKCZtqrs3ts3YmIjT4NFlYgT2Oe6syW0nX5m2l7iyrFrLXw26Zo3rx29DjGzycPdJHZZvsIy5y6yqChDuF65ng=="], - "@aws-sdk/credential-providers": ["@aws-sdk/credential-providers@3.971.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.971.0", "@aws-sdk/core": "3.970.0", "@aws-sdk/credential-provider-cognito-identity": "3.971.0", "@aws-sdk/credential-provider-env": "3.970.0", "@aws-sdk/credential-provider-http": "3.970.0", "@aws-sdk/credential-provider-ini": "3.971.0", "@aws-sdk/credential-provider-login": "3.971.0", "@aws-sdk/credential-provider-node": "3.971.0", "@aws-sdk/credential-provider-process": "3.970.0", "@aws-sdk/credential-provider-sso": "3.971.0", "@aws-sdk/credential-provider-web-identity": "3.971.0", "@aws-sdk/nested-clients": "3.971.0", "@aws-sdk/types": "3.969.0", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.20.6", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-778AT0QSdwUOqsVCrNNRHUiA/yNTvjGMIC1rUGcZGqJQmt7jTQq5W+1nHxcCy9zQkk9N9eUNx7tSCbvbyYoyVA=="], + "@aws-sdk/credential-providers": ["@aws-sdk/credential-providers@3.969.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.969.0", "@aws-sdk/core": "3.969.0", "@aws-sdk/credential-provider-cognito-identity": "3.969.0", "@aws-sdk/credential-provider-env": "3.969.0", "@aws-sdk/credential-provider-http": "3.969.0", "@aws-sdk/credential-provider-ini": "3.969.0", "@aws-sdk/credential-provider-login": "3.969.0", "@aws-sdk/credential-provider-node": "3.969.0", "@aws-sdk/credential-provider-process": "3.969.0", "@aws-sdk/credential-provider-sso": "3.969.0", "@aws-sdk/credential-provider-web-identity": "3.969.0", "@aws-sdk/nested-clients": "3.969.0", "@aws-sdk/types": "3.969.0", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.20.5", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-VTbLK8N34DyG0NprD7K7/atf2pMTaWKnQg59NNhlPDkagwv32rQBUVSUQqCTJ3kshzS96oH3FpGKd22uCPp3Tg=="], "@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.969.0", "", { "dependencies": { "@aws-sdk/types": "3.969.0", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-AWa4rVsAfBR4xqm7pybQ8sUNJYnjyP/bJjfAw34qPuh3M9XrfGbAHG0aiAfQGrBnmS28jlO6Kz69o+c6PRw1dw=="], @@ -402,23 +408,23 @@ "@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.969.0", "", { "dependencies": { "@aws-sdk/types": "3.969.0", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-2r3PuNquU3CcS1Am4vn/KHFwLi8QFjMdA/R+CRDXT4AFO/0qxevF/YStW3gAKntQIgWgQV8ZdEtKAoJvLI4UWg=="], - "@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.970.0", "", { "dependencies": { "@aws-sdk/core": "3.970.0", "@aws-sdk/types": "3.969.0", "@aws-sdk/util-endpoints": "3.970.0", "@smithy/core": "^3.20.6", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-dnSJGGUGSFGEX2NzvjwSefH+hmZQ347AwbLhAsi0cdnISSge+pcGfOFrJt2XfBIypwFe27chQhlfuf/gWdzpZg=="], + "@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.969.0", "", { "dependencies": { "@aws-sdk/core": "3.969.0", "@aws-sdk/types": "3.969.0", "@aws-sdk/util-endpoints": "3.969.0", "@smithy/core": "^3.20.5", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-Y6WkW8QQ2X9jG9HNBWyzp5KlJOCtLqX8VIvGLoGc2wXdZH7dgOy62uFhkfnHbgfiel6fkNYaycjGx/yyxi0JLQ=="], - "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.971.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.970.0", "@aws-sdk/middleware-host-header": "3.969.0", "@aws-sdk/middleware-logger": "3.969.0", "@aws-sdk/middleware-recursion-detection": "3.969.0", "@aws-sdk/middleware-user-agent": "3.970.0", "@aws-sdk/region-config-resolver": "3.969.0", "@aws-sdk/types": "3.969.0", "@aws-sdk/util-endpoints": "3.970.0", "@aws-sdk/util-user-agent-browser": "3.969.0", "@aws-sdk/util-user-agent-node": "3.971.0", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.20.6", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.7", "@smithy/middleware-retry": "^4.4.23", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.8", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.10.8", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.22", "@smithy/util-defaults-mode-node": "^4.2.25", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-TWaILL8GyYlhGrxxnmbkazM4QsXatwQgoWUvo251FXmUOsiXDFDVX3hoGIfB3CaJhV2pJPfebHUNJtY6TjZ11g=="], + "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.969.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.969.0", "@aws-sdk/middleware-host-header": "3.969.0", "@aws-sdk/middleware-logger": "3.969.0", "@aws-sdk/middleware-recursion-detection": "3.969.0", "@aws-sdk/middleware-user-agent": "3.969.0", "@aws-sdk/region-config-resolver": "3.969.0", "@aws-sdk/types": "3.969.0", "@aws-sdk/util-endpoints": "3.969.0", "@aws-sdk/util-user-agent-browser": "3.969.0", "@aws-sdk/util-user-agent-node": "3.969.0", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.20.5", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.6", "@smithy/middleware-retry": "^4.4.22", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.8", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.10.7", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.21", "@smithy/util-defaults-mode-node": "^4.2.24", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-MJrejgODxVYZjQjSpPLJkVuxnbrue1x1R8+as3anT5V/wk9Qc/Pf5B1IFjM3Ak6uOtzuRYNY4auOvcg4U8twDA=="], "@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.969.0", "", { "dependencies": { "@aws-sdk/types": "3.969.0", "@smithy/config-resolver": "^4.4.6", "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-scj9OXqKpcjJ4jsFLtqYWz3IaNvNOQTFFvEY8XMJXTv+3qF5I7/x9SJtKzTRJEBF3spjzBUYPtGFbs9sj4fisQ=="], - "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.971.0", "", { "dependencies": { "@aws-sdk/core": "3.970.0", "@aws-sdk/nested-clients": "3.971.0", "@aws-sdk/types": "3.969.0", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-4hKGWZbmuDdONMJV0HJ+9jwTDb0zLfKxcCLx2GEnBY31Gt9GeyIQ+DZ97Bb++0voawj6pnZToFikXTyrEq2x+w=="], + "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.969.0", "", { "dependencies": { "@aws-sdk/core": "3.969.0", "@aws-sdk/nested-clients": "3.969.0", "@aws-sdk/types": "3.969.0", "@smithy/property-provider": "^4.2.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-ucs6QczPkvGinbGmhMlPCQnagGJ+xsM6itsSWlJzxo9YsP6jR75cBU8pRdaM7nEbtCDnrUHf8W9g3D2Hd9mgVA=="], "@aws-sdk/types": ["@aws-sdk/types@3.969.0", "", { "dependencies": { "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-7IIzM5TdiXn+VtgPdVLjmE6uUBUtnga0f4RiSEI1WW10RPuNvZ9U+pL3SwDiRDAdoGrOF9tSLJOFZmfuwYuVYQ=="], - "@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.970.0", "", { "dependencies": { "@aws-sdk/types": "3.969.0", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-endpoints": "^3.2.8", "tslib": "^2.6.2" } }, "sha512-TZNZqFcMUtjvhZoZRtpEGQAdULYiy6rcGiXAbLU7e9LSpIYlRqpLa207oMNfgbzlL2PnHko+eVg8rajDiSOYCg=="], + "@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.969.0", "", { "dependencies": { "@aws-sdk/types": "3.969.0", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-endpoints": "^3.2.8", "tslib": "^2.6.2" } }, "sha512-H2x2UwYiA1pHg40jE+OCSc668W9GXRShTiCWy1UPKtZKREbQ63Mgd7NAj+bEMsZUSCdHywqmSsLqKM9IcqQ3Bg=="], "@aws-sdk/util-locate-window": ["@aws-sdk/util-locate-window@3.965.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-qKgO7wAYsXzhwCHhdbaKFyxd83Fgs8/1Ka+jjSPrv2Ll7mB55Wbwlo0kkfMLh993/yEc8aoDIAc1Fz9h4Spi4Q=="], "@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.969.0", "", { "dependencies": { "@aws-sdk/types": "3.969.0", "@smithy/types": "^4.12.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-bpJGjuKmFr0rA6UKUCmN8D19HQFMLXMx5hKBXqBlPFdalMhxJSjcxzX9DbQh0Fn6bJtxCguFmRGOBdQqNOt49g=="], - "@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.971.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.970.0", "@aws-sdk/types": "3.969.0", "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-Eygjo9mFzQYjbGY3MYO6CsIhnTwAMd3WmuFalCykqEmj2r5zf0leWrhPaqvA5P68V5JdGfPYgj7vhNOd6CtRBQ=="], + "@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.969.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.969.0", "@aws-sdk/types": "3.969.0", "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-D11ZuXNXdUMv8XTthMx+LPzkYNQAeQ68FnCTGnFLgLpnR8hVTeZMBBKjQ77wYGzWDk/csHKdCy697gU1On5KjA=="], "@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.969.0", "", { "dependencies": { "@smithy/types": "^4.12.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-BSe4Lx/qdRQQdX8cSSI7Et20vqBspzAjBy8ZmXVoyLkol3y4sXBXzn+BiLtR+oh60ExQn6o2DU4QjdOZbXaKIQ=="], @@ -898,7 +904,7 @@ "@monerium/sdk": ["@monerium/sdk@3.4.4", "", { "dependencies": { "crypto-js": "^4.2.0" } }, "sha512-MiVE/Z4cgoct6U6E9DTdfk/o6vuMAtNV5zg2z49L7tzFMhWgeYFt0SoZtBu1D6Z+cj36Okwikx+mKWKpJP4fzQ=="], - "@mongodb-js/saslprep": ["@mongodb-js/saslprep@1.4.5", "", { "dependencies": { "sparse-bitfield": "^3.0.3" } }, "sha512-k64Lbyb7ycCSXHSLzxVdb2xsKGPMvYZfCICXvDsI8Z65CeWQzTEKS4YmGbnqw+U9RBvLPTsB6UCmwkgsDTGWIw=="], + "@mongodb-js/saslprep": ["@mongodb-js/saslprep@1.4.4", "", { "dependencies": { "sparse-bitfield": "^3.0.3" } }, "sha512-p7X/ytJDIdwUfFL/CLOhKgdfJe1Fa8uw9seJYvdOmnP9JBWGWHW69HkOixXS6Wy9yvGf1MbhcS6lVmrhy4jm2g=="], "@motionone/animation": ["@motionone/animation@10.18.0", "", { "dependencies": { "@motionone/easing": "^10.18.0", "@motionone/types": "^10.17.1", "@motionone/utils": "^10.18.0", "tslib": "^2.3.1" } }, "sha512-9z2p5GFGCm0gBsZbi8rVMOAJCtw1WqBTIPw3ozk06gDvZInBPIsQcHgYogEJ4yuHJ+akuW8g1SEIOpTOvYs8hw=="], @@ -1154,55 +1160,55 @@ "@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="], - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.55.2", "", { "os": "android", "cpu": "arm" }, "sha512-21J6xzayjy3O6NdnlO6aXi/urvSRjm6nCI6+nF6ra2YofKruGixN9kfT+dt55HVNwfDmpDHJcaS3JuP/boNnlA=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.55.1", "", { "os": "android", "cpu": "arm" }, "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg=="], - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.55.2", "", { "os": "android", "cpu": "arm64" }, "sha512-eXBg7ibkNUZ+sTwbFiDKou0BAckeV6kIigK7y5Ko4mB/5A1KLhuzEKovsmfvsL8mQorkoincMFGnQuIT92SKqA=="], + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.55.1", "", { "os": "android", "cpu": "arm64" }, "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg=="], - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.55.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-UCbaTklREjrc5U47ypLulAgg4njaqfOVLU18VrCrI+6E5MQjuG0lSWaqLlAJwsD7NpFV249XgB0Bi37Zh5Sz4g=="], + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.55.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg=="], - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.55.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-dP67MA0cCMHFT2g5XyjtpVOtp7y4UyUxN3dhLdt11at5cPKnSm4lY+EhwNvDXIMzAMIo2KU+mc9wxaAQJTn7sQ=="], + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.55.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ=="], - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.55.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-WDUPLUwfYV9G1yxNRJdXcvISW15mpvod1Wv3ok+Ws93w1HjIVmCIFxsG2DquO+3usMNCpJQ0wqO+3GhFdl6Fow=="], + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.55.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg=="], - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.55.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Ng95wtHVEulRwn7R0tMrlUuiLVL/HXA8Lt/MYVpy88+s5ikpntzZba1qEulTuPnPIZuOPcW9wNEiqvZxZmgmqQ=="], + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.55.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw=="], - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.55.2", "", { "os": "linux", "cpu": "arm" }, "sha512-AEXMESUDWWGqD6LwO/HkqCZgUE1VCJ1OhbvYGsfqX2Y6w5quSXuyoy/Fg3nRqiwro+cJYFxiw5v4kB2ZDLhxrw=="], + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.55.1", "", { "os": "linux", "cpu": "arm" }, "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ=="], - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.55.2", "", { "os": "linux", "cpu": "arm" }, "sha512-ZV7EljjBDwBBBSv570VWj0hiNTdHt9uGznDtznBB4Caj3ch5rgD4I2K1GQrtbvJ/QiB+663lLgOdcADMNVC29Q=="], + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.55.1", "", { "os": "linux", "cpu": "arm" }, "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg=="], - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.55.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-uvjwc8NtQVPAJtq4Tt7Q49FOodjfbf6NpqXyW/rjXoV+iZ3EJAHLNAnKT5UJBc6ffQVgmXTUL2ifYiLABlGFqA=="], + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.55.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ=="], - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.55.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-s3KoWVNnye9mm/2WpOZ3JeUiediUVw6AvY/H7jNA6qgKA2V2aM25lMkVarTDfiicn/DLq3O0a81jncXszoyCFA=="], + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.55.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA=="], - "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.55.2", "", { "os": "linux", "cpu": "none" }, "sha512-gi21faacK+J8aVSyAUptML9VQN26JRxe484IbF+h3hpG+sNVoMXPduhREz2CcYr5my0NE3MjVvQ5bMKX71pfVA=="], + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g=="], - "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.55.2", "", { "os": "linux", "cpu": "none" }, "sha512-qSlWiXnVaS/ceqXNfnoFZh4IiCA0EwvCivivTGbEu1qv2o+WTHpn1zNmCTAoOG5QaVr2/yhCoLScQtc/7RxshA=="], + "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw=="], - "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.55.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-rPyuLFNoF1B0+wolH277E780NUKf+KoEDb3OyoLbAO18BbeKi++YN6gC/zuJoPPDlQRL3fIxHxCxVEWiem2yXw=="], + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.55.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw=="], - "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.55.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-g+0ZLMook31iWV4PvqKU0i9E78gaZgYpSrYPed/4Bu+nGTgfOPtfs1h11tSSRPXSjC5EzLTjV/1A7L2Vr8pJoQ=="], + "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.55.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw=="], - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.55.2", "", { "os": "linux", "cpu": "none" }, "sha512-i+sGeRGsjKZcQRh3BRfpLsM3LX3bi4AoEVqmGDyc50L6KfYsN45wVCSz70iQMwPWr3E5opSiLOwsC9WB4/1pqg=="], + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw=="], - "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.55.2", "", { "os": "linux", "cpu": "none" }, "sha512-C1vLcKc4MfFV6I0aWsC7B2Y9QcsiEcvKkfxprwkPfLaN8hQf0/fKHwSF2lcYzA9g4imqnhic729VB9Fo70HO3Q=="], + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg=="], - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.55.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-68gHUK/howpQjh7g7hlD9DvTTt4sNLp1Bb+Yzw2Ki0xvscm2cOdCLZNJNhd2jW8lsTPrHAHuF751BygifW4bkQ=="], + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.55.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg=="], - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.55.2", "", { "os": "linux", "cpu": "x64" }, "sha512-1e30XAuaBP1MAizaOBApsgeGZge2/Byd6wV4a8oa6jPdHELbRHBiw7wvo4dp7Ie2PE8TZT4pj9RLGZv9N4qwlw=="], + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.55.1", "", { "os": "linux", "cpu": "x64" }, "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg=="], - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.55.2", "", { "os": "linux", "cpu": "x64" }, "sha512-4BJucJBGbuGnH6q7kpPqGJGzZnYrpAzRd60HQSt3OpX/6/YVgSsJnNzR8Ot74io50SeVT4CtCWe/RYIAymFPwA=="], + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.55.1", "", { "os": "linux", "cpu": "x64" }, "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w=="], - "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.55.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-cT2MmXySMo58ENv8p6/O6wI/h/gLnD3D6JoajwXFZH6X9jz4hARqUhWpGuQhOgLNXscfZYRQMJvZDtWNzMAIDw=="], + "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.55.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg=="], - "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.55.2", "", { "os": "none", "cpu": "arm64" }, "sha512-sZnyUgGkuzIXaK3jNMPmUIyJrxu/PjmATQrocpGA1WbCPX8H5tfGgRSuYtqBYAvLuIGp8SPRb1O4d1Fkb5fXaQ=="], + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.55.1", "", { "os": "none", "cpu": "arm64" }, "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw=="], - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.55.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-sDpFbenhmWjNcEbBcoTV0PWvW5rPJFvu+P7XoTY0YLGRupgLbFY0XPfwIbJOObzO7QgkRDANh65RjhPmgSaAjQ=="], + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.55.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g=="], - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.55.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-GvJ03TqqaweWCigtKQVBErw2bEhu1tyfNQbarwr94wCGnczA9HF8wqEe3U/Lfu6EdeNP0p6R+APeHVwEqVxpUQ=="], + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.55.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA=="], - "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.55.2", "", { "os": "win32", "cpu": "x64" }, "sha512-KvXsBvp13oZz9JGe5NYS7FNizLe99Ny+W8ETsuCyjXiKdiGrcz2/J/N8qxZ/RSwivqjQguug07NLHqrIHrqfYw=="], + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.55.1", "", { "os": "win32", "cpu": "x64" }, "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg=="], - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.55.2", "", { "os": "win32", "cpu": "x64" }, "sha512-xNO+fksQhsAckRtDSPWaMeT1uIM+JrDRXlerpnWNXhn1TdB3YZ6uKBMBTKP0eX9XtYEP978hHk1f8332i2AW8Q=="], + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.55.1", "", { "os": "win32", "cpu": "x64" }, "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw=="], "@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=="], @@ -1280,7 +1286,7 @@ "@smithy/config-resolver": ["@smithy/config-resolver@4.4.6", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "tslib": "^2.6.2" } }, "sha512-qJpzYC64kaj3S0fueiu3kXm8xPrR3PcXDPEgnaNMRn0EjNSZFoFjvbUp0YUDsRhN1CB90EnHJtbxWKevnH99UQ=="], - "@smithy/core": ["@smithy/core@3.20.7", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.9", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-stream": "^4.5.10", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-aO7jmh3CtrmPsIJxUwYIzI5WVlMK8BMCPQ4D4nTzqTqBhbzvxHNzBMGcEg13yg/z9R2Qsz49NUFl0F0lVbTVFw=="], + "@smithy/core": ["@smithy/core@3.20.5", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.9", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-stream": "^4.5.10", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-0Tz77Td8ynHaowXfOdrD0F1IH4tgWGUhwmLwmpFyTbr+U9WHXNNp9u/k2VjBXGnSe7BwjBERRpXsokGTXzNjhA=="], "@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.8", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "tslib": "^2.6.2" } }, "sha512-FNT0xHS1c/CPN8upqbMFP83+ul5YgdisfCfkZ86Jh2NSmnqw/AJ6x5pEogVCTVvSm7j9MopRU89bmDelxuDMYw=="], @@ -1294,9 +1300,9 @@ "@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.8", "", { "dependencies": { "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-RO0jeoaYAB1qBRhfVyq0pMgBoUK34YEJxVxyjOWYZiOKOq2yMZ4MnVXMZCUDenpozHue207+9P5ilTV1zeda0A=="], - "@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.8", "", { "dependencies": { "@smithy/core": "^3.20.7", "@smithy/middleware-serde": "^4.2.9", "@smithy/node-config-provider": "^4.3.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-middleware": "^4.2.8", "tslib": "^2.6.2" } }, "sha512-TV44qwB/T0OMMzjIuI+JeS0ort3bvlPJ8XIH0MSlGADraXpZqmyND27ueuAL3E14optleADWqtd7dUgc2w+qhQ=="], + "@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.6", "", { "dependencies": { "@smithy/core": "^3.20.5", "@smithy/middleware-serde": "^4.2.9", "@smithy/node-config-provider": "^4.3.8", "@smithy/shared-ini-file-loader": "^4.4.3", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-middleware": "^4.2.8", "tslib": "^2.6.2" } }, "sha512-dpq3bHqbEOBqGBjRVHVFP3eUSPpX0BYtg1D5d5Irgk6orGGAuZfY22rC4sErhg+ZfY/Y0kPqm1XpAmDZg7DeuA=="], - "@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.24", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.8", "@smithy/protocol-http": "^5.3.8", "@smithy/service-error-classification": "^4.2.8", "@smithy/smithy-client": "^4.10.9", "@smithy/types": "^4.12.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-yiUY1UvnbUFfP5izoKLtfxDSTRv724YRRwyiC/5HYY6vdsVDcDOXKSXmkJl/Hovcxt5r+8tZEUAdrOaCJwrl9Q=="], + "@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.22", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.8", "@smithy/protocol-http": "^5.3.8", "@smithy/service-error-classification": "^4.2.8", "@smithy/smithy-client": "^4.10.7", "@smithy/types": "^4.12.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-vwWDMaObSMjw6WCC/3Ae9G7uul5Sk95jr07CDk1gkIMpaDic0phPS1MpVAZ6+YkF7PAzRlpsDjxPwRlh/S11FQ=="], "@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.9", "", { "dependencies": { "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ=="], @@ -1320,7 +1326,7 @@ "@smithy/signature-v4": ["@smithy/signature-v4@5.3.8", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.8", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-6A4vdGj7qKNRF16UIcO8HhHjKW27thsxYci+5r/uVRkdcBEkOEiY8OMPuydLX4QHSrJqGHPJzPRwwVTqbLZJhg=="], - "@smithy/smithy-client": ["@smithy/smithy-client@4.10.9", "", { "dependencies": { "@smithy/core": "^3.20.7", "@smithy/middleware-endpoint": "^4.4.8", "@smithy/middleware-stack": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "@smithy/util-stream": "^4.5.10", "tslib": "^2.6.2" } }, "sha512-Je0EvGXVJ0Vrrr2lsubq43JGRIluJ/hX17aN/W/A0WfE+JpoMdI8kwk2t9F0zTX9232sJDGcoH4zZre6m6f/sg=="], + "@smithy/smithy-client": ["@smithy/smithy-client@4.10.7", "", { "dependencies": { "@smithy/core": "^3.20.5", "@smithy/middleware-endpoint": "^4.4.6", "@smithy/middleware-stack": "^4.2.8", "@smithy/protocol-http": "^5.3.8", "@smithy/types": "^4.12.0", "@smithy/util-stream": "^4.5.10", "tslib": "^2.6.2" } }, "sha512-Uznt0I9z3os3Z+8pbXrOSCTXCA6vrjyN7Ub+8l2pRDum44vLv8qw0qGVkJN0/tZBZotaEFHrDPKUoPNueTr5Vg=="], "@smithy/types": ["@smithy/types@4.12.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw=="], @@ -1336,9 +1342,9 @@ "@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q=="], - "@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.23", "", { "dependencies": { "@smithy/property-provider": "^4.2.8", "@smithy/smithy-client": "^4.10.9", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-mMg+r/qDfjfF/0psMbV4zd7F/i+rpyp7Hjh0Wry7eY15UnzTEId+xmQTGDU8IdZtDfbGQxuWNfgBZKBj+WuYbA=="], + "@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.21", "", { "dependencies": { "@smithy/property-provider": "^4.2.8", "@smithy/smithy-client": "^4.10.7", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-DtmVJarzqtjghtGjCw/PFJolcJkP7GkZgy+hWTAN3YLXNH+IC82uMoMhFoC3ZtIz5mOgCm5+hOGi1wfhVYgrxw=="], - "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.26", "", { "dependencies": { "@smithy/config-resolver": "^4.4.6", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/smithy-client": "^4.10.9", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-EQqe/WkbCinah0h1lMWh9ICl0Ob4lyl20/10WTB35SC9vDQfD8zWsOT+x2FIOXKAoZQ8z/y0EFMoodbcqWJY/w=="], + "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.24", "", { "dependencies": { "@smithy/config-resolver": "^4.4.6", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/smithy-client": "^4.10.7", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-JelBDKPAVswVY666rezBvY6b0nF/v9TXjUbNwDNAyme7qqKYEX687wJv0uze8lBIZVbg30wlWnlYfVSjjpKYFA=="], "@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.8", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-8JaVTn3pBDkhZgHQ8R0epwWt+BqPSLCjdjXXusK1onwJlRuN69fbvSK66aIKKO7SwVFM6x2J2ox5X8pOaWcUEw=="], @@ -1490,17 +1496,17 @@ "@substrate/ss58-registry": ["@substrate/ss58-registry@1.51.0", "", {}, "sha512-TWDurLiPxndFgKjVavCniytBIw+t4ViOi7TYp9h/D0NMmkEc9klFTo+827eyEJ0lELpqO207Ey7uGxUa+BS1jQ=="], - "@supabase/auth-js": ["@supabase/auth-js@2.91.0", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-9ywvsKLsxTwv7fvN5fXzP3UfRreqrX2waylTBDu0lkmeHXa8WtSQS9e0WV9FBduiazYqQbgfBQXBNPRPsRgWOQ=="], + "@supabase/auth-js": ["@supabase/auth-js@2.90.1", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-vxb66dgo6h3yyPbR06735Ps+dK3hj0JwS8w9fdQPVZQmocSTlKUW5MfxSy99mN0XqCCuLMQ3jCEiIIUU23e9ng=="], - "@supabase/functions-js": ["@supabase/functions-js@2.91.0", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-WaakXOqLK1mLtBNFXp5o5T+LlI6KZuADSeXz+9ofPRG5OpVSvW148LVJB1DRZ16Phck1a0YqIUswOUgxCz6vMw=="], + "@supabase/functions-js": ["@supabase/functions-js@2.90.1", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-x9mV9dF1Lam9qL3zlpP6mSM5C9iqMPtF5B/tU1Jj/F0ufX5mjDf9ghVBaErVxmrQJRL4+iMKWKY2GnODkpS8tw=="], - "@supabase/postgrest-js": ["@supabase/postgrest-js@2.91.0", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-5S41zv2euNpGucvtM4Wy+xOmLznqt/XO+Lh823LOFEQ00ov7QJfvqb6VzIxufvzhooZpmGR0BxvMcJtWxCIFdQ=="], + "@supabase/postgrest-js": ["@supabase/postgrest-js@2.90.1", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-jh6vqzaYzoFn3raaC0hcFt9h+Bt+uxNRBSdc7PfToQeRGk7PDPoweHsbdiPWREtDVTGKfu+PyPW9e2jbK+BCgQ=="], - "@supabase/realtime-js": ["@supabase/realtime-js@2.91.0", "", { "dependencies": { "@types/phoenix": "^1.6.6", "@types/ws": "^8.18.1", "tslib": "2.8.1", "ws": "^8.18.2" } }, "sha512-u2YuJFG35umw8DO9beC27L/jYXm3KhF+73WQwbynMpV0tXsFIA0DOGRM0NgRyy03hJIdO6mxTTwe8efW3yx3Tg=="], + "@supabase/realtime-js": ["@supabase/realtime-js@2.90.1", "", { "dependencies": { "@types/phoenix": "^1.6.6", "@types/ws": "^8.18.1", "tslib": "2.8.1", "ws": "^8.18.2" } }, "sha512-PWbnEMkcQRuor8jhObp4+Snufkq8C6fBp+MchVp2qBPY1NXk/c3Iv3YyiFYVzo0Dzuw4nAlT4+ahuPggy4r32w=="], - "@supabase/storage-js": ["@supabase/storage-js@2.91.0", "", { "dependencies": { "iceberg-js": "^0.8.1", "tslib": "2.8.1" } }, "sha512-CI7fsVIBQHfNObqU9kmyQ1GWr+Ug44y4rSpvxT4LdQB9tlhg1NTBov6z7Dlmt8d6lGi/8a9lf/epCDxyWI792g=="], + "@supabase/storage-js": ["@supabase/storage-js@2.90.1", "", { "dependencies": { "iceberg-js": "^0.8.1", "tslib": "2.8.1" } }, "sha512-GHY+Ps/K/RBfRj7kwx+iVf2HIdqOS43rM2iDOIDpapyUnGA9CCBFzFV/XvfzznGykd//z2dkGZhlZZprsVFqGg=="], - "@supabase/supabase-js": ["@supabase/supabase-js@2.91.0", "", { "dependencies": { "@supabase/auth-js": "2.91.0", "@supabase/functions-js": "2.91.0", "@supabase/postgrest-js": "2.91.0", "@supabase/realtime-js": "2.91.0", "@supabase/storage-js": "2.91.0" } }, "sha512-Rjb0QqkKrmXMVwUOdEqysPBZ0ZDZakeptTkUa6k2d8r3strBdbWVDqjOdkCjAmvvZMtXecBeyTyMEXD1Zzjfvg=="], + "@supabase/supabase-js": ["@supabase/supabase-js@2.90.1", "", { "dependencies": { "@supabase/auth-js": "2.90.1", "@supabase/functions-js": "2.90.1", "@supabase/postgrest-js": "2.90.1", "@supabase/realtime-js": "2.90.1", "@supabase/storage-js": "2.90.1" } }, "sha512-U8KaKGLUgTIFHtwEW1dgw1gK7XrdpvvYo7nzzqPx721GqPe8WZbAiLh/hmyKLGBYQ/mmQNr20vU9tWSDZpii3w=="], "@swc-node/core": ["@swc-node/core@1.14.1", "", { "peerDependencies": { "@swc/core": ">= 1.13.3", "@swc/types": ">= 0.1" } }, "sha512-jrt5GUaZUU6cmMS+WTJEvGvaB6j1YNKPHPzC2PUi2BjaFbtxURHj6641Az6xN7b665hNniAIdvjxWcRml5yCnw=="], @@ -1510,27 +1516,27 @@ "@swc/cli": ["@swc/cli@0.5.2", "", { "dependencies": { "@swc/counter": "^0.1.3", "@xhmikosr/bin-wrapper": "^13.0.5", "commander": "^8.3.0", "fast-glob": "^3.2.5", "minimatch": "^9.0.3", "piscina": "^4.3.1", "semver": "^7.3.8", "slash": "3.0.0", "source-map": "^0.7.3" }, "peerDependencies": { "@swc/core": "^1.2.66", "chokidar": "^3.5.1" }, "optionalPeers": ["chokidar"], "bin": { "swc": "bin/swc.js", "swcx": "bin/swcx.js", "spack": "bin/spack.js" } }, "sha512-ul2qIqjM5bfe9zWLqFDmHZCf9HXXSZZAlZLe4czn+lH4PewO+OWZnQcYCscnJKlbx6MuWjzXVR7gkspjNEJwJA=="], - "@swc/core": ["@swc/core@1.15.10", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.25" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.15.10", "@swc/core-darwin-x64": "1.15.10", "@swc/core-linux-arm-gnueabihf": "1.15.10", "@swc/core-linux-arm64-gnu": "1.15.10", "@swc/core-linux-arm64-musl": "1.15.10", "@swc/core-linux-x64-gnu": "1.15.10", "@swc/core-linux-x64-musl": "1.15.10", "@swc/core-win32-arm64-msvc": "1.15.10", "@swc/core-win32-ia32-msvc": "1.15.10", "@swc/core-win32-x64-msvc": "1.15.10" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-udNofxftduMUEv7nqahl2nvodCiCDQ4Ge0ebzsEm6P8s0RC2tBM0Hqx0nNF5J/6t9uagFJyWIDjXy3IIWMHDJw=="], + "@swc/core": ["@swc/core@1.15.8", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.25" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.15.8", "@swc/core-darwin-x64": "1.15.8", "@swc/core-linux-arm-gnueabihf": "1.15.8", "@swc/core-linux-arm64-gnu": "1.15.8", "@swc/core-linux-arm64-musl": "1.15.8", "@swc/core-linux-x64-gnu": "1.15.8", "@swc/core-linux-x64-musl": "1.15.8", "@swc/core-win32-arm64-msvc": "1.15.8", "@swc/core-win32-ia32-msvc": "1.15.8", "@swc/core-win32-x64-msvc": "1.15.8" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-T8keoJjXaSUoVBCIjgL6wAnhADIb09GOELzKg10CjNg+vLX48P93SME6jTfte9MZIm5m+Il57H3rTSk/0kzDUw=="], - "@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.15.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-U72pGqmJYbjrLhMndIemZ7u9Q9owcJczGxwtfJlz/WwMaGYAV/g4nkGiUVk/+QSX8sFCAjanovcU1IUsP2YulA=="], + "@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.15.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-M9cK5GwyWWRkRGwwCbREuj6r8jKdES/haCZ3Xckgkl8MUQJZA3XB7IXXK1IXRNeLjg6m7cnoMICpXv1v1hlJOg=="], - "@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.15.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-NZpDXtwHH083L40xdyj1sY31MIwLgOxKfZEAGCI8xHXdHa+GWvEiVdGiu4qhkJctoHFzAEc7ZX3GN5phuJcPuQ=="], + "@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.15.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-j47DasuOvXl80sKJHSi2X25l44CMc3VDhlJwA7oewC1nV1VsSzwX+KOwE5tLnfORvVJJyeiXgJORNYg4jeIjYQ=="], - "@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.15.10", "", { "os": "linux", "cpu": "arm" }, "sha512-ioieF5iuRziUF1HkH1gg1r93e055dAdeBAPGAk40VjqpL5/igPJ/WxFHGvc6WMLhUubSJI4S0AiZAAhEAp1jDg=="], + "@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.15.8", "", { "os": "linux", "cpu": "arm" }, "sha512-siAzDENu2rUbwr9+fayWa26r5A9fol1iORG53HWxQL1J8ym4k7xt9eME0dMPXlYZDytK5r9sW8zEA10F2U3Xwg=="], - "@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.15.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-tD6BClOrxSsNus9cJL7Gxdv7z7Y2hlyvZd9l0NQz+YXzmTWqnfzLpg16ovEI7gknH2AgDBB5ywOsqu8hUgSeEQ=="], + "@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.15.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-o+1y5u6k2FfPYbTRUPvurwzNt5qd0NTumCTFscCNuBksycloXY16J8L+SMW5QRX59n4Hp9EmFa3vpvNHRVv1+Q=="], - "@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.15.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-4uAHO3nbfbrTcmO/9YcVweTQdx5fN3l7ewwl5AEK4yoC4wXmoBTEPHAVdKNe4r9+xrTgd4BgyPsy0409OjjlMw=="], + "@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.15.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-koiCqL09EwOP1S2RShCI7NbsQuG6r2brTqUYE7pV7kZm9O17wZ0LSz22m6gVibpwEnw8jI3IE1yYsQTVpluALw=="], - "@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.15.10", "", { "os": "linux", "cpu": "x64" }, "sha512-W0h9ONNw1pVIA0cN7wtboOSTl4Jk3tHq+w2cMPQudu9/+3xoCxpFb9ZdehwCAk29IsvdWzGzY6P7dDVTyFwoqg=="], + "@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.15.8", "", { "os": "linux", "cpu": "x64" }, "sha512-4p6lOMU3bC+Vd5ARtKJ/FxpIC5G8v3XLoPEZ5s7mLR8h7411HWC/LmTXDHcrSXRC55zvAVia1eldy6zDLz8iFQ=="], - "@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.15.10", "", { "os": "linux", "cpu": "x64" }, "sha512-XQNZlLZB62S8nAbw7pqoqwy91Ldy2RpaMRqdRN3T+tAg6Xg6FywXRKCsLh6IQOadr4p1+lGnqM/Wn35z5a/0Vw=="], + "@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.15.8", "", { "os": "linux", "cpu": "x64" }, "sha512-z3XBnbrZAL+6xDGAhJoN4lOueIxC/8rGrJ9tg+fEaeqLEuAtHSW2QHDHxDwkxZMjuF/pZ6MUTjHjbp8wLbuRLA=="], - "@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.15.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-qnAGrRv5Nj/DATxAmCnJQRXXQqnJwR0trxLndhoHoxGci9MuguNIjWahS0gw8YZFjgTinbTxOwzatkoySihnmw=="], + "@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.15.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-djQPJ9Rh9vP8GTS/Df3hcc6XP6xnG5c8qsngWId/BLA9oX6C7UzCPAn74BG/wGb9a6j4w3RINuoaieJB3t+7iQ=="], - "@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.15.10", "", { "os": "win32", "cpu": "ia32" }, "sha512-i4X/q8QSvzVlaRtv1xfnfl+hVKpCfiJ+9th484rh937fiEZKxZGf51C+uO0lfKDP1FfnT6C1yBYwHy7FLBVXFw=="], + "@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.15.8", "", { "os": "win32", "cpu": "ia32" }, "sha512-/wfAgxORg2VBaUoFdytcVBVCgf1isWZIEXB9MZEUty4wwK93M/PxAkjifOho9RN3WrM3inPLabICRCEgdHpKKQ=="], - "@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.15.10", "", { "os": "win32", "cpu": "x64" }, "sha512-HvY8XUFuoTXn6lSccDLYFlXv1SU/PzYi4PyUqGT++WfTnbw/68N/7BdUZqglGRwiSqr0qhYt/EhmBpULj0J9rA=="], + "@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.15.8", "", { "os": "win32", "cpu": "x64" }, "sha512-GpMePrh9Sl4d61o4KAHOOv5is5+zt6BEXCOCgs/H0FLGeii7j9bWDE8ExvKFy2GRRZVNR1ugsnzaGWHKM6kuzA=="], "@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="], @@ -1576,31 +1582,31 @@ "@talismn/connect-wallets": ["@talismn/connect-wallets@1.2.8", "", { "peerDependencies": { "@polkadot/api": ">=9.3.3", "@polkadot/extension-inject": ">=0.44.6" } }, "sha512-/aniEZxOUNOaOEctHDUb/1jNFgKNsmeQ3L+pm4KvfYqb+C3HjZeBxykHEIc+5xmdR0GgCm30N8QzYWv6voM/lQ=="], - "@tanstack/history": ["@tanstack/history@1.153.2", "", {}, "sha512-TVa0Wju5w6JZGq/S74Q7TQNtKXDatJaB4NYrhMZVU9ETlkgpr35NhDfOzsCJ93P0KCo1ZoDodlFp3c54/dLsyw=="], + "@tanstack/history": ["@tanstack/history@1.145.7", "", {}, "sha512-gMo/ReTUp0a3IOcZoI3hH6PLDC2R/5ELQ7P2yu9F6aEkA0wSQh+Q4qzMrtcKvF2ut0oE+16xWCGDo/TdYd6cEQ=="], - "@tanstack/query-core": ["@tanstack/query-core@5.90.19", "", {}, "sha512-GLW5sjPVIvH491VV1ufddnfldyVB+teCnpPIvweEfkpRx7CfUmUGhoh9cdcUKBh/KwVxk22aNEDxeTsvmyB/WA=="], + "@tanstack/query-core": ["@tanstack/query-core@5.90.17", "", {}, "sha512-hDww+RyyYhjhUfoYQ4es6pbgxY7LNiPWxt4l1nJqhByjndxJ7HIjDxTBtfvMr5HwjYavMrd+ids5g4Rfev3lVQ=="], "@tanstack/query-devtools": ["@tanstack/query-devtools@5.92.0", "", {}, "sha512-N8D27KH1vEpVacvZgJL27xC6yPFUy0Zkezn5gnB3L3gRCxlDeSuiya7fKge8Y91uMTnC8aSxBQhcK6ocY7alpQ=="], - "@tanstack/react-query": ["@tanstack/react-query@5.90.19", "", { "dependencies": { "@tanstack/query-core": "5.90.19" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-qTZRZ4QyTzQc+M0IzrbKHxSeISUmRB3RPGmao5bT+sI6ayxSRhn0FXEnT5Hg3as8SBFcRosrXXRFB+yAcxVxJQ=="], + "@tanstack/react-query": ["@tanstack/react-query@5.90.17", "", { "dependencies": { "@tanstack/query-core": "5.90.17" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-PGc2u9KLwohDUSchjW9MZqeDQJfJDON7y4W7REdNBgiFKxQy+Pf7eGjiFWEj5xPqKzAeHYdAb62IWI1a9UJyGQ=="], "@tanstack/react-query-devtools": ["@tanstack/react-query-devtools@5.91.2", "", { "dependencies": { "@tanstack/query-devtools": "5.92.0" }, "peerDependencies": { "@tanstack/react-query": "^5.90.14", "react": "^18 || ^19" } }, "sha512-ZJ1503ay5fFeEYFUdo7LMNFzZryi6B0Cacrgr2h1JRkvikK1khgIq6Nq2EcblqEdIlgB/r7XDW8f8DQ89RuUgg=="], - "@tanstack/react-router": ["@tanstack/react-router@1.153.2", "", { "dependencies": { "@tanstack/history": "1.153.2", "@tanstack/react-store": "^0.8.0", "@tanstack/router-core": "1.153.2", "isbot": "^5.1.22", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-fAXUBA2gZAId7h2eSHsRcgTeF8pioUz8V5rrQ+IrvA0a6IsxhbTSKLYyqUg4jRDkkcUKtM8StKtvbZCY+0IYWw=="], + "@tanstack/react-router": ["@tanstack/react-router@1.150.0", "", { "dependencies": { "@tanstack/history": "1.145.7", "@tanstack/react-store": "^0.8.0", "@tanstack/router-core": "1.150.0", "isbot": "^5.1.22", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-k/oycTCpBT2XoEk9dNd/nNYhF0X9fLSB10lT40+NVX1TjOtBq5whksk8MT6oRnSoQ8KWeb7La3G9kFaAeSULkA=="], - "@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.153.2", "", { "dependencies": { "@tanstack/router-devtools-core": "1.153.2" }, "peerDependencies": { "@tanstack/react-router": "^1.153.2", "@tanstack/router-core": "^1.153.2", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" }, "optionalPeers": ["@tanstack/router-core"] }, "sha512-LCEuRIyrF0tNKCBspR+TQj13MQ7sTCE4QkkuKAOp30nSdWLxq53bltnGs9bj/V/PTD52JibuAOYyxB94ssWZUA=="], + "@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.150.0", "", { "dependencies": { "@tanstack/router-devtools-core": "1.150.0" }, "peerDependencies": { "@tanstack/react-router": "^1.150.0", "@tanstack/router-core": "^1.150.0", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" }, "optionalPeers": ["@tanstack/router-core"] }, "sha512-TlvTE+XK5XVCfYjazoMWkjyyPKe4kMw2nCA7EuWoYUJKOqRW5oKvBY7auViGWxp51FKDEjV3bbok3wPKBYwZww=="], "@tanstack/react-store": ["@tanstack/react-store@0.8.0", "", { "dependencies": { "@tanstack/store": "0.8.0", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-1vG9beLIuB7q69skxK9r5xiLN3ztzIPfSQSs0GfeqWGO2tGIyInZx0x1COhpx97RKaONSoAb8C3dxacWksm1ow=="], "@tanstack/react-virtual": ["@tanstack/react-virtual@3.13.18", "", { "dependencies": { "@tanstack/virtual-core": "3.13.18" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-dZkhyfahpvlaV0rIKnvQiVoWPyURppl6w4m9IwMDpuIjcJ1sD9YGWrt0wISvgU7ewACXx2Ct46WPgI6qAD4v6A=="], - "@tanstack/router-core": ["@tanstack/router-core@1.153.2", "", { "dependencies": { "@tanstack/history": "1.153.2", "@tanstack/store": "^0.8.0", "cookie-es": "^2.0.0", "seroval": "^1.4.1", "seroval-plugins": "^1.4.0", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-WLaR+rSNW7bj9UCJQ3SKpuh6nZBZkpGnf2mpjn/uRB6joIQ3BU7aRdhb7w9Via/MP52iaHh5sd8NY3MaLpF2tQ=="], + "@tanstack/router-core": ["@tanstack/router-core@1.150.0", "", { "dependencies": { "@tanstack/history": "1.145.7", "@tanstack/store": "^0.8.0", "cookie-es": "^2.0.0", "seroval": "^1.4.1", "seroval-plugins": "^1.4.0", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-cAm44t/tUbfyzaDH+rE/WO4u3AgaZdpJp00xjQ4gNkC2O95ntVHq5fx+4fhtrkKpgdXoKldgk8OK66djiWpuGQ=="], - "@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.153.2", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16", "tiny-invariant": "^1.3.3" }, "peerDependencies": { "@tanstack/router-core": "^1.153.2", "csstype": "^3.0.10" }, "optionalPeers": ["csstype"] }, "sha512-53gFlnz2oUeGvRwu7hzi+jlqm5F5X1XwNniirCTjggsV5P+FVQ7YJ+gfMuN5MHonWmVCLd1QqGkl2nYRTGHeTg=="], + "@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.150.0", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16", "tiny-invariant": "^1.3.3" }, "peerDependencies": { "@tanstack/router-core": "^1.150.0", "csstype": "^3.0.10" }, "optionalPeers": ["csstype"] }, "sha512-61V+4fq2fOPru/48cuojKvWhQx2h/nuj4nVHwzu9E7O8h391h4Hks6axxRbY98/rIz96mn5TCoc0aYuoga53bg=="], - "@tanstack/router-generator": ["@tanstack/router-generator@1.153.2", "", { "dependencies": { "@tanstack/router-core": "1.153.2", "@tanstack/router-utils": "1.143.11", "@tanstack/virtual-file-routes": "1.145.4", "prettier": "^3.5.0", "recast": "^0.23.11", "source-map": "^0.7.4", "tsx": "^4.19.2", "zod": "^3.24.2" } }, "sha512-bEhmCtXq5vv3HukKq5zmTDBNDRqVllYxsHoWtqEvHv5hCb5xwKKfUMGemRoiQ96/wLFuGnA5DYkem2GZWcG3wg=="], + "@tanstack/router-generator": ["@tanstack/router-generator@1.150.0", "", { "dependencies": { "@tanstack/router-core": "1.150.0", "@tanstack/router-utils": "1.143.11", "@tanstack/virtual-file-routes": "1.145.4", "prettier": "^3.5.0", "recast": "^0.23.11", "source-map": "^0.7.4", "tsx": "^4.19.2", "zod": "^3.24.2" } }, "sha512-WsA1bN5/I+cxE6V1DkU5ABIPBQxZLlxszElYgnIhs884tzukv76rYMFOy6Xqd51YIFdYtjDrxZbp4/vfkrVCug=="], - "@tanstack/router-plugin": ["@tanstack/router-plugin@1.153.2", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@tanstack/router-core": "1.153.2", "@tanstack/router-generator": "1.153.2", "@tanstack/router-utils": "1.143.11", "@tanstack/virtual-file-routes": "1.145.4", "babel-dead-code-elimination": "^1.0.11", "chokidar": "^3.6.0", "unplugin": "^2.1.2", "zod": "^3.24.2" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2", "@tanstack/react-router": "^1.153.2", "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0", "vite-plugin-solid": "^2.11.10", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"] }, "sha512-aMMc70ChM0wBYOToq39kTMKI2A0EKWpumiKTJyAwEglXf0raF48+26Fmv0gr9/5CLvD0g8ljllsskVDyzg8oDw=="], + "@tanstack/router-plugin": ["@tanstack/router-plugin@1.150.0", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@tanstack/router-core": "1.150.0", "@tanstack/router-generator": "1.150.0", "@tanstack/router-utils": "1.143.11", "@tanstack/virtual-file-routes": "1.145.4", "babel-dead-code-elimination": "^1.0.11", "chokidar": "^3.6.0", "unplugin": "^2.1.2", "zod": "^3.24.2" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2", "@tanstack/react-router": "^1.150.0", "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0", "vite-plugin-solid": "^2.11.10", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"] }, "sha512-k2NLysBXO4Wpt4Oo0xeBhNtFsMwHOU8ud48/cWNWbV89QAjlk0XU5CGNj2JEaFMT0zlF3H/aM5/h0+vYnDjFFA=="], "@tanstack/router-utils": ["@tanstack/router-utils@1.143.11", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/generator": "^7.28.5", "@babel/parser": "^7.28.5", "ansis": "^4.1.0", "diff": "^8.0.2", "pathe": "^2.0.3", "tinyglobby": "^0.2.15" } }, "sha512-N24G4LpfyK8dOlnP8BvNdkuxg1xQljkyl6PcrdiPSA301pOjatRT1y8wuCCJZKVVD8gkd0MpCZ0VEjRMGILOtA=="], @@ -1610,7 +1616,7 @@ "@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.145.4", "", {}, "sha512-CI75JrfqSluhdGwLssgVeQBaCphgfkMQpi8MCY3UJX1hoGzXa8kHYJcUuIFMOLs1q7zqHy++EVVtMK03osR5wQ=="], - "@tanstack/zod-adapter": ["@tanstack/zod-adapter@1.153.2", "", { "peerDependencies": { "@tanstack/react-router": ">=1.43.2", "zod": "^3.23.8" } }, "sha512-E3OPtB/QSRo9lN8wWbxjvjMfQI4MOKjTEZOrwZiHXXMzlOMYWqxg99eCPeHP8kcXKJ+LdIkf29sZ/0rC62I4aQ=="], + "@tanstack/zod-adapter": ["@tanstack/zod-adapter@1.150.0", "", { "peerDependencies": { "@tanstack/react-router": ">=1.43.2", "zod": "^3.23.8" } }, "sha512-MdkmF6tqxxskG3K3LHVN+PAj8m7kHMhlArDcI4Mi+Fx5CG3BCJcpYGv4aZJhvL0CxHw3N6qyn6SIzG9FraXhWA=="], "@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="], @@ -1618,35 +1624,35 @@ "@testing-library/user-event": ["@testing-library/user-event@14.6.1", "", { "peerDependencies": { "@testing-library/dom": ">=7.21.4" } }, "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw=="], - "@theguild/federation-composition": ["@theguild/federation-composition@0.21.3", "", { "dependencies": { "constant-case": "^3.0.4", "debug": "4.4.3", "json5": "^2.2.3", "lodash.sortby": "^4.7.0" }, "peerDependencies": { "graphql": "^16.0.0" } }, "sha512-+LlHTa4UbRpZBog3ggAxjYIFvdfH3UMvvBUptur19TMWkqU4+n3GmN+mDjejU+dyBXIG27c25RsiQP1HyvM99g=="], + "@theguild/federation-composition": ["@theguild/federation-composition@0.21.2", "", { "dependencies": { "constant-case": "^3.0.4", "debug": "4.4.3", "json5": "^2.2.3", "lodash.sortby": "^4.7.0" }, "peerDependencies": { "graphql": "^16.0.0" } }, "sha512-vkaJrMaG5TXtEzdrbfTZ5IhOot/Ct2aZHEgG4fYmzZa037DpLaic6W1l1NIlFZ7c/gHZS3Wz4Wt3ZTvsDgOAOQ=="], - "@thi.ng/api": ["@thi.ng/api@8.12.12", "", {}, "sha512-cjdAUqqolcR6uAgELXoP4a2DN9Mhu+QEGiVyRPqyNdZ06fYIhkdkiJRFbl0+JnRUw9I3yhVF6C5AxtF60oKzqw=="], + "@thi.ng/api": ["@thi.ng/api@8.12.11", "", {}, "sha512-1Ucknsp1ogvfvDaBxhoIXEIEJDYfZMdBADaHEa6AIp/3n2dGMgWY06NnSvMgL+ED7OfYMkopXPpPUFSiV5nQxg=="], - "@thi.ng/arrays": ["@thi.ng/arrays@2.14.5", "", { "dependencies": { "@thi.ng/api": "^8.12.12", "@thi.ng/checks": "^3.8.2", "@thi.ng/compare": "^2.4.38", "@thi.ng/equiv": "^2.1.102", "@thi.ng/errors": "^2.6.1", "@thi.ng/random": "^4.1.37" } }, "sha512-Oqxsy/hFRNfRP3rmkfliesfsk1xeMeON49QPDJNOLp6lFqNFGjL1pjxTLSRgFxRJRDwLR9i4EbGHMrf/pruPkQ=="], + "@thi.ng/arrays": ["@thi.ng/arrays@2.14.4", "", { "dependencies": { "@thi.ng/api": "^8.12.11", "@thi.ng/checks": "^3.8.1", "@thi.ng/compare": "^2.4.37", "@thi.ng/equiv": "^2.1.101", "@thi.ng/errors": "^2.6.0", "@thi.ng/random": "^4.1.36" } }, "sha512-jWvb8VhvfZQk2csFZ64T6cTdpx2Txx2rfITYFTJwR+w/fd157bhN5fqVBWZ7uqPynWkrYlwJPhZWVjd81A1kMA=="], - "@thi.ng/cache": ["@thi.ng/cache@2.3.61", "", { "dependencies": { "@thi.ng/api": "^8.12.12", "@thi.ng/dcons": "^3.2.180" } }, "sha512-/n8vEq0QGGg+77IK/DdDgP81uoeI/UM79W5jLoeMzeQV1T2/9qRxnTs5BuFo+C7qG+BlOjlfQV9xr3wAF0126g=="], + "@thi.ng/cache": ["@thi.ng/cache@2.3.60", "", { "dependencies": { "@thi.ng/api": "^8.12.11", "@thi.ng/dcons": "^3.2.179" } }, "sha512-M9Qr9UXyaHAnXfFaqYGcJG3gQDBuXpbHErSRfJnzGxE84rYhFhTjN1pYR98y3V/N24T2mTU+kmWgvG+J6OR4Yg=="], - "@thi.ng/checks": ["@thi.ng/checks@3.8.2", "", {}, "sha512-rlFMR1nD69JffzRkifaew/W9hSx4avVIGoiaLWnGEmFSFV3GelXunSLF/YoIflketC1R+dZe7EBau6g/qSDfjg=="], + "@thi.ng/checks": ["@thi.ng/checks@3.8.1", "", {}, "sha512-RZj9o6wOBnSuRyaoEDsekCdtIRo9/kWuGvj5tcAUotzCUBtmLGRcJfDpqEWMYocNrwK8d6TyNMyUS8XJT7nYaQ=="], - "@thi.ng/compare": ["@thi.ng/compare@2.4.38", "", { "dependencies": { "@thi.ng/api": "^8.12.12" } }, "sha512-WvLWxDV2ZTNr/JW49nBc6NGfrdUM0xrqIu8vkY4vm49Dxei7u8IPVpXra1NjrveRe0VyAcQk/TNul4j9usGOjQ=="], + "@thi.ng/compare": ["@thi.ng/compare@2.4.37", "", { "dependencies": { "@thi.ng/api": "^8.12.11" } }, "sha512-Gmd9m+YDE/Dj4MVU/uuv/8EZRVx4GHjj9CCmG10h2gHXD+8nviqdLgpNcHBxynFr0pEG3n/mFn3COgFSsyJC5Q=="], - "@thi.ng/compose": ["@thi.ng/compose@3.0.49", "", { "dependencies": { "@thi.ng/api": "^8.12.12", "@thi.ng/errors": "^2.6.1" } }, "sha512-k7+8FpMSSPZJXNmv2azgxwJbk2eweP1kXG7XVdrfIe/GwUHXLy+MA+wKRsx9aK6849D2SUlFb8r2oEXxZ0bYqg=="], + "@thi.ng/compose": ["@thi.ng/compose@3.0.48", "", { "dependencies": { "@thi.ng/api": "^8.12.11", "@thi.ng/errors": "^2.6.0" } }, "sha512-M53DP4cwvLqtujCC67aS3dIoyuYdRIGJm/QJ/0XqzMqzSobBjAT6knBr7HPw7eg/Y7LM2iqW1o975iPEAgFHxA=="], - "@thi.ng/dcons": ["@thi.ng/dcons@3.2.180", "", { "dependencies": { "@thi.ng/api": "^8.12.12", "@thi.ng/checks": "^3.8.2", "@thi.ng/compare": "^2.4.38", "@thi.ng/equiv": "^2.1.102", "@thi.ng/errors": "^2.6.1", "@thi.ng/random": "^4.1.37", "@thi.ng/transducers": "^9.6.21" } }, "sha512-13YmJkdv2qemtxLEMMnQaR86KCLVHhPJ/8SBJd/VqYpIvzPvsXDJ7fMmFq2CNr+m67kphrfsmnbnz19zNTEcow=="], + "@thi.ng/dcons": ["@thi.ng/dcons@3.2.179", "", { "dependencies": { "@thi.ng/api": "^8.12.11", "@thi.ng/checks": "^3.8.1", "@thi.ng/compare": "^2.4.37", "@thi.ng/equiv": "^2.1.101", "@thi.ng/errors": "^2.6.0", "@thi.ng/random": "^4.1.36", "@thi.ng/transducers": "^9.6.20" } }, "sha512-+bqMx8b1nP5F940kOvnooBNyGm6ii4zYW8CLT+i5Gkvk/A2ALyB2gYJIrw1z6y2B4UaEP9lNQolrMsRT3+c/Cw=="], - "@thi.ng/equiv": ["@thi.ng/equiv@2.1.102", "", {}, "sha512-MwEt+wOpSDoaFlbN4X9U0t+XSCqxzNWfRjlFRPwSZ+A7cmh2CaACG3Pze5HpsI6lXS6Jyhvt/dtjs45tmDpSUQ=="], + "@thi.ng/equiv": ["@thi.ng/equiv@2.1.101", "", {}, "sha512-SESk911KgUkqYWHkNi6EPcTRDG1WoylW/03CteHxUEMKRsQLeaagNwQjgUeIG4gr9onADiaMHuiRU9IaBoLqBw=="], - "@thi.ng/errors": ["@thi.ng/errors@2.6.1", "", {}, "sha512-5kkJ1+JK6OInYMnRXtiJ6qZMt2zNqEuw0ZNwU8bFPfxF3yiWD5tcDNVLwE4EsMm8cGwH1K0h0TI5HIPfHSUWow=="], + "@thi.ng/errors": ["@thi.ng/errors@2.6.0", "", {}, "sha512-wBfSWz81sqM1FWX2ucW9KGLSFzGvsBxwX5y5t6Oty0QWO9mY8JIZC+ZBPzX5E6ExZiuGIgM8NtkTboTnXXlfUQ=="], - "@thi.ng/math": ["@thi.ng/math@5.15.1", "", { "dependencies": { "@thi.ng/api": "^8.12.12" } }, "sha512-/SvlqvxlOetGddcALBSzLnKpr4WpVdIYbpeZmKpRJIA8CzkgSVeRBXx1ypCeXjNayVXSVagT9s6eZRb7JlqQJA=="], + "@thi.ng/math": ["@thi.ng/math@5.15.0", "", { "dependencies": { "@thi.ng/api": "^8.12.11" } }, "sha512-/FTvny5HM7KKsebupp3LBeMkgo+FgJrKnOlawCiSAUzvBPyCUVr6lWvTucf0qCkiAuWW/i32clBdL4i8uuWOXg=="], - "@thi.ng/memoize": ["@thi.ng/memoize@4.0.36", "", { "dependencies": { "@thi.ng/api": "^8.12.12" } }, "sha512-9F2Zvz4VTmBnz7RHNPHy206tElDY108tuJyT8ABz9s2ejFGQvNRl29hA0b9tjleRU1NTyZ+Xdp+tFG1bTcQbFg=="], + "@thi.ng/memoize": ["@thi.ng/memoize@4.0.35", "", { "dependencies": { "@thi.ng/api": "^8.12.11" } }, "sha512-Nf2l03tGdhDta0QS4Xj3IouvbCvvaXbOxFxyHoIRsBCQ7+enlKkJJvNsjSsGK9u9oIURhi7cY0+wysVys4uTuQ=="], - "@thi.ng/random": ["@thi.ng/random@4.1.37", "", { "dependencies": { "@thi.ng/api": "^8.12.12", "@thi.ng/errors": "^2.6.1" } }, "sha512-fq9/8NeZNQyuCHgW2utu4CKRAp6w0kDlfxKaLer9hvRxybDmHsGjPaDfXkqQ+rf+ipnrpcBsi2y7sfgW/jUG9A=="], + "@thi.ng/random": ["@thi.ng/random@4.1.36", "", { "dependencies": { "@thi.ng/api": "^8.12.11", "@thi.ng/errors": "^2.6.0" } }, "sha512-ENhEZa/WZmM8ZDB3UkScDPU7SrppXJghOt8Dm/Q6Y72eM/xnMacvyMdLHzMfQiyCbhqdpKK4Hu2L1ysGYO6YUw=="], - "@thi.ng/timestamp": ["@thi.ng/timestamp@1.1.31", "", {}, "sha512-b4c8JseRfQ8CFWfthqyN/IkC1jJgN2dDnGRGl/zxUry78SevISMJsShsjgX4wT+I9ZWDVkTLHH4GLHRyyDOrng=="], + "@thi.ng/timestamp": ["@thi.ng/timestamp@1.1.30", "", {}, "sha512-Qx6+n2ZrmpYXF68qFOHEY0djO1CzudEiuD2hK78IodMR/rHkKJ5kaFjKo8Xxw43GsKojJJuRfxUjAOPuTuxw+w=="], - "@thi.ng/transducers": ["@thi.ng/transducers@9.6.21", "", { "dependencies": { "@thi.ng/api": "^8.12.12", "@thi.ng/arrays": "^2.14.5", "@thi.ng/checks": "^3.8.2", "@thi.ng/compare": "^2.4.38", "@thi.ng/compose": "^3.0.49", "@thi.ng/errors": "^2.6.1", "@thi.ng/math": "^5.15.1", "@thi.ng/random": "^4.1.37", "@thi.ng/timestamp": "^1.1.31" } }, "sha512-rIZyRrYQml1aZOyRCpLQjyNfcFn1W1yuArPBrCe7cKPMTaB98u97+GJKvr8cn4vNJ3oUhxPfUHhu280dk5HTew=="], + "@thi.ng/transducers": ["@thi.ng/transducers@9.6.20", "", { "dependencies": { "@thi.ng/api": "^8.12.11", "@thi.ng/arrays": "^2.14.4", "@thi.ng/checks": "^3.8.1", "@thi.ng/compare": "^2.4.37", "@thi.ng/compose": "^3.0.48", "@thi.ng/errors": "^2.6.0", "@thi.ng/math": "^5.15.0", "@thi.ng/random": "^4.1.36", "@thi.ng/timestamp": "^1.1.30" } }, "sha512-bC/6QEuAxQRkr2GMyJB1Y1sqYEpTeEuboCMvGSwlXtZGYtSZbTlysTF8wzAcrUFyRPFtvtsWqyxO8dsw1i5K7Q=="], "@tokenizer/inflate": ["@tokenizer/inflate@0.2.7", "", { "dependencies": { "debug": "^4.4.0", "fflate": "^0.8.2", "token-types": "^6.0.0" } }, "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg=="], @@ -1730,7 +1736,7 @@ "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], - "@types/node": ["@types/node@22.19.7", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw=="], + "@types/node": ["@types/node@22.19.6", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-qm+G8HuG6hOHQigsi7VGuLjUVu6TtBo/F05zvX04Mw2uCg9Dv0Qxy3Qw7j41SidlTcl5D/5yg0SEZqOB+EqZnQ=="], "@types/node-forge": ["@types/node-forge@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw=="], @@ -1740,7 +1746,7 @@ "@types/range-parser": ["@types/range-parser@1.2.7", "", {}, "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="], - "@types/react": ["@types/react@19.2.9", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA=="], + "@types/react": ["@types/react@19.2.8", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg=="], "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], @@ -1774,11 +1780,11 @@ "@typescript-eslint/parser": ["@typescript-eslint/parser@5.62.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "5.62.0", "@typescript-eslint/types": "5.62.0", "@typescript-eslint/typescript-estree": "5.62.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA=="], - "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.53.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.53.1", "@typescript-eslint/types": "^8.53.1", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-WYC4FB5Ra0xidsmlPb+1SsnaSKPmS3gsjIARwbEkHkoWloQmuzcfypljaJcR78uyLA1h8sHdWWPHSLDI+MtNog=="], + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.53.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.53.0", "@typescript-eslint/types": "^8.53.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Bl6Gdr7NqkqIP5yP9z1JU///Nmes4Eose6L1HwpuVHwScgDPPuEWbUVhvlZmb8hy0vX9syLk5EGNL700WcBlbg=="], "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@5.62.0", "", { "dependencies": { "@typescript-eslint/types": "5.62.0", "@typescript-eslint/visitor-keys": "5.62.0" } }, "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w=="], - "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.53.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qfvLXS6F6b1y43pnf0pPbXJ+YoXIC7HKg0UGZ27uMIemKMKA6XH2DTxsEDdpdN29D+vHV07x/pnlPNVLhdhWiA=="], + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.53.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-K6Sc0R5GIG6dNoPdOooQ+KtvT5KCKAvTcY8h2rIuul19vxH5OTQk7ArKkd4yTzkw66WnNY0kPPzzcmWA+XRmiA=="], "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@5.62.0", "", { "dependencies": { "@typescript-eslint/typescript-estree": "5.62.0", "@typescript-eslint/utils": "5.62.0", "debug": "^4.3.4", "tsutils": "^3.21.0" }, "peerDependencies": { "eslint": "*" } }, "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew=="], @@ -2036,7 +2042,7 @@ "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], - "baseline-browser-mapping": ["baseline-browser-mapping@2.9.16", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-KeUZdBuxngy825i8xvzaK1Ncnkx0tBmb3k8DkEuqjKRkmtvNTjey2ZsNeh8Dw4lfKvbCOu9oeNx2TKm2vHqcRw=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.9.14", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg=="], "basic-auth": ["basic-auth@2.0.1", "", { "dependencies": { "safe-buffer": "5.1.2" } }, "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg=="], @@ -2134,7 +2140,7 @@ "camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="], - "caniuse-lite": ["caniuse-lite@1.0.30001765", "", {}, "sha512-LWcNtSyZrakjECqmpP4qdg0MMGdN368D7X8XvvAqOcqMv0RxnlqVKZl2V6/mBR68oYMxOZPLw/gO7DuisMHUvQ=="], + "caniuse-lite": ["caniuse-lite@1.0.30001764", "", {}, "sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g=="], "capital-case": ["capital-case@1.0.4", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", "upper-case-first": "^2.0.2" } }, "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A=="], @@ -2160,6 +2166,8 @@ "cipher-base": ["cipher-base@1.0.7", "", { "dependencies": { "inherits": "^2.0.4", "safe-buffer": "^5.2.1", "to-buffer": "^1.2.2" } }, "sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA=="], + "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], + "clean-stack": ["clean-stack@2.2.0", "", {}, "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="], "cli-cursor": ["cli-cursor@3.1.0", "", { "dependencies": { "restore-cursor": "^3.1.0" } }, "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw=="], @@ -2174,7 +2182,7 @@ "clone": ["clone@2.1.2", "", {}, "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="], - "clsx": ["clsx@1.2.1", "", {}, "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg=="], + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], "color": ["color@5.0.3", "", { "dependencies": { "color-convert": "^3.1.3", "color-string": "^2.1.3" } }, "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA=="], @@ -2338,7 +2346,7 @@ "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], - "diff": ["diff@4.0.4", "", {}, "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ=="], + "diff": ["diff@4.0.2", "", {}, "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="], "diffie-hellman": ["diffie-hellman@5.0.3", "", { "dependencies": { "bn.js": "^4.1.0", "miller-rabin": "^4.0.0", "randombytes": "^2.0.0" } }, "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg=="], @@ -2488,7 +2496,7 @@ "eventemitter2": ["eventemitter2@6.4.9", "", {}, "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg=="], - "eventemitter3": ["eventemitter3@5.0.4", "", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="], + "eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="], @@ -2600,7 +2608,7 @@ "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], - "framer-motion": ["framer-motion@12.27.5", "", { "dependencies": { "motion-dom": "^12.27.5", "motion-utils": "^12.27.2", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-yUFof7Y2Y2qDJxLKeA91qMazuA6QBOoLOZ0No2J5VIQuhJLWMmGwT/5qyCfpa9mNNS3C7lOR6NhlC3mLZjLw4g=="], + "framer-motion": ["framer-motion@12.26.2", "", { "dependencies": { "motion-dom": "^12.26.2", "motion-utils": "^12.24.10", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-lflOQEdjquUi9sCg5Y1LrsZDlsjrHw7m0T9Yedvnk7Bnhqfkc89/Uha10J3CFhkL+TCZVCRw9eUGyM/lyYhXQA=="], "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], @@ -2770,6 +2778,8 @@ "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], + "input-otp": ["input-otp@1.4.2", "", { "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-l3jWwYNvrEa6NTCt7BECfCm48GvwuZzkoeG3gBL2w4CHeOXW3eKFmf9UNYkNfYc3mxMrthMnxjIE07MT0zLBQA=="], + "inquirer": ["inquirer@8.2.7", "", { "dependencies": { "@inquirer/external-editor": "^1.0.0", "ansi-escapes": "^4.2.1", "chalk": "^4.1.1", "cli-cursor": "^3.1.0", "cli-width": "^3.0.0", "figures": "^3.0.0", "lodash": "^4.17.21", "mute-stream": "0.0.8", "ora": "^5.4.1", "run-async": "^2.4.0", "rxjs": "^7.5.5", "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6", "wrap-ansi": "^6.0.1" } }, "sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA=="], "inspect-with-kind": ["inspect-with-kind@1.0.5", "", { "dependencies": { "kind-of": "^6.0.2" } }, "sha512-MAQUJuIo7Xqk8EVNP+6d3CKq9c80hi4tjIbIAT6lmGW9W6WzlHiu9PS8uSuUYU+Do+j1baiFp3H25XEVxDIG2g=="], @@ -2878,7 +2888,7 @@ "isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], - "isbot": ["isbot@5.1.33", "", {}, "sha512-P4Hgb5NqswjkI0J1CM6XKXon/sxKY1SuowE7Qx2hrBhIwICFyXy54mfgB5eMHXsbe/eStzzpbIGNOvGmz+dlKg=="], + "isbot": ["isbot@5.1.32", "", {}, "sha512-VNfjM73zz2IBZmdShMfAUg10prm6t7HFUQmNAEOAVS4YH92ZrZcvkMcGX6cIgBJAzWDzPent/EeAtYEHNPNPBQ=="], "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], @@ -3030,6 +3040,8 @@ "lru-queue": ["lru-queue@0.1.0", "", { "dependencies": { "es5-ext": "~0.10.2" } }, "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ=="], + "lucide-react": ["lucide-react@0.562.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-82hOAu7y0dbVuFfmO4bYF1XEwYk/mEbM5E+b1jgci/udUBEE/R7LF5Ip0CCEmXe8AybRM8L+04eP+LGZeDvkiw=="], + "luxon": ["luxon@3.7.2", "", {}, "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew=="], "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], @@ -3118,11 +3130,11 @@ "morgan": ["morgan@1.10.1", "", { "dependencies": { "basic-auth": "~2.0.1", "debug": "2.6.9", "depd": "~2.0.0", "on-finished": "~2.3.0", "on-headers": "~1.1.0" } }, "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A=="], - "motion": ["motion@12.27.5", "", { "dependencies": { "framer-motion": "^12.27.5", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-Am4QS7Nd9+yhAOQSefziBmX0hYtc0HaWbXY5+0r/0J8eBBFf5jXzlBew+v+7i+eNmdVpDagVqwjES8fPYtEayA=="], + "motion": ["motion@12.26.2", "", { "dependencies": { "framer-motion": "^12.26.2", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-2Q6g0zK1gUJKhGT742DAe42LgietcdiJ3L3OcYAHCQaC1UkLnn6aC8S/obe4CxYTLAgid2asS1QdQ/blYfo5dw=="], - "motion-dom": ["motion-dom@12.27.5", "", { "dependencies": { "motion-utils": "^12.27.2" } }, "sha512-UwBv2AUOkA7/TCHr67NGjg3aRT7nbsanmmenRoR7T6IJXZp34OZB+pooGnKjMd8CqqCsF/+qwT657EkukjgmiQ=="], + "motion-dom": ["motion-dom@12.26.2", "", { "dependencies": { "motion-utils": "^12.24.10" } }, "sha512-KLMT1BroY8oKNeliA3JMNJ+nbCIsTKg6hJpDb4jtRAJ7nCKnnpg/LTq/NGqG90Limitz3kdAnAVXecdFVGlWTw=="], - "motion-utils": ["motion-utils@12.27.2", "", {}, "sha512-B55gcoL85Mcdt2IEStY5EEAsrMSVE2sI14xQ/uAdPL+mfQxhKKFaEag9JmfxedJOR4vZpBGoPeC/Gm13I/4g5Q=="], + "motion-utils": ["motion-utils@12.24.10", "", {}, "sha512-x5TFgkCIP4pPsRLpKoI86jv/q8t8FQOiM/0E8QKBzfMozWHfkKap2gA1hOki+B5g3IsBNpxbUnfOum1+dgvYww=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], @@ -3506,7 +3518,7 @@ "ripemd160": ["ripemd160@2.0.3", "", { "dependencies": { "hash-base": "^3.1.2", "inherits": "^2.0.4" } }, "sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA=="], - "rollup": ["rollup@4.55.2", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.55.2", "@rollup/rollup-android-arm64": "4.55.2", "@rollup/rollup-darwin-arm64": "4.55.2", "@rollup/rollup-darwin-x64": "4.55.2", "@rollup/rollup-freebsd-arm64": "4.55.2", "@rollup/rollup-freebsd-x64": "4.55.2", "@rollup/rollup-linux-arm-gnueabihf": "4.55.2", "@rollup/rollup-linux-arm-musleabihf": "4.55.2", "@rollup/rollup-linux-arm64-gnu": "4.55.2", "@rollup/rollup-linux-arm64-musl": "4.55.2", "@rollup/rollup-linux-loong64-gnu": "4.55.2", "@rollup/rollup-linux-loong64-musl": "4.55.2", "@rollup/rollup-linux-ppc64-gnu": "4.55.2", "@rollup/rollup-linux-ppc64-musl": "4.55.2", "@rollup/rollup-linux-riscv64-gnu": "4.55.2", "@rollup/rollup-linux-riscv64-musl": "4.55.2", "@rollup/rollup-linux-s390x-gnu": "4.55.2", "@rollup/rollup-linux-x64-gnu": "4.55.2", "@rollup/rollup-linux-x64-musl": "4.55.2", "@rollup/rollup-openbsd-x64": "4.55.2", "@rollup/rollup-openharmony-arm64": "4.55.2", "@rollup/rollup-win32-arm64-msvc": "4.55.2", "@rollup/rollup-win32-ia32-msvc": "4.55.2", "@rollup/rollup-win32-x64-gnu": "4.55.2", "@rollup/rollup-win32-x64-msvc": "4.55.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-PggGy4dhwx5qaW+CKBilA/98Ql9keyfnb7lh4SR6shQ91QQQi1ORJ1v4UinkdP2i87OBs9AQFooQylcrrRfIcg=="], + "rollup": ["rollup@4.55.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.55.1", "@rollup/rollup-android-arm64": "4.55.1", "@rollup/rollup-darwin-arm64": "4.55.1", "@rollup/rollup-darwin-x64": "4.55.1", "@rollup/rollup-freebsd-arm64": "4.55.1", "@rollup/rollup-freebsd-x64": "4.55.1", "@rollup/rollup-linux-arm-gnueabihf": "4.55.1", "@rollup/rollup-linux-arm-musleabihf": "4.55.1", "@rollup/rollup-linux-arm64-gnu": "4.55.1", "@rollup/rollup-linux-arm64-musl": "4.55.1", "@rollup/rollup-linux-loong64-gnu": "4.55.1", "@rollup/rollup-linux-loong64-musl": "4.55.1", "@rollup/rollup-linux-ppc64-gnu": "4.55.1", "@rollup/rollup-linux-ppc64-musl": "4.55.1", "@rollup/rollup-linux-riscv64-gnu": "4.55.1", "@rollup/rollup-linux-riscv64-musl": "4.55.1", "@rollup/rollup-linux-s390x-gnu": "4.55.1", "@rollup/rollup-linux-x64-gnu": "4.55.1", "@rollup/rollup-linux-x64-musl": "4.55.1", "@rollup/rollup-openbsd-x64": "4.55.1", "@rollup/rollup-openharmony-arm64": "4.55.1", "@rollup/rollup-win32-arm64-msvc": "4.55.1", "@rollup/rollup-win32-ia32-msvc": "4.55.1", "@rollup/rollup-win32-x64-gnu": "4.55.1", "@rollup/rollup-win32-x64-msvc": "4.55.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A=="], "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], @@ -3816,6 +3828,8 @@ "tty-browserify": ["tty-browserify@0.0.1", "", {}, "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw=="], + "tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="], + "tweetnacl": ["tweetnacl@1.0.3", "", {}, "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw=="], "type": ["type@2.7.3", "", {}, "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ=="], @@ -3916,7 +3930,7 @@ "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], - "viem": ["viem@2.44.4", "", { "dependencies": { "@noble/curves": "1.9.1", "@noble/hashes": "1.8.0", "@scure/bip32": "1.7.0", "@scure/bip39": "1.6.0", "abitype": "1.2.3", "isows": "1.0.7", "ox": "0.11.3", "ws": "8.18.3" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-sJDLVl2EsS5Fo7GSWZME5CXEV7QRYkUJPeBw7ac+4XI3D4ydvMw/gjulTsT5pgqcpu70BploFnOAC6DLpan1Yg=="], + "viem": ["viem@2.44.2", "", { "dependencies": { "@noble/curves": "1.9.1", "@noble/hashes": "1.8.0", "@scure/bip32": "1.7.0", "@scure/bip39": "1.6.0", "abitype": "1.2.3", "isows": "1.0.7", "ox": "0.11.3", "ws": "8.18.3" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-nHY872t/T3flLpVsnvQT/89bwbrJwxaL917FDv7Oxy4E5FWIFkokRQOKXG3P+hgl30QYVZxi9o2SUHLnebycxw=="], "vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="], @@ -4070,7 +4084,7 @@ "@base-org/account/@noble/hashes": ["@noble/hashes@1.4.0", "", {}, "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg=="], - "@base-org/account/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + "@base-org/account/clsx": ["clsx@1.2.1", "", {}, "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg=="], "@base-org/account/idb-keyval": ["idb-keyval@6.2.1", "", {}, "sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg=="], @@ -4086,7 +4100,7 @@ "@coinbase/wallet-sdk/@noble/hashes": ["@noble/hashes@1.4.0", "", {}, "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg=="], - "@coinbase/wallet-sdk/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + "@coinbase/wallet-sdk/clsx": ["clsx@1.2.1", "", {}, "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg=="], "@coinbase/wallet-sdk/idb-keyval": ["idb-keyval@6.2.1", "", {}, "sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg=="], @@ -4100,8 +4114,6 @@ "@eslint/eslintrc/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], - "@gemini-wallet/core/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], - "@graphql-codegen/add/tslib": ["tslib@2.6.3", "", {}, "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ=="], "@graphql-codegen/cli/listr2": ["listr2@4.0.5", "", { "dependencies": { "cli-truncate": "^2.1.0", "colorette": "^2.0.16", "log-update": "^4.0.0", "p-map": "^4.0.0", "rfdc": "^1.3.0", "rxjs": "^7.5.5", "through": "^2.3.8", "wrap-ansi": "^7.0.0" }, "peerDependencies": { "enquirer": ">= 2.3.0 < 3" }, "optionalPeers": ["enquirer"] }, "sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA=="], @@ -4132,10 +4144,10 @@ "@graphql-tools/executor-graphql-ws/@graphql-tools/executor-common": ["@graphql-tools/executor-common@0.0.6", "", { "dependencies": { "@envelop/core": "^5.3.0", "@graphql-tools/utils": "^10.9.1" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-JAH/R1zf77CSkpYATIJw+eOJwsbWocdDjY+avY7G+P5HCXxwQjAjWVkJI1QJBQYjPQDVxwf1fmTZlIN3VOadow=="], - "@graphql-tools/executor-graphql-ws/ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], - "@graphql-tools/executor-legacy-ws/@graphql-tools/utils": ["@graphql-tools/utils@11.0.0", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@whatwg-node/promise-helpers": "^1.0.0", "cross-inspect": "1.0.1", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-bM1HeZdXA2C3LSIeLOnH/bcqSgbQgKEDrjxODjqi3y58xai2TkNrtYcQSoWzGbt9VMN1dORGjR7Vem8SPnUFQA=="], + "@graphql-tools/executor-legacy-ws/@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], + "@graphql-tools/executor-legacy-ws/ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], "@graphql-tools/git-loader/@graphql-tools/utils": ["@graphql-tools/utils@11.0.0", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@whatwg-node/promise-helpers": "^1.0.0", "cross-inspect": "1.0.1", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-bM1HeZdXA2C3LSIeLOnH/bcqSgbQgKEDrjxODjqi3y58xai2TkNrtYcQSoWzGbt9VMN1dORGjR7Vem8SPnUFQA=="], @@ -4162,6 +4174,8 @@ "@graphql-tools/schema/@graphql-tools/utils": ["@graphql-tools/utils@11.0.0", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@whatwg-node/promise-helpers": "^1.0.0", "cross-inspect": "1.0.1", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-bM1HeZdXA2C3LSIeLOnH/bcqSgbQgKEDrjxODjqi3y58xai2TkNrtYcQSoWzGbt9VMN1dORGjR7Vem8SPnUFQA=="], + "@graphql-tools/url-loader/@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], + "@graphql-tools/url-loader/sync-fetch": ["sync-fetch@0.6.0-2", "", { "dependencies": { "node-fetch": "^3.3.2", "timeout-signal": "^2.0.0", "whatwg-mimetype": "^4.0.0" } }, "sha512-c7AfkZ9udatCuAy9RSfiGPpeOKKUAUK5e1cXadLOGUjasdxqYqAK0jTNkM/FSEyJ3a5Ra27j/tw/PS0qLmaF/A=="], "@humanwhocodes/config-array/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], @@ -4210,8 +4224,6 @@ "@polkadot/util/@polkadot/x-bigint": ["@polkadot/x-bigint@13.5.9", "", { "dependencies": { "@polkadot/x-global": "13.5.9", "tslib": "^2.8.0" } }, "sha512-JVW6vw3e8fkcRyN9eoc6JIl63MRxNQCP/tuLdHWZts1tcAYao0hpWUzteqJY93AgvmQ91KPsC1Kf3iuuZCi74g=="], - "@polkadot/util-crypto/@noble/curves": ["@noble/curves@1.9.7", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw=="], - "@polkadot/util-crypto/@polkadot/x-bigint": ["@polkadot/x-bigint@13.5.9", "", { "dependencies": { "@polkadot/x-global": "13.5.9", "tslib": "^2.8.0" } }, "sha512-JVW6vw3e8fkcRyN9eoc6JIl63MRxNQCP/tuLdHWZts1tcAYao0hpWUzteqJY93AgvmQ91KPsC1Kf3iuuZCi74g=="], "@polkadot/x-bigint/@polkadot/x-global": ["@polkadot/x-global@14.0.1", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-aCI44DJU4fU0XXqrrSGIpi7JrZXK2kpe0jaQ2p6oDVXOOYEnZYXnMhTTmBE1lF/xtxzX50MnZrrU87jziU0qbA=="], @@ -4222,8 +4234,6 @@ "@polkadot/x-ws/@polkadot/x-global": ["@polkadot/x-global@14.0.1", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-aCI44DJU4fU0XXqrrSGIpi7JrZXK2kpe0jaQ2p6oDVXOOYEnZYXnMhTTmBE1lF/xtxzX50MnZrrU87jziU0qbA=="], - "@polkadot/x-ws/ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], - "@reown/appkit/@walletconnect/universal-provider": ["@walletconnect/universal-provider@2.23.1", "", { "dependencies": { "@walletconnect/events": "1.0.1", "@walletconnect/jsonrpc-http-connection": "1.0.8", "@walletconnect/jsonrpc-provider": "1.0.14", "@walletconnect/jsonrpc-types": "1.0.4", "@walletconnect/jsonrpc-utils": "1.0.8", "@walletconnect/keyvaluestorage": "1.1.1", "@walletconnect/logger": "3.0.1", "@walletconnect/sign-client": "2.23.1", "@walletconnect/types": "2.23.1", "@walletconnect/utils": "2.23.1", "es-toolkit": "1.39.3", "events": "3.3.0" } }, "sha512-XlvG1clsL7Ds+g28Oz5dXsPA+5ERtQGYvd+L8cskMaTvtphGhipVGgX8WNAhp7p1gfNcDg4tCiTHlj131jctwA=="], "@reown/appkit/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], @@ -4248,8 +4258,6 @@ "@safe-global/protocol-kit/@noble/curves": ["@noble/curves@1.9.7", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw=="], - "@scure/bip32/@noble/curves": ["@noble/curves@1.9.7", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw=="], - "@scure/bip39/@noble/hashes": ["@noble/hashes@2.0.1", "", {}, "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw=="], "@scure/bip39/@scure/base": ["@scure/base@2.0.0", "", {}, "sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w=="], @@ -4302,8 +4310,6 @@ "@supabase/realtime-js/@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], - "@supabase/realtime-js/ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], - "@swc/cli/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="], "@tailwindcss/node/jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], @@ -4320,8 +4326,6 @@ "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "@tanstack/router-devtools-core/clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], - "@tanstack/router-generator/prettier": ["prettier@3.8.0", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-yEPsovQfpxYfgWNhCfECjG5AQaO+K3dp6XERmOepyPDVqcJm+bjyCVO3pmU+nAPe0N5dDvekfGezt/EIiRe1TA=="], "@tanstack/router-plugin/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], @@ -4332,14 +4336,12 @@ "@testing-library/dom/dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], - "@typescript-eslint/project-service/@typescript-eslint/types": ["@typescript-eslint/types@8.53.1", "", {}, "sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A=="], + "@typescript-eslint/project-service/@typescript-eslint/types": ["@typescript-eslint/types@8.53.0", "", {}, "sha512-Bmh9KX31Vlxa13+PqPvt4RzKRN1XORYSLlAE+sO1i28NkisGbTtSLFVB3l7PWdHtR3E0mVMuC7JilWJ99m2HxQ=="], "@typescript-eslint/utils/eslint-scope": ["eslint-scope@5.1.1", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw=="], "@vitest/mocker/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], - "@wagmi/core/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], - "@wagmi/core/zustand": ["zustand@5.0.0", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-LE+VcmbartOPM+auOjCCLQOsQ05zUTp8RkgwRzefUk+2jISdMMFnxvyTjA4YNWr5ZGXYbVsEMZosttuxUBkojQ=="], "@walletconnect/environment/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="], @@ -4410,6 +4412,8 @@ "bson/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], + "cbw-sdk/clsx": ["clsx@1.2.1", "", {}, "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg=="], + "cbw-sdk/preact": ["preact@10.28.2", "", {}, "sha512-lbteaWGzGHdlIuiJ0l2Jq454m6kcpI1zNje6d8MlGAFlYvP2GO4ibnat7P74Esfz4sPTdM6UxtTwh/d3pwM9JA=="], "chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -4452,7 +4456,7 @@ "eslint-plugin-react/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - "eslint-plugin-storybook/@typescript-eslint/utils": ["@typescript-eslint/utils@8.53.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.53.1", "@typescript-eslint/types": "8.53.1", "@typescript-eslint/typescript-estree": "8.53.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-c4bMvGVWW4hv6JmDUEG7fSYlWOl3II2I4ylt0NM+seinYQlZMQIaKaXIIVJWt9Ofh6whrpM+EdDQXKXjNovvrg=="], + "eslint-plugin-storybook/@typescript-eslint/utils": ["@typescript-eslint/utils@8.53.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.53.0", "@typescript-eslint/types": "8.53.0", "@typescript-eslint/typescript-estree": "8.53.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-XDY4mXTez3Z1iRDI5mbRhH4DFSt46oaIFsLg+Zn97+sYrXACziXSQcSelMybnVZ5pa1P6xYkPr5cMJyunM1ZDA=="], "eth-block-tracker/@metamask/utils": ["@metamask/utils@5.0.2", "", { "dependencies": { "@ethereumjs/tx": "^4.1.2", "@types/debug": "^4.1.7", "debug": "^4.3.4", "semver": "^7.3.8", "superstruct": "^1.0.3" } }, "sha512-yfmE79bRQtnMzarnKfX7AEJBwFTxvTyw3nBQlu/5rmGXrjAeAMltoGxO62TFurxrQAFMNa/fEjIHNvungZp0+g=="], @@ -4578,8 +4582,6 @@ "ox/@scure/bip39": ["@scure/bip39@1.6.0", "", { "dependencies": { "@noble/hashes": "~1.8.0", "@scure/base": "~1.2.5" } }, "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A=="], - "ox/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], - "p-locate/p-limit": ["p-limit@4.0.0", "", { "dependencies": { "yocto-queue": "^1.0.0" } }, "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ=="], "path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], @@ -4600,8 +4602,6 @@ "qrcode/yargs": ["yargs@15.4.1", "", { "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^18.1.2" } }, "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A=="], - "react-toastify/clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], - "recast/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], "redent/strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="], @@ -4616,8 +4616,6 @@ "rpc-websockets/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], - "rpc-websockets/ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], - "seek-bzip/commander": ["commander@6.2.1", "", {}, "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA=="], "sequelize/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], @@ -4882,8 +4880,6 @@ "@walletconnect/utils/ox/@scure/bip39": ["@scure/bip39@1.6.0", "", { "dependencies": { "@noble/hashes": "~1.8.0", "@scure/base": "~1.2.5" } }, "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A=="], - "@walletconnect/utils/ox/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], - "body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "borsh/bs58/base-x": ["base-x@3.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA=="], @@ -4902,11 +4898,11 @@ "eslint-plugin-react/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], - "eslint-plugin-storybook/@typescript-eslint/utils/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.53.1", "", { "dependencies": { "@typescript-eslint/types": "8.53.1", "@typescript-eslint/visitor-keys": "8.53.1" } }, "sha512-Lu23yw1uJMFY8cUeq7JlrizAgeQvWugNQzJp8C3x8Eo5Jw5Q2ykMdiiTB9vBVOOUBysMzmRRmUfwFrZuI2C4SQ=="], + "eslint-plugin-storybook/@typescript-eslint/utils/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.53.0", "", { "dependencies": { "@typescript-eslint/types": "8.53.0", "@typescript-eslint/visitor-keys": "8.53.0" } }, "sha512-kWNj3l01eOGSdVBnfAF2K1BTh06WS0Yet6JUgb9Cmkqaz3Jlu0fdVUjj9UI8gPidBWSMqDIglmEXifSgDT/D0g=="], - "eslint-plugin-storybook/@typescript-eslint/utils/@typescript-eslint/types": ["@typescript-eslint/types@8.53.1", "", {}, "sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A=="], + "eslint-plugin-storybook/@typescript-eslint/utils/@typescript-eslint/types": ["@typescript-eslint/types@8.53.0", "", {}, "sha512-Bmh9KX31Vlxa13+PqPvt4RzKRN1XORYSLlAE+sO1i28NkisGbTtSLFVB3l7PWdHtR3E0mVMuC7JilWJ99m2HxQ=="], - "eslint-plugin-storybook/@typescript-eslint/utils/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.53.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.53.1", "@typescript-eslint/tsconfig-utils": "8.53.1", "@typescript-eslint/types": "8.53.1", "@typescript-eslint/visitor-keys": "8.53.1", "debug": "^4.4.3", "minimatch": "^9.0.5", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-RGlVipGhQAG4GxV1s34O91cxQ/vWiHJTDHbXRr0li2q/BGg3RR/7NM8QDWgkEgrwQYCvmJV9ichIwyoKCQ+DTg=="], + "eslint-plugin-storybook/@typescript-eslint/utils/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.53.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.53.0", "@typescript-eslint/tsconfig-utils": "8.53.0", "@typescript-eslint/types": "8.53.0", "@typescript-eslint/visitor-keys": "8.53.0", "debug": "^4.4.3", "minimatch": "^9.0.5", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-pw0c0Gdo7Z4xOG987u3nJ8akL9093yEEKv8QTJ+Bhkghj1xyj8cgPaavlr9rq8h7+s6plUJ4QJYw2gCZodqmGw=="], "eslint/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], @@ -4934,7 +4930,7 @@ "gaxios/https-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], - "jayson/@types/ws/@types/node": ["@types/node@22.19.7", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw=="], + "jayson/@types/ws/@types/node": ["@types/node@22.19.6", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-qm+G8HuG6hOHQigsi7VGuLjUVu6TtBo/F05zvX04Mw2uCg9Dv0Qxy3Qw7j41SidlTcl5D/5yg0SEZqOB+EqZnQ=="], "js-beautify/nopt/abbrev": ["abbrev@2.0.0", "", {}, "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ=="], @@ -4974,8 +4970,6 @@ "porto/ox/@scure/bip39": ["@scure/bip39@1.6.0", "", { "dependencies": { "@noble/hashes": "~1.8.0", "@scure/base": "~1.2.5" } }, "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A=="], - "porto/ox/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], - "qrcode/yargs/cliui": ["cliui@6.0.0", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="], "qrcode/yargs/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], @@ -5196,9 +5190,9 @@ "cli-truncate/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], - "eslint-plugin-storybook/@typescript-eslint/utils/@typescript-eslint/scope-manager/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.53.1", "", { "dependencies": { "@typescript-eslint/types": "8.53.1", "eslint-visitor-keys": "^4.2.1" } }, "sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg=="], + "eslint-plugin-storybook/@typescript-eslint/utils/@typescript-eslint/scope-manager/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.53.0", "", { "dependencies": { "@typescript-eslint/types": "8.53.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-LZ2NqIHFhvFwxG0qZeLL9DvdNAHPGCY5dIRwBhyYeU+LfLhcStE1ImjsuTG/WaVh3XysGaeLW8Rqq7cGkPCFvw=="], - "eslint-plugin-storybook/@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.53.1", "", { "dependencies": { "@typescript-eslint/types": "8.53.1", "eslint-visitor-keys": "^4.2.1" } }, "sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg=="], + "eslint-plugin-storybook/@typescript-eslint/utils/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.53.0", "", { "dependencies": { "@typescript-eslint/types": "8.53.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-LZ2NqIHFhvFwxG0qZeLL9DvdNAHPGCY5dIRwBhyYeU+LfLhcStE1ImjsuTG/WaVh3XysGaeLW8Rqq7cGkPCFvw=="], "eslint/find-up/locate-path/p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], @@ -5240,32 +5234,24 @@ "@reown/appkit-adapter-wagmi/@walletconnect/universal-provider/@walletconnect/utils/ox/@scure/bip39": ["@scure/bip39@1.6.0", "", { "dependencies": { "@noble/hashes": "~1.8.0", "@scure/base": "~1.2.5" } }, "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A=="], - "@reown/appkit-adapter-wagmi/@walletconnect/universal-provider/@walletconnect/utils/ox/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], - "@reown/appkit-controllers/@walletconnect/universal-provider/@walletconnect/utils/ox/@adraffy/ens-normalize": ["@adraffy/ens-normalize@1.11.1", "", {}, "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ=="], "@reown/appkit-controllers/@walletconnect/universal-provider/@walletconnect/utils/ox/@noble/curves": ["@noble/curves@1.9.1", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA=="], "@reown/appkit-controllers/@walletconnect/universal-provider/@walletconnect/utils/ox/@scure/bip39": ["@scure/bip39@1.6.0", "", { "dependencies": { "@noble/hashes": "~1.8.0", "@scure/base": "~1.2.5" } }, "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A=="], - "@reown/appkit-controllers/@walletconnect/universal-provider/@walletconnect/utils/ox/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], - "@reown/appkit-utils/@walletconnect/universal-provider/@walletconnect/utils/ox/@adraffy/ens-normalize": ["@adraffy/ens-normalize@1.11.1", "", {}, "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ=="], "@reown/appkit-utils/@walletconnect/universal-provider/@walletconnect/utils/ox/@noble/curves": ["@noble/curves@1.9.1", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA=="], "@reown/appkit-utils/@walletconnect/universal-provider/@walletconnect/utils/ox/@scure/bip39": ["@scure/bip39@1.6.0", "", { "dependencies": { "@noble/hashes": "~1.8.0", "@scure/base": "~1.2.5" } }, "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A=="], - "@reown/appkit-utils/@walletconnect/universal-provider/@walletconnect/utils/ox/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], - "@reown/appkit/@walletconnect/universal-provider/@walletconnect/utils/ox/@adraffy/ens-normalize": ["@adraffy/ens-normalize@1.11.1", "", {}, "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ=="], "@reown/appkit/@walletconnect/universal-provider/@walletconnect/utils/ox/@noble/curves": ["@noble/curves@1.9.1", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA=="], "@reown/appkit/@walletconnect/universal-provider/@walletconnect/utils/ox/@scure/bip39": ["@scure/bip39@1.6.0", "", { "dependencies": { "@noble/hashes": "~1.8.0", "@scure/base": "~1.2.5" } }, "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A=="], - "@reown/appkit/@walletconnect/universal-provider/@walletconnect/utils/ox/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], - "@sentry/bundler-plugin-core/find-up/locate-path/p-locate/p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], "@sentry/bundler-plugin-core/unplugin/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], diff --git a/docs/architecture/supabase-auth.md b/docs/architecture/supabase-auth.md new file mode 100644 index 000000000..614af6c42 --- /dev/null +++ b/docs/architecture/supabase-auth.md @@ -0,0 +1,86 @@ +# Supabase Auth Integration + +## Overview + +This architectural document describes the integration of Supabase Authentication into Vortex. The system uses a passwordless Email OTP flow, leveraging Supabase's infrastructure for identity management while maintaining user-related data in our local PostgreSQL database. + +## Architecture + +### User Flow + +The authentication flow is designed to be unobtrusive, allowing users to browse and calculate quotes before being required to identify themselves. + +1. **Quote Creation**: Unauthenticated users can view and calculate quotes. +2. **Confirmation**: When a user clicks "Confirm" on a quote, the system checks for an active session. +3. **Authentication**: + * If no session exists, the user is prompted for their email. + * **OTP**: A one-time password is sent to their email (via Supabase). + * **Verification**: User enters the code. On success, access and refresh tokens are issued and stored locally. +4. **Transaction**: Authenticated user proceeds to the ramp transaction. `user_id` is now attached to the created resources. + +### Data Model + +We utilize Supabase's `auth` schema for identity management but strictly separate our application data. + +* **`auth.users`**: Internal Supabase table storing identity, email, and encrypted passwords (unused here). +* **`public.profiles`**: Our local table that extends the auth user, linked 1:1 with `auth.users`. + * *Note*: This table was explicitly named `profiles` to avoid conflicts with `auth.users`. +* **Entity Linking**: The following core entities reference the Supabase `user_id` (UUID) to maintain ownership: + * `quote_tickets` + * `ramp_states` + * `kyc_level_2` + * `tax_ids` + +## Backend Architecture + +The backend acts as a bridge between the frontend and Supabase, ensuring data integrity without handling sensitive credential storage. + +### Tech Stack +* **Framework**: Express +* **Auth Client**: `@supabase/supabase-js` +* **Database**: PostgreSQL (via Sequelize) + +### Service Layer +The **Auth Service** encapsulates interactions with Supabase: +* **Admin Client**: Used for privileged operations like checking if a user exists (`admin.listUsers`) without logging them in. +* **Anon Client**: Used for standard operations like `signInWithOtp` and `verifyOtp`. + +### Middleware +* **`requireAuth`**: Validates the `Bearer` token against Supabase using `getUser()`. Attaches `userId` to the request object. +* **`optionalAuth`**: checks for a token but continues even if invalid/missing (useful for mixed-access endpoints). + +### API Endpoints +All auth-related operational endpoints are grouped under `/api/v1/auth`: +* `GET /check-email`: Checks if a user exists (determines Sign In vs Sign Up UI flow). +* `POST /request-otp`: Triggers the email OTP. +* `POST /verify-otp`: Exchanges OTP for session tokens. +* `POST /refresh`: Rotates expired access tokens using the refresh token. +* `POST /verify`: Validates a token server-side. + +## Frontend Architecture + +The frontend manages the user session and guides the user through the auth steps within the transaction flow. + +### Tech Stack +* **Framework**: React +* **State Management**: XState +* **Client**: `@supabase/supabase-js` + +### State Machine Integration +The `rampMachine` handles the authentication lifecycle as a distinct phase in the transaction flow. + +* **States**: + * `CheckAuth`: Decides whether to skip to `RampRequested` or enter the auth flow. + * `EnterEmail` / `CheckingEmail`: Captures and validates email. + * `RequestingOTP`: Calls API to send code. + * `EnterOTP` / `VerifyingOTP`: Captures code and exchanges for tokens. +* **Transitions**: The flow blocks the "Confirm" action until `AUTH_SUCCESS` is reached, ensuring no quote is finalized without a user. + +### Token Management +* **Storage**: `localStorage` is used to store `access_token`, `refresh_token`, and `user_id`. +* **Auto-Refresh**: A background process (via `useAuthTokens` hook) monitors token validity and refreshes them automatically ~5 minutes before expiry to prevent session interruption during long flows. + +### UI Components +The auth UI is embedded directly into the widget flow rather than a separate page: +* `AuthEmailStep`: Simple email input with existence check. +* `AuthOTPStep`: 6-digit code input with auto-submit and paste support. diff --git a/package.json b/package.json index 01e142a5b..616f2492d 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@polkadot/util-crypto": "^13.5.6", "@scure/bip39": "2.0.1", "@storybook/react": "^9.1.16", + "@supabase/supabase-js": "^2.80.0", "@types/big.js": "^6.0.2", "@types/node": "^22.7.5", "@vortexfi/shared": "workspace:*", diff --git a/packages/shared/src/endpoints/ramp.endpoints.ts b/packages/shared/src/endpoints/ramp.endpoints.ts index 6ce16a4b1..7393916b2 100644 --- a/packages/shared/src/endpoints/ramp.endpoints.ts +++ b/packages/shared/src/endpoints/ramp.endpoints.ts @@ -102,6 +102,7 @@ export interface IbanPaymentData { export interface RegisterRampRequest { quoteId: string; signingAccounts: AccountMeta[]; + userId?: string; additionalData?: { walletAddress?: string; destinationAddress?: string; diff --git a/supabase/config.toml b/supabase/config.toml new file mode 100644 index 000000000..d59d639da --- /dev/null +++ b/supabase/config.toml @@ -0,0 +1,391 @@ +# For detailed configuration reference documentation, visit: +# https://supabase.com/docs/guides/local-development/cli/config +# A string used to distinguish different Supabase projects on the same host. Defaults to the +# working directory name when running `supabase init`. +project_id = "vortex" + +[api] +enabled = true +# Port to use for the API URL. +port = 54321 +# Schemas to expose in your API. Tables, views and stored procedures in this schema will get API +# endpoints. `public` and `graphql_public` schemas are included by default. +schemas = ["public", "graphql_public"] +# Extra schemas to add to the search_path of every request. +extra_search_path = ["public", "extensions"] +# The maximum number of rows returns from a view, table, or stored procedure. Limits payload size +# for accidental or malicious requests. +max_rows = 1000 + +[api.tls] +# Enable HTTPS endpoints locally using a self-signed certificate. +enabled = false +# Paths to self-signed certificate pair. +# cert_path = "../certs/my-cert.pem" +# key_path = "../certs/my-key.pem" + +[db] +# Port to use for the local database URL. +port = 54322 +# Port used by db diff command to initialize the shadow database. +shadow_port = 54320 +# The database major version to use. This has to be the same as your remote database's. Run `SHOW +# server_version;` on the remote database to check. +major_version = 17 + +[db.pooler] +enabled = false +# Port to use for the local connection pooler. +port = 54329 +# Specifies when a server connection can be reused by other clients. +# Configure one of the supported pooler modes: `transaction`, `session`. +pool_mode = "transaction" +# How many server connections to allow per user/database pair. +default_pool_size = 20 +# Maximum number of client connections allowed. +max_client_conn = 100 + +# [db.vault] +# secret_key = "env(SECRET_VALUE)" + +[db.migrations] +# If disabled, migrations will be skipped during a db push or reset. +enabled = true +# Specifies an ordered list of schema files that describe your database. +# Supports glob patterns relative to supabase directory: "./schemas/*.sql" +schema_paths = [] + +[db.seed] +# If enabled, seeds the database after migrations during a db reset. +enabled = true +# Specifies an ordered list of seed files to load during db reset. +# Supports glob patterns relative to supabase directory: "./seeds/*.sql" +sql_paths = ["./seed.sql"] + +[db.network_restrictions] +# Enable management of network restrictions. +enabled = false +# List of IPv4 CIDR blocks allowed to connect to the database. +# Defaults to allow all IPv4 connections. Set empty array to block all IPs. +allowed_cidrs = ["0.0.0.0/0"] +# List of IPv6 CIDR blocks allowed to connect to the database. +# Defaults to allow all IPv6 connections. Set empty array to block all IPs. +allowed_cidrs_v6 = ["::/0"] + +[realtime] +enabled = true +# Bind realtime via either IPv4 or IPv6. (default: IPv4) +# ip_version = "IPv6" +# The maximum length in bytes of HTTP request headers. (default: 4096) +# max_header_length = 4096 + +[studio] +enabled = true +# Port to use for Supabase Studio. +port = 54323 +# External URL of the API server that frontend connects to. +api_url = "http://127.0.0.1" +# OpenAI API Key to use for Supabase AI in the Supabase Studio. +openai_api_key = "env(OPENAI_API_KEY)" + +# Email testing server. Emails sent with the local dev setup are not actually sent - rather, they +# are monitored, and you can view the emails that would have been sent from the web interface. +[inbucket] +enabled = true +# Port to use for the email testing server web interface. +port = 54324 +# Uncomment to expose additional ports for testing user applications that send emails. +# smtp_port = 54325 +# pop3_port = 54326 +# admin_email = "admin@email.com" +# sender_name = "Admin" + +[storage] +enabled = true +# The maximum file size allowed (e.g. "5MB", "500KB"). +file_size_limit = "50MiB" + +# Uncomment to configure local storage buckets +# [storage.buckets.images] +# public = false +# file_size_limit = "50MiB" +# allowed_mime_types = ["image/png", "image/jpeg"] +# objects_path = "./images" + +# Uncomment to allow connections via S3 compatible clients +# [storage.s3_protocol] +# enabled = true + +# Image transformation API is available to Supabase Pro plan. +# [storage.image_transformation] +# enabled = true + +# Store analytical data in S3 for running ETL jobs over Iceberg Catalog +# This feature is only available on the hosted platform. +[storage.analytics] +enabled = false +max_namespaces = 5 +max_tables = 10 +max_catalogs = 2 + +# Analytics Buckets is available to Supabase Pro plan. +# [storage.analytics.buckets.my-warehouse] + +# Store vector embeddings in S3 for large and durable datasets +# This feature is only available on the hosted platform. +[storage.vector] +enabled = false +max_buckets = 10 +max_indexes = 5 + +# Vector Buckets is available to Supabase Pro plan. +# [storage.vector.buckets.documents-openai] + +[auth] +enabled = true +# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used +# in emails. +site_url = "http://127.0.0.1:3000" +# A list of *exact* URLs that auth providers are permitted to redirect to post authentication. +additional_redirect_urls = ["https://127.0.0.1:3000"] +# How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 (1 week). +jwt_expiry = 3600 +# JWT issuer URL. If not set, defaults to the local API URL (http://127.0.0.1:/auth/v1). +# jwt_issuer = "" +# Path to JWT signing key. DO NOT commit your signing keys file to git. +# signing_keys_path = "./signing_keys.json" +# If disabled, the refresh token will never expire. +enable_refresh_token_rotation = true +# Allows refresh tokens to be reused after expiry, up to the specified interval in seconds. +# Requires enable_refresh_token_rotation = true. +refresh_token_reuse_interval = 10 +# Allow/disallow new user signups to your project. +enable_signup = true +# Allow/disallow anonymous sign-ins to your project. +enable_anonymous_sign_ins = false +# Allow/disallow testing manual linking of accounts +enable_manual_linking = false +# Passwords shorter than this value will be rejected as weak. Minimum 6, recommended 8 or more. +minimum_password_length = 6 +# Passwords that do not meet the following requirements will be rejected as weak. Supported values +# are: `letters_digits`, `lower_upper_letters_digits`, `lower_upper_letters_digits_symbols` +password_requirements = "" + +[auth.rate_limit] +# Number of emails that can be sent per hour. Requires auth.email.smtp to be enabled. +email_sent = 10 +# Number of SMS messages that can be sent per hour. Requires auth.sms to be enabled. +sms_sent = 30 +# Number of anonymous sign-ins that can be made per hour per IP address. Requires enable_anonymous_sign_ins = true. +anonymous_users = 30 +# Number of sessions that can be refreshed in a 5 minute interval per IP address. +token_refresh = 150 +# Number of sign up and sign-in requests that can be made in a 5 minute interval per IP address (excludes anonymous users). +sign_in_sign_ups = 30 +# Number of OTP / Magic link verifications that can be made in a 5 minute interval per IP address. +token_verifications = 30 +# Number of Web3 logins that can be made in a 5 minute interval per IP address. +web3 = 30 + +# Configure one of the supported captcha providers: `hcaptcha`, `turnstile`. +# [auth.captcha] +# enabled = true +# provider = "hcaptcha" +# secret = "" + +[auth.email] +# Allow/disallow new user signups via email to your project. +enable_signup = true +# If enabled, a user will be required to confirm any email change on both the old, and new email +# addresses. If disabled, only the new email is required to confirm. +double_confirm_changes = true +# If enabled, users need to confirm their email address before signing in. +enable_confirmations = false +# If enabled, users will need to reauthenticate or have logged in recently to change their password. +secure_password_change = false +# Controls the minimum amount of time that must pass before sending another signup confirmation or password reset email. +max_frequency = "1s" +# Number of characters used in the email OTP. +otp_length = 6 +# Number of seconds before the email OTP expires (defaults to 1 hour). +otp_expiry = 3600 + +# Use a production-ready SMTP server +# [auth.email.smtp] +# enabled = true +# host = "smtp.sendgrid.net" +# port = 587 +# user = "apikey" +# pass = "env(SENDGRID_API_KEY)" +# admin_email = "admin@email.com" +# sender_name = "Admin" + +# Uncomment to customize email template +# [auth.email.template.invite] +# subject = "You have been invited" +# content_path = "./supabase/templates/invite.html" + +# Custom email template for OTP/Magic Link +[auth.email.template.magic_link] +subject = "Your Vortex Verification Code" +content_path = "supabase/templates/magic_link.html" + +[auth.email.template.confirmation] +subject = "Confirm your signup" +content_path = "supabase/templates/signup.html" + +# Uncomment to customize notification email template +# [auth.email.notification.password_changed] +# enabled = true +# subject = "Your password has been changed" +# content_path = "./templates/password_changed_notification.html" + +[auth.sms] +# Allow/disallow new user signups via SMS to your project. +enable_signup = false +# If enabled, users need to confirm their phone number before signing in. +enable_confirmations = false +# Template for sending OTP to users +template = "Your code is {{ .Code }}" +# Controls the minimum amount of time that must pass before sending another sms otp. +max_frequency = "5s" + +# Use pre-defined map of phone number to OTP for testing. +# [auth.sms.test_otp] +# 4152127777 = "123456" + +# Configure logged in session timeouts. +# [auth.sessions] +# Force log out after the specified duration. +# timebox = "24h" +# Force log out if the user has been inactive longer than the specified duration. +# inactivity_timeout = "8h" + +# This hook runs before a new user is created and allows developers to reject the request based on the incoming user object. +# [auth.hook.before_user_created] +# enabled = true +# uri = "pg-functions://postgres/auth/before-user-created-hook" + +# This hook runs before a token is issued and allows you to add additional claims based on the authentication method used. +# [auth.hook.custom_access_token] +# enabled = true +# uri = "pg-functions:////" + +# Configure one of the supported SMS providers: `twilio`, `twilio_verify`, `messagebird`, `textlocal`, `vonage`. +[auth.sms.twilio] +enabled = false +account_sid = "" +message_service_sid = "" +# DO NOT commit your Twilio auth token to git. Use environment variable substitution instead: +auth_token = "env(SUPABASE_AUTH_SMS_TWILIO_AUTH_TOKEN)" + +# Multi-factor-authentication is available to Supabase Pro plan. +[auth.mfa] +# Control how many MFA factors can be enrolled at once per user. +max_enrolled_factors = 10 + +# Control MFA via App Authenticator (TOTP) +[auth.mfa.totp] +enroll_enabled = false +verify_enabled = false + +# Configure MFA via Phone Messaging +[auth.mfa.phone] +enroll_enabled = false +verify_enabled = false +otp_length = 6 +template = "Your code is {{ .Code }}" +max_frequency = "5s" + +# Configure MFA via WebAuthn +# [auth.mfa.web_authn] +# enroll_enabled = true +# verify_enabled = true + +# Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`, +# `discord`, `facebook`, `github`, `gitlab`, `google`, `keycloak`, `linkedin_oidc`, `notion`, `twitch`, +# `twitter`, `slack`, `spotify`, `workos`, `zoom`. +[auth.external.apple] +enabled = false +client_id = "" +# DO NOT commit your OAuth provider secret to git. Use environment variable substitution instead: +secret = "env(SUPABASE_AUTH_EXTERNAL_APPLE_SECRET)" +# Overrides the default auth redirectUrl. +redirect_uri = "" +# Overrides the default auth provider URL. Used to support self-hosted gitlab, single-tenant Azure, +# or any other third-party OIDC providers. +url = "" +# If enabled, the nonce check will be skipped. Required for local sign in with Google auth. +skip_nonce_check = false +# If enabled, it will allow the user to successfully authenticate when the provider does not return an email address. +email_optional = false + +# Allow Solana wallet holders to sign in to your project via the Sign in with Solana (SIWS, EIP-4361) standard. +# You can configure "web3" rate limit in the [auth.rate_limit] section and set up [auth.captcha] if self-hosting. +[auth.web3.solana] +enabled = false + +# Use Firebase Auth as a third-party provider alongside Supabase Auth. +[auth.third_party.firebase] +enabled = false +# project_id = "my-firebase-project" + +# Use Auth0 as a third-party provider alongside Supabase Auth. +[auth.third_party.auth0] +enabled = false +# tenant = "my-auth0-tenant" +# tenant_region = "us" + +# Use AWS Cognito (Amplify) as a third-party provider alongside Supabase Auth. +[auth.third_party.aws_cognito] +enabled = false +# user_pool_id = "my-user-pool-id" +# user_pool_region = "us-east-1" + +# Use Clerk as a third-party provider alongside Supabase Auth. +[auth.third_party.clerk] +enabled = false +# Obtain from https://clerk.com/setup/supabase +# domain = "example.clerk.accounts.dev" + +# OAuth server configuration +[auth.oauth_server] +# Enable OAuth server functionality +enabled = false +# Path for OAuth consent flow UI +authorization_url_path = "/oauth/consent" +# Allow dynamic client registration +allow_dynamic_registration = false + +[edge_runtime] +enabled = true +# Supported request policies: `oneshot`, `per_worker`. +# `per_worker` (default) — enables hot reload during local development. +# `oneshot` — fallback mode if hot reload causes issues (e.g. in large repos or with symlinks). +policy = "per_worker" +# Port to attach the Chrome inspector for debugging edge functions. +inspector_port = 8083 +# The Deno major version to use. +deno_version = 2 + +# [edge_runtime.secrets] +# secret_key = "env(SECRET_VALUE)" + +[analytics] +enabled = true +port = 54327 +# Configure one of the supported backends: `postgres`, `bigquery`. +backend = "postgres" + +# Experimental features may be deprecated any time +[experimental] +# Configures Postgres storage engine to use OrioleDB (S3) +orioledb_version = "" +# Configures S3 bucket URL, eg. .s3-.amazonaws.com +s3_host = "env(S3_HOST)" +# Configures S3 bucket region, eg. us-east-1 +s3_region = "env(S3_REGION)" +# Configures AWS_ACCESS_KEY_ID for S3 bucket +s3_access_key = "env(S3_ACCESS_KEY)" +# Configures AWS_SECRET_ACCESS_KEY for S3 bucket +s3_secret_key = "env(S3_SECRET_KEY)" diff --git a/supabase/templates/magic_link.html b/supabase/templates/magic_link.html new file mode 100644 index 000000000..ea3c14ea1 --- /dev/null +++ b/supabase/templates/magic_link.html @@ -0,0 +1,161 @@ + + + + + + Your Vortex Verification Code + + + + + + diff --git a/supabase/templates/signup.html b/supabase/templates/signup.html new file mode 100644 index 000000000..e81108425 --- /dev/null +++ b/supabase/templates/signup.html @@ -0,0 +1,163 @@ + + + + + + Welcome to Vortex - Verify Your Email + + + + + +