diff --git a/app/api/creator/analytics/route.ts b/app/api/creator/analytics/route.ts new file mode 100644 index 00000000..8ff3defe --- /dev/null +++ b/app/api/creator/analytics/route.ts @@ -0,0 +1,101 @@ +import { NextRequest, NextResponse } from 'next/server'; +import dbConnect from '@/lib/dbConnect'; +import Story from '../../../../models/Story'; +import { UserInteraction } from '../../../../models/UserInteraction'; +import { VALIDATION_PATTERNS } from '@/lib/constants'; + +export async function GET(request: NextRequest) { + try { + const { searchParams } = new URL(request.url); + const wallet = (searchParams.get('wallet') || '').trim().toLowerCase(); + + if (!wallet || !VALIDATION_PATTERNS.walletAddress.test(wallet)) { + return NextResponse.json({ error: 'Invalid wallet address' }, { status: 400 }); + } + + await dbConnect(); + + const stories = await Story.find({ authorWallet: wallet }) + .select({ _id: 1, title: 1, status: 1, updatedAt: 1, nftTokenId: 1, nftTxHash: 1 }) + .sort({ updatedAt: -1 }) + .lean(); + + if (stories.length === 0) { + return NextResponse.json({ + totals: { views: 0, likes: 0, bookmarks: 0, mints: 0 }, + stories: [], + }); + } + + const storyIds = stories.map((story: any) => story._id); + + const aggregation = await UserInteraction.aggregate([ + { + $match: { + storyId: { $in: storyIds }, + type: { $in: ['VIEW', 'LIKE', 'BOOKMARK'] }, + }, + }, + { + $group: { + _id: { storyId: '$storyId', type: '$type' }, + value: { $sum: '$value' }, + }, + }, + ]); + + const metricsByStoryId = new Map< + string, + { views: number; likes: number; bookmarks: number } + >(); + + for (const row of aggregation) { + const storyId = row?._id?.storyId?.toString?.() || ''; + const type = row?._id?.type as 'VIEW' | 'LIKE' | 'BOOKMARK'; + const value = Number(row?.value) || 0; + if (!storyId || !type) { + continue; + } + const existing = metricsByStoryId.get(storyId) || { views: 0, likes: 0, bookmarks: 0 }; + if (type === 'VIEW') existing.views += value; + if (type === 'LIKE') existing.likes += value; + if (type === 'BOOKMARK') existing.bookmarks += value; + metricsByStoryId.set(storyId, existing); + } + + const storyRows = stories.map((story: any) => { + const storyId = story._id.toString(); + const engagement = metricsByStoryId.get(storyId) || { views: 0, likes: 0, bookmarks: 0 }; + const minted = story.nftTokenId ? 1 : 0; + return { + storyId, + title: story.title || 'Untitled', + status: story.status || 'draft', + updatedAt: story.updatedAt ? new Date(story.updatedAt).getTime() : Date.now(), + views: engagement.views, + likes: engagement.likes, + bookmarks: engagement.bookmarks, + mints: minted, + }; + }); + + const totals = storyRows.reduce( + (acc, row) => { + acc.views += row.views; + acc.likes += row.likes; + acc.bookmarks += row.bookmarks; + acc.mints += row.mints; + return acc; + }, + { views: 0, likes: 0, bookmarks: 0, mints: 0 } + ); + + return NextResponse.json({ + totals, + stories: storyRows, + }); + } catch (error) { + return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }); + } +} + diff --git a/app/api/creator/drafts/[draftKey]/route.ts b/app/api/creator/drafts/[draftKey]/route.ts new file mode 100644 index 00000000..e86540be --- /dev/null +++ b/app/api/creator/drafts/[draftKey]/route.ts @@ -0,0 +1,73 @@ +import { NextRequest, NextResponse } from 'next/server'; +import dbConnect from '@/lib/dbConnect'; +import Draft from '../../../../../models/Draft'; +import { VALIDATION_PATTERNS } from '@/lib/constants'; + +export async function GET( + request: NextRequest, + { params }: { params: { draftKey: string } } +) { + try { + const { searchParams } = new URL(request.url); + const wallet = (searchParams.get('wallet') || '').trim().toLowerCase(); + const draftKey = (params.draftKey || '').trim(); + + if (!wallet || !VALIDATION_PATTERNS.walletAddress.test(wallet)) { + return NextResponse.json({ error: 'Invalid wallet address' }, { status: 400 }); + } + if (!draftKey) { + return NextResponse.json({ error: 'draftKey is required' }, { status: 400 }); + } + + await dbConnect(); + + const draft = await Draft.findOne({ draftKey, ownerWallet: wallet }).lean(); + if (!draft) { + return NextResponse.json({ error: 'Draft not found' }, { status: 404 }); + } + + const currentUpdatedAt = draft.current?.updatedAt + ? new Date(draft.current.updatedAt).getTime() + : Date.now(); + + return NextResponse.json({ + draftKey: draft.draftKey, + storyType: draft.storyType, + storyFormat: draft.storyFormat, + ownerWallet: draft.ownerWallet, + ownerRole: draft.ownerRole, + current: { + title: draft.current?.title || '', + description: draft.current?.description || '', + genre: draft.current?.genre || '', + content: draft.current?.content || '', + coverImageName: draft.current?.coverImageName || '', + updatedAt: currentUpdatedAt, + version: draft.current?.version || 1, + }, + versions: (draft.versions || []).map((version: any) => ({ + id: version._id?.toString?.() || '', + title: version.title || '', + description: version.description || '', + genre: version.genre || '', + content: version.content || '', + coverImageName: version.coverImageName || '', + updatedAt: version.updatedAt ? new Date(version.updatedAt).getTime() : Date.now(), + version: version.version || 1, + reason: version.reason || 'autosave', + })), + aiMetadata: { + pipelineState: draft.aiMetadata?.pipelineState || 'idle', + suggestedEdits: draft.aiMetadata?.suggestedEdits || [], + lastEditedByAIAt: draft.aiMetadata?.lastEditedByAIAt + ? new Date(draft.aiMetadata.lastEditedByAIAt).getTime() + : null, + }, + createdAt: draft.createdAt ? new Date(draft.createdAt).getTime() : null, + updatedAt: draft.updatedAt ? new Date(draft.updatedAt).getTime() : null, + }); + } catch (error) { + return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }); + } +} + diff --git a/app/api/creator/drafts/route.ts b/app/api/creator/drafts/route.ts new file mode 100644 index 00000000..6dc165c5 --- /dev/null +++ b/app/api/creator/drafts/route.ts @@ -0,0 +1,92 @@ +import { NextRequest, NextResponse } from 'next/server'; +import dbConnect from '@/lib/dbConnect'; +import Draft from '../../../../models/Draft'; +import { VALIDATION_PATTERNS } from '@/lib/constants'; + +export async function GET(request: NextRequest) { + try { + const { searchParams } = new URL(request.url); + const wallet = (searchParams.get('wallet') || '').trim().toLowerCase(); + const query = (searchParams.get('q') || '').trim(); + const pipelineState = (searchParams.get('pipeline') || '').trim(); + const page = Math.max(1, Number.parseInt(searchParams.get('page') || '1', 10) || 1); + const limit = Math.max( + 1, + Math.min(50, Number.parseInt(searchParams.get('limit') || '20', 10) || 20) + ); + + if (!wallet || !VALIDATION_PATTERNS.walletAddress.test(wallet)) { + return NextResponse.json({ error: 'Invalid wallet address' }, { status: 400 }); + } + + await dbConnect(); + + const filter: Record = { ownerWallet: wallet }; + + if (pipelineState && ['idle', 'ready', 'processing'].includes(pipelineState)) { + filter['aiMetadata.pipelineState'] = pipelineState; + } + + if (query) { + const escaped = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + filter.$or = [ + { draftKey: { $regex: escaped, $options: 'i' } }, + { 'current.title': { $regex: escaped, $options: 'i' } }, + { 'current.genre': { $regex: escaped, $options: 'i' } }, + { storyType: { $regex: escaped, $options: 'i' } }, + { storyFormat: { $regex: escaped, $options: 'i' } }, + ]; + } + + const total = await Draft.countDocuments(filter); + const items = await Draft.find(filter) + .sort({ updatedAt: -1 }) + .skip((page - 1) * limit) + .limit(limit) + .select({ + draftKey: 1, + storyType: 1, + storyFormat: 1, + ownerWallet: 1, + ownerRole: 1, + current: { title: 1, genre: 1, updatedAt: 1, version: 1 }, + aiMetadata: { pipelineState: 1, lastEditedByAIAt: 1 }, + createdAt: 1, + updatedAt: 1, + }) + .lean(); + + return NextResponse.json({ + items: items.map((item: any) => ({ + draftKey: item.draftKey, + storyType: item.storyType, + storyFormat: item.storyFormat, + ownerWallet: item.ownerWallet, + ownerRole: item.ownerRole, + current: { + title: item.current?.title || '', + genre: item.current?.genre || '', + updatedAt: item.current?.updatedAt ? new Date(item.current.updatedAt).getTime() : null, + version: item.current?.version || 1, + }, + aiMetadata: { + pipelineState: item.aiMetadata?.pipelineState || 'idle', + lastEditedByAIAt: item.aiMetadata?.lastEditedByAIAt + ? new Date(item.aiMetadata.lastEditedByAIAt).getTime() + : null, + }, + createdAt: item.createdAt ? new Date(item.createdAt).getTime() : null, + updatedAt: item.updatedAt ? new Date(item.updatedAt).getTime() : null, + })), + pagination: { + page, + limit, + total, + pages: Math.ceil(total / limit), + }, + }); + } catch (error) { + return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }); + } +} + diff --git a/app/api/creator/nfts/route.ts b/app/api/creator/nfts/route.ts new file mode 100644 index 00000000..0cf02167 --- /dev/null +++ b/app/api/creator/nfts/route.ts @@ -0,0 +1,86 @@ +import { NextRequest, NextResponse } from 'next/server'; +import dbConnect from '@/lib/dbConnect'; +import Story from '../../../../models/Story'; +import { BLOCKCHAIN_CONFIG, VALIDATION_PATTERNS } from '@/lib/constants'; +import { checkTxStatus } from '@/lib/blockchain'; + +export async function GET(request: NextRequest) { + try { + const { searchParams } = new URL(request.url); + const wallet = (searchParams.get('wallet') || '').trim().toLowerCase(); + + if (!wallet || !VALIDATION_PATTERNS.walletAddress.test(wallet)) { + return NextResponse.json({ error: 'Invalid wallet address' }, { status: 400 }); + } + + await dbConnect(); + + const stories = await Story.find({ authorWallet: wallet }) + .select({ + _id: 1, + title: 1, + status: 1, + ipfsHash: 1, + nftTxHash: 1, + nftTokenId: 1, + updatedAt: 1, + }) + .sort({ updatedAt: -1 }) + .lean(); + + const explorerBase = BLOCKCHAIN_CONFIG.networks.monad.explorerUrl; + const contract = BLOCKCHAIN_CONFIG.contracts.storyNFT || ''; + + const candidates = stories.filter((story: any) => story.status !== 'draft'); + + const payload = await Promise.all(candidates.map(async (story: any) => { + const txHash = story.nftTxHash || undefined; + let tokenId = story.nftTokenId || undefined; + const status = story.status || 'draft'; + let syncStatus: 'missing' | 'pending' | 'confirmed' | 'failed' = + status === 'failed' + ? 'failed' + : tokenId + ? 'confirmed' + : txHash || status === 'publishing' + ? 'pending' + : 'missing'; + + if (txHash && syncStatus === 'pending') { + try { + const chainStatus = await checkTxStatus(txHash); + if (chainStatus.status === 'confirmed') { + syncStatus = 'confirmed'; + tokenId = tokenId || (chainStatus as any).tokenId || undefined; + } else if (chainStatus.status === 'reverted') { + syncStatus = 'failed'; + } + } catch { + } + } + + const explorer = { + tx: txHash ? `${explorerBase}/tx/${txHash}` : undefined, + token: contract && tokenId ? `${explorerBase}/token/${contract}?a=${tokenId}` : undefined, + contract: contract ? `${explorerBase}/address/${contract}` : undefined, + }; + + return { + storyId: story._id.toString(), + title: story.title || 'Untitled', + status, + ipfsHash: story.ipfsHash || undefined, + nftTxHash: txHash, + nftTokenId: tokenId, + syncStatus, + explorer, + updatedAt: story.updatedAt ? new Date(story.updatedAt).getTime() : Date.now(), + }; + })); + + return NextResponse.json(payload); + } catch (error) { + return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }); + } +} + diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx index 0e22547e..23f5f0ad 100644 --- a/app/dashboard/page.tsx +++ b/app/dashboard/page.tsx @@ -1,219 +1,762 @@ "use client"; -import { useState, useEffect } from "react"; +import { useEffect, useMemo, useState } from "react"; import Link from "next/link"; -import { OnboardingChecklist } from "@/components/dashboard/OnboardingChecklist"; -import { DashboardTour } from "@/components/dashboard/DashboardTour"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { Button } from "@/components/ui/button"; +import { useRouter } from "next/navigation"; +import { formatDistanceToNowStrict } from "date-fns"; import { BarChart3, - Wallet, - BookOpen, + Bookmark, + ExternalLink, + FileText, + Heart, + Link2, + Loader2, + Search, + Sparkles, Trophy, + Wallet, + Eye, Plus, - MoreVertical, - ArrowUpRight, - DollarSign + Trash2, } from "lucide-react"; -import { useWallet } from "@/hooks/use-wallet"; -import { useCreatorEarnings } from "@/hooks/use-royalties"; -interface ChecklistStep { - id: string; - label: string; - isCompleted: boolean; - actionUrl?: string; -} +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Skeleton } from "@/components/ui/skeleton"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { useToast } from "@/components/ui/use-toast"; +import { useWallet } from "@/hooks/use-wallet"; +import { + clearDraftRecord, + listDraftRecords, + setActiveDraftKey, + StoryDraftRecord, + upsertDraftRecord, +} from "@/lib/story-draft-manager"; +import { BLOCKCHAIN_CONFIG } from "@/lib/constants"; export default function DashboardPage() { - const { address } = useWallet(); - const { earnings } = useCreatorEarnings(address || undefined); + const router = useRouter(); + const { toast } = useToast(); + const { address, connect } = useWallet(); + + const [tab, setTab] = useState<"drafts" | "nfts" | "analytics">("drafts"); + const [draftQuery, setDraftQuery] = useState(""); + const [draftPipelineFilter, setDraftPipelineFilter] = useState< + "all" | "idle" | "ready" | "processing" + >("all"); + const [cloudDraftsLoading, setCloudDraftsLoading] = useState(false); + const [cloudDraftsError, setCloudDraftsError] = useState(null); + const [cloudDrafts, setCloudDrafts] = useState< + Array<{ + draftKey: string; + storyType: string; + storyFormat: string; + current: { title: string; genre: string; updatedAt: number | null; version: number }; + aiMetadata: { pipelineState: "idle" | "ready" | "processing"; lastEditedByAIAt: number | null }; + updatedAt: number | null; + }> + >([]); + + const [analyticsLoading, setAnalyticsLoading] = useState(false); + const [analyticsError, setAnalyticsError] = useState(null); + const [analytics, setAnalytics] = useState<{ + totals: { views: number; likes: number; bookmarks: number; mints: number }; + stories: Array<{ + storyId: string; + title: string; + status: string; + updatedAt: number; + views: number; + likes: number; + bookmarks: number; + mints: number; + }>; + } | null>(null); - const [runTour, setRunTour] = useState(false); + const [nftsLoading, setNftsLoading] = useState(false); + const [nftsError, setNftsError] = useState(null); + const [nfts, setNfts] = useState< + Array<{ + storyId: string; + title: string; + status: string; + ipfsHash?: string; + nftTxHash?: string; + nftTokenId?: string; + syncStatus: "missing" | "pending" | "confirmed" | "failed"; + explorer: { tx?: string; token?: string; contract?: string }; + updatedAt: number; + }> + >([]); - const [isChecklistVisible, setIsChecklistVisible] = useState(false); + const localDrafts = useMemo(() => { + const normalizedQuery = draftQuery.trim().toLowerCase(); + return listDraftRecords((record) => { + if (draftPipelineFilter !== "all" && record.aiMetadata?.pipelineState !== draftPipelineFilter) { + return false; + } + if (!normalizedQuery) { + return true; + } + const haystack = [ + record.current?.title, + record.current?.genre, + record.storyType, + record.storyFormat, + record.draftKey, + ] + .filter(Boolean) + .join(" ") + .toLowerCase(); + return haystack.includes(normalizedQuery); + }); + }, [draftPipelineFilter, draftQuery]); - const [checklistSteps, setChecklistSteps] = useState([ - { id: "profile", label: "Complete your Creator Profile", isCompleted: true }, - { id: "wallet", label: "Connect Web3 Wallet", isCompleted: false }, - { id: "story", label: "Publish your first Story", isCompleted: false }, - { id: "mint", label: "Mint a Story NFT", isCompleted: false }, - { id: "social", label: "Share on Social Media", isCompleted: false }, - ]); + const draftCards = useMemo(() => { + const normalizedQuery = draftQuery.trim().toLowerCase(); + const matchesQuery = (fields: Array) => { + if (!normalizedQuery) { + return true; + } + return fields + .filter(Boolean) + .join(" ") + .toLowerCase() + .includes(normalizedQuery); + }; + + const byKey = new Map< + string, + { + draftKey: string; + storyType: string; + storyFormat: string; + title: string; + genre: string; + updatedAt: number; + pipelineState: "idle" | "ready" | "processing"; + source: "local" | "cloud" | "both"; + local?: StoryDraftRecord; + } + >(); + + for (const remote of cloudDrafts) { + if ( + draftPipelineFilter !== "all" && + remote.aiMetadata?.pipelineState !== draftPipelineFilter + ) { + continue; + } + if ( + !matchesQuery([ + remote.current?.title, + remote.current?.genre, + remote.storyType, + remote.storyFormat, + remote.draftKey, + ]) + ) { + continue; + } + const updatedAt = remote.updatedAt ?? remote.current?.updatedAt ?? Date.now(); + byKey.set(remote.draftKey, { + draftKey: remote.draftKey, + storyType: remote.storyType || "text", + storyFormat: remote.storyFormat || "free", + title: remote.current?.title?.trim() || "Untitled Draft", + genre: remote.current?.genre?.trim() || "Uncategorized", + updatedAt, + pipelineState: remote.aiMetadata?.pipelineState || "idle", + source: "cloud", + }); + } + + for (const local of localDrafts) { + if ( + !matchesQuery([ + local.current?.title, + local.current?.genre, + local.storyType, + local.storyFormat, + local.draftKey, + ]) + ) { + continue; + } + const updatedAt = local.updatedAt || local.current?.updatedAt || Date.now(); + const existing = byKey.get(local.draftKey); + byKey.set(local.draftKey, { + draftKey: local.draftKey, + storyType: local.storyType || "text", + storyFormat: local.storyFormat || "free", + title: local.current?.title?.trim() || "Untitled Draft", + genre: local.current?.genre?.trim() || "Uncategorized", + updatedAt: Math.max(updatedAt, existing?.updatedAt ?? 0), + pipelineState: local.aiMetadata?.pipelineState || existing?.pipelineState || "idle", + source: existing ? "both" : "local", + local, + }); + } + + return [...byKey.values()].sort((a, b) => b.updatedAt - a.updatedAt); + }, [cloudDrafts, draftPipelineFilter, draftQuery, localDrafts]); useEffect(() => { - const hasSeenTour = localStorage.getItem("has_seen_dashboard_tour"); - if (!hasSeenTour) { - setRunTour(true); + if (!address) { + setAnalytics(null); + setNfts([]); + setCloudDrafts([]); + return; } - const isDismissed = localStorage.getItem("onboarding_dismissed"); - if (!isDismissed) { - setIsChecklistVisible(true); + const controller = new AbortController(); + const fetchAnalytics = async () => { + setAnalyticsLoading(true); + setAnalyticsError(null); + try { + const response = await fetch( + `/api/creator/analytics?wallet=${encodeURIComponent(address)}`, + { signal: controller.signal } + ); + if (!response.ok) { + throw new Error("Failed to load analytics"); + } + const payload = (await response.json()) as typeof analytics; + setAnalytics(payload); + } catch (error) { + if ((error as any)?.name !== "AbortError") { + setAnalyticsError("Analytics is currently unavailable."); + setAnalytics(null); + } + } finally { + setAnalyticsLoading(false); + } + }; + + const fetchNfts = async () => { + setNftsLoading(true); + setNftsError(null); + try { + const response = await fetch( + `/api/creator/nfts?wallet=${encodeURIComponent(address)}`, + { signal: controller.signal } + ); + if (!response.ok) { + throw new Error("Failed to load NFTs"); + } + const payload = (await response.json()) as typeof nfts; + setNfts(payload); + } catch (error) { + if ((error as any)?.name !== "AbortError") { + setNftsError("NFT data is currently unavailable."); + setNfts([]); + } + } finally { + setNftsLoading(false); + } + }; + + const fetchCloudDrafts = async () => { + setCloudDraftsLoading(true); + setCloudDraftsError(null); + try { + const response = await fetch( + `/api/creator/drafts?wallet=${encodeURIComponent(address)}&limit=50`, + { signal: controller.signal } + ); + if (!response.ok) { + throw new Error("Failed to load drafts"); + } + const payload = (await response.json()) as { items: typeof cloudDrafts }; + setCloudDrafts(payload.items || []); + } catch (error) { + if ((error as any)?.name !== "AbortError") { + setCloudDraftsError("Cloud drafts are currently unavailable."); + setCloudDrafts([]); + } + } finally { + setCloudDraftsLoading(false); + } + }; + + fetchAnalytics(); + fetchNfts(); + fetchCloudDrafts(); + + return () => controller.abort(); + }, [address]); + + const openDraft = (draft: StoryDraftRecord) => { + try { + setActiveDraftKey(draft.draftKey); + localStorage.setItem( + "storyCreationData", + JSON.stringify({ + type: draft.storyType || "text", + format: draft.storyFormat || "free", + draftKey: draft.draftKey, + timestamp: Date.now(), + }) + ); + router.push("/create"); + } catch { + toast({ + title: "Unable to open draft", + description: "Please try again.", + variant: "destructive", + }); } - }, []); + }; - const handleTourComplete = () => { - setRunTour(false); - localStorage.setItem("has_seen_dashboard_tour", "true"); + const deleteDraft = (draft: StoryDraftRecord) => { + clearDraftRecord(draft.draftKey); + toast({ + title: "Draft deleted", + description: draft.current?.title?.trim() ? draft.current.title : "Untitled draft", + }); }; - const handleChecklistDismiss = () => { - setIsChecklistVisible(false); - localStorage.setItem("onboarding_dismissed", "true"); + const importCloudDraft = async (draftKey: string) => { + if (!address) { + return; + } + try { + const response = await fetch( + `/api/creator/drafts/${encodeURIComponent(draftKey)}?wallet=${encodeURIComponent( + address + )}` + ); + if (!response.ok) { + throw new Error("Failed to import"); + } + const payload = (await response.json()) as StoryDraftRecord; + const imported = upsertDraftRecord(payload); + openDraft(imported); + } catch { + toast({ + title: "Import failed", + description: "Unable to import this draft to your device.", + variant: "destructive", + }); + } }; return (
- - -
-
-

Creator Dashboard

-

Manage your stories, NFTs, and earnings.

+
+
+
+

Creator Dashboard

+

+ Drafts, NFTs, and engagement analytics in one place. +

+
+ +
+ + + + + +
- -
+
+ + + Views + + + + {analyticsLoading ? ( + + ) : ( +
{analytics?.totals.views ?? 0}
+ )} +
+
- - -
- - - Total Earnings - - - - - -
- {earnings ? `${(earnings.totalEarned ?? 0).toFixed(4)} ETH` : '0.0000 ETH'} -
-

- {address ? ( - - View details - + + + Likes + + + + {analyticsLoading ? ( + ) : ( - 'Connect wallet to track' +

{analytics?.totals.likes ?? 0}
)} -

-
-
- - - - Stories Published - - - -
12
-

- +2 new this week -

-
-
- - - - NFTs Minted - - - -
8
-

- 6 sold on secondary market -

-
-
- - - - Active Readers - - - - - - - -
+573
-

- +201 since last hour -

-
-
-
+ + -
- - - Recent Stories - - - - - -
- {[1, 2, 3].map((i) => ( -
-
-
- πŸ“š -
-
-

The Lost Algorithm of Atlantis

-

Updated 2 hours ago

-
-
- -
- ))} -
-
-
- - - - Quick Tips - - -
-
- πŸ’‘ Pro Tip: Minting your story as an NFT allows you to earn royalties every time it is resold. + + + Bookmarks + + + + {analyticsLoading ? ( + + ) : ( +
{analytics?.totals.bookmarks ?? 0}
+ )} +
+
+ + + + Mints + + + + {analyticsLoading ? ( + + ) : ( +
{analytics?.totals.mints ?? 0}
+ )} +
+
+
+ + {(analyticsError || nftsError || cloudDraftsError) && ( +
+ {analyticsError && ( +
{analyticsError}
+ )} + {nftsError &&
{nftsError}
} + {cloudDraftsError &&
{cloudDraftsError}
} +
+ )} + + setTab(value as any)}> + + + + My Drafts + + + + My NFTs + + + + Analytics + + + + + + +
+ Draft Library +

+ Search, filter, and jump back into your work. +

-
- Writing with AI: Try using the "Fantasy" prompt preset to generate immersive worlds quickly. + +
+
+ + setDraftQuery(e.target.value)} + placeholder="Search drafts..." + className="pl-9 md:w-[260px]" + /> +
+
+ + + {cloudDraftsLoading ? ( +
+ + Loading cloud drafts… +
+ ) : draftCards.length === 0 ? ( +
+ No drafts found. Start a new story to create your first draft. +
+ ) : ( +
+ {draftCards.map((draft) => { + return ( + + +
+ {draft.title} +
+ {draft.pipelineState} + {draft.source} +
+
+
+ {draft.genre} + β€’ + + Edited{" "} + {formatDistanceToNowStrict(new Date(draft.updatedAt), { + addSuffix: true, + })} + +
+
+ +
+ {draft.storyType}/{draft.storyFormat} +
+
+ {draft.local ? ( + <> + + + + ) : ( + + )} +
+
+
+ ); + })} +
+ )} +
+ + + + + + + My NFTs +

+ Off-chain metadata from Mongo + on-chain sync status and explorer links. +

+
+ + {!address ? ( +
+ Connect your wallet to view NFTs tied to your author address. +
+ ) : nftsLoading ? ( +
+ + Loading NFTs… +
+ ) : nfts.length === 0 ? ( +
+ No NFTs found for this wallet yet. +
+ ) : ( +
+ {nfts.map((item) => ( + + +
+ {item.title} + {item.syncStatus} +
+
+ {item.status} β€’ Updated{" "} + {formatDistanceToNowStrict(new Date(item.updatedAt), { + addSuffix: true, + })} +
+
+ +
+
+ Token + {item.nftTokenId || "β€”"} +
+
+ Tx + + {item.nftTxHash ? `${item.nftTxHash.slice(0, 10)}…` : "β€”"} + +
+
+ +
+ {item.explorer.tx && ( + + )} + {item.explorer.token && ( + + )} + {item.explorer.contract && ( + + )} +
+ + {item.ipfsHash && ( +
+ Metadata: {item.ipfsHash} +
+ )} +
+
+ ))} +
+ )} +
+
+
+ + + + + Engagement Analytics +

+ Views, likes, bookmarks, and mints across your stories. +

+
+ + {!address ? ( +
+ Connect your wallet to view creator analytics. +
+ ) : analyticsLoading ? ( +
+ + Loading analytics… +
+ ) : !analytics ? ( +
+ No analytics available yet. +
+ ) : ( + + + + Story + Status + Views + Likes + Bookmarks + Mints + + + + {analytics.stories.map((row) => ( + + {row.title} + + {row.status} + + {row.views} + {row.likes} + {row.bookmarks} + {row.mints} + + ))} + +
+ )} +
+
+ +
+ Explorer: {BLOCKCHAIN_CONFIG.networks.monad.explorerUrl}
- - +
+
); -} \ No newline at end of file +} diff --git a/components/dashboard/analytics/page.tsx b/components/dashboard/analytics/page.tsx index 9526b7d1..3593c142 100644 --- a/components/dashboard/analytics/page.tsx +++ b/components/dashboard/analytics/page.tsx @@ -21,7 +21,7 @@ export default function AnalyticsPage() {
} /> - } /> + } /> } /> } />
@@ -31,4 +31,4 @@ export default function AnalyticsPage() {
); -} \ No newline at end of file +} diff --git a/components/royalty/revenue-chart.tsx b/components/royalty/revenue-chart.tsx index a8efd4c9..872388b0 100644 --- a/components/royalty/revenue-chart.tsx +++ b/components/royalty/revenue-chart.tsx @@ -83,7 +83,9 @@ export function RevenueChart({ transactions }: RevenueChartProps) { { if (!active || !payload?.length) return null; - const data = payload[0].payload; + const first = payload?.[0]; + if (!first) return null; + const data = first.payload as any; return (

diff --git a/components/royalty/royalty-config-form.tsx b/components/royalty/royalty-config-form.tsx index f25e6557..4fcb0da3 100644 --- a/components/royalty/royalty-config-form.tsx +++ b/components/royalty/royalty-config-form.tsx @@ -110,7 +110,7 @@ export function RoyaltyConfigForm({ max={50} step={1} value={[percentage]} - onValueChange={([val]) => setPercentage(val)} + onValueChange={(values) => setPercentage(values[0] ?? 0)} className="flex-1" />

diff --git a/lib/blockchain.ts b/lib/blockchain.ts index d0d2ad76..6ec474dd 100644 --- a/lib/blockchain.ts +++ b/lib/blockchain.ts @@ -14,12 +14,16 @@ const CONTRACT_ABI = [ "event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)" ]; +function getProvider() { + if (!RPC_URL) throw new Error("Blockchain Config Error: Missing 'MONAD_RPC_URL'"); + return new ethers.JsonRpcProvider(RPC_URL); +} + /** * Initializes the wallet and contract instance with strict validation. * Fails fast if configuration is invalid. */ function getContract() { - if (!RPC_URL) throw new Error("Blockchain Config Error: Missing 'MONAD_RPC_URL'"); if (!PRIVATE_KEY) throw new Error("Blockchain Config Error: Missing 'MINT_AUTHORITY_PRIVATE_KEY'"); if (!CONTRACT_ADDRESS) throw new Error("Blockchain Config Error: Missing 'NEXT_PUBLIC_CONTRACT_ADDR'"); @@ -28,7 +32,7 @@ function getContract() { } try { - const provider = new ethers.JsonRpcProvider(RPC_URL); + const provider = getProvider(); const wallet = new ethers.Wallet(PRIVATE_KEY, provider); @@ -70,7 +74,7 @@ export async function mintNFT(toAddress: string, tokenURI: string): Promise boolean +): StoryDraftRecord[] { + const store = readStore(); + return Object.values(store.drafts) + .filter((record) => (filter ? filter(record) : true)) + .sort((a, b) => b.updatedAt - a.updatedAt) + .map((record) => cloneRecord(record)); +} + export function upsertDraftRecord(record: StoryDraftRecord): StoryDraftRecord { const store = readStore(); const cloned = cloneRecord(record); diff --git a/models/Draft.ts b/models/Draft.ts new file mode 100644 index 00000000..0f727a4a --- /dev/null +++ b/models/Draft.ts @@ -0,0 +1,89 @@ +import mongoose, { Schema, Document, Model } from 'mongoose'; + +export interface IDraftSnapshot { + title: string; + description: string; + genre: string; + content: string; + coverImageName?: string; + updatedAt: Date; + version: number; +} + +export interface IDraftVersion extends IDraftSnapshot { + reason: 'autosave' | 'blur' | 'manual' | 'restore'; +} + +export interface IDraftAiMetadata { + pipelineState: 'idle' | 'ready' | 'processing'; + suggestedEdits: string[]; + lastEditedByAIAt?: Date | null; +} + +export interface IDraft extends Document { + draftKey: string; + storyType: string; + storyFormat: string; + ownerWallet?: string | null; + ownerRole: 'wallet' | 'admin' | 'guest'; + current: IDraftSnapshot; + versions: IDraftVersion[]; + aiMetadata: IDraftAiMetadata; + createdAt: Date; + updatedAt: Date; +} + +const DraftSnapshotSchema = new Schema( + { + title: { type: String, default: '' }, + description: { type: String, default: '' }, + genre: { type: String, default: '' }, + content: { type: String, default: '' }, + coverImageName: { type: String, default: '' }, + updatedAt: { type: Date, default: Date.now }, + version: { type: Number, default: 1 }, + }, + { _id: false } +); + +const DraftVersionSchema = new Schema( + { + title: { type: String, default: '' }, + description: { type: String, default: '' }, + genre: { type: String, default: '' }, + content: { type: String, default: '' }, + coverImageName: { type: String, default: '' }, + updatedAt: { type: Date, default: Date.now }, + version: { type: Number, default: 1 }, + reason: { + type: String, + enum: ['autosave', 'blur', 'manual', 'restore'], + default: 'autosave', + }, + }, + { _id: true } +); + +const DraftSchema = new Schema( + { + draftKey: { type: String, required: true, unique: true, trim: true, index: true }, + storyType: { type: String, default: 'text' }, + storyFormat: { type: String, default: 'free' }, + ownerWallet: { type: String, default: null, lowercase: true, trim: true, index: true }, + ownerRole: { type: String, enum: ['wallet', 'admin', 'guest'], default: 'wallet' }, + current: { type: DraftSnapshotSchema, required: true, default: () => ({}) }, + versions: { type: [DraftVersionSchema], default: [] }, + aiMetadata: { + pipelineState: { type: String, enum: ['idle', 'ready', 'processing'], default: 'idle' }, + suggestedEdits: { type: [String], default: [] }, + lastEditedByAIAt: { type: Date, default: null }, + }, + }, + { timestamps: true, strict: false } +); + +DraftSchema.index({ ownerWallet: 1, updatedAt: -1 }); + +export const Draft: Model = mongoose.models.Draft || mongoose.model('Draft', DraftSchema); +export default Draft; + diff --git a/package-lock.json b/package-lock.json index d82960df..f73bd9de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -299,7 +299,6 @@ "resolved": "https://registry.npmjs.org/preact/-/preact-10.24.3.tgz", "integrity": "sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==", "license": "MIT", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/preact" @@ -345,7 +344,6 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -838,7 +836,6 @@ "resolved": "https://registry.npmjs.org/@base-org/account/-/account-2.4.0.tgz", "integrity": "sha512-A4Umpi8B9/pqR78D1Yoze4xHyQaujioVRqqO3d6xuDFw9VRtjg6tK3bPlwE0aW+nVH/ntllCpPa2PbI8Rnjcug==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@coinbase/cdp-sdk": "^1.0.0", "@noble/hashes": "1.4.0", @@ -1131,7 +1128,6 @@ "resolved": "https://registry.npmjs.org/@wagmi/core/-/core-2.22.1.tgz", "integrity": "sha512-cG/xwQWsBEcKgRTkQVhH29cbpbs/TdcUJVFXCyri3ZknxhMyGv0YEjTcrNpRgt2SaswL1KrvslSNYKKo+5YEAg==", "license": "MIT", - "peer": true, "dependencies": { "eventemitter3": "5.0.1", "mipd": "0.0.7", @@ -2188,7 +2184,6 @@ "resolved": "https://registry.npmjs.org/@farcaster/miniapp-sdk/-/miniapp-sdk-0.2.1.tgz", "integrity": "sha512-2SnDeOtDdlN1lGQt7UyH2jkrZRQDOkmhcrlzNWazYChyPh9XfV8+9fMS+Lr/E2pEws9Q40Xl9POrCzdpUC19lg==", "license": "MIT", - "peer": true, "dependencies": { "@farcaster/miniapp-core": "0.4.1", "@farcaster/quick-auth": "^0.0.6", @@ -2293,7 +2288,6 @@ "resolved": "https://registry.npmjs.org/@gemini-wallet/core/-/core-0.3.2.tgz", "integrity": "sha512-Z4aHi3ECFf5oWYWM3F1rW83GJfB9OvhBYPTmb5q+VyK3uvzvS48lwo+jwh2eOoCRWEuT/crpb9Vwp2QaS5JqgQ==", "license": "MIT", - "peer": true, "dependencies": { "@metamask/rpc-errors": "7.0.2", "eventemitter3": "5.0.1" @@ -3942,6 +3936,7 @@ "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" @@ -4612,7 +4607,6 @@ "version": "0.33.1", "resolved": "https://registry.npmjs.org/@metamask/sdk/-/sdk-0.33.1.tgz", "integrity": "sha512-1mcOQVGr9rSrVcbKPNVzbZ8eCl1K0FATsYH3WJ/MH4WcZDWGECWrXJPNMZoEAkLxWiMe8jOQBumg2pmcDa9zpQ==", - "peer": true, "dependencies": { "@babel/runtime": "^7.26.0", "@metamask/onboarding": "^1.0.1", @@ -4678,7 +4672,6 @@ "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz", "integrity": "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==", "license": "MIT", - "peer": true, "dependencies": { "node-fetch": "^2.7.0" } @@ -5269,7 +5262,6 @@ "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", "license": "MIT", - "peer": true, "engines": { "node": "^14.21.3 || >=16" }, @@ -8721,7 +8713,6 @@ "resolved": "https://registry.npmjs.org/@safe-global/safe-apps-provider/-/safe-apps-provider-0.18.6.tgz", "integrity": "sha512-4LhMmjPWlIO8TTDC2AwLk44XKXaK6hfBTWyljDm0HQ6TWlOEijVWNrt2s3OCVMSxlXAcEzYfqyu1daHZooTC2Q==", "license": "MIT", - "peer": true, "dependencies": { "@safe-global/safe-apps-sdk": "^9.1.0", "events": "^3.3.0" @@ -8732,7 +8723,6 @@ "resolved": "https://registry.npmjs.org/@safe-global/safe-apps-sdk/-/safe-apps-sdk-9.1.0.tgz", "integrity": "sha512-N5p/ulfnnA2Pi2M3YeWjULeWbjo7ei22JwU/IXnhoHzKq3pYCN6ynL9mJBOlvDVv892EgLPCWCOwQk/uBT2v0Q==", "license": "MIT", - "peer": true, "dependencies": { "@safe-global/safe-gateway-typescript-sdk": "^3.5.3", "viem": "^2.1.1" @@ -8788,7 +8778,6 @@ "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-9.0.1.tgz", "integrity": "sha512-wD2WpbkaEP4170s13/HUxPcAV5y4ZXaKo1TfNklS5zDefPinIgXOpgz1kpEvobAsaLPa2KeH7AKKX/od1mrBJw==", "license": "MIT", - "peer": true, "dependencies": { "@simplewebauthn/types": "^9.0.1" } @@ -9143,7 +9132,6 @@ "resolved": "https://registry.npmjs.org/@solana/kit/-/kit-3.0.3.tgz", "integrity": "sha512-CEEhCDmkvztd1zbgADsEQhmj9GyWOOGeW1hZD+gtwbBSF5YN1uofS/pex5MIh/VIqKRj+A2UnYWI1V+9+q/lyQ==", "license": "MIT", - "peer": true, "dependencies": { "@solana/accounts": "3.0.3", "@solana/addresses": "3.0.3", @@ -9863,7 +9851,6 @@ "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.98.4.tgz", "integrity": "sha512-vv9lfnvjUsRiq//+j5pBdXig0IQdtzA0BRZ3bXEP4KaIyF1CcaydWqgyzQgfZMNIsWNWmG+AUHwPy4AHOD6gpw==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", @@ -10140,7 +10127,6 @@ "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.10.tgz", "integrity": "sha512-EhZVFu9rl7GfRNuJLJ3Y7wtbTnENsvzp+YpcAV7kCYiXni1v8qZh++lpw4ch4rrwC0u/EZRnBHIehzCGzwXDSQ==", "license": "MIT", - "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" @@ -10151,7 +10137,6 @@ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.10.tgz", "integrity": "sha512-BKLss9Y8PQ9IUjPYQiv3/Zmlx92uxffUOX8ZZNoQlCIZBJPT5M+GOMQj7xislvVQ6l1BstBjcX0XB/aHfFYVNw==", "license": "MIT", - "peer": true, "dependencies": { "@tanstack/query-core": "5.90.10" }, @@ -10371,6 +10356,7 @@ "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/eslint": "*", "@types/estree": "*" @@ -10381,7 +10367,8 @@ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/graceful-fs": { "version": "4.1.9", @@ -10479,7 +10466,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.25.tgz", "integrity": "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ==", "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -10501,7 +10487,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz", "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -10513,7 +10498,6 @@ "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "devOptional": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^18.0.0" } @@ -10641,7 +10625,6 @@ "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -11116,7 +11099,6 @@ "resolved": "https://registry.npmjs.org/@upstash/redis/-/redis-1.36.2.tgz", "integrity": "sha512-C0Yt8hc12vLaQYRG1fMci8iPrLtnTdbJG0HR5T8vKnvEP/1RdMMblsOJs5/jp0JXZJ1oSzMnQz4J9EVezNpI6A==", "license": "MIT", - "peer": true, "dependencies": { "uncrypto": "^0.1.3" } @@ -12290,6 +12272,7 @@ "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/helper-numbers": "1.13.2", "@webassemblyjs/helper-wasm-bytecode": "1.13.2" @@ -12300,21 +12283,24 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@webassemblyjs/helper-buffer": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.13.2", @@ -12322,6 +12308,7 @@ "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.13.2", "@webassemblyjs/helper-api-error": "1.13.2", @@ -12333,7 +12320,8 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.14.1", @@ -12341,6 +12329,7 @@ "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -12354,6 +12343,7 @@ "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } @@ -12364,6 +12354,7 @@ "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@xtuc/long": "4.2.2" } @@ -12373,7 +12364,8 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@webassemblyjs/wasm-edit": { "version": "1.14.1", @@ -12381,6 +12373,7 @@ "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -12398,6 +12391,7 @@ "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-wasm-bytecode": "1.13.2", @@ -12412,6 +12406,7 @@ "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -12425,6 +12420,7 @@ "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-api-error": "1.13.2", @@ -12440,6 +12436,7 @@ "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" @@ -12450,14 +12447,16 @@ "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", "dev": true, - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true, - "license": "Apache-2.0" + "license": "Apache-2.0", + "peer": true }, "node_modules/@zerodev/ecdsa-validator": { "version": "5.4.9", @@ -12506,7 +12505,6 @@ "resolved": "https://registry.npmjs.org/@zerodev/sdk/-/sdk-5.5.6.tgz", "integrity": "sha512-4syvV0qXQORBO0l0RRUwXDvBK8kzXdvHJpH1L6UGagXqPajyMM4D6r/ybZjZMI6hiurEkgblz0S6IfGznBTiWw==", "license": "MIT", - "peer": true, "dependencies": { "semver": "^7.6.0" }, @@ -12519,6 +12517,7 @@ "resolved": "https://registry.npmjs.org/@zerodev/webauthn-key/-/webauthn-key-5.4.4.tgz", "integrity": "sha512-LgBwViiGdOD5K6mP+ko/mPD82Be3yQBb+fSQwsz6YB7cwbcRXmYPWgL2ozvhHmEuR+fkpHscazPDGoCm1pii4Q==", "license": "MIT", + "peer": true, "dependencies": { "@noble/curves": "^1.3.0", "@simplewebauthn/browser": "^8.3.4", @@ -12533,6 +12532,7 @@ "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-8.3.7.tgz", "integrity": "sha512-ZtRf+pUEgOCvjrYsbMsJfiHOdKcrSZt2zrAnIIpfmA06r0FxBovFYq0rJ171soZbe13KmWzAoLKjSxVW7KxCdQ==", "license": "MIT", + "peer": true, "dependencies": { "@simplewebauthn/typescript-types": "^8.3.4" } @@ -12600,7 +12600,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -12625,6 +12624,7 @@ "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10.13.0" }, @@ -12709,6 +12709,7 @@ "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ajv": "^8.0.0" }, @@ -12727,6 +12728,7 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -12743,7 +12745,8 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/alchemy-sdk": { "version": "3.6.5", @@ -13194,7 +13197,6 @@ "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", "license": "MIT", - "peer": true, "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", @@ -13673,7 +13675,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -13788,7 +13789,6 @@ "integrity": "sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==", "hasInstallScript": true, "license": "MIT", - "peer": true, "dependencies": { "node-gyp-build": "^4.3.0" }, @@ -14035,6 +14035,7 @@ "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6.0" } @@ -15538,7 +15539,6 @@ "resolved": "https://registry.npmjs.org/eciesjs/-/eciesjs-0.4.16.tgz", "integrity": "sha512-dS5cbA9rA2VR4Ybuvhg6jvdmp46ubLn3E+px8cG/35aEDNclrqoCjg6mt0HYZ/M+OoESS3jSkCrqk1kWAEhWAw==", "license": "MIT", - "peer": true, "dependencies": { "@ecies/ciphers": "^0.2.4", "@noble/ciphers": "^1.3.0", @@ -15571,7 +15571,6 @@ "resolved": "https://registry.npmjs.org/effect/-/effect-3.17.7.tgz", "integrity": "sha512-dpt0ONUn3zzAuul6k4nC/coTTw27AL5nhkORXgTi6NfMPzqWYa1M05oKmOMTxpVSTKepqXVcW9vIwkuaaqx9zA==", "license": "MIT", - "peer": true, "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" @@ -15621,8 +15620,7 @@ "version": "8.6.0", "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/embla-carousel-react": { "version": "8.6.0", @@ -15793,6 +15791,7 @@ "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" @@ -15963,7 +15962,8 @@ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/es-object-atoms": { "version": "1.1.1", @@ -16146,7 +16146,6 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -16265,7 +16264,6 @@ "integrity": "sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -16389,7 +16387,6 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -17053,7 +17050,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "@adraffy/ens-normalize": "1.10.1", "@noble/curves": "1.2.0", @@ -17161,8 +17157,7 @@ "version": "6.4.9", "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/eventemitter3": { "version": "5.0.1", @@ -17242,7 +17237,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", @@ -17475,7 +17469,8 @@ "url": "https://opencollective.com/fastify" } ], - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/fastestsmallesttextencoderdecoder": { "version": "1.0.22", @@ -18061,7 +18056,8 @@ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true, - "license": "BSD-2-Clause" + "license": "BSD-2-Clause", + "peer": true }, "node_modules/globals": { "version": "13.24.0", @@ -18160,7 +18156,6 @@ "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.12.0.tgz", "integrity": "sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==", "license": "MIT", - "peer": true, "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } @@ -18183,7 +18178,6 @@ "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.5.tgz", "integrity": "sha512-xEyq3rSl+dhGX2Lm0+eFQIAzlDN6Fs0EcC4f7BNUmzaRX/PTzeuM+Tr2lHB8FoXggsQIeXLj8EDVgs5ywxyxmg==", "license": "MIT", - "peer": true, "dependencies": { "cookie-es": "^1.2.2", "crossws": "^0.3.5", @@ -18559,8 +18553,7 @@ "version": "6.2.2", "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz", "integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==", - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/ieee754": { "version": "1.2.1", @@ -20014,7 +20007,6 @@ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -22388,7 +22380,6 @@ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "license": "MIT", - "peer": true, "bin": { "jiti": "bin/jiti.js" } @@ -23055,6 +23046,7 @@ "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6.11.5" }, @@ -23693,7 +23685,6 @@ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.21.0.tgz", "integrity": "sha512-URyb/VXMjJ4da46OeSXg+puO39XH9DeQpWCslifrRn9JWugy0D+DvvBvkm2WxmHe61O/H19JM66p1z7RHVkZ6A==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@mongodb-js/saslprep": "^1.3.0", "bson": "^6.10.4", @@ -24179,7 +24170,6 @@ "resolved": "https://registry.npmjs.org/next/-/next-14.1.0.tgz", "integrity": "sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==", "license": "MIT", - "peer": true, "dependencies": { "@next/env": "14.1.0", "@swc/helpers": "0.5.2", @@ -24376,7 +24366,6 @@ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "license": "MIT", - "peer": true, "dependencies": { "whatwg-url": "^5.0.0" }, @@ -25492,7 +25481,6 @@ "resolved": "https://registry.npmjs.org/porto/-/porto-0.2.35.tgz", "integrity": "sha512-gu9FfjjvvYBgQXUHWTp6n3wkTxVtEcqFotM7i3GEZeoQbvLGbssAicCz6hFZ8+xggrJWwi/RLmbwNra50SMmUQ==", "license": "MIT", - "peer": true, "dependencies": { "hono": "^4.10.3", "idb-keyval": "^6.2.1", @@ -25605,7 +25593,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz", "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -25667,7 +25654,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -25905,7 +25891,6 @@ "resolved": "https://registry.npmjs.org/preact/-/preact-10.28.2.tgz", "integrity": "sha512-lbteaWGzGHdlIuiJ0l2Jq454m6kcpI1zNje6d8MlGAFlYvP2GO4ibnat7P74Esfz4sPTdM6UxtTwh/d3pwM9JA==", "license": "MIT", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/preact" @@ -25945,7 +25930,6 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -26456,7 +26440,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -26500,7 +26483,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -26553,7 +26535,6 @@ "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.66.1.tgz", "integrity": "sha512-2KnjpgG2Rhbi+CIiIBQQ9Df6sMGH5ExNyFl4Hw9qO7pIqMBR8Bvu9RQyjl3JM4vehzCh9soiNUM/xYMswb2EiA==", "license": "MIT", - "peer": true, "engines": { "node": ">=18.0.0" }, @@ -26782,7 +26763,6 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", - "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -26931,6 +26911,7 @@ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -27379,6 +27360,7 @@ "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -27417,6 +27399,7 @@ "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -27429,7 +27412,8 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/scroll": { "version": "3.0.1", @@ -27504,6 +27488,7 @@ "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "dependencies": { "randombytes": "^2.1.0" } @@ -27817,7 +27802,6 @@ "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", "license": "MIT", - "peer": true, "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.2", @@ -28650,7 +28634,6 @@ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.18.tgz", "integrity": "sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==", "license": "MIT", - "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -28711,6 +28694,7 @@ "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6" }, @@ -28725,6 +28709,7 @@ "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", @@ -28744,6 +28729,7 @@ "integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", @@ -28779,6 +28765,7 @@ "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -28793,7 +28780,8 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/terser/node_modules/source-map-support": { "version": "0.5.21", @@ -28801,6 +28789,7 @@ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -28968,7 +28957,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -29192,7 +29180,6 @@ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -29450,7 +29437,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -29607,7 +29593,6 @@ "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", "license": "MIT", - "peer": true, "dependencies": { "@fastify/busboy": "^2.0.0" }, @@ -29847,7 +29832,6 @@ "resolved": "https://registry.npmjs.org/uploadthing/-/uploadthing-7.7.4.tgz", "integrity": "sha512-rlK/4JWHW5jP30syzWGBFDDXv3WJDdT8gn9OoxRJmXLoXi94hBmyyjxihGlNrKhBc81czyv8TkzMioe/OuKGfA==", "license": "MIT", - "peer": true, "dependencies": { "@effect/platform": "0.90.3", "@standard-schema/spec": "1.0.0-beta.4", @@ -29977,7 +29961,6 @@ "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", "hasInstallScript": true, "license": "MIT", - "peer": true, "dependencies": { "node-gyp-build": "^4.3.0" }, @@ -30150,7 +30133,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "@noble/curves": "1.9.1", "@noble/hashes": "1.8.0", @@ -30420,7 +30402,6 @@ "resolved": "https://registry.npmjs.org/@wagmi/core/-/core-2.22.1.tgz", "integrity": "sha512-cG/xwQWsBEcKgRTkQVhH29cbpbs/TdcUJVFXCyri3ZknxhMyGv0YEjTcrNpRgt2SaswL1KrvslSNYKKo+5YEAg==", "license": "MIT", - "peer": true, "dependencies": { "eventemitter3": "5.0.1", "mipd": "0.0.7", @@ -30783,7 +30764,6 @@ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", "license": "MIT", - "peer": true, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } @@ -30804,6 +30784,7 @@ "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -30902,6 +30883,7 @@ "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10.13.0" } @@ -30912,6 +30894,7 @@ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -30926,6 +30909,7 @@ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "engines": { "node": ">=4.0" } @@ -30936,6 +30920,7 @@ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -30946,6 +30931,7 @@ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "mime-db": "1.52.0" }, @@ -31166,7 +31152,6 @@ "resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz", "integrity": "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==", "license": "MIT", - "peer": true, "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.8", @@ -31392,7 +31377,6 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "license": "MIT", - "peer": true, "engines": { "node": ">=10.0.0" }, diff --git a/tests/ai-security.test.ts b/tests/ai-security.test.ts index d4636916..6d2d7b93 100644 --- a/tests/ai-security.test.ts +++ b/tests/ai-security.test.ts @@ -282,8 +282,11 @@ describe('Security logging', () => { }); const log = getSecurityLog(); expect(log).toHaveLength(1); - expect(log[0].type).toBe('injection_attempt'); - expect(log[0].timestamp).toBeDefined(); + const [first] = log; + expect(first).toBeDefined(); + if (!first) return; + expect(first.type).toBe('injection_attempt'); + expect(first.timestamp).toBeDefined(); }); it('should clear the log', () => {