From 9af71c85c2e779aeabd6249ff32f9f054a266b55 Mon Sep 17 00:00:00 2001 From: pavel444-byte Date: Sat, 10 Oct 2026 10:27:47 +0500 Subject: [PATCH] Add MineGeo gameplay systems and interface --- .env.example | 13 + .gitignore | 1 + .../integrations/discord/dispatch/route.ts | 29 + app/appearance.css | 3 + app/community.css | 40 + app/layout.tsx | 1 + app/support.css | 3 + components/CommunityLinks.tsx | 65 + components/DiscordIntegration.tsx | 118 + components/Moderation.tsx | 31 +- components/ModeratorSettings.tsx | 2 +- components/MultiplayerRoom.tsx | 74 +- components/Shell.tsx | 2 + components/Username.tsx | 6 + docs/bug-reports.md | 2 +- docs/discord-integration.md | 135 + docs/moderation.md | 17 +- docs/vercel-deployment.md | 18 + lib/api.ts | 135 +- lib/discord-announcements.ts | 226 + lib/discord-cleanup-scheduler.ts | 38 + lib/discord-cleanup-workflow.ts | 154 + lib/discord-client.ts | 291 + lib/discord-delivery.ts | 67 + lib/multiplayer-api.ts | 47 +- next.config.ts | 3 +- package-lock.json | 6705 ++++++++++++++++- package.json | 1 + proxy.ts | 2 +- scripts/test-admin-permissions-production.mjs | 247 + scripts/test-admin-permissions-sql.mjs | 190 + scripts/test-bug-reports-sql.mjs | 10 +- scripts/test-discord-http.mjs | 184 + scripts/test-discord-sql.mjs | 391 + scripts/test-profile-appearance-browser.mjs | 10 +- scripts/test-site-settings-sql.mjs | 30 +- shared/database.ts | 39 + shared/discord.ts | 35 + shared/locales/ru.json | 54 +- shared/multiplayer.ts | 1 + shared/permissions.ts | 10 + .../20261009123642_admin_permissions.sql | 119 + .../20261009133431_discord_announcements.sql | 100 + .../20261009153708_discord_party_cleanup.sql | 82 + tests/discord.test.ts | 293 + tests/permissions.test.ts | 19 + tests/sql/moderation.sql | 4 +- 47 files changed, 9954 insertions(+), 93 deletions(-) create mode 100644 app/api/integrations/discord/dispatch/route.ts create mode 100644 app/community.css create mode 100644 components/CommunityLinks.tsx create mode 100644 components/DiscordIntegration.tsx create mode 100644 docs/discord-integration.md create mode 100644 lib/discord-announcements.ts create mode 100644 lib/discord-cleanup-scheduler.ts create mode 100644 lib/discord-cleanup-workflow.ts create mode 100644 lib/discord-client.ts create mode 100644 lib/discord-delivery.ts create mode 100644 scripts/test-admin-permissions-production.mjs create mode 100644 scripts/test-admin-permissions-sql.mjs create mode 100644 scripts/test-discord-http.mjs create mode 100644 scripts/test-discord-sql.mjs create mode 100644 shared/discord.ts create mode 100644 shared/permissions.ts create mode 100644 supabase/migrations/20261009123642_admin_permissions.sql create mode 100644 supabase/migrations/20261009133431_discord_announcements.sql create mode 100644 supabase/migrations/20261009153708_discord_party_cleanup.sql create mode 100644 tests/discord.test.ts create mode 100644 tests/permissions.test.ts diff --git a/.env.example b/.env.example index 6e3dd1f..89e81fa 100644 --- a/.env.example +++ b/.env.example @@ -4,6 +4,19 @@ MINEGEO_URL=http://localhost:3000 MINEGEO_HOST=127.0.0.1 # Optional comma-separated exact public origins for a reverse proxy or aliases. MINEGEO_ALLOWED_ORIGINS= +# Optional public community links. Leave either blank to hide its footer button. +# Discord: https://discord.gg/ or https://discord.com/invite/ +# Telegram: https://t.me/ (private group invite links also work). +MINEGEO_DISCORD_URL= +MINEGEO_TELEGRAM_URL= +# Optional server-only Discord bot integration. All published parties use one channel. +# Create a bot at https://discord.com/developers/applications and invite it to your server. +# Channel IDs are copied with Discord Developer Mode enabled. Never use NEXT_PUBLIC_ here. +DISCORD_BOT_TOKEN= +DISCORD_MAPS_CHANNEL_ID= +DISCORD_PARTIES_CHANNEL_ID= +# Optional bearer secret for scheduled GET /api/integrations/discord/dispatch retries. +CRON_SECRET= # Development testing only; production rejects this bypass of CSRF protection. MINEGEO_ALLOW_ANY_ORIGIN=false NEXT_PUBLIC_SUPABASE_URL= diff --git a/.gitignore b/.gitignore index 8a89982..25f6875 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ public/downloads/*.jar # Generated by Next.js next-env.d.ts +/.swc diff --git a/app/api/integrations/discord/dispatch/route.ts b/app/api/integrations/discord/dispatch/route.ts new file mode 100644 index 0000000..8721da7 --- /dev/null +++ b/app/api/integrations/discord/dispatch/route.ts @@ -0,0 +1,29 @@ +import { timingSafeEqual } from "node:crypto"; +import { NextRequest, NextResponse } from "next/server"; +import { dispatchDiscordAnnouncements } from "@/lib/discord-announcements"; +export const runtime = "nodejs"; +export const dynamic = "force-dynamic"; +export const maxDuration = 300; +export async function GET(request: NextRequest) { + const secret = process.env.CRON_SECRET; + const actual = Buffer.from(request.headers.get("authorization") ?? ""); + const expected = Buffer.from(`Bearer ${secret ?? ""}`); + if ( + !secret || + actual.length !== expected.length || + !timingSafeEqual(actual, expected) + ) + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + try { + await dispatchDiscordAnnouncements(); + return NextResponse.json( + { ok: true }, + { headers: { "Cache-Control": "no-store" } }, + ); + } catch { + return NextResponse.json( + { error: "Discord dispatch will retry" }, + { status: 503 }, + ); + } +} diff --git a/app/appearance.css b/app/appearance.css index b052105..ae83a9d 100644 --- a/app/appearance.css +++ b/app/appearance.css @@ -326,6 +326,9 @@ html[data-theme="light"] .success-message { html[data-theme="light"] .username-moderator { color: #246494; } +html[data-theme="light"] .username-admin { + color: #8c491e; +} html[data-theme="light"] .username-bug-finder { color: #866318; } diff --git a/app/community.css b/app/community.css new file mode 100644 index 0000000..854b965 --- /dev/null +++ b/app/community.css @@ -0,0 +1,40 @@ +.community-links { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 10px; + margin-top: 18px; +} +.community-link { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 8px; + min-height: 44px; + max-width: 100%; + padding: 10px 13px; + border: 1px solid var(--line); + border-radius: var(--radius-control); + background: var(--panel); + color: var(--text); + font: + 600 12px/1.5 Arial, + Helvetica, + sans-serif; +} +.community-link:hover { + border-color: var(--green); + background: var(--panel2); +} +.community-discord > svg:first-child { + color: #bab3ff; +} +.community-telegram > svg:first-child { + color: #83d3ff; +} +html[data-theme="light"] .community-discord > svg:first-child { + color: #6250b4; +} +html[data-theme="light"] .community-telegram > svg:first-child { + color: #20709d; +} diff --git a/app/layout.tsx b/app/layout.tsx index 8f58744..f612e12 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -17,6 +17,7 @@ import "./mobile-game.css"; import "./multiplayer.css"; import "./support.css"; import "./appearance.css"; +import "./community.css"; import { themeInitScript } from "@/shared/theme"; import { SiteSettingsProvider } from "@/components/SiteSettingsProvider"; import { initialSiteSettings } from "@/lib/site-settings"; diff --git a/app/support.css b/app/support.css index e4538ef..8e1cfbe 100644 --- a/app/support.css +++ b/app/support.css @@ -178,6 +178,9 @@ .username-moderator { color: #91d3ff; } +.username-admin { + color: #ffc18a; +} .username-bug-finder { color: #ffe6a1; } diff --git a/components/CommunityLinks.tsx b/components/CommunityLinks.tsx new file mode 100644 index 0000000..0394818 --- /dev/null +++ b/components/CommunityLinks.tsx @@ -0,0 +1,65 @@ +import "server-only"; +import { MessageCircle, Send, ArrowUpRight } from "lucide-react"; +import { getI18n } from "@/lib/i18n"; + +function communityUrl(value: string | undefined, hosts: readonly string[]) { + if (!value?.trim()) return null; + try { + const url = new URL(value.trim()); + if ( + url.protocol !== "https:" || + url.username || + url.password || + url.port || + !hosts.includes(url.hostname) || + url.pathname === "/" + ) + return null; + return url.href; + } catch { + return null; + } +} + +export async function CommunityLinks() { + const { t } = await getI18n(); + const discord = communityUrl(process.env.MINEGEO_DISCORD_URL, [ + "discord.gg", + "discord.com", + "www.discord.com", + "discordapp.com", + ]); + const telegram = communityUrl(process.env.MINEGEO_TELEGRAM_URL, [ + "t.me", + "telegram.me", + ]); + if (!discord && !telegram) return null; + return ( +
+ {discord && ( + + + )} + {telegram && ( + + + )} +
+ ); +} diff --git a/components/DiscordIntegration.tsx b/components/DiscordIntegration.tsx new file mode 100644 index 0000000..91acbd7 --- /dev/null +++ b/components/DiscordIntegration.tsx @@ -0,0 +1,118 @@ +"use client"; +import { useEffect, useState } from "react"; +import { MessageCircle } from "lucide-react"; +import { api } from "@/lib/client-api"; +import { useI18n } from "./I18nProvider"; +import type { DiscordStatus } from "@/shared/discord"; +export function DiscordIntegration() { + const { t } = useI18n(); + const [status, setStatus] = useState(null), + [error, setError] = useState(""), + [message, setMessage] = useState(""), + [busy, setBusy] = useState(false); + async function load() { + try { + setStatus(await api("moderation/discord")); + setError(""); + } catch (e) { + setError((e as Error).message); + } + } + useEffect(() => { + void load(); + }, []); + const configured = !!( + status?.botConfigured && + status.publicUrlConfigured && + (status.mapsConfigured || status.partiesConfigured) + ); + async function run(action: "check" | "retry") { + setBusy(true); + setError(""); + setMessage(""); + try { + await api("moderation/discord", { action }); + setMessage( + action === "check" + ? "Discord bot and channel access checked. Send Messages and Embed Links are also required." + : "Saved Discord announcements will retry.", + ); + await load(); + } catch (e) { + setError((e as Error).message); + } finally { + setBusy(false); + } + } + return ( +
+
+
+

+ {t( + "New approved maps are announced automatically. Hosts can publish an open party to the shared Discord channel.", + )} +

+ {status && ( + <> +

+ {configured + ? t("Discord bot configured") + : t("Discord setup required in Vercel environment variables")} +

+

+ {t("Map announcements: {state}", { + state: status.mapsConfigured + ? t("Configured") + : t("Not configured"), + })}{" "} + ·{" "} + {t("Party announcements: {state}", { + state: status.partiesConfigured + ? t("Configured") + : t("Not configured"), + })} +

+

+ {t("{pending} pending · {failed} need attention · {sent} sent", { + pending: status.pending, + failed: status.failed, + sent: status.sent, + })} +

+ + )} + {error && ( +

+ {t(error)} +

+ )} + {message &&

{t(message)}

} +
+ + + +
+
+ ); +} diff --git a/components/Moderation.tsx b/components/Moderation.tsx index 3674123..97e2fd8 100644 --- a/components/Moderation.tsx +++ b/components/Moderation.tsx @@ -10,6 +10,8 @@ import { reportCategories } from "@/shared/moderation"; import type { Appeal } from "./MapAppeal"; import { ModeratorSettings } from "./ModeratorSettings"; import { ModeratorPrefixes } from "./ModeratorPrefixes"; +import { isAdministrator, type StaffRole } from "@/shared/permissions"; +import { DiscordIntegration } from "./DiscordIntegration"; type ReviewMap = { id: string; name: string; @@ -45,7 +47,8 @@ export function Moderation() { [reasons, setReasons] = useState>({}); const [preview, setPreview] = useState(null), [loading, setLoading] = useState(true); - const [authorized, setAuthorized] = useState(false); + const [role, setRole] = useState(null); + const administrator = isAdministrator(role ?? undefined); const [reportPreview, setReportPreview] = useState<{ id: string; panorama: string | null; @@ -53,17 +56,21 @@ export function Moderation() { async function load() { try { const d = await api<{ + role: StaffRole; maps: ReviewMap[]; reports: typeof reports; appeals: Appeal[]; }>("moderation"); setMaps(d.maps); - setAuthorized(true); + setRole(d.role); setReports(d.reports); setAppeals(d.appeals); setError(""); } catch (e) { - setAuthorized(false); + setRole(null); + setMaps([]); + setReports([]); + setAppeals([]); setError((e as Error).message); } finally { setLoading(false); @@ -114,8 +121,16 @@ export function Moderation() { } return ( <> - {authorized && } - {authorized && } + {administrator && } + {administrator && } + {administrator && } + {role === "moderator" && ( +

+ {t( + "Moderators can inspect and review pending maps and updates. Other controls require Admin permission.", + )} +

+ )} {error && (

{t(error)} @@ -185,7 +200,7 @@ export function Moderation() { ) : null} - {m.state === "published" && ( + {administrator && m.state === "published" && ( + {owner && (room.discord?.available || room.discord?.state) && ( +

+

+ {t( + "Publishing shares this invite with everyone in the community Discord channel.", + )} +

+ {room.discord.state === null && ( + + )} + {room.discord.state === "sent" && ( +

{t("Party posted to Discord.")}

+ )} + {["pending", "sending"].includes( + room.discord.state ?? "", + ) && ( +

+ {t( + "Party announcement is waiting for Discord delivery.", + )} +

+ )} + {room.discord.state === "failed" && ( +

+ {t( + "Discord delivery needs an administrator to check the bot setup and retry.", + )} +

+ )} + {room.discord.state === "skipped" && ( +

+ {t( + "This party announcement expired or the lobby is no longer available.", + )} +

+ )} + {shareMessage && !room.discord.state && ( +

{t(shareMessage)}

+ )} +
+ )}

{t("{count} / {limit} players", { count: room.playerCount, diff --git a/components/Shell.tsx b/components/Shell.tsx index be09f3d..87f4115 100644 --- a/components/Shell.tsx +++ b/components/Shell.tsx @@ -8,6 +8,7 @@ import { Brand } from "./Brand"; import { SiteNav } from "./SiteNav"; import { ReportBugButton } from "./ReportBugButton"; import { ThemeToggle } from "./ThemeToggle"; +import { CommunityLinks } from "./CommunityLinks"; export async function Header() { const { t } = await getI18n(); const account = await user(); @@ -37,6 +38,7 @@ export async function Footer() {

{t("A familiar world. A whole new perspective.")}

+
diff --git a/components/Username.tsx b/components/Username.tsx index a8b89d9..b097ddc 100644 --- a/components/Username.tsx +++ b/components/Username.tsx @@ -38,6 +38,12 @@ export function Username({ Star; return ( + {appearance.role === "admin" && ( + + + )} {appearance.role === "moderator" && ( `. +This endpoint claims at most five due announcements. A Vercel Cron entry can use +that path; scheduling limits depend on your plan. No schedule is installed by +default, and the secret must never be placed in a URL. + +## Database and verification + +Apply `20261009133431_discord_announcements.sql` and +`20261009153708_discord_party_cleanup.sql` after existing migrations. +The queue and functions are service-role-only, use RLS and invoker permissions, +and verify the host from the room's server-verified access hash. No Discord +credentials are stored in Supabase or sent to a browser. + +Vercel Workflow uses the project's managed backend automatically. Keep **Settings +→ Environment Variables → Enable access to System Environment Variables** enabled +and redeploy after changing it. No additional secret or scheduled cron is needed +for the five-minute timer. The bot edits and deletes its own announcements, so +**Manage Messages** is not required. + +Run `npm test`, `npm run typecheck -- --incremental false`, `npm run build`, and +`node scripts/test-discord-sql.mjs`. `node scripts/test-discord-http.mjs` verifies +the live role gates, destination restrictions, optional configuration, and +host-only room action with temporary accounts and a room, which it removes. +Discord transport tests use a mocked API; real delivery requires your bot token +and channel permissions. diff --git a/docs/moderation.md b/docs/moderation.md index 26238e7..0f72664 100644 --- a/docs/moderation.md +++ b/docs/moderation.md @@ -1,11 +1,13 @@ -# Moderator access +# Admin and Moderator access MineGeo checks `public.profiles.role` on the server for every moderation request. A contributor cannot change their own role through the website or Supabase's public API. No Discord role, JWT metadata, or additional plugin is required. 1. Have the moderator sign in to MineGeo and open their dashboard once. 2. In the **MineGeo** Supabase project, open **Authentication → Users** and copy that person's user UUID. -3. Open **Table Editor → public.profiles**, find the matching `id`, and set `role` to `moderator`. Keep the existing ID and display name. -4. The moderator can open `/moderation`, inspect each location in 360°, approve a submitted map, return it with feedback, unpublish a map, and resolve community reports. The role takes effect on the next request. +3. Open **Table Editor → public.profiles**, find the matching `id`, and set `role` to `moderator` or `admin`. Keep the existing ID and display name. +4. Moderators can open `/moderation`, inspect pending maps and updates in 360°, approve them, or return them with feedback. Admins also manage the beta badge, player prefixes, community reports, ban appeals, and unpublishing. The role takes effect on the next request. + +Apply `20261009123642_admin_permissions.sql` before deploying this role split. The account **PerfectPython500** has Admin permission. The username shows an Admin crown; Moderators retain their shield. Neither role can be assigned by editing a player profile through the website or a public Supabase client. Admin assignment remains a project-owner operation in Supabase. Alternatively, the project owner can run this in Supabase's SQL Editor, replacing the email with the intended account: @@ -26,9 +28,9 @@ Treat the website role separately from Supabase organization membership: moderat Signed-in players can use **Report content** on a published map page to report the entire map, or during a game to report the current panorama. Reports include a category, a 10–2000 character explanation, and an optional HTTP/HTTPS evidence link. The website derives panorama identity from the player's accessible game round; it retains the reported immutable revision for moderator inspection. Owners cannot report their own maps. Each person can have only one open or confirmed report per map until its ban is appealed successfully; dismissed reports can be corrected and resubmitted. -In `/moderation`, inspect the reported surface map and 360° locations, then **Confirm report · add strike** or **Dismiss report**, with written feedback. Pending and dismissed reports never count toward bans. Each distinct reporter can contribute one confirmed strike. Confirming the third report atomically unpublishes the map and blocks publication. The ban applies to the map, not the owner's account. Existing games retain their original revisions; the map disappears from the public catalog and cannot start new games. +In `/moderation`, Admins inspect the reported surface map and 360° locations, then **Confirm report · add strike** or **Dismiss report**, with written feedback. Pending and dismissed reports never count toward bans. Each distinct reporter can contribute one confirmed strike. Confirming the third report atomically unpublishes the map and blocks publication. The ban applies to the map, not the owner's account. Existing games retain their original revisions; the map disappears from the public catalog and cannot start new games. -The owner opens the banned map from `/dashboard` and uses **Appeal map ban**. One appeal can await review at a time. Moderators can inspect the map and approve or reject the appeal with feedback. Rejection keeps the ban and permits another appeal with new information. Approval restores the last published revision, resets the three-strike counter, closes pending reports from the old cycle, and preserves all report and appeal history. A moderator cannot confirm their own report, confirm reports on their own map, or approve their own map's appeal. +The owner opens the banned map from `/dashboard` and uses **Appeal map ban**. One appeal can await review at a time. Admins can inspect the map and approve or reject the appeal with feedback. Rejection keeps the ban and permits another appeal with new information. Approval restores the last published revision, resets the three-strike counter, closes pending reports from the old cycle, and preserves all report and appeal history. An Admin cannot confirm their own report, confirm reports on their own map, or approve their own map's appeal. Contribution uploads older than five days are hidden from the dashboard and the owner's **Player submissions** list. Nothing is deleted: direct contribution links, accepted panoramas, and quota accounting still work. The cutoff uses the contribution's creation time, including pending submissions. @@ -37,8 +39,13 @@ Apply `supabase/migrations/20261006074343_content_reports_and_appeals.sql` after ```sh npm install --prefix .data/sql-verification --no-save --package-lock=false @electric-sql/pglite@0.3.14 node scripts/test-moderation-sql.mjs +node scripts/test-admin-permissions-sql.mjs ``` +After deployment, `node scripts/test-admin-permissions-production.mjs` verifies the +live API using disposable player, Moderator, and Admin accounts. It removes its +own maps and accounts afterward. It does not require a browser. + The reporting migration was applied to the configured **MineGeo** Supabase project on October 6, 2026 after `/api/v1/moderation` returned `PGRST205` for the missing `map_appeals` table. The live Data API now successfully reads moderation maps, open reports, and the appeal queue. Appeal RLS is enabled, and public roles cannot read appeals or execute moderation decisions. The earlier `map_refresh_fixes` migration remains unapplied; applying it was rejected by automatic approval review as outside this reporting fix. # First beta map diff --git a/docs/vercel-deployment.md b/docs/vercel-deployment.md index 5f8005a..e2f2ca3 100644 --- a/docs/vercel-deployment.md +++ b/docs/vercel-deployment.md @@ -1,5 +1,23 @@ # Deploy MineGeo +Optional Discord map and multiplayer-party announcements use `DISCORD_BOT_TOKEN`, +`DISCORD_MAPS_CHANNEL_ID`, and `DISCORD_PARTIES_CHANNEL_ID`. All published parties +use the same configured channel. Follow [Discord bot setup](discord-integration.md) +for creation, permissions, Vercel variables, and retries. Apply the Discord queue +migration before deploying this feature; the bot token stays server-only. + +Optional community buttons appear in the footer when `MINEGEO_DISCORD_URL` or +`MINEGEO_TELEGRAM_URL` is configured. Set these public links in `.env.local` for +local development (ordinary `.env` also works). Restart the local server after +changing a link. For the hosted site, add the same names as Production Config +variables in the MineGeo Vercel project, then redeploy. These values are public +destinations; no Discord bot token or Telegram API key is needed. + +Discord accepts HTTPS links on `discord.gg`, `discord.com`, `www.discord.com`, +and `discordapp.com`. Telegram accepts `t.me` and `telegram.me`, including group +invite links. Blank, malformed, or unsupported URLs hide the corresponding +button. Each button opens its configured destination in a new tab. + Run `npm run deploy` from the project directory. The script collects all web source files and public assets, uploads them in one request, waits for the build, and prints the deployment URL. It uses the [Vercel deployments API](https://vercel.com/docs/rest-api/deployments/create-a-new-deployment). The database credentials already configured in diff --git a/lib/api.ts b/lib/api.ts index a769cfd..323c433 100644 --- a/lib/api.ts +++ b/lib/api.ts @@ -21,7 +21,17 @@ import { moderatorUsersApi } from "./moderator-users-api"; import { contributionCutoff, reportSchema } from "../shared/moderation"; import { siteSettingsSchema } from "../shared/site-settings"; import { getSiteSettings } from "./site-settings"; +import { + canReviewMaps, + isAdministrator, + type StaffRole, +} from "@/shared/permissions"; import { processUploadAfterResponse } from "./process-uploads"; +import { + discordAfterResponse, + discordStatus, + checkDiscordSetup, +} from "./discord-announcements"; import { needsProcessing } from "./fabric-jobs"; import { archiveMetadataSchema, importManifest } from "../shared/map-archive"; import { @@ -56,14 +66,19 @@ async function contributor(request: NextRequest) { if (error) throw new Error(error.message); return owner; } -async function moderator(request: NextRequest) { +async function moderator(request: NextRequest, adminOnly = false) { const owner = await contributor(request); const profile = required( await admin().from("profiles").select("role").eq("id", owner).single(), ); - if (profile.role !== "moderator") - throw new Error("Moderator permission required"); - return owner; + if (!canReviewMaps(profile.role)) + throw new ServiceError("Moderator permission required", 403); + if (adminOnly) requireAdministrator(profile.role); + return { id: owner, role: profile.role }; +} +function requireAdministrator(role: StaffRole) { + if (!isAdministrator(role)) + throw new ServiceError("Admin permission required", 403); } async function limit(key: string, count: number, seconds: number) { const allowed = required( @@ -903,10 +918,42 @@ export async function handle(request: NextRequest, path: string[]) { } } if (path[0] === "moderation") { - const reviewer = await moderator(request); - if (path[1] === "users") + const { id: reviewer, role } = await moderator(request); + if (path[1] === "discord" && path.length === 2) { + requireAdministrator(role); + if (method === "GET") return NextResponse.json(await discordStatus()); + if (method === "POST") { + const body = z + .object({ action: z.enum(["check", "retry"]) }) + .strict() + .parse(await jsonBody(request)); + if (body.action === "check") + return NextResponse.json(await checkDiscordSetup()); + required( + await client.rpc("retry_discord_announcements", { + p_reviewer: reviewer, + }), + ); + required( + await client + .from("discord_announcements") + .update({ cleanup_lease_until: null }) + .not("cleanup_error", "is", null) + .is("cleanup_run_id", null) + .is("deleted_at", null) + .select("id"), + ); + discordAfterResponse(); + return NextResponse.json({ ok: true }); + } + return NextResponse.json({ error: "Not found" }, { status: 404 }); + } + if (path[1] === "users") { + requireAdministrator(role); return await moderatorUsersApi(request, path, reviewer, jsonBody); + } if (path[1] === "settings" && path.length === 2 && method === "PATCH") { + requireAdministrator(role); const body = siteSettingsSchema.parse(await jsonBody(request)); const enabled = required( await client.rpc("set_beta_badge", { @@ -922,6 +969,7 @@ export async function handle(request: NextRequest, path: string[]) { ); } if (path[1] === "appeals" && path.length === 3 && method === "POST") { + requireAdministrator(role); const body = z .object({ decision: z.enum(["approve", "reject"]), @@ -946,6 +994,7 @@ export async function handle(request: NextRequest, path: string[]) { method === "GET" && [4, 5].includes(path.length) ) { + requireAdministrator(role); const report = required( await client .from("reports") @@ -975,7 +1024,11 @@ export async function handle(request: NextRequest, path: string[]) { path[4], ); } - if (method === "GET" && path[2] === "panoramas") { + if ( + method === "GET" && + path[2] === "panoramas" && + [3, 4].includes(path.length) + ) { const d = required( await client .from("datasets") @@ -987,6 +1040,14 @@ export async function handle(request: NextRequest, path: string[]) { throw new Error("Map is not awaiting review or published"); if (d.parent_dataset_id) throw new Error("Contributions require owner approval first"); + if ( + !isAdministrator(role) && + !( + d.state === "review" || + (d.state === "published" && d.update_state === "review") + ) + ) + throw new ServiceError("Map is not awaiting review", 403); const input = required( (d.state === "published" && d.update_state !== "review") || d.state === "unpublished" @@ -1007,29 +1068,36 @@ export async function handle(request: NextRequest, path: string[]) { path[3], ); } - if (method === "GET") { - const maps = required( - await client - .from("datasets") - .select("*") - .is("parent_dataset_id", null) - .in("state", ["review", "published", "unpublished"]) - .order("created_at"), - ); - const reports = required( - await client - .from("reports") - .select("*") - .eq("state", "open") - .order("created_at"), - ); - const appeals = required( - await client - .from("map_appeals") - .select("*") - .eq("state", "open") - .order("created_at"), - ); + if (method === "GET" && path.length === 1) { + let mapQuery = client + .from("datasets") + .select("*") + .is("parent_dataset_id", null) + .in("state", ["review", "published", "unpublished"]) + .order("created_at"); + if (!isAdministrator(role)) + mapQuery = mapQuery.or( + "state.eq.review,and(state.eq.published,update_state.eq.review)", + ); + const maps = required(await mapQuery); + const reports = isAdministrator(role) + ? required( + await client + .from("reports") + .select("*") + .eq("state", "open") + .order("created_at"), + ) + : []; + const appeals = isAdministrator(role) + ? required( + await client + .from("map_appeals") + .select("*") + .eq("state", "open") + .order("created_at"), + ) + : []; const reportViews = await Promise.all( reports.map(async (report) => { const revision = report.revision_id @@ -1058,6 +1126,7 @@ export async function handle(request: NextRequest, path: string[]) { }), ); return NextResponse.json({ + role, maps: await Promise.all( maps.map(async (m) => { const input = @@ -1087,7 +1156,7 @@ export async function handle(request: NextRequest, path: string[]) { appeals, }); } - if (method === "POST") { + if (method === "POST" && path.length === 1) { const b = z .object({ datasetId: idSchema, @@ -1095,6 +1164,7 @@ export async function handle(request: NextRequest, path: string[]) { reason: z.string().max(2000).optional(), }) .parse(await jsonBody(request)); + if (b.decision === "unpublish") requireAdministrator(role); if (b.decision === "reject" && !b.reason?.trim()) throw new Error("Provide a rejection reason"); const { error } = await client.rpc("publish_dataset", { @@ -1104,6 +1174,7 @@ export async function handle(request: NextRequest, path: string[]) { p_reason: b.reason ?? null, }); checkServiceError(error); + if (b.decision === "approve") discordAfterResponse(); return NextResponse.json({ ok: true }); } } @@ -1164,7 +1235,7 @@ export async function handle(request: NextRequest, path: string[]) { return NextResponse.json({ id }); } if (path[0] === "reports" && path.length === 2 && method === "PATCH") { - const reviewer = await moderator(request); + const { id: reviewer } = await moderator(request, true); const b = z .object({ decision: z.enum(["confirm", "dismiss"]), diff --git a/lib/discord-announcements.ts b/lib/discord-announcements.ts new file mode 100644 index 0000000..0b9a262 --- /dev/null +++ b/lib/discord-announcements.ts @@ -0,0 +1,226 @@ +import "server-only"; +import { randomUUID } from "node:crypto"; +import { after } from "next/server"; +import { admin, required } from "./supabase"; +import { + announcementMessage, + discordConfiguration, + DiscordClient, +} from "./discord-client"; +import { deliverDiscordAnnouncement } from "./discord-delivery"; +import { scheduleDiscordCleanup } from "./discord-cleanup-scheduler"; +import type { + DiscordAnnouncement, + DiscordKind, + DiscordStatus, + PartyAnnouncement, +} from "@/shared/discord"; +import type { RoomView } from "@/shared/multiplayer"; +const configuration = () => discordConfiguration(process.env); +export function discordPartyReady() { + const config = configuration(); + return !!(config.token && config.origin && config.partyChannel); +} +async function announcementSource( + job: DiscordAnnouncement, +): Promise[3] | null> { + const db = admin(); + if (job.kind === "map") { + const row = await db + .from("datasets") + .select("id,name,owner_id,panorama_count") + .eq("id", job.target_id) + .eq("state", "published") + .is("parent_dataset_id", null) + .is("imported_from_id", null) + .maybeSingle(); + if (row.error) required(row); + if (!row.data) return null; + const owner = await db + .from("profiles") + .select("display_name") + .eq("id", row.data.owner_id) + .maybeSingle(); + if (owner.error) required(owner); + return { + id: row.data.id, + name: row.data.name, + owner: owner.data?.display_name ?? "Explorer", + locations: row.data.panorama_count, + }; + } + const found = await db + .from("multiplayer_rooms") + .select("*") + .eq("id", job.target_id) + .maybeSingle(); + if (found.error) required(found); + const room = found.data; + if ( + !room || + room.status !== "lobby" || + Date.parse(room.created_at) < Date.now() - 86400000 || + Date.parse(job.created_at) < Date.now() - 3600000 + ) + return null; + const revision = required( + await db + .from("revisions") + .select("dataset_id") + .eq("id", room.revision_id) + .single(), + ); + const map = await db + .from("datasets") + .select("name") + .eq("id", revision.dataset_id) + .eq("state", "published") + .is("parent_dataset_id", null) + .is("imported_from_id", null) + .maybeSingle(); + if (map.error) required(map); + if (!map.data) return null; + const players = required( + await db + .from("multiplayer_players") + .select("id,display_name") + .eq("room_id", room.id) + .eq("has_left", false), + ); + const host = players.find((p) => p.id === room.host_player_id); + if (!host || players.length >= room.player_limit) return null; + return { + id: room.id, + name: map.data.name, + owner: host.display_name, + difficulty: room.difficulty, + players: players.length, + limit: room.player_limit, + }; +} +export async function dispatchDiscordAnnouncements(target?: string) { + const config = configuration(); + if (!config.token || !config.origin) return; + await scheduleDiscordCleanup(target); + const kinds: DiscordKind[] = []; + if (config.mapChannel) kinds.push("map"); + if (config.partyChannel) kinds.push("party"); + if (!kinds.length) return; + const db = admin(), + worker = randomUUID(); + const jobs = required( + await db.rpc("claim_discord_announcements", { + p_worker: worker, + p_kinds: kinds, + p_target: target ?? null, + }), + ); + const client = new DiscordClient(config.token); + for (const job of jobs) { + await deliverDiscordAnnouncement(job, { + origin: config.origin, + channel: job.kind === "map" ? config.mapChannel! : config.partyChannel!, + client, + load: () => announcementSource(job), + finish: async (patch) => { + required( + await db + .from("discord_announcements") + .update(patch) + .eq("id", job.id) + .eq("lease_owner", worker) + .select("id") + .single(), + ); + }, + }); + } + await scheduleDiscordCleanup(target); +} +export function discordAfterResponse(target?: string) { + const c = configuration(); + if (!c.token || !c.origin || (!c.mapChannel && !c.partyChannel)) return; + after(async () => { + try { + await dispatchDiscordAnnouncements(target); + } catch { + console.error( + "Discord delivery paused; saved announcements remain available for retry.", + ); + } + }); +} +export async function partyAnnouncement( + room: RoomView, +): Promise { + if (!room.you || room.you !== room.hostId) return undefined; + const db = admin(); + const result = await db + .from("discord_announcements") + .select("state,next_attempt_at,lease_until") + .eq("kind", "party") + .eq("target_id", room.id) + .maybeSingle(); + if (result.error) required(result); + const map = await db + .from("datasets") + .select("state,imported_from_id,parent_dataset_id") + .eq("id", room.datasetId) + .maybeSingle(); + if (map.error) required(map); + const publicMap = + map.data?.state === "published" && + !map.data.imported_from_id && + !map.data.parent_dataset_id; + if ( + result.data && + ["pending", "sending"].includes(result.data.state) && + Date.parse(result.data.next_attempt_at) <= Date.now() && + (!result.data.lease_until || + Date.parse(result.data.lease_until) <= Date.now()) + ) + discordAfterResponse(room.id); + return { + available: discordPartyReady() && !!publicMap, + state: result.data?.state ?? null, + }; +} +export async function discordStatus(): Promise { + const c = configuration(); + const count = async (states: DiscordAnnouncement["state"][]) => { + let query = admin() + .from("discord_announcements") + .select("id", { count: "exact", head: true }); + query = states.includes("failed") + ? query.or("state.eq.failed,cleanup_error.not.is.null") + : query.in("state", states); + const r = await query; + if (r.error) required(r); + return r.count ?? 0; + }; + const [pending, failed, sent] = await Promise.all([ + count(["pending", "sending"]), + count(["failed"]), + count(["sent"]), + ]); + discordAfterResponse(); + return { + botConfigured: !!c.token, + publicUrlConfigured: !!c.origin, + mapsConfigured: !!c.mapChannel, + partiesConfigured: !!c.partyChannel, + pending, + failed, + sent, + }; +} +export async function checkDiscordSetup() { + const c = configuration(); + if (!c.token || !c.origin || (!c.mapChannel && !c.partyChannel)) + throw new Error( + "Configure the Discord bot token, public site URL, and at least one channel ID.", + ); + return new DiscordClient(c.token).check( + [c.mapChannel, c.partyChannel].filter((v): v is string => !!v), + ); +} diff --git a/lib/discord-cleanup-scheduler.ts b/lib/discord-cleanup-scheduler.ts new file mode 100644 index 0000000..32c75e9 --- /dev/null +++ b/lib/discord-cleanup-scheduler.ts @@ -0,0 +1,38 @@ +import "server-only"; +import { randomUUID } from "node:crypto"; +import { start } from "workflow/api"; +import { admin, required } from "./supabase"; +import { discordConfiguration } from "./discord-client"; +import { cleanupClosedDiscordParty } from "./discord-cleanup-workflow"; + +export async function scheduleDiscordCleanup(target?: string) { + const config = discordConfiguration(process.env); + if (!config.token || !config.origin) return; + const db = admin(), + claim = `starting:${randomUUID()}`; + const jobs = required( + await db.rpc("claim_discord_cleanup", { + p_worker: claim, + p_target: target ?? null, + }), + ); + for (const job of jobs) { + try { + const run = await start(cleanupClosedDiscordParty, [job.id, claim]); + required( + await db + .from("discord_announcements") + .update({ cleanup_run_id: run.runId, cleanup_lease_until: null }) + .eq("id", job.id) + .eq("cleanup_run_id", claim) + .select("id"), + ); + } catch { + // An ambiguous start may already have enqueued a run. Keep the short lease; + // a replay keeps the persisted grace period and safely repeats deletion. + console.error( + "Discord cleanup scheduling paused; the saved announcement will retry.", + ); + } + } +} diff --git a/lib/discord-cleanup-workflow.ts b/lib/discord-cleanup-workflow.ts new file mode 100644 index 0000000..c266bc7 --- /dev/null +++ b/lib/discord-cleanup-workflow.ts @@ -0,0 +1,154 @@ +import { + FatalError, + RetryableError, + getWorkflowMetadata, + sleep, +} from "workflow"; +import { admin, required } from "./supabase"; +import { + DiscordClient, + DiscordDeliveryError, + discordConfiguration, +} from "./discord-client"; + +export async function cleanupClosedDiscordParty(event: string, claim: string) { + "use workflow"; + const run = getWorkflowMetadata().workflowRunId; + try { + const deadline = await markClosed(event); + if (deadline) { + await sleep(new Date(deadline)); + await deleteClosed(event); + } + } catch { + await releaseFailedCleanup(event, claim, run); + throw new Error( + "Discord party cleanup failed; the saved announcement can be retried.", + ); + } +} + +async function record(event: string) { + const result = await admin() + .from("discord_announcements") + .select("*") + .eq("id", event) + .maybeSingle(); + if (result.error) required(result); + const job = result.data; + return job?.kind === "party" && + job.state === "sent" && + job.closed_at && + !job.deleted_at && + job.channel_id && + job.message_id + ? job + : null; +} + +function client() { + const config = discordConfiguration(process.env); + if (!config.token || !config.origin) + throw new FatalError( + "Configure the Discord bot token and public site URL.", + ); + return { client: new DiscordClient(config.token), origin: config.origin! }; +} + +function retry(error: unknown): never { + if (error instanceof DiscordDeliveryError) { + if (error.permanent) throw new FatalError(error.message); + throw new RetryableError(error.message, { + retryAfter: error.retryAfter * 1000, + }); + } + throw new RetryableError("Discord cleanup could not be confirmed.", { + retryAfter: "30s", + }); +} + +async function markClosed(event: string): Promise { + "use step"; + try { + const job = await record(event); + if (!job) return null; + if (job.delete_after) return job.delete_after; + const setup = client(); + const exists = await setup.client.closeParty( + job.channel_id!, + job.message_id!, + setup.origin, + job.target_id, + ); + if (!exists) { + required( + await admin() + .from("discord_announcements") + .update({ deleted_at: new Date().toISOString(), cleanup_error: null }) + .eq("id", event) + .select("id") + .single(), + ); + return null; + } + const result = await admin().rpc("mark_discord_party_closed", { + p_event: event, + }); + if (result.error) required(result); + return result.data; + } catch (error) { + if (error instanceof FatalError || error instanceof RetryableError) + throw error; + retry(error); + } +} +markClosed.maxRetries = 10; + +async function deleteClosed(event: string) { + "use step"; + try { + const job = await record(event); + if (!job) return; + if (!job.delete_after) + throw new FatalError("The closed notice has not been confirmed."); + const deadline = new Date(job.delete_after); + if (deadline.getTime() > Date.now()) + throw new RetryableError("Keep the five-minute grace period.", { + retryAfter: deadline, + }); + await client().client.remove(job.channel_id!, job.message_id!); + required( + await admin() + .from("discord_announcements") + .update({ deleted_at: new Date().toISOString(), cleanup_error: null }) + .eq("id", event) + .select("id") + .single(), + ); + } catch (error) { + if (error instanceof FatalError || error instanceof RetryableError) + throw error; + retry(error); + } +} +deleteClosed.maxRetries = 10; + +async function releaseFailedCleanup(event: string, claim: string, run: string) { + "use step"; + required( + await admin() + .from("discord_announcements") + .update({ + cleanup_run_id: null, + cleanup_lease_until: new Date(Date.now() + 300000).toISOString(), + cleanup_error: + "Party cleanup failed. Check Discord setup and retry saved announcements.", + }) + .eq("id", event) + .in("cleanup_run_id", [claim, run]) + .is("deleted_at", null) + .select("id"), + ); + console.error("Discord party cleanup failed", { event, run }); +} +releaseFailedCleanup.maxRetries = 10; diff --git a/lib/discord-client.ts b/lib/discord-client.ts new file mode 100644 index 0000000..e01be31 --- /dev/null +++ b/lib/discord-client.ts @@ -0,0 +1,291 @@ +import { createHash } from "node:crypto"; +import type { DiscordKind } from "../shared/discord"; + +export function discordConfiguration(env: Record) { + let origin: string | null = null; + try { + const url = new URL(env.MINEGEO_URL ?? ""); + if ( + url.protocol === "https:" && + !url.username && + !url.password && + !url.search && + !url.hash && + url.pathname === "/" + ) + origin = url.origin; + } catch { + /* An integration needs a public site URL. */ + } + const token = env.DISCORD_BOT_TOKEN?.trim() || null; + const channel = (value: string | undefined) => + /^\d{17,20}$/.test(value?.trim() ?? "") ? value!.trim() : null; + return { + token: token && !/\s/.test(token) ? token : null, + origin, + mapChannel: channel(env.DISCORD_MAPS_CHANNEL_ID), + partyChannel: channel(env.DISCORD_PARTIES_CHANNEL_ID), + }; +} +export function discordText(value: string, max = 256) { + return ( + value + .replace(/[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/gu, " ") + .replace(/([\\*_~`|[\]<>])/g, "\\$1") + .slice(0, max) || "Unknown" + ); +} +export type DiscordMessage = { + content: string; + embeds: { + title: string; + url: string; + description: string; + color: number; + thumbnail: { url: string }; + fields: { name: string; value: string; inline: boolean }[]; + footer: { text: string }; + }[]; + allowed_mentions: { parse: never[] }; + nonce: string; + enforce_nonce: true; +}; +export function announcementMessage( + event: string, + kind: DiscordKind, + origin: string, + data: { + id: string; + name: string; + owner: string; + locations?: number; + difficulty?: string; + players?: number; + limit?: number; + }, +): DiscordMessage { + const link = `${origin}/${kind === "map" ? "servers" : "multiplayer"}/${data.id}`; + return { + content: + kind === "map" + ? "A new MineGeo map is ready to explore!" + : "A MineGeo multiplayer party is looking for players!", + allowed_mentions: { parse: [] }, + nonce: createHash("sha256").update(event).digest("hex").slice(0, 24), + enforce_nonce: true, + embeds: [ + { + title: discordText(data.name), + url: link, + color: 0x55d6ad, + description: + kind === "map" + ? `[Explore this map](${link})` + : `[Join this party](${link}) — join while the lobby is open.`, + thumbnail: { url: `${origin}/images/minegeo-mark.webp` }, + fields: + kind === "map" + ? [ + { + name: "Created by", + value: discordText(data.owner), + inline: true, + }, + { + name: "Locations", + value: String(data.locations), + inline: true, + }, + ] + : [ + { name: "Host", value: discordText(data.owner), inline: true }, + { + name: "Difficulty", + value: discordText(data.difficulty ?? "Normal"), + inline: true, + }, + { + name: "Players", + value: `${data.players} / ${data.limit}`, + inline: true, + }, + ], + footer: { + text: + kind === "map" + ? "MineGeo · Approved community map" + : "MineGeo · Lobby details at the time of sharing", + }, + }, + ], + }; +} +export class DiscordDeliveryError extends Error { + constructor( + message: string, + public retryAfter = 60, + public permanent = false, + ) { + super(message); + } +} +export class DiscordClient { + constructor( + private token: string, + private request: typeof fetch = fetch, + ) {} + private async api( + path: string, + body?: object, + method = body ? "POST" : "GET", + ) { + let response: Response; + try { + response = await this.request(`https://discord.com/api/v10${path}`, { + method, + headers: { + Authorization: `Bot ${this.token}`, + "Content-Type": "application/json", + "User-Agent": "MineGeo (https://minegeo.vercel.app, 1.0)", + }, + ...(body ? { body: JSON.stringify(body) } : {}), + signal: AbortSignal.timeout(15_000), + redirect: "error", + }); + } catch { + throw new DiscordDeliveryError( + "Discord could not be reached. Delivery will retry.", + ); + } + const data = await response.json().catch(() => null); + // A manually removed message already satisfies cleanup. Other 404s still fail. + if ( + ["PATCH", "DELETE"].includes(method) && + response.status === 404 && + data?.code === 10008 + ) + return null; + if (!response.ok) { + if (response.status === 429) { + const delay = Number( + data?.retry_after ?? response.headers.get("retry-after"), + ); + throw new DiscordDeliveryError( + "Discord rate limit. Delivery will retry.", + Number.isFinite(delay) && delay > 0 + ? Math.min(Math.ceil(delay), 86400) + : 60, + ); + } + if (response.status === 401) + throw new DiscordDeliveryError( + "Discord bot token is invalid. Update it in Vercel.", + 60, + true, + ); + if (response.status === 403) + throw new DiscordDeliveryError( + "Discord bot needs View Channel, Send Messages, and Embed Links in the configured channel.", + 60, + true, + ); + if (response.status === 404) + throw new DiscordDeliveryError( + "Discord channel was not found. Check the channel ID and bot access.", + 60, + true, + ); + throw new DiscordDeliveryError( + "Discord rejected this notification.", + 60, + response.status < 500, + ); + } + if (method === "DELETE" && response.status === 204) return null; + if (!data) + throw new DiscordDeliveryError("Discord did not confirm delivery."); + return data; + } + async send(channel: string, message: DiscordMessage) { + if (!/^\d{17,20}$/.test(channel)) + throw new DiscordDeliveryError( + "Discord channel ID is invalid.", + 60, + true, + ); + const result = await this.api(`/channels/${channel}/messages`, message); + if (!/^\d{17,20}$/.test(result.id ?? "") || result.channel_id !== channel) + throw new DiscordDeliveryError("Discord did not confirm delivery."); + return result.id as string; + } + private messagePath(channel: string, message: string) { + if (![channel, message].every((id) => /^\d{17,20}$/.test(id))) + throw new DiscordDeliveryError( + "Discord message identity is invalid.", + 60, + true, + ); + return `/channels/${channel}/messages/${message}`; + } + async closeParty( + channel: string, + message: string, + origin: string, + room: string, + ) { + const result = await this.api( + this.messagePath(channel, message), + closedPartyMessage(origin, room), + "PATCH", + ); + if (result === null) return false; + if (result.id !== message || result.channel_id !== channel) + throw new DiscordDeliveryError( + "Discord did not confirm the party update.", + ); + return true; + } + async remove(channel: string, message: string) { + await this.api(this.messagePath(channel, message), undefined, "DELETE"); + } + async check(channels: string[]) { + const bot = await this.api("/users/@me"); + if (!bot.bot) + throw new DiscordDeliveryError("Use a Discord bot token.", 60, true); + for (const channel of new Set(channels)) { + if (!/^\d{17,20}$/.test(channel)) + throw new DiscordDeliveryError( + "Discord channel ID is invalid.", + 60, + true, + ); + const found = await this.api(`/channels/${channel}`); + if (!found.guild_id || ![0, 5].includes(found.type)) + throw new DiscordDeliveryError( + "Choose a Discord server text or announcement channel.", + 60, + true, + ); + } + return { botName: discordText(bot.username) }; + } +} + +export function closedPartyMessage(origin: string, room: string) { + return { + content: "🔴 This MineGeo multiplayer party is closed.", + allowed_mentions: { parse: [] }, + embeds: [ + { + title: "Party closed", + url: `${origin}/multiplayer/${room}`, + color: 0xed4245, + description: + "This party is no longer accepting players. This message will be removed in five minutes.", + fields: [{ name: "Status", value: "Closed", inline: true }], + footer: { text: "MineGeo · Party closed" }, + }, + ], + components: [], + }; +} diff --git a/lib/discord-delivery.ts b/lib/discord-delivery.ts new file mode 100644 index 0000000..fffb7b6 --- /dev/null +++ b/lib/discord-delivery.ts @@ -0,0 +1,67 @@ +import { + announcementMessage, + DiscordClient, + DiscordDeliveryError, +} from "./discord-client"; +import type { DiscordAnnouncement } from "../shared/discord"; +type Source = Parameters[3]; +export async function deliverDiscordAnnouncement( + job: DiscordAnnouncement, + dependencies: { + origin: string; + channel: string; + client: Pick; + load: () => Promise; + finish: (patch: Partial) => Promise; + now?: () => number; + }, +) { + const { + origin, + channel, + client, + load, + finish, + now = Date.now, + } = dependencies; + const released = { lease_owner: null, lease_until: null }; + try { + const source = await load(); + if (!source) { + await finish({ ...released, state: "skipped", last_error: null }); + return; + } + const messageId = await client.send( + channel, + announcementMessage(job.id, job.kind, origin, source), + ); + await finish({ + ...released, + state: "sent", + channel_id: channel, + message_id: messageId, + sent_at: new Date(now()).toISOString(), + last_error: null, + }); + } catch (failure) { + const error = + failure instanceof DiscordDeliveryError + ? failure + : new DiscordDeliveryError( + "Discord delivery could not be confirmed. Delivery will retry.", + ); + await finish({ + ...released, + state: error.permanent || job.attempts >= 8 ? "failed" : "pending", + last_error: error.message, + next_attempt_at: new Date( + now() + + Math.max( + error.retryAfter, + Math.min(3600, 30 * 2 ** (job.attempts - 1)), + ) * + 1000, + ).toISOString(), + }); + } +} diff --git a/lib/multiplayer-api.ts b/lib/multiplayer-api.ts index 5528983..2aefcf9 100644 --- a/lib/multiplayer-api.ts +++ b/lib/multiplayer-api.ts @@ -8,6 +8,12 @@ import { multiplayerAppearance } from "./multiplayer-appearance"; import { mediaUrls } from "./catalog"; import { requestOrigin } from "./request-origin"; import { ServiceError } from "./service-error"; +import { scheduleDiscordCleanup } from "./discord-cleanup-scheduler"; +import { + discordPartyReady, + discordAfterResponse, + partyAnnouncement, +} from "./discord-announcements"; import { createRoomSchema, joinRoomSchema, @@ -83,6 +89,35 @@ export async function multiplayerApi( } const id = z.string().uuid().parse(path[1]); const action = path[2] ?? "view"; + if (action === "discord" && path.length === 3 && request.method === "POST") { + const view = required( + await client.rpc("multiplayer_action", { + p_room: id, + p_access_hash: accessHash, + p_action: "view", + p_payload: {}, + }), + ) as unknown as RoomView; + if (!savedSecret || !view.you || view.you !== view.hostId) + throw new ServiceError("Only the room owner can publish this party", 403); + z.object({}) + .strict() + .parse(await jsonBody(request)); + if (!discordPartyReady()) + throw new ServiceError( + "Discord party sharing is not configured yet", + 503, + ); + await limit("multiplayer:discord:" + accessHash, 5, 3600); + required( + await client.rpc("publish_discord_party", { + p_room: id, + p_access_hash: accessHash, + }), + ); + discordAfterResponse(id); + return respond({ ok: true }); + } if ( action === "chat" && path.length === 3 && @@ -144,6 +179,13 @@ export async function multiplayerApi( p_payload: payload, }), ) as unknown as RoomView; + if (room.status === "closed") { + try { + await scheduleDiscordCleanup(room.id); + } catch { + console.error("Closed Discord party remains queued for cleanup."); + } + } if (action === "join" && room.you) { const account = await user(); if (account) { @@ -170,7 +212,10 @@ export async function multiplayerApi( ...appearances.get(player.id), })); } - if (action !== "media") return respond(room); + if (action !== "media") { + room.discord = await partyAnnouncement(room); + return respond(room); + } if (!room.you) throw new ServiceError("Join this room to play", 403); if (["lobby", "closed"].includes(room.status)) throw new ServiceError("This round is not available yet", 409); diff --git a/next.config.ts b/next.config.ts index e2aef9d..3ce84de 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,4 +1,5 @@ import type { NextConfig } from "next"; +import { withWorkflow } from "workflow/next"; const config: NextConfig = { poweredByHeader: false, outputFileTracingIncludes: { "/downloads": ["./public/tools/**/*.jar"] }, @@ -26,4 +27,4 @@ const config: NextConfig = { }, allowedDevOrigins: ["192.168.0.120", "minegeotest.12345em.ru"], }; -export default config; +export default withWorkflow(config); diff --git a/package-lock.json b/package-lock.json index d36a94c..a778265 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "sharp": "0.35.5", "three": "0.186.1", "tus-js-client": "4.3.1", + "workflow": "5.2.0", "zod": "4.1.12" }, "devDependencies": { @@ -61,6 +62,200 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@aws-sdk/core": { + "version": "3.978.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.978.1.tgz", + "integrity": "sha512-LbY9aGsEiznDWmUc30Nwv3aIX/+dbwTx8KfS0yOC3NPYMO+O91e6jkT1azf34FwjOndq8/Q+RcVVZz5xnerwdg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.974.6", + "@aws-sdk/xml-builder": "^3.972.41", + "@aws/lambda-invoke-store": "^0.3.0", + "@smithy/core": "^3.35.0", + "@smithy/signature-v4": "^5.7.3", + "@smithy/types": "^4.19.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.972.49", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.49.tgz", + "integrity": "sha512-IYx1lN38MnnPXv+NBLpuATu0cZakbZ321TAfjW+aVkw7HIJF38YnEwdeEO55MSl3pl7hIX1IvvnD6EmnAzmAJw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.18", + "@aws-sdk/nested-clients": "^3.997.17", + "@aws-sdk/types": "^3.973.11", + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.997.46", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.46.tgz", + "integrity": "sha512-oRxtBcka/JGHGs9l9p9IVajGoTP8vTPmoAzdHGy4Qcy9P5vPnDf6nhIeM/COQNY9k/OahImTRaLkHftoXvfcmQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.978.1", + "@aws-sdk/signature-v4-multi-region": "^3.996.47", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.35.0", + "@smithy/fetch-http-handler": "^5.8.0", + "@smithy/node-http-handler": "^4.12.1", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.996.47", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.47.tgz", + "integrity": "sha512-Zk08macMvQTHzQJCLJVkOlviVoqwYMrpXv4lmLN7b7sAbiMoOK7Go0NYdR5UeF+MW8LIbRmwrNy9u/5VvX1U5g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.974.6", + "@smithy/signature-v4": "^5.7.3", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.974.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.974.6.tgz", + "integrity": "sha512-v/clNZzZnDxGyvpHMOGpJKVXFAExJzUNAAjaWGdcx8QAcXLGwTaOkw33p5SHAi0YAioK32xB3hWwOekRVfmfKg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.972.41", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.41.tgz", + "integrity": "sha512-ctjVSyCMegrWfXlx6VqzSBFI6UqmQ5ZlnfMhdLIiWmhoH8UAQxSCP5N3OpG7X3k4LnS7ou74C4mt20+bfTW2aQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.3.0.tgz", + "integrity": "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@borewit/text-codec": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", + "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==", + "license": "MIT", + "peer": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@cbor-extract/cbor-extract-darwin-arm64": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-darwin-arm64/-/cbor-extract-darwin-arm64-2.2.2.tgz", + "integrity": "sha512-ZKZ/F8US7JR92J4DMct6cLW/Y66o2K576+zjlEN/MevH70bFIsB10wkZEQPLzl2oNh2SMGy55xpJ9JoBRl5DOA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@cbor-extract/cbor-extract-darwin-x64": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-darwin-x64/-/cbor-extract-darwin-x64-2.2.2.tgz", + "integrity": "sha512-32b1mgc+P61Js+KW9VZv/c+xRw5EfmOcPx990JbCBSkYJFY0l25VinvyyWfl+3KjibQmAcYwmyzKF9J4DyKP/Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@cbor-extract/cbor-extract-linux-arm": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-arm/-/cbor-extract-linux-arm-2.2.2.tgz", + "integrity": "sha512-tNg0za41TpQfkhWjptD+0gSD2fggMiDCSacuIeELyb2xZhr7PrhPe5h66Jc67B/5dmpIhI2QOUtv4SBsricyYQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@cbor-extract/cbor-extract-linux-arm64": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-arm64/-/cbor-extract-linux-arm64-2.2.2.tgz", + "integrity": "sha512-wfqgzqCAy/Vn8i6WVIh7qZd0DdBFaWBjPdB6ma+Wihcjv0gHqD/mw3ouVv7kbbUNrab6dKEx/w3xQZEdeXIlzg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@cbor-extract/cbor-extract-linux-x64": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-x64/-/cbor-extract-linux-x64-2.2.2.tgz", + "integrity": "sha512-rpiLnVEsqtPJ+mXTdx1rfz4RtUGYIUg2rUAZgd1KjiC1SehYUSkJN7Yh+aVfSjvCGtVP0/bfkQkXpPXKbmSUaA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@cbor-extract/cbor-extract-win32-x64": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-win32-x64/-/cbor-extract-win32-x64-2.2.2.tgz", + "integrity": "sha512-dI+9P7cfWxkTQ+oE+7Aa6onEn92PHgfWXZivjNheCRmTBDBf2fx6RyTi0cmgpYLnD1KLZK9ZYrMxaPZ4oiXhGA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@dimforge/rapier3d-compat": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz", @@ -1053,7 +1248,6 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -1064,7 +1258,6 @@ "version": "2.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -1075,7 +1268,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -1085,20 +1277,431 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@keyv/serialize": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", + "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", + "license": "MIT", + "peer": true + }, + "node_modules/@lukeed/csprng": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@napi-rs/nice": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice/-/nice-1.1.1.tgz", + "integrity": "sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/nice-android-arm-eabi": "1.1.1", + "@napi-rs/nice-android-arm64": "1.1.1", + "@napi-rs/nice-darwin-arm64": "1.1.1", + "@napi-rs/nice-darwin-x64": "1.1.1", + "@napi-rs/nice-freebsd-x64": "1.1.1", + "@napi-rs/nice-linux-arm-gnueabihf": "1.1.1", + "@napi-rs/nice-linux-arm64-gnu": "1.1.1", + "@napi-rs/nice-linux-arm64-musl": "1.1.1", + "@napi-rs/nice-linux-ppc64-gnu": "1.1.1", + "@napi-rs/nice-linux-riscv64-gnu": "1.1.1", + "@napi-rs/nice-linux-s390x-gnu": "1.1.1", + "@napi-rs/nice-linux-x64-gnu": "1.1.1", + "@napi-rs/nice-linux-x64-musl": "1.1.1", + "@napi-rs/nice-openharmony-arm64": "1.1.1", + "@napi-rs/nice-win32-arm64-msvc": "1.1.1", + "@napi-rs/nice-win32-ia32-msvc": "1.1.1", + "@napi-rs/nice-win32-x64-msvc": "1.1.1" + } + }, + "node_modules/@napi-rs/nice-android-arm-eabi": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm-eabi/-/nice-android-arm-eabi-1.1.1.tgz", + "integrity": "sha512-kjirL3N6TnRPv5iuHw36wnucNqXAO46dzK9oPb0wj076R5Xm8PfUVA9nAFB5ZNMmfJQJVKACAPd/Z2KYMppthw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-android-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm64/-/nice-android-arm64-1.1.1.tgz", + "integrity": "sha512-blG0i7dXgbInN5urONoUCNf+DUEAavRffrO7fZSeoRMJc5qD+BJeNcpr54msPF6qfDD6kzs9AQJogZvT2KD5nw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-darwin-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-arm64/-/nice-darwin-arm64-1.1.1.tgz", + "integrity": "sha512-s/E7w45NaLqTGuOjC2p96pct4jRfo61xb9bU1unM/MJ/RFkKlJyJDx7OJI/O0ll/hrfpqKopuAFDV8yo0hfT7A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-darwin-x64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-x64/-/nice-darwin-x64-1.1.1.tgz", + "integrity": "sha512-dGoEBnVpsdcC+oHHmW1LRK5eiyzLwdgNQq3BmZIav+9/5WTZwBYX7r5ZkQC07Nxd3KHOCkgbHSh4wPkH1N1LiQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-freebsd-x64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-freebsd-x64/-/nice-freebsd-x64-1.1.1.tgz", + "integrity": "sha512-kHv4kEHAylMYmlNwcQcDtXjklYp4FCf0b05E+0h6nDHsZ+F0bDe04U/tXNOqrx5CmIAth4vwfkjjUmp4c4JktQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm-gnueabihf": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm-gnueabihf/-/nice-linux-arm-gnueabihf-1.1.1.tgz", + "integrity": "sha512-E1t7K0efyKXZDoZg1LzCOLxgolxV58HCkaEkEvIYQx12ht2pa8hoBo+4OB3qh7e+QiBlp1SRf+voWUZFxyhyqg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-gnu/-/nice-linux-arm64-gnu-1.1.1.tgz", + "integrity": "sha512-CIKLA12DTIZlmTaaKhQP88R3Xao+gyJxNWEn04wZwC2wmRapNnxCUZkVwggInMJvtVElA+D4ZzOU5sX4jV+SmQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm64-musl": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-musl/-/nice-linux-arm64-musl-1.1.1.tgz", + "integrity": "sha512-+2Rzdb3nTIYZ0YJF43qf2twhqOCkiSrHx2Pg6DJaCPYhhaxbLcdlV8hCRMHghQ+EtZQWGNcS2xF4KxBhSGeutg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-ppc64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-ppc64-gnu/-/nice-linux-ppc64-gnu-1.1.1.tgz", + "integrity": "sha512-4FS8oc0GeHpwvv4tKciKkw3Y4jKsL7FRhaOeiPei0X9T4Jd619wHNe4xCLmN2EMgZoeGg+Q7GY7BsvwKpL22Tg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-riscv64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-riscv64-gnu/-/nice-linux-riscv64-gnu-1.1.1.tgz", + "integrity": "sha512-HU0nw9uD4FO/oGCCk409tCi5IzIZpH2agE6nN4fqpwVlCn5BOq0MS1dXGjXaG17JaAvrlpV5ZeyZwSon10XOXw==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-s390x-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-s390x-gnu/-/nice-linux-s390x-gnu-1.1.1.tgz", + "integrity": "sha512-2YqKJWWl24EwrX0DzCQgPLKQBxYDdBxOHot1KWEq7aY2uYeX+Uvtv4I8xFVVygJDgf6/92h9N3Y43WPx8+PAgQ==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-x64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-gnu/-/nice-linux-x64-gnu-1.1.1.tgz", + "integrity": "sha512-/gaNz3R92t+dcrfCw/96pDopcmec7oCcAQ3l/M+Zxr82KT4DljD37CpgrnXV+pJC263JkW572pdbP3hP+KjcIg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-x64-musl": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-musl/-/nice-linux-x64-musl-1.1.1.tgz", + "integrity": "sha512-xScCGnyj/oppsNPMnevsBe3pvNaoK7FGvMjT35riz9YdhB2WtTG47ZlbxtOLpjeO9SqqQ2J2igCmz6IJOD5JYw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-openharmony-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-openharmony-arm64/-/nice-openharmony-arm64-1.1.1.tgz", + "integrity": "sha512-6uJPRVwVCLDeoOaNyeiW0gp2kFIM4r7PL2MczdZQHkFi9gVlgm+Vn+V6nTWRcu856mJ2WjYJiumEajfSm7arPQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-arm64-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-arm64-msvc/-/nice-win32-arm64-msvc-1.1.1.tgz", + "integrity": "sha512-uoTb4eAvM5B2aj/z8j+Nv8OttPf2m+HVx3UjA5jcFxASvNhQriyCQF1OB1lHL43ZhW+VwZlgvjmP5qF3+59atA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-ia32-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-ia32-msvc/-/nice-win32-ia32-msvc-1.1.1.tgz", + "integrity": "sha512-CNQqlQT9MwuCsg1Vd/oKXiuH+TcsSPJmlAFc5frFyX/KkOh0UpBLEj7aoY656d5UKZQMQFP7vJNa1DNUNORvug==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-x64-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-x64-msvc/-/nice-win32-x64-msvc-1.1.1.tgz", + "integrity": "sha512-vB+4G/jBQCAh0jelMTY3+kgFy00Hlx2f2/1zjMoH821IbplbWZOkLiTYXQkygNTzQJTq5cvwBDgn2ppHD+bglQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nestjs/common": { + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-12.1.2.tgz", + "integrity": "sha512-e2tvLcEaG18sJy5IXrdSLv0ZBpR5djWxypCLkLuMAEFCvVPYL1lEIhvAMBkarlg+ZA8tbZN40J0tFurUl9NpdA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@standard-schema/spec": "1.1.0", + "file-type": "22.1.1", + "iterare": "1.2.1", + "load-esm": "1.0.3", + "tslib": "2.8.1", + "uid": "2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "class-transformer": ">=0.4.1", + "class-validator": ">=0.13.2", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, + "node_modules/@nestjs/core": { + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-12.1.2.tgz", + "integrity": "sha512-ieNRDv6P6dLsF/wMz9IUjwnyQbj7/Oluq1FXe3Xhje7TFSKPprLc83QniA2I6tjkr7YVDosdL2Dj7CVv9kU8og==", + "license": "MIT", + "peer": true, + "dependencies": { + "fast-safe-stringify": "2.1.1", + "iterare": "1.2.1", + "path-to-regexp": "8.4.2", + "tslib": "2.8.1", + "uid": "2.0.2" + }, + "engines": { + "node": ">= 20" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^12.0.0", + "@nestjs/microservices": "^12.0.0", + "@nestjs/platform-express": "^12.0.0", + "@nestjs/websockets": "^12.0.0", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/platform-express": { + "optional": true + }, + "@nestjs/websockets": { + "optional": true + } + } + }, "node_modules/@next/env": { "version": "16.3.8", "resolved": "https://registry.npmjs.org/@next/env/-/env-16.3.8.tgz", @@ -1233,6 +1836,114 @@ "node": ">= 10" } }, + "node_modules/@nuxt/kit": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/@nuxt/kit/-/kit-4.4.8.tgz", + "integrity": "sha512-ZUlZ5iYfyfJFDPluhn6ZxFWcsuxWbLnZBc8w3MAROcQ4lYfZ+qFpALBLSNlpc0zhOa++33EE+5PEbOAdVIY+dw==", + "license": "MIT", + "dependencies": { + "c12": "^3.3.4", + "consola": "^3.4.2", + "defu": "^6.1.7", + "destr": "^2.0.5", + "errx": "^0.1.0", + "exsolve": "^1.0.8", + "ignore": "^7.0.5", + "jiti": "^2.7.0", + "klona": "^2.0.6", + "mlly": "^1.8.2", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "pkg-types": "^2.3.1", + "rc9": "^3.0.1", + "scule": "^1.3.0", + "semver": "^7.8.1", + "tinyglobby": "^0.2.17", + "ufo": "^1.6.4", + "unctx": "^2.5.0", + "untyped": "^2.0.0" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/@oclif/core": { + "version": "4.11.4", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-4.11.4.tgz", + "integrity": "sha512-URwiQ5ALx/sJ2iH4vzXEd+H4K6NAI7LRs6Jag3hrgKEpGmaE6alfRC8qjO4GIgb6A3ACaJumqP9twi/M9ywdHQ==", + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.3.2", + "ansis": "^3.17.0", + "clean-stack": "^3.0.1", + "cli-spinners": "^2.9.2", + "debug": "^4.4.3", + "ejs": "^3.1.10", + "get-package-type": "^0.1.0", + "indent-string": "^4.0.0", + "is-wsl": "^2.2.0", + "lilconfig": "^3.1.3", + "minimatch": "^10.2.5", + "semver": "^7.8.1", + "string-width": "^4.2.3", + "supports-color": "^8", + "tinyglobby": "^0.2.16", + "widest-line": "^3.1.0", + "wordwrap": "^1.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@oclif/core/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@oclif/core/node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@oclif/core/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@oclif/plugin-help": { + "version": "6.2.37", + "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-6.2.37.tgz", + "integrity": "sha512-5N/X/FzlJaYfpaHwDC0YHzOzKDWa41s9t+4FpCDu4f9OMReds4JeNBaaWk9rlIzdKjh2M6AC5Q18ORfECRkHGA==", + "license": "MIT", + "dependencies": { + "@oclif/core": "^4" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@playwright/test": { "version": "1.56.1", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.56.1.tgz", @@ -1249,6 +1960,100 @@ "node": ">=18" } }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "license": "MIT", + "peer": true + }, + "node_modules/@sindresorhus/is": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.2.0.tgz", + "integrity": "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@smithy/core": { + "version": "3.35.2", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.35.2.tgz", + "integrity": "sha512-ejTukBuP/Pg3t9N8Ux+0zKsXeHWm7p6W3HbXFOyltKasSxZwCFmvTut7kBdZSCF3m9y1bXXyVu6p2xuMFcsTyQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.8.0.tgz", + "integrity": "sha512-ycSJu3tFAQ4v04CBB0agqFMVsSQ1iG3yw+SpgxRqKfaURpQD4CZ8Wn0zPMmSnOuTpTh65Vz+EA0rMrw089wvkA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.18.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.12.1.tgz", + "integrity": "sha512-ThMkboGeONWXAelq9FvGsuJC4rOi+qyC4/zhUF58xYpxUg5sQKx2VXZYJmtNjr4dSuBJ1HeJXETQILCz3wOHvw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.18.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.7.4.tgz", + "integrity": "sha512-tHy0K0VtqNd5Y7Y41h0a0Lhh0L1GzC08dTWg0F7vRJWFtTENg7IZikf3wQkanYIRdb7ngoIPMTmqgUi401fEeQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.35.0", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.19.0.tgz", + "integrity": "sha512-r7jh49VJxGerfAcTQA6gXcKc+98zOp/tqRwzYjgOE+iSQsP6cEU1hq2QzbuipmP68QtYdY9wKEhiCQZIzHgZ4Q==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT", + "peer": true + }, "node_modules/@supabase/auth-js": { "version": "2.117.2", "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.117.2.tgz", @@ -1353,6 +2158,254 @@ } } }, + "node_modules/@sveltejs/load-config": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@sveltejs/load-config/-/load-config-0.2.3.tgz", + "integrity": "sha512-VT3qmUb8pRV2QrZjd8iAmtg8lf4W0TIjZbvXtz5MKei/q96teWZgGJyyidJzOjzZzvdq616eSRVeMYIQChUTAQ==", + "license": "MIT", + "engines": { + "node": ">= 18.0.0" + } + }, + "node_modules/@swc/cli": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@swc/cli/-/cli-0.8.1.tgz", + "integrity": "sha512-L+ACCGHCiS0VqHVep/INLVnvRvJ2XooQFLZq4L8snhxw1jsqz+XRcY313UsyPVturPPE1shW3jic7rt3qEQTSQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@swc/counter": "^0.1.3", + "@xhmikosr/bin-wrapper": "^14.0.0", + "commander": "^8.3.0", + "minimatch": "^9.0.3", + "piscina": "^4.3.1", + "semver": "^7.3.8", + "slash": "3.0.0", + "source-map": "^0.7.3", + "tinyglobby": "^0.2.13" + }, + "bin": { + "spack": "bin/spack.js", + "swc": "bin/swc.js", + "swcx": "bin/swcx.js" + }, + "engines": { + "node": ">= 20.19.0" + }, + "peerDependencies": { + "@swc/core": "^1.2.66", + "chokidar": "^5.0.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@swc/core": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.3.tgz", + "integrity": "sha512-Qd8eBPkUFL4eAONgGjycZXj1jFCBW8Fd+xF0PzdTlBCWQIV1xnUT7B93wUANtW3KGjl3TRcOyxwSx/u/jyKw/Q==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.25" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.15.3", + "@swc/core-darwin-x64": "1.15.3", + "@swc/core-linux-arm-gnueabihf": "1.15.3", + "@swc/core-linux-arm64-gnu": "1.15.3", + "@swc/core-linux-arm64-musl": "1.15.3", + "@swc/core-linux-x64-gnu": "1.15.3", + "@swc/core-linux-x64-musl": "1.15.3", + "@swc/core-win32-arm64-msvc": "1.15.3", + "@swc/core-win32-ia32-msvc": "1.15.3", + "@swc/core-win32-x64-msvc": "1.15.3" + }, + "peerDependencies": { + "@swc/helpers": ">=0.5.17" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.3.tgz", + "integrity": "sha512-AXfeQn0CvcQ4cndlIshETx6jrAM45oeUrK8YeEY6oUZU/qzz0Id0CyvlEywxkWVC81Ajpd8TQQ1fW5yx6zQWkQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.3.tgz", + "integrity": "sha512-p68OeCz1ui+MZYG4wmfJGvcsAcFYb6Sl25H9TxWl+GkBgmNimIiRdnypK9nBGlqMZAcxngNPtnG3kEMNnvoJ2A==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.3.tgz", + "integrity": "sha512-Nuj5iF4JteFgwrai97mUX+xUOl+rQRHqTvnvHMATL/l9xE6/TJfPBpd3hk/PVpClMXG3Uvk1MxUFOEzM1JrMYg==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.3.tgz", + "integrity": "sha512-2Nc/s8jE6mW2EjXWxO/lyQuLKShcmTrym2LRf5Ayp3ICEMX6HwFqB1EzDhwoMa2DcUgmnZIalesq2lG3krrUNw==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.3.tgz", + "integrity": "sha512-j4SJniZ/qaZ5g8op+p1G9K1z22s/EYGg1UXIb3+Cg4nsxEpF5uSIGEE4mHUfA70L0BR9wKT2QF/zv3vkhfpX4g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.3.tgz", + "integrity": "sha512-aKttAZnz8YB1VJwPQZtyU8Uk0BfMP63iDMkvjhJzRZVgySmqt/apWSdnoIcZlUoGheBrcqbMC17GGUmur7OT5A==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.3.tgz", + "integrity": "sha512-oe8FctPu1gnUsdtGJRO2rvOUIkkIIaHqsO9xxN0bTR7dFTlPTGi2Fhk1tnvXeyAvCPxLIcwD8phzKg6wLv9yug==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.3.tgz", + "integrity": "sha512-L9AjzP2ZQ/Xh58e0lTRMLvEDrcJpR7GwZqAtIeNLcTK7JVE+QineSyHp0kLkO1rttCHyCy0U74kDTj0dRz6raA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.3.tgz", + "integrity": "sha512-B8UtogMzErUPDWUoKONSVBdsgKYd58rRyv2sHJWKOIMCHfZ22FVXICR4O/VwIYtlnZ7ahERcjayBHDlBZpR0aw==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.3.tgz", + "integrity": "sha512-SpZKMR9QBTecHeqpzJdYEfgw30Oo8b/Xl6rjSzBt1g0ZsXyy60KLXrp6IagQyfTYqNYE/caDvwtF2FPn7pomog==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "license": "Apache-2.0" + }, "node_modules/@swc/helpers": { "version": "0.5.23", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz", @@ -1362,6 +2415,15 @@ "tslib": "^2.8.0" } }, + "node_modules/@swc/types": { + "version": "0.1.28", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.28.tgz", + "integrity": "sha512-V6Mnml8v09QALx6K0elJ7o9K/MkVDtW3t6L+7Ou/JcWtb3xwId2AH4FeOceySd2JaO87IMw4+6vSZxLm34LPbw==", + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, "node_modules/@tailwindcss/node": { "version": "4.1.14", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.14.tgz", @@ -1698,6 +2760,31 @@ "tailwindcss": "4.1.14" } }, + "node_modules/@tokenizer/inflate": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", + "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "^4.4.3", + "token-types": "^6.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "license": "MIT", + "peer": true + }, "node_modules/@tweenjs/tween.js": { "version": "23.1.3", "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", @@ -1705,6 +2792,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "license": "MIT" + }, "node_modules/@types/geojson": { "version": "7946.0.16", "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", @@ -1712,6 +2805,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", + "license": "MIT", + "peer": true + }, "node_modules/@types/leaflet": { "version": "1.9.20", "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.20.tgz", @@ -1722,6 +2822,12 @@ "@types/geojson": "*" } }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "24.7.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.2.tgz", @@ -1836,6 +2942,203 @@ } } }, + "node_modules/@vercel/cli-auth": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@vercel/cli-auth/-/cli-auth-0.0.1.tgz", + "integrity": "sha512-CnqiuMlZ4pjs2LCPYiR6aLKPPd3Xb8SBI1Y7eotXKgpx6qgrGNY+E7EIyUt5ErGHJGIrCZyGG5WEo4bHtVmz2Q==", + "dependencies": { + "async-listen": "3.0.0", + "open": "8.4.0", + "xdg-app-paths": "5", + "zod": "4.1.11" + } + }, + "node_modules/@vercel/cli-auth/node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@vercel/cli-auth/node_modules/open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vercel/cli-auth/node_modules/zod": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.11.tgz", + "integrity": "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@vercel/cli-config": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@vercel/cli-config/-/cli-config-0.3.1.tgz", + "integrity": "sha512-sISbygqlFXYb8qfPu/+2Cf/A8IjrlAuZePdag3EDQr2PRA455s7UZtl9Ro674CWsB/saBRmz3jEkw0YsQs8OoQ==", + "license": "Apache-2.0", + "dependencies": { + "xdg-app-paths": "5", + "zod": "4.1.11" + } + }, + "node_modules/@vercel/cli-config/node_modules/zod": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.11.tgz", + "integrity": "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@vercel/cli-exec": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@vercel/cli-exec/-/cli-exec-1.0.1.tgz", + "integrity": "sha512-g9XerViJ/paZujufXYcu5XYI2vU2rtB4sgdpjUHde5RnOkdmpu0ngH46LCFGHoPXO/C+qDPSczIHIRN+8Q2YKQ==", + "license": "Apache-2.0", + "dependencies": { + "execa": "5.1.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@vercel/functions": { + "version": "3.9.11", + "resolved": "https://registry.npmjs.org/@vercel/functions/-/functions-3.9.11.tgz", + "integrity": "sha512-DbqKqcbUHGBTYgHZieSNDuF1/L2iSPZV0OGm0Xkm/t+s61lFk4VX8yGQ22Og3WYGxypW1l4Elx1NP8BL928u8Q==", + "license": "Apache-2.0", + "dependencies": { + "@vercel/oidc": "4.0.0" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@aws-sdk/credential-provider-web-identity": "*", + "ws": ">=8" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-provider-web-identity": { + "optional": true + }, + "ws": { + "optional": true + } + } + }, + "node_modules/@vercel/oidc": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@vercel/oidc/-/oidc-4.0.0.tgz", + "integrity": "sha512-ko6ATeRUlHZMB5hLFdC7+/Q1dI2f3NX3gZHxtpALdkxurwjD5HFHGWYF+4o+4SF0E47jg6KzcHknmRpHcKgJ5A==", + "license": "Apache-2.0", + "dependencies": { + "@vercel/cli-config": "0.3.1", + "@vercel/cli-exec": "1.0.1", + "jose": "^5.9.6" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@vercel/queue": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@vercel/queue/-/queue-0.8.0.tgz", + "integrity": "sha512-Rwaoq8DbSN2/qVPMdHzBdBXqpoD2h2s7+gOSaE3Gff9XI1NjNh281uMesP30k1kbsnjgeaibLTSiuYDlNPlw+g==", + "license": "MIT", + "dependencies": { + "@vercel/oidc": "^3.0.5", + "minimatch": "^10.2.4", + "mixpart": "0.0.6", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + } + } + }, + "node_modules/@vercel/queue/node_modules/@vercel/oidc": { + "version": "3.8.10", + "resolved": "https://registry.npmjs.org/@vercel/oidc/-/oidc-3.8.10.tgz", + "integrity": "sha512-sPw1kIfQEA6/2dB+zlAJHnhhNBRJ8t/t4HfFvKO+g0EPBA7NbCKyGovuhgAV6Mnl865ka7HqCEguIy596czdAw==", + "license": "Apache-2.0", + "dependencies": { + "@vercel/cli-config": "0.3.1", + "@vercel/cli-exec": "1.0.1", + "jose": "^5.9.6" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@vercel/queue/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@vercel/queue/node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@vercel/queue/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vercel/queue/node_modules/mixpart": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/mixpart/-/mixpart-0.0.6.tgz", + "integrity": "sha512-CRdXtgfQH2jARmtNmPR0Q7jL20fiESbaYk1b0KvLD0jCdUuemepREtsbd8nbiY6BHV9OGGddAZITNXklupUPUQ==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@webgpu/types": { "version": "0.1.74", "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.74.tgz", @@ -1843,6 +3146,2239 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/@workflow/astro": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@workflow/astro/-/astro-5.0.3.tgz", + "integrity": "sha512-A3YIzkwgiIH21kdfZhS6NKCwDHlsjs+Cfz9Gq5xd1SS5YcBcgMmuN3QZgqF6gzpH8IfG0hf04tBRLtjNgFHiqg==", + "license": "Apache-2.0", + "dependencies": { + "@swc/core": "1.15.3", + "@workflow/builders": "5.0.3", + "@workflow/rollup": "5.0.3", + "@workflow/swc-plugin": "5.0.1", + "@workflow/vite": "5.0.3", + "exsolve": "^1.0.8", + "pathe": "^2.0.3" + } + }, + "node_modules/@workflow/builders": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@workflow/builders/-/builders-5.0.3.tgz", + "integrity": "sha512-/Icf19hTBg1EaHFbMj8a4HrRaLVM2JdBj5cx6YvIgNwE0E5dFRlX67PGPZX+wVwtuP1b+NrlZmuX0tQ6fPl4QA==", + "license": "Apache-2.0", + "dependencies": { + "@swc/core": "1.15.3", + "@workflow/core": "5.2.0", + "@workflow/errors": "5.0.3", + "@workflow/swc-plugin": "5.0.1", + "@workflow/utils": "5.0.1", + "acorn": "8.16.0", + "builtin-modules": "5.0.0", + "chalk": "5.6.2", + "enhanced-resolve": "5.19.0", + "esbuild": "^0.28.1", + "find-up": "7.0.0", + "json5": "2.2.3", + "tinyglobby": "0.2.17" + } + }, + "node_modules/@workflow/builders/node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/builders/node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/builders/node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/builders/node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/builders/node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/builders/node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/builders/node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/builders/node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/builders/node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/builders/node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/builders/node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/builders/node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/builders/node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/builders/node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/builders/node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/builders/node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/builders/node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/builders/node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/builders/node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/builders/node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/builders/node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/builders/node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/builders/node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/builders/node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/builders/node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/builders/node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/builders/node_modules/enhanced-resolve": { + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", + "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@workflow/builders/node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/@workflow/cli": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@workflow/cli/-/cli-5.0.3.tgz", + "integrity": "sha512-nWF2FwxAcJbxAC/3rqeufopdfVZ4dWh5hidSUbZl6Q+bRZTMStaL0+TcVychveQVpmvvzv5ocD5nnkEH8sSExQ==", + "license": "Apache-2.0", + "dependencies": { + "@oclif/core": "4.11.4", + "@oclif/plugin-help": "6.2.37", + "@swc/core": "1.15.3", + "@vercel/cli-auth": "0.0.1", + "@workflow/builders": "5.0.3", + "@workflow/core": "5.2.0", + "@workflow/errors": "5.0.3", + "@workflow/swc-plugin": "5.0.1", + "@workflow/utils": "5.0.1", + "@workflow/web": "5.0.3", + "@workflow/world": "5.0.3", + "@workflow/world-local": "5.1.0", + "@workflow/world-vercel": "5.2.0", + "boxen": "8.0.1", + "builtin-modules": "5.0.0", + "chalk": "5.6.2", + "chokidar": "4.0.3", + "date-fns": "4.1.0", + "dotenv": "^17.3.1", + "easy-table": "1.2.0", + "enhanced-resolve": "5.19.0", + "esbuild": "^0.28.1", + "find-up": "7.0.0", + "mixpart": "0.0.4", + "open": "10.2.0", + "ora": "8.2.0", + "terminal-link": "5.0.0", + "tinyglobby": "0.2.17", + "xdg-app-paths": "5.1.0", + "zod": "~4.5.4" + }, + "bin": { + "wf": "bin/run.js", + "workflow": "bin/run.js" + } + }, + "node_modules/@workflow/cli/node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/cli/node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/cli/node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/cli/node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/cli/node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/cli/node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/cli/node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/cli/node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/cli/node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/cli/node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/cli/node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/cli/node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/cli/node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/cli/node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/cli/node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/cli/node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/cli/node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/cli/node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/cli/node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/cli/node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/cli/node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/cli/node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/cli/node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/cli/node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/cli/node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/cli/node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/cli/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@workflow/cli/node_modules/enhanced-resolve": { + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", + "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@workflow/cli/node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/@workflow/cli/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@workflow/cli/node_modules/zod": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.5.4.tgz", + "integrity": "sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@workflow/core": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@workflow/core/-/core-5.2.0.tgz", + "integrity": "sha512-vwXrT0TLxpycwKxZZSDROiKcHIJXRlLzTybEppM8fRl73bVvKiCVmHZ2tM70xe/smvsWiYNT7Uia8x8OaJSy2Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-web-identity": "3.972.49", + "@jridgewell/trace-mapping": "0.3.31", + "@standard-schema/spec": "1.0.0", + "@types/ms": "2.1.0", + "@vercel/functions": "^3.8.0", + "@workflow/errors": "5.0.3", + "@workflow/serde": "5.0.0", + "@workflow/utils": "5.0.1", + "@workflow/world": "5.0.3", + "@workflow/world-local": "5.1.0", + "@workflow/world-vercel": "5.2.0", + "acorn": "8.15.0", + "debug": "4.4.3", + "devalue": "5.9.2", + "ms": "2.1.3", + "nanoid": "5.1.6", + "quickjs-wasi": "3.4.0", + "seedrandom": "3.0.5", + "semver": "7.7.4", + "ulid": "~3.0.1", + "zod": "~4.5.4" + }, + "peerDependencies": { + "@opentelemetry/api": "1" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + } + } + }, + "node_modules/@workflow/core/node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "license": "MIT" + }, + "node_modules/@workflow/core/node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/@workflow/core/node_modules/nanoid": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz", + "integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "node_modules/@workflow/core/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@workflow/core/node_modules/zod": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.5.4.tgz", + "integrity": "sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@workflow/errors": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@workflow/errors/-/errors-5.0.3.tgz", + "integrity": "sha512-mzpphB/R1fiAlANz6+ZFOjtu9EHQyFp3zs5FLxEU+I6eNbdWsqMZbi37PI53bQ9QBOS6u8+aHajp6IVAcTqOSQ==", + "license": "Apache-2.0", + "dependencies": { + "@workflow/utils": "5.0.1", + "@workflow/world": "5.0.3", + "ms": "2.1.3" + } + }, + "node_modules/@workflow/nest": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@workflow/nest/-/nest-5.1.1.tgz", + "integrity": "sha512-KK06v1YTGUEqD7gRmf/cIOFwOAItVAoeSUVa5WWrFS5MM5S3kClgD7pUok8VTvBDGUr5yQ9NM38yI+LMmvbs2Q==", + "license": "Apache-2.0", + "dependencies": { + "@swc/core": "1.15.3", + "@workflow/builders": "5.0.3", + "@workflow/swc-plugin": "5.0.1", + "@workflow/utils": "5.0.1", + "esbuild": "^0.28.1", + "pathe": "2.0.3" + }, + "bin": { + "workflow-nest": "dist/cli.js" + }, + "peerDependencies": { + "@nestjs/common": ">=10.0.0", + "@nestjs/core": ">=10.0.0", + "@swc/cli": ">=0.4.0", + "@swc/core": ">=1.5.0" + }, + "peerDependenciesMeta": { + "@nestjs/common": { + "optional": false + }, + "@nestjs/core": { + "optional": false + }, + "@swc/cli": { + "optional": false + }, + "@swc/core": { + "optional": false + } + } + }, + "node_modules/@workflow/nest/node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/nest/node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/nest/node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/nest/node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/nest/node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/nest/node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/nest/node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/nest/node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/nest/node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/nest/node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/nest/node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/nest/node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/nest/node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/nest/node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/nest/node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/nest/node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/nest/node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/nest/node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/nest/node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/nest/node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/nest/node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/nest/node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/nest/node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/nest/node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/nest/node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/nest/node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@workflow/nest/node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/@workflow/next": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@workflow/next/-/next-5.0.3.tgz", + "integrity": "sha512-w/UFn5kY0mbplUSdU9JUO5+hGbMIZLE2Rb8ORP0siEQPLilC1mNCDN9tNmDuiXU8SG2bmepUzxSaOtobN327dQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/core": "1.15.3", + "@workflow/builders": "5.0.3", + "@workflow/core": "5.2.0", + "@workflow/swc-plugin": "5.0.1", + "ignore": "7.0.5", + "semver": "7.7.4", + "watchpack": "2.5.1" + }, + "peerDependencies": { + "next": ">13" + }, + "peerDependenciesMeta": { + "next": { + "optional": true + } + } + }, + "node_modules/@workflow/next/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@workflow/nitro": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@workflow/nitro/-/nitro-5.0.3.tgz", + "integrity": "sha512-Dp9SfHONfTw+X+infGk0WTTfBrgt1rhojTRcQFNwV7ryVC1+mhD6FMp1qomGqEVCXEQ9FUP9SvFN1bCMqNNFwQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/core": "1.15.3", + "@workflow/builders": "5.0.3", + "@workflow/core": "5.2.0", + "@workflow/rollup": "5.0.3", + "@workflow/swc-plugin": "5.0.1", + "@workflow/vite": "5.0.3", + "@workflow/web": "5.0.3", + "exsolve": "1.0.8", + "pathe": "2.0.3" + } + }, + "node_modules/@workflow/nitro/node_modules/exsolve": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", + "license": "MIT" + }, + "node_modules/@workflow/nuxt": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@workflow/nuxt/-/nuxt-5.0.3.tgz", + "integrity": "sha512-qhIwYZPVZ2DKOgePYLbB+R/Qg6mTYhthk/qqsCm9+QKlZruK+iqgtz0YNRq7K+kqMidDAJJJWa+KFSdqjzJssw==", + "license": "Apache-2.0", + "dependencies": { + "@nuxt/kit": "4.4.8", + "@workflow/nitro": "5.0.3" + } + }, + "node_modules/@workflow/rollup": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@workflow/rollup/-/rollup-5.0.3.tgz", + "integrity": "sha512-sgTCzt/8NC3KRxdQY6yrOzxjcDZrzo1nKSws+aOSCUKdsXTbFLssfG4vptod+sIIxnTjB3zgT09+lT9fQHz3/Q==", + "license": "Apache-2.0", + "dependencies": { + "@swc/core": "1.15.3", + "@workflow/builders": "5.0.3", + "@workflow/swc-plugin": "5.0.1", + "exsolve": "1.0.7" + } + }, + "node_modules/@workflow/rollup/node_modules/exsolve": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", + "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", + "license": "MIT" + }, + "node_modules/@workflow/serde": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@workflow/serde/-/serde-5.0.0.tgz", + "integrity": "sha512-78smVK/ejx/MIrjOwqAH14jGZkGBIO3u3B6u6WhWOyfjBJWl21xyHdsh0gFsncD96HlavCTbsWg/pN7MLSnrbw==", + "license": "Apache-2.0" + }, + "node_modules/@workflow/sveltekit": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@workflow/sveltekit/-/sveltekit-5.0.3.tgz", + "integrity": "sha512-iiziyl6dedXgILJYlGMfLHP/9VmCaroRN8KHiUvN+5W32QF3fuTkEu6621hP9LfP6S1aADRUNDXGdbZr4OsFZQ==", + "license": "Apache-2.0", + "dependencies": { + "@sveltejs/load-config": "^0.2.3", + "@swc/core": "1.15.3", + "@workflow/builders": "5.0.3", + "@workflow/rollup": "5.0.3", + "@workflow/swc-plugin": "5.0.1", + "@workflow/vite": "5.0.3", + "exsolve": "^1.0.8", + "fs-extra": "^11.3.2", + "pathe": "^2.0.3" + } + }, + "node_modules/@workflow/swc-plugin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@workflow/swc-plugin/-/swc-plugin-5.0.1.tgz", + "integrity": "sha512-uO6u9nWJKGrD3x9F9GhkwYwIIHJXrBO5xXiAePizLuWXoBSt7NlLKTmoOBgPQcN58j5m3pyWh+ZEeqK5x1g3Sg==", + "license": "Apache-2.0", + "peerDependencies": { + "@swc/core": "1.15.3" + } + }, + "node_modules/@workflow/typescript-plugin": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@workflow/typescript-plugin/-/typescript-plugin-5.0.0.tgz", + "integrity": "sha512-OklEiKRYbehA2q8wNG9CZE3iwBP93meLbAWbd8n8QTAByEc09iliJ5Tjsvw4s8r1GssrSLeokb+VKUbHiykiUA==", + "license": "Apache-2.0", + "peerDependencies": { + "typescript": ">=5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@workflow/utils": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@workflow/utils/-/utils-5.0.1.tgz", + "integrity": "sha512-s/32vojtP1qYDPINp9wfE5JZRkjdhXI8lyg4l+QNrKjMwiaXv6vNRpsuGQrcdj2Nfnnvz3L4DOxdNyPW3MG+Jg==", + "license": "Apache-2.0", + "dependencies": { + "ms": "2.1.3" + } + }, + "node_modules/@workflow/vite": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@workflow/vite/-/vite-5.0.3.tgz", + "integrity": "sha512-jJD7rXPNbG5JI5lu2Wy/IDnml7A9tq+U09bCN0GCqF/UOwrrwnsx8pWoNVU0IqPcZz0x9nJk0hEc+WgHYw+sHQ==", + "license": "Apache-2.0", + "dependencies": { + "@workflow/builders": "5.0.3" + } + }, + "node_modules/@workflow/web": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@workflow/web/-/web-5.0.3.tgz", + "integrity": "sha512-Cm4EaTBqcwniQq0MQbWue3ozAneyLUcV+VmxCtOp1Vh3C+V9NNYwSMjuYxPpZFFdENkK8uOQ6zRlt37nbHlgJA==", + "license": "Apache-2.0", + "dependencies": { + "@workflow/world-local": "5.1.0", + "srvx": "^0.12.4", + "swr": "^2.3.6" + } + }, + "node_modules/@workflow/world": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@workflow/world/-/world-5.0.3.tgz", + "integrity": "sha512-/V2rxExhLgTwrho3VOZSdz2VdB5b1TaaybZI4oNbFb1KIhwGlZIxEsW+eKA+UAmYxJdmnh28yxkGIVS5ry7n2w==", + "license": "Apache-2.0", + "dependencies": { + "ulid": "~3.0.1", + "zod": "~4.5.4" + } + }, + "node_modules/@workflow/world-local": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@workflow/world-local/-/world-local-5.1.0.tgz", + "integrity": "sha512-lNdroOV8eLLIu+gibNAtawqemjr5nxSkEKuixhm2EzDadMpoKjQscvQaFKmhkAtX1s1IRPXud1RGTQfCa2soKw==", + "license": "Apache-2.0", + "dependencies": { + "@vercel/queue": "0.8.0", + "@workflow/errors": "5.0.3", + "@workflow/utils": "5.0.1", + "@workflow/world": "5.0.3", + "async-sema": "3.1.1", + "proper-lockfile": "4.1.2", + "ulid": "~3.0.1", + "undici": "7.30.0", + "zod": "~4.5.4" + }, + "peerDependencies": { + "@opentelemetry/api": "1" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + } + } + }, + "node_modules/@workflow/world-local/node_modules/zod": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.5.4.tgz", + "integrity": "sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@workflow/world-vercel": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@workflow/world-vercel/-/world-vercel-5.2.0.tgz", + "integrity": "sha512-+Fc8pe1388eHD7dwm2xeG61dMYPiHVbF7wEH/QAIaaZPqpN2zIccWvNW1/2AEGkBkK3k5Cw57uGtrqy6+xPv0A==", + "license": "Apache-2.0", + "dependencies": { + "@vercel/functions": "^3.8.0", + "@vercel/oidc": "3.2.0", + "@vercel/queue": "0.8.0", + "@workflow/errors": "5.0.3", + "@workflow/utils": "5.0.1", + "@workflow/world": "5.0.3", + "cbor-x": "1.6.0", + "ulid": "~3.0.1", + "undici": "7.30.0", + "ws": "^8.20.0", + "zod": "~4.5.4" + }, + "peerDependencies": { + "@opentelemetry/api": "1" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + } + } + }, + "node_modules/@workflow/world-vercel/node_modules/@vercel/oidc": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@vercel/oidc/-/oidc-3.2.0.tgz", + "integrity": "sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==", + "license": "Apache-2.0", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@workflow/world-vercel/node_modules/zod": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.5.4.tgz", + "integrity": "sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@workflow/world/node_modules/zod": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.5.4.tgz", + "integrity": "sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@xhmikosr/archive-type": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@xhmikosr/archive-type/-/archive-type-8.1.0.tgz", + "integrity": "sha512-EXOjEbnZFE5c/nFMf4FOrEURVanzHpnkPYmnmr78u02/8hAhE0FMq8p9TK1IM0/bFr5VcyBUY0gfLm8f7dKy+Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "file-type": "^21.3.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@xhmikosr/archive-type/node_modules/file-type": { + "version": "21.3.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.4.tgz", + "integrity": "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.4", + "token-types": "^6.1.1", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/@xhmikosr/bin-check": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@xhmikosr/bin-check/-/bin-check-8.3.0.tgz", + "integrity": "sha512-ALFOzArQWaPhSb6zr5yQslFr/vyc/ZJENLzFeFzqeTWI2exm3V84zMieU2FEI29KBuotkX1zf5RTKXIc0UaChQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "isexe": "^4.0.0", + "tinyexec": "^1.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@xhmikosr/bin-wrapper": { + "version": "14.5.1", + "resolved": "https://registry.npmjs.org/@xhmikosr/bin-wrapper/-/bin-wrapper-14.5.1.tgz", + "integrity": "sha512-UZUuTYWxeAbTIiRKKEAmV3csoE36B3CGFZrYYn87+bSEBTyJ32p5gx5Gmj5HOgyOtioFUypbOZ1V5M/l/VoePw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@xhmikosr/bin-check": "^8.2.2", + "@xhmikosr/downloader": "^16.3.1", + "@xhmikosr/os-filter-obj": "^4.1.0", + "binary-version-check": "^6.1.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@xhmikosr/decompress": { + "version": "11.1.4", + "resolved": "https://registry.npmjs.org/@xhmikosr/decompress/-/decompress-11.1.4.tgz", + "integrity": "sha512-ZbYL7SAfY37/TMpopqBR3mQiuQ76kI/RNpN4q82YHSw/UxktZNy8P4wgiKPSrTImMAWRaUG8UK1pgEa56YdLaw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@xhmikosr/decompress-tar": "^9.0.2", + "@xhmikosr/decompress-tarbz2": "^9.0.2", + "@xhmikosr/decompress-targz": "^9.0.1", + "@xhmikosr/decompress-unzip": "^8.2.1", + "graceful-fs": "^4.2.11", + "strip-dirs": "^3.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@xhmikosr/decompress-tar": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/@xhmikosr/decompress-tar/-/decompress-tar-9.0.2.tgz", + "integrity": "sha512-8nPZ6lZ3ExhsSxi/X/PMB3K+Vtsuxk43HowxYpxw4AsCHTYqFBXwC8B3Y+M/meaUOGOVm+2tFNUAfWGRjBt+Ww==", + "license": "MIT", + "peer": true, + "dependencies": { + "file-type": "^21.3.4", + "is-stream": "^4.0.1", + "tar-stream": "3.1.7" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@xhmikosr/decompress-tar/node_modules/file-type": { + "version": "21.3.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.4.tgz", + "integrity": "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.4", + "token-types": "^6.1.1", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/@xhmikosr/decompress-tar/node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@xhmikosr/decompress-tarbz2": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/@xhmikosr/decompress-tarbz2/-/decompress-tarbz2-9.0.2.tgz", + "integrity": "sha512-m0DvZhE7remCxtS8xY2iHSjivT4v+iyYDdfNoeuu8Nm+7g8xEXdLKSyDEicu4u1ImJLLGEfjMuTLera/F6UGWw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@xhmikosr/decompress-tar": "^9.0.1", + "file-type": "^21.3.4", + "is-stream": "^4.0.1", + "seek-bzip": "^2.0.0", + "unbzip2-stream": "^1.4.3" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@xhmikosr/decompress-tarbz2/node_modules/file-type": { + "version": "21.3.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.4.tgz", + "integrity": "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.4", + "token-types": "^6.1.1", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/@xhmikosr/decompress-tarbz2/node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@xhmikosr/decompress-targz": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@xhmikosr/decompress-targz/-/decompress-targz-9.0.1.tgz", + "integrity": "sha512-1JXu2b6yrpm5EuBoOzMU57B4qrHXJKWQQ7LlMynNEiz85mEjDciO3ayf//GXaTLLCEKiHjWlU3q3THjgf7uODA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@xhmikosr/decompress-tar": "^9.0.0", + "file-type": "^21.3.0", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@xhmikosr/decompress-targz/node_modules/file-type": { + "version": "21.3.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.4.tgz", + "integrity": "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.4", + "token-types": "^6.1.1", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/@xhmikosr/decompress-targz/node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@xhmikosr/decompress-unzip": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@xhmikosr/decompress-unzip/-/decompress-unzip-8.2.1.tgz", + "integrity": "sha512-2MS94QnmXQwjkKN8WyFiu1sU7J3rcWJcMze4kRYsX7tN+CXpUGECgkh4YSOhujpkWPuVlFudIziJHO/TxOqkQQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "file-type": "^21.3.4", + "get-stream": "^9.0.1", + "yauzl": "^3.4.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@xhmikosr/decompress-unzip/node_modules/file-type": { + "version": "21.3.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.4.tgz", + "integrity": "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.4", + "token-types": "^6.1.1", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/@xhmikosr/downloader": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/@xhmikosr/downloader/-/downloader-16.3.1.tgz", + "integrity": "sha512-M67dvznaFbsvoqhGT4FsHWysaXXQ8386OViGZm0WOyQS3apW9p16WgvHp9nWj2vfKQAR2ZdqIBPNCHSM5rKSCg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@xhmikosr/archive-type": "^8.1.0", + "@xhmikosr/decompress": "^11.1.4", + "content-disposition": "^2.0.1", + "ext-name": "^5.0.0", + "file-type": "^21.3.4", + "filenamify": "^7.0.2", + "got": "^14.6.6" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@xhmikosr/downloader/node_modules/file-type": { + "version": "21.3.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.4.tgz", + "integrity": "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.4", + "token-types": "^6.1.1", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/@xhmikosr/os-filter-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@xhmikosr/os-filter-obj/-/os-filter-obj-4.1.0.tgz", + "integrity": "sha512-y5ArHvQ7BVule/+L9yE2nYMhceiJhgsqo58lOfnisQ7bg+Kjfmkgr7JBuVFiTkl+ErdShpp829QstZQyLugl8g==", + "license": "MIT", + "peer": true, + "dependencies": { + "system-architecture": "^1.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/agent-browser": { "version": "0.21.0", "resolved": "https://registry.npmjs.org/agent-browser/-/agent-browser-0.21.0.tgz", @@ -1854,6 +5390,141 @@ "agent-browser": "bin/agent-browser.js" } }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "license": "ISC", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansis": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz", + "integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==", + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/async-listen": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/async-listen/-/async-listen-3.0.0.tgz", + "integrity": "sha512-V+SsTpDqkrWTimiotsyl33ePSjA5/KrithwupuvJ6ztsqPvGv6ge4OredFhPffVXiLN/QUWvE0XcqJaYgt6fOg==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/async-sema": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/async-sema/-/async-sema-3.1.1.tgz", + "integrity": "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==", + "license": "MIT" + }, + "node_modules/b4a": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.9.0.tgz", + "integrity": "sha512-dpfcF9fDNR6++cthXR67iyhgqWy9CBouAvIWhIntzBG6cvK/cnIPiZQjBwi/ZqjjBEDGfoNDtmB0kTjroOJ3pQ==", + "license": "Apache-2.0", + "peer": true, + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/bare-events": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.2.tgz", + "integrity": "sha512-AIPKioV7/Y/8KfZ3AAhjPJxLLbY49S64Ym5DakZlUg75qQiTgUq9hEJoEwa4eUezPUlXRy/i5NpsKvo9jgKmoA==", + "license": "Apache-2.0", + "peer": true, + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "peer": true + }, "node_modules/baseline-browser-mapping": { "version": "2.11.27", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.27.tgz", @@ -1866,12 +5537,441 @@ "node": ">=6.0.0" } }, + "node_modules/binary-version": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/binary-version/-/binary-version-7.1.0.tgz", + "integrity": "sha512-Iy//vPc3ANPNlIWd242Npqc8MK0a/i4kVcHDlDA6HNMv5zMxz4ulIFhOSYJVKw/8AbHdHy0CnGYEt1QqSXxPsw==", + "license": "MIT", + "peer": true, + "dependencies": { + "execa": "^8.0.1", + "find-versions": "^6.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/binary-version-check": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/binary-version-check/-/binary-version-check-6.1.0.tgz", + "integrity": "sha512-REKdLKmuViV2WrtWXvNSiPX04KbIjfUV3Cy8batUeOg+FtmowavzJorfFhWq95cVJzINnL/44ixP26TrdJZACA==", + "license": "MIT", + "peer": true, + "dependencies": { + "binary-version": "^7.1.0", + "semver": "^7.6.0", + "semver-truncate": "^3.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/binary-version/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "license": "MIT", + "peer": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/binary-version/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/binary-version/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/binary-version/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/binary-version/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/binary-version/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/binary-version/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/binary-version/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/binary-version/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/binary-version/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bowser": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", + "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", + "license": "MIT" + }, + "node_modules/boxen": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz", + "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==", + "license": "MIT", + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^8.0.0", + "chalk": "^5.3.0", + "cli-boxes": "^3.0.0", + "string-width": "^7.2.0", + "type-fest": "^4.21.0", + "widest-line": "^5.0.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/boxen/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "license": "MIT" + }, + "node_modules/boxen/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/widest-line": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz", + "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==", + "license": "MIT", + "dependencies": { + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/brace-expansion": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", + "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "license": "MIT" }, + "node_modules/builtin-modules": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-5.0.0.tgz", + "integrity": "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==", + "license": "MIT", + "engines": { + "node": ">=18.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bundle-name": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.1.tgz", + "integrity": "sha512-DdH81/zPLVS11EUgWq3tEu/xn+EzljlMYooDNdzWEnFha3R3NBMpMV1UqYIpjYHV/SgpFKMIX1Oh7o06SjM/oA==", + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/byte-counter": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/byte-counter/-/byte-counter-0.1.0.tgz", + "integrity": "sha512-jheRLVMeUKrDBjVw2O5+k4EvR4t9wtxHL+bo/LxfkxsVeuGMy3a5SEGgXdAFA4FSzTrU8rQXQIrsZ3oBq5a0pQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/c12": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.3.4.tgz", + "integrity": "sha512-cM0ApFQSBXuourJejzwv/AuPRvAxordTyParRVcHjjtXirtkzM0uK2L9TTn9s0cXZbG7E55jCivRQzoxYmRAlA==", + "license": "MIT", + "dependencies": { + "chokidar": "^5.0.0", + "confbox": "^0.2.4", + "defu": "^6.1.6", + "dotenv": "^17.3.1", + "exsolve": "^1.0.8", + "giget": "^3.2.0", + "jiti": "^2.6.1", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^2.1.0", + "pkg-types": "^2.3.0", + "rc9": "^3.0.1" + }, + "peerDependencies": { + "magicast": "*" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, + "node_modules/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/cacheable-request": { + "version": "13.0.19", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-13.0.19.tgz", + "integrity": "sha512-SVXGH037+Mo1aIMO5B2UcleR43FGjFdN+M8JObSyEoQ2Mn4CODRWx28gN5jiTF0n5ItsgtIZfyargMNs8GX4kg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/http-cache-semantics": "^4.2.0", + "get-stream": "^9.0.1", + "http-cache-semantics": "^4.2.0", + "keyv": "^5.6.0", + "mimic-response": "^4.0.0", + "normalize-url": "^8.1.1", + "responselike": "^4.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/camelcase": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", + "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001814", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001814.tgz", @@ -1892,6 +5992,64 @@ ], "license": "CC-BY-4.0" }, + "node_modules/cbor-extract": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/cbor-extract/-/cbor-extract-2.2.2.tgz", + "integrity": "sha512-hlSxxI9XO2yQfe9g6msd3g4xCfDqK5T5P0fRMLuaLHhxn4ViPrm+a+MUfhrvH2W962RGxcBwEGzLQyjbDG1gng==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.1.1" + }, + "bin": { + "download-cbor-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@cbor-extract/cbor-extract-darwin-arm64": "2.2.2", + "@cbor-extract/cbor-extract-darwin-x64": "2.2.2", + "@cbor-extract/cbor-extract-linux-arm": "2.2.2", + "@cbor-extract/cbor-extract-linux-arm64": "2.2.2", + "@cbor-extract/cbor-extract-linux-x64": "2.2.2", + "@cbor-extract/cbor-extract-win32-x64": "2.2.2" + } + }, + "node_modules/cbor-x": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/cbor-x/-/cbor-x-1.6.0.tgz", + "integrity": "sha512-0kareyRwHSkL6ws5VXHEf8uY1liitysCVJjlmhaLG+IXLqhSaOO+t63coaso7yjwEzWZzLy8fJo06gZDVQM9Qg==", + "license": "MIT", + "optionalDependencies": { + "cbor-extract": "^2.2.0" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/chownr": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", @@ -1902,12 +6060,103 @@ "node": ">=18" } }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, + "node_modules/clean-stack": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.1.tgz", + "integrity": "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, "node_modules/combine-errors": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/combine-errors/-/combine-errors-3.0.3.tgz", @@ -1917,6 +6166,58 @@ "lodash.uniqby": "4.5.0" } }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/confbox": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/content-disposition": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-2.0.1.tgz", + "integrity": "sha512-e+H0ZXHSWYrENhQzw1LPuP4oF5MzVKmDU6d3hxlvaPEYLLg62MxtQNPRx4SYSuYJSBUgnQIG4HIN2tEtNv7Dog==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/convert-hrtime": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/convert-hrtime/-/convert-hrtime-5.0.0.tgz", + "integrity": "sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cookie": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", @@ -1930,6 +6231,20 @@ "url": "https://opencollective.com/express" } }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", @@ -1943,6 +6258,123 @@ "integrity": "sha512-p6JFxJc3M4OTD2li2qaHkDCw9SfMw82Ldr6OC9Je1aXiGfhx2W8p3GaoeaGrPJTUN9NirTM/KTxHWMUdR1rsUg==", "license": "ISC" }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-10.0.0.tgz", + "integrity": "sha512-oj7KWToJuuxlPr7VV0vabvxEIiqNMo+q0NueIiL3XhtwC6FVOX7Hr1c0C4eD0bmf7Zr+S/dSf2xvkH3Ad6sU3Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "mimic-response": "^4.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.1.tgz", + "integrity": "sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==", + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "license": "MIT", + "optional": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defu": { + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", + "license": "MIT" + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "license": "MIT" + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -1952,6 +6384,57 @@ "node": ">=8" } }, + "node_modules/devalue": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.9.2.tgz", + "integrity": "sha512-po4PAY5c53tw5XMocSnf8A/5OHhbbUftpr93aEN6BBoAdntUmK7vu7wOATqvt7cXO7m1Cl4gMVn6p7n6n4mj0w==", + "license": "MIT" + }, + "node_modules/dotenv": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", + "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/easy-table": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.2.0.tgz", + "integrity": "sha512-OFzVOv03YpvtcWGe5AayU5G2hgybsg3iqA6drU8UaoZyB9jLGMTrz9+asnLp/E+6qPh88yEI1gvyZFZ41dmgww==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "optionalDependencies": { + "wcwidth": "^1.0.1" + } + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, "node_modules/enhanced-resolve": { "version": "5.26.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.26.0.tgz", @@ -1966,6 +6449,24 @@ "node": ">=10.13.0" } }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/errx": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/errx/-/errx-0.1.2.tgz", + "integrity": "sha512-chfpPHmCerdo/rXr/nNvPZRkV4WwDRwzwnsJ0Uzz3tVi8Z41tDctRjduYy1138ii77AFlts1qvWtX3g/Acg91Q==", + "license": "MIT" + }, "node_modules/esbuild": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", @@ -2008,12 +6509,269 @@ "@esbuild/win32-x64": "0.25.12" } }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "bare-events": "^2.7.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/exsolve": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.1.3.tgz", + "integrity": "sha512-kuzL9zherLQoqDeQaS55AiiIg1kb4KYXaSusTtgZTPlvKP2gGCI82LNmBNh5RjDVUXIcmgx+/Mx7vPwRzBbhJA==", + "license": "MIT" + }, + "node_modules/ext-list": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", + "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==", + "license": "MIT", + "peer": true, + "dependencies": { + "mime-db": "^1.28.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ext-name": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz", + "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "ext-list": "^2.0.0", + "sort-keys-length": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "license": "MIT", + "peer": true + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "license": "MIT", + "peer": true + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/fflate": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz", "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==", "license": "MIT" }, + "node_modules/file-type": { + "version": "22.1.1", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-22.1.1.tgz", + "integrity": "sha512-sEB0oStmeLGtpmSAD88RDNuLsvhv4iC3u0lE25CUvfCULvj49xoQ3l3K525Vn+ZzsZ6DOLajQCV6RmhwwhqiJg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.5", + "token-types": "^6.1.2", + "uint8array-extras": "^1.5.0" + }, + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/filelist": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", + "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/filename-reserved-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-4.1.0.tgz", + "integrity": "sha512-pdgqcVlu789LoYmu0kz4pxSphL0pX/UVFFDJ/kTHk+1ZRc43UiokPZFsRV8OGxW49khh11rj54AhqczGIwLa+g==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/filenamify": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-7.0.3.tgz", + "integrity": "sha512-Pf0dwHrWs1GcIK15ps304fmxp2AOcfNR6vOIMvCPEy6ZztKYxzRQHlOuPVWo2HRjbCLHaRTY+kD8n3RbQ0A/fw==", + "license": "MIT", + "peer": true, + "dependencies": { + "filename-reserved-regex": "^4.0.1" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-up": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", + "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==", + "license": "MIT", + "dependencies": { + "locate-path": "^7.2.0", + "path-exists": "^5.0.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-versions": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-6.0.0.tgz", + "integrity": "sha512-2kCCtc+JvcZ86IGAz3Z2Y0A1baIz9fL31pH/0S1IqZr9Iwnjq8izfPtrCyQKO6TLMPELLsQMre7VDqeIKCsHkA==", + "license": "MIT", + "peer": true, + "dependencies": { + "semver-regex": "^4.0.5", + "super-regex": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/form-data-encoder": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-4.1.0.tgz", + "integrity": "sha512-G6NsmEW15s0Uw9XnCg+33H3ViYRyiM0hMrMhhqQOR8NFc5GhYrI+6I3u7OTw7b91J2g8rtvMBZJDbcGb2YUniw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 18" + } + }, + "node_modules/fs-extra": { + "version": "11.4.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.4.1.tgz", + "integrity": "sha512-KYAb4c9BJQI6QqGKthV68OHe0badztdXJWKo0WtBA9IuCFPTKvE5ZdUBglP833aMjhaSPNO4A5j/EkzZtGlKjA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2029,6 +6787,70 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-timeout": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/function-timeout/-/function-timeout-1.0.2.tgz", + "integrity": "sha512-939eZS4gJ3htTHAldmyyuzlrD58P03fHG49v2JfFXbV6OhvZKRC9j2yAtdHw/zrp2zXHuv05zMIy40F0ge7spA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz", + "integrity": "sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stream/node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-tsconfig": { "version": "4.14.3", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.3.tgz", @@ -2042,12 +6864,106 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/giget": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/giget/-/giget-3.3.1.tgz", + "integrity": "sha512-r+mvuDjrjMpsdw46Kmeydb8bdHm7wOKw8wNBtTndkjbPjgAp5oUJUxRE76wZFknxIPokfWvep2qSXK37aXE6zg==", + "license": "MIT", + "bin": { + "giget": "dist/cli.mjs" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "license": "BSD-2-Clause" + }, + "node_modules/got": { + "version": "14.6.6", + "resolved": "https://registry.npmjs.org/got/-/got-14.6.6.tgz", + "integrity": "sha512-QLV1qeYSo5l13mQzWgP/y0LbMr5Plr5fJilgAIwgnwseproEbtNym8xpLsDzeZ6MWXgNE6kdWGBjdh3zT/Qerg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@sindresorhus/is": "^7.0.1", + "byte-counter": "^0.1.0", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^13.0.12", + "decompress-response": "^10.0.0", + "form-data-encoder": "^4.0.2", + "http2-wrapper": "^2.2.1", + "keyv": "^5.5.3", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^4.0.1", + "responselike": "^4.0.2", + "type-fest": "^4.26.1" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/got/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "license": "(MIT OR CC0-1.0)", + "peer": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.3.0.tgz", + "integrity": "sha512-M5t5LlJpS1UHMjvwRQVdFHvPISGeLAxNcrWuJkeGh0KxsqCHZ1O3NXZU/8x7cD0BDcGW8kapxMKTvwlqrNkHkA==", + "license": "BSD-2-Clause", + "peer": true + }, + "node_modules/http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, "node_modules/iceberg-js": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz", @@ -2057,6 +6973,134 @@ "node": ">=20.0.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inspect-with-kind": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/inspect-with-kind/-/inspect-with-kind-1.0.5.tgz", + "integrity": "sha512-MAQUJuIo7Xqk8EVNP+6d3CKq9c80hi4tjIbIAT6lmGW9W6WzlHiu9PS8uSuUYU+Do+j1baiFp3H25XEVxDIG2g==", + "license": "ISC", + "peer": true, + "dependencies": { + "kind-of": "^6.0.2" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container/node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -2069,22 +7113,150 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "license": "BlueOak-1.0.0", + "peer": true, + "engines": { + "node": ">=20" + } + }, + "node_modules/iterare": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", + "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jiti": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", - "dev": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/jose": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.10.0.tgz", + "integrity": "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-base64": { "version": "3.9.4", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.9.4.tgz", "integrity": "sha512-PtOMXpEGuP0RRiRXsjzHzl44dMHxSu2CPvAhinupR1tBa88me+1DPqsobl7eupn5UukjLkln1ZnAOAOXOrYG0Q==", "license": "BSD-3-Clause" }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@keyv/serialize": "^1.1.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/knitwork": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/knitwork/-/knitwork-1.3.0.tgz", + "integrity": "sha512-4LqMNoONzR43B1W0ek0fhXMsDNW/zxa1NdFAVMY+k28pgZLovR4G3PB5MrpTxCy1QaZCqNoiaKPr5w5qZHfSNw==", + "license": "MIT" + }, "node_modules/leaflet": { "version": "1.9.4", "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", @@ -2329,6 +7501,53 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/load-esm": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/load-esm/-/load-esm-1.0.3.tgz", + "integrity": "sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + }, + { + "type": "buymeacoffee", + "url": "https://buymeacoffee.com/borewit" + } + ], + "license": "MIT", + "peer": true, + "engines": { + "node": ">=13.2.0" + } + }, + "node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "license": "MIT", + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lodash._baseiteratee": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash._baseiteratee/-/lodash._baseiteratee-4.7.0.tgz", @@ -2391,6 +7610,47 @@ "lodash._baseuniq": "~4.6.0" } }, + "node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lucide-react": { "version": "0.468.0", "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.468.0.tgz", @@ -2404,12 +7664,48 @@ "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/make-asynchronous": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/make-asynchronous/-/make-asynchronous-1.1.0.tgz", + "integrity": "sha512-ayF7iT+44LXdxJLTrTd3TLQpFDDvPCBxXxbv+pMUSuHA5Q8zyAfwkRP6aHHwNVFBUFWtxAHqwNJxF8vMZLAbVg==", + "license": "MIT", + "peer": true, + "dependencies": { + "p-event": "^6.0.0", + "type-fest": "^4.6.0", + "web-worker": "^1.5.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-asynchronous/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "license": "(MIT OR CC0-1.0)", + "peer": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" + }, "node_modules/meshoptimizer": { "version": "0.22.0", "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.22.0.tgz", @@ -2417,6 +7713,66 @@ "dev": true, "license": "MIT" }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/minipass": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", @@ -2440,6 +7796,50 @@ "node": ">= 18" } }, + "node_modules/mixpart": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/mixpart/-/mixpart-0.0.4.tgz", + "integrity": "sha512-RAoaOSXnMLrfUfmFbNynRYjeMru/bhgAYRy/GQVI8gmRq7vm9V9c2gGVYnYoQ008X6YTmRIu5b0397U7vb0bIA==", + "license": "MIT", + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "license": "MIT", + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, + "node_modules/mlly/node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "license": "MIT" + }, + "node_modules/mlly/node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/nanoid": { "version": "3.3.19", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", @@ -2539,6 +7939,257 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.1.1.tgz", + "integrity": "sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==", + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/normalize-url": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.1.1.tgz", + "integrity": "sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ohash": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.12.tgz", + "integrity": "sha512-65S/5gk9YSsaRjcyf7Nfa6h/d3E8/1gslpXfI4W7Dxn/oap8IKRuNT5VXkLQ1YFKIEg4apRY4Pj6aiwFzrDdmw==", + "license": "MIT" + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "license": "MIT" + }, + "node_modules/ora/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-paths": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/os-paths/-/os-paths-4.4.0.tgz", + "integrity": "sha512-wrAwOeXp1RRMFfQY8Sy7VaGVmPocaLwSFOYCGKSyo8qmJ+/yaafCl5BCA1IQZWqFSRBrKDYFeR9d/VyQzfH/jg==", + "license": "MIT", + "engines": { + "node": ">= 6.0" + } + }, + "node_modules/p-cancelable": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-4.0.1.tgz", + "integrity": "sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/p-event": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-6.0.1.tgz", + "integrity": "sha512-Q6Bekk5wpzW5qIyUP4gdMEujObYstZl6DMMOSenwBvV0BlE5LkDwkjs5yHbZmdCEq2o4RJx4tE1vwxFVf2FG1w==", + "license": "MIT", + "peer": true, + "dependencies": { + "p-timeout": "^6.1.2" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "license": "MIT", + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.4.tgz", + "integrity": "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "license": "MIT" + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "license": "MIT", + "peer": true + }, + "node_modules/perfect-debounce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz", + "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==", + "license": "MIT" + }, "node_modules/pg": { "version": "8.16.3", "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", @@ -2634,6 +8285,45 @@ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "license": "ISC" }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/piscina": { + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.9.4.tgz", + "integrity": "sha512-RyBDr2VheQ8ZfH3N8SzQZHztqVyOtadTwLnUYof6gdj5161/eu2wNhCbGl5GHnNKDpnkicJYpKtL2J+y/gk6XA==", + "license": "MIT", + "peer": true, + "optionalDependencies": { + "@napi-rs/nice": "^1.0.1" + } + }, + "node_modules/pkg-types": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.3.tgz", + "integrity": "sha512-j/lCFdcppV0JxWpCEITdbDltBxPP6cHT+yNJ6Go2OgoSA9518X847X9z0p6LtA4Nc16+eQzCZjRrWanTGvHJ5w==", + "license": "MIT", + "dependencies": { + "confbox": "^0.3.1", + "exsolve": "^1.1.1", + "pathe": "^2.0.3" + } + }, + "node_modules/pkg-types/node_modules/confbox": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.3.1.tgz", + "integrity": "sha512-cKUSoKa8YxFZZSmraVi7onONx3amu77ngK3kGpsYHDH7drPwCRkQE1RYMPlLRrMtnciRj274XNRxcHxnKmDSnA==", + "license": "MIT" + }, "node_modules/playwright": { "version": "1.56.1", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.56.1.tgz", @@ -2781,6 +8471,35 @@ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", "license": "MIT" }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/quickjs-wasi": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/quickjs-wasi/-/quickjs-wasi-3.4.0.tgz", + "integrity": "sha512-ttKaBD8u0RXhz2UqDU8wOEruhxADt0WWHfMfnNR8aO0UyhlifFS6NvPYFbyhASjqIp2/1dpneoP1IzBUxION1w==", + "license": "MIT" + }, + "node_modules/rc9": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-3.1.0.tgz", + "integrity": "sha512-ufjkNVzbRHKcCOmTahZkmVsyc3W+MSk3jY03m+a7tGHkIsdVMG9l10/3HvFbWkkKzY5VFp3pkRsIo/UYgmFL7Q==", + "license": "MIT", + "dependencies": { + "defu": "^6.1.7", + "destr": "^2.0.5" + } + }, "node_modules/react": { "version": "19.3.0", "resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz", @@ -2802,12 +8521,39 @@ "react": "^19.3.0" } }, + "node_modules/readdirp": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", + "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "license": "Apache-2.0", + "peer": true + }, "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "license": "MIT" }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "license": "MIT", + "peer": true + }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", @@ -2818,6 +8564,65 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/responselike": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-4.0.2.tgz", + "integrity": "sha512-cGk8IbWEAnaCpdAt1BHzJ3Ahz5ewDJa0KseTsE3qIRMJ3C698W8psM7byCeWVpd/Ha7FUYzuRVzXoKoM6nRUbA==", + "license": "MIT", + "peer": true, + "dependencies": { + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", @@ -2827,12 +8632,70 @@ "node": ">= 4" } }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/scheduler": { "version": "0.28.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz", "integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==", "license": "MIT" }, + "node_modules/scule": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/scule/-/scule-1.3.0.tgz", + "integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==", + "license": "MIT" + }, + "node_modules/seedrandom": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==", + "license": "MIT" + }, + "node_modules/seek-bzip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-2.0.0.tgz", + "integrity": "sha512-SMguiTnYrhpLdk3PwfzHeotrcwi8bNV4iemL9tx9poR/yeaMYwB9VzR1w7b57DuWpuqR8n6oZboi0hj3AxZxQg==", + "license": "MIT", + "peer": true, + "dependencies": { + "commander": "^6.0.0" + }, + "bin": { + "seek-bunzip": "bin/seek-bunzip", + "seek-table": "bin/seek-bzip-table" + } + }, + "node_modules/seek-bzip/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/semver": { "version": "7.8.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", @@ -2845,6 +8708,35 @@ "node": ">=10" } }, + "node_modules/semver-regex": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz", + "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semver-truncate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/semver-truncate/-/semver-truncate-3.0.0.tgz", + "integrity": "sha512-LJWA9kSvMolR51oDE6PN3kALBNaUdkxzAGcexw8gjMA8xr5zUqK0JiR3CgARSqanYF3Z1YHvsErb1KDgh+v7Rg==", + "license": "MIT", + "peer": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/server-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz", @@ -2900,12 +8792,79 @@ } } }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC" }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-plain-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sort-keys-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz", + "integrity": "sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==", + "license": "MIT", + "peer": true, + "dependencies": { + "sort-keys": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">= 12" + } + }, "node_modules/source-map-js": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", @@ -2924,6 +8883,132 @@ "node": ">= 10.x" } }, + "node_modules/srvx": { + "version": "0.12.8", + "resolved": "https://registry.npmjs.org/srvx/-/srvx-0.12.8.tgz", + "integrity": "sha512-7k9HoDhluTlD7UJ+F+OopZoPZXB2J8f/5nW2VMLaB8KsSlZlRQxyyoX0hWEQCxB+F+3ZvB5QX3LqB6xSBRfxQQ==", + "license": "MIT", + "bin": { + "srvx": "bin/srvx.mjs" + }, + "engines": { + "node": ">=20.16.0" + } + }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/streamx": { + "version": "2.28.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.1.tgz", + "integrity": "sha512-zEzXb0s5Cds7tqMH6rhZ05lcJydCWiQPEwiNngVqzsxCc962vLY4Uw+mW7od8kDH258k2Uz/JrOkdIAAhSh9VA==", + "license": "MIT", + "peer": true, + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/strip-dirs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-3.0.0.tgz", + "integrity": "sha512-I0sdgcFTfKQlUPZyAqPJmSG3HLO9rWDFnxonnIbskYNM3DwFOeTNB5KzVq3dA1GdRAc/25b5Y7UO2TQfKWw4aQ==", + "license": "ISC", + "peer": true, + "dependencies": { + "inspect-with-kind": "^1.0.5", + "is-plain-obj": "^1.1.0" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strtok3": { + "version": "10.3.5", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz", + "integrity": "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@tokenizer/token": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/styled-jsx": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", @@ -2947,6 +9032,105 @@ } } }, + "node_modules/super-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/super-regex/-/super-regex-1.1.0.tgz", + "integrity": "sha512-WHkws2ZflZe41zj6AolvvmaTrWds/VuyeYr9iPVv/oQeaIoVxMKaushfFWpOGDT+GuBrM/sVqF8KUCYQlSSTdQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "function-timeout": "^1.0.1", + "make-asynchronous": "^1.0.1", + "time-span": "^5.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-hyperlinks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-4.6.0.tgz", + "integrity": "sha512-FL/q/YePS4tFFTNiBOR+zPk2ZrKheqNtmTJ/9OUZ4e1wKHJofDbGQSmSsxS0XNnabSkqexryCaXGvEKz1vf9UQ==", + "license": "MIT", + "dependencies": { + "has-flag": "^5.0.1", + "supports-color": "^10.2.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, + "node_modules/supports-hyperlinks/node_modules/has-flag": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-5.0.1.tgz", + "integrity": "sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-hyperlinks/node_modules/supports-color": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/swr": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.5.1.tgz", + "integrity": "sha512-BRw55e8r0B7SpDN20CAzoQAHl7y1yP7/Zt7oqUjMv0vSt2u2Xnkm88Ws+VypbV9BXHQVuSuyVq7zMjO16wSExw==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/system-architecture": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/system-architecture/-/system-architecture-1.0.0.tgz", + "integrity": "sha512-0OJWD12D7XX3KUg1DYkMaTTjSTo2k/mhIYI3HlBlceXSMcJhW/1qO735fPKS5prcyjvn57Ub151vvASYXpQrEw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/tailwindcss": { "version": "4.1.14", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.14.tgz", @@ -2958,7 +9142,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -2985,12 +9168,133 @@ "node": ">=18" } }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/terminal-link": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-5.0.0.tgz", + "integrity": "sha512-qFAy10MTMwjzjU8U16YS4YoZD+NQLHzLssFMNqgravjbvIPNiqkGFR4yjhJfmY9R5OFU7+yHxc6y+uGHkKwLRA==", + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "supports-hyperlinks": "^4.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terminal-link/node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/text-decoder": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "b4a": "^1.6.4" + } + }, "node_modules/three": { "version": "0.186.1", "resolved": "https://registry.npmjs.org/three/-/three-0.186.1.tgz", "integrity": "sha512-blFeqb49wRCSGUGj7gtpfnSGHy2lwDk94RhUmS1c/hTby70kvChbWpkJ4Pm1390LqzzvTmzgXKHPEafJwCb8jA==", "license": "MIT" }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "license": "MIT", + "peer": true + }, + "node_modules/time-span": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/time-span/-/time-span-5.1.0.tgz", + "integrity": "sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==", + "license": "MIT", + "peer": true, + "dependencies": { + "convert-hrtime": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tinyexec": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/token-types": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", + "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", + "license": "MIT", + "peer": true, + "dependencies": { + "@borewit/text-codec": "^0.2.1", + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -3035,11 +9339,23 @@ "node": ">=18" } }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -3049,6 +9365,79 @@ "node": ">=14.17" } }, + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "license": "MIT" + }, + "node_modules/uid": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", + "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@lukeed/csprng": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/uint8array-extras": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.6.0.tgz", + "integrity": "sha512-8iAasVS4wUx0gPLjH8Xtz2PeDzTSiy8QiLGu2DT3X5GU+egFEQhpnbtkehbAwjvDcxIERmCYcysiODFmE3Ud0Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ulid": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/ulid/-/ulid-3.0.2.tgz", + "integrity": "sha512-yu26mwteFYzBAot7KVMqFGCVpsF6g8wXfJzQUHvu1no3+rRRSFcSV2nKeYvNPLD2J4b08jYBDhHUjeH0ygIl9w==", + "license": "MIT", + "bin": { + "ulid": "dist/cli.js" + } + }, + "node_modules/unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "license": "MIT", + "peer": true, + "dependencies": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, + "node_modules/unctx": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/unctx/-/unctx-2.5.0.tgz", + "integrity": "sha512-p+Rz9x0R7X+CYDkT+Xg8/GhpcShTlU8n+cf9OtOEf7zEQsNcCZO1dPKNRDqvUTaq+P32PMMkxWHwfrxkqfqAYg==", + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21", + "unplugin": "^2.3.11" + } + }, + "node_modules/undici": { + "version": "7.30.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.30.0.tgz", + "integrity": "sha512-dkrQXeHSaoamnItlYbmzG0wFYrM0ZwDxCIg0A7aKjTyyhh9svRzCNFEzV+Vm05/yehjCzjDZ31KXfGEjYSztDQ==", + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/undici-types": { "version": "7.14.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", @@ -3056,6 +9445,58 @@ "dev": true, "license": "MIT" }, + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unplugin": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.11.tgz", + "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "acorn": "^8.15.0", + "picomatch": "^4.0.3", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/untyped": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/untyped/-/untyped-2.0.0.tgz", + "integrity": "sha512-nwNCjxJTjNuLCgFr42fEak5OcLuB3ecca+9ksPFNvtfYSLpjf+iJqSIaSnIile6ZPbKYxI5k2AfXqeopGudK/g==", + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "defu": "^6.1.4", + "jiti": "^2.4.2", + "knitwork": "^1.2.0", + "scule": "^1.3.0" + }, + "bin": { + "untyped": "dist/cli.mjs" + } + }, "node_modules/url-parse": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", @@ -3066,6 +9507,227 @@ "requires-port": "^1.0.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.7.0.tgz", + "integrity": "sha512-6L+EeigHMQhdaIPNIFUKwfWJSwWFQ8gJbJ2DLOs5sDIegTwR9fRxvnM3uciHKjIZhFz+KAv2emhWMRvDmMcY8A==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/watchpack": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", + "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "license": "MIT", + "optional": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/web-worker": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.5.0.tgz", + "integrity": "sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==", + "license": "Apache-2.0", + "peer": true + }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "license": "MIT", + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "license": "MIT" + }, + "node_modules/workflow": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/workflow/-/workflow-5.2.0.tgz", + "integrity": "sha512-9Oqrk/btZ2G5PBiDIk1O9KFVowSgsJ/TrpJlH0ZI4v7Jha/Ckgk9ux8lW2wuOl4OsidnMak8w2gBrpdcMgBeeQ==", + "license": "Apache-2.0", + "dependencies": { + "@workflow/astro": "5.0.3", + "@workflow/cli": "5.0.3", + "@workflow/core": "5.2.0", + "@workflow/errors": "5.0.3", + "@workflow/nest": "5.1.1", + "@workflow/next": "5.0.3", + "@workflow/nitro": "5.0.3", + "@workflow/nuxt": "5.0.3", + "@workflow/rollup": "5.0.3", + "@workflow/sveltekit": "5.0.3", + "@workflow/typescript-plugin": "5.0.0", + "@workflow/utils": "5.0.1", + "ms": "2.1.3" + }, + "bin": { + "wf": "bin/run.js", + "workflow": "bin/run.js" + }, + "peerDependencies": { + "@opentelemetry/api": "1" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + } + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ws": { + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.22.0.tgz", + "integrity": "sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wsl-utils/node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xdg-app-paths": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/xdg-app-paths/-/xdg-app-paths-5.1.0.tgz", + "integrity": "sha512-RAQ3WkPf4KTU1A8RtFx3gWywzVKe00tfOPFfl2NDGqbIFENQO4kqAJp7mhQjNj/33W5x5hiWWUdyfPq/5SU3QA==", + "license": "MIT", + "dependencies": { + "xdg-portable": "^7.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/xdg-portable": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/xdg-portable/-/xdg-portable-7.3.0.tgz", + "integrity": "sha512-sqMMuL1rc0FmMBOzCpd0yuy9trqF2yTTVe+E9ogwCSWQCdDEtQUwrZPT6AxqtsFGRNxycgncbP/xmOOSPw5ZUw==", + "license": "MIT", + "dependencies": { + "os-paths": "^4.0.1" + }, + "engines": { + "node": ">= 6.0" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -3085,6 +9747,31 @@ "node": ">=18" } }, + "node_modules/yauzl": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.4.0.tgz", + "integrity": "sha512-jIH9yLR9wqr0wOS0TpBvo/g/2UgZH5qePVbjgRliiF0BYvOZyaBknKsF+x9Iht0O6sqgnB93rCICdOZFecJuDw==", + "license": "MIT", + "peer": true, + "dependencies": { + "pend": "~1.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zod": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", diff --git a/package.json b/package.json index 824f6aa..b7ff643 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "sharp": "0.35.5", "three": "0.186.1", "tus-js-client": "4.3.1", + "workflow": "5.2.0", "zod": "4.1.12" }, "devDependencies": { diff --git a/proxy.ts b/proxy.ts index 105da5f..b7415e7 100644 --- a/proxy.ts +++ b/proxy.ts @@ -33,6 +33,6 @@ export async function proxy(request: NextRequest) { } export const config = { matcher: [ - "/((?!_next/static|_next/image|favicon.ico|tools/|fonts/|images/|.*\\.(?:svg|png|jpg|jpeg|gif|webp|ico|ttf|woff|woff2|jar)$).*)", + "/((?!_next/static|_next/image|favicon.ico|\\.well-known/workflow/|tools/|fonts/|images/|.*\\.(?:svg|png|jpg|jpeg|gif|webp|ico|ttf|woff|woff2|jar)$).*)", ], }; diff --git a/scripts/test-admin-permissions-production.mjs b/scripts/test-admin-permissions-production.mjs new file mode 100644 index 0000000..00b99db --- /dev/null +++ b/scripts/test-admin-permissions-production.mjs @@ -0,0 +1,247 @@ +import assert from "node:assert/strict"; +import { randomUUID, randomBytes } from "node:crypto"; +import { createClient } from "@supabase/supabase-js"; +process.loadEnvFile(".env.local"); +const origin = process.argv[2] || "https://minegeo.vercel.app"; +const db = createClient( + process.env.NEXT_PUBLIC_SUPABASE_URL, + process.env.SUPABASE_SECRET_KEY, + { auth: { persistSession: false, autoRefreshToken: false } }, +); +const accounts = [], + maps = []; +const checked = (r) => { + if (r.error) throw new Error(r.error.message); + return r.data; +}; +async function request( + account, + path, + body, + method = body === undefined ? "GET" : "POST", + status = 200, +) { + const response = await fetch(`${origin}/api/v1/${path}`, { + method, + headers: { + origin, + "content-type": "application/json", + ...(account?.cookie ? { cookie: account.cookie } : {}), + }, + ...(body === undefined ? {} : { body: JSON.stringify(body) }), + signal: AbortSignal.timeout(60000), + }); + const data = await response.json(); + assert.equal(response.status, status, `${path}: ${JSON.stringify(data)}`); + return data; +} +async function account(role) { + const email = `admin-permissions-${randomUUID()}@example.com`, + password = randomBytes(24).toString("base64url"); + const { user } = checked( + await db.auth.admin.createUser({ + email, + password, + email_confirm: true, + user_metadata: { display_name: `Permissions ${role} fixture` }, + }), + ); + const fixture = { id: user.id, cookie: "" }; + accounts.push(fixture); + checked( + await db.from("profiles").insert({ + id: user.id, + role, + display_name: `Permissions ${role} fixture`, + }), + ); + const response = await fetch(`${origin}/api/v1/auth`, { + method: "POST", + headers: { origin, "content-type": "application/json" }, + body: JSON.stringify({ + action: "login", + email, + password, + next: "/moderation", + }), + }); + assert.equal(response.status, 200, "Fixture login failed"); + fixture.cookie = response.headers + .getSetCookie() + .map((value) => value.split(";")[0]) + .join("; "); + assert.ok(fixture.cookie, "Login did not set a session cookie"); + return fixture; +} +try { + const player = await account("contributor"), + moderator = await account("moderator"), + admin = await account("admin"), + missing = randomUUID(); + const bounds = { minX: 0, minZ: 0, maxX: 64, maxZ: 64 }; + for (const state of ["review", "unpublished"]) { + const map = checked( + await db + .from("datasets") + .insert({ + owner_id: player.id, + name: `Permissions ${state} fixture`, + source: "plugin", + bounds, + state, + panorama_count: 5, + }) + .select("id") + .single(), + ); + maps.push(map.id); + checked( + await db.from("dataset_inputs").insert({ + dataset_id: map.id, + validated: true, + manifest: { + format: "minegeo-dataset/v1", + minecraftVersion: "1.21.11", + dimension: "minecraft:overworld", + source: "plugin", + name: "Permissions map fixture", + description: "Disposable permission verification", + serverName: "Fixture server", + north: "-z", + bounds, + viewpoints: Array.from({ length: 5 }, (_, i) => ({ + id: `p${i}`, + x: i * 4, + y: 64, + z: i * 4, + })), + assets: [ + { path: "terrain.json.gz", sha256: "a".repeat(64), bytes: 1 }, + ], + }, + }), + ); + } + await request(player, "moderation", undefined, "GET", 403); + const queue = await request(moderator, "moderation"); + assert.equal(queue.role, "moderator"); + assert.deepEqual(queue.reports, []); + assert.deepEqual(queue.appeals, []); + assert.ok( + queue.maps.every( + (m) => + m.state === "review" || + (m.state === "published" && m.update_state === "review"), + ), + ); + assert.ok(queue.maps.some((m) => m.id === maps[0])); + assert.ok(!queue.maps.some((m) => m.id === maps[1])); + assert.ok( + (await request(moderator, `moderation/${maps[0]}/panoramas`)).locations + .length === 5, + ); + await request( + moderator, + `moderation/${maps[1]}/panoramas`, + undefined, + "GET", + 403, + ); + for (const actor of [player, moderator]) { + for (const [path, body, method] of [ + ["moderation/users", undefined, "GET"], + [ + `moderation/users/${player.id}`, + { prefix: "Spoof", icon: "star" }, + "PATCH", + ], + ["moderation/settings", { betaBadgeEnabled: false }, "PATCH"], + [ + `moderation/appeals/${missing}`, + { decision: "approve", reason: "A valid reason here." }, + "POST", + ], + [`moderation/reports/${missing}/panoramas`, undefined, "GET"], + [ + `reports/${missing}`, + { decision: "confirm", reason: "A valid reason here." }, + "PATCH", + ], + ["moderation", { datasetId: maps[0], decision: "unpublish" }, "POST"], + ]) + await request(actor, path, body, method, 403); + } + await request(moderator, "moderation", { + datasetId: maps[0], + decision: "reject", + reason: "Returned by the permission verification fixture.", + }); + assert.equal( + checked( + await db.from("datasets").select("state").eq("id", maps[0]).single(), + ).state, + "rejected", + ); + const full = await request(admin, "moderation"); + assert.equal(full.role, "admin"); + assert.ok(full.maps.some((m) => m.id === maps[1])); + assert.equal( + (await request(admin, `moderation/users?q=${player.id}`)).users[0].id, + player.id, + ); + await request( + admin, + `moderation/users/${player.id}`, + { prefix: "Builder", icon: "gem" }, + "PATCH", + ); + assert.equal( + (await request(player, "profile")).profile.custom_prefix, + "Builder", + ); + await request( + admin, + `moderation/users/${player.id}`, + { prefix: null, icon: "star" }, + "PATCH", + ); + await request( + admin, + "moderation/settings", + { betaBadgeEnabled: "invalid" }, + "PATCH", + 400, + ); + await request( + admin, + `reports/${missing}`, + { decision: "dismiss", reason: "A valid reason here." }, + "PATCH", + 400, + ); + await request( + admin, + `moderation/appeals/${missing}`, + { decision: "approve", reason: "A valid reason here." }, + "POST", + 400, + ); + for (const actor of [player, moderator, admin]) + await request(actor, "profile", { role: "admin" }, "PATCH", 400); + assert.equal((await request(admin, "profile")).profile.role, "admin"); + console.log( + "Production permissions passed: signed-in players denied, moderator queue restricted, pending panoramas and feedback work, admin controls available, role spoofing denied.", + ); +} finally { + if (maps.length) + checked(await db.from("review_decisions").delete().in("dataset_id", maps)); + for (const id of maps) + checked(await db.from("datasets").delete().eq("id", id)); + for (const fixture of accounts) { + if (fixture.cookie) + await request(fixture, "auth/logout", {}).catch(() => {}); + checked(await db.from("profiles").delete().eq("id", fixture.id)); + checked(await db.auth.admin.deleteUser(fixture.id)); + } + console.log("Disposable permission fixtures removed."); +} diff --git a/scripts/test-admin-permissions-sql.mjs b/scripts/test-admin-permissions-sql.mjs new file mode 100644 index 0000000..bdcb955 --- /dev/null +++ b/scripts/test-admin-permissions-sql.mjs @@ -0,0 +1,190 @@ +import assert from "node:assert/strict"; +import { randomUUID } from "node:crypto"; +import { readFile, readdir } from "node:fs/promises"; +import { PGlite } from "../.data/sql-verification/node_modules/@electric-sql/pglite/dist/index.js"; +const db = new PGlite(); +try { + await db.exec(`create role anon; create role authenticated; create role service_role; + create schema auth; create table auth.users(id uuid primary key); + create function auth.uid() returns uuid language sql as $$ select null::uuid $$; + create schema storage; create table storage.buckets(id text primary key,name text,public boolean,file_size_limit bigint);`); + for (const file of (await readdir("supabase/migrations")) + .filter((f) => f.endsWith(".sql")) + .sort()) + await db.exec(await readFile(`supabase/migrations/${file}`, "utf8")); + const player = randomUUID(), + moderator = randomUUID(), + admin = randomUUID(), + missing = randomUUID(); + for (const [id, role] of [ + [player, "contributor"], + [moderator, "moderator"], + [admin, "admin"], + ]) { + await db.query("insert into auth.users values($1)", [id]); + await db.query("insert into public.profiles(id,role) values($1,$2)", [ + id, + role, + ]); + } + const makeMap = async () => { + const { rows } = await db.query( + `insert into public.datasets(owner_id,name,source,bounds,state,panorama_count) + values($1,'Permissions fixture','plugin','{"minX":0,"minZ":0,"maxX":64,"maxZ":64}','review',5) returning id`, + [player], + ); + const id = rows[0].id; + await db.query( + "insert into public.dataset_inputs(dataset_id,manifest,validated) values($1,$2,true)", + [ + id, + JSON.stringify({ + name: "Permissions fixture", + description: "Role verification", + viewpoints: Array.from({ length: 5 }, (_, i) => ({ + id: `p${i}`, + assetId: `p${i}`, + })), + }), + ], + ); + return id; + }; + const id = await makeMap(); + const decide = (map, reviewer, decision, reason = null) => + db.query("select public.publish_dataset($1,$2,$3,$4)", [ + map, + reviewer, + decision, + reason, + ]); + await assert.rejects( + decide(id, player, "approve"), + /Moderator permission required/, + ); + await decide(id, moderator, "approve"); + assert.equal( + (await db.query("select state from public.datasets where id=$1", [id])) + .rows[0].state, + "published", + ); + await assert.rejects( + decide(id, moderator, "unpublish"), + /Admin permission required/, + ); + await decide(id, admin, "unpublish"); + assert.equal( + (await db.query("select state from public.datasets where id=$1", [id])) + .rows[0].state, + "unpublished", + ); + const rejected = await makeMap(); + await decide( + rejected, + moderator, + "reject", + "Please fix the missing terrain.", + ); + assert.equal( + ( + await db.query("select state from public.datasets where id=$1", [ + rejected, + ]) + ).rows[0].state, + "rejected", + ); + const updated = await makeMap(); + await decide(updated, admin, "approve"); + await db.query( + "update public.datasets set update_state='review' where id=$1", + [updated], + ); + await decide(updated, moderator, "approve"); + assert.equal( + ( + await db.query("select update_state from public.datasets where id=$1", [ + updated, + ]) + ).rows[0].update_state, + null, + ); + for (const actor of [player, moderator]) { + await assert.rejects( + db.query("select public.set_beta_badge($1,false)", [actor]), + /Admin permission required/, + ); + await assert.rejects( + db.query("select public.set_user_prefix($1,$2,'Spoof','crown')", [ + actor, + player, + ]), + /Admin permission required/, + ); + await assert.rejects( + db.query( + "select public.review_content_report($1,$2,'dismiss','Valid feedback here')", + [missing, actor], + ), + /Admin permission required/, + ); + await assert.rejects( + db.query( + "select public.review_map_appeal($1,$2,'approve','Valid feedback here')", + [missing, actor], + ), + /Admin permission required/, + ); + } + await db.query("select public.set_beta_badge($1,false)", [admin]); + assert.equal( + (await db.query("select beta_badge_enabled from public.site_settings")) + .rows[0].beta_badge_enabled, + false, + ); + await db.query("select public.set_user_prefix($1,$2,'Builder','gem')", [ + admin, + player, + ]); + assert.equal( + ( + await db.query("select custom_prefix from public.profiles where id=$1", [ + player, + ]) + ).rows[0].custom_prefix, + "Builder", + ); + await db.exec( + "create role identity_probe bypassrls; grant select,update on public.profiles to identity_probe; grant usage on schema public to identity_probe; set role identity_probe", + ); + await assert.rejects( + db.query("update public.profiles set role='admin' where id=$1", [player]), + /Moderator permission required/, + ); + await db.exec("reset role"); + for (const role of ["anon", "authenticated"]) { + await db.exec(`set role ${role}`); + for (const query of [ + [ + "select public.publish_dataset($1,$2,'unpublish',null)", + [updated, admin], + ], + ["select public.set_beta_badge($1,true)", [admin]], + ["select public.set_user_prefix($1,$2,'Spoof','star')", [admin, player]], + [ + "select public.review_content_report($1,$2,'dismiss','Valid feedback here')", + [missing, admin], + ], + [ + "select public.review_map_appeal($1,$2,'approve','Valid feedback here')", + [missing, admin], + ], + ]) + await assert.rejects(db.query(...query), /permission denied/); + await db.exec("reset role"); + } + console.log( + "Admin permissions SQL passed: moderators approve/reject maps and updates; administrative actions and role spoofing are blocked; admins retain full access.", + ); +} finally { + await db.close(); +} diff --git a/scripts/test-bug-reports-sql.mjs b/scripts/test-bug-reports-sql.mjs index c3b0dac..b993a00 100644 --- a/scripts/test-bug-reports-sql.mjs +++ b/scripts/test-bug-reports-sql.mjs @@ -167,16 +167,15 @@ try { ); const mod = randomUUID(); await db.query("insert into auth.users values($1)", [mod]); - await db.query( - "insert into public.profiles(id,role) values($1,'moderator')", - [mod], - ); + await db.query("insert into public.profiles(id,role) values($1,'admin')", [ + mod, + ]); await assert.rejects( db.query("select public.set_user_prefix($1,$2,'Builder','gem')", [ account, mod, ]), - /Moderator permission/, + /Admin permission/, ); await db.query("select public.set_user_prefix($1,$2,'Builder','gem')", [ mod, @@ -205,6 +204,7 @@ try { await db.exec("set role reward_probe"); for (const column of [ "role='moderator'", + "role='admin'", "bug_finder=false", "custom_prefix='Spoof'", "custom_prefix_icon='crown'", diff --git a/scripts/test-discord-http.mjs b/scripts/test-discord-http.mjs new file mode 100644 index 0000000..bd37455 --- /dev/null +++ b/scripts/test-discord-http.mjs @@ -0,0 +1,184 @@ +import assert from "node:assert/strict"; +import { randomUUID, randomBytes } from "node:crypto"; +import { mkdir, writeFile } from "node:fs/promises"; +import { createClient } from "@supabase/supabase-js"; +process.loadEnvFile(".env.local"); +const origin = process.argv[2] || "https://minegeo.vercel.app"; +const db = createClient( + process.env.NEXT_PUBLIC_SUPABASE_URL, + process.env.SUPABASE_SECRET_KEY, + { auth: { persistSession: false, autoRefreshToken: false } }, +); +const accounts = [], + rooms = []; +const checked = (r) => { + if (r.error) throw new Error(r.error.message); + return r.data; +}; +await mkdir(".data/discord-check", { recursive: true }); +const journal = () => + writeFile( + ".data/discord-check/http-fixtures.json", + JSON.stringify({ accounts, rooms }), + ); +function session() { + const cookies = new Map(); + return async ( + path, + body, + method = body === undefined ? "GET" : "POST", + expected = 200, + ) => { + const response = await fetch(`${origin}/api/v1/${path}`, { + method, + headers: { + origin, + "content-type": "application/json", + cookie: [...cookies] + .map(([key, value]) => `${key}=${value}`) + .join("; "), + }, + ...(body === undefined ? {} : { body: JSON.stringify(body) }), + signal: AbortSignal.timeout(60000), + }); + for (const cookie of response.headers.getSetCookie()) { + const part = cookie.split(";")[0], + at = part.indexOf("="); + cookies.set(part.slice(0, at), part.slice(at + 1)); + } + const data = await response.json(); + assert.equal(response.status, expected, `${path}: ${JSON.stringify(data)}`); + assert.match(response.headers.get("cache-control") ?? "", /no-store/); + return data; + }; +} +async function account(role) { + const email = `discord-check-${randomUUID()}@example.com`, + password = randomBytes(24).toString("base64url"); + const { user } = checked( + await db.auth.admin.createUser({ + email, + password, + email_confirm: true, + user_metadata: { display_name: `Discord ${role} fixture` }, + }), + ); + accounts.push(user.id); + await journal(); + checked( + await db + .from("profiles") + .insert({ id: user.id, role, display_name: `Discord ${role} fixture` }), + ); + const api = session(); + await api("auth", { action: "login", email, password, next: "/moderation" }); + return api; +} +try { + const admin = await account("admin"), + moderator = await account("moderator"), + visitor = session(); + const status = await admin("moderation/discord"); + assert.equal(typeof status.botConfigured, "boolean"); + assert.equal(typeof status.pending, "number"); + assert.ok( + !Object.keys(status).some((key) => /token|secret|channel_?id/i.test(key)), + ); + await visitor("moderation/discord", undefined, "GET", 401); + for (const [body, method] of [ + [undefined, "GET"], + [{ action: "check" }, "POST"], + [{ action: "retry" }, "POST"], + ]) + await moderator("moderation/discord", body, method, 403); + if ( + !status.botConfigured || + !status.publicUrlConfigured || + (!status.mapsConfigured && !status.partiesConfigured) + ) + await admin("moderation/discord", { action: "check" }, "POST", 400); + const dispatch = await fetch(`${origin}/api/integrations/discord/dispatch`); + assert.equal(dispatch.status, 401); + console.log( + "Discord HTTP: admin-only setup, secret-free status, disabled configuration and private dispatch verified.", + ); + const dataset = checked( + await db + .from("datasets") + .select("id") + .eq("state", "published") + .is("parent_dataset_id", null) + .is("imported_from_id", null) + .limit(1) + .single(), + ); + const host = session(), + friend = session(), + outsider = session(); + const created = await host("rooms", { + datasetId: dataset.id, + difficulty: "normal", + displayName: "Discord host fixture", + playerLimit: 4, + }); + rooms.push(created.id); + await journal(); + const room = await host(`rooms/${created.id}`); + assert.equal(typeof room.discord.available, "boolean"); + assert.equal(room.discord.state, null); + await outsider(`rooms/${created.id}/discord`, {}, "POST", 403); + await friend(`rooms/${created.id}/join`, { + displayName: "Discord friend fixture", + }); + assert.equal((await friend(`rooms/${created.id}`)).discord, undefined); + await friend(`rooms/${created.id}/discord`, {}, "POST", 403); + await host( + `rooms/${created.id}/discord`, + { channelId: "123456789012345678" }, + "POST", + 400, + ); + if ( + !status.botConfigured || + !status.publicUrlConfigured || + !status.partiesConfigured + ) { + assert.equal(room.discord.available, false); + await host(`rooms/${created.id}/discord`, {}, "POST", 503); + } + assert.equal( + checked( + await db + .from("discord_announcements") + .select("id") + .eq("kind", "party") + .eq("target_id", created.id), + ).length, + 0, + "disabled and unauthorized actions never queue a notification", + ); + await host(`rooms/${created.id}/close`, {}); + console.log( + "Discord HTTP passed: guests and members cannot publish, destination overrides denied, unconfigured hosts get an actionable error, no test Discord messages sent.", + ); +} finally { + for (const id of rooms) { + checked( + await db + .from("discord_announcements") + .delete() + .eq("kind", "party") + .eq("target_id", id), + ); + checked(await db.from("multiplayer_rooms").delete().eq("id", id)); + } + for (const id of accounts) { + checked(await db.from("profiles").delete().eq("id", id)); + checked(await db.auth.admin.deleteUser(id)); + } + await writeFile( + ".data/discord-check/http-fixtures.json", + JSON.stringify({ accounts: [], rooms: [] }), + ); + console.log("Discord HTTP fixtures removed."); +} diff --git a/scripts/test-discord-sql.mjs b/scripts/test-discord-sql.mjs new file mode 100644 index 0000000..074b706 --- /dev/null +++ b/scripts/test-discord-sql.mjs @@ -0,0 +1,391 @@ +import assert from "node:assert/strict"; +import { randomUUID } from "node:crypto"; +import { readFile, readdir } from "node:fs/promises"; +import { PGlite } from "../.data/sql-verification/node_modules/@electric-sql/pglite/dist/index.js"; +const db = new PGlite(); +try { + await db.exec(`create role anon;create role authenticated;create role service_role; + create schema auth;create table auth.users(id uuid primary key); + create function auth.uid() returns uuid language sql as $$select null::uuid$$; + create schema storage;create table storage.buckets(id text primary key,name text,public boolean,file_size_limit bigint);`); + for (const file of (await readdir("supabase/migrations")) + .filter((f) => f.endsWith(".sql")) + .sort()) + await db.exec(await readFile(`supabase/migrations/${file}`, "utf8")); + const owner = randomUUID(), + mod = randomUUID(), + admin = randomUUID(); + for (const [id, role] of [ + [owner, "contributor"], + [mod, "moderator"], + [admin, "admin"], + ]) { + await db.query("insert into auth.users values($1)", [id]); + await db.query("insert into public.profiles(id,role) values($1,$2)", [ + id, + role, + ]); + } + const manifest = { + name: "Discord fixture", + description: "Notification verification", + bounds: { minX: 0, minZ: 0, maxX: 64, maxZ: 64 }, + viewpoints: Array.from({ length: 5 }, (_, i) => ({ + id: `p${i}`, + x: i, + y: 64, + z: i, + })), + }; + const map = ( + await db.query( + `insert into public.datasets(owner_id,name,source,bounds,state,panorama_count) values($1,'Discord fixture','plugin',$2,'review',5) returning id`, + [owner, JSON.stringify(manifest.bounds)], + ) + ).rows[0].id; + await db.query( + "insert into public.dataset_inputs(dataset_id,manifest,validated) values($1,$2,true)", + [map, JSON.stringify(manifest)], + ); + assert.equal( + ( + await db.query( + "select count(*)::int as n from public.discord_announcements", + ) + ).rows[0].n, + 0, + ); + await db.query("select public.publish_dataset($1,$2,'approve',null)", [ + map, + mod, + ]); + assert.equal( + ( + await db.query( + "select count(*)::int as n from public.discord_announcements where kind='map' and target_id=$1", + [map], + ) + ).rows[0].n, + 1, + ); + await db.query( + "update public.datasets set update_state='review' where id=$1", + [map], + ); + await db.query("select public.publish_dataset($1,$2,'approve',null)", [ + map, + mod, + ]); + await db.query("select public.publish_dataset($1,$2,'unpublish',null)", [ + map, + admin, + ]); + await db.query("update public.datasets set state='review' where id=$1", [ + map, + ]); + await db.query("select public.publish_dataset($1,$2,'approve',null)", [ + map, + admin, + ]); + assert.equal( + ( + await db.query( + "select count(*)::int as n from public.discord_announcements", + ) + ).rows[0].n, + 1, + "updates and re-publication do not spam new-map announcements", + ); + const hash = "a".repeat(64), + friend = "b".repeat(64), + outsider = "c".repeat(64); + const create = async () => + ( + await db.query( + "select public.create_map_room($1,'normal','Host fixture',4,$2,$3) as id", + [map, hash, owner], + ) + ).rows[0].id; + const room = await create(); + await db.query( + "select public.multiplayer_action($1,$2,'join','{\"name\":\"Friend fixture\"}')", + [room, friend], + ); + for (const secret of [friend, outsider]) + await assert.rejects( + db.query("select public.publish_discord_party($1,$2)", [room, secret]), + /Only the room owner/, + ); + const event = ( + await db.query("select public.publish_discord_party($1,$2) as id", [ + room, + hash, + ]) + ).rows[0].id; + assert.equal( + ( + await db.query("select public.publish_discord_party($1,$2) as id", [ + room, + hash, + ]) + ).rows[0].id, + event, + "repeated host clicks return the same event", + ); + const claimed = ( + await db.query( + "select * from public.claim_discord_announcements('first',array['party'],null)", + ) + ).rows; + assert.equal(claimed.length, 1); + assert.equal(claimed[0].attempts, 1); + assert.equal( + ( + await db.query( + "select * from public.claim_discord_announcements('second',array['party'],null)", + ) + ).rows.length, + 0, + "active leases prevent double sends", + ); + await db.query( + "update public.discord_announcements set lease_until=clock_timestamp()-interval '1 second' where id=$1", + [event], + ); + assert.equal( + ( + await db.query( + "select * from public.claim_discord_announcements('second',array['party'],null)", + ) + ).rows[0].attempts, + 2, + "expired workers recover", + ); + await db.query( + "update public.discord_announcements set attempts=8,lease_until=clock_timestamp()-interval '1 second' where id=$1", + [event], + ); + assert.equal( + ( + await db.query( + "select * from public.claim_discord_announcements('third',array['party'],null)", + ) + ).rows.length, + 0, + ); + assert.equal( + ( + await db.query( + "select state from public.discord_announcements where id=$1", + [event], + ) + ).rows[0].state, + "failed", + "exhausted expired leases become actionable failures", + ); + await assert.rejects( + db.query("select public.retry_discord_announcements($1)", [mod]), + /Admin permission/, + ); + await db.query("select public.retry_discord_announcements($1)", [admin]); + assert.equal( + ( + await db.query( + "select state from public.discord_announcements where id=$1", + [event], + ) + ).rows[0].state, + "pending", + ); + const full = await create(); + for (let i = 0; i < 3; i++) + await db.query("select public.multiplayer_action($1,$2,'join',$3)", [ + full, + String(i + 4).repeat(64), + JSON.stringify({ name: `Player ${i}` }), + ]); + await assert.rejects( + db.query("select public.publish_discord_party($1,$2)", [full, hash]), + /room is full/, + ); + const expired = await create(); + await db.query( + "update public.multiplayer_rooms set created_at=clock_timestamp()-interval '25 hours' where id=$1", + [expired], + ); + await assert.rejects( + db.query("select public.publish_discord_party($1,$2)", [expired, hash]), + /Only open lobbies/, + ); + await db.query( + "update public.multiplayer_rooms set status='closed' where id=$1", + [room], + ); + await assert.rejects( + db.query("select public.publish_discord_party($1,$2)", [room, hash]), + /Only open lobbies/, + ); + await db.query( + "update public.discord_announcements set state='sent',channel_id='123456789012345678',message_id='223456789012345678' where id=$1", + [event], + ); + let cleanup = ( + await db.query( + "select * from public.claim_discord_cleanup('starting:first',$1)", + [room], + ) + ).rows; + assert.equal( + cleanup.length, + 1, + "a send acknowledged after closure is cleaned up", + ); + assert.ok(cleanup[0].closed_at); + assert.equal( + ( + await db.query( + "select * from public.claim_discord_cleanup('starting:second',$1)", + [room], + ) + ).rows.length, + 0, + "cleanup scheduling has a lease", + ); + const deadline = ( + await db.query("select public.mark_discord_party_closed($1) as deadline", [ + event, + ]) + ).rows[0].deadline; + assert.ok( + Math.abs(new Date(deadline).getTime() - Date.now() - 300000) < 5000, + "red notice gets five minutes", + ); + assert.equal( + String( + ( + await db.query( + "select public.mark_discord_party_closed($1) as deadline", + [event], + ) + ).rows[0].deadline, + ), + String(deadline), + "retry never extends deletion deadline", + ); + await db.query( + "update public.discord_announcements set cleanup_run_id='run_active',cleanup_lease_until=null where id=$1", + [event], + ); + assert.equal( + ( + await db.query( + "select * from public.claim_discord_cleanup('starting:third',$1)", + [room], + ) + ).rows.length, + 0, + "durable workflow remains scheduled beyond the lease", + ); + await db.query( + "update public.discord_announcements set deleted_at=clock_timestamp(),cleanup_run_id=null where id=$1", + [event], + ); + assert.equal( + ( + await db.query( + "select * from public.claim_discord_cleanup('starting:fourth',$1)", + [room], + ) + ).rows.length, + 0, + "removed messages stay removed", + ); + const closedSentRoom = await create(); + const closedSentEvent = ( + await db.query("select public.publish_discord_party($1,$2) as id", [ + closedSentRoom, + hash, + ]) + ).rows[0].id; + await db.query( + "update public.discord_announcements set state='sent',channel_id='123456789012345678',message_id='223456789012345679' where id=$1", + [closedSentEvent], + ); + assert.equal( + ( + await db.query( + "select * from public.claim_discord_cleanup('starting:open',$1)", + [closedSentRoom], + ) + ).rows.length, + 0, + "open rooms are untouched", + ); + await db.query("select public.multiplayer_action($1,$2,'leave','{}')", [ + closedSentRoom, + hash, + ]); + assert.equal( + ( + await db.query( + "select * from public.claim_discord_cleanup('starting:leave',$1)", + [closedSentRoom], + ) + ).rows.length, + 1, + "last-player departure queues cleanup", + ); + const hiddenRoom = await create(); + await db.query("select public.publish_dataset($1,$2,'unpublish',null)", [ + map, + admin, + ]); + await assert.rejects( + db.query("select public.publish_discord_party($1,$2)", [hiddenRoom, hash]), + /Only parties on published maps/, + ); + const imported = ( + await db.query( + `insert into public.datasets(owner_id,name,source,bounds,state,panorama_count,imported_from_id,imported_owner_name) values($1,'Imported fixture','plugin',$2,'draft',5,$3,'Original owner') returning id`, + [owner, JSON.stringify(manifest.bounds), map], + ) + ).rows[0].id; + await db.query( + "insert into public.dataset_inputs(dataset_id,manifest,validated) values($1,$2,true)", + [imported, JSON.stringify(manifest)], + ); + const privateRoom = ( + await db.query( + "select public.create_map_room($1,'normal','Host fixture',4,$2,$3) as id", + [imported, hash, owner], + ) + ).rows[0].id; + await assert.rejects( + db.query("select public.publish_discord_party($1,$2)", [privateRoom, hash]), + /Only parties on published maps/, + ); + for (const role of ["anon", "authenticated"]) { + await db.exec(`set role ${role}`); + await assert.rejects( + db.query("select * from public.discord_announcements"), + /permission denied/, + ); + for (const query of [ + ["select public.publish_discord_party($1,$2)", [room, hash]], + [ + "select * from public.claim_discord_announcements('spoof',array['map'],null)", + [], + ], + ["select public.retry_discord_announcements($1)", [admin]], + ["select * from public.claim_discord_cleanup('spoof',null)", []], + ["select public.mark_discord_party_closed($1)", [event]], + ]) + await assert.rejects(db.query(...query), /permission denied/); + await db.exec("reset role"); + } + console.log( + "Discord SQL passed: first-publication events, one post per party, host authorization, private-map isolation, channel-independent queue, leases, retries, expiry, capacity and server-only access.", + ); +} finally { + await db.close(); +} diff --git a/scripts/test-profile-appearance-browser.mjs b/scripts/test-profile-appearance-browser.mjs index a25f817..b1e50d3 100644 --- a/scripts/test-profile-appearance-browser.mjs +++ b/scripts/test-profile-appearance-browser.mjs @@ -70,7 +70,7 @@ async function account(role) { email_confirm: true, user_metadata: { display_name: - role === "moderator" ? "Prefix moderator test" : "Badge player test", + role === "admin" ? "Prefix admin test" : "Badge player test", }, }), ); @@ -81,7 +81,7 @@ async function account(role) { id, role, display_name: - role === "moderator" ? "Prefix moderator test" : "Badge player test", + role === "admin" ? "Prefix admin test" : "Badge player test", }), ); const context = await browser.newContext({ locale: "en-US" }); @@ -98,7 +98,7 @@ try { await mkdir(".data/support-deploy", { recursive: true }); await phase("starting"); const player = await account("contributor"), - mod = await account("moderator"); + mod = await account("admin"); await api(player.context, "profile", { glowColor: "#54baff" }, "PATCH", 403); await api( player.context, @@ -118,7 +118,7 @@ try { await player.page.goto(origin + "/settings", { waitUntil: "networkidle" }); assert.equal(await player.page.locator("input[type=color]").count(), 0); await mod.page.goto(origin + "/settings", { waitUntil: "networkidle" }); - assert.ok((await mod.page.locator(".username-moderator svg").count()) > 0); + assert.ok((await mod.page.locator(".username-admin svg").count()) > 0); await mod.page.goto(origin + "/moderation", { waitUntil: "networkidle" }); await mod.page .getByLabel("Player name or account ID", { exact: true }) @@ -343,7 +343,7 @@ try { ); await phase("passed"); console.log( - "Profile appearance production passed: moderator-only prefixes, removal, automatic badges, real webhook grant/revoke, persistent color, authenticated multiplayer/chat, guest spoof prevention and mobile layouts.", + "Profile appearance production passed: admin-only prefixes, removal, automatic badges, real webhook grant/revoke, persistent color, authenticated multiplayer/chat, guest spoof prevention and mobile layouts.", ); } catch (error) { for (const context of contexts) diff --git a/scripts/test-site-settings-sql.mjs b/scripts/test-site-settings-sql.mjs index e2ec79f..dbace05 100644 --- a/scripts/test-site-settings-sql.mjs +++ b/scripts/test-site-settings-sql.mjs @@ -1,21 +1,21 @@ import assert from "node:assert/strict"; -import { readFile } from "node:fs/promises"; +import { readFile, readdir } from "node:fs/promises"; import { PGlite } from "../.data/sql-verification/node_modules/@electric-sql/pglite/dist/index.js"; const db = new PGlite(); try { await db.exec(`create role anon; create role authenticated; create role service_role bypassrls; - create table public.profiles(id uuid primary key, role text not null); - grant select on public.profiles to service_role; - insert into public.profiles values - ('00000000-0000-4000-8000-000000000001', 'moderator'), - ('00000000-0000-4000-8000-000000000002', 'contributor');`); - await db.exec( - await readFile( - "supabase/migrations/20261006141532_site_settings.sql", - "utf8", - ), - ); + create schema auth; create table auth.users(id uuid primary key); + create function auth.uid() returns uuid language sql as $$ select null::uuid $$; + create schema storage; create table storage.buckets(id text primary key,name text,public boolean,file_size_limit bigint);`); + for (const file of (await readdir("supabase/migrations")) + .filter((f) => f.endsWith(".sql")) + .sort()) + await db.exec(await readFile(`supabase/migrations/${file}`, "utf8")); + await db.exec(`insert into auth.users values + ('00000000-0000-4000-8000-000000000001'),('00000000-0000-4000-8000-000000000002'); + insert into public.profiles(id,role) values + ('00000000-0000-4000-8000-000000000001','admin'),('00000000-0000-4000-8000-000000000002','contributor');`); const flag = async () => (await db.query("select beta_badge_enabled from public.site_settings")) .rows[0].beta_badge_enabled; @@ -39,11 +39,11 @@ try { db.query( "select public.set_beta_badge('00000000-0000-4000-8000-000000000002', false)", ), - /Moderator permission required/, + /Admin permission required/, ); await assert.rejects( db.query("select public.set_beta_badge(null, false)"), - /Moderator permission required/, + /Admin permission required/, ); assert.equal(await flag(), true); await db.query( @@ -66,7 +66,7 @@ try { /visibility is required/, ); console.log( - "Site settings SQL passed: guests and players denied; moderators can hide and restore; updates persist and record the moderator.", + "Site settings SQL passed: guests and players denied; admins can hide and restore; updates persist and record the admin.", ); } finally { await db.close(); diff --git a/shared/database.ts b/shared/database.ts index 64be178..db2b6aa 100644 --- a/shared/database.ts +++ b/shared/database.ts @@ -43,6 +43,13 @@ export type Database = { }; public: { Tables: { + discord_announcements: { + Row: import("./discord").DiscordAnnouncement; + Insert: Partial & + Pick; + Update: Partial; + Relationships: []; + }; bug_reports: { Row: import("./bug-reports").BugReportRecord; Insert: Partial & @@ -695,6 +702,38 @@ export type Database = { [_ in never]: never; }; Functions: { + claim_discord_cleanup: { + Args: { p_worker: string; p_target?: string | null }; + Returns: import("./discord").DiscordAnnouncement[]; + SetofOptions: { + from: "*"; + to: "discord_announcements"; + isOneToOne: false; + isSetofReturn: true; + }; + }; + mark_discord_party_closed: { + Args: { p_event: string }; + Returns: string; + }; + publish_discord_party: { + Args: { p_room: string; p_access_hash: string }; + Returns: string; + }; + claim_discord_announcements: { + Args: { p_worker: string; p_kinds: string[]; p_target?: string | null }; + Returns: import("./discord").DiscordAnnouncement[]; + SetofOptions: { + from: "*"; + to: "discord_announcements"; + isOneToOne: false; + isSetofReturn: true; + }; + }; + retry_discord_announcements: { + Args: { p_reviewer: string }; + Returns: number; + }; set_user_prefix: { Args: { p_reviewer: string; diff --git a/shared/discord.ts b/shared/discord.ts new file mode 100644 index 0000000..5b44e98 --- /dev/null +++ b/shared/discord.ts @@ -0,0 +1,35 @@ +export type DiscordKind = "map" | "party"; +export type DiscordAnnouncement = { + id: string; + kind: DiscordKind; + target_id: string; + state: "pending" | "sending" | "sent" | "skipped" | "failed"; + attempts: number; + next_attempt_at: string; + lease_owner: string | null; + lease_until: string | null; + channel_id: string | null; + message_id: string | null; + last_error: string | null; + created_at: string; + sent_at: string | null; + closed_at?: string | null; + delete_after?: string | null; + deleted_at?: string | null; + cleanup_run_id?: string | null; + cleanup_lease_until?: string | null; + cleanup_error?: string | null; +}; +export type PartyAnnouncement = { + available: boolean; + state: DiscordAnnouncement["state"] | null; +}; +export type DiscordStatus = { + botConfigured: boolean; + publicUrlConfigured: boolean; + mapsConfigured: boolean; + partiesConfigured: boolean; + pending: number; + failed: number; + sent: number; +}; diff --git a/shared/locales/ru.json b/shared/locales/ru.json index 6d97500..372a82b 100644 --- a/shared/locales/ru.json +++ b/shared/locales/ru.json @@ -609,7 +609,7 @@ "Game mode": "Режим игры", "Solo": "Одиночная игра", "Multiplayer": "Мультиплеер", - "Your player name": "Ваше игровое имя", + "Your player name": "Ваше имя игрока", "Enter a player name": "Введите игровое имя", "Player limit (including you)": "Лимит игроков (включая вас)", "Create a room, share the invite link, and start when your friends arrive.": "Создайте комнату, поделитесь ссылкой и начните игру, когда друзья присоединятся.", @@ -751,15 +751,12 @@ "Tell us what happened. We’ll reply here.": "Расскажите, что произошло. Мы ответим здесь.", "Guest reports stay in this browser. Sign in to keep your conversation in your account.": "Обращения гостей сохраняются в этом браузере. Войдите, чтобы сохранить переписку в аккаунте.", "Bug reporting is temporarily unavailable. Please try again later.": "Отправка обращений временно недоступна. Попробуйте позже.", - "Your player name": "Ваше имя игрока", "Bug title": "Название ошибки", - "What happened?": "Что произошло?", "What did you do, what did you expect, and what happened instead?": "Что вы сделали, чего ожидали и что произошло вместо этого?", "Screenshot (optional)": "Скриншот (необязательно)", "PNG, JPG, or WebP, up to 5 MB. Shared privately with support.": "PNG, JPG или WebP, до 5 МБ. Доступен только вам и поддержке.", "Your bug reports": "Ваши обращения", "Your reports and support replies will appear here.": "Здесь появятся ваши обращения и ответы поддержки.", - "Draft": "Черновик", "Waiting for support": "Ожидает поддержки", "← Your bug reports": "← Ваши обращения", "Your report is saved. We’re connecting it to support.": "Обращение сохранено. Передаём его поддержке.", @@ -767,7 +764,6 @@ "Finish sending report": "Завершить отправку", "Keep this report in my account": "Сохранить обращение в аккаунте", "Support awarded you a username glow.": "Поддержка подарила вам свечение имени.", - "Account settings": "Настройки аккаунта", "For guest reports, sign in and link this report to receive your reward.": "Если вы отправили обращение как гость, войдите и привяжите его к аккаунту, чтобы получить награду.", "Attached bug screenshot": "Прикреплённый скриншот ошибки", "Support conversation": "Переписка с поддержкой", @@ -788,5 +784,51 @@ "Sign in to link this report": "Войдите, чтобы привязать обращение", "Remove hidden control characters": "Удалите скрытые управляющие символы", "Turn on light mode": "Включить светлую тему", - "Turn on dark mode": "Включить тёмную тему" + "Turn on dark mode": "Включить тёмную тему", + "Community": "Сообщество", + "Join us on Discord": "Присоединиться к Discord", + "Follow us on Telegram": "Мы в Telegram", + "Admin": "Администратор", + "Admin permission required": "Требуются права администратора", + "Moderators can inspect and review pending maps and updates. Other controls require Admin permission.": "Модераторы могут проверять ожидающие рассмотрения карты и обновления. Для остальных действий нужны права администратора.", + "Changes apply to every player. Only admins can edit these settings.": "Изменения применяются ко всем игрокам. Менять эти настройки могут только администраторы.", + "Map is not awaiting review": "Карта не ожидает проверки", + "Discord integration": "Интеграция с Discord", + "New approved maps are announced automatically. Hosts can publish an open party to the shared Discord channel.": "Новые одобренные карты публикуются автоматически. Хозяева открытых лобби могут поделиться приглашением в общем канале Discord.", + "Discord bot configured": "Бот Discord настроен", + "Discord setup required in Vercel environment variables": "Настройте Discord через переменные окружения Vercel", + "Map announcements: {state}": "Объявления о картах: {state}", + "Party announcements: {state}": "Объявления о лобби: {state}", + "Configured": "Настроено", + "Not configured": "Не настроено", + "{pending} pending · {failed} need attention · {sent} sent": "{pending} ожидают · {failed} требуют проверки · {sent} отправлено", + "Check Discord setup": "Проверить настройку Discord", + "Retry saved announcements": "Повторить отправку объявлений", + "Refresh status": "Обновить статус", + "Discord bot and channel access checked. Send Messages and Embed Links are also required.": "Бот и доступ к каналам проверены. Также нужны права на отправку сообщений и вставку ссылок.", + "Saved Discord announcements will retry.": "Сохранённые объявления Discord будут отправлены повторно.", + "Publish party to Discord": "Опубликовать лобби в Discord", + "Publishing…": "Публикация…", + "Publishing shares this invite with everyone in the community Discord channel.": "После публикации это приглашение увидят все участники общего канала Discord.", + "Party posted to Discord.": "Лобби опубликовано в Discord.", + "Party announcement is waiting for Discord delivery.": "Объявление о лобби ожидает отправки в Discord.", + "Discord delivery needs an administrator to check the bot setup and retry.": "Администратору нужно проверить настройку бота и повторить отправку.", + "This party announcement expired or the lobby is no longer available.": "Объявление устарело или лобби больше недоступно.", + "Party announcement queued. Anyone in the Discord channel can open the invite and join.": "Объявление добавлено в очередь. Любой участник канала Discord сможет открыть приглашение и присоединиться.", + "Only the room owner can publish this party": "Только хозяин лобби может опубликовать его", + "Discord party sharing is not configured yet": "Публикация лобби в Discord пока не настроена", + "Only open lobbies can be published": "Можно публиковать только открытые лобби", + "Only parties on published maps can be shared on Discord": "В Discord можно публиковать только лобби с общедоступными картами", + "Configure the Discord bot token, public site URL, and at least one channel ID.": "Настройте токен бота Discord, публичный адрес сайта и хотя бы один ID канала.", + "Discord bot token is invalid. Update it in Vercel.": "Токен бота Discord недействителен. Обновите его в Vercel.", + "Discord bot needs View Channel, Send Messages, and Embed Links in the configured channel.": "Боту Discord нужны права View Channel, Send Messages и Embed Links в выбранном канале.", + "Discord channel was not found. Check the channel ID and bot access.": "Канал Discord не найден. Проверьте ID канала и доступ бота.", + "Discord channel ID is invalid.": "Неверный ID канала Discord.", + "Choose a Discord server text or announcement channel.": "Выберите текстовый канал или канал объявлений на сервере Discord.", + "Use a Discord bot token.": "Используйте токен бота Discord.", + "Discord could not be reached. Delivery will retry.": "Discord недоступен. Отправка будет повторена.", + "Discord rate limit. Delivery will retry.": "Discord ограничил частоту запросов. Отправка будет повторена.", + "Discord rejected this notification.": "Discord отклонил это объявление.", + "Discord did not confirm delivery.": "Discord не подтвердил отправку.", + "Discord delivery could not be confirmed. Delivery will retry.": "Не удалось подтвердить отправку в Discord. Она будет повторена." } diff --git a/shared/multiplayer.ts b/shared/multiplayer.ts index bfa9af2..06113d7 100644 --- a/shared/multiplayer.ts +++ b/shared/multiplayer.ts @@ -48,6 +48,7 @@ export type RoomResult = { guess: { x: number; z: number } | null; }; export type RoomView = { + discord?: import("./discord").PartyAnnouncement; id: string; datasetId: string; name: string; diff --git a/shared/permissions.ts b/shared/permissions.ts new file mode 100644 index 0000000..b3e7d8e --- /dev/null +++ b/shared/permissions.ts @@ -0,0 +1,10 @@ +export type AccountRole = "contributor" | "moderator" | "admin"; +export type StaffRole = Exclude; + +export function canReviewMaps(role: string | undefined): role is StaffRole { + return role === "moderator" || role === "admin"; +} + +export function isAdministrator(role: string | undefined) { + return role === "admin"; +} diff --git a/supabase/migrations/20261009123642_admin_permissions.sql b/supabase/migrations/20261009123642_admin_permissions.sql new file mode 100644 index 0000000..73b8d34 --- /dev/null +++ b/supabase/migrations/20261009123642_admin_permissions.sql @@ -0,0 +1,119 @@ +-- Admin accounts manage settings and enforcement. Moderators review map submissions. +alter table public.profiles drop constraint profiles_role_check; +alter table public.profiles add constraint profiles_role_check + check (role in ('contributor','moderator','admin')); + +create or replace function public.publish_dataset(p_dataset uuid,p_reviewer uuid,p_decision text,p_reason text default null) returns void +language plpgsql security invoker set search_path='' as $$ +declare d public.datasets; m jsonb; rev uuid; first jsonb; count integer; +begin + if not exists(select 1 from public.profiles where id=p_reviewer and role in ('moderator','admin')) then raise exception 'Moderator permission required'; end if; + if p_decision='unpublish' and not exists(select 1 from public.profiles where id=p_reviewer and role='admin') then raise exception 'Admin permission required'; end if; + select * into d from public.datasets where id=p_dataset and parent_dataset_id is null for update; + if not found then raise exception 'Map not found'; end if; + if p_decision='approve' then + if not(d.state='review' or (d.state='published' and coalesce(d.update_state,'')='review')) then raise exception 'Map is not ready for publication'; end if; + select manifest into m from public.dataset_inputs where dataset_id=p_dataset and validated; + count:=jsonb_array_length(m->'viewpoints'); first:=m->'viewpoints'->0; + if m is null or count<5 or count>24 then raise exception 'Map requires 5–24 validated panoramas'; end if; + insert into public.revisions(dataset_id,manifest,asset_root) values(p_dataset,m,p_dataset::text) returning id into rev; + update public.datasets set state='published',revision_id=rev,update_state=null,name=m->>'name',description=m->>'description',panorama_count=count, + map_path=coalesce(m->>'mapPath',d.map_path), + cover_path=coalesce(first->>'assetRoot',id::text)||'/'||coalesce(first->>'assetId',first->>'id')||'/preview.webp',rejection_reason=null,updated_at=now() where id=p_dataset; + elsif p_decision='reject' and (d.state='review' or (d.state='published' and d.update_state='review')) then + if coalesce(length(trim(p_reason)),0)=0 then raise exception 'Provide a rejection reason'; end if; + update public.datasets set state=case when state='published' then state else 'rejected' end,update_state=case when state='published' then 'rejected' end,rejection_reason=p_reason,updated_at=now() where id=p_dataset; + elsif p_decision='unpublish' and d.state='published' then + update public.datasets set state='unpublished',update_state=null,updated_at=now() where id=p_dataset; + else raise exception 'Invalid review transition'; end if; + insert into public.review_decisions(dataset_id,reviewer_id,decision,reason) values(p_dataset,p_reviewer,p_decision,p_reason); +end; $$; + +create or replace function public.review_content_report(p_report uuid,p_reviewer uuid,p_decision text,p_reason text) returns void +language plpgsql security invoker set search_path='' as $$ +declare r public.reports; d public.datasets; map_id uuid; strikes integer; +begin + if not exists(select 1 from public.profiles where id=p_reviewer and role = 'admin') then raise exception 'Admin permission required'; end if; + select dataset_id into map_id from public.reports where id=p_report; + select * into d from public.datasets where id=map_id for update; + select * into r from public.reports where id=p_report for update; + if not found or r.state<>'open' or r.report_cycle<>d.report_cycle then raise exception 'Report is no longer awaiting review'; end if; + if p_decision is null or p_decision not in ('confirm','dismiss') or coalesce(length(trim(p_reason)),0) not between 10 and 2000 then raise exception 'Provide a review decision and feedback in 10–2000 characters'; end if; + if p_decision='confirm' then + if d.banned_at is not null then raise exception 'This map is already banned; dismiss the remaining report or review its appeal'; end if; + if p_reviewer=r.owner_id or p_reviewer=d.owner_id then raise exception 'An independent moderator must confirm this report'; end if; + -- Distinct reporters provide each strike, including legacy reports. + if exists(select 1 from public.reports where dataset_id=d.id and owner_id=r.owner_id and report_cycle=d.report_cycle and state='confirmed') then raise exception 'This reporter already contributed a strike'; end if; + strikes:=d.report_strikes+1; + update public.datasets set report_strikes=strikes, + state=case when strikes>=3 then 'unpublished' else state end, + update_state=case when strikes>=3 then null else update_state end, + banned_at=case when strikes>=3 then now() end, + ban_reason=case when strikes>=3 then trim(p_reason) end,updated_at=now() where id=d.id; + end if; + update public.reports set state=case when p_decision='confirm' then 'confirmed' else 'dismissed' end, + reviewer_id=p_reviewer,reviewed_at=now(),review_reason=trim(p_reason) where id=r.id; +end; $$; + +create or replace function public.review_map_appeal(p_appeal uuid,p_reviewer uuid,p_decision text,p_reason text) returns void +language plpgsql security invoker set search_path='' as $$ +declare a public.map_appeals; d public.datasets; map_id uuid; +begin + if not exists(select 1 from public.profiles where id=p_reviewer and role = 'admin') then raise exception 'Admin permission required'; end if; + select dataset_id into map_id from public.map_appeals where id=p_appeal; + select * into d from public.datasets where id=map_id for update; + select * into a from public.map_appeals where id=p_appeal for update; + if not found or a.state<>'open' or d.banned_at is null or a.report_cycle<>d.report_cycle then raise exception 'Appeal is no longer awaiting review'; end if; + if p_reviewer=d.owner_id then raise exception 'An independent moderator must review your appeal'; end if; + if p_decision is null or p_decision not in ('approve','reject') or coalesce(length(trim(p_reason)),0) not between 10 and 2000 then raise exception 'Provide an appeal decision and feedback in 10–2000 characters'; end if; + if p_decision='approve' then + if d.revision_id is null then raise exception 'Map has no approved version to restore'; end if; + update public.datasets set report_strikes=0,report_cycle=report_cycle+1,banned_at=null,ban_reason=null,state='published',updated_at=now() where id=d.id; + update public.reports set state='dismissed',reviewer_id=p_reviewer,reviewed_at=now(),review_reason='Closed when the map ban appeal was approved.' where dataset_id=d.id and report_cycle=d.report_cycle and state='open'; + end if; + update public.map_appeals set state=case when p_decision='approve' then 'approved' else 'rejected' end,reviewer_id=p_reviewer,reviewed_at=now(),review_reason=trim(p_reason) where id=a.id; +end; $$; + +create or replace function public.set_beta_badge(p_reviewer uuid, p_enabled boolean) +returns boolean language plpgsql security invoker set search_path = '' as $$ +begin + if not exists ( + select 1 from public.profiles where id = p_reviewer and role = 'admin' + ) then + raise exception 'Admin permission required'; + end if; + if p_enabled is null then + raise exception 'Beta badge visibility is required'; + end if; + update public.site_settings + set beta_badge_enabled = p_enabled, updated_at = now(), updated_by = p_reviewer + where id = true; + if not found then raise exception 'Site settings not found'; end if; + return p_enabled; +end; +$$; + +create or replace function public.set_user_prefix(p_reviewer uuid,p_user uuid,p_prefix text,p_icon text) returns void +language plpgsql security invoker set search_path='' as $$ +begin + if not exists(select 1 from public.profiles where id=p_reviewer and role = 'admin') then raise exception 'Admin permission required'; end if; + if p_icon not in ('star','crown','heart','flame','gem','trophy','sparkles') or p_icon is null then raise exception 'Invalid prefix icon'; end if; + if p_prefix is not null and (length(btrim(p_prefix)) not between 1 and 24 or p_prefix ~ '[[:cntrl:]]') then raise exception 'Invalid custom prefix'; end if; + update public.profiles set custom_prefix=case when p_prefix is null then null else btrim(p_prefix) end,custom_prefix_icon=p_icon where id=p_user; + if not found then raise exception 'Player not found'; end if; +end; $$; + +revoke all on function public.publish_dataset(uuid,uuid,text,text) from public,anon,authenticated; +grant execute on function public.publish_dataset(uuid,uuid,text,text) to service_role; + +revoke all on function public.review_content_report(uuid,uuid,text,text) from public,anon,authenticated; +grant execute on function public.review_content_report(uuid,uuid,text,text) to service_role; + +revoke all on function public.review_map_appeal(uuid,uuid,text,text) from public,anon,authenticated; +grant execute on function public.review_map_appeal(uuid,uuid,text,text) to service_role; + +revoke all on function public.set_beta_badge(uuid,boolean) from public,anon,authenticated; +grant execute on function public.set_beta_badge(uuid,boolean) to service_role; + +revoke all on function public.set_user_prefix(uuid,uuid,text,text) from public,anon,authenticated; +grant execute on function public.set_user_prefix(uuid,uuid,text,text) to service_role; diff --git a/supabase/migrations/20261009133431_discord_announcements.sql b/supabase/migrations/20261009133431_discord_announcements.sql new file mode 100644 index 0000000..482535d --- /dev/null +++ b/supabase/migrations/20261009133431_discord_announcements.sql @@ -0,0 +1,100 @@ +-- Durable, server-only announcements. One event per public map or shared party. +create table public.discord_announcements ( + id uuid primary key default gen_random_uuid(), + kind text not null check (kind in ('map','party')), + target_id uuid not null, + state text not null default 'pending' check (state in ('pending','sending','sent','skipped','failed')), + attempts integer not null default 0 check (attempts>=0), + next_attempt_at timestamptz not null default clock_timestamp(), + lease_owner text, + lease_until timestamptz, + channel_id text, + message_id text, + last_error text, + created_at timestamptz not null default clock_timestamp(), + sent_at timestamptz, + unique(kind,target_id) +); +create index discord_announcements_due on public.discord_announcements(next_attempt_at,created_at) + where state in ('pending','sending'); +alter table public.discord_announcements enable row level security; +revoke all on public.discord_announcements from public,anon,authenticated; +grant all on public.discord_announcements to service_role; +create policy "Server manages Discord delivery" on public.discord_announcements + for all to service_role using (true) with check (true); + +create function public.queue_new_map_discord() returns trigger +language plpgsql security invoker set search_path='' as $$ +begin + if new.state='published' and old.state<>'published' and old.revision_id is null + and new.parent_dataset_id is null and new.imported_from_id is null then + insert into public.discord_announcements(kind,target_id) values('map',new.id) + on conflict(kind,target_id) do nothing; + end if; + return new; +end; $$; +create trigger queue_new_map_discord after update of state on public.datasets + for each row execute function public.queue_new_map_discord(); + +create function public.publish_discord_party(p_room uuid,p_access_hash text) returns uuid +language plpgsql security invoker set search_path='' as $$ +declare room public.multiplayer_rooms; event_id uuid; active integer; +begin + select * into room from public.multiplayer_rooms where id=p_room for update; + if not found then raise exception 'Room not found'; end if; + if not exists(select 1 from public.multiplayer_players where room_id=p_room + and id=room.host_player_id and access_hash=p_access_hash and not has_left) then + raise exception 'Only the room owner can publish this party'; + end if; + if room.status<>'lobby' or room.created_at=room.player_limit then raise exception 'This room is full'; end if; + if not exists(select 1 from public.revisions r join public.datasets d on d.id=r.dataset_id + where r.id=room.revision_id and d.state='published' and d.parent_dataset_id is null and d.imported_from_id is null) then + raise exception 'Only parties on published maps can be shared on Discord'; + end if; + insert into public.discord_announcements(kind,target_id) values('party',p_room) + on conflict(kind,target_id) do nothing; + select id into event_id from public.discord_announcements where kind='party' and target_id=p_room; + return event_id; +end; $$; + +create function public.claim_discord_announcements(p_worker text,p_kinds text[],p_target uuid default null) +returns setof public.discord_announcements language sql security invoker set search_path='' as $$ + with exhausted as ( + update public.discord_announcements set state='failed',lease_owner=null,lease_until=null, + last_error='Discord delivery could not be confirmed. Check the channel before retrying.' + where state='sending' and attempts>=8 and lease_until'closed') then + new.closed_at=clock_timestamp(); + end if; + return new; +end; $$; +create trigger capture_closed_discord_party before insert or update of state,channel_id,message_id + on public.discord_announcements for each row execute function public.capture_closed_discord_party(); + +-- Older closed announcements receive the same red notice and five-minute grace period. +update public.discord_announcements e set closed_at=clock_timestamp() + where kind='party' and state='sent' and not exists( + select 1 from public.multiplayer_rooms r where r.id=e.target_id and r.status<>'closed'); + +create function public.claim_discord_cleanup(p_worker text,p_target uuid default null) +returns setof public.discord_announcements language plpgsql security invoker set search_path='' as $$ +begin + -- Reconcile overlapping close/send transactions before claiming cleanup. + update public.discord_announcements e set closed_at=clock_timestamp() + where kind='party' and state='sent' and closed_at is null + and (p_target is null or target_id=p_target) + and not exists(select 1 from public.multiplayer_rooms r where r.id=e.target_id and r.status<>'closed'); + return query + update public.discord_announcements e set cleanup_run_id=p_worker, + cleanup_lease_until=clock_timestamp()+interval '3 minutes' + where e.id in ( + select id from public.discord_announcements where kind='party' and state='sent' + and closed_at is not null and deleted_at is null and channel_id is not null and message_id is not null + and (p_target is null or target_id=p_target) + and (cleanup_run_id is null or cleanup_run_id like 'starting:%') + and (cleanup_lease_until is null or cleanup_lease_until", + difficulty: "normal", + players: 1, + limit: 8, +}; +test("closed Discord parties turn red, lose their join prompt, and delete idempotently", async () => { + const payload = closedPartyMessage("https://minegeo.vercel.app", target); + assert.equal(payload.embeds[0].color, 0xed4245); + assert.ok(payload.embeds[0].description.includes("five minutes")); + assert.ok(!JSON.stringify(payload).includes("Join this party")); + assert.deepEqual(payload.allowed_mentions, { parse: [] }); + const methods: string[] = []; + const client = new DiscordClient("private-token", async (url, init) => { + assert.equal( + url, + `https://discord.com/api/v10/channels/${channel}/messages/${messageId}`, + ); + methods.push(init!.method!); + if (init?.method === "PATCH") { + assert.deepEqual(JSON.parse(String(init.body)), payload); + return Response.json({ id: messageId, channel_id: channel }); + } + assert.equal(init?.body, undefined); + return new Response(null, { status: 204 }); + }); + assert.equal( + await client.closeParty( + channel, + messageId, + "https://minegeo.vercel.app", + target, + ), + true, + ); + await client.remove(channel, messageId); + assert.deepEqual(methods, ["PATCH", "DELETE"]); + const missing = new DiscordClient("token", async () => + Response.json({ code: 10008 }, { status: 404 }), + ); + assert.equal( + await missing.closeParty( + channel, + messageId, + "https://minegeo.vercel.app", + target, + ), + false, + ); + await missing.remove(channel, messageId); + await assert.rejects( + new DiscordClient("token", async () => + Response.json({ code: 10003 }, { status: 404 }), + ).remove(channel, messageId), + /channel was not found/, + ); + await assert.rejects( + new DiscordClient("token", async () => + Response.json({ retry_after: 10 }, { status: 429 }), + ).remove(channel, messageId), + (e) => e instanceof DiscordDeliveryError && e.retryAfter === 10, + ); +}); +test("Discord config requires a public HTTPS site and valid channel IDs without exposing a public token", () => { + const env = { + MINEGEO_URL: "https://minegeo.vercel.app", + DISCORD_BOT_TOKEN: "test-token", + DISCORD_PARTIES_CHANNEL_ID: channel, + }; + assert.equal(discordConfiguration(env).partyChannel, channel); + for (const url of [ + "http://localhost:3000", + "https://user:secret@example.com", + "https://example.com/private?key=secret", + ]) + assert.equal( + discordConfiguration({ ...env, MINEGEO_URL: url }).origin, + null, + ); + for (const id of ["bad", "123", `${channel}/messages`]) + assert.equal( + discordConfiguration({ ...env, DISCORD_PARTIES_CHANNEL_ID: id }) + .partyChannel, + null, + ); + assert.equal( + discordConfiguration({ ...env, DISCORD_BOT_TOKEN: "secret\nheader" }).token, + null, + ); +}); +test("Discord announcements use fixed public links, bounded escaped text, suppressed mentions and a stable nonce", () => { + const body = announcementMessage( + job.id, + "party", + "https://minegeo.vercel.app", + source, + ); + assert.deepEqual(body.allowed_mentions, { parse: [] }); + assert.equal( + body.embeds[0].url, + `https://minegeo.vercel.app/multiplayer/${target}`, + ); + assert.ok(!body.embeds[0].title.includes("[click](")); + assert.equal(body.nonce.length, 24); + assert.equal(body.enforce_nonce, true); + assert.equal( + body.nonce, + announcementMessage(job.id, "party", "https://minegeo.vercel.app", source) + .nonce, + ); + assert.notEqual( + body.nonce, + announcementMessage( + "another", + "party", + "https://minegeo.vercel.app", + source, + ).nonce, + ); + const map = announcementMessage(job.id, "map", "https://minegeo.vercel.app", { + ...source, + name: "A".repeat(500), + locations: 10, + }); + assert.equal(map.embeds[0].title.length, 256); + assert.equal(map.embeds[0].fields[1].value, "10"); +}); +test("Discord REST sends the bot token only to Discord, confirms message and channel identity, and honors rate limits", async () => { + const payload = announcementMessage( + job.id, + "party", + "https://minegeo.vercel.app", + source, + ); + const client = new DiscordClient("private-token", async (url, init) => { + assert.equal( + url, + `https://discord.com/api/v10/channels/${channel}/messages`, + ); + assert.equal( + (init?.headers as Record).Authorization, + "Bot private-token", + ); + assert.equal(init?.redirect, "error"); + assert.deepEqual(JSON.parse(String(init?.body)), payload); + return Response.json({ id: messageId, channel_id: channel }); + }); + assert.equal(await client.send(channel, payload), messageId); + const limited = new DiscordClient("token", async () => + Response.json({ retry_after: 65.4 }, { status: 429 }), + ); + await assert.rejects( + limited.send(channel, payload), + (e) => + e instanceof DiscordDeliveryError && e.retryAfter === 66 && !e.permanent, + ); + for (const status of [401, 403, 404]) { + const bad = new DiscordClient("secret-token", async () => + Response.json({ message: "secret-token internal raw error" }, { status }), + ); + await assert.rejects( + bad.send(channel, payload), + (e) => + e instanceof DiscordDeliveryError && + e.permanent && + !e.message.includes("secret-token"), + ); + } + await assert.rejects( + new DiscordClient("token", async () => + Response.json({ id: messageId, channel_id: "wrong" }), + ).send(channel, payload), + /did not confirm/, + ); + await assert.rejects( + new DiscordClient("token", async () => { + throw new Error("token-secret"); + }).send(channel, payload), + /could not be reached/, + ); +}); +test("durable delivery skips unavailable parties, records acknowledged sends and preserves recoverable failures", async () => { + const run = async (options: { + source: typeof source | null; + fail?: Error; + attempts?: number; + }) => { + const patches: Partial[] = [], + requests: unknown[] = []; + await deliverDiscordAnnouncement( + { ...job, attempts: options.attempts ?? 1 }, + { + origin: "https://minegeo.vercel.app", + channel, + now: () => 1000, + load: async () => options.source, + client: { + send: async (...args) => { + requests.push(args); + if (options.fail) throw options.fail; + return messageId; + }, + }, + finish: async (patch) => { + patches.push(patch); + }, + }, + ); + return { patch: patches.at(-1)!, requests }; + }; + const skipped = await run({ source: null }); + assert.equal(skipped.patch.state, "skipped"); + assert.equal(skipped.requests.length, 0); + const sent = await run({ source }); + assert.equal(sent.patch.state, "sent"); + assert.equal(sent.patch.message_id, messageId); + assert.equal(sent.patch.lease_owner, null); + const rate = await run({ + source, + fail: new DiscordDeliveryError("Rate limited", 65), + }); + assert.equal(rate.patch.state, "pending"); + assert.equal(Date.parse(rate.patch.next_attempt_at!), 66000); + assert.equal( + ( + await run({ + source, + fail: new DiscordDeliveryError("Bad credentials", 60, true), + }) + ).patch.state, + "failed", + ); + assert.equal( + (await run({ source, attempts: 8, fail: new Error("private raw error") })) + .patch.state, + "failed", + ); +}); +test("Discord setup check only reads bot/channel details and rejects non-server text channels", async () => { + const paths: string[] = []; + const bot = new DiscordClient("token", async (url, init) => { + assert.equal(init?.method, "GET"); + paths.push(String(url)); + return Response.json( + String(url).endsWith("@me") + ? { bot: true, username: "MineGeo" } + : { guild_id: channel, type: 0 }, + ); + }); + assert.equal((await bot.check([channel, channel])).botName, "MineGeo"); + assert.equal(paths.length, 2); + await assert.rejects( + new DiscordClient("token", async (url) => + Response.json( + String(url).endsWith("@me") + ? { bot: true, username: "Test" } + : { guild_id: channel, type: 2 }, + ), + ).check([channel]), + /text or announcement channel/, + ); +}); diff --git a/tests/permissions.test.ts b/tests/permissions.test.ts new file mode 100644 index 0000000..a1ca11d --- /dev/null +++ b/tests/permissions.test.ts @@ -0,0 +1,19 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { canReviewMaps, isAdministrator } from "../shared/permissions"; +test("only staff can review maps and only admins have administrative access", () => { + for (const role of [ + undefined, + "contributor", + "Admin", + "owner", + "administrator", + ]) { + assert.equal(canReviewMaps(role), false); + assert.equal(isAdministrator(role), false); + } + assert.equal(canReviewMaps("moderator"), true); + assert.equal(isAdministrator("moderator"), false); + assert.equal(canReviewMaps("admin"), true); + assert.equal(isAdministrator("admin"), true); +}); diff --git a/tests/sql/moderation.sql b/tests/sql/moderation.sql index 0c25e94..86a6ac0 100644 --- a/tests/sql/moderation.sql +++ b/tests/sql/moderation.sql @@ -15,7 +15,7 @@ begin select id into fourth from auth.users where id not in (owner,moderator,reporter,second,third) order by id limit 1; if fourth is null then raise exception 'Six fixture accounts are required'; end if; insert into public.profiles(id) values(owner),(moderator),(reporter),(second),(third),(fourth) on conflict(id) do nothing; - update public.profiles set role='moderator' where id=moderator; + update public.profiles set role='admin' where id=moderator; update public.profiles set role='contributor' where id in (owner,reporter,second,third,fourth); insert into public.datasets(owner_id,name,source,bounds,state,panorama_count) values(owner,'Report regression','plugin','{"minX":0,"minZ":0,"maxX":64,"maxZ":64}','published',5) returning id into fixture; @@ -38,7 +38,7 @@ begin begin perform public.review_content_report(report,owner,'confirm','Confirmed by the map owner.'); raise exception 'Unauthorized moderator succeeded'; - exception when others then if sqlerrm<>'Moderator permission required' then raise; end if; end; + exception when others then if sqlerrm<>'Admin permission required' then raise; end if; end; perform public.review_content_report(report,moderator,'dismiss','The reported terrain looks correct.'); if (select report_strikes from public.datasets where id=fixture)<>0 then raise exception 'Dismissed report added a strike'; end if; report:=public.submit_content_report(fixture,reporter,revision,null,'spam','The map contains inappropriate advertisements.');