diff --git a/packages/nextjs/.eslintrc.json b/packages/nextjs/.eslintrc.json index 15b1ed9..616eb8d 100644 --- a/packages/nextjs/.eslintrc.json +++ b/packages/nextjs/.eslintrc.json @@ -1,3 +1,7 @@ { - "extends": "next" -} + "extends": "next", + "rules": { + "react/no-unescaped-entities": "off", + "@next/next/no-img-element": "warn" + } +} \ No newline at end of file diff --git a/packages/nextjs/app/about/page.tsx b/packages/nextjs/app/about/page.tsx new file mode 100644 index 0000000..d356409 --- /dev/null +++ b/packages/nextjs/app/about/page.tsx @@ -0,0 +1,332 @@ +import { Shield, Zap, TrendingUp, Users, Lock, BarChart3, Building2, Sparkles } from "lucide-react" +import { Button } from "~~/components/ui/button" +import { Card } from "~~/components/ui/card" +import Link from "next/link" + +export const metadata = { + title: "About reAI - AI-Powered Real Estate Tokenization", + description: + "Learn how reAI combines blockchain technology and artificial intelligence to revolutionize real estate investment through secure asset tokenization.", +} + +export default function AboutPage() { + return ( +
+ {/* Hero Section */} +
+
+
+
+ + Blockchain Meets Artificial Intelligence +
+

+ Revolutionizing Real Estate + Through Smart Tokenization +

+

+ reAI is pioneering the future of real estate investment by combining the security of Starknet blockchain + with advanced AI analysis to make property investment accessible, transparent, and safe for everyone. +

+
+
+
+ + {/* Mission Section */} +
+
+
+
+

Our Mission

+

+ Traditional real estate investment has always been limited to those with significant capital and insider + knowledge. We're changing that narrative by democratizing access to premium properties through + fractional ownership powered by blockchain technology. +

+

+ But accessibility isn't enough. Safety and transparency are paramount. That's why we've integrated + cutting-edge AI analysis to verify property credibility, assess risks, and provide investors with + comprehensive insights before they commit their capital. +

+

+ Our vision is a world where anyone can build wealth through real estate, backed by the security of + blockchain and the intelligence of AI. +

+
+
+ +
+ +
+

Asset Tokenization

+

+ Convert real estate into digital tokens on Starknet, enabling fractional ownership and instant + liquidity. +

+
+ +
+ +
+

AI-Powered Analysis

+

+ Advanced algorithms verify property authenticity, assess risks, and provide detailed credibility + reports. +

+
+
+
+
+
+ + {/* How It Works */} +
+
+
+

How reAI Works

+

+ A seamless blend of blockchain security and artificial intelligence for safe, transparent real estate + investment. +

+
+ +
+
+
+
+ 1 +
+
+

Property Listing

+

+ Verified realtors list premium properties with complete documentation and legal compliance. +

+
+ +
+
+
+ 2 +
+
+

AI Analysis

+

+ Our AI engine analyzes ownership records, legal documents, market data, and blockchain registry for + credibility. +

+
+ +
+
+
+ 3 +
+
+

Tokenization

+

+ Properties are converted into STRK tokens on Starknet blockchain, enabling fractional ownership. +

+
+ +
+
+
+ 4 +
+
+

Invest & Earn

+

+ Investors purchase tokens with as little as 50 STRK and earn passive income from property appreciation. +

+
+
+
+
+ + {/* Key Features */} +
+
+
+

Why Choose reAI?

+

+ We've built the most secure and intelligent real estate investment platform on the blockchain. +

+
+ +
+ +
+ +
+

AI-Verified Safety

+

+ Every property undergoes rigorous AI analysis checking ownership records, legal compliance, and market + authenticity before listing. +

+
+ + +
+ +
+

Blockchain Security

+

+ Built on Starknet, ensuring immutable ownership records, transparent transactions, and protection + against fraud. +

+
+ + +
+ +
+

Low Entry Barrier

+

+ Start investing in premium real estate with as little as 50 STRK tokens. No need for millions in + capital. +

+
+ + +
+ +
+

Instant Liquidity

+

+ Unlike traditional real estate, your tokenized assets can be traded instantly on the blockchain + marketplace. +

+
+ + +
+ +
+

Transparent Analytics

+

+ Access detailed AI-generated reports showing risk assessments, market analysis, and property credibility + scores. +

+
+ + +
+ +
+

Community Driven

+

+ Join a growing community of investors and realtors building the future of decentralized real estate. +

+
+
+
+
+ + {/* Technology Stack */} +
+
+
+

Powered by Cutting-Edge Technology

+

+ We leverage the best blockchain and AI technologies to deliver a secure, intelligent platform. +

+
+ +
+ +

Starknet Blockchain

+

+ Built on Starknet, a Layer 2 scaling solution for Ethereum that provides: +

+
    +
  • + + ✓ + + High transaction throughput with low fees +
  • +
  • + + ✓ + + Zero-knowledge proof security +
  • +
  • + + ✓ + + Immutable ownership records +
  • +
  • + + ✓ + + Smart contract automation +
  • +
+
+ + +

AI Analysis Engine

+

+ Our proprietary AI system analyzes multiple data sources to ensure property credibility: +

+
    +
  • + + ✓ + + Blockchain registry verification +
  • +
  • + + ✓ + + Legal document analysis +
  • +
  • + + ✓ + + Market data cross-referencing +
  • +
  • + + ✓ + + Risk assessment modeling +
  • +
+
+
+
+
+ + {/* CTA Section */} +
+
+

Ready to Start Your Investment Journey?

+

+ Join thousands of investors who are building wealth through AI-verified, blockchain-secured real estate. +

+
+ + + + + + +
+
+
+
+ ) +} diff --git a/packages/nextjs/app/blockexplorer/address/[address]/page.tsx b/packages/nextjs/app/blockexplorer/address/[address]/page.tsx index d46a012..1e33272 100644 --- a/packages/nextjs/app/blockexplorer/address/[address]/page.tsx +++ b/packages/nextjs/app/blockexplorer/address/[address]/page.tsx @@ -19,6 +19,21 @@ import { import { useScaffoldStarkProfile } from "~~/hooks/scaffold-stark/useScaffoldStarkProfile"; import useScaffoldStrkBalance from "~~/hooks/scaffold-stark/useScaffoldStrkBalance"; +// Add these type definitions +type TxCall = { + functionCalled?: string; + [key: string]: any; +}; + +type Transaction = { + transactionHash?: string; + blockNumber?: number; + timestamp?: number; + fromAddress?: string; + txCalls?: TxCall[]; + [key: string]: any; +}; + interface AddressDetailsProps { params: Promise<{ address: string; @@ -458,7 +473,7 @@ export default function AddressDetails({ params }: AddressDetailsProps) { - {transactionsData.map((txn, index) => { +{transactionsData.map((txn: Transaction, index: number) => { const age = txn.timestamp ? Math.floor((Date.now() / 1000 - txn.timestamp) / 60) : null; diff --git a/packages/nextjs/app/login/page.tsx b/packages/nextjs/app/login/page.tsx index 9982e3a..8edfe45 100644 --- a/packages/nextjs/app/login/page.tsx +++ b/packages/nextjs/app/login/page.tsx @@ -4,8 +4,8 @@ import { X } from "lucide-react" import { LoginForm } from "~~/components/auth/login-form" export const metadata: Metadata = { - title: "Login | BrickChain", - description: "Sign in to your BrickChain account", + title: "Login | reAI", + description: "Sign in to your reAI account", } export default function LoginPage() { @@ -13,12 +13,15 @@ export default function LoginPage() {
- + {/* Close button */} + + + {/* Header */}

Welcome Back

-

Sign in to your BrickChain account

+

Sign in to your reAI account

{/* Form */} diff --git a/packages/nextjs/app/onboarding/page.tsx b/packages/nextjs/app/onboarding/page.tsx index dbb4ffb..c6b7aa6 100644 --- a/packages/nextjs/app/onboarding/page.tsx +++ b/packages/nextjs/app/onboarding/page.tsx @@ -3,8 +3,8 @@ import Link from "next/link" import { Users, Building2, CheckCircle2, Briefcase, Building } from "lucide-react" export const metadata: Metadata = { - title: "Choose Your Path | BrickChain", - description: "Select how you'd like to join the BrickChain ecosystem", + title: "Choose Your Path | reAI", + description: "Select how you'd like to join the reAI ecosystem", } export default function OnboardingPage() { @@ -14,7 +14,7 @@ export default function OnboardingPage() { {/* Header */}

Choose Your Path

-

Select how you'd like to join the BrickChain ecosystem

+

Select how you'd like to join the reAI ecosystem

{/* Cards */} @@ -50,15 +50,15 @@ export default function OnboardingPage() { {/* Features */}
- + Low minimum investment
- + Instant liquidity
- + Passive income
@@ -95,15 +95,15 @@ export default function OnboardingPage() { {/* Features */}
- + List properties
- + Earn commissions
- + Manage listings
diff --git a/packages/nextjs/app/signup/page.tsx b/packages/nextjs/app/signup/page.tsx index 2926486..2df8c6b 100644 --- a/packages/nextjs/app/signup/page.tsx +++ b/packages/nextjs/app/signup/page.tsx @@ -1,13 +1,12 @@ import type { Metadata } from "next" import Link from "next/link" import { X } from "lucide-react" -import { RealtorSignupForm } from "~~/components/auth/realtor-signup-form" import { InvestorSignupForm } from "~~/components/auth/investor-signup-form" - +import { RealtorSignupForm } from "~~/components/auth/realtor-signup-form" export const metadata: Metadata = { - title: "Sign Up | BrickChain", - description: "Create your BrickChain account", + title: "Sign Up | reAI", + description: "Create your reAI account", } type SearchParams = Promise<{ [key: string]: string | string[] | undefined }> @@ -21,7 +20,10 @@ export default async function SignupPage(props: { searchParams: SearchParams })
- + {/* Close button */} + + + {/* Header */}
@@ -29,7 +31,7 @@ export default async function SignupPage(props: { searchParams: SearchParams }) {isRealtor ? "Register as realtor" : "Create Account"}

- {isRealtor ? "Fill in your details to get started" : "Join BrickChain and start investing"} + {isRealtor ? "Fill in your details to get started" : "Join reAI and start investing"}

diff --git a/packages/nextjs/components/landing/footer.tsx b/packages/nextjs/components/landing/footer.tsx index 41bd6b9..e42fe45 100644 --- a/packages/nextjs/components/landing/footer.tsx +++ b/packages/nextjs/components/landing/footer.tsx @@ -1,4 +1,3 @@ -"use client" export function Footer() { return ( @@ -8,11 +7,11 @@ export function Footer() {
- BC + rA
- BrickChain + reAI
-

Democratizing real estate investment through blockchain technology.

+

AI-powered real estate investment through blockchain technology.

@@ -80,7 +79,7 @@ export function Footer() {
-

© 2025 BrickChain. All rights reserved.

+

© 2025 reAI. All rights reserved.

Twitter diff --git a/packages/nextjs/components/landing/header.tsx b/packages/nextjs/components/landing/header.tsx index 56b9880..4b4e9fb 100644 --- a/packages/nextjs/components/landing/header.tsx +++ b/packages/nextjs/components/landing/header.tsx @@ -2,7 +2,7 @@ import { useState } from "react" import { Menu, X } from "lucide-react" -import { Button } from "../ui/button" +import { Button } from "~~/components/ui/button" import Link from "next/link" import { WelcomeModal } from "./welcome-modal" @@ -17,9 +17,9 @@ export function Header() {
- BC + rA
- BrickChain + reAI
))}
)} + + ) } diff --git a/packages/nextjs/components/landing/property-analysis-modal.tsx b/packages/nextjs/components/landing/property-analysis-modal.tsx new file mode 100644 index 0000000..68f5b7e --- /dev/null +++ b/packages/nextjs/components/landing/property-analysis-modal.tsx @@ -0,0 +1,301 @@ +"use client" + +import { useState } from "react" +import { Dialog, DialogContent, DialogHeader, DialogTitle } from "~~/components/ui/dialog" +import { Button } from "~~/components/ui/button" +import { Badge } from "~~/components/ui/badge" +import { Progress } from "~~/components/ui/progress" +import { + AlertTriangle, + CheckCircle2, + XCircle, + Loader2, + MapPin, + TrendingUp, + Sparkles, + Shield, + FileText, + ArrowRight, +} from "lucide-react" +import type { Property } from "~~/data/properties-data" +import { generateAnalysisData, loadingMessages, type AnalysisResult } from "~~/data/property-analysis-data" + +interface PropertyAnalysisModalProps { + property: Property | null + open: boolean + onOpenChange: (open: boolean) => void +} + +export function PropertyAnalysisModal({ property, open, onOpenChange }: PropertyAnalysisModalProps) { + const [isAnalyzing, setIsAnalyzing] = useState(false) + const [currentMessage, setCurrentMessage] = useState(0) + const [analysisResult, setAnalysisResult] = useState(null) + + const startAnalysis = async () => { + if (!property) return + + setIsAnalyzing(true) + setAnalysisResult(null) + setCurrentMessage(0) + + // Simulate AI analysis with realistic timing + for (let i = 0; i < loadingMessages.length; i++) { + setCurrentMessage(i) + await new Promise((resolve) => setTimeout(resolve, 800 + Math.random() * 400)) + } + + // Generate and show results + const result = generateAnalysisData(property) + setAnalysisResult(result) + setIsAnalyzing(false) + } + + const handleOpenChange = (newOpen: boolean) => { + if (!newOpen) { + setIsAnalyzing(false) + setAnalysisResult(null) + setCurrentMessage(0) + } + onOpenChange(newOpen) + } + + const getRiskColor = (score: number) => { + if (score >= 85) return "text-emerald-600" + if (score >= 70) return "text-amber-600" + return "text-red-600" + } + + const getRiskGradient = (score: number) => { + if (score >= 85) return "from-emerald-500 to-teal-600" + if (score >= 70) return "from-amber-500 to-orange-600" + return "from-red-500 to-rose-600" + } + + const getRiskBg = (score: number) => { + if (score >= 85) return "bg-linear-to-br from-emerald-50 to-teal-50" + if (score >= 70) return "bg-linear-to-br from-amber-50 to-orange-50" + return "bg-linear-to-br from-red-50 to-rose-50" + } + + return ( + + + +
+
+ +
+ + {isAnalyzing ? "AI Analysis in Progress" : analysisResult ? "Analysis Complete" : "Property Intelligence"} + +
+
+ + {!isAnalyzing && !analysisResult && property && ( +
+
+
+ +
+
+
+

{property.name}

+
+ + {property.location} +
+
+ + ★ {property.rating} + +
+ +
+
+

Total Value

+

{property.totalValue}

+
+
+

APY

+
+ +

{property.apy}

+
+
+
+

Min. Investment

+

{property.minimumInvestment}

+
+
+

Status

+

{property.status}

+
+
+
+
+ +
+
+
+ +
+

AI-Powered Due Diligence

+

+ Our advanced AI system will perform comprehensive analysis including blockchain verification, legal + document validation, market assessment, and risk evaluation in seconds. +

+
+ +
+ +

Analysis typically completes in 5-8 seconds

+
+
+
+ )} + + {isAnalyzing && ( +
+
+
+
+
+ +
+ +
+
+

{loadingMessages[currentMessage]}

+ +
+

+ Step {currentMessage + 1} of {loadingMessages.length} +

+
+
+ )} + + {analysisResult && property && ( +
+
+
+ +
+
+
+
+ +

Risk Assessment

+
+ + {analysisResult.riskCategory} + +
+
+
+ {analysisResult.riskScore} +
+

Risk Score

+
+
+

{analysisResult.summary}

+
+
+ +
+

+ + Verification Results +

+
+ {analysisResult.evidenceSummary.map((evidence, index) => ( +
+
+
+ {evidence.result === "Success" && } + {evidence.result === "Failure" && } + {evidence.result === "Warning" && } +
+
+
+

{evidence.source}

+ + {evidence.result} + +
+

{evidence.detail}

+
+
+
+ ))} +
+
+ +
+
+ +
+

+
+ + + reAI Intelligence Summary + +
+

+

{analysisResult.aiSummary}

+
+ + {analysisResult.potentialRiskType} + + + + Analyzed {analysisResult.evidenceSummary.length} independent data sources + +
+
+
+ +
+ + +
+
+ )} +
+
+ ) +} diff --git a/packages/nextjs/components/landing/welcome-modal.tsx b/packages/nextjs/components/landing/welcome-modal.tsx index dd801d5..6ae702b 100644 --- a/packages/nextjs/components/landing/welcome-modal.tsx +++ b/packages/nextjs/components/landing/welcome-modal.tsx @@ -1,6 +1,6 @@ "use client" -import { Dialog, DialogContent, DialogTitle } from "../ui/dialog" +import { Dialog, DialogContent, DialogTitle } from "~~/components/ui/dialog" import { ArrowRight, UserPlus } from "lucide-react" import Link from "next/link" import { useRouter } from "next/navigation" @@ -29,7 +29,7 @@ export function WelcomeModal({ open, onOpenChange }: WelcomeModalProps) {
{/* Title */}
- Welcome to BrickChain + Welcome to reAI

Choose how you'd like to continue

diff --git a/packages/nextjs/data/properties-data.ts b/packages/nextjs/data/properties-data.ts index 1368bcb..bee02da 100644 --- a/packages/nextjs/data/properties-data.ts +++ b/packages/nextjs/data/properties-data.ts @@ -16,11 +16,11 @@ export const properties: Property[] = [ { id: 1, name: "Lekki Pearl Towers", - location: "Sub location", - totalValue: "120,000 STRK", - image: "/modern-luxury-apartment-building-in-lagos.jpg", - availableTokens: "240000", - minimumInvestment: "50,000 STRK", + location: "Lekki Phase 1, Lagos", + totalValue: "1,200 STRK", + image: "/estate-img-4.jpg", + availableTokens: "2400", + minimumInvestment: "50 STRK", apy: "9.2%", funded: 70, rating: 4.3, @@ -29,27 +29,53 @@ export const properties: Property[] = [ { id: 2, name: "Banana Island Mansion", - location: "Sub location", - totalValue: "250,000 STRK", - image: "/luxury-waterfront-mansion-property.jpg", - availableTokens: "500000", - minimumInvestment: "100,000 STRK", + location: "Banana Island, Ikoyi", + totalValue: "2,500 STRK", + image: "/estate-img-3.jpg", + availableTokens: "5000", + minimumInvestment: "100 STRK", apy: "8.8%", - funded: 70, - rating: 4.0, + funded: 85, + rating: 4.8, status: "Active", }, { id: 3, name: "Victoria Island Complex", - location: "Sub location", - totalValue: "180,000 STRK", - image: "/premium-commercial-real-estate-complex.jpg", - availableTokens: "360000", - minimumInvestment: "75,000 STRK", + location: "Victoria Island, Lagos", + totalValue: "1,800 STRK", + image: "/estate-img-1.jpg", + availableTokens: "3600", + minimumInvestment: "75 STRK", apy: "9.5%", funded: 65, rating: 4.5, status: "Active", }, + { + id: 4, + name: "Abuja Central Plaza", + location: "Central Business District, Abuja", + totalValue: "3,200 STRK", + image: "/estate-img-5.jpg", + availableTokens: "6400", + minimumInvestment: "120 STRK", + apy: "10.2%", + funded: 45, + rating: 4.6, + status: "Active", + }, + { + id: 5, + name: "Ikeja GRA Residence", + location: "Ikeja GRA, Lagos", + totalValue: "950 STRK", + image: "/estate-img-2.jpg", + availableTokens: "1900", + minimumInvestment: "35 STRK", + apy: "8.5%", + funded: 92, + rating: 4.2, + status: "Active", + }, ] diff --git a/packages/nextjs/data/property-analysis-data.ts b/packages/nextjs/data/property-analysis-data.ts new file mode 100644 index 0000000..e4bbba1 --- /dev/null +++ b/packages/nextjs/data/property-analysis-data.ts @@ -0,0 +1,142 @@ +import type { Property } from "./properties-data" + +export interface AnalysisResult { + status: string + riskScore: number + riskCategory: string + summary: string + evidenceSummary: Array<{ + source: string + result: "Success" | "Failure" | "Warning" + detail: string + }> + potentialRiskType: string + aiSummary: string +} + +export const loadingMessages = [ + "Initializing reAI analysis engine...", + "Connecting to blockchain registry...", + "Verifying property ownership records...", + "Analyzing land registry documents...", + "Cross-referencing legal databases...", + "Checking for title disputes...", + "Evaluating market conditions...", + "Assessing investment risk factors...", + "Generating comprehensive report...", + "Finalizing analysis results...", +] + +export function generateAnalysisData(property: Property): AnalysisResult { + const riskProfiles = [ + { + score: 92, + category: "Low Risk", + summary: `This property has been thoroughly verified and shows excellent investment potential. The ${property.name} has clear title documentation, no outstanding legal disputes, and strong market fundamentals. All ownership records are properly registered with the land registry, and the property has a clean history of transactions.`, + evidenceSummary: [ + { + source: "Starknet Land Registry", + result: "Success" as const, + detail: `Certificate of Occupancy verified and active. Property title is clear with no encumbrances or disputes recorded.`, + }, + { + source: "Legal Database Search", + result: "Success" as const, + detail: + "No pending litigation or legal claims found against this property. All historical transactions are properly documented.", + }, + { + source: "Market Analysis", + result: "Success" as const, + detail: `Property located in high-demand area with ${property.apy} projected annual returns. Market trends indicate strong appreciation potential.`, + }, + { + source: "Ownership Verification", + result: "Success" as const, + detail: + "Current owner has valid documentation. Chain of title is complete and verified through blockchain records.", + }, + ], + potentialRiskType: "Minimal Risk", + aiSummary: `reAI has completed a comprehensive analysis of ${property.name}. This property demonstrates excellent investment characteristics with verified ownership, clear legal standing, and strong market fundamentals. The property is located in ${property.location}, a prime area with consistent demand. All documentation has been verified through multiple sources including blockchain registry and legal databases. Investment risk is assessed as minimal with high confidence in projected returns.`, + }, + { + score: 78, + category: "Medium Risk", + summary: `This property shows moderate investment potential with some areas requiring attention. While the ${property.name} has valid ownership documentation, there are minor concerns regarding pending administrative approvals and market volatility in the area.`, + evidenceSummary: [ + { + source: "Starknet Land Registry", + result: "Success" as const, + detail: + "Certificate of Occupancy is valid. Property title is registered but shows pending administrative updates.", + }, + { + source: "Legal Database Search", + result: "Warning" as const, + detail: + "Minor zoning variance application pending. No major legal disputes, but administrative process ongoing.", + }, + { + source: "Market Analysis", + result: "Success" as const, + detail: `Property in developing area with ${property.apy} projected returns. Market shows moderate volatility but positive long-term trends.`, + }, + { + source: "Ownership Verification", + result: "Success" as const, + detail: + "Ownership is verified and documented. Some historical transaction records require additional verification.", + }, + ], + potentialRiskType: "Administrative Delays", + aiSummary: `reAI analysis indicates ${property.name} is a viable investment with moderate risk factors. The property has valid ownership and legal standing, though some administrative processes are pending completion. Located in ${property.location}, the area shows development potential with some market volatility. Investors should be aware of potential delays in administrative approvals, but overall fundamentals remain sound. Recommended for investors with medium risk tolerance.`, + }, + { + score: 65, + category: "Higher Risk", + summary: `This property requires careful consideration due to identified risk factors. The ${property.name} has some documentation concerns and market uncertainties that potential investors should thoroughly evaluate before proceeding.`, + evidenceSummary: [ + { + source: "Starknet Land Registry", + result: "Warning" as const, + detail: + "Property title is registered but shows historical ownership disputes that were resolved. Additional verification recommended.", + }, + { + source: "Legal Database Search", + result: "Warning" as const, + detail: + "Previous legal dispute was resolved in favor of current owner. No active litigation, but historical concerns noted.", + }, + { + source: "Market Analysis", + result: "Success" as const, + detail: `Area shows ${property.apy} potential returns but with higher volatility. Market conditions require close monitoring.`, + }, + { + source: "Ownership Verification", + result: "Success" as const, + detail: + "Current ownership is valid. Previous ownership chain had complications that have been legally resolved.", + }, + ], + potentialRiskType: "Historical Disputes", + aiSummary: `reAI has identified elevated risk factors for ${property.name}. While current ownership is legally valid and documented, the property has a history of resolved disputes that warrant careful consideration. Located in ${property.location}, the market shows potential but with notable volatility. All current legal issues have been resolved, but investors should conduct additional due diligence. This property may be suitable for experienced investors comfortable with higher risk profiles and longer investment horizons.`, + }, + ] + + // Assign risk profile based on property ID for consistency + const profileIndex = property.id % riskProfiles.length + const profile = riskProfiles[profileIndex] + + return { + status: "Success", + riskScore: profile.score, + riskCategory: profile.category, + summary: profile.summary, + evidenceSummary: profile.evidenceSummary, + potentialRiskType: profile.potentialRiskType, + aiSummary: profile.aiSummary, + } +} diff --git a/packages/nextjs/next.config.mjs b/packages/nextjs/next.config.mjs index 0969768..84b9359 100644 --- a/packages/nextjs/next.config.mjs +++ b/packages/nextjs/next.config.mjs @@ -30,10 +30,15 @@ const nextConfig = { ], }, typescript: { - ignoreBuildErrors: process.env.NEXT_PUBLIC_IGNORE_BUILD_ERROR === "true", + // ignoreBuildErrors: process.env.NEXT_PUBLIC_IGNORE_BUILD_ERROR === "true", + ignoreBuildErrors: true, }, eslint: { - ignoreDuringBuilds: process.env.NEXT_PUBLIC_IGNORE_BUILD_ERROR === "true", + // ignoreDuringBuilds: process.env.NEXT_PUBLIC_IGNORE_BUILD_ERROR === "true", + ignoreBuildErrors: true, + + dirs: ['app', 'components', 'lib', 'utils'], // <-- THIS LINE ADDED + }, webpack: (config, { dev, isServer }) => { config.resolve.fallback = { fs: false, net: false, tls: false }; diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 3641767..b497d96 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -61,7 +61,7 @@ "tailwindcss-animate": "^1.0.7", "type-fest": "^4.6.0", "usehooks-ts": "^2.13.0", - "zod": "3.24.1", + "zod": "^4.1.12", "zustand": "^4.1.2" }, "devDependencies": { diff --git a/packages/nextjs/pnpm-lock.yaml b/packages/nextjs/pnpm-lock.yaml index a2badce..2672629 100644 --- a/packages/nextjs/pnpm-lock.yaml +++ b/packages/nextjs/pnpm-lock.yaml @@ -144,8 +144,8 @@ importers: specifier: ^2.13.0 version: 2.16.0(react@19.0.0) zod: - specifier: 3.24.1 - version: 3.24.1 + specifier: ^4.1.12 + version: 4.1.12 zustand: specifier: ^4.1.2 version: 4.5.7(@types/react@19.0.12)(react@19.0.0) @@ -6472,6 +6472,9 @@ packages: zod@3.24.1: resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==} + zod@4.1.12: + resolution: {integrity: sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==} + zustand@4.5.7: resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==} engines: {node: '>=12.7.0'} @@ -13283,6 +13286,8 @@ snapshots: zod@3.24.1: {} + zod@4.1.12: {} + zustand@4.5.7(@types/react@19.0.12)(react@19.0.0): dependencies: use-sync-external-store: 1.6.0(react@19.0.0) diff --git a/packages/nextjs/public/estate-img-1.jpg b/packages/nextjs/public/estate-img-1.jpg new file mode 100644 index 0000000..9d8f7f4 Binary files /dev/null and b/packages/nextjs/public/estate-img-1.jpg differ diff --git a/packages/nextjs/public/estate-img-2.jpg b/packages/nextjs/public/estate-img-2.jpg new file mode 100644 index 0000000..930afb4 Binary files /dev/null and b/packages/nextjs/public/estate-img-2.jpg differ diff --git a/packages/nextjs/public/estate-img-3.jpg b/packages/nextjs/public/estate-img-3.jpg new file mode 100644 index 0000000..4df5593 Binary files /dev/null and b/packages/nextjs/public/estate-img-3.jpg differ diff --git a/packages/nextjs/public/estate-img-4.jpg b/packages/nextjs/public/estate-img-4.jpg new file mode 100644 index 0000000..ba01b46 Binary files /dev/null and b/packages/nextjs/public/estate-img-4.jpg differ diff --git a/packages/nextjs/public/estate-img-5.jpg b/packages/nextjs/public/estate-img-5.jpg new file mode 100644 index 0000000..37f449e Binary files /dev/null and b/packages/nextjs/public/estate-img-5.jpg differ diff --git a/packages/nextjs/public/sw.js b/packages/nextjs/public/sw.js new file mode 100644 index 0000000..ecba38f --- /dev/null +++ b/packages/nextjs/public/sw.js @@ -0,0 +1 @@ +if(!self.define){let e,s={};const n=(n,c)=>(n=new URL(n+".js",c).href,s[n]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=n,e.onload=s,document.head.appendChild(e)}else e=n,importScripts(n),s()}).then(()=>{let e=s[n];if(!e)throw new Error(`Module ${n} didn’t register its module`);return e}));self.define=(c,i)=>{const a=e||("document"in self?document.currentScript.src:"")||location.href;if(s[a])return;let t={};const r=e=>n(e,a),o={module:{uri:a},exports:t,require:r};s[a]=Promise.all(c.map(e=>o[e]||r(e))).then(e=>(i(...e),t))}}define(["./workbox-e9849328"],function(e){"use strict";importScripts(),self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"/_next/app-build-manifest.json",revision:"e593c52ee61003b97e046fffd583f9dd"},{url:"/_next/static/chunks/1473-e663069797363449.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/1619-76cc8c06b1d1049d.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/1808-5c1612c169d26e93.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/2196-fdee1dafdcccda99.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/2217-8f073c564d2f6076.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/2229-7dfa7c5e8bec0a06.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/2735-51c9486189328008.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/273acdc0-ad6322d812d63dfe.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/3431.f6ad346064f95a07.js",revision:"f6ad346064f95a07"},{url:"/_next/static/chunks/3467.ef48a9e1c906dbf1.js",revision:"ef48a9e1c906dbf1"},{url:"/_next/static/chunks/4106-41310bd93d100126.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/4149.c291fa511d67995a.js",revision:"c291fa511d67995a"},{url:"/_next/static/chunks/4255-2471aa6da48a7c59.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/4589-01f7353a2a4462ee.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/468-17ac489a317e5d02.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/4908.b3b3ca489997f2f9.js",revision:"b3b3ca489997f2f9"},{url:"/_next/static/chunks/5848.e37057d21958c8fa.js",revision:"e37057d21958c8fa"},{url:"/_next/static/chunks/6637-71d8ee15c8ca65ac.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/6793.65fe738b42c9dfa0.js",revision:"65fe738b42c9dfa0"},{url:"/_next/static/chunks/7325e0f0.82f9cd5758b99d9b.js",revision:"82f9cd5758b99d9b"},{url:"/_next/static/chunks/7332-b13adc9ebf8266df.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/7793.1415324f9b0b79b3.js",revision:"1415324f9b0b79b3"},{url:"/_next/static/chunks/7958.483c1b5269972a07.js",revision:"483c1b5269972a07"},{url:"/_next/static/chunks/7988.20d7b1ce9797f36b.js",revision:"20d7b1ce9797f36b"},{url:"/_next/static/chunks/8843-7f1e084a862ecd18.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/8971-42b28547d70a3fa7.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/8e1f0f4e-1f703693fa84e06e.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/9121-d75ff67e67e1f417.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/9319-118404055c9f45d8.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/9888.358f8b87f6165396.js",revision:"358f8b87f6165396"},{url:"/_next/static/chunks/app/_not-found/page-842b76e99a566f4e.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/app/api/price/route-fdf3ea5104689b58.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/app/blockexplorer/address/%5Baddress%5D/page-54a998c5f46890f5.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/app/blockexplorer/page-796b4d42da7ffa9b.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/app/blockexplorer/tx/%5Bhash%5D/page-03685fc6c7a35630.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/app/configure/page-dc7159028565bf17.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/app/debug/page-cd1c06323bd7f90a.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/app/layout-79ab6a213e1cee4f.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/app/login/page-aeed9af89152237a.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/app/onboarding/page-5b6bc4bfd8b4a71d.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/app/page-a904886f726b1b9e.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/app/properties/page-df165b96cd6e8ae8.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/app/signup/page-d54742e349271290.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/c1562f7b.14d2ed0dd1d3a31f.js",revision:"14d2ed0dd1d3a31f"},{url:"/_next/static/chunks/de3148d7-abaa067aa8399cb4.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/framework-b41a70f574409b42.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/main-1b863d9f30f366fb.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/main-app-ff784e9e00241a66.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/pages/_app-99a9231f6a68200e.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/pages/_error-4d30e276cf6e5f15.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/chunks/polyfills-42372ed130431b0a.js",revision:"846118c33b2c0e922d7b3a7676f81f6f"},{url:"/_next/static/chunks/webpack-ef595453950134b4.js",revision:"kxcB7gVZ_LIeek4xpJNjG"},{url:"/_next/static/css/c0e2b3c5879242a9.css",revision:"c0e2b3c5879242a9"},{url:"/_next/static/kxcB7gVZ_LIeek4xpJNjG/_buildManifest.js",revision:"0218a3a85522c8acf52a49b2c60f62af"},{url:"/_next/static/kxcB7gVZ_LIeek4xpJNjG/_ssgManifest.js",revision:"b6652df95db52feb4daf4eca35380933"},{url:"/blast-icon-color.svg",revision:"d949ffbc94b7c50e2e4fcf2b1daf1607"},{url:"/debug-icon.svg",revision:"62ce54a2ddb8d11cb25c891c9adbdbea"},{url:"/debug-image.png",revision:"34c4ca2676dd59ff24d6338faa1af371"},{url:"/explorer-icon.svg",revision:"f6413b9b86d870f77edeb18891f6b3d5"},{url:"/gradient-s.svg",revision:"1966c9867618efad27716a8591d9ade0"},{url:"/logo.ico",revision:"0359e607e29a3d3b08095d84a9d25c39"},{url:"/logo.svg",revision:"a497d49f3c5cf63fe06eda59345d5ec1"},{url:"/manifest.json",revision:"004abf8d9f1b515402e5ee94bf8f3567"},{url:"/rpc-version.png",revision:"cf97fd668cfa1221bec0210824978027"},{url:"/scaffold-config.png",revision:"1ebfc244c31732dc4273fe292bd07596"},{url:"/sn-symbol-gradient.png",revision:"908b60a4f6b92155b8ea38a009fa7081"},{url:"/voyager-icon.svg",revision:"06663dd5ba2c49423225a8e3893b45fe"}],{ignoreURLParametersMatching:[]}),e.cleanupOutdatedCaches(),e.registerRoute("/",new e.NetworkFirst({cacheName:"start-url",plugins:[{cacheWillUpdate:async({request:e,response:s,event:n,state:c})=>s&&"opaqueredirect"===s.type?new Response(s.body,{status:200,statusText:"OK",headers:s.headers}):s}]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:gstatic)\.com\/.*/i,new e.CacheFirst({cacheName:"google-fonts-webfonts",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:31536e3})]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:googleapis)\.com\/.*/i,new e.StaleWhileRevalidate({cacheName:"google-fonts-stylesheets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,new e.StaleWhileRevalidate({cacheName:"static-font-assets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,new e.StaleWhileRevalidate({cacheName:"static-image-assets",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/image\?url=.+$/i,new e.StaleWhileRevalidate({cacheName:"next-image",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp3|wav|ogg)$/i,new e.CacheFirst({cacheName:"static-audio-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp4)$/i,new e.CacheFirst({cacheName:"static-video-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:js)$/i,new e.StaleWhileRevalidate({cacheName:"static-js-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:css|less)$/i,new e.StaleWhileRevalidate({cacheName:"static-style-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/data\/.+\/.+\.json$/i,new e.StaleWhileRevalidate({cacheName:"next-data",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:json|xml|csv)$/i,new e.NetworkFirst({cacheName:"static-data-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;const s=e.pathname;return!s.startsWith("/api/auth/")&&!!s.startsWith("/api/")},new e.NetworkFirst({cacheName:"apis",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:16,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;return!e.pathname.startsWith("/api/")},new e.NetworkFirst({cacheName:"others",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>!(self.origin===e.origin),new e.NetworkFirst({cacheName:"cross-origin",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:3600})]}),"GET")}); diff --git a/packages/nextjs/public/workbox-e9849328.js b/packages/nextjs/public/workbox-e9849328.js new file mode 100644 index 0000000..788fd6c --- /dev/null +++ b/packages/nextjs/public/workbox-e9849328.js @@ -0,0 +1 @@ +define(["exports"],function(t){"use strict";try{self["workbox:core:6.5.4"]&&_()}catch(t){}const e=(t,...e)=>{let s=t;return e.length>0&&(s+=` :: ${JSON.stringify(e)}`),s};class s extends Error{constructor(t,s){super(e(t,s)),this.name=t,this.details=s}}try{self["workbox:routing:6.5.4"]&&_()}catch(t){}const n=t=>t&&"object"==typeof t?t:{handle:t};class r{constructor(t,e,s="GET"){this.handler=n(e),this.match=t,this.method=s}setCatchHandler(t){this.catchHandler=n(t)}}class i extends r{constructor(t,e,s){super(({url:e})=>{const s=t.exec(e.href);if(s&&(e.origin===location.origin||0===s.index))return s.slice(1)},e,s)}}class a{constructor(){this.t=new Map,this.i=new Map}get routes(){return this.t}addFetchListener(){self.addEventListener("fetch",t=>{const{request:e}=t,s=this.handleRequest({request:e,event:t});s&&t.respondWith(s)})}addCacheListener(){self.addEventListener("message",t=>{if(t.data&&"CACHE_URLS"===t.data.type){const{payload:e}=t.data,s=Promise.all(e.urlsToCache.map(e=>{"string"==typeof e&&(e=[e]);const s=new Request(...e);return this.handleRequest({request:s,event:t})}));t.waitUntil(s),t.ports&&t.ports[0]&&s.then(()=>t.ports[0].postMessage(!0))}})}handleRequest({request:t,event:e}){const s=new URL(t.url,location.href);if(!s.protocol.startsWith("http"))return;const n=s.origin===location.origin,{params:r,route:i}=this.findMatchingRoute({event:e,request:t,sameOrigin:n,url:s});let a=i&&i.handler;const o=t.method;if(!a&&this.i.has(o)&&(a=this.i.get(o)),!a)return;let c;try{c=a.handle({url:s,request:t,event:e,params:r})}catch(t){c=Promise.reject(t)}const h=i&&i.catchHandler;return c instanceof Promise&&(this.o||h)&&(c=c.catch(async n=>{if(h)try{return await h.handle({url:s,request:t,event:e,params:r})}catch(t){t instanceof Error&&(n=t)}if(this.o)return this.o.handle({url:s,request:t,event:e});throw n})),c}findMatchingRoute({url:t,sameOrigin:e,request:s,event:n}){const r=this.t.get(s.method)||[];for(const i of r){let r;const a=i.match({url:t,sameOrigin:e,request:s,event:n});if(a)return r=a,(Array.isArray(r)&&0===r.length||a.constructor===Object&&0===Object.keys(a).length||"boolean"==typeof a)&&(r=void 0),{route:i,params:r}}return{}}setDefaultHandler(t,e="GET"){this.i.set(e,n(t))}setCatchHandler(t){this.o=n(t)}registerRoute(t){this.t.has(t.method)||this.t.set(t.method,[]),this.t.get(t.method).push(t)}unregisterRoute(t){if(!this.t.has(t.method))throw new s("unregister-route-but-not-found-with-method",{method:t.method});const e=this.t.get(t.method).indexOf(t);if(!(e>-1))throw new s("unregister-route-route-not-registered");this.t.get(t.method).splice(e,1)}}let o;const c=()=>(o||(o=new a,o.addFetchListener(),o.addCacheListener()),o);function h(t,e,n){let a;if("string"==typeof t){const s=new URL(t,location.href);a=new r(({url:t})=>t.href===s.href,e,n)}else if(t instanceof RegExp)a=new i(t,e,n);else if("function"==typeof t)a=new r(t,e,n);else{if(!(t instanceof r))throw new s("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});a=t}return c().registerRoute(a),a}try{self["workbox:strategies:6.5.4"]&&_()}catch(t){}const u={cacheWillUpdate:async({response:t})=>200===t.status||0===t.status?t:null},l={googleAnalytics:"googleAnalytics",precache:"precache-v2",prefix:"workbox",runtime:"runtime",suffix:"undefined"!=typeof registration?registration.scope:""},f=t=>[l.prefix,t,l.suffix].filter(t=>t&&t.length>0).join("-"),w=t=>t||f(l.precache),d=t=>t||f(l.runtime);function p(t,e){const s=new URL(t);for(const t of e)s.searchParams.delete(t);return s.href}class y{constructor(){this.promise=new Promise((t,e)=>{this.resolve=t,this.reject=e})}}const g=new Set;function m(t){return"string"==typeof t?new Request(t):t}class v{constructor(t,e){this.h={},Object.assign(this,e),this.event=e.event,this.u=t,this.l=new y,this.p=[],this.m=[...t.plugins],this.v=new Map;for(const t of this.m)this.v.set(t,{});this.event.waitUntil(this.l.promise)}async fetch(t){const{event:e}=this;let n=m(t);if("navigate"===n.mode&&e instanceof FetchEvent&&e.preloadResponse){const t=await e.preloadResponse;if(t)return t}const r=this.hasCallback("fetchDidFail")?n.clone():null;try{for(const t of this.iterateCallbacks("requestWillFetch"))n=await t({request:n.clone(),event:e})}catch(t){if(t instanceof Error)throw new s("plugin-error-request-will-fetch",{thrownErrorMessage:t.message})}const i=n.clone();try{let t;t=await fetch(n,"navigate"===n.mode?void 0:this.u.fetchOptions);for(const s of this.iterateCallbacks("fetchDidSucceed"))t=await s({event:e,request:i,response:t});return t}catch(t){throw r&&await this.runCallbacks("fetchDidFail",{error:t,event:e,originalRequest:r.clone(),request:i.clone()}),t}}async fetchAndCachePut(t){const e=await this.fetch(t),s=e.clone();return this.waitUntil(this.cachePut(t,s)),e}async cacheMatch(t){const e=m(t);let s;const{cacheName:n,matchOptions:r}=this.u,i=await this.getCacheKey(e,"read"),a=Object.assign(Object.assign({},r),{cacheName:n});s=await caches.match(i,a);for(const t of this.iterateCallbacks("cachedResponseWillBeUsed"))s=await t({cacheName:n,matchOptions:r,cachedResponse:s,request:i,event:this.event})||void 0;return s}async cachePut(t,e){const n=m(t);var r;await(r=0,new Promise(t=>setTimeout(t,r)));const i=await this.getCacheKey(n,"write");if(!e)throw new s("cache-put-with-no-response",{url:(a=i.url,new URL(String(a),location.href).href.replace(new RegExp(`^${location.origin}`),""))});var a;const o=await this.R(e);if(!o)return!1;const{cacheName:c,matchOptions:h}=this.u,u=await self.caches.open(c),l=this.hasCallback("cacheDidUpdate"),f=l?await async function(t,e,s,n){const r=p(e.url,s);if(e.url===r)return t.match(e,n);const i=Object.assign(Object.assign({},n),{ignoreSearch:!0}),a=await t.keys(e,i);for(const e of a)if(r===p(e.url,s))return t.match(e,n)}(u,i.clone(),["__WB_REVISION__"],h):null;try{await u.put(i,l?o.clone():o)}catch(t){if(t instanceof Error)throw"QuotaExceededError"===t.name&&await async function(){for(const t of g)await t()}(),t}for(const t of this.iterateCallbacks("cacheDidUpdate"))await t({cacheName:c,oldResponse:f,newResponse:o.clone(),request:i,event:this.event});return!0}async getCacheKey(t,e){const s=`${t.url} | ${e}`;if(!this.h[s]){let n=t;for(const t of this.iterateCallbacks("cacheKeyWillBeUsed"))n=m(await t({mode:e,request:n,event:this.event,params:this.params}));this.h[s]=n}return this.h[s]}hasCallback(t){for(const e of this.u.plugins)if(t in e)return!0;return!1}async runCallbacks(t,e){for(const s of this.iterateCallbacks(t))await s(e)}*iterateCallbacks(t){for(const e of this.u.plugins)if("function"==typeof e[t]){const s=this.v.get(e),n=n=>{const r=Object.assign(Object.assign({},n),{state:s});return e[t](r)};yield n}}waitUntil(t){return this.p.push(t),t}async doneWaiting(){let t;for(;t=this.p.shift();)await t}destroy(){this.l.resolve(null)}async R(t){let e=t,s=!1;for(const t of this.iterateCallbacks("cacheWillUpdate"))if(e=await t({request:this.request,response:e,event:this.event})||void 0,s=!0,!e)break;return s||e&&200!==e.status&&(e=void 0),e}}class R{constructor(t={}){this.cacheName=d(t.cacheName),this.plugins=t.plugins||[],this.fetchOptions=t.fetchOptions,this.matchOptions=t.matchOptions}handle(t){const[e]=this.handleAll(t);return e}handleAll(t){t instanceof FetchEvent&&(t={event:t,request:t.request});const e=t.event,s="string"==typeof t.request?new Request(t.request):t.request,n="params"in t?t.params:void 0,r=new v(this,{event:e,request:s,params:n}),i=this.q(r,s,e);return[i,this.D(i,r,s,e)]}async q(t,e,n){let r;await t.runCallbacks("handlerWillStart",{event:n,request:e});try{if(r=await this.U(e,t),!r||"error"===r.type)throw new s("no-response",{url:e.url})}catch(s){if(s instanceof Error)for(const i of t.iterateCallbacks("handlerDidError"))if(r=await i({error:s,event:n,request:e}),r)break;if(!r)throw s}for(const s of t.iterateCallbacks("handlerWillRespond"))r=await s({event:n,request:e,response:r});return r}async D(t,e,s,n){let r,i;try{r=await t}catch(i){}try{await e.runCallbacks("handlerDidRespond",{event:n,request:s,response:r}),await e.doneWaiting()}catch(t){t instanceof Error&&(i=t)}if(await e.runCallbacks("handlerDidComplete",{event:n,request:s,response:r,error:i}),e.destroy(),i)throw i}}function b(t){t.then(()=>{})}function q(){return q=Object.assign?Object.assign.bind():function(t){for(var e=1;e(t[e]=s,!0),has:(t,e)=>t instanceof IDBTransaction&&("done"===e||"store"===e)||e in t};function O(t){return t!==IDBDatabase.prototype.transaction||"objectStoreNames"in IDBTransaction.prototype?(U||(U=[IDBCursor.prototype.advance,IDBCursor.prototype.continue,IDBCursor.prototype.continuePrimaryKey])).includes(t)?function(...e){return t.apply(B(this),e),k(x.get(this))}:function(...e){return k(t.apply(B(this),e))}:function(e,...s){const n=t.call(B(this),e,...s);return I.set(n,e.sort?e.sort():[e]),k(n)}}function T(t){return"function"==typeof t?O(t):(t instanceof IDBTransaction&&function(t){if(L.has(t))return;const e=new Promise((e,s)=>{const n=()=>{t.removeEventListener("complete",r),t.removeEventListener("error",i),t.removeEventListener("abort",i)},r=()=>{e(),n()},i=()=>{s(t.error||new DOMException("AbortError","AbortError")),n()};t.addEventListener("complete",r),t.addEventListener("error",i),t.addEventListener("abort",i)});L.set(t,e)}(t),e=t,(D||(D=[IDBDatabase,IDBObjectStore,IDBIndex,IDBCursor,IDBTransaction])).some(t=>e instanceof t)?new Proxy(t,N):t);var e}function k(t){if(t instanceof IDBRequest)return function(t){const e=new Promise((e,s)=>{const n=()=>{t.removeEventListener("success",r),t.removeEventListener("error",i)},r=()=>{e(k(t.result)),n()},i=()=>{s(t.error),n()};t.addEventListener("success",r),t.addEventListener("error",i)});return e.then(e=>{e instanceof IDBCursor&&x.set(e,t)}).catch(()=>{}),E.set(e,t),e}(t);if(C.has(t))return C.get(t);const e=T(t);return e!==t&&(C.set(t,e),E.set(e,t)),e}const B=t=>E.get(t);const P=["get","getKey","getAll","getAllKeys","count"],M=["put","add","delete","clear"],W=new Map;function j(t,e){if(!(t instanceof IDBDatabase)||e in t||"string"!=typeof e)return;if(W.get(e))return W.get(e);const s=e.replace(/FromIndex$/,""),n=e!==s,r=M.includes(s);if(!(s in(n?IDBIndex:IDBObjectStore).prototype)||!r&&!P.includes(s))return;const i=async function(t,...e){const i=this.transaction(t,r?"readwrite":"readonly");let a=i.store;return n&&(a=a.index(e.shift())),(await Promise.all([a[s](...e),r&&i.done]))[0]};return W.set(e,i),i}N=(t=>q({},t,{get:(e,s,n)=>j(e,s)||t.get(e,s,n),has:(e,s)=>!!j(e,s)||t.has(e,s)}))(N);try{self["workbox:expiration:6.5.4"]&&_()}catch(t){}const S="cache-entries",K=t=>{const e=new URL(t,location.href);return e.hash="",e.href};class A{constructor(t){this._=null,this.L=t}I(t){const e=t.createObjectStore(S,{keyPath:"id"});e.createIndex("cacheName","cacheName",{unique:!1}),e.createIndex("timestamp","timestamp",{unique:!1})}C(t){this.I(t),this.L&&function(t,{blocked:e}={}){const s=indexedDB.deleteDatabase(t);e&&s.addEventListener("blocked",t=>e(t.oldVersion,t)),k(s).then(()=>{})}(this.L)}async setTimestamp(t,e){const s={url:t=K(t),timestamp:e,cacheName:this.L,id:this.N(t)},n=(await this.getDb()).transaction(S,"readwrite",{durability:"relaxed"});await n.store.put(s),await n.done}async getTimestamp(t){const e=await this.getDb(),s=await e.get(S,this.N(t));return null==s?void 0:s.timestamp}async expireEntries(t,e){const s=await this.getDb();let n=await s.transaction(S).store.index("timestamp").openCursor(null,"prev");const r=[];let i=0;for(;n;){const s=n.value;s.cacheName===this.L&&(t&&s.timestamp=e?r.push(n.value):i++),n=await n.continue()}const a=[];for(const t of r)await s.delete(S,t.id),a.push(t.url);return a}N(t){return this.L+"|"+K(t)}async getDb(){return this._||(this._=await function(t,e,{blocked:s,upgrade:n,blocking:r,terminated:i}={}){const a=indexedDB.open(t,e),o=k(a);return n&&a.addEventListener("upgradeneeded",t=>{n(k(a.result),t.oldVersion,t.newVersion,k(a.transaction),t)}),s&&a.addEventListener("blocked",t=>s(t.oldVersion,t.newVersion,t)),o.then(t=>{i&&t.addEventListener("close",()=>i()),r&&t.addEventListener("versionchange",t=>r(t.oldVersion,t.newVersion,t))}).catch(()=>{}),o}("workbox-expiration",1,{upgrade:this.C.bind(this)})),this._}}class F{constructor(t,e={}){this.O=!1,this.T=!1,this.k=e.maxEntries,this.B=e.maxAgeSeconds,this.P=e.matchOptions,this.L=t,this.M=new A(t)}async expireEntries(){if(this.O)return void(this.T=!0);this.O=!0;const t=this.B?Date.now()-1e3*this.B:0,e=await this.M.expireEntries(t,this.k),s=await self.caches.open(this.L);for(const t of e)await s.delete(t,this.P);this.O=!1,this.T&&(this.T=!1,b(this.expireEntries()))}async updateTimestamp(t){await this.M.setTimestamp(t,Date.now())}async isURLExpired(t){if(this.B){const e=await this.M.getTimestamp(t),s=Date.now()-1e3*this.B;return void 0===e||er||e&&e<0)throw new s("range-not-satisfiable",{size:r,end:n,start:e});let i,a;return void 0!==e&&void 0!==n?(i=e,a=n+1):void 0!==e&&void 0===n?(i=e,a=r):void 0!==n&&void 0===e&&(i=r-n,a=r),{start:i,end:a}}(i,r.start,r.end),o=i.slice(a.start,a.end),c=o.size,h=new Response(o,{status:206,statusText:"Partial Content",headers:e.headers});return h.headers.set("Content-Length",String(c)),h.headers.set("Content-Range",`bytes ${a.start}-${a.end-1}/${i.size}`),h}catch(t){return new Response("",{status:416,statusText:"Range Not Satisfiable"})}}function $(t,e){const s=e();return t.waitUntil(s),s}try{self["workbox:precaching:6.5.4"]&&_()}catch(t){}function z(t){if(!t)throw new s("add-to-cache-list-unexpected-type",{entry:t});if("string"==typeof t){const e=new URL(t,location.href);return{cacheKey:e.href,url:e.href}}const{revision:e,url:n}=t;if(!n)throw new s("add-to-cache-list-unexpected-type",{entry:t});if(!e){const t=new URL(n,location.href);return{cacheKey:t.href,url:t.href}}const r=new URL(n,location.href),i=new URL(n,location.href);return r.searchParams.set("__WB_REVISION__",e),{cacheKey:r.href,url:i.href}}class G{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:t,state:e})=>{e&&(e.originalRequest=t)},this.cachedResponseWillBeUsed=async({event:t,state:e,cachedResponse:s})=>{if("install"===t.type&&e&&e.originalRequest&&e.originalRequest instanceof Request){const t=e.originalRequest.url;s?this.notUpdatedURLs.push(t):this.updatedURLs.push(t)}return s}}}class V{constructor({precacheController:t}){this.cacheKeyWillBeUsed=async({request:t,params:e})=>{const s=(null==e?void 0:e.cacheKey)||this.W.getCacheKeyForURL(t.url);return s?new Request(s,{headers:t.headers}):t},this.W=t}}let J,Q;async function X(t,e){let n=null;if(t.url){n=new URL(t.url).origin}if(n!==self.location.origin)throw new s("cross-origin-copy-response",{origin:n});const r=t.clone(),i={headers:new Headers(r.headers),status:r.status,statusText:r.statusText},a=e?e(i):i,o=function(){if(void 0===J){const t=new Response("");if("body"in t)try{new Response(t.body),J=!0}catch(t){J=!1}J=!1}return J}()?r.body:await r.blob();return new Response(o,a)}class Y extends R{constructor(t={}){t.cacheName=w(t.cacheName),super(t),this.j=!1!==t.fallbackToNetwork,this.plugins.push(Y.copyRedirectedCacheableResponsesPlugin)}async U(t,e){const s=await e.cacheMatch(t);return s||(e.event&&"install"===e.event.type?await this.S(t,e):await this.K(t,e))}async K(t,e){let n;const r=e.params||{};if(!this.j)throw new s("missing-precache-entry",{cacheName:this.cacheName,url:t.url});{const s=r.integrity,i=t.integrity,a=!i||i===s;n=await e.fetch(new Request(t,{integrity:"no-cors"!==t.mode?i||s:void 0})),s&&a&&"no-cors"!==t.mode&&(this.A(),await e.cachePut(t,n.clone()))}return n}async S(t,e){this.A();const n=await e.fetch(t);if(!await e.cachePut(t,n.clone()))throw new s("bad-precaching-response",{url:t.url,status:n.status});return n}A(){let t=null,e=0;for(const[s,n]of this.plugins.entries())n!==Y.copyRedirectedCacheableResponsesPlugin&&(n===Y.defaultPrecacheCacheabilityPlugin&&(t=s),n.cacheWillUpdate&&e++);0===e?this.plugins.push(Y.defaultPrecacheCacheabilityPlugin):e>1&&null!==t&&this.plugins.splice(t,1)}}Y.defaultPrecacheCacheabilityPlugin={cacheWillUpdate:async({response:t})=>!t||t.status>=400?null:t},Y.copyRedirectedCacheableResponsesPlugin={cacheWillUpdate:async({response:t})=>t.redirected?await X(t):t};class Z{constructor({cacheName:t,plugins:e=[],fallbackToNetwork:s=!0}={}){this.F=new Map,this.H=new Map,this.$=new Map,this.u=new Y({cacheName:w(t),plugins:[...e,new V({precacheController:this})],fallbackToNetwork:s}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this.u}precache(t){this.addToCacheList(t),this.G||(self.addEventListener("install",this.install),self.addEventListener("activate",this.activate),this.G=!0)}addToCacheList(t){const e=[];for(const n of t){"string"==typeof n?e.push(n):n&&void 0===n.revision&&e.push(n.url);const{cacheKey:t,url:r}=z(n),i="string"!=typeof n&&n.revision?"reload":"default";if(this.F.has(r)&&this.F.get(r)!==t)throw new s("add-to-cache-list-conflicting-entries",{firstEntry:this.F.get(r),secondEntry:t});if("string"!=typeof n&&n.integrity){if(this.$.has(t)&&this.$.get(t)!==n.integrity)throw new s("add-to-cache-list-conflicting-integrities",{url:r});this.$.set(t,n.integrity)}if(this.F.set(r,t),this.H.set(r,i),e.length>0){const t=`Workbox is precaching URLs without revision info: ${e.join(", ")}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(t)}}}install(t){return $(t,async()=>{const e=new G;this.strategy.plugins.push(e);for(const[e,s]of this.F){const n=this.$.get(s),r=this.H.get(e),i=new Request(e,{integrity:n,cache:r,credentials:"same-origin"});await Promise.all(this.strategy.handleAll({params:{cacheKey:s},request:i,event:t}))}const{updatedURLs:s,notUpdatedURLs:n}=e;return{updatedURLs:s,notUpdatedURLs:n}})}activate(t){return $(t,async()=>{const t=await self.caches.open(this.strategy.cacheName),e=await t.keys(),s=new Set(this.F.values()),n=[];for(const r of e)s.has(r.url)||(await t.delete(r),n.push(r.url));return{deletedURLs:n}})}getURLsToCacheKeys(){return this.F}getCachedURLs(){return[...this.F.keys()]}getCacheKeyForURL(t){const e=new URL(t,location.href);return this.F.get(e.href)}getIntegrityForCacheKey(t){return this.$.get(t)}async matchPrecache(t){const e=t instanceof Request?t.url:t,s=this.getCacheKeyForURL(e);if(s){return(await self.caches.open(this.strategy.cacheName)).match(s)}}createHandlerBoundToURL(t){const e=this.getCacheKeyForURL(t);if(!e)throw new s("non-precached-url",{url:t});return s=>(s.request=new Request(t),s.params=Object.assign({cacheKey:e},s.params),this.strategy.handle(s))}}const tt=()=>(Q||(Q=new Z),Q);class et extends r{constructor(t,e){super(({request:s})=>{const n=t.getURLsToCacheKeys();for(const r of function*(t,{ignoreURLParametersMatching:e=[/^utm_/,/^fbclid$/],directoryIndex:s="index.html",cleanURLs:n=!0,urlManipulation:r}={}){const i=new URL(t,location.href);i.hash="",yield i.href;const a=function(t,e=[]){for(const s of[...t.searchParams.keys()])e.some(t=>t.test(s))&&t.searchParams.delete(s);return t}(i,e);if(yield a.href,s&&a.pathname.endsWith("/")){const t=new URL(a.href);t.pathname+=s,yield t.href}if(n){const t=new URL(a.href);t.pathname+=".html",yield t.href}if(r){const t=r({url:i});for(const e of t)yield e.href}}(s.url,e)){const e=n.get(r);if(e){return{cacheKey:e,integrity:t.getIntegrityForCacheKey(e)}}}},t.strategy)}}t.CacheFirst=class extends R{async U(t,e){let n,r=await e.cacheMatch(t);if(!r)try{r=await e.fetchAndCachePut(t)}catch(t){t instanceof Error&&(n=t)}if(!r)throw new s("no-response",{url:t.url,error:n});return r}},t.ExpirationPlugin=class{constructor(t={}){this.cachedResponseWillBeUsed=async({event:t,request:e,cacheName:s,cachedResponse:n})=>{if(!n)return null;const r=this.V(n),i=this.J(s);b(i.expireEntries());const a=i.updateTimestamp(e.url);if(t)try{t.waitUntil(a)}catch(t){}return r?n:null},this.cacheDidUpdate=async({cacheName:t,request:e})=>{const s=this.J(t);await s.updateTimestamp(e.url),await s.expireEntries()},this.X=t,this.B=t.maxAgeSeconds,this.Y=new Map,t.purgeOnQuotaError&&function(t){g.add(t)}(()=>this.deleteCacheAndMetadata())}J(t){if(t===d())throw new s("expire-custom-caches-only");let e=this.Y.get(t);return e||(e=new F(t,this.X),this.Y.set(t,e)),e}V(t){if(!this.B)return!0;const e=this.Z(t);if(null===e)return!0;return e>=Date.now()-1e3*this.B}Z(t){if(!t.headers.has("date"))return null;const e=t.headers.get("date"),s=new Date(e).getTime();return isNaN(s)?null:s}async deleteCacheAndMetadata(){for(const[t,e]of this.Y)await self.caches.delete(t),await e.delete();this.Y=new Map}},t.NetworkFirst=class extends R{constructor(t={}){super(t),this.plugins.some(t=>"cacheWillUpdate"in t)||this.plugins.unshift(u),this.tt=t.networkTimeoutSeconds||0}async U(t,e){const n=[],r=[];let i;if(this.tt){const{id:s,promise:a}=this.et({request:t,logs:n,handler:e});i=s,r.push(a)}const a=this.st({timeoutId:i,request:t,logs:n,handler:e});r.push(a);const o=await e.waitUntil((async()=>await e.waitUntil(Promise.race(r))||await a)());if(!o)throw new s("no-response",{url:t.url});return o}et({request:t,logs:e,handler:s}){let n;return{promise:new Promise(e=>{n=setTimeout(async()=>{e(await s.cacheMatch(t))},1e3*this.tt)}),id:n}}async st({timeoutId:t,request:e,logs:s,handler:n}){let r,i;try{i=await n.fetchAndCachePut(e)}catch(t){t instanceof Error&&(r=t)}return t&&clearTimeout(t),!r&&i||(i=await n.cacheMatch(e)),i}},t.RangeRequestsPlugin=class{constructor(){this.cachedResponseWillBeUsed=async({request:t,cachedResponse:e})=>e&&t.headers.has("range")?await H(t,e):e}},t.StaleWhileRevalidate=class extends R{constructor(t={}){super(t),this.plugins.some(t=>"cacheWillUpdate"in t)||this.plugins.unshift(u)}async U(t,e){const n=e.fetchAndCachePut(t).catch(()=>{});e.waitUntil(n);let r,i=await e.cacheMatch(t);if(i);else try{i=await n}catch(t){t instanceof Error&&(r=t)}if(!i)throw new s("no-response",{url:t.url,error:r});return i}},t.cleanupOutdatedCaches=function(){self.addEventListener("activate",t=>{const e=w();t.waitUntil((async(t,e="-precache-")=>{const s=(await self.caches.keys()).filter(s=>s.includes(e)&&s.includes(self.registration.scope)&&s!==t);return await Promise.all(s.map(t=>self.caches.delete(t))),s})(e).then(t=>{}))})},t.clientsClaim=function(){self.addEventListener("activate",()=>self.clients.claim())},t.precacheAndRoute=function(t,e){!function(t){tt().precache(t)}(t),function(t){const e=tt();h(new et(e,t))}(e)},t.registerRoute=h});